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
Example #2
0
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
Example #3
0
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