{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"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\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n'''for dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n'''\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# What files do I have?\n\n- train.csv - contains image IDs, binary labels, and patient IDs.\n- sample_submission.csv - a sample submission file in the correct format\n- test - test images\n- train - training images\n\n\n# What do the columns mean?\n\n\n- StudyInstanceUID - unique ID for each image\n- ETT - Abnormal - endotracheal tube placement abnormal\n- ETT - Borderline - endotracheal tube placement borderline abnormal\n- ETT - Normal - endotracheal tube placement normal\n- NGT - Abnormal - nasogastric tube placement abnormal\n- NGT - Borderline - nasogastric tube placement borderline abnormal\n- NGT - Incompletely Imaged - nasogastric tube placement inconclusive due to imaging\n- NGT - Normal - nasogastric tube placement borderline normal\n- CVC - Abnormal - central venous catheter placement abnormal\n- CVC - Borderline - central venous catheter placement borderline abnormal\n- CVC - Normal - central venous catheter placement normal\n- Swan Ganz Catheter Present\n- PatientID - unique ID for each patient in the dataset\n"},{"metadata":{},"cell_type":"markdown","source":"# Some domain knowledge\n\n- Endotrachial Tube: An endotracheal tube is a flexible plastic tube that is placed **through the mouth** into the trachea (windpipe) to help a patient breathe. The endotracheal tube is then connected to a ventilator, which delivers oxygen to the lungs\n\n- Nasogastric Tube: A nasogastric (NG) tube is a flexible tube of rubber or plastic that is passed **through the nose**, down through the esophagus, and into the stomach.\n\n- A central venous catheter is a thin, flexible tube that is **inserted into a vein**, usually below the right collarbone, and guided (threaded) into a large vein above the right side of the heart called the superior vena cava.\n\n- Swanz Ganz Catheter: Swan-Ganz catheterization is the passing of a thin tube (catheter) **into the right side of the heart and the arteries leading to the lungs**. It is done to monitor the heart's function and blood flow and pressures in and around the heart. This test is most often done in people who are very ill.\n\n\n# Normal-Borderline-Abnormal\n\n- Normal: Catheter placement was proper\n- Borderline: Catheter placement needed repositioning but worked normally\n- Abnormal: Completely abnormal"},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\n\ntrain = pd.read_csv(\"../input/ranzcr-clip-catheter-line-classification/train.csv\")\ntrain.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\ndef plot_categorical(df,cat_names):\n    fig,ax = plt.subplots(3,4,figsize=(12,8))\n    for ii,feature in enumerate(cat_names):\n        sns.countplot(data=df,x=feature,ax=ax[ii//4][ii%4])\n    \n    plt.tight_layout()\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train.columns","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_categorical(train,[\"ETT - Abnormal\",\"ETT - Borderline\",\"ETT - Normal\",\"NGT - Abnormal\",\"NGT - Borderline\",\n                        \"NGT - Incompletely Imaged\",\"NGT - Normal\",\"CVC - Abnormal\",\"CVC - Borderline\",\"CVC - Normal\",\n                       \"Swan Ganz Catheter Present\"])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can see that there is a heavy class imbalance in almost all the features"},{"metadata":{},"cell_type":"markdown","source":"Now let's plot some X-Rays and see what data we have"},{"metadata":{"trusted":true},"cell_type":"code","source":"import cv2\nimport matplotlib.image as mpimg\ndef read_n_plot_image(filenames,rows,cols,figsize=(10,10)):\n    fig,ax = plt.subplots(rows,cols,figsize=figsize)\n\n    for ii,filename in enumerate(filenames):\n        filename = \"../input/ranzcr-clip-catheter-line-classification/train/\"+filename+\".jpg\"\n        img = mpimg.imread(filename)\n        \n        ax[ii//cols][ii%cols].imshow(np.array(img),cmap=\"gray\")\n\n    plt.tight_layout()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"var_df = train[\"StudyInstanceUID\"].values[:12]\nread_n_plot_image(var_df,4,3,(15,15))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"It can be clearly seen in just the first 12 images that there exist images in which the catheters cannot be seen due to a haze or a foggy effect in the image. This usually happens due to improper illumination. There are several techniques to solve this problems, some of which are implemented below."},{"metadata":{"trusted":true},"cell_type":"code","source":"import cv2\ndef sharpen_image(image):\n    #image = cv2.imread(img_name)\n    sharpen_kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])\n    sharpen = cv2.filter2D(image, -1, sharpen_kernel)\n    return sharpen\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# A. Histogram Equalization\n\nIn this method we transform the image such that the distribution of the intensity values becomes more uniform throughout the image. As a result, the foggy effect produced due to high number of pixels in the white range, does not overpower the image after the transformation and gives us a clear view of the catheters in this case. Wikipedia gives a great expalanation of the same: https://en.wikipedia.org/wiki/Histogram_equalization\n\n# B. Contrast Limited Adaptive Histogram Equalization (CLAHE)\n\nIf histogram equalization is done globally, some areas which have a higher contrast than others may get unnecessarily dark. Hence CLAHE is a method which applies histogram equalization to small windows instead thus is a form of local histogram equalization. Due to this areas are equalized aptly. You can read about it more in OpenCV's documentation: https://docs.opencv.org/master/d5/daf/tutorial_py_histogram_equalization.html"},{"metadata":{"trusted":true},"cell_type":"code","source":"def dehaze_img(img_name):\n    '''Applies histogram equalization and CLAHE to an image\n    \n    \n    Input: img_name (str): Name of the image\n    Output: dst1 (np.ndarray): CLAHE applied image\n            dst2 (np.ndarray): Global HE applied image\n    '''\n    image = cv2.imread(img_name)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n    clahe = cv2.createCLAHE(clipLimit=15.0, tileGridSize=(10,10))\n    dst1 = clahe.apply(image)\n    dst2 = cv2.equalizeHist(image)\n    return dst1,dst2\n\n\n\n\ndef dehaze_df(filenames,rows):\n    '''Applies the above two algorithms to a series\n    \n        Input: filenames(pd.Series): Series consisting of study instance ids\n               rows (int): Number of rows for plotting\n        \n        Output: Plots of each of the filenames\n               \n    '''\n    fig,ax = plt.subplots(rows,3,figsize=(30,30))\n    for ii,img_name in enumerate(filenames):\n        img_name =  \"../input/ranzcr-clip-catheter-line-classification/train/\"+img_name+\".jpg\"\n        ax[ii][0].imshow(cv2.imread(img_name))\n        \n        i1,i2 = dehaze_img(img_name)\n        ax[ii][1].imshow(i1,cmap=\"gray\")\n        \n\n        ax[ii][2].imshow(i2,cmap=\"gray\")\n        \n        \n        if ii==0:\n            ax[ii][0].set_title(\"Original\")\n            ax[ii][1].set_title(\"Dehazed CLAHE\")\n            ax[ii][2].set_title(\"Dehazed Global\")\n    plt.tight_layout()\n    plt.show()\nfilenames = train[\"StudyInstanceUID\"][:5]\ndehaze_df(filenames,5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As we can see Histogram equalization makes the catheters much more clear and probably will for the networks too"},{"metadata":{},"cell_type":"markdown","source":"# Retinex Based Methods\n\n- In these methods, the image is considered to be composed of two different components, a reflection component and an illumination component.\n- The hazy effect is a part of the illumination component. Hence if we get rid of this component, we have practivally removed the fog.\n- More about it here: http://html.rhhz.net/ieee-jas/html/2017-3-410.htm\n- The below code is an implementation of Retinex based image restoration. The credit for the code goes to Masato Tamura. You can check out his repo here: https://github.com/dongb5/Retinex\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport cv2\n\ndef singleScaleRetinex(img, sigma):\n\n    retinex = np.log10(img) - np.log10(cv2.GaussianBlur(img, (0, 0), sigma))\n\n    return retinex\n\ndef multiScaleRetinex(img, sigma_list):\n\n    retinex = np.zeros_like(img)\n    for sigma in sigma_list:\n        retinex += singleScaleRetinex(img, sigma)\n\n    retinex = retinex / len(sigma_list)\n\n    return retinex\n\ndef colorRestoration(img, alpha, beta):\n\n    img_sum = np.sum(img, axis=2, keepdims=True)\n\n    color_restoration = beta * (np.log10(alpha * img) - np.log10(img_sum))\n\n    return color_restoration\n\ndef simplestColorBalance(img, low_clip, high_clip):    \n\n    total = img.shape[0] * img.shape[1]\n    for i in range(img.shape[2]):\n        unique, counts = np.unique(img[:, :, i], return_counts=True)\n        current = 0\n        for u, c in zip(unique, counts):            \n            if float(current) / total < low_clip:\n                low_val = u\n            if float(current) / total < high_clip:\n                high_val = u\n            current += c\n                \n        img[:, :, i] = np.maximum(np.minimum(img[:, :, i], high_val), low_val)\n\n    return img    \n\ndef MSRCR(img, sigma_list, G, b, alpha, beta, low_clip, high_clip):\n\n    img = np.float64(img) + 1.0\n\n    img_retinex = multiScaleRetinex(img, sigma_list)    \n    img_color = colorRestoration(img, alpha, beta)    \n    img_msrcr = G * (img_retinex * img_color + b)\n\n    for i in range(img_msrcr.shape[2]):\n        img_msrcr[:, :, i] = (img_msrcr[:, :, i] - np.min(img_msrcr[:, :, i])) / \\\n                             (np.max(img_msrcr[:, :, i]) - np.min(img_msrcr[:, :, i])) * \\\n                             255\n    \n    img_msrcr = np.uint8(np.minimum(np.maximum(img_msrcr, 0), 255))\n    img_msrcr = simplestColorBalance(img_msrcr, low_clip, high_clip)       \n\n    return img_msrcr\n\ndef automatedMSRCR(img, sigma_list):\n\n    img = np.float64(img) + 1.0\n\n    img_retinex = multiScaleRetinex(img, sigma_list)\n\n    for i in range(img_retinex.shape[2]):\n        unique, count = np.unique(np.int32(img_retinex[:, :, i] * 100), return_counts=True)\n        for u, c in zip(unique, count):\n            if u == 0:\n                zero_count = c\n                break\n            \n        low_val = unique[0] / 100.0\n        high_val = unique[-1] / 100.0\n        for u, c in zip(unique, count):\n            if u < 0 and c < zero_count * 0.1:\n                low_val = u / 100.0\n            if u > 0 and c < zero_count * 0.1:\n                high_val = u / 100.0\n                break\n            \n        img_retinex[:, :, i] = np.maximum(np.minimum(img_retinex[:, :, i], high_val), low_val)\n        \n        img_retinex[:, :, i] = (img_retinex[:, :, i] - np.min(img_retinex[:, :, i])) / \\\n                               (np.max(img_retinex[:, :, i]) - np.min(img_retinex[:, :, i])) \\\n                               * 255\n\n    img_retinex = np.uint8(img_retinex)\n        \n    return img_retinex\n\ndef MSRCP(img, sigma_list, low_clip, high_clip):\n\n    img = np.float64(img) + 1.0\n\n    intensity = np.sum(img, axis=2) / img.shape[2]    \n\n    retinex = multiScaleRetinex(intensity, sigma_list)\n\n    intensity = np.expand_dims(intensity, 2)\n    retinex = np.expand_dims(retinex, 2)\n\n    intensity1 = simplestColorBalance(retinex, low_clip, high_clip)\n\n    intensity1 = (intensity1 - np.min(intensity1)) / \\\n                 (np.max(intensity1) - np.min(intensity1)) * \\\n                 255.0 + 1.0\n\n    img_msrcp = np.zeros_like(img)\n    \n    for y in range(img_msrcp.shape[0]):\n        for x in range(img_msrcp.shape[1]):\n            B = np.max(img[y, x])\n            A = np.minimum(256.0 / B, intensity1[y, x, 0] / intensity[y, x, 0])\n            img_msrcp[y, x, 0] = A * img[y, x, 0]\n            img_msrcp[y, x, 1] = A * img[y, x, 1]\n            img_msrcp[y, x, 2] = A * img[y, x, 2]\n\n    img_msrcp = np.uint8(img_msrcp - 1.0)\n\n    return img_msrcp\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"config = {\n    \"sigma_list\": [15, 80, 250],\n    \"G\"         : 5.0,\n    \"b\"         : 25.0,\n    \"alpha\"     : 125.0,\n    \"beta\"      : 46.0,\n    \"low_clip\"  : 0.01,\n    \"high_clip\" : 0.99\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"\ndef amsrcr_df(filenames,config):\n    \n    '''Plots retinex based cleaned image and the original image\n    \n    Input: filenames (pd.Series): Series of filenames to be plotted\n           config: Config dict\n    \n    Output: Plots of retinex based filtered image and original image\n    '''\n    \n    fig,ax = plt.subplots(len(filenames),2,figsize=(15,15))\n    for ii,img_name in enumerate(filenames):\n    \n        img = cv2.imread( \"../input/ranzcr-clip-catheter-line-classification/train/\"+img_name+\".jpg\")\n        img_amsrcr = automatedMSRCR(\n            img,\n            config['sigma_list']\n        )\n        \n        ax[ii][0].imshow(img_amsrcr)\n        \n\n        ax[ii][1].imshow(img)\n        if ii==0:\n            ax[ii][0].set_title(\"Retinex\")\n            ax[ii][1].set_title(\"Normal\")\n        \n    plt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"## UNCOMMENT THIS LINE TO RUN THE RETINEX METHOD,CURRENTLY ITS VERY SLOW\n#amsrcr_df(filenames,config)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}