Image Encoding & Decoding in Python

Wondering what the image is?

That's HELLO.THERE.YOU.ARE.AWESOME encoded in this image.

The encoding is:

The pixels in the above image are numbered 0..99 for the first row, 100..199 for the second row etc.
White pixels represent ASCII codes.
The ASCII code for a particular white pixel is equal to the offset from the last white pixel.
For example, the first white pixel at location 65 would represent ASCII code 65 ('A'), the next at location 131 would represent ASCII code (131 - 65) = 66 ('B') and so on.
The text contained in the image is the answer encoded in Morse, where "a test" would be encoded as ".- / - . ... -"

So, how do we decode this image into readable form?

Program? Yes, you're right but which language? You can use any language out there and analyze the image but python with PIL (Python Imaging Library) makes it incredibly simple task.

Here is the python code to do this:

#ImageDecoder.py

from PIL import Image
import optparse

morse_dict={
 'A':'.-','B':'-...','C':'-.-.','D':'-..','E':'.','F':'..-.',
 'G':'--.','H':'....','I':'..','J':'.---','K':'-.-','L':'.-..',
 'M':'--','N':'-.','O':'---','P':'.--.','Q':'--.-','R':'.-.',
 'S':'...','T':'-','U':'..-','V':'...-','W':'.--','X':'-..-',
 'Y':'-.--','Z':'--..','0':'-----','1':'.----','2':'..---','3':'...--',
 '4':'....-','5':'.....','6':'-....','7':'--...','8':'---..','9':'----.',
 '.':'.-.-.-',',':'--..--','?':'..--..',"'":'.----.','/':'-..-.','(':'-.--.-',
 ')':'-.--.-',':':'---...',';':'-.-.-.','=':'-...-',' ':'.-.-.','-':'-....-',
 '_':'..--.-','"':'.-..-.','$':'...-..-','':''
 }
def getLetterForMorse(l):
 
 for key, value in morse_dict.iteritems():
  if(value == l):
   return str(key)
def Main():
 parser = optparse.OptionParser('usage: python ImageDecoder.py -i <input PNG image name>')
 parser.add_option('-i',dest='inPNG',type='string',help='Please specify the input image file')
 (options,arg) = parser.parse_args()
 if (options.inPNG == None):
  print parser.usage
  exit(0)
 else:
  inPNG = options.inPNG
 _img = Image.open(inPNG)
 W = _img.size[0]
 H = _img.size[1]
 _pixs = _img.load()
 lastOff = 0
 _letter = ""
 answer = ""

 for y in range(H):
  for x in range(W):
   if(_pixs[x,y] == (255,255,255)):
    offset = y*100   x - lastOff
    lastOff = y*100   x
    _char = chr(offset)
    if(_char != ' '):
     _letter  = _char
    else:
     answer  = getLetterForMorse(_letter)
     _letter = ""

 print answer
 
if __name__ == '__main__':
 Main()

Download ImageDecoder.py from pastebin.

Okay, now when we did it we think of creating an encoder which can encode text into such kind of images.

Python again. Here we go:

#ImageEncoder.py 

from PIL import Image
import optparse

morse_list={
'A':'.-','B':'-...','C':'-.-.','D':'-..','E':'.','F':'..-.',
'G':'--.','H':'....','I':'..','J':'.---','K':'-.-','L':'.-..',
'M':'--','N':'-.','O':'---','P':'.--.','Q':'--.-','R':'.-.',
'S':'...','T':'-','U':'..-','V':'...-','W':'.--','X':'-..-',
'Y':'-.--','Z':'--..','0':'-----','1':'.----','2':'..---','3':'...--',
'4':'....-','5':'.....','6':'-....','7':'--...','8':'---..','9':'----.',
'.':'.-.-.-',',':'--..--','?':'..--..',"'":'.----.','/':'-..-.','(':'-.--.-',
')':'-.--.-',':':'---...',';':'-.-.-.','=':'-...-',' ':'.-.-.','-':'-....-',
'_':'..--.-','"':'.-..-.','$':'...-..-','':''
}

def EncodeToImage(data,_PNG):
 letters = list(data)
 pixsum = 0
 whitePixels = []
 for letter in letters:
  _lettermorse = morse_list[letter]
  _morsechars = list(_lettermorse)
  for morsechar in _morsechars:
   intval = ord(morsechar)
   pixsum  = intval
   whitePixels.append(pixsum)
  pixsum  = 32
  whitePixels.append(pixsum)
 W = 100
 H = whitePixels[len(whitePixels)-1]/100   1
 img = Image.new( 'RGB', (W,H), "black")
 pixels = img.load()
 
 for whitePixel in whitePixels:
  y = whitePixel/100
  x = whitePixel%100
  pixels[x,y] = (255,255,255)
 img.save(_PNG)

def Main():
 parser = optparse.OptionParser('usage: python ImageEncoder.py -o <output PNG image name>')
 parser.add_option('-o',dest='outPNG',type='string',help='Please specify the output image file')
 (options,arg) = parser.parse_args()
 if (options.outPNG == None):
  print parser.usage
  exit(0)
 else:
  outPNG = options.outPNG
 datatoencode = raw_input("Please enter data to encode (without spaces):")
 EncodeToImage(datatoencode.upper(),outPNG)
 
if __name__ == '__main__':
 Main()

Download ImageEncoder.py from pastebin.

Notes:

  1. The concept for this encoding has been taken from Hack This Site's programming challenge 2 and the first part i.e. the ImageDecoder.py is also the solution of this HTS challenge. I took it further to create an encoder to have fun.
  2. You need to install Python 2.7.x and PIL (Python Imaging Library) for this to work.