{"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":"code","source":"import numpy as np\nimport pydicom\nimport matplotlib.pyplot as plt\nimport cv2\nfrom pydicom.pixel_data_handlers import apply_windowing","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-12-01T14:05:05.559119Z","iopub.execute_input":"2022-12-01T14:05:05.559521Z","iopub.status.idle":"2022-12-01T14:05:05.565182Z","shell.execute_reply.started":"2022-12-01T14:05:05.559487Z","shell.execute_reply":"2022-12-01T14:05:05.563978Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<div class='alert alert-info' style='text-align:center'><h1>Why use windowing?</h1>\n- yet another DICOM processing notebook -</div>\n\n### Get better quality image exports .. view images as they were intended to be viewed!\n\n##### DICOM images routinely contain values greater than 8 bits (255). \n#####  Normalizing the entire (non 8-bit) range down to 8 bit results in the loss of information.\n##### \"Windowing\" allows us to select a range of pixels from the original image **before** normalization.\n\n##### This has the effect of giving much greater contrast between soft and dense tissues.\n\n- Don't forget to install GDCM and pylibjpeg for the JPG compressed DICOM files in this dataset.","metadata":{}},{"cell_type":"code","source":"# This function extracts pixels from a DICOM file and uses stantard normalization to crunch the image down to 8 bit.\n\ndef get_pixels(dcm_file):\n    im = pydicom.dcmread(dcm_file)\n    \n    data = im.pixel_array\n    \n    if im.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n    else:\n        data = data - np.min(data)\n        \n    if np.max(data) != 0:\n        data = data / np.max(data)\n    data=(data * 255).astype(np.uint8)\n\n    return data","metadata":{"execution":{"iopub.status.busy":"2022-12-01T14:05:05.56784Z","iopub.execute_input":"2022-12-01T14:05:05.568245Z","iopub.status.idle":"2022-12-01T14:05:05.577473Z","shell.execute_reply.started":"2022-12-01T14:05:05.568203Z","shell.execute_reply":"2022-12-01T14:05:05.576738Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# This function uses pydicom's apply_windowing() function to apply the default window width and level specified in the DICOM tags\n\ndef get_pixels_with_windowing(dcm_file):\n    im = pydicom.dcmread(dcm_file)\n    \n    data = im.pixel_array\n    \n    # This line is the only difference in the two functions\n    data = apply_windowing(data, im)\n    \n    if im.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n    else:\n        data = data - np.min(data)\n        \n    if np.max(data) != 0:\n        data = data / np.max(data)\n    data=(data * 255).astype(np.uint8)\n\n    return data","metadata":{"execution":{"iopub.status.busy":"2022-12-01T14:05:05.579162Z","iopub.execute_input":"2022-12-01T14:05:05.579927Z","iopub.status.idle":"2022-12-01T14:05:05.593219Z","shell.execute_reply.started":"2022-12-01T14:05:05.579884Z","shell.execute_reply":"2022-12-01T14:05:05.592407Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### The apply_windowing() function from pydicom uses the DICOM WindowWidth and WindowLevel tags to apply the specified \"Window\" to the image. \n\n#### You can also use apply_voi_lut() here. Since there aren't any VOI LUTs in this dataset, it reverts back to using the default WW/WL tags.","metadata":{}},{"cell_type":"code","source":"# Open an image and get the pixels twice .. once without windowing and once with it.\n#\nfile = \"/kaggle/input/rsna-breast-cancer-detection/train_images/10025/562340703.dcm\"\npixels = get_pixels(file)\npixels_with_windowing = get_pixels_with_windowing(file)","metadata":{"execution":{"iopub.status.busy":"2022-12-01T14:05:05.594946Z","iopub.execute_input":"2022-12-01T14:05:05.595725Z","iopub.status.idle":"2022-12-01T14:05:08.991473Z","shell.execute_reply.started":"2022-12-01T14:05:05.595683Z","shell.execute_reply":"2022-12-01T14:05:08.990297Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the images\nfig, axes = plt.subplots(nrows=1, ncols=2,sharex=False, sharey=True, figsize=(14, 10))\nax = axes.ravel()\nax[0].set_title(f'Standard normalization')\nax[0].imshow(pixels, cmap='gray');\nax[1].set_title(f'With windowing')\nax[1].imshow(pixels_with_windowing, cmap='gray');","metadata":{"execution":{"iopub.status.busy":"2022-12-01T14:05:08.994031Z","iopub.execute_input":"2022-12-01T14:05:08.994357Z","iopub.status.idle":"2022-12-01T14:05:16.564658Z","shell.execute_reply.started":"2022-12-01T14:05:08.994327Z","shell.execute_reply":"2022-12-01T14:05:16.563475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### - Notice the image on the right looks less \"washed out\" and the contrast between soft and dense tissue is greater?","metadata":{}},{"cell_type":"code","source":"# Open another image and take a look\n#\nfile = \"/kaggle/input/rsna-breast-cancer-detection/train_images/10050/1428987847.dcm\"\npixels = get_pixels(file)\npixels_with_windowing = get_pixels_with_windowing(file)","metadata":{"execution":{"iopub.status.busy":"2022-12-01T14:05:16.565772Z","iopub.execute_input":"2022-12-01T14:05:16.566082Z","iopub.status.idle":"2022-12-01T14:05:21.29632Z","shell.execute_reply.started":"2022-12-01T14:05:16.566055Z","shell.execute_reply":"2022-12-01T14:05:21.295366Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the images\nfig, axes = plt.subplots(nrows=1, ncols=2,sharex=False, sharey=True, figsize=(14, 10))\nax = axes.ravel()\nax[0].set_title(f'Standard normalization')\nax[0].imshow(pixels, cmap='gray');\nax[1].set_title(f'With windowing')\nax[1].imshow(pixels_with_windowing, cmap='gray');","metadata":{"execution":{"iopub.status.busy":"2022-12-01T14:05:21.297349Z","iopub.execute_input":"2022-12-01T14:05:21.298182Z","iopub.status.idle":"2022-12-01T14:05:29.049811Z","shell.execute_reply.started":"2022-12-01T14:05:21.298135Z","shell.execute_reply":"2022-12-01T14:05:29.048726Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Conclusion\n- Applying windowing to DICOM images provides much better contrast and width of images.\n- This technique should be applied to JPG/PNG exports.\n- If the pydicom function apply_voi_lut() is used, it will also apply the default WW/WL values if a LUT does not exist .. which they routinely do not exist in mammography.\n- Applying windowing allows for greater range when manually adjusting brightness/contrast later.","metadata":{}}]}