Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from PIL import Image # Import the image module from PIL
- #------------
- def encStegan( srcImg = None, txt = "", colourChannel = 0 ):
- """Morph image file with hidden bytes .. eg. ASCII secret message !
- RETURN new version of Image ( or an error String )"""
- if ( not srcImg or not txt ): return "Function REQUIRES an image and some text."
- if ( colourChannel not in range(3)): return "Colour channel must be in (0,1,2) for R, G or B"
- srcImg = srcImg.convert('RGB') # Convert the image to RGB
- srcSize = srcImg.size
- width, height = srcSize # (unpack from tuple) the image's width and height
- imgTxtLimit = (( width * height ) // 8 ) - 1 # less 1 to allow for \x00
- if ( len( txt ) > imgTxtLimit ): return "NOT enough pixels to encode text. Text limit is "+ imgTxtLimit +" chars"
- #-----------------
- newImg = Image.new('RGB', srcSize ) # create a grid for the output image
- bytesList = [ ord(c) for c in txt ] # map text to bytes (ints)
- bytesList.append(0x00) # add NULL on end
- lenBytes = len( bytesList )
- bytesIndex = 0
- copyPixel = False
- bitMask = 0x80 # 0x80 == 0b10000000
- thisByte = bytesList[0] # get 1st byte to encode
- #Set up loops to modify each pixel within the image
- for row in range( height ):
- for col in range( width ):
- #
- pixXY = (col, row)
- thisColour = srcImg.getpixel( pixXY ) # (r, g, b)
- if ( copyPixel ): # set True when txt is exhausted
- newImg.putpixel( pixXY, thisColour )
- else:
- channelValue = thisColour[ colourChannel ] # colourChannel 0,1,2 ==> r, g, b
- #
- # I am working with 8 pixel (byte) sequences
- # .. using bit0 from each byte to make a hidden byte
- # tweak the low-order bit (bit0) in the channelValue to hold a bit value from the byte being encoded
- if ( thisByte & bitMask ): # bit set to 1 ?
- channelValue |= 0x01 # set bit0 to 1 in colour channel
- else:
- channelValue &= 0xFE # set bit0 to 0 in colour channel .. 0xFE == 0b11111110
- #
- newColor = list( thisColour )
- newColor[ colourChannel ] = channelValue # replace
- newImg.putpixel( pixXY, tuple( newColor ))
- bitMask >>= 1 # work down through bits in the byte being encoded
- if ( not bitMask ): # bitMask now 0x00 -> all bits mapped for thisByte
- #
- bytesIndex += 1
- if ( bytesIndex >= lenBytes ):
- copyPixel = True # FINISHED encoding text. Just copy pixels from now on !
- else:
- thisByte = bytesList[ bytesIndex ] # get next byte to encode
- bitMask = 0x80
- #
- #
- #
- return newImg
- #----------------
- #------------
- def decStegan( srcImg = None, colourChannel = 0):
- """Scan image file for hidden bytes .. eg. ASCII secret message !"""
- srcImg = srcImg.convert('RGB') # Convert the image to RGB
- width, height = srcImg.size # Assign the image's width and height to variables
- buildByte = 0x00 # .. using | ( binary OR ) and << (left_shift) 1
- byteList = []
- pixelCount = 0 # wrap at 8
- for row in range( height ):
- for col in range( width ):
- #
- thisColour = srcImg.getpixel( (col, row) ) # (r, g, b)
- channelValue = thisColour[ colourChannel ] # colourChannel 0,1,2 ==> r, g, b
- #
- # I am working with 8 pixel (byte) sequences
- # .. taking bit0 from each of the 8 channelValue bytes to make a hidden byte
- buildByte <<= 1 # left shift puts a 0 at bit0 .. move ALL bits left, ready for next bit
- # if bit0 is set on (1) in image data, set (copy to) bit0 in buildByte
- if ( channelValue & 0x01 ): buildByte |= 0x01 # AND (&) tests .. OR (|) sets bit(s) to 1
- # print( channelValue, thisColour, hex(buildByte)) # DEBUG .. use to comprehend ..!
- pixelCount += 1
- if ( pixelCount >= 8 ): # got to 8 ..
- #
- #print( hex( buildByte )) # DEBUG
- #
- if ( not buildByte ): return byteList # 0x00 so return RESULT !
- # ====== ========
- # else NOT 0x00 NULL terminator so ..
- # ..add byte to sequence, then reset for next 8 pixel sequence
- byteList.append( buildByte )
- # RESET for new byte ..
- pixelCount = 0
- buildByte = 0x00 # 0
- #
- #
- #
- return [] # "NO NULL terminator"
- #------------------
- #-- ----------------
- def showByteSequence( ordSequence ) :
- print( 'OUTPUT follows ..')
- print( 'DECIMAL ..')
- print( ordSequence )
- print( 'BINARY ..')
- resChars = [ bin(n) for n in ordSequence ]
- print( ",".join(resChars))
- print( 'HEX ..')
- resChars = [ hex(n) for n in ordSequence ]
- print( ",".join(resChars))
- print( 'ASCII ..')
- resChars = [ ascii(chr(n)) for n in ordSequence ]
- print( ",".join(resChars))
- print( 'TEXT .. show only standard ASCII with non-printables excluded .. IF ANY')
- resChars = []
- for thisOrd in ordSequence :
- if ( thisOrd >= 32 and thisOrd < 127 ) : resChars.append( chr( thisOrd ))
- #
- print( "".join(resChars)) # make a String of it
- print("--------------------------------------------------------------------")
- #--------------
- # TEST CALLS ..
- print('\nReading file "encoded_image.png" ...')
- encImg = Image.open('encoded_image.png')
- #
- print('Scanning RED channel for Steganography text ..')
- print('====================')
- bytesFound = decStegan( encImg, 0 ) # 0 -> red
- showByteSequence( bytesFound )
- print('Scanning GREEN channel for Steganography text ..')
- print('======================')
- bytesFound = decStegan( encImg, 1 ) # 1 -> green
- showByteSequence( bytesFound )
- print('Scanning BLUE channel for Steganography text ..')
- print('=====================')
- bytesFound = decStegan( encImg, 2 ) # 2 -> blue
- showByteSequence( bytesFound )
- #---------------
- # OK, now encode a message in the BLUE channel !
- print('ENCODING file "encoded_image2.png" in the BLUE channel ...')
- theMessage = "FutureLearn rocks !! I love Python & my Raspberry Pi 4"
- encBLUEimg = encStegan( encImg, theMessage, 2 ) # 2 -> blue
- encBLUEimg.save("encoded_image2.png")
- print('--------------------------------------------------------------------')
- print('NOW scan new image file "encoded_image2.png" in the BLUE channel ...')
- print('Scanning BLUE channel for Steganography text ..')
- print('=====================')
- bytesFound = decStegan( encBLUEimg, 2 ) # 2 -> blue
- showByteSequence( bytesFound )
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement