{"cells":[{"metadata":{},"cell_type":"markdown","source":"### Motivation\n---\nShare some ideas of preprocessing. **Note** that I haven't looked through most of the public notebooks of this competition, and thus the below might be a repetition of those notebooks!\n\n\n---\nSections (each section/part can be run separately):\n\n* [Part 1: Draw a rectangle of minimum area](#Part-1)\n* [Part 2: Multiple rectangles](#Part-2)\n* [Part 3: Utilize train_label_masks/](#Part-3)"},{"metadata":{},"cell_type":"markdown","source":"### import packages"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport pandas as pd\nimport numpy as np\nimport cv2\nimport skimage.io","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Part 1"},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_PATH = '../input/prostate-cancer-grade-assessment/train_images/'\ndata = pd.read_csv('../input/prostate-cancer-grade-assessment/train.csv')\ndata.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def read_img(ID, path, level=2):\n    image_path = path + ID + '.tiff'\n    img = skimage.io.MultiImage(image_path)\n    img = img[level]\n    return img\n\nimg = read_img(data.iloc[9].image_id, IMG_PATH)\n\n\n# plot\nplt.figure(figsize=(10, 10))\nplt.imshow(img)\nplt.title('original (level 2)', fontsize=20);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def img_to_gray(img):\n    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    return img_gray\n\nimg_gray = img_to_gray(img)\n\n\n# plot\nplt.figure(figsize=(10, 10))\nplt.imshow(img_gray)\nplt.title('original -> gray-scaled', fontsize=20);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def mask_img(img_gray, tol=240):\n    mask = (img_gray < tol).astype(np.uint8)\n    return mask\n\nmask = mask_img(img_gray)\n\n\n# plot\nplt.figure(figsize=(10, 10))\nplt.imshow(mask)\nplt.title('gray-scaled -> binary mask', fontsize=20);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def compute_rect(mask):\n    contours, _ = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n    contours = max(contours, key = cv2.contourArea) # select only the biggest rect\n    rect = cv2.minAreaRect(contours)\n    box = cv2.boxPoints(rect)\n    box = np.int0(box)\n    return rect, box\n\nrect, box = compute_rect(mask)\n\n\n# plot\nimg_with_rect = img.copy() # draw only on original img\ncv2.drawContours(img_with_rect, [box], 0, (0, 0, 0), 2)\nplt.figure(figsize=(10, 10))\nplt.imshow(img_with_rect)\nplt.title('binary mask -> get rect -> draw rect on original', fontsize=20);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def warp_perspective(img, rect, box):\n    width = int(rect[1][0])\n    height = int(rect[1][1])\n    src_pts = box.astype(\"float32\")\n    dst_pts = np.array([[0, height-1],\n                        [0, 0],\n                        [width-1, 0],\n                        [width-1, height-1]], dtype=\"float32\")\n    M = cv2.getPerspectiveTransform(src_pts, dst_pts)\n    warped = cv2.warpPerspective(img, M, (width, height))\n    return warped\n\nimg = warp_perspective(img, rect, box)\n\n\n# plot\nplt.figure(figsize=(10, 10))\nplt.imshow(img)\nplt.title('original -> rect -> warped perspective', fontsize=20);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%timeit img = read_img(data.iloc[1].image_id, IMG_PATH)\n%timeit img_gray = img_to_gray(img)\n%timeit mask = mask_img(img_gray)\n%timeit rect, box = compute_rect(mask)\n%timeit warped_img = warp_perspective(img, rect, box)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(10, 2, figsize=(10, 40))\n\nfor i, ax in enumerate(axes):\n    img = read_img(data.iloc[i].image_id, IMG_PATH)\n    img_gray = img_to_gray(img)\n    mask = mask_img(img_gray)\n    rect, box = compute_rect(mask)\n    img_with_rect = img.copy() # draw only on original\n    cv2.drawContours(img_with_rect, [box], 0, (0, 0, 0), 2)\n    warped_img = warp_perspective(img, rect, box)\n    ax[0].imshow(img_with_rect)\n    ax[1].imshow(warped_img)\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Part 2"},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_PATH = '../input/prostate-cancer-grade-assessment/train_images/'\ndata = pd.read_csv('../input/prostate-cancer-grade-assessment/train.csv')\n\ndef read_img(ID, path, level=2):\n    image_path = path + ID + '.tiff'\n    img = skimage.io.MultiImage(image_path)\n    img = img[level]\n    return img\n\ndef img_to_gray(img):\n    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n    return img_gray\n\ndef mask_img(img_gray, tol=210):\n    mask = (img_gray < tol).astype(np.uint8)\n    return mask\n","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# plots\nfig, axes = plt.subplots(3, 3, figsize=(17, 12))\n\n# first image\nimg1 = read_img(data.iloc[9].image_id, IMG_PATH)\nimg_gray1 = img_to_gray(img1)\nmask1 = mask_img(img_gray1)\n\naxes[0, 0].imshow(img1)\naxes[0, 0].axis('off');\naxes[1, 0].imshow(img_gray1)\naxes[1, 0].axis('off');\naxes[2, 0].imshow(mask1)\naxes[2, 0].axis('off');\n\n# second image\nimg2 = read_img(data.iloc[99].image_id, IMG_PATH)\nimg_gray2 = img_to_gray(img2)\nmask2 = mask_img(img_gray2)\n\naxes[0, 1].imshow(img2)\naxes[0, 1].axis('off');\naxes[1, 1].imshow(img_gray2)\naxes[1, 1].axis('off');\naxes[2, 1].imshow(mask2)\naxes[2, 1].axis('off');\n\n\n# third image\nimg3 = read_img(data.iloc[999].image_id, IMG_PATH)\nimg_gray3 = img_to_gray(img3)\nmask3 = mask_img(img_gray3)\n\naxes[0, 2].imshow(img3)\naxes[0, 2].axis('off');\naxes[1, 2].imshow(img_gray3)\naxes[1, 2].axis('off');\naxes[2, 2].imshow(mask3)\naxes[2, 2].axis('off');\n\nplt.subplots_adjust(hspace=0.0, wspace=0.0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def compute_rects(mask, min_rect=32):\n    \n    contours, _ = cv2.findContours(\n        mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n\n    height, width = mask.shape\n\n    boxes = []\n    dims = []\n    for contour in contours:\n        rect = cv2.minAreaRect(contour)\n        height = rect[1][1]\n        width = rect[1][0]\n        if width > min_rect and height > min_rect:\n            dims.append((int(width), int(height)))\n            box = cv2.boxPoints(rect)\n            box = np.int0(box)\n            boxes.append(box)\n    \n    return dims, boxes\n\ndims, boxes = compute_rects(mask1)\n\n\n# plot\nfor box in boxes:\n    cv2.drawContours(img1, [box], 0, (0, 0, 0), 2)\nplt.figure(figsize=(10, 10))\nplt.imshow(img1);\nplt.axis('off');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def warp_perspectives(img, dims, boxes):\n\n    imgs = []\n    for (width, height), box in zip(dims, boxes):\n        src_pts = box.astype(\"float32\")\n        dst_pts = np.array([[0, height-1],\n                            [0, 0],\n                            [width-1, 0],\n                            [width-1, height-1]], dtype=\"float32\")\n        M = cv2.getPerspectiveTransform(src_pts, dst_pts)\n        warped = cv2.warpPerspective(img, M, (width, height))\n        imgs.append(warped)\n    return imgs\n    \nimgs = warp_perspectives(img1, dims, boxes)\n\n\n# plot\nfig, axes = plt.subplots(1, len(imgs), figsize=(10, 10*len(imgs)))\nfor im, ax in zip(imgs, axes.reshape(-1)):\n    ax.imshow(im)\n    ax.axis('off')\nplt.subplots_adjust(hspace=0.1)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Part 3"},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_PATH = '../input/prostate-cancer-grade-assessment/train_images/'\nMASK_PATH = '../input/prostate-cancer-grade-assessment/train_label_masks/'\n\ndata = pd.read_csv('../input/prostate-cancer-grade-assessment/train.csv')\n\n\ndef read_mask(ID, path, level=2):\n    mask_path = path + ID + '_mask.tiff'\n    mask = skimage.io.MultiImage(mask_path)\n    mask = mask[level]\n    return mask[:, :, 0]\n\ndef read_img(ID, path, level=2):\n    image_path = path + ID + '.tiff'\n    img = skimage.io.MultiImage(image_path)\n    img = img[level]\n    return img\n\ndef compute_rects(mask, min_rect=32):\n    \n    contours, _ = cv2.findContours(\n        mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n\n    height, width = mask.shape\n\n    boxes = []\n    dims = []\n    for contour in contours:\n        rect = cv2.minAreaRect(contour)\n        height = rect[1][1]\n        width = rect[1][0]\n        if width > min_rect and height > min_rect:\n            dims.append((int(width), int(height)))\n            box = cv2.boxPoints(rect)\n            box = np.int0(box)\n            boxes.append(box)\n    \n    return dims, boxes\n\ndef warp_perspectives(img, dims, boxes):\n\n    imgs = []\n    for (width, height), box in zip(dims, boxes):\n        src_pts = box.astype(\"float32\")\n        dst_pts = np.array([[0, height-1],\n                            [0, 0],\n                            [width-1, 0],\n                            [width-1, height-1]], dtype=\"float32\")\n        M = cv2.getPerspectiveTransform(src_pts, dst_pts)\n        warped = cv2.warpPerspective(img, M, (width, height))\n        imgs.append(warped)\n    return imgs\n\n# leave these functions out for now\n# def img_to_gray(img):\n#     img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n#     return img_gray\n\n# def mask_img(img_gray, tol=210):\n#     mask = (img_gray < tol).astype(np.uint8)\n#     return mask","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Provider: karolinska (image level = 2)"},{"metadata":{"trusted":true},"cell_type":"code","source":"ID = data.iloc[10606].image_id\nmask = read_mask(ID, MASK_PATH)\nimg = read_img(ID, IMG_PATH)\nmask_background = np.where(mask == 0, 1, 0).astype(np.uint8)\nmask_benign = np.where(mask == 1, 1, 0).astype(np.uint8)\nmask_cancerous = np.where(mask == 2, 1, 0).astype(np.uint8)\n\nfig, axes = plt.subplots(1, 3, figsize=(12, 6))\n\naxes[0].imshow(mask_background.astype(float), cmap=plt.cm.gray)\naxes[0].axis('off')\naxes[0].set_title('background');\naxes[1].imshow(mask_benign.astype(float), cmap=plt.cm.gray)\naxes[1].axis('off')\naxes[1].set_title('benign');\naxes[2].imshow(mask_cancerous.astype(float), cmap=plt.cm.gray)\naxes[2].axis('off')\naxes[2].set_title('cancerous');\n#plt.imshow(mask.astype(np.uint8));\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# hardcoding this\ndims, boxes = compute_rects(mask_benign)\nimg_benign = warp_perspectives(img, dims, boxes)\n\ndims, boxes = compute_rects(mask_cancerous.astype(np.uint8))\nimg_cancerous = warp_perspectives(img, dims, boxes)\n\nfig, axes = plt.subplots(1, 3, figsize=(10, 8))\naxes[0].imshow(img_benign[1])\naxes[0].set_title(\"benign\")\naxes[1].imshow(img_benign[0])\naxes[1].set_title(\"benign\")\naxes[2].imshow(img_cancerous[0])\naxes[2].set_title(\"cancerous\");","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Provider: Radboud (image level = 1)"},{"metadata":{"trusted":true},"cell_type":"code","source":"ID = data.iloc[10612].image_id\nmask = read_mask(ID, MASK_PATH, level=1)\nimg = read_img(ID, IMG_PATH, level=1)\nmask_background = np.where(mask == 0, 1, 0).astype(np.uint8)\nmask_benign = np.where(mask == 1, 1, 0).astype(np.uint8)\nmask_cancerous = np.where(mask == 5, 1, 0).astype(np.uint8)\n\nfig, axes = plt.subplots(3,1, figsize=(8, 8))\n\naxes[0].imshow(mask_background.astype(float), cmap=plt.cm.gray)\naxes[0].axis('off')\naxes[0].set_title('background');\naxes[1].imshow(mask_benign.astype(float), cmap=plt.cm.gray)\naxes[1].axis('off')\naxes[1].set_title('benign');\naxes[2].imshow(mask_cancerous.astype(float), cmap=plt.cm.gray)\naxes[2].axis('off')\naxes[2].set_title('cancerous');\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# rewriting compute_rects to take into account hierarchies \ndef compute_rects(mask, min_rect=32):\n    \n    contours, hierarchy = cv2.findContours(\n        mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n\n    height, width = mask.shape\n\n    boxes = []\n    dims = []\n    for (contour, hier) in zip(contours, hierarchy[0]):\n        rect = cv2.minAreaRect(contour)\n        height = rect[1][1]\n        width = rect[1][0]\n        if width > min_rect and height > min_rect and hier[3] == -1:\n            dims.append((int(width), int(height)))\n            box = cv2.boxPoints(rect)\n            box = np.int0(box)\n            boxes.append(box)\n    \n    return dims, boxes\n\ndims, boxes = compute_rects(mask_cancerous, min_rect=32)\n\n\n# plot\nimg_with_rects = img.copy()\nmask_cancerous_with_rects = mask_cancerous.copy()\nfor box in boxes:\n    cv2.drawContours(mask_cancerous_with_rects, [box], 0, 1, 5)\n    cv2.drawContours(img_with_rects, [box], 0, 1, 5)\n\nfig, axes = plt.subplots(1,2, figsize=(20, 20))\naxes[0].imshow(mask_cancerous_with_rects)\naxes[0].axis('off')\naxes[0].set_title('Too many rectangles are drawn and they are also too small')\naxes[1].imshow(img_with_rects)\naxes[1].axis('off');\naxes[1].set_title('Original');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def dilate_mask(mask, kernel_size=0.015, iterations=5):\n    # 'kernel_size' and 'iterations' needs finetunning\n    h, w = mask.shape\n    kernel_size = int(min(h, w) * kernel_size)\n    kernel = np.ones((kernel_size, kernel_size), np.uint8)\n    dilated_mask = cv2.dilate(mask, kernel, iterations=iterations)\n    return dilated_mask\n\nmask_cancerous_dilated = dilate_mask(mask_cancerous)\n\n# plot\nfig, axes = plt.subplots(3, 1, figsize=(10, 10))\naxes[0].imshow(img)\naxes[0].axis('off')\naxes[0].set_title(\"original\")\naxes[1].imshow(mask_cancerous)\naxes[1].axis('off')\naxes[1].set_title(\"mask\")\naxes[2].imshow(mask_cancerous_dilated)\naxes[2].axis('off')\naxes[2].set_title(\"mask dilated\");","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# rewriting compute_rects to take into account hierarchies \ndef compute_rects(mask, min_rect=32):\n    \n    contours, hierarchy = cv2.findContours(\n        mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n\n    height, width = mask.shape\n\n    boxes = []\n    dims = []\n    for (contour, hier) in zip(contours, hierarchy[0]):\n        rect = cv2.minAreaRect(contour)\n        height = rect[1][1]\n        width = rect[1][0]\n        if width > min_rect and height > min_rect and hier[3] == -1:\n            dims.append((int(width), int(height)))\n            box = cv2.boxPoints(rect)\n            box = np.int0(box)\n            boxes.append(box)\n    \n    return dims, boxes\n\ndims, boxes = compute_rects(mask_cancerous_dilated, min_rect=32)\n\n\n# plot\nimg_with_rects = img.copy()\nfor box in boxes:\n    cv2.drawContours(mask_cancerous_dilated, [box], 0, 1, 5)\n    cv2.drawContours(img_with_rects, [box], 0, 1, 5)\n\nfig, axes = plt.subplots(1,2, figsize=(20, 20))\naxes[0].imshow(mask_cancerous_dilated)\naxes[0].axis('off')\naxes[0].set_title('Still too many rectangles?')\naxes[1].imshow(img_with_rects)\naxes[1].axis('off');\naxes[1].set_title('Original');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# let's rewrite dilate_mask to remove the \"spurious\" islands\ndef erode_dilate_mask(mask, \n                      kernel_size_closing=0.015, \n                      kernel_size_opening=0.025,\n                      kernel_size_final_dilate=0.025,\n                      final_dilate_iterations=5):\n    \n    \"\"\"\n    This implementation is likely far from perfect and probably also overly complicated.\n    \n    skimage.morphology.remove_small_objects could, in one line of code,\n    also be used to filter out some small islands. \n    \n    Note:\n      'kernel_size_closing', 'kernel_size_opening', 'kernel_size_final_dilate' and \n      'final_dilate_iterations' all needs to be carefully experimented with and finetuned\n    \"\"\"\n    \n\n    h, w = mask.shape\n    \n    # the bigger the closing, the more we keep islands that are sufficiently big\n    kernel_size_closing = int(min(h, w) * kernel_size_closing)\n    # the bigger the opening, the more we remove islands of bigger sizes\n    kernel_size_opening = int(min(h, w) * kernel_size_opening) \n    # the bigger the final dilate, the bigger rectangles we get\n    kernel_size_final_dilate = int(min(h, w) * kernel_size_final_dilate) \n    \n    # obtain elliptic kernel\n    # e.g. np.ones((kernel_size, kernel_size) could also be used as a kernel\n    kernel_closing = cv2.getStructuringElement(\n        cv2.MORPH_ELLIPSE, (kernel_size_closing, kernel_size_closing))\n    kernel_opening = cv2.getStructuringElement(\n        cv2.MORPH_ELLIPSE, (kernel_size_opening, kernel_size_opening))\n    kernel_final_dilate = cv2.getStructuringElement(\n        cv2.MORPH_ELLIPSE, (kernel_size_final_dilate, kernel_size_final_dilate))\n    \n    # cv2.MORPH_CLOSE = erode -> dilation\n    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel_closing)\n    # cv2.MORPH_OPEN = dilation -> erode\n    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_opening)\n    # final dilate\n    mask = cv2.dilate(mask, kernel_final_dilate, iterations=final_dilate_iterations)\n    \n    return mask\n\nmask_cancerous_dilated = erode_dilate_mask(mask_cancerous)\n\n# plot\nfig, axes = plt.subplots(3, 1, figsize=(10, 10))\naxes[0].imshow(img)\naxes[0].axis('off')\naxes[0].set_title(\"original\")\naxes[1].imshow(mask_cancerous)\naxes[1].axis('off')\naxes[1].set_title(\"mask\")\naxes[2].imshow(mask_cancerous_dilated)\naxes[2].axis('off')\naxes[2].set_title(\"mask eroded + dilated\");","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dims, boxes = compute_rects(mask_cancerous_dilated, min_rect=32)\n\n# plot\nimg_with_rects = img.copy()\nfor box in boxes:\n    cv2.drawContours(mask_cancerous_dilated, [box], 0, 1, 10)\n    cv2.drawContours(img_with_rects, [box], 0, 1, 5)\n\nfig, axes = plt.subplots(1,2, figsize=(14, 8))\naxes[0].imshow(mask_cancerous_dilated)\naxes[0].axis('off')\naxes[0].set_title('Now only two rectangles are drawn')\naxes[1].imshow(img_with_rects)\naxes[1].axis('off');\naxes[1].set_title('Original');","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"dims, boxes = compute_rects(mask_cancerous_dilated, min_rect=32)\n\nimgs = warp_perspectives(img, dims, boxes)\n\nfig, axes = plt.subplots(1, len(imgs), figsize=(8 * len(imgs), 8))\n\nif isinstance(axes, np.ndarray):\n    axes = axes.reshape(-1)\nelse:\n    axes = [axes]\n    \nfor ax, img in zip(axes, imgs):\n    ax.imshow(img)\n    ax.axis('off')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}