{"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":"In this kernel, we will learn about what is windowing and how exactly it is helpful.\n\nCredits to - https://www.kaggle.com/code/redwankarimsony/ct-scans-dicom-files-windowing-explained\n\n### Windowing: \nWindowing, also known as **grey-level mapping**, **contrast stretching**, **histogram modification** or **contrast enhancement** is the process in which the CT image greyscale component of an image is manipulated via the CT numbers; doing this will change the appearance of the picture to highlight particular structures. The brightness of the image is adjusted via the window level. The contrast is adjusted via the window width.\n\nTissue density is measured in [Hounsfield units (HU)](https://en.wikipedia.org/wiki/Hounsfield_scale)\n\n* This is defined as **Air = −1000 HU**; **Water = 0 HU.**\n\nDensity of tissues in CT-Scans: $$Air < Fat < Fluid < Soft tissue < Bone < Metal$$\nThe easier way to remember this is (Fat floats on water, so is less dense than fluid; Soft tissue is mostly intracellular fluid with some connective tissue)\n\n* Air = −1000 HU\n* Lung ≈ −500 HU (partially air, partially soft tissue)\n* Fat ≈ −50 HU (slightly less dense than simple fluid)\n* Water = 0 HU\n* Soft tissue (& blood) ≈ +50 HU (slightly more dense than simple fluid)\n* Bone ≈ +1000 HU (much more dense)\n\n![](https://www.radiologycafe.com/images/basics/window-basic.png)\nTo ascertain a window, a ‘level’ and a ‘width’ is defined. For example, a window with a level of 0 HU and a width of 400 HU will have a range of −200 HU to +200 HU. Any tissue with a density of −200 HU or less will be black, and any tissue with a density of +200 HU or more will be white. And values between -200 HU to +200 HU will be spread between the whole grayscale range. A **window** can be set to look at certain tissues of interest. **A small range of tissue density is represented by a full greyscale spectrum from black to white, thus making subtle density differences within the specified range easier to see.**\n\n\n\n\n\n\n### Typical window width and level values: \nAlthough this varies somewhat from institution to institution and vendor to vendor, window width and centers are generally fairly similar. **The values below are written as width and level (W:x L:y) in Hounsfield units (HU).**\n\n* head and neck\n* brain W:80 L:40\n* subdural W:130-300 L:50-100\n* stroke W:8 L:32 or W:40 L:40 3\n* temporal bones W:2800 L:600\n* soft tissues: W:350–400 L:20–60 4\n* chest\n* lungs W:1500 L:-600\n* mediastinum W:350 L:50\n* abdomen\n* soft tissues W:400 L:50\n* liver W:150 L:30\n* spine\n* soft tissues W:250 L:50\n* bone W:1800 L:400\n","metadata":{}},{"cell_type":"markdown","source":"\n### Window width\nThe window width (WW) as the name suggests is the measure of the range of CT numbers that an image contains. A wider window width (2000 HU), therefore, will display a wider range of CT numbers. Consequently, the transition of dark to light structures will occur over a larger transition area to that of a narrow window width (<1000 HU). Accordingly, it is important to note, that a significantly wide window displaying all the CT numbers will result in different attenuations between soft tissues to become obscured.\n\n**Wide window:** When you are looking at an area with predominantly different tissue density, a wide window is used. A good example is lungs or cortical tissue, where air and vessels will sit side by side.\n\n**Narrow window:** When you are looking at tissues with almost similar density, you should use narrow window. As a result subtle changes in tissued density (small window) is magnified over the whole grayscale range. \n\n### Window level/center\nThe window level (WL), often also referred to as window center, is the midpoint of the range of the CT numbers displayed. **When the window level is decreased the CT image will be brighter and vice versa.** ","metadata":{}},{"cell_type":"markdown","source":"### Upper and lower grey level calculation \nWhen presented with a Window width (WW) and Widnow Level (WL) one can calculate the upper and lower grey levels i.e. values over x will be white and values below y will be black. \n\n* the upper grey level (x) is calculated via WL + (WW ÷ 2)\n* the lower grey level (y) is calculated via WL - (WW ÷ 2)\n\nFor example, a brain is W:80 L:40.  Therefore, all values above +80 will be set to maximum grayscale level (white) and all values below 0 will be set to lowest grayscale level in the display (black). And values between +0 to +80 will be spread between the whole grayscale range.  \n\nExamples of commonly used windows are soft tissue, lung, and bone are given below: ","metadata":{}},{"cell_type":"markdown","source":"## LIbraries","metadata":{}},{"cell_type":"code","source":"!pip install -qU ../input/for-pydicom/python_gdcm-3.0.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl ../input/for-pydicom/pylibjpeg-1.4.0-py3-none-any.whl --find-links frozen_packages --no-index","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:48:13.18015Z","iopub.execute_input":"2022-09-26T17:48:13.180607Z","iopub.status.idle":"2022-09-26T17:48:26.034931Z","shell.execute_reply.started":"2022-09-26T17:48:13.180569Z","shell.execute_reply":"2022-09-26T17:48:26.033703Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n%matplotlib inline\nimport matplotlib.patches as patches\nimport seaborn as sns\nsns.set(style='darkgrid', font_scale=1.6)\nimport cv2\nimport os\nimport re\nimport gc\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nfrom tqdm import tqdm\nfrom glob import glob\nimport warnings","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:48:54.054067Z","iopub.execute_input":"2022-09-26T17:48:54.054509Z","iopub.status.idle":"2022-09-26T17:48:54.065707Z","shell.execute_reply.started":"2022-09-26T17:48:54.054468Z","shell.execute_reply":"2022-09-26T17:48:54.064472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv(\"../input/rsna-2022-cervical-spine-fracture-detection/train.csv\")\ntrain_bbox = pd.read_csv(\"../input/rsna-2022-cervical-spine-fracture-detection/train_bounding_boxes.csv\")\ntest_df = pd.read_csv(\"../input/rsna-2022-cervical-spine-fracture-detection/test.csv\")\nss = pd.read_csv(\"../input/rsna-2022-cervical-spine-fracture-detection/sample_submission.csv\")\n\nTRAIN_IMAGES_PATH = '../input/rsna-2022-cervical-spine-fracture-detection/train_images'\n\n# Print dataframe shapes\nprint('train shape:', train_df.shape)\nprint('train bbox shape:', train_bbox.shape)\nprint('test shape:', test_df.shape)\nprint('ss shape:', ss.shape)\nprint('')\n\n# Show first few entries\ntrain_df.head(3)","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:52:25.360754Z","iopub.execute_input":"2022-09-26T17:52:25.361237Z","iopub.status.idle":"2022-09-26T17:52:25.398495Z","shell.execute_reply.started":"2022-09-26T17:52:25.361199Z","shell.execute_reply":"2022-09-26T17:52:25.397684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Images","metadata":{}},{"cell_type":"code","source":"def load_dicom(path):\n    \"\"\"\n    This supports loading both regular and compressed JPEG images. \n    See the first sell with `pip install` commands for the necessary dependencies\n    \"\"\"\n    img = pydicom.dcmread(path)\n    img.PhotometricInterpretation = 'YBR_FULL'\n    data = img.pixel_array\n    data = data - np.min(data)\n    if np.max(data) != 0:\n        data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n    return cv2.cvtColor(data, cv2.COLOR_GRAY2RGB), img","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:50:46.171655Z","iopub.execute_input":"2022-09-26T17:50:46.172079Z","iopub.status.idle":"2022-09-26T17:50:46.178908Z","shell.execute_reply.started":"2022-09-26T17:50:46.172043Z","shell.execute_reply":"2022-09-26T17:50:46.178059Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample = '1.2.826.0.1.3680043.10001/1.dcm'\nimage, meta = load_dicom(f'{TRAIN_IMAGES_PATH}/1.2.826.0.1.3680043.10001/1.dcm')\n\nplt.figure()\nplt.axis('off')\nplt.imshow(image)","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:53:03.819791Z","iopub.execute_input":"2022-09-26T17:53:03.820607Z","iopub.status.idle":"2022-09-26T17:53:04.052784Z","shell.execute_reply.started":"2022-09-26T17:53:03.820559Z","shell.execute_reply":"2022-09-26T17:53:04.051954Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Utility","metadata":{}},{"cell_type":"code","source":"def window_image(img, window_center,window_width, intercept, slope, rescale=True):\n    img = (img*slope +intercept) #for translation adjustments given in the dicom file. \n    img_min = window_center - window_width//2 #minimum HU level\n    img_max = window_center + window_width//2 #maximum HU level\n    img[img<img_min] = img_min #set img_min for all HU levels less than minimum HU level\n    img[img>img_max] = img_max #set img_max for all HU levels higher than maximum HU level\n    if rescale: \n        img = (img - img_min) / (img_max - img_min)*255.0 \n    return img\n    \ndef get_first_of_dicom_field_as_int(x):\n    #get x[0] as in int is x is a 'pydicom.multival.MultiValue', otherwise get int(x)\n    if type(x) == pydicom.multival.MultiValue: return int(x[0])\n    else: return int(x)\n    \ndef get_windowing(data):\n    dicom_fields = [data[('0028','1050')].value, #window center\n                    data[('0028','1051')].value, #window width\n                    data[('0028','1052')].value, #intercept\n                    data[('0028','1053')].value] #slope\n    return [get_first_of_dicom_field_as_int(x) for x in dicom_fields]","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:53:20.45263Z","iopub.execute_input":"2022-09-26T17:53:20.453044Z","iopub.status.idle":"2022-09-26T17:53:20.462685Z","shell.execute_reply.started":"2022-09-26T17:53:20.453001Z","shell.execute_reply":"2022-09-26T17:53:20.461348Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def view_images(files, title = '', aug = None, windowing = True):\n    width = 2\n    height = 2\n    fig, axs = plt.subplots(height, width, figsize=(15,15))\n    \n    for im in range(0, height * width):\n        data = pydicom.dcmread(files[im])\n        image = data.pixel_array\n        window_center , window_width, intercept, slope = get_windowing(data)\n        if windowing:\n            output = window_image(image, window_center, window_width, intercept, slope, rescale = False)\n        else:\n            output = image\n        i = im // width\n        j = im % width\n        axs[i,j].imshow(output, cmap=plt.cm.gray) \n        axs[i,j].axis('off')\n        \n    plt.suptitle(title)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:54:37.636006Z","iopub.execute_input":"2022-09-26T17:54:37.636407Z","iopub.status.idle":"2022-09-26T17:54:37.645057Z","shell.execute_reply.started":"2022-09-26T17:54:37.636351Z","shell.execute_reply":"2022-09-26T17:54:37.64386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"list_to_view = [f'{TRAIN_IMAGES_PATH}/1.2.826.0.1.3680043.10041/{dcm_file}'\n                for dcm_file in os.listdir(f'{TRAIN_IMAGES_PATH}/1.2.826.0.1.3680043.10041')]\n\nprint(f'there are {len(list_to_view)} files in this directory')","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:59:35.242177Z","iopub.execute_input":"2022-09-26T17:59:35.242585Z","iopub.status.idle":"2022-09-26T17:59:35.248951Z","shell.execute_reply.started":"2022-09-26T17:59:35.242549Z","shell.execute_reply":"2022-09-26T17:59:35.247856Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"view_images(list_to_view[:10], 'Images with Windowing')","metadata":{"execution":{"iopub.status.busy":"2022-09-26T18:00:32.95995Z","iopub.execute_input":"2022-09-26T18:00:32.960333Z","iopub.status.idle":"2022-09-26T18:00:33.827094Z","shell.execute_reply.started":"2022-09-26T18:00:32.960303Z","shell.execute_reply":"2022-09-26T18:00:33.826293Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"view_images(list_to_view[10:20], 'Images with Windowing')","metadata":{"execution":{"iopub.status.busy":"2022-09-26T18:00:38.197234Z","iopub.execute_input":"2022-09-26T18:00:38.197747Z","iopub.status.idle":"2022-09-26T18:00:39.016279Z","shell.execute_reply.started":"2022-09-26T18:00:38.1977Z","shell.execute_reply":"2022-09-26T18:00:39.015194Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"view_images(list_to_view[20:30], 'Images with Windowing')","metadata":{"execution":{"iopub.status.busy":"2022-09-26T18:00:45.593209Z","iopub.execute_input":"2022-09-26T18:00:45.594509Z","iopub.status.idle":"2022-09-26T18:00:46.492348Z","shell.execute_reply.started":"2022-09-26T18:00:45.594454Z","shell.execute_reply":"2022-09-26T18:00:46.491306Z"},"trusted":true},"execution_count":null,"outputs":[]}]}