{"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":"!pip install ydata-profiling","metadata":{"_uuid":"7659e948-c1f6-4096-b7e8-b1842f24383b","_cell_guid":"1ba44bd7-cba9-4c4f-ba0b-6669d5681e24","_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-10-19T23:27:10.010199Z","iopub.execute_input":"2023-10-19T23:27:10.010514Z","iopub.status.idle":"2023-10-19T23:27:18.283797Z","shell.execute_reply.started":"2023-10-19T23:27:10.010489Z","shell.execute_reply":"2023-10-19T23:27:18.282865Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"_uuid":"00748d46-694a-4ace-8702-42f27ead8960","_cell_guid":"569ebb3f-dde8-47a3-acb8-685efa31f1a7","collapsed":false,"_kg_hide-input":true,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:27:18.286034Z","iopub.execute_input":"2023-10-19T23:27:18.286407Z","iopub.status.idle":"2023-10-19T23:27:18.290888Z","shell.execute_reply.started":"2023-10-19T23:27:18.286371Z","shell.execute_reply":"2023-10-19T23:27:18.290057Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import nibabel as nib\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom pathlib import Path\nimport seaborn as sns\nimport pydicom\n\n# For profiling\nfrom ydata_profiling import ProfileReport","metadata":{"_uuid":"759ef25b-c26d-451b-8488-f81e47fab60b","_cell_guid":"2e58ee90-30c4-474b-87f8-52b78544f370","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:27:18.29201Z","iopub.execute_input":"2023-10-19T23:27:18.292252Z","iopub.status.idle":"2023-10-19T23:27:18.303901Z","shell.execute_reply.started":"2023-10-19T23:27:18.292232Z","shell.execute_reply":"2023-10-19T23:27:18.303138Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sns.set_theme(context=\"notebook\", style=\"dark\")","metadata":{"_uuid":"6e9ea6ba-15de-4f32-932c-5c33b3b58908","_cell_guid":"5984a4f9-e4e4-4e42-8e9c-054571e8bb73","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:27:18.305368Z","iopub.execute_input":"2023-10-19T23:27:18.305628Z","iopub.status.idle":"2023-10-19T23:27:18.31725Z","shell.execute_reply.started":"2023-10-19T23:27:18.305608Z","shell.execute_reply":"2023-10-19T23:27:18.316332Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"COMPETITION_DATA_DIR = Path(\"/kaggle/input/rsna-2023-abdominal-trauma-detection/\")\nDICOM_META_DIR = Path(\"/kaggle/input/rsna-atd-2023-dicom-metadata/\")","metadata":{"_uuid":"6329895e-fb06-4bbc-b2db-86bbf01bfb16","_cell_guid":"053845d2-5c72-46a9-9b13-5b886e1330b9","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:27:18.31833Z","iopub.execute_input":"2023-10-19T23:27:18.31855Z","iopub.status.idle":"2023-10-19T23:27:18.330338Z","shell.execute_reply.started":"2023-10-19T23:27:18.318531Z","shell.execute_reply":"2023-10-19T23:27:18.329509Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = pd.read_csv(COMPETITION_DATA_DIR.joinpath(\"train.csv\"))\ntrain_meta_df = pd.read_csv(COMPETITION_DATA_DIR.joinpath(\"train_series_meta.csv\"))\ntrain_df = train_df.merge(train_meta_df)","metadata":{"_uuid":"490285c9-f748-49cb-a922-b2c6dd9a202c","_cell_guid":"1811c07b-8914-4ed4-a016-e9fb22f40359","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:27:18.331122Z","iopub.execute_input":"2023-10-19T23:27:18.33136Z","iopub.status.idle":"2023-10-19T23:27:18.361129Z","shell.execute_reply.started":"2023-10-19T23:27:18.331341Z","shell.execute_reply":"2023-10-19T23:27:18.360371Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Number of Patients in the Study: ', len(train_df.patient_id.unique()))","metadata":{"_uuid":"3423e844-d794-4397-a41e-972cef0007a5","_cell_guid":"7b14f072-1d6b-4f5f-8c6f-2a25c4c100f6","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:27:18.362053Z","iopub.execute_input":"2023-10-19T23:27:18.362274Z","iopub.status.idle":"2023-10-19T23:27:18.366919Z","shell.execute_reply.started":"2023-10-19T23:27:18.362255Z","shell.execute_reply":"2023-10-19T23:27:18.366096Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Confirm that each injury_type is mutually exclusive to injury_health\nBINARY_INJURIES = [\"bowel\", \"extravasation\"]\nTERNARY_INJURIES = [\"kidney\", \"spleen\", \"liver\"]","metadata":{"_uuid":"c3f87146-0926-47de-8e23-f4141424773e","_cell_guid":"2504fbd8-7eae-43eb-aecc-909e11c30d86","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:35:35.423563Z","iopub.execute_input":"2023-10-19T23:35:35.424517Z","iopub.status.idle":"2023-10-19T23:35:35.428671Z","shell.execute_reply.started":"2023-10-19T23:35:35.424483Z","shell.execute_reply":"2023-10-19T23:35:35.427716Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"profile = ProfileReport(train_df, title=\"Profiling Report\", correlations=None)\nprofile","metadata":{"_uuid":"9a19d136-b416-42cf-af5d-1eee900ef923","_cell_guid":"5b198643-d551-4885-b39b-19de2ee8e1b7","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:27:18.368231Z","iopub.execute_input":"2023-10-19T23:27:18.368529Z","iopub.status.idle":"2023-10-19T23:27:33.813391Z","shell.execute_reply.started":"2023-10-19T23:27:18.368502Z","shell.execute_reply":"2023-10-19T23:27:33.812527Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Is there any correlation between injury types?","metadata":{}},{"cell_type":"code","source":"all_injuries = (\n    [f'{organ}_injury' for organ in BINARY_INJURIES] +\n    [f'{organ}_low' for organ in TERNARY_INJURIES] + \n    [f'{organ}_high' for organ in TERNARY_INJURIES]\n)\n\nplt.figure(figsize=(10, 8))\nsns.heatmap(train_df[all_injuries].corr())\nplt.title(\"Correlation Heatmap\")","metadata":{"execution":{"iopub.status.busy":"2023-10-19T23:57:23.773593Z","iopub.execute_input":"2023-10-19T23:57:23.773948Z","iopub.status.idle":"2023-10-19T23:57:24.255011Z","shell.execute_reply.started":"2023-10-19T23:57:23.77392Z","shell.execute_reply":"2023-10-19T23:57:24.254122Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> **Insights**: There is no significant correlation between injury types","metadata":{}},{"cell_type":"code","source":"plt.pie(\n    train_df.any_injury.value_counts(),\n    labels=train_df.any_injury.unique().astype(bool),\n    autopct=\"%.0f%%\",\n)\nplt.title(\"Percentage of Patients' with Abdominal Injuries\")","metadata":{"_uuid":"f101b50f-289e-4cf7-ba18-0ba5142ccd57","_cell_guid":"74f47154-ac96-4f76-8741-436ebafda99c","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:55:58.975914Z","iopub.execute_input":"2023-10-19T23:55:58.976627Z","iopub.status.idle":"2023-10-19T23:55:59.117322Z","shell.execute_reply.started":"2023-10-19T23:55:58.976596Z","shell.execute_reply":"2023-10-19T23:55:59.116191Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Dataset isn't balanced and should be balanced for training","metadata":{"_uuid":"70ad81a9-9672-496c-b6c3-4c36225fd270","_cell_guid":"f0264f9c-b2c6-41be-bfd5-ade7af6b170b","trusted":true}},{"cell_type":"markdown","source":"#### What was the most prevalent injury type?","metadata":{"_uuid":"de7dcd9c-9c82-42d7-b0e7-6d0db955512c","_cell_guid":"31608a37-500c-4916-8348-baaf92f27358","trusted":true}},{"cell_type":"code","source":"all_injuries = (\n    train_df[[f\"{injury}_injury\" for injury in BINARY_INJURIES]].sum().to_dict()\n    | train_df[[f\"{injury}_high\" for injury in TERNARY_INJURIES]].sum().to_dict()\n    | train_df[[f\"{injury}_low\" for injury in TERNARY_INJURIES]].sum().to_dict()\n)\nplt.pie(\n    all_injuries.values(),\n    labels=all_injuries.keys(),\n    autopct=\"%.0f%%\",\n)\nplt.title(\"Distribution of different kinds of Abdominal Injuries\")","metadata":{"_uuid":"2eb96677-deb5-43e6-a80d-bc1f1cf0e6a2","_cell_guid":"6f9cff27-b977-4702-a52c-05b49c4c2ac5","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-19T23:56:05.901395Z","iopub.execute_input":"2023-10-19T23:56:05.901734Z","iopub.status.idle":"2023-10-19T23:56:06.116334Z","shell.execute_reply.started":"2023-10-19T23:56:05.901708Z","shell.execute_reply":"2023-10-19T23:56:06.115173Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Liver Internal injuries are the most prevalent type of abdominal trauma in the dataset.\nWhy might this be? The liver is the largest internal organ in the human abdomen, surpassing the spleen, kidneys, and bowels in size and mass. Consequently, there is a larger chance of it getting damaged.","metadata":{}},{"cell_type":"code","source":"for injury in BINARY_INJURIES:\n    is_healthy_msk = train_df[f\"{injury}_healthy\"] == 1\n    is_injured_msk = train_df[f\"{injury}_injury\"] == 1\n    print(f\"# {injury}\")\n    print(\n        \"No. of Patients marked as both healthy and injured: \",\n        train_df[is_healthy_msk & is_injured_msk][\"patient_id\"].count(),\n    )","metadata":{"_uuid":"18ff44e4-0cbc-4188-a040-85db91abbd7e","_cell_guid":"3d22bbf1-776c-4a6f-8fde-3c3303d0b676","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:01:34.702124Z","iopub.execute_input":"2023-10-20T00:01:34.702507Z","iopub.status.idle":"2023-10-20T00:01:34.710212Z","shell.execute_reply.started":"2023-10-20T00:01:34.70248Z","shell.execute_reply":"2023-10-20T00:01:34.709411Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for injury in TERNARY_INJURIES:\n    is_healthy_msk = train_df[f\"{injury}_healthy\"] == 1\n    is_injured_msk = (train_df[f\"{injury}_high\"] == 1) | (\n        train_df[f\"{injury}_low\"] == 1\n    )\n    print(f\"# {injury}\")\n    print(\n        \"No. of Patients marked as both healthy and injured: \",\n        train_df[is_healthy_msk & is_injured_msk][\"patient_id\"].count(),\n    )","metadata":{"_uuid":"ecdbacde-7d70-4534-acb6-4fe4e6e21300","_cell_guid":"f4eab158-2a03-429a-9937-34ff2a2ab14a","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:01:36.460283Z","iopub.execute_input":"2023-10-20T00:01:36.460616Z","iopub.status.idle":"2023-10-20T00:01:36.470607Z","shell.execute_reply.started":"2023-10-20T00:01:36.460591Z","shell.execute_reply":"2023-10-20T00:01:36.469731Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Our data is clean in that regard!","metadata":{"_uuid":"6c4606f7-f619-4478-821f-98f58901e14a","_cell_guid":"122c7803-34a1-43c3-847d-1d9edf3b09af","trusted":true}},{"cell_type":"code","source":"# Check that no individual is marked as injured who doesn't have any injury (and the reverse)\ninjury_msks = []\nfor injury in BINARY_INJURIES + TERNARY_INJURIES:\n    if injury in BINARY_INJURIES:\n        injury_msks.append(train_df[f\"{injury}_injury\"] == 1)\n    if injury in TERNARY_INJURIES:\n        injury_msks.append(\n            (train_df[f\"{injury}_high\"] == 1) | (train_df[f\"{injury}_low\"] == 1)\n        )\n\ncombined_msk = injury_msks[0]\nfor mask in injury_msks[1:]:\n    combined_msk |= mask\n\nprint(\n    f\"No. of patients reported to have injuries, but don't have any: \",\n    train_df[~combined_msk & (train_df[\"any_injury\"] == 1)][\"patient_id\"].count(),\n)\n\nprint(\n    f\"No. of patients reported *not* to have injuries, but don't have:\",\n    train_df[combined_msk & (train_df[\"any_injury\"] == 0)][\"patient_id\"].count(),\n)","metadata":{"_uuid":"428abd5b-6324-491b-87d4-ec24c98d9ae4","_cell_guid":"3f39cdc6-6416-4b23-b751-bea971efe851","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:01:38.252517Z","iopub.execute_input":"2023-10-20T00:01:38.252841Z","iopub.status.idle":"2023-10-20T00:01:38.264015Z","shell.execute_reply.started":"2023-10-20T00:01:38.252815Z","shell.execute_reply":"2023-10-20T00:01:38.263068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Insights\nClean again!","metadata":{"_uuid":"42a3072c-8ed1-40c6-890d-eb3eb01082d8","_cell_guid":"736e9b4a-ef71-4542-9738-967b57933897","trusted":true}},{"cell_type":"markdown","source":"### What is the Aortic Volume?\nThe aortic volume, as measured in CT scans provides information about the health of the heart and the circulatory system. It finds relevance in:\n\n1. **Assessing Heart Function**: The aorta is the largest artery in the body and plays a crucial role in carrying oxygen-rich blood from the heart to the rest of the body. CT scans can measure the volume of the aorta, which helps doctors evaluate how efficiently the heart is pumping blood.\n\n2. **Detecting Aortic Aneurysms**: Aortic aneurysms are abnormal bulges or enlargements in the aorta. These can be dangerous because they may rupture if left untreated. CT scans can accurately measure the size and volume of the aorta, allowing doctors to identify and monitor the growth of an aneurysm over time. Early detection can lead to timely intervention, reducing the risk of rupture.\n\n##### Does a large aortic volume imply internal injury\n\nA large aortic volume, by itself, does not necessarily imply internal injury. The size of the aorta can vary among individuals, and it is influenced by factors such as age, sex, and overall body size. What's considered \"large\" may differ from person to person.\n\nHowever, an abnormally enlarged aorta, particularly when it's larger than what is considered normal for a given individual, can be a cause for concern. This enlargement is often referred to as an aortic aneurysm. Aortic aneurysms can result from various causes, including:\n*Atherosclerosis, Genetics, High Blood Pressure, Connective Tissue Disorders, Trauma, and Infection*\n","metadata":{}},{"cell_type":"code","source":"sns.histplot(train_df.aortic_hu, kde=True)\nplt.title('Aortic Volume (in Hounsfield unit $(HU)$)')","metadata":{"_uuid":"b386695b-8a39-470c-8d51-84d431069594","_cell_guid":"4b5aa875-4c84-446a-8218-45b6e466ae60","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:01:41.947887Z","iopub.execute_input":"2023-10-20T00:01:41.948255Z","iopub.status.idle":"2023-10-20T00:01:43.391933Z","shell.execute_reply.started":"2023-10-20T00:01:41.948224Z","shell.execute_reply":"2023-10-20T00:01:43.391108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Taking a closer look at `aortic_hu`, we see that it's right-skewed, with majority of patient scans having an aorta volume between **0** and **50**","metadata":{"_uuid":"d26ed0f6-677c-4767-b8ef-8217ec9da892","_cell_guid":"608f986f-e0f4-4c11-824f-838a3cd9fea7","trusted":true}},{"cell_type":"code","source":"for group, group_data in train_df.groupby('any_injury'):\n    plt.figure()\n    sns.histplot(group_data['aortic_hu'], kde=True)\n    plt.title(f'Histogram for Patient is Injured is: {bool(group)}')\n    plt.xlabel('Value')\n    plt.ylabel('Frequency')","metadata":{"execution":{"iopub.status.busy":"2023-10-20T00:11:57.525599Z","iopub.execute_input":"2023-10-20T00:11:57.525917Z","iopub.status.idle":"2023-10-20T00:11:58.655994Z","shell.execute_reply.started":"2023-10-20T00:11:57.525881Z","shell.execute_reply":"2023-10-20T00:11:58.655003Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"There doesn't appear to be any significant difference in the distribution of *Aortic Volume* for healthy and injured patients","metadata":{}},{"cell_type":"markdown","source":"### Loading the DICOM metadata","metadata":{"_uuid":"1281a951-b367-4d0f-95c1-997a9a8c4d06","_cell_guid":"a1f648cd-04a5-4b77-ac62-575b4194fe63","trusted":true}},{"cell_type":"code","source":"dicom_meta_df = pd.read_csv(DICOM_META_DIR.joinpath(\"train_images_dicom_meta.csv\"))","metadata":{"_uuid":"ee0f8462-f6e2-4504-80f3-2a4a603e07e1","_cell_guid":"a4e1907b-c3c7-4790-9b19-462313b12caf","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:08:41.895652Z","iopub.execute_input":"2023-10-20T00:08:41.896475Z","iopub.status.idle":"2023-10-20T00:08:57.42228Z","shell.execute_reply.started":"2023-10-20T00:08:41.896441Z","shell.execute_reply":"2023-10-20T00:08:57.421318Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dicom_meta_df.sample(5)","metadata":{"_uuid":"9368a027-e68a-4380-9201-97de9b9b8106","_cell_guid":"dfc6cd2d-0965-4bd7-b8e4-7e8fa28b0dc8","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:08:57.423656Z","iopub.execute_input":"2023-10-20T00:08:57.423918Z","iopub.status.idle":"2023-10-20T00:08:57.487453Z","shell.execute_reply.started":"2023-10-20T00:08:57.423896Z","shell.execute_reply":"2023-10-20T00:08:57.486579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# The average slice thickness used for each patient\n\nsns.histplot(\n    dicom_meta_df.groupby(\"patient id\")[\"slice thickness\"].mean(),\n    kde=True,\n    color=\"orange\",\n)\nplt.title(\"Average Slice Thickness Used by Patients\")","metadata":{"_uuid":"bb0a92bf-4ea6-4e5c-8f2a-20266dfa7140","_cell_guid":"206d1012-ed93-4d16-af67-a40671210a1a","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:08:57.488355Z","iopub.execute_input":"2023-10-20T00:08:57.488567Z","iopub.status.idle":"2023-10-20T00:08:57.998718Z","shell.execute_reply.started":"2023-10-20T00:08:57.488549Z","shell.execute_reply":"2023-10-20T00:08:57.997935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### What is Patient Position?\nIn DICOM (Digital Imaging and Communications in Medicine) image scans, the \"Patient Position\" refers to the orientation or positioning of the patient's body during the scan. Different patient positions are used to capture images from various angles and perspectives. Some common patient positions include:\n\n1. **Supine**: The patient lies flat on their back with their face and abdomen facing upward. Could be Head First (HFS) or Feet First (FFS)\n\n2. **Prone**: The patient lies flat on their stomach with their back and abdomen facing upward.\n\n3. **Decubitus**: The patient is positioned on their side, either right or left, to capture images of the lateral (side) view.\n\n4. **Left Lateral Decubitus**: The patient lies on their left side.\n\n5. **Right Lateral Decubitus**: The patient lies on their right side.\n\nThese different patient positions allow healthcare professionals to obtain specific views and information from medical imaging scans, aiding in diagnosis and treatment planning.","metadata":{}},{"cell_type":"code","source":"patient_positions = dicom_meta_df.groupby(\"patient id\")[\"patient position\"].max()\n\nplt.pie(\n    patient_positions.value_counts(),\n    labels=patient_positions.value_counts().index,\n    autopct=\"%.0f%%\",\n)\nplt.title(\"Patient Scan Position\")","metadata":{"_uuid":"d95972e0-8a5f-465a-a083-74f3c3572f82","_cell_guid":"7781cc85-2d80-4f21-bc58-04aa2770f56b","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:08:58.001254Z","iopub.execute_input":"2023-10-20T00:08:58.001805Z","iopub.status.idle":"2023-10-20T00:08:58.480565Z","shell.execute_reply.started":"2023-10-20T00:08:58.001772Z","shell.execute_reply":"2023-10-20T00:08:58.479684Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Taking a look at KVP (Kilovolt Peak):\nThe peak voltage of the X-ray machine used to acquire the image. It affects the quality and contrast of the image.\n\nAll patients used the same KVP for all their scans (KVP is consistent for a particular patient)","metadata":{"_uuid":"b0c0850c-b31f-4d4c-a741-3185165576ab","_cell_guid":"ee368914-d4f5-4e1d-9932-e4b7a8c42177","trusted":true}},{"cell_type":"code","source":"scan_kvp = dicom_meta_df.groupby(\"patient id\")[\"kvp\"].max()\n\nsns.barplot(y=scan_kvp.value_counts().index, x=scan_kvp.value_counts(), orient=\"h\")","metadata":{"_uuid":"2676159f-9360-47a3-9f55-559b1e05a064","_cell_guid":"b8d64629-3868-4c71-aa6a-fb2b5348aeb3","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:08:58.481743Z","iopub.execute_input":"2023-10-20T00:08:58.482623Z","iopub.status.idle":"2023-10-20T00:08:58.789611Z","shell.execute_reply.started":"2023-10-20T00:08:58.482588Z","shell.execute_reply":"2023-10-20T00:08:58.788833Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Most scans make use of 120 KVP to produce CT scans","metadata":{"_uuid":"bbc49eae-6c59-49df-8084-905bcd045a39","_cell_guid":"e9338f65-4e4c-42a5-ad09-88fa95039e97","trusted":true}},{"cell_type":"markdown","source":"#### How many series did each patient do?","metadata":{"_uuid":"c720bfff-24b0-4386-999d-4e29eb9a149b","_cell_guid":"716e59d5-8863-45ac-bcfa-0bcdc7bfc743","trusted":true}},{"cell_type":"code","source":"sns.histplot(\n    dicom_meta_df.groupby(by=[\"patient id\", \"series number\"])[\n        \"instance number\"\n    ].count(),\n    kde=True,\n    color=\"green\",\n)\nplt.title(\"Number of scans done by each patient in a series\")","metadata":{"_uuid":"7252b911-6000-4ab5-8af3-741982662a9b","_cell_guid":"f77bac47-35f6-4575-8bd0-c465e0c96be4","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:08:58.790544Z","iopub.execute_input":"2023-10-20T00:08:58.790784Z","iopub.status.idle":"2023-10-20T00:08:59.329352Z","shell.execute_reply.started":"2023-10-20T00:08:58.790762Z","shell.execute_reply":"2023-10-20T00:08:59.328466Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Most patients had between **100** and **250** scans made, with a few outliers having more than a **1000** scans.","metadata":{}},{"cell_type":"markdown","source":"### Taking a look at photometric interpretation\n\nThis attribute specifies how the pixel data in medical images should be interpreted and displayed. Some common values for Photometric Interpretation are:\n\n1. **MONOCHROME1**:\n   - a grayscale representation where higher pixel values correspond to darker shades (black is represented by the highest pixel value).\n   - Interpretation: This is commonly used for X-ray and other grayscale images where high pixel values indicate dense structures.\n#\n2. **MONOCHROME2**:\n   - a grayscale representation where higher pixel values correspond to lighter shades (white is represented by the highest pixel value).\n   - Interpretation: Similar to MONOCHROME1, this is often used for grayscale images but with reversed intensity mapping.\n\n3. **PALETTE COLOR**:\n   - Palette Color refers to images where pixel values are indices to a color lookup table (LUT) that maps indices to specific colors.\n   \n4. **RGB**:\n   - RGB (Red, Green, Blue) represents full-color images where each pixel has three color channels.\n   \n5. **YBR_FULL**:\n   - YBR_FULL (YCbCr Full) represents color images using the YCbCr color space with all color channels.\n\n6. **YBR_FULL_422**:\n   - YBR_FULL_422 is similar to YBR_FULL but uses subsampling to reduce the amount of chrominance information.\n \n7. **YBR_PARTIAL_420**:\n   - YBR_PARTIAL_420 represents color images using the YCbCr color space with further chrominance subsampling.\n","metadata":{"_uuid":"4a4488d8-c683-4761-a1a0-3a428e53d8d7","_cell_guid":"99c23008-fb3e-4a6f-8b8f-597107fd6f88","trusted":true}},{"cell_type":"code","source":"def load_dcm(path):\n    dcm = pydicom.dcmread(path)\n    data = dcm.pixel_array\n\n    if dcm.PixelRepresentation == 1:\n        bit_shift = dcm.BitsAllocated - dcm.BitsStored\n        dtype = data.dtype\n        data = (data << bit_shift).astype(dtype) >> bit_shift\n\n    # Scans are B&W\n    if dcm.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n\n    # Min-Max Normalization\n    data = data - np.min(data)\n    data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n    return data","metadata":{"_uuid":"efd0b5f9-074f-48d3-8922-ecdf4a1e2ff7","_cell_guid":"b0b05c5b-27a7-4985-ad91-e3c72871cbeb","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:08:59.330713Z","iopub.execute_input":"2023-10-20T00:08:59.330942Z","iopub.status.idle":"2023-10-20T00:08:59.336043Z","shell.execute_reply.started":"2023-10-20T00:08:59.330923Z","shell.execute_reply":"2023-10-20T00:08:59.335192Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from PIL import Image\n\nfig, axes = plt.subplots(2, 2)\naxes = iter(axes.flat)\nfig.suptitle(\"$MONOCHROME_2$ Photometric Interpretation\")\nfor file in list(COMPETITION_DATA_DIR.joinpath(\"train_images/10004/21057\").iterdir())[\n    :4\n]:\n    random_img = load_dcm(file)\n    ax = next(axes)\n    # The correct interpretation of MONOCHROME_2\n    ax.imshow(random_img, cmap=\"Greys_r\")","metadata":{"_uuid":"2c491dc1-f6fa-4de6-acc1-fb8e2a13180f","_cell_guid":"3aab8fc9-d804-4990-9a18-83ff5519898a","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:08:59.337121Z","iopub.execute_input":"2023-10-20T00:08:59.337384Z","iopub.status.idle":"2023-10-20T00:09:00.637264Z","shell.execute_reply.started":"2023-10-20T00:08:59.337363Z","shell.execute_reply":"2023-10-20T00:09:00.636372Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from PIL import Image\n\nfig, axes = plt.subplots(2, 2)\naxes = iter(axes.flat)\nfig.suptitle(\"$MONOCHROME_1$ Photometric Interpretation\")\n\nfor file in list(COMPETITION_DATA_DIR.joinpath(\"train_images/10004/21057\").iterdir())[:4]:\n    random_img = load_dcm(file)\n    ax = next(axes)\n    ax.imshow(random_img, cmap=\"Greys\")","metadata":{"_uuid":"c8972c45-dfb5-411a-8933-bc927f941727","_cell_guid":"22281a46-dcaf-4b07-be73-eee64c0cbd21","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:09:00.638673Z","iopub.execute_input":"2023-10-20T00:09:00.638916Z","iopub.status.idle":"2023-10-20T00:09:01.549401Z","shell.execute_reply.started":"2023-10-20T00:09:00.638895Z","shell.execute_reply":"2023-10-20T00:09:01.548536Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dicom_meta_df[[\"image position (patient)\", \"image orientation (patient)\"]].sample(4)","metadata":{"_uuid":"4230af26-eaf4-4caf-8d69-3bef3a665cf5","_cell_guid":"5dfcee39-5150-4406-81d3-f3c0a431ca0d","execution":{"iopub.status.busy":"2023-10-20T00:09:41.000412Z","iopub.execute_input":"2023-10-20T00:09:41.000745Z","iopub.status.idle":"2023-10-20T00:09:41.093894Z","shell.execute_reply.started":"2023-10-20T00:09:41.00071Z","shell.execute_reply":"2023-10-20T00:09:41.093029Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### An example Image Position was given as [-156.62109375, -419.12109375, -203] and Image Orientation was given as [1, 0, 0, 0, 1, 0], what does that mean?\n\n1. **Image Position (Patient)**: [-156.62109375, -419.12109375, -203]\n   - The Image Position (Patient) values specify the position of the upper left-hand corner (in the patient's coordinate system) of the image slice within the 3D volume of the patient. These values are usually given in millimeters.\n   - In this case, the first value (-156.62109375) indicates the position along the X-axis (left-to-right), the second value (-419.12109375) represents the position along the Y-axis (anterior-to-posterior), and the third value (-203) indicates the position along the Z-axis (head-to-foot).\n   - So, the image slice is positioned approximately 156.6 mm to the left, 419.1 mm anterior, and 203 mm below the reference point (typically a reference point within the patient).\n\n2. **Image Orientation (Patient)**: [1, 0, 0, 0, 1, 0]\n   - The Image Orientation (Patient) values define the orientation of the image slice within the patient's coordinate system. These values indicate the direction of the rows (X), columns (Y), and the slice plane normal (Z) in the patient's coordinate system.\n   - In this case, the values suggest that the image slice is aligned with the patient's body in the following way:\n     - Rows (X-direction) are parallel to the patient's right-to-left direction.\n     - Columns (Y-direction) are parallel to the patient's anterior-to-posterior direction.\n     - The slice plane normal (Z-direction) is perpendicular to the image slice and points in the head-to-foot direction.\n","metadata":{"_uuid":"741d2ab9-2e01-4d09-805d-34ee74076987","_cell_guid":"22989739-6596-4dc2-9491-6215d8e3d4c0","trusted":true}},{"cell_type":"markdown","source":"## Segmentations\n\n#### Inspired by: \n - https://www.kaggle.com/code/bvinning/interactive-viewing-of-scans-and-segmentations!\n - https://github.com/hbiom/DataViz_CT_CCR","metadata":{"_uuid":"d6730fb1-0f7d-40a2-bfad-152df686f55f","_cell_guid":"e34c9a6e-027a-4b16-b285-6b7a1dd27197","trusted":true}},{"cell_type":"code","source":"from matplotlib import animation\nfrom matplotlib.patches import Patch\nfrom matplotlib.lines import Line2D\nfrom skimage.measure import find_contours\nfrom matplotlib.colors import LinearSegmentedColormap\nfrom IPython.display import HTML\n\nSEGMENTATION_CODES = {\n    1: \"liver\",\n    2: \"spleen\",\n    3: \"kidney_left\",\n    4: \"kidney_right\",\n    5: \"bowel\",\n}\nSEGMENTATION_CODES_REVERSED = {value: key for key, value in SEGMENTATION_CODES.items()}\n\nSEGMENTATION_CMAP = plt.get_cmap(\"rainbow\")\nSEGMENTATION_PALETTE = np.array(\n    [[np.nan] * 4] + [SEGMENTATION_CMAP(x) for x in np.linspace(0, 1, 6)]\n)\n\n\ndef generate_injuries_img(seg_img, series_id):\n    injury_img = seg_img.copy()\n    patient_results = train_df[train_df[\"series_id\"] == series_id].iloc[0].astype(bool)\n    injured_organs = {\n        SEGMENTATION_CODES_REVERSED[\"liver\"]: ~patient_results.liver_healthy,\n        SEGMENTATION_CODES_REVERSED[\"spleen\"]: ~patient_results.spleen_healthy,\n        SEGMENTATION_CODES_REVERSED[\"kidney_left\"]: ~patient_results.kidney_healthy,\n        SEGMENTATION_CODES_REVERSED[\"kidney_right\"]: ~patient_results.kidney_healthy,\n        SEGMENTATION_CODES_REVERSED[\"bowel\"]: ~patient_results.bowel_healthy,\n    }\n\n    msk = np.vectorize(lambda x: injured_organs.get(x, False))(injury_img)\n    injury_img[msk] = 10  # Random constant picked for clarity\n    injury_img[~msk] = 0  # Random constant picked for clarity\n    return injury_img\n\n\ndef plot_img(series_img, seg_img, injury_img, ax):\n    series_img = load_dcm(series_img)\n    series_img = series_img\n    seg_img_coloured = SEGMENTATION_PALETTE[seg_img]\n\n    ax.imshow(series_img, cmap=\"Greys_r\")\n    ax.imshow(seg_img_coloured, alpha=0.5)\n\n    contours = find_contours(injury_img, 0)\n    for contour in contours:\n        ax.plot(contour[:, 1], contour[:, 0], linewidth=2, c=\"r\")\n\n    return {\"scan_img\": series_img, \"seg_img\": \"seg_img_coloured\"}\n\n\ndef plot_legend(ax):\n    #     mask_levels = [x for x in np.unique(seg_img) if x != 0]\n    legend_elements = [\n        Patch(\n            facecolor=SEGMENTATION_PALETTE[x],\n            edgecolor=SEGMENTATION_PALETTE[x],\n            label=SEGMENTATION_CODES[x],\n        )\n        for x in SEGMENTATION_CODES.keys()\n    ]\n    ax.legend(handles=legend_elements, loc=\"upper right\")\n\n\ndef load_series_segmentation(series_id):\n    seg = nib.load(COMPETITION_DATA_DIR.joinpath(f\"segmentations/{series_id}.nii\"))\n    seg = nib.as_closest_canonical(seg)  # Ensures output is RAS, DICOM is LPS.\n    return seg.get_fdata().astype(int)[::-1, ::-1, :]  # Reverse to match Z order","metadata":{"_uuid":"36a7a6bf-8618-46fa-8348-da7a24f29e7c","_cell_guid":"8e946d18-16a5-4777-9904-e4bff6e9c1c9","_kg_hide-input":true,"jupyter":{"source_hidden":true},"execution":{"iopub.status.busy":"2023-10-20T00:09:46.615523Z","iopub.execute_input":"2023-10-20T00:09:46.615859Z","iopub.status.idle":"2023-10-20T00:09:46.704003Z","shell.execute_reply.started":"2023-10-20T00:09:46.615832Z","shell.execute_reply":"2023-10-20T00:09:46.703214Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def visualize_patient_series(series_id, no_frames=50):\n    patient_id = dicom_meta_df[dicom_meta_df[\"_series_id\"] == series_id][\n        \"_patient_id\"\n    ].unique()[0]\n    scans = list(\n        COMPETITION_DATA_DIR.joinpath(\n            f\"train_images/{patient_id}/{series_id}/\"\n        ).iterdir()\n    )\n    scans = list(\n        sorted(scans, key=lambda f: int(\"\".join([c for c in f.name if c.isdigit()])))\n    )\n    segmentations = load_series_segmentation(series_id)\n\n    no_scans = len(scans)\n    indx_scans_to_display = iter(range(no_scans)[:: no_scans // no_frames])\n\n    # Create a figure and axis for plotting\n    fig, ax = plt.subplots(figsize=(10, 8))\n    plot_legend(ax)\n\n    series_img = load_dcm(scans[0])\n    seg_img = segmentations[:, :, -1].T\n\n    injury_img = generate_injuries_img(seg_img, series_id)\n    seg_img_coloured = SEGMENTATION_PALETTE[seg_img]\n\n    fig_scan_img = ax.imshow(series_img, cmap=\"Greys_r\")\n    fig_seg_img = ax.imshow(seg_img_coloured, alpha=0.5)\n\n    contours = find_contours(injury_img, 0)\n    fax_c = {}\n    for indx, contour in enumerate(contours):\n        fax_c[indx] = ax.plot(contour[:, 1], contour[:, 0], linewidth=4, c=\"r\")\n\n    def update_animation(img_num, *args, **kwargs):\n        img_indx = next(indx_scans_to_display)\n\n        seg_img = segmentations[:, :, -(img_indx + 1)].T\n        seg_img_coloured = SEGMENTATION_PALETTE[seg_img]\n\n        series_img = load_dcm(scans[img_indx])\n\n        fig_scan_img.set_array(series_img)\n        fig_seg_img.set_array(seg_img_coloured)\n\n        for line in ax.get_lines():  # ax.lines:\n            line.remove()\n        \n        injury_img = generate_injuries_img(seg_img, series_id)\n        contours = find_contours(injury_img, 0)\n        for indx, contour in enumerate(contours):\n            fax_c[indx] = ax.plot(contour[:, 1], contour[:, 0], linewidth=4, c=\"r\")\n\n        return [fig_scan_img, fig_seg_img] + list(fax_c.values())\n\n    \n    # Create the animation\n    amn = animation.FuncAnimation(\n        fig, update_animation, frames=no_frames, repeat=True, repeat_delay=50000\n    )\n\n    # Demonstrate the animation\n    return HTML(amn.to_jshtml())","metadata":{"_uuid":"3ccdf0c9-0fdd-4e51-a52a-931c26434c61","_cell_guid":"b4891e9f-6d4e-4c54-9009-b1169af0cedf","_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-10-20T00:09:46.894475Z","iopub.execute_input":"2023-10-20T00:09:46.895079Z","iopub.status.idle":"2023-10-20T00:09:46.904604Z","shell.execute_reply.started":"2023-10-20T00:09:46.895051Z","shell.execute_reply":"2023-10-20T00:09:46.903727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### What's in the Animation?\nThe `visualize_patient_series` creates a visual animation of the different scans in a series, with annotations of different organs. Organs with internal injuries have a red outline about them.","metadata":{}},{"cell_type":"code","source":"visualize_patient_series(10000, no_frames=50)","metadata":{"_uuid":"1e7e721e-6fee-46ef-a705-8c946cf74224","_cell_guid":"1ea5e187-33bd-4ba9-af5b-81d3e3ff682d","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:13:34.807591Z","iopub.execute_input":"2023-10-20T00:13:34.807918Z","iopub.status.idle":"2023-10-20T00:13:55.791646Z","shell.execute_reply.started":"2023-10-20T00:13:34.807894Z","shell.execute_reply":"2023-10-20T00:13:55.790763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"visualize_patient_series(13041, no_frames=50)","metadata":{"_uuid":"c0c7db12-4f2d-41f1-82a0-ee89a2ff6ae9","_cell_guid":"1df23be4-7a84-4734-9e02-f2b6840070f2","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2023-10-20T00:13:55.793022Z","iopub.execute_input":"2023-10-20T00:13:55.793282Z","iopub.status.idle":"2023-10-20T00:14:18.178066Z","shell.execute_reply.started":"2023-10-20T00:13:55.793258Z","shell.execute_reply":"2023-10-20T00:14:18.177205Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"_uuid":"72859440-e26e-49ca-acd8-61ed4ee0d1ae","_cell_guid":"deb9aac5-9854-4b61-bc94-923a63746482","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"_uuid":"2a0cf5ce-e72d-4f8f-a690-f5ba9f608597","_cell_guid":"0cd30265-7ffd-4d73-bbe7-c092eaf92960","collapsed":false,"jupyter":{"outputs_hidden":false},"trusted":true},"execution_count":null,"outputs":[]}]}