{"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":"\n# Import packages","metadata":{"execution":{"iopub.status.busy":"2023-08-18T19:41:32.389546Z","iopub.execute_input":"2023-08-18T19:41:32.38999Z","iopub.status.idle":"2023-08-18T19:41:32.39528Z","shell.execute_reply.started":"2023-08-18T19:41:32.389955Z","shell.execute_reply":"2023-08-18T19:41:32.394021Z"}}},{"cell_type":"code","source":"!pip install dicomsdl","metadata":{"execution":{"iopub.status.busy":"2023-08-20T15:53:36.527282Z","iopub.execute_input":"2023-08-20T15:53:36.527661Z","iopub.status.idle":"2023-08-20T15:53:50.714223Z","shell.execute_reply.started":"2023-08-20T15:53:36.527632Z","shell.execute_reply":"2023-08-20T15:53:50.712347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore', category=UserWarning)\nfrom pathlib import Path\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport nibabel as nib\nimport pydicom as pyd\nimport dicomsdl as dic\nimport plotly.express as px","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-08-20T17:33:14.682382Z","iopub.execute_input":"2023-08-20T17:33:14.682773Z","iopub.status.idle":"2023-08-20T17:33:15.470892Z","shell.execute_reply.started":"2023-08-20T17:33:14.682743Z","shell.execute_reply":"2023-08-20T17:33:15.469608Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# TFX Installation","metadata":{}},{"cell_type":"code","source":"!pip install --user tensorflow_data_validation","metadata":{"scrolled":true,"execution":{"iopub.status.busy":"2023-08-20T15:11:46.49859Z","iopub.execute_input":"2023-08-20T15:11:46.498973Z","iopub.status.idle":"2023-08-20T15:13:05.470022Z","shell.execute_reply.started":"2023-08-20T15:11:46.498942Z","shell.execute_reply":"2023-08-20T15:13:05.468711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow_data_validation as tfdv\nprint('TFDV version:', tfdv.version.__version__)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T15:13:05.472657Z","iopub.execute_input":"2023-08-20T15:13:05.47313Z","iopub.status.idle":"2023-08-20T15:13:11.899006Z","shell.execute_reply.started":"2023-08-20T15:13:05.473088Z","shell.execute_reply":"2023-08-20T15:13:11.897766Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Main Folder\n### Define main compatition data directory","metadata":{}},{"cell_type":"code","source":"root = Path('/kaggle/input/rsna-2023-abdominal-trauma-detection')","metadata":{"execution":{"iopub.status.busy":"2023-08-20T15:13:11.900501Z","iopub.execute_input":"2023-08-20T15:13:11.901287Z","iopub.status.idle":"2023-08-20T15:13:11.906888Z","shell.execute_reply.started":"2023-08-20T15:13:11.901251Z","shell.execute_reply":"2023-08-20T15:13:11.905902Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Read train.csv\n### In this cell, we split kidney, liver, and spleen organs which have three target level into binary and categorical mode.","metadata":{}},{"cell_type":"code","source":"def get_category_value(feat,instance):\n    if instance[feat+'_low']:\n        return 'low'\n    elif instance[feat+'_high']:\n        return 'high'\n    else:\n        return 'healthy'\ntrain_df = pd.read_csv(root.joinpath('train.csv'))\ntrain_df['bowel'] = train_df.apply(lambda x: 1 if x.bowel_healthy == 1 else 0,axis=1)\ntrain_df['extravasation'] = train_df.apply(lambda x: 1 if x.extravasation_healthy == 1 else 0,axis=1)\ntrain_df['kidney_binary'] = train_df.apply(lambda x: 0 if x.kidney_low == 1 or x.kidney_high else 1,axis=1)\ntrain_df['liver_binary'] = train_df.apply(lambda x: 0 if x.liver_low == 1 or x.liver_high else 1,axis=1)\ntrain_df['spleen_binary'] = train_df.apply(lambda x: 0 if x.spleen_low == 1 or x.spleen_high else 1,axis=1)\n\ntrain_df['kidney_category'] = train_df.apply(lambda x: get_category_value('kidney',x),axis=1)\ntrain_df['liver_category'] = train_df.apply(lambda x: get_category_value('liver',x),axis=1)\ntrain_df['spleen_category'] = train_df.apply(lambda x: get_category_value('spleen',x),axis=1)\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2023-08-20T15:19:57.082186Z","iopub.execute_input":"2023-08-20T15:19:57.082596Z","iopub.status.idle":"2023-08-20T15:19:57.679829Z","shell.execute_reply.started":"2023-08-20T15:19:57.082565Z","shell.execute_reply":"2023-08-20T15:19:57.678744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualize train.csv statistics state\n### We visualize the organs states","metadata":{}},{"cell_type":"code","source":"approved_cols = ['any_injury','bowel','extravasation','kidney_binary','liver_binary','spleen_binary','kidney_category', 'liver_category', 'spleen_category']\nstats_options = tfdv.StatsOptions(feature_allowlist=approved_cols)\ntrain_stats = tfdv.generate_statistics_from_dataframe(train_df, stats_options)\ntfdv.visualize_statistics(train_stats)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T15:19:59.433988Z","iopub.execute_input":"2023-08-20T15:19:59.434741Z","iopub.status.idle":"2023-08-20T15:19:59.511351Z","shell.execute_reply.started":"2023-08-20T15:19:59.434707Z","shell.execute_reply":"2023-08-20T15:19:59.510208Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Some definitions\n### **Anatomical Plane**: An anatomical plane is an imaginary flat surface that divides the body into two parts. There are three principal anatomical planes\n\n<p align=\"center\" width=\"100%\">\n    <img width=\"100%\" height=\"90%\" src=\"https://physiquedevelopment.com/wp-content/uploads/2020/02/Planes-of-Motion.jpg\">\n</p>\n\n#### **Sagittal** plane (also known as the lateral plane): This plane divides the body into right and left halves. It runs parallel to the median plane, which is the imaginary line that runs down the center of the body from the head to the feet. you can dealt with that [0,1,0,0,0,-1].\n\n#### **Coronal** plane (also known as the frontal plane): This plane divides the body into front and back halves. It runs perpendicular to the sagittal plane. you can dealt with that [1,0,0,0,0,-1].\n\n\n#### **Transverse** plane (also known as the axial plane): This plane divides the body into upper and lower halves. It runs perpendicular to both the sagittal and coronal planes. you can dealt with that [1,0,0,0,1,0].","metadata":{}},{"cell_type":"markdown","source":"### Let's dive-in to determine the anatomical plane","metadata":{}},{"cell_type":"code","source":"def determine_plane(plane):\n    plane = np.cross(plane[0:3], plane[3:6])\n    plane = [abs(x) for x in plane]\n    if plane[0] == 1:\n        return \"Sagittal\"\n    elif plane[1] == 1:\n        return \"Coronal\"\n    elif plane[2] == 1:\n        return \"Axial\"\n    \np_dicom_path = \"/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/12332/15276/119.dcm\"\np_dicom = dic.open(p_dicom_path)\npix_img = p_dicom.pixelData()\np_ori = determine_plane(p_dicom.ImageOrientationPatient)\nplt.imshow(pix_img)\nplt.axis('off')\nplt.title(\"Sample\")\nplt.figtext(0.5, 0.02, f'Anatomical Plane: {p_ori}', wrap=True, horizontalalignment='center', fontsize=12)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-08-20T16:15:08.905809Z","iopub.execute_input":"2023-08-20T16:15:08.907032Z","iopub.status.idle":"2023-08-20T16:15:09.127566Z","shell.execute_reply.started":"2023-08-20T16:15:08.906993Z","shell.execute_reply":"2023-08-20T16:15:09.126379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Read Dicom instances\n### 2D","metadata":{}},{"cell_type":"code","source":"class Dicom2D(object):\n    def __init__(self,filename):\n        self.dcm = dic.open(filename)\n        infos = self.dcm.getPixelDataInfo()\n        self.center = infos['WindowCenter']\n        self.width = infos['WindowWidth']\n        self.intercept = infos['RescaleIntercept']\n        self.slope = infos['RescaleSlope']\n    def plane(self):\n        return determine_plane(self.dcm.ImageOrientationPatient)\n    def img(self):\n        img = self.dcm.pixelData()\n        \n        low = self.center - self.width / 2\n        high = self.center + self.width / 2\n        \n        img = (img*self.slope)+self.intercept\n        img = np.clip(img, low, high)\n        img = (img / np.max(img) * 255).astype(np.int16)\n        return img","metadata":{"execution":{"iopub.status.busy":"2023-08-20T17:10:33.547265Z","iopub.execute_input":"2023-08-20T17:10:33.547878Z","iopub.status.idle":"2023-08-20T17:10:33.555481Z","shell.execute_reply.started":"2023-08-20T17:10:33.547832Z","shell.execute_reply":"2023-08-20T17:10:33.554687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"some_dcm_path = [\n    '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/1027/24515/31.dcm',\n    '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/11656/37803/208.dcm',\n    '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/12893/992/1.dcm',\n    '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/1278/34096/106.dcm',\n    '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/12893/992/105.dcm',\n    '/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/13496/317/224.dcm'\n]\n\nfig,axes = plt.subplots(1,len(some_dcm_path),figsize=(25, 10))\nfor idx in range(len(some_dcm_path)):\n    dcm_obj = Dicom2D(some_dcm_path[idx])\n    axes[idx].imshow(dcm_obj.img())\n    axes[idx].axis('off')\n    axes[idx].set_title(f\"Sample {idx+1} - {dcm_obj.plane()}\")\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2023-08-20T17:10:34.070028Z","iopub.execute_input":"2023-08-20T17:10:34.071238Z","iopub.status.idle":"2023-08-20T17:10:34.949477Z","shell.execute_reply.started":"2023-08-20T17:10:34.071199Z","shell.execute_reply":"2023-08-20T17:10:34.948323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3D","metadata":{}},{"cell_type":"code","source":"class Dicom3D(object):\n    def __init__(self,case_dir:Path):\n        self.case_name = case_dir.stem\n        self.filenames =  list(case_dir.glob('*.dcm'))\n        self.filenames = sorted(self.filenames,key=lambda x: int(x.stem.split('.')[0]))\n        self.tensor = self.read_pyd_frames()\n        self.ann = self.read_annotation()\n        self._ori = 'axial'\n        \n    def read_annotation(self):\n        try:\n            ann = nib.load(str(Path('/kaggle/input/rsna-2023-abdominal-trauma-detection/segmentations/').joinpath(f\"{self.case_name}.nii\"))).get_fdata()\n            ann = np.transpose(ann, [1, 0, 2])\n            ann = np.rot90(ann, 1, (1,2))\n            ann = ann[::-1,:,:]\n            ann = np.transpose(ann, [1, 0, 2])\n            return ann\n        except:\n            return None\n        \n    def axial(self):\n        if self._ori == 'coronal':\n            self.tensor = self.tensor.transpose([1,0,2])\n            if self.ann is not None:\n                self.ann = self.ann.transpose([1,0,2])\n            self._ori = 'axial'\n        elif self._ori == 'sagittal':\n            self.tensor = self.tensor.transpose([1,2,0])\n            if self.ann is not None:\n                self.ann = self.ann.transpose([1,2,0])\n            self._ori = 'axial'\n        return self\n    \n    def coronal(self):\n        if self._ori=='coronal':\n            return self\n        elif self._ori=='sagittal':\n            self.axial()\n        self.tensor = self.tensor.transpose([1,0,2])\n        if self.ann is not None:\n            self.ann = self.ann.transpose([1,0,2])\n        self._ori='coronal'\n        return self\n    \n    def sagittal(self):\n        if self._ori=='sagittal':\n            return self\n        elif self._ori=='coronal':\n            self.axial()\n        self.tensor = self.tensor.transpose([2,0,1])\n        if self.ann is not None:\n            self.ann = self.ann.transpose([2,0,1])\n        self._ori='sagittal'\n        return self\n\n        \n    def read_pyd_frames(self):\n        voxels = []\n        for filename in self.filenames:\n            ds = pyd.dcmread(filename)\n            image = ds.pixel_array\n            # find rescale params\n            if (\"RescaleIntercept\" in ds) and (\"RescaleSlope\" in ds):\n                intercept = float(ds.RescaleIntercept)\n                slope = float(ds.RescaleSlope)\n\n            # find clipping params\n            center = int(ds.WindowCenter)\n            width = int(ds.WindowWidth)\n            low = center - width / 2\n            high = center + width / 2    \n\n\n            image = (image * slope) + intercept\n            image = np.clip(image, low, high)\n\n            image = (image / np.max(image) * 255).astype(np.int16)\n            voxels.append(image)\n        return np.stack(voxels,axis=0) \n    \n    def read_frames(self):\n        voxels = []\n        for filename in self.filenames:\n            dcm = dic.open(str(filename))\n            img = dcm.pixelData()\n            infos = dcm.getPixelDataInfo()\n            center = infos['WindowCenter']\n            width = infos['WindowWidth']\n            intercept = infos['RescaleIntercept']\n            slope = infos['RescaleSlope']\n            \n            low = center - width / 2\n            high = center + width / 2\n\n            img = (img*slope)+intercept\n            img = np.clip(img, low, high)\n            img = (img / np.max(img) * 255).astype(np.int16)\n            voxels.append(img)\n        return np.stack(voxels,axis=0)        ","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:29:47.032847Z","iopub.execute_input":"2023-08-20T19:29:47.033349Z","iopub.status.idle":"2023-08-20T19:29:47.057885Z","shell.execute_reply.started":"2023-08-20T19:29:47.033312Z","shell.execute_reply":"2023-08-20T19:29:47.057068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_inline(volume,imz=10):\n    rb_range = np.linspace(0, volume.shape[0]-1, imz).astype(np.int16)\n    fig,axes = plt.subplots(1,imz,figsize=(25, 15))\n    for idx,sl in enumerate(rb_range):\n        axes[idx].imshow(volume[sl,:,:],cmap='gray')\n        axes[idx].axis('off')\n    fig.show()","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:30:54.284905Z","iopub.execute_input":"2023-08-20T19:30:54.285295Z","iopub.status.idle":"2023-08-20T19:30:54.293444Z","shell.execute_reply.started":"2023-08-20T19:30:54.285265Z","shell.execute_reply":"2023-08-20T19:30:54.292147Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"p = Path('/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/10026/29700')\nvox_obj = Dicom3D(p)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:29:48.185085Z","iopub.execute_input":"2023-08-20T19:29:48.185493Z","iopub.status.idle":"2023-08-20T19:30:00.020118Z","shell.execute_reply.started":"2023-08-20T19:29:48.185463Z","shell.execute_reply":"2023-08-20T19:30:00.018891Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Axial** without Annotation","metadata":{}},{"cell_type":"code","source":"vox_obj.axial()\nplot_inline(vox_obj.tensor)\nif vox_obj.ann is not None:\n    plot_inline(vox_obj.ann)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:30:24.333451Z","iopub.execute_input":"2023-08-20T19:30:24.334341Z","iopub.status.idle":"2023-08-20T19:30:25.07814Z","shell.execute_reply.started":"2023-08-20T19:30:24.334302Z","shell.execute_reply":"2023-08-20T19:30:25.077004Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Coronal** without Annotation","metadata":{}},{"cell_type":"code","source":"vox_obj.coronal()\nplot_inline(vox_obj.tensor)\nif vox_obj.ann is not None:\n    plot_inline(vox_obj.ann)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:30:34.109268Z","iopub.execute_input":"2023-08-20T19:30:34.10997Z","iopub.status.idle":"2023-08-20T19:30:35.437259Z","shell.execute_reply.started":"2023-08-20T19:30:34.109934Z","shell.execute_reply":"2023-08-20T19:30:35.436245Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Sagittal** without Annotation","metadata":{}},{"cell_type":"code","source":"vox_obj.sagittal()\nplot_inline(vox_obj.tensor)\nif vox_obj.ann is not None:\n    plot_inline(vox_obj.ann)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:30:38.858334Z","iopub.execute_input":"2023-08-20T19:30:38.859166Z","iopub.status.idle":"2023-08-20T19:30:39.591684Z","shell.execute_reply.started":"2023-08-20T19:30:38.859129Z","shell.execute_reply":"2023-08-20T19:30:39.590524Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"p = Path('/kaggle/input/rsna-2023-abdominal-trauma-detection/train_images/10004/21057')\nvox_obj = Dicom3D(p)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:32:02.983773Z","iopub.execute_input":"2023-08-20T19:32:02.984278Z","iopub.status.idle":"2023-08-20T19:32:24.30403Z","shell.execute_reply.started":"2023-08-20T19:32:02.984241Z","shell.execute_reply":"2023-08-20T19:32:24.302761Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Axial** with Annotation","metadata":{}},{"cell_type":"code","source":"vox_obj.axial()\nplot_inline(vox_obj.tensor)\nif vox_obj.ann is not None:\n    plot_inline(vox_obj.ann)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:32:29.125777Z","iopub.execute_input":"2023-08-20T19:32:29.126486Z","iopub.status.idle":"2023-08-20T19:32:30.558408Z","shell.execute_reply.started":"2023-08-20T19:32:29.126454Z","shell.execute_reply":"2023-08-20T19:32:30.557444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Coronal** without Annotation","metadata":{}},{"cell_type":"code","source":"vox_obj.coronal()\nplot_inline(vox_obj.tensor)\nif vox_obj.ann is not None:\n    plot_inline(vox_obj.ann)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:34:38.068089Z","iopub.execute_input":"2023-08-20T19:34:38.068491Z","iopub.status.idle":"2023-08-20T19:34:40.087896Z","shell.execute_reply.started":"2023-08-20T19:34:38.068461Z","shell.execute_reply":"2023-08-20T19:34:40.086845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### **Sagittal** without Annotation","metadata":{}},{"cell_type":"code","source":"vox_obj.sagittal()\nplot_inline(vox_obj.tensor)\nif vox_obj.ann is not None:\n    plot_inline(vox_obj.ann)","metadata":{"execution":{"iopub.status.busy":"2023-08-20T19:34:26.282216Z","iopub.execute_input":"2023-08-20T19:34:26.283229Z","iopub.status.idle":"2023-08-20T19:34:28.440114Z","shell.execute_reply.started":"2023-08-20T19:34:26.283185Z","shell.execute_reply":"2023-08-20T19:34:28.438935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Notes\n1. All dicom files have axial plane\n2. **Coronal** and **Sagittal** planes depend on number of sequences that **each series** have.","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}