├── test.txt ├── images ├── samoyed.jpg ├── decoded_image.png ├── encoded_image.png └── encoded_sample.png ├── README.md ├── .gitignore ├── steganography.py └── solution.py /test.txt: -------------------------------------------------------------------------------- 1 | hi this is test 2 | -------------------------------------------------------------------------------- /images/samoyed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srli/image_steganography/HEAD/images/samoyed.jpg -------------------------------------------------------------------------------- /images/decoded_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srli/image_steganography/HEAD/images/decoded_image.png -------------------------------------------------------------------------------- /images/encoded_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srli/image_steganography/HEAD/images/encoded_image.png -------------------------------------------------------------------------------- /images/encoded_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srli/image_steganography/HEAD/images/encoded_sample.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Image Steganography 3 | Fun little exercise in image steganography. [Full implementation walkthrough here]( http://blog.justsophie.com/image-steganography-in-python/). 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | -------------------------------------------------------------------------------- /steganography.py: -------------------------------------------------------------------------------- 1 | """A program that encodes and decodes hidden messages in images through LSB steganography""" 2 | from PIL import Image, ImageFont, ImageDraw 3 | import textwrap 4 | 5 | def decode_image(file_location="images/encoded_sample.png"): 6 | """Decodes the hidden message in an image 7 | 8 | file_location: the location of the image file to decode. By default is the provided encoded image in the images folder 9 | """ 10 | encoded_image = Image.open(file_location) 11 | red_channel = encoded_image.split()[0] 12 | 13 | x_size = encoded_image.size[0] 14 | y_size = encoded_image.size[1] 15 | 16 | decoded_image = Image.new("RGB", encoded_image.size) 17 | pixels = decoded_image.load() 18 | 19 | for i in range(x_size): 20 | for j in range(y_size): 21 | pass #TODO: Fill in decoding functionality 22 | 23 | decoded_image.save("images/decoded_image.png") 24 | 25 | def write_text(text_to_write, image_size): 26 | """Writes text to an RGB image. Automatically line wraps 27 | 28 | text_to_write: the text to write to the image 29 | image_size: size of the resulting text image. Is a tuple (x_size, y_size) 30 | """ 31 | image_text = Image.new("RGB", image_size) 32 | font = ImageFont.load_default().font 33 | drawer = ImageDraw.Draw(image_text) 34 | 35 | #Text wrapping. Change parameters for different text formatting 36 | margin = offset = 10 37 | for line in textwrap.wrap(text_to_write, width=60): 38 | drawer.text((margin,offset), line, font=font) 39 | offset += 10 40 | return image_text 41 | 42 | def encode_image(text_to_encode, template_image="images/samoyed.jpg"): 43 | """Encodes a text message into an image 44 | 45 | text_to_encode: the text to encode into the template image 46 | template_image: the image to use for encoding. An image is provided by default. 47 | """ 48 | pass #TODO: Fill out this function 49 | 50 | if __name__ == '__main__': 51 | print("Decoding the image...") 52 | decode_image() 53 | 54 | print("Encoding the image...") 55 | encode_image() 56 | -------------------------------------------------------------------------------- /solution.py: -------------------------------------------------------------------------------- 1 | """A program that encodes and decodes hidden messages in images through LSB steganography""" 2 | from PIL import Image, ImageFont, ImageDraw 3 | import textwrap 4 | 5 | def decode_image(file_location="images/encoded_sample.png"): 6 | """Decodes the hidden message in an image 7 | 8 | file_location: the location of the image file to decode. By default is the provided encoded image in the images folder 9 | """ 10 | encoded_image = Image.open(file_location) 11 | red_channel = encoded_image.split()[0] 12 | 13 | x_size = encoded_image.size[0] 14 | y_size = encoded_image.size[1] 15 | 16 | decoded_image = Image.new("RGB", encoded_image.size) 17 | pixels = decoded_image.load() 18 | 19 | for i in range(x_size): 20 | for j in range(y_size): 21 | if bin(red_channel.getpixel((i, j)))[-1] == '0': 22 | pixels[i, j] = (255, 255, 255) 23 | else: 24 | pixels[i, j] = (0,0,0) 25 | decoded_image.save("images/decoded_image.png") 26 | 27 | def write_text(text_to_write, image_size): 28 | """Writes text to an RGB image. Automatically line wraps 29 | 30 | text_to_write: the text to write to the image 31 | """ 32 | image_text = Image.new("RGB", image_size) 33 | font = ImageFont.load_default().font 34 | drawer = ImageDraw.Draw(image_text) 35 | 36 | #Text wrapping. Change parameters for different text formatting 37 | margin = offset = 10 38 | for line in textwrap.wrap(text_to_write, width=60): 39 | drawer.text((margin,offset), line, font=font) 40 | offset += 10 41 | return image_text 42 | 43 | def encode_image(text_to_encode, template_image="images/samoyed.jpg"): 44 | """Encodes a text message into an image 45 | 46 | text_to_encode: the text to encode into the template image 47 | template_image: the image to use for encoding. An image is provided by default. 48 | """ 49 | template_image = Image.open(template_image) 50 | red_template = template_image.split()[0] 51 | green_template = template_image.split()[1] 52 | blue_template = template_image.split()[2] 53 | 54 | x_size = template_image.size[0] 55 | y_size = template_image.size[1] 56 | 57 | #text draw 58 | image_text = write_text(text_to_encode, template_image.size) 59 | bw_encode = image_text.convert('1') 60 | 61 | #encode text into image 62 | encoded_image = Image.new("RGB", (x_size, y_size)) 63 | pixels = encoded_image.load() 64 | for i in range(x_size): 65 | for j in range(y_size): 66 | red_template_pix = bin(red_template.getpixel((i,j))) 67 | old_pix = red_template.getpixel((i,j)) 68 | tencode_pix = bin(bw_encode.getpixel((i,j))) 69 | 70 | if tencode_pix[-1] == '1': 71 | red_template_pix = red_template_pix[:-1] + '1' 72 | else: 73 | red_template_pix = red_template_pix[:-1] + '0' 74 | pixels[i, j] = (int(red_template_pix, 2), green_template.getpixel((i,j)), blue_template.getpixel((i,j))) 75 | 76 | encoded_image.save("images/encoded_image.png") 77 | 78 | if __name__ == '__main__': 79 | decode_image() 80 | encode_image("hello world") 81 | --------------------------------------------------------------------------------