{"cells":[{"metadata":{},"cell_type":"markdown","source":"# About this Competition\n\n\"Wait!What? Another one , No man this can't be happening\", Calm down there Pal! , it's just the quarantine , It's messing with Kaggle.\nKaggle is going wild with competitons releasing 4th competiton this week, but we need to support Kaggle as good community members right? I mean these are tough times 😛 .\nThis is an interesting competition though,I mean Stegnography, now we are talking, it's time to give all those suppressed spy fantasies we have had for a long time , some air  😍 and dive right in .\n\nThis competition wants us to create an efficient and reliable method to detect secret data hidden within innocuous-seeming digital images."},{"metadata":{},"cell_type":"markdown","source":"# About this Notebook\n\nWhat most people might think : This competition is not for me ,I'm having zero understanding and knowledge of stegnography and stegnanalysis, but we are not like them , are we? <br>\nWell I leave it for you to decide , as for me I am not , I am eager to learn,explore and most of all get the feel of an NSA agent\nThus I will dive right in and in this notebook I will build on the concept from zero to publishing a solution. We can do this together if you want to , just come along,because all it requires is willingness to learn.<br><br>\n\nWe will see the following in this notebook :-\n\n* What's Steganagraphy and Steganalysis? : Basic Definitions\n* Knaive approach for Steganalysis\n* Had we been doing it all wrong?\n* Steganography : Complete Understandind of what it is and why we were doing it all wrong?\n* New and Improved approach of solution\n\nBy the end , You will have all the understanding of the data and the competition and would be well equipped to do well in this competition, just stay patient\n\nThis notebook is a work in progress , if this notebook receives support , I will keep on updating it with new and latest developments which I make in the this competition\n\n**<span style=\"color: red;\">If you like my effort and contribution please show token of gratitude by upvoting my kernel</span>**"},{"metadata":{},"cell_type":"markdown","source":"# PRELIMINARIES"},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!pip install stegano","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# PRELIMINARIES\nimport os\nfrom stegano import lsb #USED FOR PNG IMAGE\nimport skimage.io as sk\nimport matplotlib.pyplot as plt\nfrom scipy import spatial\nfrom tqdm import tqdm\n\nfrom PIL import Image\nfrom random import shuffle\n\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Q1) What is Steganography?\n\nLet's dive in by understanding our first point\n\nWell in pure english it has the following meaning : the practice of concealing messages or information within other non-secret text or data.\n\nHere is how one research paper describes it : \"Steganography is usually referred to as the techniques and methods that allow to hide information within an innocuous-like cover object. The resulting stego-object resembles, as much as possible, the original cover object. Therefore it can be sent over an unsecured communication channel that may be subject to wiretapping by an eavesdropper\"\n\nIn context to this competition it mean hiding information in the digital image.The same technology employed for digital watermarking and for digital markings/copyrights .But it's not limited to it , anyone can use it hide information , to bypass securities , contact specific person, send malicious content ,etc .Now you see right , why I was talking about all the spy work.\n\nNowadays, 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\n\n## Difference B/w steganalysis and steganography\n\nSteganography is the encoding of information on a viable object source and steganography is the decoding of information from the encoded object."},{"metadata":{},"cell_type":"markdown","source":"## Fun Exercise\nBefore we begin with the original dataset and the problem of steganoanalysis, let's first try to perform steganography just for fun, using a python module called stegano and after doing steganography, we will understand how it was done.\n\nMore can be read here from docs [here](https://stegano.readthedocs.io/en/latest/module/)"},{"metadata":{},"cell_type":"markdown","source":"I have added few png images , you can add yours and play around"},{"metadata":{"trusted":true},"cell_type":"code","source":"image = sk.imread(\"/kaggle/input/png-for-steg/bald-eagle-png-transparent-image-pngpix-7.png\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"secret = lsb.hide(\"/kaggle/input/png-for-steg/bald-eagle-png-transparent-image-pngpix-7.png\", \"I will be there but you can't find me even if I'm a very very very long sentence\")\nsecret.save(\"encoded.png\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We just hid a message in our image with two lines of code , Cool huh? <br>\nLet's try looking at the image to observe if there is any difference"},{"metadata":{"trusted":true},"cell_type":"code","source":"img1 = sk.imread(\"/kaggle/input/png-for-steg/bald-eagle-png-transparent-image-pngpix-7.png\")\nimg2 = sk.imread(\"/kaggle/working/encoded.png\")\n\nfig,ax = plt.subplots(1,2,figsize=(18,8))\n    \nax[0].imshow(img1)\nax[1].imshow(img2)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Surpised? Can't tell the difference can we? This is exceptional , Now we why it is so difficult**  <br>\nSteagano also provides a function for decoding the message hidden in our image , Let's Try that"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(lsb.reveal(\"/kaggle/working/encoded.png\"))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### So we can see the text that we hid, But What's going on under the hood?\n\nThe module uses a technique that create a covert channel in the parts of the cover image in which changes are likely to be a bit scant when compared to the human visual system (HVS).It hides the information in the least significant bit (LSB) of the image data . This embedding method is basically based on the fact that the least significant bits in an image can be thought of as random noise, and consequently they become not responsive to any changes on the image <br>\n\nThis is one of the first and classical ways of doing stegnagraphy on images .The well-known steganographic tools based on LSB embedding are different as far as the way they hide information is concerned. Some of them change the LSB of pixels randomly, others modify pixels not in the whole image but in selected areas of it, and still others increase or decrease the pixel value of the LSB, rather than change the value"},{"metadata":{},"cell_type":"markdown","source":"# Knaive Approach\n\nSo now that we understand how stegano works and how one of the first and oldest way of stegnanalyis work , we can use a knaive approach for detecting whether a message has been encoded in a image file or not. The following presents a knaive approach :\n\n\n* An RGB image is stored in a form of a 3-D numpy array where each dimensions contains pixels of Red,Green and blue colors.The values in this vary between 0 and 255. We can flatten the numpy array into a vaactor of rdim x gdim x bdim . This will get us a feature vector for an img\n* Now we can find the cosine similarity between the two vectors of our normal image and encoded image, if they are alike then cosine dissimilarity(1-similarity) must be one else it will be less than 1 and we can get an idea that's something is wrong\n\nLet's do this exercise and see the results"},{"metadata":{"trusted":true},"cell_type":"code","source":"vec1 = np.reshape(img1,(1151*1665*4))\nvec2 = np.reshape(img2,(1151*1665*4))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's first verify our argument that cosine similarity is 1 for exact same vectors"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(1 - spatial.distance.cosine(vec1,vec1))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(1 - spatial.distance.cosine(vec1,vec2)) #Cosine Similarity","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(spatial.distance.cosine(vec2,vec1)) #Cosine Dissimilarity","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* So we can see that we can find whether an information is hidden or not using a naive approach without much efforts .\n* **I  tried to use this knaive with the main dataset , but was made aware by the organizers that this knaive approach is not wrong but not exactly right because the information is hidden in the DCT coefficients of the image rather than the RGB pixels** \n\n* Don't worry if all of this does not make sense, we will look into everything , but first look at some visualizations"},{"metadata":{},"cell_type":"markdown","source":"# Data Exploration\n\nNow that we are familiar to the techniques of steganograhy , we can now move to exploration of data and steganalysis part"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"BASE_PATH = \"/kaggle/input/alaska2-image-steganalysis\"\ntrain_imageids = pd.Series(os.listdir(BASE_PATH + '/Cover')).sort_values(ascending=True).reset_index(drop=True)\ntest_imageids = pd.Series(os.listdir(BASE_PATH + '/Test')).sort_values(ascending=True).reset_index(drop=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"cover_images_path = pd.Series(BASE_PATH + '/Cover/' + train_imageids ).sort_values(ascending=True)\nJMIPOD_images_path = pd.Series(BASE_PATH + '/JMiPOD/'+train_imageids).sort_values(ascending=True)\nJUNIWARD_images_path = pd.Series(BASE_PATH + '/JUNIWARD/'+train_imageids).sort_values(ascending=True)\nUERD_images_path = pd.Series(BASE_PATH + '/UERD/'+train_imageids).sort_values(ascending=True)\ntest_images_path = pd.Series(BASE_PATH + '/Test/'+test_imageids).sort_values(ascending=True)\nss = pd.read_csv(f'{BASE_PATH}/sample_submission.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"So the organizers have used four algorithms for encoding data into the cover images, they are [JUNIWARD , JMiPOD, UERD] \n* UNIWARD -  Universal Wavelet Relative Distortion<br>\nPaper describing CNN for stegnalysis of [JUNIWARD images](https://arxiv.org/ftp/arxiv/papers/1704/1704.08378.pdf)\n \n* JMiPOD -\n Paper describing CNN for stegnalysis of [JMiPOD images](https://hal-utt.archives-ouvertes.fr/hal-02542075/file/J_MiPOD_vPub.pdf)\n \n* UERD - Uniform Embedding Revisited Distortion\n<br> No resources found yet , I will update as soon as I find something\n\nFor now just assume they are certain algorithms that are used for encoding data into image we will understand everything in a bit"},{"metadata":{"trusted":true},"cell_type":"code","source":"#VISUALIZING SOME IMAGES FROM COVER SECTION\nfig, ax = plt.subplots(nrows=2, ncols=2, figsize=(30, 15))\nk=0\nfor i, row in enumerate(ax):\n    for j, col in enumerate(row):\n        img = sk.imread(cover_images_path[k])\n        col.imshow(img)\n        col.set_title(cover_images_path[k])\n        k=k+1\nplt.suptitle('Samples from Cover Images', fontsize=14)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Visualizing Cover and Encoded side by side\n\nLet's visualize cover image and encoded images side by side"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(nrows=3, ncols=4, figsize=(30, 15))\nfor i in range(3):\n    '''\n    If you want to print more images just change the values in range and ncols in subplot\n    \n    '''\n    cvimg = sk.imread(cover_images_path[i])\n    uniimg = sk.imread(JUNIWARD_images_path[i])\n    jpodimg = sk.imread(JMIPOD_images_path[i])\n    uerdimg = sk.imread(UERD_images_path[i])\n    \n    ax[i,0].imshow(cvimg)\n    ax[i,0].set_title('Cover_IMG'+train_imageids[i])\n    ax[i,1].imshow(uniimg)\n    ax[i,1].set_title('JNIWARD_IMG'+train_imageids[i])\n    ax[i,2].imshow(jpodimg)\n    ax[i,2].set_title('JMiPOD_IMG'+train_imageids[i])\n    ax[i,3].imshow(uerdimg)\n    ax[i,3].set_title('UERD_IMG'+train_imageids[i])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**As expected we can't see any difference with naked eye ,We know that information is hidden in somewhere using distortion function**<br>\n<br>\nI will try to do one final visualization of pixel deviation in the image channels and see if it works \nIdea taken shamelessly from: https://www.kaggle.com/iamleonie/alaska2-first-look-into-the-wild-data"},{"metadata":{"trusted":true},"cell_type":"code","source":"img_cover = sk.imread(cover_images_path[0])\nimg_jmipod = sk.imread(JMIPOD_images_path[0])\nimg_juniward = sk.imread(JUNIWARD_images_path[0])\nimg_uerd = sk.imread(UERD_images_path[0])\n\n\nfig, ax = plt.subplots(nrows=3, ncols=4, figsize=(16, 12))\nax[0,0].imshow(img_jmipod)\nax[0,1].imshow((img_cover == img_jmipod).astype(int)[:,:,0])\nax[0,1].set_title(f'{train_imageids[k]} Channel 0')\n\nax[0,2].imshow((img_cover == img_jmipod).astype(int)[:,:,1])\nax[0,2].set_title(f'{train_imageids[k]} Channel 1')\nax[0,3].imshow((img_cover == img_jmipod).astype(int)[:,:,2])\nax[0,3].set_title(f'{train_imageids[k]} Channel 2')\nax[0,0].set_ylabel('JMiPOD', rotation=90, size='large', fontsize=14)\n\n\nax[1,0].imshow(img_juniward)\nax[1,1].imshow((img_cover == img_juniward).astype(int)[:,:,0])\nax[1,2].imshow((img_cover == img_juniward).astype(int)[:,:,1])\nax[1,3].imshow((img_cover == img_juniward).astype(int)[:,:,2])\nax[1,0].set_ylabel('JUNIWARD', rotation=90, size='large', fontsize=14)\n\nax[2,0].imshow(img_uerd)\nax[2,1].imshow((img_cover == img_uerd).astype(int)[:,:,0])\nax[2,2].imshow((img_cover == img_uerd).astype(int)[:,:,1])\nax[2,3].imshow((img_cover == img_uerd).astype(int)[:,:,2])\nax[2,0].set_ylabel('UERD', rotation=90, size='large', fontsize=14)\n\nplt.suptitle('Pixel Deviation from Cover Image', fontsize=14)\n\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* So Again, on doing these visualizations , the organizers again made the point that comparing the pixel values of RGB channels of normal and stego images is not exactly right.\nhttps://www.kaggle.com/c/alaska2-image-steganalysis/discussion/147494 ---> Please read this before Continuing , don't worry if you don't understand it , we will dig deeper\n\nTo understand everything we will have to dive deeper into the underlying principles of JPEG and Stregnography"},{"metadata":{},"cell_type":"markdown","source":"# Had we been doing it all wrong?\n\n1)**We will understand everything step by step one at a time , so let's start with what is 'JPEG'?**\n\nSo JPEG is not a file format , it's a compressing algorithm for an image file to reduce it's size , without losing a lot of information. So let's look at the steps invloved in compressing an image using JPEG image :\n\n![image.png](attachment:image.png)","attachments":{"image.png":{"image/png":"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"}}},{"metadata":{},"cell_type":"markdown","source":"* Firstly the image is converted into YCbCr from RGB channels. YCbCr and RGB are both colorspaces having different channels where YCbCr consists three channels as Luminance(Y) , Cb(Cb is blue minus luma (B-Y)) ,  Cr(Cr is red minus luma (R-Y)). To know more about color space please watch this [video][1]\n[1]: https://www.youtube.com/watch?v=LFXN9PiOGtY&list=PLzH6n4zXuckoAod3z31QEST1ZaizBuNHh&index=1\n\n* Then DCT is applied on the pixels of these channels , using DCT coeff , To understand DCT please watch this [video][2]\n[2]: https://www.youtube.com/watch?v=Q2aEzeMDHMA&list=PLzH6n4zXuckoAod3z31QEST1ZaizBuNHh&index=3\n\n* The image encoded using JPEG algorithm stays in YCbCr colorspace untill it is decoded by an Image viewer software. When a JPEG is read it is decoded and converted back to RGB colorspace to be rendered on screen using the techniques described in the video above"},{"metadata":{},"cell_type":"markdown","source":"# Stenography : Complete Understanding\n\nNow that we know  how JPEG works , let's understand how stegnography works and how techniques used have evolved over the years\n\nSo what do you think can be the potential hiding spots of information in a JPEG File ?\n* Channels of the respective color Spaces like RGB and YCbCr.<br>\n  These were the techniques used in the starting algorithms but as we have seen it is easily recognizeable even using knaive approach , so people started to find a new technique for hiding information \n<br>\n* DCT coeffs of different channels<br>\n  The more newer approaches hides information in the DCT coeffs of different channels of JPEG image and that too the payload(secret data) is randomly distributed among them taking into the statistics of DCT coeffs\n\nAfter watching this [video](https://www.youtube.com/watch?v=TWEXCYQKyDc&t=342s) you will have a clear understanding of everything that has been discussed uptill and now  "},{"metadata":{},"cell_type":"markdown","source":"# What happened to the Knaive approach\nI have made this notebook new again from scratch , as for my knaive approach to this problem i.e approaching this as regreesion problem based on Pixel Difference, I have added it here if you want to know about it here: https://www.kaggle.com/tanulsingh077/creating-labels-for-steganalysis/\n\n# New and Improved Approach\nSo now we have got to approache for model building :-\n* Use pixels as input of our neural nets as we were doing uptill now and we can also use naive method\n                                             or\n* Use DCT coeff's instead of pixel values as an input to our neural networks for prediction of labels\n\n\nIn this section we will and try and determine what would be a good idea (What you think about it, let's discuss in the comments)"},{"metadata":{},"cell_type":"markdown","source":"### Let'se First go and see how we can visualize different Channels of a YCbCr colorspace Image"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig,ax = plt.subplots(3,4,figsize=(20,12))\n\nfor i,paths in enumerate(cover_images_path[:3]):\n    image = Image.open(paths)\n    ycbcr = image.convert('YCbCr')\n    (y, cb, cr) = ycbcr.split()\n\n    ax[i,0].imshow(image)\n    ax[i,0].set_title('Cover'+train_imageids[i])\n    ax[i,1].imshow(y)\n    ax[i,1].set_title('Luminance')\n    ax[i,2].imshow(cb)\n    ax[i,2].set_title('Cb:Chroma Blue')\n    ax[i,3].imshow(cr)\n    ax[i,3].set_title('Cr:Chroma Red')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Now let's explore the Different channels of YCbCr of Cover and Encoded Image side by side"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig,ax = plt.subplots(4,4,figsize=(20,16))\nplt.tight_layout()\n\n\nim1 = Image.open(cover_images_path[0])\nim2 = Image.open(JUNIWARD_images_path[0])\nim3 = Image.open(JMIPOD_images_path[0])\nim4 = Image.open(UERD_images_path[0])\n\nfor i,image in enumerate([im1,im2,im3,im4]):\n    ycbcr = image.convert('YCbCr')\n    (y, cb, cr) = ycbcr.split()\n\n    ax[i,0].imshow(image)\n    ax[i,0].set_title('Image')\n    ax[i,1].imshow(y)\n    ax[i,1].set_title('Luminance')\n    ax[i,2].imshow(cb)\n    ax[i,2].set_title('Cb:Chroma Blue')\n    ax[i,3].imshow(cr)\n    ax[i,3].set_title('Cr:Chroma Red')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"After covering all the videos in the above sections , we know how an Image is compressed using JPEG and how DCT coeffs are calculated using Quantization and Encoding\n\nNow we can go deeper and analyze for which model would be better . We will start this by getting the DCT coeffs  of images and visualizing them"},{"metadata":{"trusted":true},"cell_type":"code","source":"! git clone https://github.com/dwgoon/jpegio","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install jpegio/.\nimport jpegio as jio","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Usage and review of the Library can be viewed [here](https://github.com/dwgoon/jpegio)\n* The attribute coeff_arrays gives the DCT coeffs of all the three channels of YCbCr of the images , Let's go and plot DCT coeffs of Cover and Encoded Image and try to see if there is any difference"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig,ax = plt.subplots(4,4,figsize=(20,16))\nplt.tight_layout()\n\nfor i,path in enumerate([cover_images_path[0],JUNIWARD_images_path[0],JMIPOD_images_path[0],UERD_images_path[0]]):\n    \n    image = Image.open(path)\n    jpeg = jio.read(path)\n    DCT_Y = jpeg.coef_arrays[0]\n    DCT_Cr = jpeg.coef_arrays[1]\n    DCT_Cb = jpeg.coef_arrays[2]\n    \n    \n    ax[i,0].imshow(image)\n    ax[i,0].set_title('Image')\n    ax[i,1].imshow(DCT_Y)\n    ax[i,1].set_title('Luminance')\n    ax[i,2].imshow(DCT_Cb)\n    ax[i,2].set_title('Cb:Chroma Blue')\n    ax[i,3].imshow(DCT_Cr)\n    ax[i,3].set_title('Cr:Chroma Red')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Up untill now we were using difference in pixel values and normal pixel values for analysis and it has been argued that's it is not the right way \nLets now look at difference in DCT values and difference in pixels and see the differences, this will help us to make better  decision "},{"metadata":{"trusted":true},"cell_type":"code","source":"coverDCT = np.zeros([512,512,3])\nstegoDCT = np.zeros([512,512,3])\njpeg = jio.read(cover_images_path[0])\nstego_juni = jio.read(JUNIWARD_images_path[0])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"coverDCT[:,:,0] = jpeg.coef_arrays[0] ; coverDCT[:,:,1] = jpeg.coef_arrays[1] ; coverDCT[:,:,2] = jpeg.coef_arrays[2]\nstegoDCT[:,:,0] = stego_juni.coef_arrays[0] ; stegoDCT[:,:,1] = stego_juni.coef_arrays[1] ; stegoDCT[:,:,2] = stego_juni.coef_arrays[2]\n\nDCT_diff = coverDCT - stegoDCT\n# So since they are not the same Images the DCT_diff would not be zero\nprint(len(DCT_diff[np.where(DCT_diff!=0)]))\nprint(np.unique(DCT_diff))\nplt.figure(figsize=(16,10))\nplt.imshow( abs(DCT_diff) )\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"coverPixels = np.array(Image.open(cover_images_path[0])).astype('float')\nstegoPixels = np.array(Image.open(JUNIWARD_images_path[0])).astype('float')\n\npixelsDiff = coverPixels - stegoPixels\n\n# So since they are not the same Images the pixels_diff would not be zero\nprint(len(pixelsDiff[np.where(pixelsDiff!=0)]))\nprint(np.unique(pixelsDiff))\nplt.figure(figsize=(16,10))\nplt.imshow( abs(pixelsDiff) )\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"* From the images it is hard to conclude which one works better, as images of both DCT and Pixel pattern give us the same underlying pattern. But one can argue that pixel values contain a lot of noise whereas DCT values do not and can get you a better model\n\n* But from the number of non-zero difference values of both DCT and pixels we can see that Pixels have more non-zero values than DCT and hence are more distintive for differentiation between cover and stego images\n\n* Let's also look the images side by side to get a better unders"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig,ax = plt.subplots(1,2,figsize=(16,12))\nax[0].imshow(abs(DCT_diff))\nax[1].imshow(abs(pixelsDiff))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# What's Next\n\n* Building a baseline with pixel difference and my naive way as I have discussed in 8th version of my notebook \n* Building a baseline with pixel value only and with DCT values and seeing which works better\n* After deciding the approach using SOTA models for perfections\n* More insights that would help us\n\nIf you have read this kernel , do share your  thoughts on which would be a better way to model in the comments below\n\nSTAY 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}