{"cells":[{"metadata":{"_uuid":"56390c7d3e58d327496356bcab9b7be8addf4a9c"},"cell_type":"markdown","source":"# Introduction\n\nThis is a kernel to do some initial EDA for the problem. That is to say, it displays some statistics about training image data set and objects within. It also features my implementation of some useful metrics and utility functions. I hope it would be useful or interesting for somebody."},{"metadata":{"trusted":true,"_uuid":"95e15103c5229d5406ff3c5bb571d17273a50fcd"},"cell_type":"code","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\nimport math\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nfrom skimage.data import imread\n\n# Input data files are available in the \"../input/\" directory.\n\n\n# Globals (!implied parameters to some functions)\ntrain_img_dir = '../input/train/'\ntrain_seg_csv = '../input/train_ship_segmentations.csv'\n\ntest_img_dir = '../input/test/'\n\ntraincsv = pd.read_csv('../input/train_ship_segmentations.csv')\n\n\n# Reproducibility: keep it for later!\n#np.random.seed(100)\n\n\n# ::::::::::::::::::::::::::: conventions :::::::::::::::::::::::::::::::::::::::\n# encodedpixels: the pixels of the object in the image in run-length encoding\n#   a string of 'run-start run-length' whitespace-separtated sequence of integers\n#   note: run-start is the pixel position in COLUMN-major order and it is 1-based!\n# rle: run-length encoding (sequence)\n#   the incarnation of the string 'encodedpixels'\n#   in a numpy 2D array (dtype=numpy.uint8) with a row format [run-start, run-length]\n#   note: run-start is still 1-based and column-major!\n# mask: the full-image binary mask of the object (0->background, 1->object)\n#   numpy ndarray (2D) (dtype=numpy.uint8)\n# combined_masks: the combined masks of objects as a single mask\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b6b87e1d0f15b2e39076c4020ca0f08f02b84d35"},"cell_type":"markdown","source":"# Utilities"},{"metadata":{"trusted":true,"_uuid":"83b20adcf9d3b933cee7c0f91697dd38bf84c306"},"cell_type":"code","source":"def rle_pixels(rle):\n    \"\"\" returns: the pixel count in the object encoded by 'rle' \"\"\"\n    if rle.size > 0:\n        return np.sum(rle[:,1])\n    return 0\n\n\ndef rle_dims(rle):\n    \"\"\" returns: the dimensions (height, width) of the object encoded by 'rle' \"\"\"\n    if rle.size > 0:\n        return (np.max(rle[:,1]), len(rle))\n    return (0,0)\n\n\ndef encodedpixels2rle(encodedpixels):\n    if isinstance(encodedpixels, str):\n        return np.array(list(zip(*[iter(int(x) for x in encodedpixels.split())]*2)))\n    return np.array([])\n\n\ndef object_dims(encodedpixels):\n    \"\"\" returns: the dimensions (height, width) of the object encoded by 'encodedpixels' \"\"\"\n    return rle_dims(encodedpixels2rle(encodedpixels))\n\n\ndef object_pixels(encodedpixels):\n    \"\"\" returns: the number of pixels in the object encoded by 'encodedpixels' \"\"\"\n    return rle_pixels(encodedpixels2rle(encodedpixels))\n\n\ndef rle2mask(rle, shape=(768, 768)):\n    \"\"\"\n    rle: 2D numpy array with rows of form [start, run-length]\n    shape: (rows, cols) the shape of the referenced image\n    \"\"\"\n    mask = np.zeros(shape[0] * shape[1], dtype=np.uint8)\n\n    run_ranges = [(start - 1, start + length - 1) for (start, length) in rle]\n\n    for a, b in run_ranges:\n        mask[a:b] = 1\n\n    return mask.reshape(shape).T\n\n\ndef mask2rle(mask):\n    mask = mask.T.ravel()\n    \n    start_mask = np.concatenate((mask[0:1] > 0, mask[1:] > mask[0:-1]))\n    end_mask = np.concatenate((mask[0:-1] > mask[1:], mask[-1:] > 0))\n\n    run_starts = np.where(start_mask.T.ravel())[0] # 0-based!\n    run_lengths = np.where(end_mask.T.ravel())[0] - run_starts + 1\n\n    return np.array(list(zip(run_starts + 1, run_lengths)))\n\n\ndef read_train_image(imgid):\n    return(imread(train_img_dir + '/' + imgid))\n\n\ndef read_test_image(imgid):\n    return(imread(test_img_dir + '/' + imgid))\n\n\ndef get_train_masks(imgid):\n    return [rle2mask(encodedpixels2rle(encodedpixels))\n            for encodedpixels in \n                traincsv[traincsv.ImageId == imgid]['EncodedPixels']]\n\n\ndef get_train_combined_masks(imgid):\n    return(rle2mask(encodedpixels2rle(' '.join(\n        traincsv[traincsv.ImageId == imgid]['EncodedPixels'].fillna('').astype(str)))))\n\n\ndef get_train_objcount(imgid):\n    return traincsv[traincsv.ImageId == imgid]['EncodedPixels'].count()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a187f87905f24109cafaf46085ca7a6bd9993d24"},"cell_type":"code","source":"# simple test\nprint('Mask decode-rencode example:')\nprint(mask2rle(rle2mask([(1,3), (769+1,766), (1537+1, 10)])))\n\nprint('Image 00003e153.jpg has %s object(s)' % get_train_objcount('00003e153.jpg'))\nprint('Image 6c06acaa5.jpg has %s object(s)' % get_train_objcount('6c06acaa5.jpg'))","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"76cd28d0b39f7257993ebdd806afdc656f19a568"},"cell_type":"markdown","source":"# Metrics"},{"metadata":{"trusted":true,"_uuid":"776479307cc57b5327bdbd21555abc9a30e6ce93"},"cell_type":"code","source":"# :::::::::::::::::::::::::: metrics ::::::::::::::::::::::::::::::::::::::::\ndef IoU(mask1, mask2):\n    Inter = np.sum((mask1 >= 0.5) & (mask2 >= 0.5))\n    Union = np.sum((mask1 >= 0.5) | (mask2 >= 0.5))\n    return Inter / (1e-8 + Union)\n\ndef fscore(tp, fn, fp, beta=2.):\n    if tp + fn + fp < 1:\n        return 1.\n    num = (1 + beta ** 2) * tp\n    return num / (num + (beta ** 2) * fn + fp)\n\ndef confusion_counts(predict_mask_seq, truth_mask_seq, iou_thresh=0.5):\n    predict_masks = [m for m in predict_mask_seq if np.any(m >= 0.5)]\n    truth_masks = [m for m in truth_mask_seq if np.any(m >= 0.5)]\n    \n    if len(truth_masks) == 0:\n        tp, fn, fp = 0.0, 0.0, float(len(predict_masks))\n        return tp, fn, fp\n\n    pred_hits = np.zeros(len(predict_masks), dtype=np.bool) # 0 miss, 1 hit\n    truth_hits = np.zeros(len(truth_masks), dtype=np.bool)  # 0 miss, 1 hit\n\n    for p, pred_mask in enumerate(predict_masks):\n        for t, truth_mask in enumerate(truth_masks):\n            if IoU(pred_mask, truth_mask) > iou_thresh:\n                truth_hits[t] = True\n                pred_hits[p] = True\n\n    tp = np.sum(pred_hits)\n    fn = len(truth_masks) - np.sum(truth_hits)\n    fp = len(predict_masks) - tp\n\n    return tp, fn, fp\n\ndef mean_fscore(predict_mask_seq, truth_mask_seq,\n              iou_thresholds=[0.5, 0.55, 0.6, 0.65, 0.7,\n                              0.75, 0.8, 0.85, 0.9, 0.95], beta=2.):\n    \"\"\" calculates the average FScore for the predictions in an image over\n    the iou_thresholds sets.\n    predict_mask_seq: list of masks of the predicted objects in the image\n    truth_mask_seq: list of masks of ground-truth objects in the image\n    \"\"\"\n    return np.mean(\n        [fscore(tp, fn, fp, beta) for (tp, fn, fp) in \n            [confusion_counts(predict_mask_seq, truth_mask_seq, iou_thresh)\n                for iou_thresh in iou_thresholds]])","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b72b87f56065cd4ce4ca1fc5907a7cbaad0ad9f3"},"cell_type":"markdown","source":"# Segmentation training data"},{"metadata":{"scrolled":true,"trusted":true,"_uuid":"cfc623c790e7e3dc60db34bea0d6f6c77857f7bf"},"cell_type":"code","source":"print(traincsv.head())\n\ntraincsv.describe()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"7b289c7e4a09f5c475a76e8367fab451179dad16"},"cell_type":"markdown","source":"## Image statistics"},{"metadata":{"trusted":true,"_uuid":"9191b8282e02da797c2aca537fbdf288d34d9852"},"cell_type":"code","source":"print(\"Total inferrences in the train set: \" , traincsv.shape[0])\n\nid_images = traincsv.ImageId.unique()\nid_images_noships = traincsv[traincsv.EncodedPixels.isna()].ImageId.unique()\nid_images_ships = traincsv[traincsv.EncodedPixels.notna()].ImageId.unique()\n\nn_images = id_images.shape[0]\nn_images_noships = id_images_noships.shape[0]\nn_images_ships = id_images_ships.shape[0]\n\nprint(\"Total no. of images: \", n_images)\nprint(\"No. of images with no ships: \", n_images_noships)\nprint(\"No. of images with ships: \", n_images_ships)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"48dc0b2cdab9c69a94033f290a4dda4fad0c84e1"},"cell_type":"code","source":"plt.bar(['No ships', 'Ships'], [n_images_noships, n_images_ships]);\nplt.ylabel('Image count');","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4d5b732bf768353413e08f188863f8f36bd786bb"},"cell_type":"markdown","source":"**Distribution of number of objects in positive images:**"},{"metadata":{"trusted":true,"_uuid":"07cebf531cf5c9853c7d5155bdb4a1924b0a26da"},"cell_type":"code","source":"id_images_obj = traincsv.dropna().groupby('ImageId').count()\n\nid_images_obj.rename({'EncodedPixels': 'ObjCount'}, axis='columns', inplace=True)\n\nobjects = id_images_obj.ObjCount.sum()\n\nprint(\"Total No. of object: \", objects)\n\nid_images_obj.describe()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"638e08d0ac996dd4ccb57ec1547ef67287d15988"},"cell_type":"code","source":"id_images_obj.ObjCount.hist(bins=15)\nplt.xlabel('No. of objects');\nplt.ylabel('Images');","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"86fd0a797e3a757b6ea93c548ecfb2d8fe66a794"},"cell_type":"markdown","source":"**Distribution of object size (in pixel) in positive images:**"},{"metadata":{"trusted":true,"_uuid":"c9cb81ede66304f740feeab393a049f0b5a13a0b"},"cell_type":"code","source":"obj_pixels = traincsv.dropna().EncodedPixels.map(lambda x: object_pixels(x))\n\nobj_pixels.hist()\nplt.xlabel('Object size (pixels)');\nplt.ylabel('Images');\n\nobj_pixels.describe()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"9704cec88aaea3e3eca26040c2f5f4c6cebc0c0c"},"cell_type":"markdown","source":"**Distribution of object length (maximum of height and width) in positive images:**"},{"metadata":{"trusted":true,"_uuid":"4d76007d9e6a91d6c318f8b5a33384a61124a73c"},"cell_type":"code","source":"obj_maxdim = traincsv.dropna().EncodedPixels.map(lambda x: max(object_dims(x)))\n\nobj_maxdim.hist()\n\nplt.xlabel('Object length (pixels)');\nplt.ylabel('Images');\n\nobj_maxdim.describe()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"4681e41b8149edf48bcad49a6171ea4449596c01"},"cell_type":"markdown","source":"## displaying some images with masks"},{"metadata":{"_uuid":"69d5df36a4fdc27de9418cbacf428bc6b47b35ee"},"cell_type":"markdown","source":"**Starting with images where no ship appears:**"},{"metadata":{"trusted":true,"_uuid":"90c94e1f4281b207ddecb42181c08b982bc5f36e"},"cell_type":"code","source":"fig, ax = plt.subplots(4, 4, figsize=(8, 8), dpi=96)\n\nax = ax.reshape(-1)\n\nfor a in ax: \n    a.axis('off')\n\nimgids = np.random.choice(id_images_noships, 8, replace=False)\n\nfor i, imgid in enumerate(imgids):\n    msk = get_train_combined_masks(imgid)\n    ax[2*i].imshow(read_train_image(imgid))\n    ax[2*i].set_title(imgid)\n    ax[2*i+1].imshow(msk)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"e60ea1f5d119dfa3a8d1afb553ee39c2a325f99f"},"cell_type":"markdown","source":"**Now, for images with ships:**\n\nAs a test: the average F2-score is displayed for a single prediction of the combined-objects mask. The object count and the F2-score is displayed above each corresponding mask."},{"metadata":{"trusted":true,"_uuid":"2d2bc7712928ca26a023ed28400d4019297268ba"},"cell_type":"code","source":"fig, ax = plt.subplots(4, 4, figsize=(8, 8), dpi=96)\n\nax = ax.reshape(-1)\n\nfor a in ax: \n    a.axis('off')\n\nimgids = np.random.choice(id_images_ships, 8, replace=False)\n\nfor i, imgid in enumerate(imgids):\n    msk = get_train_combined_masks(imgid)\n    avg_fscore = mean_fscore([msk], get_train_masks(imgid))\n    ax[2*i].imshow(read_train_image(imgid))\n    ax[2*i].set_title(imgid)\n    ax[2*i+1].imshow(msk)\n    ax[2*i+1].set_title(\"%s[%f]\" % (get_train_objcount(imgid), avg_fscore))","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}