You’re going to use the same Python library, PIL, to work with your images. The first part of the code is the same you’ve used before before:
#import Image module from PIL library
from PIL import Image
#path to image to hide within here
image = Image.open('images/image.png')
rgb_image = image.convert('RGB') #convert to RGB
width, height = image.size #assign the image width and height to variables
You are going to encode your message using the binary representation of the ASCII characters in the message: this next bit of code converts the message into a string of 1s and 0s. Read the included comments to see how it works.
message = input("What message do you want to hide?")
#I'm going to use the message "hello everybody!" as an example
binary_message = ""
#The for loop will repeat for each character of "hello everybody!", including the space and the exclamation mark
for char in message:
ascii_number = ord(char) #This converts the ASCII character into the denary number that represents it
#For the first letter, 'h', this gives '104'
bin_as_string = bin(ascii_number) #This converts that denary number into a string representing a binary number
#So '104' is converted into the string '0b1101000'
#The 'b' is added in by 'bin()' to show the number is binary
bin_as_string = bin_as_string.replace('b','') #This removes the 'b'
#So finally you get '01101000'
while len(bin_as_string) < 8 :
bin_as_string = '0' + bin_as_string #This lengthens the binary number string until it is 8 bits
#Our example is already 8 bits long, so it remains '01101000'
binary_message = binary_message + bin_as_string #This result is then added to the binary message
Now it’s time to hide the binary message in the bitmap image the code opened. As you know, each pixel in the image has a red value from 0 to 255, and that value represented in binary has 8 bits. For example, if the red value is 200, it is represented in binary as 11001000:
We’re going to use this red value’s last bit (in the 1 column) to store a bit of the binary message. To do this, we replace the last bit in the binary value with a bit from our binary message: 1100100 plus 1 or 0. So we get back 11001000 if we need to store a 0, or 11001001 if we need to store a 1.
You can use the following code to do this.
#Create a grid for the output image
output_image = Image.new('RGB', (width,height))
#Create a variable to keep track of how far through the binary message the code has got
i = 0
#Set up loops to modify each pixel within the image
for row in range(height):
for col in range(width):
r, g, b = rgb_image.getpixel((col, row))
#Start by converting the red value of the pixel to binary
bin_r = bin(r)
#If the binary message has not been completely encoded, the code does the following things
if i < len(binary_message):
#Replace the final bit with a 1 or a 0 from the message
if binary_message[i] == '1':
bin_r = bin_r[:-1] + '1'
else:
bin_r = bin_r[:-1] + '0'
#Once the message has been completely encoded, the code replaces every red value's last bit with a zero
#This isn't good steganography, but it's simple to implement
else:
bin_r = bin_r[:-1] + '0'
#The binary number for the red value is converted back to an integer
new_r = int(bin_r,2)
#This integer is set as the pixel's red value
output_image.putpixel((col, row), (new_r,g,b))
#The variable i is increased by 1 to move on to the next binary character of the message
i = i + 1
#Once the code has iterated over all pixels in the image to change their red values, the image is saved
output_image.save("encoded_image.png")