{"cells":[{"metadata":{},"cell_type":"markdown","source":"In this kernel I'll explore the changes in the DCT tables of the Cover and modified images - how big they are in terms of how many bits have been changed compared to the cover image. The number of the changed bits doesn't nescessary equals to payload size, because sometimes you don't have to change the coefficient at all when you encoding the bit. But I'd expect it to be somewhat proportional\n\nSome key findings:\n1. The payloads can vary quite significantly in size\n2. JMiPOD pictures have generally mode bits altered\n3. Higher quality images have mode bits altered\n4. For some images there are no changes at all.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"#install jpegio\n! git clone https://github.com/dwgoon/jpegio\n# Once downloaded install the package\n!pip install jpegio/.\nimport jpegio as jio","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nfrom tqdm.notebook import tqdm\n\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"WORKDIR = \"/kaggle/input/alaska2-image-steganalysis/\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"subdirs = [\"Cover\", \"JUNIWARD\", \"JMiPOD\", \"UERD\"]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def getqf(image):\n    qt0 =  image.quant_tables[0][0, 0]\n    dic = {2:95, 3:90, 8:75}\n    return dic[qt0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"qfs = []\nmethods = []\nbits = []\nfnames_ser = []\nfnames = sorted(os.listdir(os.path.join(WORKDIR, subdirs[0])))\nfor fn in tqdm(fnames):\n    cover_dct = None\n    for method in subdirs:\n        fulldir = os.path.join(WORKDIR, method)\n        im = jio.read(os.path.join(fulldir, fn))\n        qf = getqf(im)\n        dct = im.coef_arrays\n        if cover_dct is None:\n            cover_dct = np.array(dct).copy()\n            nbits = 0\n        else:\n            nbits = np.count_nonzero(np.array(dct) != np.array(cover_dct))\n        qfs.append(qf)\n        bits.append(nbits)\n        methods.append(method)\n        fnames_ser.append(fn)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats = pd.DataFrame({\"file\":fnames_ser, \"method\": methods, \"quality\": qfs, \"nbits\": bits})\ndf_stats.head(10)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.to_csv(\"changed_bits.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.groupby([\"method\", \"quality\"]).nbits.agg(['mean', 'median', 'min', 'max'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.groupby(\"method\").nbits.mean().plot.bar()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.groupby(\"method\").nbits.median().plot.bar()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.groupby(\"quality\").nbits.mean().plot.bar()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.groupby(\"quality\").nbits.median().plot.bar()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.query(\"method=='JUNIWARD' and quality == 75\").nbits.hist()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.query(\"method=='JUNIWARD' and quality == 90\").nbits.hist()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.query(\"method=='JUNIWARD' and quality == 95\").nbits.hist()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Do we have any modified files that are actually not modified at all? Seems like we do.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"\ndf_unchanged = df_stats.query(\"method !='Cover' and nbits == 0\")\ndf_unchanged.shape, df_unchanged.head(20)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_stats.query(\"file=='02137.jpg'\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Is there really no difference? Yes.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"!diff --report-identical /kaggle/input/alaska2-image-steganalysis/Cover/02137.jpg /kaggle/input/alaska2-image-steganalysis/UERD/02137.jpg","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}