{"nbformat_minor": 1, "cells": [{"cell_type": "markdown", "metadata": {"_uuid": "e08b15bd08e492e287719e9df85f333d7a8095a9", "_cell_guid": "c8ca13a2-17fc-4bd8-9805-50ebc2d32d47"}, "source": ["A noise pattern intrinsic to a camera will be visible if we juxtapose spectral amplitudes of images taken by the camera after applying laplacian filters to separate noise and content of an image.  Here is an implementation of this idea."]}, {"source": ["import numpy as np, pandas as pd, matplotlib.pyplot as plt\n", "import glob, functools, tqdm, PIL\n", "from multiprocess import Pool\n", "\n", "def imread(fn):\n", "    return np.array(PIL.Image.open(fn))\n", "\n", "def imsize(fn):\n", "    im = PIL.Image.open(fn)\n", "    sz = im.size\n", "    im.close()\n", "    return sz\n", "\n", "def is_landscape(fn):\n", "    s = imsize(fn)\n", "    return s[0] > s[1]"], "cell_type": "code", "metadata": {"collapsed": true, "_uuid": "09d4fec2658fdc27d1cfe0697599b5c4dce481b3", "_cell_guid": "bfbadd01-60f3-4fbd-abf8-a4dee697886e"}, "outputs": [], "execution_count": 1}, {"source": ["train = pd.DataFrame({'path':glob.glob('../input/train/*/*')})\n", "train['modelname'] = train.path.map(lambda p:p.split('/')[-2])"], "cell_type": "code", "metadata": {"collapsed": true, "_uuid": "32879e1335ea1f5a43de058bdd5eff604cf851ef", "_cell_guid": "5c61325a-93e7-4742-b4ea-8867054be463"}, "outputs": [], "execution_count": 2}, {"source": ["import cv2\n", "\n", "def random_crop_fft(img, W):\n", "    nr, nc = img.shape[:2]\n", "    r1, c1 = np.random.randint(nr-W), np.random.randint(nc-W) \n", "    imgc = img[r1:r1+W, c1:c1+W, :]\n", "\n", "    img1 = imgc - cv2.GaussianBlur(imgc, (3,3), 0)\n", "    imgs1 = np.sum(img1, axis=2)\n", "    \n", "    sf = np.stack([\n", "         np.fft.fftshift(np.fft.fft2( imgs1 )),\n", "         np.fft.fftshift(np.fft.fft2( img1[:,:,0] - img1[:,:,1] )),\n", "         np.fft.fftshift(np.fft.fft2( img1[:,:,1] - img1[:,:,2] )),\n", "         np.fft.fftshift(np.fft.fft2( img1[:,:,2] - img1[:,:,0] )) ], axis=-1)\n", "    return np.abs(sf)\n", "    \n", "def imread_residual_fft(fn, W, navg):\n", "    #print(fn, rss())\n", "    img = imread(fn).astype(np.float32) / 255.0\n", "    return sum(map(lambda x:random_crop_fft(img, W), range(navg))) / navg\n", "\n", "def noise_pattern(modelname, W, navg=256):\n", "    files = train.path[train.modelname == modelname].values\n", "    orientations = np.vectorize(is_landscape)(files)\n", "    if np.sum(orientations) < len(orientations)//2:\n", "        orientations = ~orientations\n", "    files = files[orientations]\n", "\n", "    from multiprocess import Pool\n", "    with Pool() as pool:\n", "        s = sum(tqdm.tqdm(pool.imap(lambda fn:imread_residual_fft(fn, W, navg), files), total=len(files), desc=modelname)) / len(files)\n", "    \n", "    return s"], "cell_type": "code", "metadata": {"collapsed": true, "_uuid": "f16b6779065191e4b64f7048a59621e85d693d16", "_cell_guid": "957c9074-89e4-47a8-856b-6f52497ceb90"}, "outputs": [], "execution_count": 3}, {"source": ["def plot_model_features(modelname, W):\n", "    s = noise_pattern(modelname, W)\n", "    nchans = s.shape[2]\n", "    nrows = (nchans + 3) // 4\n", "    _, ax = plt.subplots(nrows, 4, figsize=(16, 4 * nrows))\n", "    ax = ax.flatten()\n", "\n", "    for c in range(nchans):\n", "        eps = np.max(s[:,:,c]) * 1e-2\n", "        s1 = np.log(s[:,:,c] + eps) - np.log(eps) \n", "        img = (s1 * 255 / np.max(s1)).astype(np.uint8)\n", "        ax[c].imshow(cv2.equalizeHist(img))\n", "        \n", "    for ax1 in ax[nchans:]:\n", "        ax1.axis('off')\n", "\n", "    plt.show()\n", "    \n", "def plot_all_model_features(W):\n", "    print(\"Feature Size={}\".format(W))\n", "    for modelname in train.modelname.unique():\n", "        plot_model_features(modelname, W)"], "cell_type": "code", "metadata": {"_uuid": "bc3bb2ffaf7dfd541131a13f14047901115fe526", "_cell_guid": "ee017d29-5a39-4c49-861b-e86afed20cf4"}, "outputs": [], "execution_count": 4}, {"source": ["plot_all_model_features(W=128)"], "cell_type": "code", "metadata": {"_uuid": "cf5397ee317e64e8756845b629e4cbe156e4af83", "_cell_guid": "54ef58dc-7ebb-4d43-a35d-9fc3cc16a909"}, "outputs": [], "execution_count": 5}, {"source": [], "cell_type": "code", "metadata": {"collapsed": true, "_uuid": "df8cb0393076b4f650305f883fe723333eaacf32", "_cell_guid": "e1f912a2-9e32-41b3-b4f8-5389867d6342"}, "outputs": [], "execution_count": null}, {"source": [], "cell_type": "code", "metadata": {"collapsed": true, "_uuid": "59ed6af3c29f9065a5f85cb88288367dcc967774", "_cell_guid": "58ab39a5-bdd0-4867-aef2-47a7bbe84b3f"}, "outputs": [], "execution_count": null}], "metadata": {"language_info": {"file_extension": ".py", "mimetype": "text/x-python", "version": "3.6.4", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "codemirror_mode": {"version": 3, "name": "ipython"}, "name": "python"}, "kernelspec": {"display_name": "Python 3", "name": "python3", "language": "python"}}, "nbformat": 4}