{"metadata": {"kernelspec": {"name": "python3", "language": "python", "display_name": "Python 3"}, "language_info": {"codemirror_mode": {"version": 3, "name": "ipython"}, "file_extension": ".py", "pygments_lexer": "ipython3", "version": "3.6.4", "name": "python", "mimetype": "text/x-python", "nbconvert_exporter": "python"}}, "nbformat": 4, "cells": [{"cell_type": "markdown", "metadata": {"_cell_guid": "7c98027d-419e-43fc-a0ae-4624139a7a8f", "_uuid": "e4499069e2d9cf9ecb70d3a1c007ecb28b6c7298"}, "source": ["# Unsupervised Computer Vision Segmentation with Type-Separation"]}, {"cell_type": "markdown", "metadata": {"_cell_guid": "2173936d-3f60-45f6-a13d-71c03fff05fb", "_uuid": "3720514a7f186139f61445999c360c6406d7ec5d"}, "source": ["In this notebook I won't use the training data, only for exploration and algorithm checking.\n", "\n", "Type-separation means that there are different type of images (colored/gray/gray with light background) and I try to \"detect\" which type the image belongs to and after than apply different separation algorithm for each types.\n", "\n", "I am not saying that this is the solution for the problem, so it is only a experiment.\n", "\n", "The output of the notebook reaches **0.230 LB**"]}, {"cell_type": "code", "metadata": {"_cell_guid": "59ba9ada-4d31-43b2-b0f3-f74a23880448", "collapsed": true, "_uuid": "21091f438dab1fc97a8c9d72efdd78c9ab4ed3c4"}, "execution_count": null, "source": ["import glob\n", "import os\n", "import numpy as np\n", "import pandas as pd\n", "import cv2\n", "%matplotlib inline\n", "import matplotlib.pyplot as plt"], "outputs": []}, {"cell_type": "markdown", "metadata": {"_cell_guid": "c352403a-64f6-425e-bb28-af30c6c30313", "_uuid": "cf1df6f76e0d646420efcf90bea68cdd5e7a49e5"}, "source": ["# Prepare data"]}, {"cell_type": "markdown", "metadata": {"_cell_guid": "de048914-f3a4-46b7-bdaa-b6c14b0bcaf2", "_uuid": "8d40dbdcf2995b3af195d2d0ab964c4f4d4e8d37"}, "source": ["## Train DataFrame"]}, {"cell_type": "code", "metadata": {"_cell_guid": "76df44d9-99f9-4c5f-895b-90b9b592e674", "collapsed": true, "_uuid": "04d525dfce2e067354c168815f499d5368dbf637"}, "execution_count": null, "source": ["train_df = pd.DataFrame()"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "eb4687f5-56bd-4766-bf00-f4f5b20bc091", "collapsed": true, "_uuid": "f299c3d591c331b42e396bd9016f0b9a4a7c40fc"}, "execution_count": null, "source": ["train_image_ids = []\n", "train_image_paths = []\n", "train_image_mask_paths = []\n", "\n", "for base_path in glob.glob(\"../input/stage1_train/*\"):\n", "    image_id = os.path.basename(base_path)\n", "    train_image_path = glob.glob(os.path.join(base_path, \"images\", \"*.png\"))[0]\n", "    mask_paths = glob.glob(os.path.join(base_path, \"masks\", \"*.png\"))\n", "    \n", "    train_image_ids.append(image_id)\n", "    train_image_paths.append(train_image_path)\n", "    train_image_mask_paths.append(mask_paths)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "e0a55b8e-1ad0-4127-9414-bed41895924d", "collapsed": true, "_uuid": "bceab02ea2926d45955ae01f0de14e924f10e925"}, "execution_count": null, "source": ["train_df[\"image_id\"] = train_image_ids\n", "train_df[\"image_path\"] = train_image_paths\n", "train_df[\"mask_path\"] = train_image_mask_paths"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "6e5a4137-f0f6-4b39-a808-5c5a6ef15599", "collapsed": true, "_uuid": "7565ad9c8fb3b9cd6d2f3e0e6eab8cb749160714"}, "execution_count": null, "source": ["train_df.sample(5)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "a09fd443-2f56-4b8a-bebc-6edbc1ea8eb0", "collapsed": true, "_uuid": "9ccd41cc36c27b56db35befaa5ae5aa587a969fb"}, "execution_count": null, "source": ["train_df.to_csv(\"train_df.csv\")"], "outputs": []}, {"cell_type": "markdown", "metadata": {"_cell_guid": "54b3419c-3849-406c-a0be-1ff21d239e3b", "_uuid": "9a19a2ab936dc80df7cbaf2e0ed4f4ab60c9f8af"}, "source": ["## Test DataFrame"]}, {"cell_type": "code", "metadata": {"_cell_guid": "5bc6f6ba-aa38-4760-9d13-4da238bafff1", "collapsed": true, "_uuid": "8d16467caaea6ae9b24ffc3d391d4dab6080c4b6"}, "execution_count": null, "source": ["test_df = pd.DataFrame()"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "be28f987-467c-46aa-9678-7161818f1f7d", "collapsed": true, "_uuid": "4ca09f21ddc4a5e5ea6cb14bc106c9bd89714ec0"}, "execution_count": null, "source": ["test_image_ids = []\n", "test_image_paths = []\n", "\n", "for base_path in glob.glob(\"../input/stage1_test/*\"):\n", "    image_id = os.path.basename(base_path)\n", "    test_image_path = glob.glob(os.path.join(base_path, \"images\", \"*.png\"))[0]\n", "    \n", "    test_image_ids.append(image_id)\n", "    test_image_paths.append(test_image_path)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "eb77a375-375f-4a15-b9e2-d760235b7777", "collapsed": true, "_uuid": "774e7b5c15cf65db25bd30477fe160e538551310"}, "execution_count": null, "source": ["test_df[\"image_id\"] = test_image_ids\n", "test_df[\"image_path\"] = test_image_paths"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "b2f265dd-f20a-4aa0-aab7-ae36967bbbd0", "collapsed": true, "_uuid": "f7818cb08c2258a295f955fe542d2a9fd3e56536"}, "execution_count": null, "source": ["test_df.sample(5)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "fc7a27c5-01fe-4cd8-be17-f82188dfd63e", "collapsed": true, "_uuid": "8ee748974e3c2023b2a869d14e316f7a680b715f"}, "execution_count": null, "source": ["test_df.to_csv(\"test_df.csv\")"], "outputs": []}, {"cell_type": "markdown", "metadata": {"_cell_guid": "1993081d-94ab-4da2-b13b-285289f040cb", "_uuid": "e47aeb71fa0d8e267e222090d3b0217a2d865863"}, "source": ["# Type-Separation and processing"]}, {"cell_type": "markdown", "metadata": {"_cell_guid": "e281f061-f1b8-4354-86d7-42c4f95b61c0", "_uuid": "640b65b57ceeec9af3e3f3f0a99407a05cba901f"}, "source": ["- Type gray with white bg: `3594684b9ea0e16196f498815508f8d364d55fea2933a2e782122b6f00375d04`\n", "- Type color: `74a7785530687a11ecd073e772f90912d9967d02407a192bfab282c35f55ab94`\n", "- Type gray with black bg: `f113626a04125d97b27f21b45a0ce9a686d73dee7b5dbc0725d49194ba0203bd`"]}, {"cell_type": "code", "metadata": {"_cell_guid": "6b15f410-a43f-4bca-a8ba-e4f472294114", "collapsed": true, "_uuid": "6d5f704986f2382e4dd11bbecdbabdd3c345ce21"}, "execution_count": null, "source": ["# Select random train image\n", "\n", "tmp_image_row = train_df.sample(1)\n", "tmp_image_id = tmp_image_row[\"image_id\"].values[0]\n", "print(\"Imge id is: {0}\".format(tmp_image_id))\n", "tmp_image_path = tmp_image_row[\"image_path\"].values[0]\n", "tmp_image_masks = tmp_image_row[\"mask_path\"].values[0]"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "f1098826-5662-4936-840f-f4650ce6bb98", "collapsed": true, "_uuid": "5686ab8aa7bab0388e5124fd1b43ff0aa5cb33da"}, "execution_count": null, "source": ["tmp_image = cv2.imread(tmp_image_path)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "22396453-4b6c-4312-882d-09994b0f6e8a", "collapsed": true, "_uuid": "2a1f4bc25d686cb9a210bf99f161657783f6e748"}, "execution_count": null, "source": ["plt.imshow(tmp_image)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "f70ae44e-a61a-4aa2-8a6f-7f4ee0c259c8", "collapsed": true, "_uuid": "f195af593344665458fe768a16b66330a39a698b"}, "execution_count": null, "source": ["def create_unified_mask(mask_image_paths):\n", "    tmp_image_mask = None\n", "    for m in mask_image_paths:\n", "        m = cv2.imread(m, cv2.IMREAD_GRAYSCALE)\n", "        if tmp_image_mask is None:\n", "            tmp_image_mask = m\n", "        tmp_image_mask = cv2.bitwise_or(tmp_image_mask, m)\n", "    return tmp_image_mask"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "f77928a2-ee13-4180-a8a5-a67bd669d238", "collapsed": true, "_uuid": "190519149ff5c0b6654e3da93d44de0bb12e633b"}, "execution_count": null, "source": ["tmp_image_mask = create_unified_mask(tmp_image_masks)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "525387f2-a964-4466-8edd-cc18f2c02418", "collapsed": true, "_uuid": "06119aa9ea969686fec0f729eceba4ed01b93b4b"}, "execution_count": null, "source": ["fig, axs = plt.subplots(1, 2, figsize=(10,10))\n", "\n", "axs[0].imshow(tmp_image)\n", "axs[0].grid()\n", "\n", "axs[1].imshow(tmp_image_mask)\n", "axs[1].grid()"], "outputs": []}, {"cell_type": "markdown", "metadata": {"_cell_guid": "cecfc320-74e9-44e7-a259-cbe32fb7b334", "_uuid": "9d9918bb4c16927d47a5e76e573df17813ea13b7"}, "source": ["### Type-separation"]}, {"cell_type": "code", "metadata": {"_cell_guid": "873cd43e-de42-4535-97fe-2482f1ef261b", "collapsed": true, "_uuid": "4c90bb9c81bfdf631236238d6b87e91f3718dd6c"}, "execution_count": null, "source": ["hsv_image = cv2.cvtColor(tmp_image, cv2.COLOR_BGR2HSV)\n", "h, s, v =cv2.split(hsv_image)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "2e173b84-51ad-4609-89bd-402195711148", "collapsed": true, "_uuid": "eca9b57015ca449f0d3b1535a971d841cc525634"}, "execution_count": null, "source": ["fig, axs = plt.subplots(1, 3, figsize=(20,20))\n", "axs[0].imshow(h)\n", "axs[1].imshow(s)\n", "axs[2].imshow(v)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "11e7f767-0cff-4db7-9a3b-941c2ae60c17", "collapsed": true, "_uuid": "8f7c0fdcf3d6bdf91606c49d01cf05c77da9bea9"}, "execution_count": null, "source": ["def get_image_type(image):\n", "    # 0 is gray with black bg\n", "    # 1 is gray with white/gray bg\n", "    # 2 is colored\n", "\n", "    image_type = -1\n", "    \n", "    hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)\n", "    h, s, v =cv2.split(hsv_image)\n", "    \n", "    # Decide if it is a colored image or not\n", "    \n", "    if np.max(h) == 0 and np.min(h) == 0:\n", "        v_blurred = cv2.GaussianBlur(v, (5,5), 10)\n", "        ret, thresh = cv2.threshold(v, 0, 255, cv2.THRESH_OTSU)\n", "        _, cnts, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n", "        cnts = sorted(cnts, key=cv2.contourArea, reverse=True)\n", "        max_cnt_area = cv2.contourArea(cnts[0])\n", "        \n", "        # Decide which type of gray it is\n", "        \n", "        if max_cnt_area > 65000:\n", "            image_type = 1\n", "        else:\n", "            image_type=0\n", "    else:\n", "        # TODO: here we can separate colored images based on the lightness of the BG. Just like we did it\n", "        # for the gray images\n", "        image_type = 2\n", "    \n", "    return image_type, (h, s, v)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "9190f706-52c0-465e-87cd-9299c3a1c914", "collapsed": true, "_uuid": "c91b05d7b27683fd3ed7d7b567167636095efdd4"}, "execution_count": null, "source": ["image_type, (h, s, v) = get_image_type(tmp_image)"], "outputs": []}, {"cell_type": "markdown", "metadata": {"_cell_guid": "46b876ae-36c1-42d4-ade9-d7f5f50d0515", "_uuid": "d40a2d9390cea474ca029a9788d72da3f06c63f5"}, "source": ["### Method 1: Thresholding only"]}, {"cell_type": "code", "metadata": {"_cell_guid": "b7eea9a5-473a-4af1-8d5d-7d909b6d2fd5", "collapsed": true, "_uuid": "996ffc752968dc213886b2d61b21eacb6cdede11"}, "execution_count": null, "source": ["if image_type == 0:\n", "    v_blurred = cv2.GaussianBlur(v, (7,7), 1)\n", "    ret, thresh = cv2.threshold(v_blurred, 0, 255, cv2.THRESH_OTSU)\n", "    print(\"Type GRAY with black bg\")\n", "elif image_type == 1:\n", "    ret, thresh = cv2.threshold(v, 100, 150, cv2.THRESH_BINARY_INV)\n", "    print(\"Type GRAY with white/light-gray bg\")\n", "elif image_type == 2:\n", "    s_blurred = cv2.GaussianBlur(s, (7,7), 1)\n", "    ret, thresh = cv2.threshold(s_blurred,0, 255, cv2.THRESH_OTSU)\n", "    print(\"Type COLOR with light bg\")"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "39641e82-7e53-40a6-9234-377c6a1585c8", "collapsed": true, "_uuid": "28671f8b4c933139a49d732d58b13fec919e9bd5"}, "execution_count": null, "source": ["fig, axs = plt.subplots(1, 2, figsize=(10,10))\n", "\n", "axs[0].imshow(thresh)\n", "axs[0].grid()\n", "\n", "axs[1].imshow(tmp_image_mask)\n", "axs[1].grid()"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "93ff70dc-dd10-4ace-8f96-48d7df77bdbc", "collapsed": true, "_uuid": "6ca6867a4bba1db0e9d73a2edcea451d8f99d924"}, "execution_count": null, "source": ["kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (4, 4))"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "26103211-6e71-4f8b-9f6d-ecde96f2a987", "collapsed": true, "_uuid": "4ee2a03d7fc20d0acb51dd1beeba0f8e3878501c"}, "execution_count": null, "source": ["mask = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)\n", "mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2)\n", "mask = cv2.dilate(mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3)))"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "5e4a116e-84d6-4d74-9507-b78d5fd38890", "collapsed": true, "scrolled": true, "_uuid": "fb121b82952e39b4fb84c083c644711fe4bc61b0"}, "execution_count": null, "source": ["fig, axs = plt.subplots(1, 2, figsize=(10,10))\n", "axs[0].imshow(mask)\n", "axs[1].imshow(tmp_image_mask)"], "outputs": []}, {"cell_type": "markdown", "metadata": {"_cell_guid": "9d891dc0-71a7-40a5-9756-2e6c6e22b5eb", "_uuid": "877dff27c39038f59c4f345a90814ed94823922a"}, "source": ["### Method 2: Watershed"]}, {"cell_type": "code", "metadata": {"_cell_guid": "01e7146a-bff7-4b60-b107-16933702e645", "collapsed": true, "_uuid": "c252d6c6c0ca45796585740e33f3447eb4d27fe3"}, "execution_count": null, "source": [], "outputs": []}, {"cell_type": "markdown", "metadata": {"_cell_guid": "72be7a51-9303-4a32-a9fa-4d9cd47de124", "_uuid": "d675f567a49a2a4a62f3e89f6e76c0c2daaf6d5e"}, "source": ["# Create submission with test images"]}, {"cell_type": "code", "metadata": {"_cell_guid": "7d0250dc-b0b9-4575-bb38-220747492c27", "collapsed": true, "_uuid": "887a6d0e5d0cf20f4fe90dbdb3aaff1bfd33de82"}, "execution_count": null, "source": ["def preproces_image_based_on_type(image_type, saturation_image, value_image):\n", "    if image_type == 0:\n", "        # v_blurred = cv2.GaussianBlur(value_image, (7,7), 1)\n", "        v_blurred  = v\n", "        ret, thresh = cv2.threshold(v_blurred, 0, 255, cv2.THRESH_OTSU)\n", "    elif image_type == 1:\n", "        ret, thresh = cv2.threshold(value_image, 100, 150, cv2.THRESH_BINARY_INV)\n", "    elif image_type == 2:\n", "        # s_blurred = cv2.GaussianBlur(saturation_image, (7,7), 1)\n", "        s_blurred = s\n", "        ret, thresh = cv2.threshold(s_blurred, 0, 255, cv2.THRESH_OTSU)\n", "    else:\n", "        raise ValueError(\"Not known image type\")\n", "    return thresh"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "d7c3eec6-83ba-4ee7-803b-667dd39dc592", "collapsed": true, "_uuid": "b120d4f7fa1c398565b74a507e14df9ce6cb23a6"}, "execution_count": null, "source": ["def apply_morphology(mask_image):\n", "    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))\n", "    mask_image = cv2.morphologyEx(mask_image, cv2.MORPH_CLOSE, kernel, iterations=1)\n", "    mask_image = cv2.morphologyEx(mask_image, cv2.MORPH_OPEN, kernel, iterations=1)\n", "    # mask_image = cv2.dilate(mask_image, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2,2)))\n", "    return mask_image"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "9c986d44-372e-4c8d-bda0-6d1471ec6df9", "collapsed": true, "_uuid": "a526d2b5d009423baf3435e0b30d77e21ce0a072"}, "execution_count": null, "source": ["submission_image_masks = []\n", "submission_image_ids = test_df[\"image_id\"].values\n", "\n", "for n, image_path in enumerate(test_df[\"image_path\"].values):\n", "    image = cv2.imread(image_path, cv2.IMREAD_COLOR)\n", "    image_type, (h, s, v) = get_image_type(image)\n", "    mask = preproces_image_based_on_type(image_type, s, v)\n", "    mask = apply_morphology(mask)\n", "    submission_image_masks.append(mask)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "8c28a870-9af8-4921-8e7f-3e54031e4cec", "collapsed": true, "_uuid": "fe04a43e3926cddcfd5ff4385c109aea82c742e3"}, "execution_count": null, "source": ["# Run length Encoding from https://www.kaggle.com/rakhlin/fast-run-length-encoding-python\n", "\n", "from skimage.morphology import label\n", "\n", "def rle_encoding(x):\n", "    dots = np.where(x.T.flatten() == 1)[0]\n", "    run_lengths = []\n", "    prev = -2\n", "    for b in dots:\n", "        if (b>prev+1): run_lengths.extend((b + 1, 0))\n", "        run_lengths[-1] += 1\n", "        prev = b\n", "    return run_lengths\n", "\n", "def prob_to_rles(x, cutoff=0.5):\n", "    lab_img = label(x > cutoff)\n", "    for i in range(1, lab_img.max() + 1):\n", "        yield rle_encoding(lab_img == i)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "5e3aec7f-4bef-4e89-b610-abde770bfc72", "collapsed": true, "_uuid": "6c2592b81e65577d9a2ab7fe5b7915ce9303a080"}, "execution_count": null, "source": ["new_test_ids = []\n", "rles = []\n", "for n, id_ in enumerate(submission_image_ids):\n", "    rle = list(prob_to_rles(submission_image_masks[n]))\n", "    rles.extend(rle)\n", "    new_test_ids.extend([id_] * len(rle))"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "997315e7-f7f5-4688-867c-16732ed9f17a", "collapsed": true, "_uuid": "125f03553a410d0418cea275216d69ac6f4cf251"}, "execution_count": null, "source": ["submission_df = pd.DataFrame()\n", "submission_df['ImageId'] = new_test_ids\n", "submission_df['EncodedPixels'] = pd.Series(rles).apply(lambda x: ' '.join(str(y) for y in x))"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "e8c68cf3-8cc5-4bbc-97e6-63e80a104736", "collapsed": true, "_uuid": "1514bb019d20a34d30d995d22de1debc95092648"}, "execution_count": null, "source": ["submission_df.head(5)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "85df8fa1-1f6b-4da2-9985-111bbd7ed5d1", "collapsed": true, "_uuid": "35f3d61d6f509b97d7e349a75ff82266f5405380"}, "execution_count": null, "source": ["len(submission_df)"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "85cb222b-d906-434f-9f59-0b04035801a0", "collapsed": true, "_uuid": "895bd6666f327111d4728a41cf6c6d87cbdeb652"}, "execution_count": null, "source": ["if not len(np.unique(submission_df[\"ImageId\"])) == len(test_image_ids):\n", "    print(\"Submission is not complete\")\n", "    print(\"Missing test ids: {0}\".format(set(test_image_ids).difference(set(np.unique(submission_df[\"ImageId\"])))))\n", "else:\n", "    print(\"Submission is ready\")"], "outputs": []}, {"cell_type": "code", "metadata": {"_cell_guid": "15341a99-6ecb-4669-b7d0-c974725ec158", "collapsed": true, "_uuid": "ddc415901147162e9ce02df49f2bdfd000f90492"}, "execution_count": null, "source": ["submission_df.to_csv('submission_computer_vision.csv', index=False)"], "outputs": []}], "nbformat_minor": 1}