{"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":"<div class='alert alert-info' style='text-align: center'><h1>Unsharp Masking Chest X-Rays</h1>\n    - yet another chest x-ray processing notebook -\n</div>\n\n#### I noticed a lot of the images have various filter convolutions applied to them. Some are blurred, some have unsharp mask. There are some that were *over filtered* and clipping happens.\n- Here, I will show how to add an unsharp mask (from scikit), and then histogram equalize the image for a bit of normalization.\n- This will create more consistent images across the dataset.\n\n#### The idea is to enhance the contrast and give CNN edge detectors a little more to grab onto.","metadata":{}},{"cell_type":"code","source":"# You may need to uncomment and run this command if you intend to load JPEG encoded DICOM files.\n#!conda install gdcm -c conda-forge -y","metadata":{"execution":{"iopub.status.busy":"2021-09-16T15:36:26.198500Z","iopub.execute_input":"2021-09-16T15:36:26.199100Z","iopub.status.idle":"2021-09-16T15:36:26.202738Z","shell.execute_reply.started":"2021-09-16T15:36:26.199016Z","shell.execute_reply":"2021-09-16T15:36:26.202129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport pydicom\nimport matplotlib.pyplot as plt\nfrom skimage.filters import unsharp_mask\nfrom skimage import exposure","metadata":{"execution":{"iopub.status.busy":"2021-09-16T15:36:26.206681Z","iopub.execute_input":"2021-09-16T15:36:26.207099Z","iopub.status.idle":"2021-09-16T15:36:27.832089Z","shell.execute_reply.started":"2021-09-16T15:36:26.207071Z","shell.execute_reply":"2021-09-16T15:36:27.831099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# This function gets the first image path in a StudyInstanceUID directory in the train set\ndef get_image_by_study_id(study_id):\n    base_path = \"/kaggle/input/siim-covid19-detection/\"\n    study_path = base_path + \"train/\" + study_id + \"/\"\n    images = []\n    for subdir, dirs, files in os.walk(study_path):\n        for file in files:     \n            image = os.path.join(subdir, file)\n            if os.path.isfile(image):\n                return image\n    return \"none\"","metadata":{"execution":{"iopub.status.busy":"2021-09-16T15:36:27.833614Z","iopub.execute_input":"2021-09-16T15:36:27.834006Z","iopub.status.idle":"2021-09-16T15:36:27.839218Z","shell.execute_reply.started":"2021-09-16T15:36:27.833964Z","shell.execute_reply":"2021-09-16T15:36:27.838466Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function to load image\ndef load_image(study_id):\n    img_file = get_image_by_study_id(study_id)\n    image = pydicom.dcmread(img_file)\n    pixels = image.pixel_array\n\n    min_pixel = np.min(pixels)\n    max_pixel = np.max(pixels)\n\n    if image.PhotometricInterpretation == \"MONOCHROME1\":\n        pixels = max_pixel - pixels\n    else:\n        pixels = pixels\n    return pixels","metadata":{"execution":{"iopub.status.busy":"2021-09-16T15:36:27.840641Z","iopub.execute_input":"2021-09-16T15:36:27.841122Z","iopub.status.idle":"2021-09-16T15:36:27.870468Z","shell.execute_reply.started":"2021-09-16T15:36:27.841090Z","shell.execute_reply":"2021-09-16T15:36:27.869470Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Apply unsharp mask and hist equalization\ndef unsharp_image(pixels):\n\n    # Tweak the radius and amount for more/less sharpening\n    unsharp_image = unsharp_mask(pixels, radius=5, amount=2)\n    equalized_image = exposure.equalize_hist(unsharp_image)\n    \n    return equalized_image","metadata":{"execution":{"iopub.status.busy":"2021-09-16T15:36:27.871959Z","iopub.execute_input":"2021-09-16T15:36:27.872298Z","iopub.status.idle":"2021-09-16T15:36:27.881509Z","shell.execute_reply.started":"2021-09-16T15:36:27.872254Z","shell.execute_reply":"2021-09-16T15:36:27.880837Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Display images\ndef plot_images(pixels, image_unsharp):\n    # Plot both images\n    fig, axes = plt.subplots(nrows=1, ncols=2,sharex=True, sharey=True, figsize=(12, 12))\n    ax = axes.ravel()\n    ax[0].imshow(pixels, cmap=plt.cm.gray)\n    ax[0].set_title('Original image')\n    ax[1].imshow(image_unsharp, cmap=plt.cm.gray)\n    ax[1].set_title('After unsharp / hist eq')\n    for a in ax:\n        a.axis('off')\n    fig.tight_layout()\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-09-16T15:36:27.882647Z","iopub.execute_input":"2021-09-16T15:36:27.883211Z","iopub.status.idle":"2021-09-16T15:36:27.892378Z","shell.execute_reply.started":"2021-09-16T15:36:27.883169Z","shell.execute_reply":"2021-09-16T15:36:27.891395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Grab an random study ID and get the pixels\npixels = load_image('013d698aeecb')\n\n# Apply unsharp and hist equalization\npixels_unsharp = unsharp_image(pixels)\n\n# Plot the images\nplot_images(pixels, pixels_unsharp)","metadata":{"execution":{"iopub.status.busy":"2021-09-16T15:36:27.893920Z","iopub.execute_input":"2021-09-16T15:36:27.894457Z","iopub.status.idle":"2021-09-16T15:36:31.966013Z","shell.execute_reply.started":"2021-09-16T15:36:27.894415Z","shell.execute_reply":"2021-09-16T15:36:31.965096Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### As you can tell, the image is more contrasted and seems much clearer.\n- Some anatomy like the distal bronchial/vascular markings in the upper lobes are better demonstrated.\n- I believe this technique can help with normalization across the dataset and edge detection for CNNs.\n- I won't go into the details of unsharp masking. Read more about it here -> https://en.wikipedia.org/wiki/Unsharp_masking\n- This should be applied sparingly to images that already have sharpening/unsharp filters.","metadata":{}},{"cell_type":"markdown","source":"**Here are some other processing notebooks I made:**\n- Lung segmentation without CNN ->https://www.kaggle.com/davidbroberts/lung-segmentation-without-cnn\n- Applying filters to x-rays -> https://www.kaggle.com/davidbroberts/applying-filters-to-chest-x-rays\n- Rib supression on Chest X-Rays -> https://www.kaggle.com/davidbroberts/rib-suppression-poc\n- Manual DICOM VOI LUT -> https://www.kaggle.com/davidbroberts/manual-dicom-voi-lut\n- Apply Unsharp Mask to Chest X-Rays -> https://www.kaggle.com/davidbroberts/unsharp-masking-chest-x-rays\n- Cropping Chest X-Rays -> https://www.kaggle.com/davidbroberts/cropping-chest-x-rays\n- Bounding Boxes on Cropped Images -> https://www.kaggle.com/davidbroberts/bounding-boxes-on-cropped-images\n- Visualizing Chest X-Ray bit planes -> https://www.kaggle.com/davidbroberts/visualizing-chest-x-ray-bitplanes\n- DICOM full range pixels as CNN input -> https://www.kaggle.com/davidbroberts/dicom-full-range-pixels-as-cnn-input\n- Standardizing Chest X-Ray Dataset Exports -> https://www.kaggle.com/davidbroberts/standardizing-cxr-datasets","metadata":{}}]}