{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Introduction"},{"metadata":{},"cell_type":"markdown","source":"First of all I wanted to say that, as a French student, I'm very happy to find a competition hosted by a French University, it's not so frequent. Thanks to the organizers!"},{"metadata":{},"cell_type":"markdown","source":"### Definition of steganography"},{"metadata":{},"cell_type":"markdown","source":"**Definition** : steganography is usually referred to as the techniques and methods that allow to hide information within an innocuous-like cover\nobject. The resulting stego-object resembles, as much as possible,\nthe original cover objet. Therefore it can be sent over an unsecured\ncommunication channel that may be subject to wiretapping by an\neavesdropper. Nowadays, steganography has been mostly developed for digital images because of their massive presence over the\nInternet, the universally adopted JPEG compression scheme and\nits relative simplicity to be modified [[1]](https://hal-utt.archives-ouvertes.fr/hal-02542075/file/J_MiPOD_vPub.pdf)"},{"metadata":{},"cell_type":"markdown","source":"> The main difference with cryptography is that in cryptography you don't want the user to understand the message. In steganography, you don't want the user to even know that there is a message."},{"metadata":{},"cell_type":"markdown","source":"Here's a short video from Computerphile about Steganography in general and how you can hide information in the pixels of an image. Very instructive if you don't know much about the field!"},{"metadata":{},"cell_type":"markdown","source":"<iframe width=\"891\" height=\"501\" src=\"https://www.youtube.com/embed/TWEXCYQKyDc\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>"},{"metadata":{},"cell_type":"markdown","source":"This image explains the basic principle of steganography : you change the one or two least significant bytes (LSB). This way, the global modification of the picture is undetectable, but you can carry much information!\n<img align=\"right\" width=\"300\" height=\"300\" src=\"https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcSWW1PzPeVFMp9F3IhRq4zCN5FP3DvZpI5DEmOsOgH0ud2OhLew&usqp=CAU\">"},{"metadata":{},"cell_type":"markdown","source":"## Load Packages"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import pandas as pd \nimport numpy as np\nimport os\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nimport seaborn as sns\nimport random\n\nfrom tqdm.notebook import tqdm\nfrom collections import defaultdict\n\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"PATH = \"/kaggle/input/alaska2-image-steganalysis/\"\nIMAGE_IDS = os.listdir(os.path.join(PATH, 'Cover'))\nN_IMAGES = len(IMAGE_IDS)\nALGORITHMS = ['JMiPOD', 'JUNIWARD', 'UERD']","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"sample_submission = pd.read_csv(os.path.join(PATH, 'sample_submission.csv'))\n\nprint(f\"Number of training samples: {len(os.listdir(os.path.join(PATH, 'Cover')))} images\")\nprint(f\"Number of samples to predict: {sample_submission.shape[0]} images\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Lets take a look at what the sample submission look like\n* Note here that the term \"Label\" can be misleading because for each image, we want to predict a score and not a Label!"},{"metadata":{"trusted":true},"cell_type":"code","source":"sample_submission.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## First Simple EDA"},{"metadata":{},"cell_type":"markdown","source":"The \"format\" of the competition is strange because we are asked to predict the probability that an image was modified using a steganography algorithm, but there is no clear label. We are provided 75k images and their respective transformation with 4 different algorithms, but we don't know which algorithm may have been applied to each image in the test set. "},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data Samples\nn_rows = 3\nsamples = np.random.randint(0, N_IMAGES, size=n_rows ** 2)\nf, ax = plt.subplots(n_rows, n_rows, figsize=(14, 10))\n\nfor j, sample_id in enumerate(samples):\n    img = mpimg.imread(os.path.join(PATH, 'Cover', IMAGE_IDS[sample_id]))\n    ax[j // n_rows, j % n_rows].imshow(img)\n\nplt.tight_layout()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Quite random pictures, isn't it?\nSeems like all photos are 512 x 512 x 3 : let's check that"},{"metadata":{},"cell_type":"markdown","source":"It would be a bit long to check that all 75k images are the same size, so we randomly sample 1000 of them. (If you know a faster way to load .jpg images, please ping me!)"},{"metadata":{"trusted":true},"cell_type":"code","source":"for img_id in tqdm(random.choices(IMAGE_IDS, k=1000)):\n    img = mpimg.imread(os.path.join(PATH, 'Cover', img_id))\n    assert img.shape == (512, 512, 3)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"So we're now know that all images are **512 x 512 x 3**."},{"metadata":{},"cell_type":"markdown","source":"## Inspect images from crypted folders"},{"metadata":{},"cell_type":"markdown","source":"Someting natural now is to look at a picture in its different versions, e.g. with and without the different algorithms applied."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data Samples\nn_rows = 3\nsamples = np.random.randint(0, N_IMAGES, size=n_rows)\nsamples_r = np.repeat(samples, repeats=n_rows)\nf, ax = plt.subplots(n_rows, n_rows, figsize=(14, 10))\n\nfor j, sample_id in enumerate(samples_r):\n    for algo in ['Cover'] + ALGORITHMS:\n        img = mpimg.imread(os.path.join(PATH, algo, IMAGE_IDS[sample_id]))\n        ax[j // n_rows, j % n_rows].imshow(img)\n\nplt.tight_layout()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Seems like we don't see much difference. But that makes sense, since encrypted information should not be distinguishable just by seing the picture. We will know take a look at the difference in pixels. "},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data Samples\nn_rows = 3\nsamples = np.random.randint(0, N_IMAGES, size=n_rows)\n\nimg = defaultdict(dict)\nfor sample_id in samples:\n    for algo in ['Cover'] + ALGORITHMS:\n        img[algo][sample_id] = mpimg.imread(os.path.join(PATH, algo, IMAGE_IDS[sample_id]))\n\nf, ax = plt.subplots(n_rows, n_rows, figsize=(14, 10))\n        \nimg_diff = defaultdict(dict)\nfor j, sample_id in enumerate(samples):\n    for k, algo in enumerate(ALGORITHMS):\n        diff_im = (img['Cover'][sample_id] - img[algo][sample_id]) % 255\n        \n        ax[(n_rows * j + k) // n_rows, (n_rows * j + k) % n_rows].imshow(diff_im)\n        ax[(n_rows * j + k) // n_rows, (n_rows * j + k) % n_rows].set_title(f'Mean Pixel difference = {diff_im.mean() :.2f}')\n        \n        img_diff[algo][sample_id] = diff_im\n        ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Again, we don't see many any message... That's because the hidden message is stored in the least significant bytes!"},{"metadata":{},"cell_type":"markdown","source":"## Analysing Least-Significant Bytes (LSBs)"},{"metadata":{},"cell_type":"markdown","source":"> [Credits](https://github.com/ragibson/Steganography)"},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nfrom time import time\nfrom PIL import Image\n\n\ndef lsb_filter(path, n=2):\n    image = Image.open(path)\n\n    mask = (1 << n) - 1  # sets first bytes to 0\n\n    color_data = [(255 * ((channel[0] & mask) + (channel[1] & mask) + (channel[2] & mask)) // (3 * mask),) * 3 for channel in image.getdata()]\n\n    image.putdata(color_data)\n    \n    return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data Samples\nn_rows = 4\nsamples = np.random.randint(0, N_IMAGES, size=n_rows)\nf, ax = plt.subplots(n_rows, n_rows, figsize=(14, 10))\n\nlsb_images = defaultdict(dict)\n\nfor j, sample_id in enumerate(samples):\n    for k, algo in enumerate(['Cover'] + ALGORITHMS):\n        img = lsb_filter(os.path.join(PATH, algo, IMAGE_IDS[sample_id]))\n        \n        lsb_images[algo][sample_id] = img\n        ax[(n_rows * j + k) // n_rows, (n_rows * j + k) % n_rows].imshow(img)\n        ax[(n_rows * j + k) // n_rows, (n_rows * j + k) % n_rows].set_title(algo)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Data Samples\nn_rows = 4\n#samples = np.random.randint(0, N_IMAGES, size=n_rows)\nf, ax = plt.subplots(n_rows, n_rows - 1, figsize=(14, 10))\n        \n#img_diff = defaultdict(dict)\nfor j, sample_id in enumerate(samples):\n    for k, algo in enumerate(ALGORITHMS):\n        diff_im = (np.asarray(lsb_images['Cover'][sample_id]) - np.asarray(lsb_images[algo][sample_id])) % 255\n        \n        ax[(n_rows * j + k) // n_rows, (n_rows * j + k) % n_rows].imshow(diff_im)\n        ax[(n_rows * j + k) // n_rows, (n_rows * j + k) % n_rows].set_title(f'Mean Pixel difference = {diff_im.mean() :.2f}')\n        \n        img_diff[algo][sample_id] = diff_im\n        ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Even on the LSBs we don't see significant differences. Let's continue digging!"},{"metadata":{},"cell_type":"markdown","source":"### I will update this notebook continuously so stay tuned!"},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}