def tagger(image,position): new_image = [] row_n = 0 for row in image: row_bits = bits.message_to_bits(str(row_n)) i = 0 new_row = [] # Nested 'for' loop iterates over each pixel in the current row-list # (r,g,b) matches r,g,b to the three intenstiy values in each pixel # respectively, so r = first intensity value (red), and so on. for (r,g,b) in row: # Use codebit function to set the LSB of the # new intensity values to match the bit being indexed new_r = bits.set_bit(r,bits.codebit(i,row_bits),position) new_g = bits.set_bit(g,bits.codebit(i + 1,row_bits),position) new_b = bits.set_bit(b,bits.codebit(i + 2,row_bits),position) # Amalgamate the three new intensity values into a new pixel new_pixel = (new_r,new_g,new_b) # Append new pixel to new row-list new_row.append(new_pixel) # increment counter by 3, as each pixel has 3 intensity values i += 3 # attach new row-list to new image-list new_image.append(new_row) row_n += 1 return new_image
def round_trip(message): """ - Used to test bits_to_message and message_to_bits functions - Should always return True """ return bits.bits_to_message(bits.message_to_bits(message)) == message
def encode(image,message): """ This function takes an image as its first argument, a message as its second argument, and returns an encoded image as its result. The input image is a rectangular two-dimensional list of pixels, in zero-based row-major order. Each pixel is a 3-element tuple of integers in the range [0,255]. Input message is a string of ASCII characters. Encoding process: 1: Convert the ASCII message into a string of binary digits using message_to_bits function 2: Take the first intensity value (red intenstiy, of top-left pixel) convert it into binary, and generate a new BINARY intenstiy value exactly the same as the original, except change THE LEAST SIGNIFICANT BIT (LSB) of this number to match the FIRST bit of the message bitstream. 3: Repeat the above step for intenstiy value 2, ( green intensity of top-left pixel), for second bitstream bit, and same for third intenstiy value, so you now have three new 'encoded' intenstiy values. 4: Put these values into a NEW three elemnt tuple, i.e an encoded PIXEL, then put this new pixel into a new row-list. 5: Repeat steps 2-4 for the entire first row-list of the original image, to generate an encoded row-list of tuples almost identical to the original one, except for having (possibly) different LSBs for each intensity value. Add this row-list to a new image-list, i.e. the encoded image. 6: Repeat 5 for each row-list of the original image, to build up an entirely new, encoded image-list which the encode function will return as its result. General cases: - NOT ENOUGH INTENSTIY VALUES TO ENCODE ENTIRE MESSAGE Program will encode as much of the message as possible - EXTRA INTENSTIY VALUES LEFTOVER Program will change the LSB of all 'unencoded' intensity values to 0 Inputs: image: a two-dimensional list of pixels, in zero-based row-major order. Each pixel is a three element tuple of integers in the range [0,255]. message: a string of ASCII characters Result: new_image: a new image in the same format as the original, with the same dimensions, but with the message coded into the LSBs of the intenstiy values in each pixel. Examples: >>> test_image = [[(15,103,255),(0,3,19)],[(22,200,1),(8,8,8)], [(0,0,0),(5,123,19)]] >>> encode(test_image, '') [[(14, 102, 254), (0, 2, 18)], [(22, 200, 0), (8, 8, 8)], [(0, 0, 0), (4, 122, 18)]] >>> encode(test_image, 'hello') [[(14, 103, 255), (0, 3, 18)], [(22, 200, 0), (9, 9, 8)], [(0, 1, 0), (5, 122, 19)]] >>> encode([], 'hello') [] """ bitstream = bits.message_to_bits(message) new_image = [] i = 0 # Set counter i to keep track of index location in bitstream # The ith bit is encoded for row in image: # First 'for' loop iterates over each row-list # Empty list intialised for each new row-list new_row = [] # Nested 'for' loop iterates over each pixel in the current row-list # (r,g,b) matches r,g,b to the three intenstiy values in each pixel # respectively, so r = first intensity value (red), and so on. for (r,g,b) in row: # Use codebit function to set the LSB of the # new intensity values to match the bit being indexed new_r = bits.set_bit(r,bits.codebit(i,bitstream),0) new_g = bits.set_bit(g,bits.codebit(i + 1,bitstream),0) new_b = bits.set_bit(b,bits.codebit(i + 2,bitstream),0) # Amalgamate the three new intensity values into a new pixel new_pixel = (new_r,new_g,new_b) # Append new pixel to new row-list new_row.append(new_pixel) # increment counter by 3, as each pixel has 3 intensity values i += 3 # attach new row-list to new image-list new_image.append(new_row) return new_image
def encode_ext(image, message, num_bits): """ Essentially the same as the encode function, but: - in the original encode function, only the LSB was used to encode the message - in this function, an extra input, num_bits is introduced, which controls how many bits per 8-bit chunk are used to encode the message, up to using all 8 bits. Inputs: image: a two-dimensional list of pixels, in zero-based row-major order. Each pixel is a three element tuple of integers in the range [0,255]. message: a string of ASCII characters num_bits: integer value between 1 and 8 inclusive, which determines how many bits are used to encode the message, starting with the LSB. Result: new_image: a new image in the same format as the original, with the same dimensions, but with the message coded into the intenstiy values in each pixel. Number of bits used depends on value of num_bits. Examples: >>> encode_ext(test_image,'',4) [[(0, 96, 240), (0, 0, 16)], [(16, 192, 0), (0, 0, 0)], [(0, 0, 0), (0, 112, 16)]] >>> encode_ext(test_image,'',8) [[(0, 0, 0), (0, 0, 0)], [(0, 0, 0), (0, 0, 0)], [(0, 0, 0), (0, 0, 0)]] >>> encode_ext(test_image,'hello',4) [[(6, 97, 246), (10, 6, 19)], [(22, 195, 6), (15, 0, 0)], [(0, 0, 0), (0, 112, 16)]] >>> encode_ext(test_image,'hello',8) [[(22, 166, 54), (54, 246, 0)], [(0, 0, 0), (0, 0, 0)], [(0, 0, 0), (0, 0, 0)]] """ # Check num_bits is within accepted range if not(0 < num_bits <= 8): print ('Number of bits must be an integer between 1 and 8\ inclusive') return None bitstream = bits.message_to_bits(message) new_image = [] i = 0 for row in image: new_row = [] for pixel in row: pre_pixel = [] # Iterate over each intenstiy value in each pixel, # instead of iterating one pixel at a time for intensity in pixel: # need to set a seperate variable, as this may # be iterated over multiple times by next 'for' # loop new_i = intensity for pos in range(num_bits): # bit position increases after each loop, # until (num_bits -1) is reached new_i = bits.set_bit(new_i,bits.codebit(i,bitstream),pos) # only need to increment by one, as we are now only # setting one bit per loop i += 1 # append each intensity value to a list; # tuples cannot be used as they are immutable pre_pixel.append(new_i) # convert full pixel into three-element tuple new_pixel = (pre_pixel[0],pre_pixel[1],pre_pixel[2]) new_row.append(new_pixel) new_image.append(new_row) return new_image