{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Slicing blood clots with OpenCV\n\n![A test image](https://storage.googleapis.com/kaggle-competitions/kaggle/37333/logos/header.png?t=2022-06-2)","metadata":{}},{"cell_type":"markdown","source":"This notebook focuses on showing a quick way to slice the blood clot images into smaller segments, ***without image reducing***, ***without image quality loss***, ***AND without much runtime***.","metadata":{}},{"cell_type":"markdown","source":"# Libraries","metadata":{}},{"cell_type":"code","source":"!pip install imutils\n\n\nimport os\nimport imutils\nimport matplotlib.pyplot as plt\nimport tifffile as tiff\nimport numpy","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-07-14T18:46:21.027886Z","iopub.execute_input":"2022-07-14T18:46:21.028571Z","iopub.status.idle":"2022-07-14T18:46:34.021833Z","shell.execute_reply.started":"2022-07-14T18:46:21.028473Z","shell.execute_reply":"2022-07-14T18:46:34.020144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### **Important line of code**\n\nthis will allow opencv to open these huge tiff files without running into an error","metadata":{}},{"cell_type":"code","source":"os.environ[\"OPENCV_IO_MAX_IMAGE_PIXELS\"] = str(pow(2,100))\nimport cv2","metadata":{"execution":{"iopub.status.busy":"2022-07-14T18:46:34.024103Z","iopub.execute_input":"2022-07-14T18:46:34.024663Z","iopub.status.idle":"2022-07-14T18:46:34.031434Z","shell.execute_reply.started":"2022-07-14T18:46:34.024614Z","shell.execute_reply":"2022-07-14T18:46:34.030142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Inspiration\n\nI have seen some people using a classification algorithm to remove the white noise from the blood clot images. Although it is a very viable method I feel it maybe overkill, and since I have been working with a Captcha Reader project, where I needed to [separate letters](https://www.kaggle.com/code/iuryck/captcha-image-preprocessing) to feed each one to a ConvNet, I thought I could use the same methods for this dataset and here is what I got.","metadata":{}},{"cell_type":"markdown","source":"# Method\n\nWe will be using OpenCV's ***findContour*** method to slice up our blood clots and remove all the white noise, with this method we will get alot of zoomed-in pieces of our blood clots and might just make a better dataset over all.\n\nFrom left to right, we have our original image, then transform it into binary, and on that image we run findContours. The result is on the right, where all the reddish pixels are contours","metadata":{}},{"cell_type":"code","source":"#Append images for presentation\nimages=[]\n\n#Reading a image from the dataset\nimg = cv2.imread('../input/mayo-clinic-strip-ai/train/008e5c_0.tif')\n\nprint('IMAGE SIZE: '+str(img.shape[:2]))\n\n\n#Append images for presentation\nimages.append(img)\n\n#Transforming from RGB to GrayScale, preserving original image\nimg_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) \n\n\n\n# apply binary thresholding\nret, thresh = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)\n\n#Append Binary Image\nimages.append(thresh)\n\nthresh = 255-thresh\n\n# detect the contours on the binary image using cv2.CHAIN_APPROX_NONE\ncontours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)\n                                     \n# draw contours on copy of the original image\nimg_copy = img.copy()\ncv2.drawContours(image=img_copy, contours=contours, contourIdx=-1, color=(255, 0, 0), thickness=1, lineType=cv2.LINE_AA)\n\n#Append images for presentation\nimages.append(img_copy)\n\n\n#Showing steps in contour drawing\nplt.figure(figsize=(32,32))\nfor i, img in enumerate(images):\n    \n    plt.subplot(2,5,i+1)\n    plt.grid(False)\n    plt.imshow(img)\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-07-14T18:46:34.032812Z","iopub.execute_input":"2022-07-14T18:46:34.033142Z","iopub.status.idle":"2022-07-14T18:47:19.507035Z","shell.execute_reply.started":"2022-07-14T18:46:34.033114Z","shell.execute_reply":"2022-07-14T18:47:19.505764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del img\ndel img_copy\ndel images\ndel img_gray\ndel thresh","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-07-14T18:47:19.509869Z","iopub.execute_input":"2022-07-14T18:47:19.510462Z","iopub.status.idle":"2022-07-14T18:47:19.521859Z","shell.execute_reply.started":"2022-07-14T18:47:19.510428Z","shell.execute_reply":"2022-07-14T18:47:19.52061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This is how long the code takes to get the slices on this particular image:","metadata":{}},{"cell_type":"code","source":"%%time\n\nimg = cv2.imread('../input/mayo-clinic-strip-ai/train/008e5c_0.tif')\n\nimg_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) \n\n\n\n# apply binary thresholding\nret, thresh = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)\n\nthresh = 255-thresh\n# detect the contours on the binary image using cv2.CHAIN_APPROX_NONE\ncontours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)\ncontours = sorted(contours, key=cv2.contourArea, reverse=True)\n\nclots = []\n\n# Find bounding box \nfor c in contours:\n    x,y,w,h = cv2.boundingRect(c)\n    area = cv2.contourArea(c)\n    if area>5000 and area<20000:\n        clot = img[y:y+h, x:x+w]\n        clots.append(clot)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-07-14T18:47:19.523862Z","iopub.execute_input":"2022-07-14T18:47:19.524256Z","iopub.status.idle":"2022-07-14T18:47:25.339062Z","shell.execute_reply.started":"2022-07-14T18:47:19.524218Z","shell.execute_reply":"2022-07-14T18:47:25.337674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Code and Visualization","metadata":{}},{"cell_type":"markdown","source":"Let us see how it goes on a 1.31gb file","metadata":{"execution":{"iopub.status.busy":"2022-07-14T03:10:40.977055Z","iopub.execute_input":"2022-07-14T03:10:40.977462Z","iopub.status.idle":"2022-07-14T03:10:40.986404Z","shell.execute_reply.started":"2022-07-14T03:10:40.977428Z","shell.execute_reply":"2022-07-14T03:10:40.984491Z"}}},{"cell_type":"code","source":"%%time\n#Reading image\nimg = tiff.imread('../input/mayo-clinic-strip-ai/train/006388_0.tif')\n\nimg_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) \n\nprint('IMAGE SIZE: '+str(img.shape[:2]))\n\n# apply binary thresholding\nret, thresh = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)\n\nthresh = 255-thresh\n# detect the contours on the binary image using cv2.CHAIN_APPROX_NONE\ncontours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)\ncontours = sorted(contours, key=cv2.contourArea, reverse=True)\n\nclots = []\n\n# Find bounding box \nfor c in contours:\n    x,y,w,h = cv2.boundingRect(c)\n    area = cv2.contourArea(c)\n    if area>5000:\n        clot = img[y:y+h, x:x+w]\n        clots.append(clot)","metadata":{"execution":{"iopub.status.busy":"2022-07-14T18:47:25.34043Z","iopub.execute_input":"2022-07-14T18:47:25.340746Z","iopub.status.idle":"2022-07-14T18:48:15.421857Z","shell.execute_reply.started":"2022-07-14T18:47:25.340712Z","shell.execute_reply":"2022-07-14T18:48:15.420531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('TOTAL SLICES: '+str(len(clots)))  \nplt.figure(figsize=(32,32))\nfor i, clot in enumerate(clots):\n   \n    \n    try:\n        plt.subplot(10,10,i+1)\n        plt.grid(False)\n        plt.imshow(clot)\n    except:pass","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-07-14T18:48:15.423223Z","iopub.execute_input":"2022-07-14T18:48:15.423571Z","iopub.status.idle":"2022-07-14T18:49:37.328481Z","shell.execute_reply.started":"2022-07-14T18:48:15.423539Z","shell.execute_reply":"2022-07-14T18:49:37.326975Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\ngc.collect()\ndel img\ndel img_gray\ndel thresh\ndel clots\ndel contours","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2022-07-14T18:49:37.329798Z","iopub.execute_input":"2022-07-14T18:49:37.330146Z","iopub.status.idle":"2022-07-14T18:49:37.929856Z","shell.execute_reply.started":"2022-07-14T18:49:37.330116Z","shell.execute_reply":"2022-07-14T18:49:37.928713Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Making a function to experiment around\n\nThe function will get the path to the image and a lower limit to determine the smallest a contour can be when you get them back. The higher limit will determine the biggest a contour can get but by default is set to a huge number. This way you can play around slicing in bigger and smaller sizes.\n\n**You can also choose to crop the areas in a square of size X by X with *crop_square***. You can use this to make a dataset right off the bat","metadata":{}},{"cell_type":"code","source":" def get_slices(img_path, lower_limit, higher_limit=999999999999, zoom_out=0, crop_square=0):\n    #Reading image\n    if '.tif' in img_path:\n        img = tiff.imread(img_path)\n    else:\n        img = cv2.imread(img_path)\n\n    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) \n    \n    width = img.shape[1]\n    height = img.shape[0]\n\n\n    # apply binary thresholding\n    ret, thresh = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)\n\n    thresh = 255-thresh\n    # detect the contours on the binary image using cv2.CHAIN_APPROX_NONE\n    contours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)\n    contours = sorted(contours, key=cv2.contourArea, reverse=True)\n\n    clots = []\n    \n    zoom_out_x1  = zoom_out\n    zoom_out_x0  = zoom_out\n    zoom_out_y0  = zoom_out\n    zoom_out_y1  = zoom_out\n\n    \n    # Find bounding box \n    for c in contours:\n        x,y,w,h = cv2.boundingRect(c)\n        area = cv2.contourArea(c)\n        \n        \n        \n        \n        \n        \n        if area>lower_limit and area<higher_limit:\n            \n            if crop_square>0:\n                \n                \n                c_height = (y+h)-(y)\n                c_witdth = (x+w)-(x)\n                \n                center_y = int(c_height/2)\n                center_x = int(c_witdth/2)\n                \n                center_y = (center_y + y)\n                center_x = (center_x + x)\n                zoom_out = int(crop_square/2)\n                zoom_out_x1  = zoom_out\n                zoom_out_x0  = zoom_out\n                zoom_out_y0  = zoom_out\n                zoom_out_y1  = zoom_out\n                \n                if center_y - zoom_out_y0 < 0: \n                    zoom_out_y1 = zoom_out_y1 - (center_y-zoom_out_y0)\n                    zoom_out_y0 = center_y\n                    \n                else:pass\n                \n                if center_y + zoom_out_y1 > height:\n                    zoom_out_y1 = height - (center_y)\n                    zoom_out_y0 = zoom_out_y0 + (zoom_out-zoom_out_y1)\n                else:pass\n                \n                if center_x - zoom_out_x0<0:\n                    zoom_out_x1 = zoom_out_x1 - (center_x-zoom_out_x0)\n                    zoom_out_x0 = center_x\n                else:pass\n                \n                if center_x+zoom_out > width:\n                    zoom_out_x1 = width - (center_x)\n                    zoom_out_x0 = zoom_out_x0 + (zoom_out-zoom_out_x1)\n                else:pass\n                \n                \n                clot = img[center_y-zoom_out_y0:center_y+zoom_out_y1, center_x-zoom_out_x0:center_x+zoom_out_x1]\n                \n            \n            else:\n                \n                \n                if y-zoom_out_y0 < 0: \n                    zoom_out_y1 = zoom_out_y1 - (y-zoom_out_y0)\n                    zoom_out_y0 = y\n                elif y+h+zoom_out_y1 > height:\n                    zoom_out_y1 = height - (y+h)\n                    zoom_out_y0 = zoom_out_y0 + (zoom_out-zoom_out_y1)\n\n                elif x-zoom_out_x0<0:\n                    zoom_out_x1 = zoom_out_x1 - (x-zoom_out_x0)\n                    zoom_out_x0 = x\n                elif x+w+zoom_out > width:\n                    zoom_out_x1 = width - (x+w)\n                    zoom_out_x0 = zoom_out_x0 + (zoom_out-zoom_out_x1)\n\n\n                clot = img[y-zoom_out_y0:y+h+zoom_out_y1, x-zoom_out_x0:x+w+zoom_out_x1]\n            \n        \n            \n            if clot.size==0:\n                clot = img[y:y+h, x:x+w]\n                \n        else: continue\n                \n        \n                \n        clots.append(clot)\n            \n    return clots","metadata":{"execution":{"iopub.status.busy":"2022-07-14T18:49:37.93216Z","iopub.execute_input":"2022-07-14T18:49:37.932641Z","iopub.status.idle":"2022-07-14T18:49:37.95056Z","shell.execute_reply.started":"2022-07-14T18:49:37.932598Z","shell.execute_reply":"2022-07-14T18:49:37.949365Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Getting Smaller Slices","metadata":{}},{"cell_type":"code","source":"clots = get_slices('../input/mayo-clinic-strip-ai/train/008e5c_0.tif', 5000, 7000)\n\nprint('TOTAL SLICES: '+str(len(clots)))  \nplt.figure(figsize=(32,32))\nfor i, clot in enumerate(clots):\n   \n    \n    try:\n        plt.subplot(10,10,i+1)\n        plt.grid(False)\n        plt.imshow(clot)\n    except:pass","metadata":{"execution":{"iopub.status.busy":"2022-07-14T18:49:37.955111Z","iopub.execute_input":"2022-07-14T18:49:37.955478Z","iopub.status.idle":"2022-07-14T18:49:45.266515Z","shell.execute_reply.started":"2022-07-14T18:49:37.955448Z","shell.execute_reply":"2022-07-14T18:49:45.265253Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Getting Bigger Slices","metadata":{}},{"cell_type":"code","source":"clots = get_slices('../input/mayo-clinic-strip-ai/train/008e5c_0.tif', 10000)\n\nprint('TOTAL SLICES: '+str(len(clots)))  \nplt.figure(figsize=(32,32))\nfor i, clot in enumerate(clots):\n   \n    \n    try:\n        plt.subplot(10,10,i+1)\n        plt.grid(False)\n        plt.imshow(clot)\n    except:pass","metadata":{"execution":{"iopub.status.busy":"2022-07-14T18:49:45.26822Z","iopub.execute_input":"2022-07-14T18:49:45.268567Z","iopub.status.idle":"2022-07-14T18:49:59.679427Z","shell.execute_reply.started":"2022-07-14T18:49:45.268535Z","shell.execute_reply":"2022-07-14T18:49:59.677091Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Cropping to Equal Squares\n\nWith crop square argument you can pass a size of square, where all images will be cropped to fit.\n\n**NOTE: This CROPS the image, it does NOT do any scaling, so image aspect is preserved**","metadata":{}},{"cell_type":"code","source":"clots = get_slices('../input/mayo-clinic-strip-ai/train/008e5c_0.tif', 10000, crop_square=1000)#Squares of 1000x1000\n\nprint('TOTAL SLICES: '+str(len(clots)))  \nplt.figure(figsize=(32,32))\nfor i, clot in enumerate(clots):\n   \n    \n    try:\n        plt.subplot(10,10,i+1)\n        plt.grid(False)\n        plt.imshow(clot)\n    except:pass","metadata":{"execution":{"iopub.status.busy":"2022-07-14T18:49:59.681721Z","iopub.execute_input":"2022-07-14T18:49:59.682145Z","iopub.status.idle":"2022-07-14T18:50:17.445988Z","shell.execute_reply.started":"2022-07-14T18:49:59.682109Z","shell.execute_reply":"2022-07-14T18:50:17.443114Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Checking how many images are NOT 1000x1000","metadata":{}},{"cell_type":"code","source":"shapes = []\nfor clot in clots:\n    shapes.append(clot.shape[:2])\n    \n[shape==(1000,1000) for shape in shapes].count(False)","metadata":{"execution":{"iopub.status.busy":"2022-07-14T18:50:17.447581Z","iopub.execute_input":"2022-07-14T18:50:17.448014Z","iopub.status.idle":"2022-07-14T18:50:17.45664Z","shell.execute_reply.started":"2022-07-14T18:50:17.44798Z","shell.execute_reply":"2022-07-14T18:50:17.455191Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Running On Whole Dataset\n\nTo do this for the whole dataset you will have to run this small function in a .py file and use CMD code to loop through the data. If you were to loop through in a python file you will most likely run out of memory. Running in a batch would automatically dump any data in memory after every loop, and with such, saves memory.","metadata":{}},{"cell_type":"markdown","source":"Hope this helps anyone and if so, please upvote the notebook :)","metadata":{}}]}