{"cells":[{"metadata":{},"cell_type":"markdown","source":"This notebook kernel attemps to do basic analysis of images in the dataset and uncover evidence of tampering using steganography algorithms. It is targeted at anyone just starting out with this competition. You need no prior knowledge of steganography to understand this kernel\n\n\n# Basics\nFirst up, let us understand the basics of [steganogarphy](https://www.wired.com/story/steganography-hacker-lexicon/). \n* ***Definition:*** Steganography is essentially the practice of hiding secret messages in an otherwise non-secret medium. It has existed for centuries in different forms.\n* ***Current:*** Modern day steganography (including this competition) deals with encoding secret information in digital images. How is this done:\n    * The pixel values are altered to store hidden messages (henceforth called the payload).\n    * However, the altered & original image does **not** show any visual difference to the naked eye. \n* ***Techniques:*** Now, different algorithms can be used to encode the payload (i.e., change the pixel values). This competition uses 3 algorithms:\n    1. JMiPOD\n    2. JUNIWARD\n    3. UERD\n* ***Data:*** We are given a dataset of 75k images with each image in its original unaltered state(cover) and modified by each of the 3 algorithms respectively.\n    \n* ***Objective:*** The goal of this competition  is to come up with a machine learning model to predict whether a given test image has a payload encoded or not. We are not concerend with recovering the contents of the payload.\n  * Essentially, we are dealing with one aspect of steganalysis which is the study of detecting and possibly recovering messages hidden using steganography."},{"metadata":{},"cell_type":"markdown","source":"# Load Libraries"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport cv2\nimport os\nimport matplotlib\n# matplotlib.use('nbagg')\nimport matplotlib.pyplot as plt\n%matplotlib inline\n# %matplotlib inline\n# import mpld3\n# mpld3.enable_notebook()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Display samples\nLet us load & display some random sample images from the dataset"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"base_path = '/kaggle/input/alaska2-image-steganalysis/'\nalgorithm = ('Cover(Unaltered)', 'JMiPOD', 'UERD', 'JUNIWARD')\nfig, axes = plt.subplots(nrows=4, ncols=4, figsize = (11,11) )\nnp.random.seed(55)\nfor i,id in enumerate(np.random.randint(0,75001,4)):\n    id = '{:05d}'.format(id)    \n    cover_path = os.path.join(base_path, 'Cover', id + '.jpg')\n    jmipod_path = os.path.join(base_path, 'JMiPOD', id + '.jpg')\n    uerd_path = os.path.join(base_path, 'UERD', id + '.jpg')\n    juniward_path = os.path.join(base_path, 'JUNIWARD', id + '.jpg')\n    cover_img = plt.imread(cover_path)\n    jmipod_img = plt.imread(jmipod_path)\n    uerd_img = plt.imread(uerd_path)\n    juniward_img = plt.imread(juniward_path)\n    axes[i,0].imshow(cover_img)\n    axes[i,1].imshow(jmipod_img)\n    axes[i,2].imshow(uerd_img)\n    axes[i,3].imshow(juniward_img)\n    axes[i,0].set(ylabel=id+'.jpg')\n\nfor i,algo in enumerate(algorithm):\n    axes[0,i].set(title=algo) \nfor ax in axes.flat:\n    ax.set(xticks=[], yticks=[])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* As you can see above, we displayed 5 random images.\n* By mere visual observation, there is no sign of any modification to the image.\n* They look completely visually similar. \n* Lets look deeper"},{"metadata":{},"cell_type":"markdown","source":"# Histogram analysis\nLet us plot the histogram of the images and look to comapare the images. We will pick one of the samples and plot its histogram for all 3 channels and all 3 algorithms."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"cover_hist = {}\njmipod_hist = {}\nuerd_hist = {}\njuniward_hist = {}\ncolor = ('b','g','r')\nfor i,col in enumerate(color):\n    cover_hist[col] = cv2.calcHist([cover_img],[i],None,[256],[0,256])\n    jmipod_hist[col] = cv2.calcHist([jmipod_img],[i],None,[256],[0,256])\n    uerd_hist[col] = cv2.calcHist([uerd_img],[i],None,[256],[0,256])\n    juniward_hist[col] = cv2.calcHist([juniward_img],[i],None,[256],[0,256])\n    \nfig_hist, axes_hist = plt.subplots(nrows=2, ncols=2, figsize=(12,12))\nfor ax, hist, algo in zip(axes_hist.flat, [cover_hist, jmipod_hist, uerd_hist, juniward_hist], algorithm):\n    ax.plot(hist['r'], color = 'r', label='r')\n    ax.plot(hist['g'], color = 'g', label='g')\n    ax.plot(hist['b'], color = 'b', label='b')\n    ax.set(ylabel='# of pixels', xlabel='Pixel value(0-255)', title=algo)\n    ax.legend()\nfig_hist.subplots_adjust(wspace=0.4, hspace=0.3)\nfig_hist.suptitle('Histogram of a sample (' + id + '.jpg)', fontsize=20)\n    #     ax.xlim([0,256])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* Looking at the histogram gives us more clues.\n* Indeed, all 4 histograms are quite similar. \n  * Yet, if we look closely, we observe some minor differences.\n  * If you open the notebook in editing mode and run it, you can zoom and check for yourself.\n\nLet us zoom in and plot the red channel of all 4 images in a single plot for pixel values between 50-80."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(10,10))\nax.plot(cover_hist['r'][50:80], color = 'c', label=algorithm[0])\nax.plot(jmipod_hist['r'][50:80], color = 'm', label=algorithm[1])\nax.plot(uerd_hist['r'][50:80], color = 'y', label=algorithm[2])\nax.plot(juniward_hist['r'][50:80], color = 'g', label=algorithm[3])\nax.legend()\nax.set_ylabel('# of pixels', fontsize=15) \nax.set_xlabel('Pixel value(50-80)', fontsize=15)\nax.xaxis.set(ticklabels=np.linspace(50,80,8, dtype=np.int))\nax.set_title('R-channel Histogram Compared (zoomed in)', fontsize=20)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* The difference was subtle before.\n* But this histogram shows the images are clearly modified with minor changes using the steganography algorithms.\n* But they are modified in a manner not visible to the naked eye."},{"metadata":{},"cell_type":"markdown","source":"# Difference image\nLet us print & look at some pixel values directly now."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print('Cover image:\\n', cover_img[10:20,10:20,0])\nprint('\\nJMiPOD image:\\n', jmipod_img[10:20,10:20,0])\nprint('\\nUERD image:\\n', uerd_img[10:20,10:20,0])\nprint('\\nJUNIWARD image:\\n', juniward_img[10:20,10:20,0])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Above, you can clearly see similar but slightly modified pixel values. \n\nAn easy way to observe the dissimilarities is to display the absolute value difference of altered and cover image."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(nrows=4, ncols=4, figsize = (11,11) )\nnp.random.seed(55)\ndef disp_diff_img(alt, ref, ax, chnl=0):\n    diff = np.abs(alt.astype(np.int)-ref.astype(np.int)).astype(np.uint8)\n    ax.imshow(diff[:,:,chnl], vmin=0, vmax=np.amax(diff[:,:,chnl]), cmap='hot')\nfor i,id in enumerate(np.random.randint(0,75001,4)):\n    id = '{:05d}'.format(id)    \n    cover_path = os.path.join(base_path, 'Cover', id + '.jpg')\n    jmipod_path = os.path.join(base_path, 'JMiPOD', id + '.jpg')\n    uerd_path = os.path.join(base_path, 'UERD', id + '.jpg')\n    juniward_path = os.path.join(base_path, 'JUNIWARD', id + '.jpg')\n    cover_img = plt.imread(cover_path)\n    jmipod_img = plt.imread(jmipod_path)\n    uerd_img = plt.imread(uerd_path)\n    juniward_img = plt.imread(juniward_path)\n    axes[i,0].imshow(cover_img)\n    disp_diff_img(jmipod_img, cover_img, axes[i,1], 0)\n    disp_diff_img(uerd_img, cover_img, axes[i,2], 0)\n    disp_diff_img(juniward_img, cover_img, axes[i,3], 0)\n    axes[i,0].set(ylabel=id+'.jpg')\n\nfor i,algo in enumerate(algorithm):\n    axes[0,i].set(title=algo + 'diff') \nfor ax in axes.flat:\n    ax.set(xticks=[], yticks=[])\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* We took absolute of (original - altered image) and plot it using the 'hot' colorspace.\n* This basic analysis clearly shows evidence of tampering with the pixel values.\n* Also, its possible to see how all 3 algorithms has modified the original image in very different ways.\n* The exact technique of each algorithm needs to be known to understand why each image has been modified so differently.\n\n\nI plan to update it further with more analysis on the algorithms in the future.\n\nPlease upvote if you have enjoyed reading the kernel :)"}],"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}