def _lsb_dct_coefficient_encode(image, output, message, nbits=1): assert len(image.quantization) * 64 >= len(message) * 8 bit_mask = (1 << nbits) - 1 msg_iter = iter_bits(message, nbits) for i in range(len(image.quantization)): for j, msg_bit in zip(range(64), msg_iter): image.quantization[i][j] = (image.quantization[i][j] & ~bit_mask) | msg_bit image.save(output, qtables=image.quantization) return image
def test_iter_bits_double(self): # Try comparing chunks returned by iter_bits (when chunksize=2) # to string returned by bin() using capital letters # e.g. 'A' == 65 == 0b1000001 # iter_bits('A', chunksize=2) == [01, 00, 00, 01] for o in range(65, 91): char = chr(o) bits = list(iter_bits(char, chunksize=2)) exp_bin_str = '{:08b}'.format(o) bin_str = self._bin_str_from_chunks(bits, chunksize=2) self.assertEqual(bin_str, exp_bin_str)
def test_iter_bits_single(self): # Try comparing bits returned by iter_bits to string returned # by bin() using capital letters from the alphabet. for o in range(65, 91): char = chr(o) bits = list(iter_bits(char)) while bits[0] == 0 and len(bits) > 1: # bin() drops leading zeros bits.pop(0) bin_str = '0b' + ''.join(map(str, bits)) self.assertEqual(bin_str, bin(o))
def _lsb_pixel_encode(image, output, message, nbits=1): assert fits_in_image(image, message, nbits) # TODO: can this be accomplished with Image.tobytes/frombytes/BytesIO or ImageChops with a dummy message Image # Encode nbits of the message into each of the three color-components (RGB) # for each pixel until we're at the end of the message. # The bit mask needs to capture the least nbits significant bits. pixels = image.load() bit_mask = (1 << nbits) - 1 msg_iterator = grouper(iter_bits(message, chunksize=nbits), 3) for xy, msg_part in zip(iter_coords(image), msg_iterator): rgb = tuple((component & ~bit_mask | chunk) if chunk is not None else component for component, chunk in zip(pixels[xy], msg_part)) pixels[xy] = rgb image.save(output) return image