{"cells": [{"cell_type": "markdown", "source": ["## Hi, everyone! \n", "## Here's a simple baseline based on PCA features extracted from images. There are many things you can to do on top of that, e.g. tuning number of principal components, varying size of training sample for PCA, splitting image by channels etc.\n", "#### Credits for the public starter to @inversion"], "metadata": {"_uuid": "0c7846c0f4c10f60bc0d611d508d723fd738fb1c", "_cell_guid": "e9c0dcf0-e896-42ec-bb6a-cd8280c9aa31"}}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "7dc1c3c83093d804a2f396b14cd73d7c9a1e21fe", "collapsed": true, "_cell_guid": "60ad1bf0-11fd-4d10-a38a-8abead246b90"}, "outputs": [], "source": ["# This Python 3 environment comes with many helpful analytics libraries installed\n", "# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n", "# For example, here's several helpful packages to load in \n", "\n", "import os\n", "from pathlib import Path\n", "import multiprocessing as mp\n", "\n", "import numpy as np # linear algebra\n", "import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n", "\n", "from skimage.data import imread\n", "from sklearn.ensemble import RandomForestClassifier\n", "import time\n", "import cv2\n", "from sklearn.decomposition import PCA\n", "from sklearn.svm import SVC\n", "from skimage.restoration import  estimate_sigma\n", "\n", "# Input data files are available in the \"../input/\" directory.\n", "# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n", "\n", "from subprocess import check_output\n", "print(check_output([\"ls\", \"../input\"]).decode(\"utf8\"))\n", "\n", "# Any results you write to the current directory are saved as output."]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "cb14a753d32dacec06c2b9767aa7e9f6befa1fb2", "collapsed": true, "_cell_guid": "ef13e211-e719-473c-abca-4a78837b7b1a"}, "outputs": [], "source": ["input_path = Path('../input')\n", "train_path = input_path / 'train'\n", "test_path = input_path / 'test'"]}, {"cell_type": "code", "execution_count": null, "metadata": {"scrolled": true, "_uuid": "14cf6f0df305ffa690e1b1068535791b01fbcd68", "collapsed": true, "_cell_guid": "36327e90-b9fd-4323-b860-c382a42f61d0"}, "outputs": [], "source": ["cameras = os.listdir(train_path)\n", "\n", "train_images = []\n", "for camera in cameras:\n", "    for fname in sorted(os.listdir(train_path / camera)):\n", "        train_images.append((camera, fname))\n", "\n", "train = pd.DataFrame(train_images, columns=['camera', 'fname'])\n", "print(train.shape)"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "f1eb110d8b012cdb110a61ba17ca0f23f3502d86", "collapsed": true, "_cell_guid": "5293c67a-6040-4125-8362-39bdf153914e"}, "outputs": [], "source": ["test_images = []\n", "for fname in sorted(os.listdir(test_path)):\n", "    test_images.append(fname)\n", "\n", "test = pd.DataFrame(test_images, columns=['fname'])\n", "print(test.shape)"]}, {"cell_type": "markdown", "source": ["### We define a center crop and will compute PCA on cropped train images. "], "metadata": {"_uuid": "6321698f38bd6b39f5c206f759c60af42cade9fc", "_cell_guid": "3867e219-dfdf-4dcc-972f-f89ee0a0a627"}}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "3d1040aa33b03a956682d06eddfb3c013d2b328c", "collapsed": true, "_cell_guid": "6b4c488c-e3ea-406c-92f1-393e6525fe9f"}, "outputs": [], "source": ["def get_center_crop(img, d=250): # delta\n", "    cy = img.shape[0] // 2\n", "    cx = img.shape[1] // 2\n", "    return img[cy - d:cy + d, cx - d:cx + d]"]}, {"cell_type": "markdown", "source": ["### We define PCA with5 principal components, which we later input to Random Forest. Indeed, it's _far_ not the best way to compute PCA only on train non-altered images, but let's give it a try!"], "metadata": {"_uuid": "cda19473ac76459747389bd8ed9c147cf52a36da", "_cell_guid": "c672a710-cf17-45c1-b1b2-5b27774b91f6"}}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "901c004d3496f284c9c7379e7929d0bf59698f4d", "collapsed": true, "_cell_guid": "a75285e6-50db-408e-b912-970e84cdf4e7"}, "outputs": [], "source": ["n_components = 5\n", "pca = PCA(n_components=n_components, svd_solver='randomized',\n", "          whiten=True)\n", "\n", "# Get some training data for PCA\n", "random_images = train.sample(300)\n", "random_images[:5]"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "098bd08e5b312c1646f55f50db300139aa0e247c", "collapsed": true, "_cell_guid": "12887f21-c454-43ad-815a-22eb7c257b0b"}, "outputs": [], "source": ["img_set_reds = []\n", "for i, r in random_images.iterrows():\n", "    # If you uncomment last part, you can extract features only over a certain channel\n", "    x = get_center_crop(cv2.imread(\"../input/train/\" + r['camera'] + '/' + r['fname']))#[:,:,0] \n", "    img_set_reds.append(np.ravel(x)) # PCA takes instances as flatten vectors, not 2-d array\n", "img_set_reds = np.asarray(img_set_reds)\n", "print(img_set_reds.shape)\n", "print([img_set_reds[i].shape for i in range(10)])\n", "pf = pca.fit(np.asarray(img_set_reds))"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "f924b59cbbd06f163c9130ae5b5e114cbc5a5480", "collapsed": true, "_cell_guid": "cb58da53-ed88-4f4b-8345-a5dfab29e6ed"}, "outputs": [], "source": ["def get_pca_features(img):\n", "    img = np.ravel(img).reshape(1, -1)\n", "    return pf.transform(img)"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "3d9de0fc6bd669d0c4f619bd67f8fbe1f560be65", "collapsed": true, "_cell_guid": "b7c86851-7ab1-4068-97cb-b0ac779e2f9c"}, "outputs": [], "source": ["t = get_pca_features(get_center_crop(\n", "        cv2.imread(\"../input/train/\" + r['camera'] + '/' + r['fname']))\n", ")\n", "t[0]"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "afa04be47c0896c4b848dde201c5d29ac7a9a43c", "collapsed": true, "_cell_guid": "9d325fb5-a180-4bb6-9afe-b0b6beabf09d"}, "outputs": [], "source": ["def color_stats(q, iolock):\n", "    while True:\n", "        img_path = q.get()\n", "        if img_path is None:\n", "            break\n", "        if type(img_path) is tuple:\n", "            img = imread(train_path / img_path[0] / img_path[1])\n", "            key = img_path[1]\n", "        else:\n", "            img = imread(test_path / img_path)\n", "            key = img_path         \n", "        # Some images read return info in a 2nd dim. We only want the first dim.\n", "        if img.shape == (2,):\n", "            img = img[0]\n", "        # crop to center as in test    \n", "        img = get_center_crop(img)\n", "        pca_feats = get_pca_features(img)\n", "        # Estimate the average noise standard deviation across color channels.\n", "        # average_sigmas = True if you want to average across channels\n", "        rgb_sigma_est = estimate_sigma(img, multichannel=True, average_sigmas=False)\n", "        color_info[key] = ( pca_feats[0][0],pca_feats[0][1],pca_feats[0][2],\n", "          #                 pca_feats[0][3],pca_feats[0][4],\n", "                           rgb_sigma_est[0],rgb_sigma_est[1],rgb_sigma_est[2]\n", "        )"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "67665bc0ae916c0bbb09398b74c4bcd6dcf71397", "collapsed": true, "_cell_guid": "b31dee68-c9cf-4846-b833-a146fc64ea35"}, "outputs": [], "source": ["cols = ['pca0','pca1', 'pca2',\n", "#        'pca3','pca4',\n", "        's1','s2','s3']\n", "\n", "for col in cols:\n", "    train[col] = None\n", "    test[col] = None"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "6a3c34861ac79ede9a089aa0447195b686059a22", "collapsed": true, "_cell_guid": "cb58a4ba-fcd5-4af7-9290-7683576a86c3"}, "outputs": [], "source": ["NCORE = 8\n", "\n", "color_info = mp.Manager().dict()\n", "\n", "# Using a queue since the image read is a bottleneck\n", "q = mp.Queue(maxsize=NCORE)\n", "iolock = mp.Lock()\n", "pool = mp.Pool(NCORE, initializer=color_stats, initargs=(q, iolock))\n", "\n", "for i in train_images:\n", "    q.put(i)  # blocks until q below its max size\n", "\n", "for i in test_images:\n", "    q.put(i)  # blocks until q below its max size\n", "    \n", "# tell workers we're done\n", "for _ in range(NCORE):  \n", "    q.put(None)\n", "pool.close()\n", "pool.join()\n", "\n", "color_info = dict(color_info)"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "5e75d21c7550b513ff35d6d3908fa037affa45ae", "collapsed": true, "_cell_guid": "4014789a-4789-4543-9e9f-1cd7cdc0b914"}, "outputs": [], "source": ["for n, col in enumerate(cols):\n", "    train[col] = train['fname'].apply(lambda x: color_info[x][n])\n", "    test[col] = test['fname'].apply(lambda x: color_info[x][n])"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "09804d7c6c035680b706e7d45788a76827f1268a", "collapsed": true, "_cell_guid": "683f1f9c-9980-453f-8056-4bb9ebd19208"}, "outputs": [], "source": ["y = train['camera'].values\n", "X_train = train[cols].values\n", "X_test = test[cols].values\n", "clf = RandomForestClassifier(n_estimators=5)\n", "# clf = SVC(decision_function_shape='ovo',kernel='rbf')\n", "clf.fit(X_train, y)"]}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "38d2f192330ec20226ddfc59a397c31dc00928f4", "collapsed": true, "_cell_guid": "5ace0db9-c188-492b-828b-4923413c0879"}, "outputs": [], "source": []}, {"cell_type": "code", "execution_count": null, "metadata": {"_uuid": "36a0fa3086766ea77f6b2b02dbf0280773343041", "collapsed": true, "_cell_guid": "e0be27ae-9da7-4017-a187-411c476afd3c"}, "outputs": [], "source": ["y_pred = clf.predict(X_test)\n", "subm = pd.read_csv(input_path / 'sample_submission.csv', index_col='fname')\n", "subm['camera'] = y_pred\n", "subm.to_csv('pca_svm_benchmark.csv')"]}, {"cell_type": "markdown", "source": ["### That's it! Thank you for reaching to the end and welcome to share your thought about PCA practice for this problem in comments!"], "metadata": {"_uuid": "078159ca34b266ce4de5bccd6d601327a130b1e0", "_cell_guid": "0587e901-dcbf-429b-957f-3dd0eb1456b0"}}], "nbformat_minor": 1, "metadata": {"language_info": {"file_extension": ".py", "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "mimetype": "text/x-python", "codemirror_mode": {"version": 3, "name": "ipython"}, "name": "python", "version": "3.6.4"}, "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}}, "nbformat": 4}