{"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 gdcm","metadata":{"execution":{"iopub.status.busy":"2022-08-20T14:18:07.365191Z","iopub.execute_input":"2022-08-20T14:18:07.36565Z","iopub.status.idle":"2022-08-20T14:18:18.189218Z","shell.execute_reply.started":"2022-08-20T14:18:07.365615Z","shell.execute_reply":"2022-08-20T14:18:18.18793Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ROOT = \"/kaggle/input/rsna-2022-cervical-spine-fracture-detection/\"","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-08-20T12:02:59.66825Z","iopub.execute_input":"2022-08-20T12:02:59.668755Z","iopub.status.idle":"2022-08-20T12:02:59.699498Z","shell.execute_reply.started":"2022-08-20T12:02:59.668659Z","shell.execute_reply":"2022-08-20T12:02:59.698395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd \nimport numpy as np\nimport pydicom\nimport SimpleITK as sitk\nfrom pathlib import Path\n\nfrom typing import List\nfrom ipywidgets import interact, widgets\n\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nplt.style.use(\"bmh\")","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:02:59.701515Z","iopub.execute_input":"2022-08-20T12:02:59.701853Z","iopub.status.idle":"2022-08-20T12:03:00.434073Z","shell.execute_reply.started":"2022-08-20T12:02:59.701823Z","shell.execute_reply":"2022-08-20T12:03:00.433101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Understanding the train.csv annotations","metadata":{}},{"cell_type":"code","source":"df = pd.read_csv(Path(ROOT)/\"train.csv\")\nprint(df.shape)\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:03:00.435346Z","iopub.execute_input":"2022-08-20T12:03:00.435852Z","iopub.status.idle":"2022-08-20T12:03:00.47853Z","shell.execute_reply.started":"2022-08-20T12:03:00.43582Z","shell.execute_reply":"2022-08-20T12:03:00.477012Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"- we have StudyInstanceUID of a patient \n- The patient level outcome, i.e. if any of the vertebrae are fractured.\n- 7 columns representing 7 vertebrates telling if which vertebrae got fractured (1) within the studyInstancUID","metadata":{}},{"cell_type":"markdown","source":"> Percentage people with atleast one vertebrae of the given study is fractured or not.\n\n52.4%","metadata":{}},{"cell_type":"code","source":"\ntarget = df[\"patient_overall\"].value_counts()\nx, y = target.index, target.values \n\nfig, ax = plt.subplots(figsize=(8, 3.5), nrows=1, ncols=1)\nax.bar(x, y)\nax.set_title(\"patient_overall\")\nfor i in range(len(x)):\n    ax.text(i,y[i], f\"{y[i]}-{round((y[i]/sum(y))*100, 2)}%\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:03:00.482109Z","iopub.execute_input":"2022-08-20T12:03:00.482587Z","iopub.status.idle":"2022-08-20T12:03:00.747129Z","shell.execute_reply.started":"2022-08-20T12:03:00.48255Z","shell.execute_reply":"2022-08-20T12:03:00.745599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> Each vertebrae has following % of fractured cases within the training dataset \n\n- C1 = 7.23%\n- C2 = 14.12%\n- C3 = 3.62%\n- C4 = 5.35%\n- C5 = 8.02%\n- C6 = 13.72%\n- C7 = 19.47%","metadata":{}},{"cell_type":"code","source":"target_each = df[[f\"C{i}\" for i in range(1, 8)]].sum(axis=0)/df.shape[0]\nx, y = target_each.index, target_each.values\n\nfig, ax = plt.subplots(figsize=(8, 3.5), nrows=1, ncols=1)\nax.bar(x, y)\nax.set_title(\"Individual vertebrae % fractured cases\")\nfor i in range(len(x)):\n    ax.text(i,y[i], f\"{round(y[i]*100, 2)}%\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:03:00.749105Z","iopub.execute_input":"2022-08-20T12:03:00.750031Z","iopub.status.idle":"2022-08-20T12:03:01.127071Z","shell.execute_reply.started":"2022-08-20T12:03:00.749993Z","shell.execute_reply":"2022-08-20T12:03:01.126269Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> How many vertebrae fractures are in a study ?\n\n- 1058 cases doesn't have any type of fracture\n- 623 has one type of fracture\n- 239 has two\n- 64 has three\n- 26 has four\n- 7 and 2 cases has five and six fractures","metadata":{}},{"cell_type":"code","source":"target_overall = df[[f\"C{i}\" for i in range(1, 8)]].sum(axis=1).value_counts()\nx, y = target_overall.index, target_overall.values\n\nfig, ax = plt.subplots(figsize=(8, 3.5), nrows=1, ncols=1)\nax.bar(x, y)\nax.set_title(\"Individual vertebrae % fractured cases\")\nfor i in range(len(x)):\n    ax.text(i,y[i], f\"{y[i]}-{round((y[i]/sum(y))*100, 2)}%\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:03:01.12859Z","iopub.execute_input":"2022-08-20T12:03:01.129201Z","iopub.status.idle":"2022-08-20T12:03:01.386557Z","shell.execute_reply.started":"2022-08-20T12:03:01.129168Z","shell.execute_reply":"2022-08-20T12:03:01.385071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## DICOM data\n- Scans are stored in dicom (.dcm) format. \n- Each slice in a scan is a 2D array stored in (.dcm) format. the .dcm file also has meta data including patient info.","metadata":{}},{"cell_type":"code","source":"\nfrom pydantic import BaseModel\nfrom typing import List, Optional \n\nclass ScanData(BaseModel):\n    series_id: str\n    pixel_array: Optional[np.ndarray]\n    bone_pixel_array: Optional[np.ndarray]\n    meta_data: Optional[pd.DataFrame]\n    labels: Optional[List[str]]\n    bbox: Optional[List[List[float]]] #[x,y, w, h, slice_number]\n    segm: Optional[List[np.ndarray]]\n    \n    class Config:\n        arbitrary_types_allowed = True\n\nclass DCMDB:\n    #A class function to read meta info and image from dicom folders.\n    def __init__(self, root, dtype=\"train_images\"):\n        self.root=root \n        self.dtype = dtype\n        self.dcm_folders = list((Path(self.root)/self.dtype).glob(\"*\"))\n        self.series_ids = [study_id.name for study_id in self.dcm_folders]\n    \n    def __getitem__(self, idx):\n        study_id = self.series_ids[idx] if isinstance(idx, int) else idx\n        loc = self.root/Path(self.dtype)/Path(study_id)\n        pixel_array, meta_data = self.load_scan(loc)\n        data = ScanData(series_id= study_id, pixel_array=pixel_array, meta_data=meta_data)\n        return data\n    \n    def __len__(self):\n        return len(self.dcm_folders)\n    \n    def list_dcms(self, idx):\n        if isinstance(idx, int):\n            return list(self.dcm_folders[idx].glob(\"*.dcm\"))\n        else:\n            return list(idx.glob(\"*.dcm\"))\n    \n    @staticmethod\n    def get_observation_data(path: Path):\n        '''\n        Get information from the .dcm files\n        Source: https://www.kaggle.com/code/kretes/fork-of-great-eda-with-fix-for-slice-count-dist\n        '''\n\n        dataset = pydicom.read_file(path)\n\n        # Dictionary to store the information from the image\n        observation_data = {\n            \"file_name\": path.name,\n            \"Rows\" : dataset.get(\"Rows\"),\n            \"Columns\" : dataset.get(\"Columns\"),\n            \"SOPInstanceUID\" : dataset.get(\"SOPInstanceUID\"),\n            \"ContentDate\" : dataset.get(\"ContentDate\"),\n            \"SliceThickness\" : dataset.get(\"SliceThickness\"),\n            \"InstanceNumber\" : dataset.get(\"InstanceNumber\"),\n            \"ImagePositionPatient\" : dataset.get(\"ImagePositionPatient\"),\n            \"ImageOrientationPatient\" : dataset.get(\"ImageOrientationPatient\"),\n        }\n\n        # String columns\n        str_columns = [\"SOPInstanceUID\", \"ContentDate\"]\n        for k in str_columns:\n            observation_data[k] = str(dataset.get(k)) if k in dataset else None\n        return observation_data\n    \n    def get_meta_of_scan(self, path: Path):\n        meta_data = pd.DataFrame([self.get_observation_data(i) for i in x.list_dcms(path)])\n        meta_data = meta_data.sort_values(\"InstanceNumber\").reset_index(drop=True)\n        return meta_data\n    \n    def load_scan(self, path: Path):\n        meta_data = self.get_meta_of_scan(path)\n        \n        # lets make sure that we constant slice_thickness \n        assert meta_data[\"SliceThickness\"].unique().shape[0] == 1, \"We have different slice thickness values\"\n        \n        #Lets make sure that files are \n        file_names = meta_data[\"file_name\"].values.tolist()\n        \n        pixel_array = np.concatenate([np.expand_dims(pydicom.dcmread(path/filename).pixel_array, 0) for filename in file_names])\n        return pixel_array, meta_data","metadata":{"execution":{"iopub.status.busy":"2022-08-20T14:06:29.621707Z","iopub.execute_input":"2022-08-20T14:06:29.62234Z","iopub.status.idle":"2022-08-20T14:06:29.647773Z","shell.execute_reply.started":"2022-08-20T14:06:29.622297Z","shell.execute_reply":"2022-08-20T14:06:29.646391Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"x = DCMDB(ROOT)\nscan_info = x[np.random.randint(len(x))]\nprint(scan_info.series_id, scan_info.pixel_array.shape, scan_info.meta_data.shape)\nscan_info.meta_data.head()","metadata":{"execution":{"iopub.status.busy":"2022-08-20T14:06:30.061256Z","iopub.execute_input":"2022-08-20T14:06:30.061726Z","iopub.status.idle":"2022-08-20T14:06:34.733514Z","shell.execute_reply.started":"2022-08-20T14:06:30.061689Z","shell.execute_reply":"2022-08-20T14:06:34.732077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Visualize a Scan","metadata":{"execution":{"iopub.status.busy":"2022-08-15T17:50:21.721225Z","iopub.execute_input":"2022-08-15T17:50:21.721645Z","iopub.status.idle":"2022-08-15T17:50:21.729721Z","shell.execute_reply.started":"2022-08-15T17:50:21.721619Z","shell.execute_reply":"2022-08-15T17:50:21.728431Z"}}},{"cell_type":"code","source":"def plot_matplotlib_scans3d(img_arr_list: List, title_list: List = None, fsize: int = 7, rgb=False):\n    \"\"\"Plot 3D scans using matplotlib.\n    \"\"\"\n    cols = len(img_arr_list)\n    num_slices = img_arr_list[0].shape[0]\n    print(cols, num_slices)\n\n    def callback(z=None):\n\n        fig, ax = plt.subplots(1, cols, figsize=(fsize * cols, fsize * cols), squeeze=False)\n        for idx in range(cols):\n            if rgb:\n                ax[0][idx].imshow(img_arr_list[idx][z])\n            else:\n                ax[0][idx].imshow(img_arr_list[idx][z], cmap=\"bone\")\n            ax[0][idx].title.set_text(title_list[idx])\n            ax[0][idx].axis(\"off\")\n            ax[0][idx].grid(False)\n\n        fig.tight_layout()\n        fig.show()\n\n    interact(\n        callback,\n        z=widgets.IntSlider(value=0, min=0, max=(num_slices - 1), step=1),\n    )\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-08-20T14:19:48.388685Z","iopub.execute_input":"2022-08-20T14:19:48.389733Z","iopub.status.idle":"2022-08-20T14:19:48.401404Z","shell.execute_reply.started":"2022-08-20T14:19:48.389665Z","shell.execute_reply":"2022-08-20T14:19:48.400355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_matplotlib_scans3d([scan_info.pixel_array], [f\"CT Scan: {scan_info.series_id}\"])","metadata":{"execution":{"iopub.status.busy":"2022-08-20T14:20:15.537377Z","iopub.execute_input":"2022-08-20T14:20:15.537854Z","iopub.status.idle":"2022-08-20T14:20:15.793247Z","shell.execute_reply.started":"2022-08-20T14:20:15.537819Z","shell.execute_reply":"2022-08-20T14:20:15.792084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Windowing \nhttps://radiopaedia.org/articles/ct-head-an-approach  \nFor bony windw, W:2800 HU L:600 HU","metadata":{}},{"cell_type":"code","source":"def window_generator(window_width, window_level):\n    # copied from qer_utils\n    \"\"\"Return CT window transform for given width and level.\"\"\"\n    low = window_level - window_width / 2\n    high = window_level + window_width / 2\n\n    def window_fn(img):\n        img = (img - low) / (high - low)\n        img = np.clip(img, 0, 1)\n        return img\n\n    return window_fn\n\nbone_window = window_generator(2800, 600)","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:03:07.068543Z","iopub.execute_input":"2022-08-20T12:03:07.06922Z","iopub.status.idle":"2022-08-20T12:03:07.077592Z","shell.execute_reply.started":"2022-08-20T12:03:07.069178Z","shell.execute_reply":"2022-08-20T12:03:07.076183Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"scan_info.bone_pixel_array = bone_window(scan_info.pixel_array)\nplot_matplotlib_scans3d([scan_info.bone_pixel_array], [\"CT Scan\"])","metadata":{"execution":{"iopub.status.busy":"2022-08-20T14:20:24.594788Z","iopub.execute_input":"2022-08-20T14:20:24.595244Z","iopub.status.idle":"2022-08-20T14:20:25.498505Z","shell.execute_reply.started":"2022-08-20T14:20:24.595209Z","shell.execute_reply":"2022-08-20T14:20:25.497337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## HU hist, bone window hist\nLets see what HU values are present within a scan.","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(15, 4))\nax.flat[0].hist(scan_info.pixel_array.reshape(-1), bins=60)\nax.flat[0].set_title(\"HU units\")\n\nax.flat[1].hist(scan_info.bone_pixel_array.reshape(-1), bins=30)\nax.flat[1].set_title(\"Bone window\")\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:40:50.214582Z","iopub.execute_input":"2022-08-20T12:40:50.215048Z","iopub.status.idle":"2022-08-20T12:40:53.237324Z","shell.execute_reply.started":"2022-08-20T12:40:50.215012Z","shell.execute_reply":"2022-08-20T12:40:53.23637Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## visualizing bone window in a collage format. \n\nwe will visualize every 5th scan","metadata":{"execution":{"iopub.status.busy":"2022-08-15T18:06:45.32079Z","iopub.execute_input":"2022-08-15T18:06:45.321751Z","iopub.status.idle":"2022-08-15T18:06:45.329919Z","shell.execute_reply.started":"2022-08-15T18:06:45.3217Z","shell.execute_reply":"2022-08-15T18:06:45.328377Z"}}},{"cell_type":"code","source":"total_vis = round(scan_info.bone_pixel_array.shape[0]/5, 0)\ncols = 6 \nrows = total_vis/cols\nfig, axes = plt.subplots(nrows=int(rows), ncols=int(cols), figsize=(12,12), constrained_layout=True)\nfig.suptitle(f'ID: {scan_info.series_id}', weight=\"bold\", size=20)\nt = 0\nfor slice_no in range(scan_info.bone_pixel_array.shape[0]):\n    if t >= len(axes.flat):\n        continue\n    img = scan_info.bone_pixel_array[slice_no]\n    \n    if slice_no%5 ==0:\n        axes.flat[t].imshow(img, cmap=\"bone\")\n        #axes.flat[t].set_title(f\"Slice: {slice_no}\", fontsize=14, weight='bold')\n        axes.flat[t].axis('off')\n        t+=1","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:41:22.484955Z","iopub.execute_input":"2022-08-20T12:41:22.485445Z","iopub.status.idle":"2022-08-20T12:41:26.404954Z","shell.execute_reply.started":"2022-08-20T12:41:22.485409Z","shell.execute_reply":"2022-08-20T12:41:26.403831Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Histogram of total number of slices in each study","metadata":{}},{"cell_type":"code","source":"total_slices = [len(x.list_dcms(i)) for i in range(len(x))]\n\nfig, ax = plt.subplots(figsize=(8, 3.5), nrows=1, ncols=1)\nax.hist(total_slices, bins=30)\nax.set_title(\"total slices\")\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:03:16.427906Z","iopub.execute_input":"2022-08-20T12:03:16.428394Z","iopub.status.idle":"2022-08-20T12:05:11.09272Z","shell.execute_reply.started":"2022-08-20T12:03:16.428349Z","shell.execute_reply":"2022-08-20T12:05:11.091262Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Detection ","metadata":{}},{"cell_type":"code","source":"from PIL import Image, ImageDraw\nfrom copy import deepcopy\nfrom typing import Union\n\nclass DCMDBAnnot(DCMDB):\n    def __init__(self, root, csv_loc, bbox_csv_loc: str=None, dtype=\"train\"):\n        super().__init__(root, dtype+\"_images\")\n        self.csv_loc = csv_loc \n        self.bbox_csv_loc = bbox_csv_loc\n        self.csv = pd.read_csv(self.csv_loc)\n        self.bbox_csv = pd.read_csv(self.bbox_csv_loc)\n        self.bbox_series_ids = annot.bbox_csv[\"StudyInstanceUID\"].unique().tolist() #235 scans have bbox info \n    \n    def __getitem__(self, idx):\n        scan_info = super().__getitem__(idx)\n        \n        ## labels\n        labels = self.csv[self.csv[\"StudyInstanceUID\"] == scan_info.series_id][[\"C1\", \"C2\", \"C3\", \"C4\", \"C5\", \"C6\", \"C7\"]].to_dict(\"records\")[0]\n        scan_info.labels = [k for k, v in labels.items() if v ==1 ]\n        \n        ## bbox\n        bbox = annot.bbox_csv[annot.bbox_csv[\"StudyInstanceUID\"] == scan_info.series_id].reset_index(drop=True)[[\"x\", \"y\", \"width\", \"height\", \"slice_number\"]].values.tolist()\n        scan_info.bbox = bbox \n        \n        ## TODO: segm\n        return scan_info\n    \n    def vis_bbox(self, scan:Union[str, str, ScanData]):\n        scan_info = self[scan] if not isinstance(scan, ScanData) else scan\n        scan = deepcopy(scan_info)\n        scan.bone_pixel_array = bone_window(scan.pixel_array)\n        \n        def _draw(scan, bboxes):\n            for box in bboxes:\n                x, y, w, h, slice_number = box\n                img = Image.fromarray(scan[int(slice_number)])\n                img1 = ImageDraw.Draw(img)\n                shape = [(x, y), (x+w, y+h)]\n                img1.rectangle(shape, outline =\"black\")\n                scan[i] = np.asarray(img)\n            return scan\n        scan.bone_pixel_array = _draw(scan.bone_pixel_array, scan.bbox)\n        return scan\n    \n    def vis_segm(self, idx:Union[str, str]):\n        pass ","metadata":{"execution":{"iopub.status.busy":"2022-08-20T14:20:40.501165Z","iopub.execute_input":"2022-08-20T14:20:40.501611Z","iopub.status.idle":"2022-08-20T14:20:40.517233Z","shell.execute_reply.started":"2022-08-20T14:20:40.501575Z","shell.execute_reply":"2022-08-20T14:20:40.515816Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"csv_loc = ROOT+\"/train.csv\"\nbbox_csv_loc = ROOT+\"/train_bounding_boxes.csv\"\nannot = DCMDBAnnot(ROOT, csv_loc, bbox_csv_loc)\n#scan_info = annot[np.random.randint(len(annot))]","metadata":{"execution":{"iopub.status.busy":"2022-08-20T14:20:40.787407Z","iopub.execute_input":"2022-08-20T14:20:40.788625Z","iopub.status.idle":"2022-08-20T14:20:40.827099Z","shell.execute_reply.started":"2022-08-20T14:20:40.788556Z","shell.execute_reply":"2022-08-20T14:20:40.82561Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"idx = np.random.randint(len(annot.bbox_series_ids))\nscan_vis = annot.vis_bbox(annot.bbox_series_ids[idx])\nplot_matplotlib_scans3d([scan_vis.bone_pixel_array], [f\"CT Scan: {min([i[-1] for i in scan_vis.bbox])}-{max([i[-1] for i in scan_vis.bbox])}\"])","metadata":{"execution":{"iopub.status.busy":"2022-08-20T14:20:47.981261Z","iopub.execute_input":"2022-08-20T14:20:47.981769Z","iopub.status.idle":"2022-08-20T14:20:54.757207Z","shell.execute_reply.started":"2022-08-20T14:20:47.981731Z","shell.execute_reply":"2022-08-20T14:20:54.755969Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"scan_vis = annot.vis_bbox()","metadata":{"execution":{"iopub.status.busy":"2022-08-20T13:01:18.187531Z","iopub.execute_input":"2022-08-20T13:01:18.187924Z","iopub.status.idle":"2022-08-20T13:01:18.19826Z","shell.execute_reply.started":"2022-08-20T13:01:18.187893Z","shell.execute_reply":"2022-08-20T13:01:18.196682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tt[[\"x\", \"y\", \"width\", \"height\", \"slice_number\"]].values.tolist()","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:57:50.225152Z","iopub.execute_input":"2022-08-20T12:57:50.225594Z","iopub.status.idle":"2022-08-20T12:57:50.234205Z","shell.execute_reply.started":"2022-08-20T12:57:50.225559Z","shell.execute_reply":"2022-08-20T12:57:50.233305Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"annot.bbox_csv","metadata":{"execution":{"iopub.status.busy":"2022-08-20T12:14:28.691457Z","iopub.execute_input":"2022-08-20T12:14:28.691908Z","iopub.status.idle":"2022-08-20T12:14:28.71269Z","shell.execute_reply.started":"2022-08-20T12:14:28.691872Z","shell.execute_reply":"2022-08-20T12:14:28.711309Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"TODO:\n- ~~windowing of the raw scan to expose the bone part ~~\n- ~~visualize every 5th slice in a collage view ~~\n- ~~see Hu of a scan and corresponding pixels hist ~~\n- can we visually tell where a bone crack is present in the scan?\n- understanding bounding boxes of fractures\n- understanding segmentation annotations of fractures.","metadata":{}}]}