{"cells": [{"metadata": {"_cell_guid": "033e27a4-d600-4bf1-adf9-d9a0ba316f3e", "_uuid": "1a48ea0f604b56754cd25a290a0f37a8dfd5623c"}, "source": ["# Overview\n", "A notebook which tries to classify the camera based on the FFT of small regions, just an experiment"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "8e119507-463b-4349-bf53-a0d70f59d0e1", "collapsed": true, "_uuid": "c17b461b803801610e2eabc2e6f492301f9084c6"}, "source": ["import numpy as np # linear algebra\n", "import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n", "import os\n", "from skimage.io import imread # read image\n", "from PIL import Image \n", "# imread fails on some of the tiffs so we use PIL\n", "pil_imread = lambda c_file: np.array(Image.open(c_file)) \n", "from skimage.exposure import equalize_adapthist\n", "from glob import glob\n", "\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "941710b6-33b6-4c40-89c5-c30452eeba7e", "_uuid": "a7e4afe4d4802460f236159cce4b73b031932c77"}, "source": ["list_train = glob(os.path.join('..', 'input', 'train', '*', '*.jpg'))\n", "print('Train Files found', len(list_train), 'first file:', list_train[0])\n", "list_test = glob(os.path.join('..', 'input', '*', '*.tif'))\n", "print('Test Files found', len(list_test), 'first file:', list_test[0])"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "7c57e7f2-fab6-4a3a-90f7-2ce782a2d71d", "_uuid": "a9c632622efc366a3d2b44b3b5b114f879072a18"}, "source": ["from sklearn.preprocessing import LabelEncoder\n", "def get_class_from_path(filepath):\n", "    return os.path.dirname(filepath).split(os.sep)[-1]\n", "full_train_df = pd.DataFrame([{'path': x, 'category': get_class_from_path(x)} for x in list_train])\n", "cat_encoder = LabelEncoder()\n", "cat_encoder.fit(full_train_df['category'])\n", "nclass = cat_encoder.classes_.shape[0]\n", "full_train_df.sample(3)"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "a488fbfe-53f2-4091-847b-de9b91ba412a", "_uuid": "b563f2c97d0d3a106c338dd5f63669cc0b881ed8"}, "source": ["# Camera Distribution\n", "A quick look at how the training data are distributed to get a feeling for how common each camera type is. To make sure the training data isn't all too skewed"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "775aaa10-4bcc-45b3-a13d-79e6de2f2e4e", "_uuid": "5166e06773a0cce7614496c51d9cf48d9e4c2d24"}, "source": ["fig, ax1 = plt.subplots(1,1,figsize = (8, 6))\n", "ax1.hist(cat_encoder.transform(full_train_df['category']), np.arange(nclass+1))\n", "ax1.set_xticks(np.arange(nclass))\n", "_ = ax1.set_xticklabels(cat_encoder.classes_, rotation = 45)"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "4ea59577-eb9c-4cb2-9451-bfd4dd7d373c", "_uuid": "250ea42bf046505d50597fb22f48fd11c50219a7"}, "source": ["## Preprocessing\n", "Here is some basic preprocessing code to try and correct for things we are not interested in light illumination, and low frequency scene information"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "e533b8e1-5c20-4bbf-98ac-f973f8e021a0", "_uuid": "039600ab3e17e88347f65d5c455ac30cfa99e7b0"}, "source": ["import cv2\n", "def imread_and_normalize(im_path):\n", "    img_data = pil_imread(im_path)\n", "    return (img_data.astype(np.float32))/255.0"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "54120bd6-085a-48d6-a13a-d0b7b6872e04", "_uuid": "204d7d6ddaacd2240696a4ae33fc9ac272b6a82b"}, "source": ["%%time\n", "from numpy.fft import fft2\n", "def rgb_fft_norm(in_img):\n", "    out_fft = fft2(in_img, axes = (0,1))[1:-1, 1:-1] # crop edges\n", "    cat_fft = np.concatenate([np.real(out_fft), np.imag(out_fft)], -1)\n", "    for i in range(cat_fft.shape[2]):\n", "        cat_fft[:,:,i] -= cat_fft[:,:,i].mean()\n", "        cat_fft[:,:,i] /= np.clip(cat_fft[:,:,i].std(), 1e-2,10)\n", "    return cat_fft.astype(np.float32)\n", "# code for reading in a random chunk of the image\n", "def read_chunk(im_path, n_chunk = 10, chunk_x = 16, chunk_y = 16):\n", "    img_data = imread_and_normalize(im_path)\n", "    img_x, img_y, _ = img_data.shape\n", "    out_chunk = []\n", "    for _ in range(n_chunk):\n", "        x_pos = np.random.choice(range(img_x-chunk_x))\n", "        y_pos = np.random.choice(range(img_y-chunk_y))\n", "        c_data = img_data[x_pos:(x_pos+chunk_x), y_pos:(y_pos+chunk_y),:3]\n", "        out_chunk += [rgb_fft_norm(c_data)]\n", "    return np.stack(out_chunk, 0)\n", "\n", "t_img = read_chunk(full_train_df['path'].values[0])\n", "fig, c_axs = plt.subplots(2, t_img.shape[3], figsize = (12, 4))\n", "for i, (c_ax, m_ax) in enumerate(c_axs.T):\n", "    c_ax.imshow(t_img[0,:,:,i], interpolation='none')\n", "    c_ax.axis('off')\n", "    m_ax.hist(t_img[0,:,:,i].ravel())"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "7c0bfe63-629d-4169-8635-a5c79481da32", "_uuid": "33d90eca35fdfb61df9214986ecd25f0597a34de"}, "source": ["from keras.utils.np_utils import to_categorical\n", "def generate_even_batch(base_df, sample_count = 1, chunk_count = 50):\n", "    while True:\n", "        cur_df = base_df.groupby('category').apply(lambda x: x[['path']].sample(sample_count)).reset_index()\n", "        x_out = np.concatenate(cur_df['path'].map(lambda x: read_chunk(x, n_chunk=chunk_count)),\n", "                             0)\n", "        y_raw = [x for x in cur_df['category'].values for _ in range(chunk_count)]\n", "        y_out = to_categorical(cat_encoder.transform(y_raw))\n", "        yield x_out, y_out"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "eadbb3e3-8796-4788-bbdb-af33abc6624f", "_uuid": "8d914bbd80be00cbb14e0d9bc9a9cdc3929e7665"}, "source": ["d_gen = generate_even_batch(full_train_df)\n", "for _, (x, y) in zip(range(1), d_gen):\n", "    print(x.shape, y.shape)"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "d9d73db0-fc4c-449b-9ec6-865688540797", "_uuid": "68335a88200a19c6754224c074a5fd56803af84d"}, "source": ["# Build Model\n", "Here we make a model for processing the snippets"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "23c1d5e9-0202-4905-b790-aba502e34ccc", "collapsed": true, "_uuid": "cdcc74a8f7f8eb58e9577353a250edb9b1151b68"}, "source": ["from keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau, TensorBoard\n", "from keras import optimizers, losses, activations, models\n", "from keras.layers import Convolution2D, Dense, Input, Flatten, Dropout, MaxPooling2D, BatchNormalization, GlobalAveragePooling2D, GlobalMaxPool2D, concatenate\n", "\n", "def create_model():\n", "    inp = Input(shape=(14, 14, 6))\n", "    norm_inp = BatchNormalization()(inp)\n", "    gap_layers = []\n", "    \n", "    img_1 = Convolution2D(16, \n", "                          kernel_size=1)(norm_inp)\n", "    img_1 = Convolution2D(16, \n", "                          kernel_size=1)(norm_inp)\n", "    \n", "    vec_1 = Flatten()(img_1)\n", "    # simple feature analysis\n", "    feat_1 = Convolution2D(16, kernel_size = (3,3))(img_1)\n", "    feat_1 = Convolution2D(32, kernel_size = (3,3))(feat_1)\n", "    feat_1 = MaxPooling2D((2,2))(feat_1)\n", "    feat_1 = Convolution2D(32, kernel_size = (3,3))(feat_1)\n", "    feat_1 = Convolution2D(64, kernel_size = (3,3))(feat_1)\n", "    fvec_1 = Flatten()(feat_1)\n", "    \n", "    vec_1 = concatenate([vec_1, fvec_1])\n", "    vec_1 = Dropout(0.5)(vec_1)\n", "    \n", "    \n", "    dense_1 = Dense(32, activation=activations.relu)(vec_1)\n", "    dense_1 = Dense(nclass, activation='softmax')(dense_1)\n", "\n", "    model = models.Model(inputs=inp, outputs=dense_1)\n", "    opt = optimizers.Adam(lr=1e-3) # karpathy's magic learning rate\n", "    model.compile(optimizer=opt, \n", "                  loss='categorical_crossentropy', \n", "                  metrics=['acc'])\n", "    model.summary()\n", "    return model"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "26bcaba6-cecf-4913-908a-ff9d2ef468a7", "_uuid": "d47f14aa01ef7656e9c2a6f276f30f3300e21f3a"}, "source": ["# Training Testing Split\n", "Split the groups apart to have an untainted metric of the success\n"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "824d617b-ed40-46e5-8658-b4ca3d390928", "_uuid": "f012e4bfb32530ea42bd6afc0e34534be5cdc275"}, "source": ["%%time\n", "from sklearn.model_selection import train_test_split\n", "train_df, test_df = train_test_split(full_train_df, \n", "                                     test_size = 0.15,\n", "                                    random_state = 2018,\n", "                                    stratify = full_train_df['category'])\n", "print('Train', train_df.shape[0], 'Test', test_df.shape[0])\n", "train_gen = generate_even_batch(train_df, 3, chunk_count = 20)\n", "test_gen = generate_even_batch(test_df, 10, chunk_count = 30)\n", "# cache the test_gen_data\n", "(test_x, test_y) = next(test_gen)\n", "print('Test Data', test_x.shape)"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "a93fd2f8-6a74-45f9-8cc9-f4e9e35fb292", "_uuid": "63c6c88123cc34dc144b7ac34403941cb5d03f3b"}, "source": ["model = create_model()\n", "file_path=\"weights.best.hdf5\"\n", "\n", "checkpoint = ModelCheckpoint(file_path, monitor='val_acc', verbose=1, save_best_only=True, mode='max')\n", "\n", "early = EarlyStopping(monitor=\"val_acc\", mode=\"max\", patience=3)\n", "callbacks_list = [checkpoint, early] #early"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "6ce307c4-ba6a-4e6e-b315-f88914402645", "_uuid": "5c6c99418b781aa887e193475b6acf488762bbca"}, "source": ["history = model.fit_generator(train_gen, \n", "                              steps_per_epoch = 10,\n", "                              validation_data = (test_x, test_y), \n", "                              epochs = 10, \n", "                              verbose = True,\n", "                              workers = 4,\n", "                              use_multiprocessing = False,\n", "                              callbacks = callbacks_list)\n", "\n", "#print(history)\n", "\n", "model.load_weights(file_path)"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "05b97451-491b-456c-a1a9-55b404274d9b", "_uuid": "ee2d9cd9418aba722fbfd9cc019f0c3cc0d8f896"}, "source": ["# Predict on output\n", "We run the model on the full test image, one at a time, and save the category"], "cell_type": "markdown"}, {"metadata": {"_cell_guid": "1d0318af-3195-400a-9e38-d8d055671b59", "_uuid": "e1c8db8e1c6f09b00319154e489f27de265e3f74"}, "source": ["# show the processed image\n", "t_img = read_chunk(np.random.choice(list_test))\n", "fig, c_axs = plt.subplots(2, t_img.shape[3], figsize = (12, 4))\n", "for i, (c_ax, m_ax) in enumerate(c_axs.T):\n", "    c_ax.imshow(t_img[0,:,:,i], interpolation='none')\n", "    c_ax.axis('off')\n", "    m_ax.hist(t_img[0,:,:,i].ravel())"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "9c03ee90-34a2-488d-9d11-fa24b3cc810a", "_uuid": "7e80ba17eb1285184618d900df76035847d946bc"}, "source": ["from tqdm import tqdm\n", "out_dict_list = []\n", "for c_file in tqdm(list_test):\n", "    ck_data = read_chunk(c_file, n_chunk = 100)\n", "    ck_pred = model.predict(ck_data)\n", "    # take the average prediction\n", "    mean_vec = np.mean(ck_pred,0)\n", "    out_dict_list += [{\n", "        'fname': os.path.basename(c_file),\n", "        'camera': np.argmax(mean_vec,0)\n", "    }]  "], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "3078ba99-1e6e-4f72-8f95-cd579236a930", "_uuid": "5d11a7d9c02cf6508332ec5d19db2f5848c46f79"}, "source": ["df = pd.DataFrame(out_dict_list)\n", "df['camera'] = df['camera'].map(cat_encoder.inverse_transform)\n", "df[['fname', 'camera']].to_csv(\"submission.csv\", index=False)\n", "df.sample(3)"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "26cb2059-1f98-4182-8076-38bc97a55195", "_uuid": "31775597358630adb28c5874f260d672cb449747"}, "source": ["fig, ax1 = plt.subplots(1,1,figsize = (8, 6))\n", "ax1.hist(cat_encoder.transform(df['camera']), np.arange(nclass+1))\n", "ax1.set_xticks(np.arange(nclass)+0.5)\n", "_ = ax1.set_xticklabels(cat_encoder.classes_, rotation = 90)"], "cell_type": "code", "execution_count": null, "outputs": []}, {"metadata": {"_cell_guid": "75d80c11-6e0f-4fb4-bc6a-1bce0e302fd3", "collapsed": true, "_uuid": "91727a7713a92be6c3c0f312d848216cce202168"}, "source": [], "cell_type": "code", "execution_count": null, "outputs": []}], "metadata": {"language_info": {"name": "python", "codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "pygments_lexer": "ipython3", "mimetype": "text/x-python", "nbconvert_exporter": "python", "version": "3.6.4"}, "kernelspec": {"language": "python", "name": "python3", "display_name": "Python 3"}}, "nbformat": 4, "nbformat_minor": 1}