def test_image_fit(self): # Message size: 1024 bits # Min image size: 18x19 (18x19x3 bits per pixel = 1026) msg = self._create_message_of_len(1024) image = self._create_image_mock(18, 19) small_image = self._create_image_mock(18, 18) large_image = self._create_image_mock(120, 120) self.assertTrue(fits_in_image(image, msg, nbits=1)) self.assertFalse(fits_in_image(small_image, msg, nbits=1)) self.assertTrue(fits_in_image(large_image, msg, nbits=1))
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