{"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":"Hi everyone, \n\nIn this notebook, I would like to show you how we can extract the breast only part from the input images by using OpenCV and [Otsu's thresholding](https://en.wikipedia.org/wiki/Otsu%27s_method).\nSource code of this repository can be found here: https://github.com/Guarouba/breast_mass_detection\n\nI am using [@radek1](https://www.kaggle.com/radek1)'s https://www.kaggle.com/competitions/rsna-breast-cancer-detection/discussion/369762 processed dataset.\n\nLet's start with the imports:","metadata":{}},{"cell_type":"code","source":"import glob\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\n\n%matplotlib inline","metadata":{"execution":{"iopub.status.busy":"2022-12-01T12:58:41.969465Z","iopub.execute_input":"2022-12-01T12:58:41.969973Z","iopub.status.idle":"2022-12-01T12:58:41.978238Z","shell.execute_reply.started":"2022-12-01T12:58:41.969932Z","shell.execute_reply":"2022-12-01T12:58:41.976952Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Here we are reading the image paths. \ntrain_images = glob.glob(\"../input/rsna-mammography-images-as-pngs/images_as_pngs_1024/train_images_processed_1024/*/*\")","metadata":{"execution":{"iopub.status.busy":"2022-12-01T12:49:35.00671Z","iopub.execute_input":"2022-12-01T12:49:35.007837Z","iopub.status.idle":"2022-12-01T12:50:26.431743Z","shell.execute_reply.started":"2022-12-01T12:49:35.007768Z","shell.execute_reply":"2022-12-01T12:50:26.42996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_images[:10]","metadata":{"execution":{"iopub.status.busy":"2022-12-01T12:50:26.434059Z","iopub.execute_input":"2022-12-01T12:50:26.434551Z","iopub.status.idle":"2022-12-01T12:50:26.442988Z","shell.execute_reply.started":"2022-12-01T12:50:26.434513Z","shell.execute_reply":"2022-12-01T12:50:26.4418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def crop_coords(img):\n    \"\"\"\n    Crop ROI from image.\n    \"\"\"\n    # Otsu's thresholding after Gaussian filtering\n    blur = cv2.GaussianBlur(img, (5, 5), 0)\n    _, breast_mask = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)\n    \n    cnts, _ = cv2.findContours(breast_mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n    cnt = max(cnts, key = cv2.contourArea)\n    x, y, w, h = cv2.boundingRect(cnt)\n    return (x, y, w, h)\n\n\ndef truncation_normalization(img):\n    \"\"\"\n    Clip and normalize pixels in the breast ROI.\n    @img : numpy array image\n    return: numpy array of the normalized image\n    \"\"\"\n    Pmin = np.percentile(img[img!=0], 5)\n    Pmax = np.percentile(img[img!=0], 99)\n    truncated = np.clip(img,Pmin, Pmax)  \n    normalized = (truncated - Pmin)/(Pmax - Pmin)\n    normalized[img==0]=0\n    return normalized\n\n\ndef clahe(img, clip):\n    \"\"\"\n    Image enhancement.\n    @img : numpy array image\n    @clip : float, clip limit for CLAHE algorithm\n    return: numpy array of the enhanced image\n    \"\"\"\n    clahe = cv2.createCLAHE(clipLimit=clip)\n    cl = clahe.apply(np.array(img*255, dtype=np.uint8))\n    return cl","metadata":{"execution":{"iopub.status.busy":"2022-12-01T13:13:06.652036Z","iopub.execute_input":"2022-12-01T13:13:06.652695Z","iopub.status.idle":"2022-12-01T13:13:06.666588Z","shell.execute_reply.started":"2022-12-01T13:13:06.652657Z","shell.execute_reply":"2022-12-01T13:13:06.665026Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here we crop the rectangle from the image by using Gaussian Blur and thresholding and save them for the final processing.","metadata":{}},{"cell_type":"code","source":"_, axs = plt.subplots(2, 5, figsize=(16, 8))\naxs = axs.flatten()\nimages = []\nfor img_path, ax in zip(train_images[:10], axs):\n    img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)\n    (x, y, w, h) = crop_coords(img)\n    # Create a Rectangle patch\n    rect = patches.Rectangle((x, y), w, h, linewidth=1, edgecolor='r', facecolor='none')\n    # Add the patch to the Axes\n    ax.add_patch(rect)\n    img_cropped = img[y:y+h, x:x+w]\n    images.append(img_cropped)\n    ax.imshow(img, cmap=\"bone\")\n\nplt.savefig(\"rectangles.png\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-12-01T13:13:07.544616Z","iopub.execute_input":"2022-12-01T13:13:07.545966Z","iopub.status.idle":"2022-12-01T13:13:11.37496Z","shell.execute_reply.started":"2022-12-01T13:13:07.545908Z","shell.execute_reply":"2022-12-01T13:13:11.373899Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"And we apply normalization and enhance the contrast of the image. ","metadata":{}},{"cell_type":"code","source":"_, axs = plt.subplots(2, 5, figsize=(16, 8))\naxs = axs.flatten()\nfinal_imgs = []\nIMG_SIZE = 512\nfor img_cropped, ax in zip(images, axs):\n    img_normalized = truncation_normalization(img_cropped)\n    # Enhancing the contrast of the image.\n    cl1 = clahe(img_normalized, 1.0)\n    cl2 = clahe(img_normalized, 2.0)\n    img_final = cv2.merge((np.array(img_normalized*255, dtype=np.uint8),cl1,cl2))\n    # Resize the image to the final shape. \n    img_final = cv2.resize(img_final, (IMG_SIZE, IMG_SIZE))\n    ax.imshow(img_final)\n    \nplt.savefig(\"final_imgs.png\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-12-01T13:14:39.831273Z","iopub.execute_input":"2022-12-01T13:14:39.831766Z","iopub.status.idle":"2022-12-01T13:14:42.260443Z","shell.execute_reply.started":"2022-12-01T13:14:39.831731Z","shell.execute_reply":"2022-12-01T13:14:42.258819Z"},"trusted":true},"execution_count":null,"outputs":[]}]}