Exemple #1
0
def decode(infile):
    """
    Decode the data from a given file or file-like object.

    :param infile: File path or file-like object.
    :return: The encoded data as a byte array.
    """

    encoded_image = Image.open(infile)

    # If this is a multi-channel image, the call to getdata() will return
    # an array of tuples for the pixel data. If this is a single-channel
    # image, we just get an array of pixel values. To normalize this, we
    # will try to flatten the array first. If this throws a TypeError, we
    # can just assume we have a single-channel image.
    try:
        encoded_data = encoded_image.getdata()
        encoded_data = itertools.chain.from_iterable(encoded_data)
        encoded_data = bytearray(encoded_data)
    except TypeError:
        encoded_data = encoded_image.getdata()
        encoded_data = bytearray(encoded_data)

    decoded_byte = 0
    decoded_bytes = bytearray()

    current_byte = 0

    # Find the size of the encoded data from the first N bytes of the image
    size_bytes = struct.calcsize('l') * 8
    for i in xrange(size_bytes):
        if bits.is_bit_set(encoded_data[current_byte], 0):
            decoded_byte = bits.set_bit(decoded_byte, 7 - (i % 8))
        else:
            decoded_byte = bits.unset_bit(decoded_byte, 7 - (i % 8))

        if 7 - (i % 8) == 0:
            decoded_bytes.append(decoded_byte)

        current_byte += 1

    decoded_size, = struct.unpack('l', str(decoded_bytes))
    decoded_size *= 8
    decoded_bytes = bytearray()
    decoded_byte = 0

    # Decode the original datas
    for i in xrange(decoded_size):
        if bits.is_bit_set(encoded_data[current_byte], 0):
            decoded_byte = bits.set_bit(decoded_byte, 7 - (i % 8))
        else:
            decoded_byte = bits.unset_bit(decoded_byte, 7 - (i % 8))

        if 7 - (i % 8) == 0:
            decoded_bytes.append(decoded_byte)

        current_byte += 1

    return decoded_bytes
Exemple #2
0
def encode(data, infile, outfile):
    """
    Encode the given data in a new copy of a given image file.

    :param data: Data to be encoded. This is typically a bytearray.

    :param infile: The original image to use. This can be either a
        file name or an open file object.

    :param outfile: The new image to create. This can be either a
        file name of an open file object.
    """

    base_image = Image.open(infile)

    # If this is a multi-channel image, the call to getdata() will return
    # an array of tuples for the pixel data. If this is a single-channel
    # image, we just get an array of pixel values. To normalize this, we
    # will try to flatten the array first. If this throws a TypeError, we
    # can just assume we have a single-channel image.
    try:
        composite_data = base_image.getdata()
        composite_data = itertools.chain.from_iterable(composite_data)
        composite_data = bytearray(composite_data)
    except TypeError:
        composite_data = base_image.getdata()
        composite_data = bytearray(composite_data)

    # Build a simple header that will hold the size of the encoded data.
    # We use a structure here to make sure the header size itself remains
    # consistent.
    encoding_header = struct.pack('l', len(data))

    # Prepend the header to the original data
    full_data = bytearray(encoding_header)
    full_data.extend(bytearray(data))

    current_byte = 0

    # Iterate over each byte in the input data.
    for data_byte in full_data:

        # Iterate over each bit in the input data byte in high-to-low order.
        for bit_shift in xrange(7, -1, -1):

            # If the current input bit is set, set the low bit in the current byte
            # of the base image. Similarly, if the bit is unset, then unset the low
            # bit of the current image byte.
            if bits.is_bit_set(data_byte, bit_shift):
                composite_data[current_byte] = bits.set_bit(composite_data[current_byte], 0)
            else:
                composite_data[current_byte] = bits.unset_bit(composite_data[current_byte], 0)

            # Go on to the next byte in the base image
            current_byte += 1

    # Re-encode the data into a new image
    data_image_size = base_image.size
    composite_image = Image.frombytes(base_image.mode, data_image_size, str(composite_data))

    # Save the image to the supplied output file
    composite_image.save(outfile, format=base_image.format)