{"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":"# <center style=\"font-family: consolas; font-size: 32px; font-weight: bold;\"> 3D GEOMETRY MODELING</center>\n<p><center style=\"color:#949494; font-family: consolas; font-size: 20px;\">CONVERT 2D PHOTOS INTO 3D AS POINT CLOUDS</center></p>\n\n***","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"#### <a id=\"top\"></a>\n# <div style=\"box-shadow: rgb(60, 121, 245) 0px 0px 0px 3px inset, rgb(255, 255, 255) 10px -10px 0px -3px, rgb(31, 193, 27) 10px -10px, rgb(255, 255, 255) 20px -20px 0px -3px, rgb(255, 217, 19) 20px -20px, rgb(255, 255, 255) 30px -30px 0px -3px, rgb(255, 156, 85) 30px -30px, rgb(255, 255, 255) 40px -40px 0px -3px, rgb(255, 85, 85) 40px -40px; padding:20px; margin-right: 40px; font-size:30px; font-family: consolas; text-align:center; display:fill; border-radius:15px; color:rgb(60, 121, 245);\"><b>TABLE OF CONTENTS</b></div>\n\n<div style=\"background-color: rgba(60, 121, 245, 0.03); padding:30px; font-size:15px; font-family: consolas;\">\n</div>","metadata":{}},{"cell_type":"markdown","source":"# **BRAIN**","metadata":{}},{"cell_type":"markdown","source":"Import the necessary libraries:","metadata":{}},{"cell_type":"code","source":"import os\nimport json\nimport glob\nimport random\nimport collections\n\nimport numpy as np\nimport pandas as pd\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport cv2\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\ntrain_df = pd.read_csv(\"../input/rsna-miccai-brain-tumor-radiogenomic-classification/train_labels.csv\")\ntrain_df","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:43:19.600499Z","iopub.execute_input":"2023-10-20T07:43:19.600802Z","iopub.status.idle":"2023-10-20T07:43:20.812515Z","shell.execute_reply.started":"2023-10-20T07:43:19.600774Z","shell.execute_reply":"2023-10-20T07:43:20.811518Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(5, 5))\nsns.countplot(data=train_df, x=\"MGMT_value\");","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:43:20.813754Z","iopub.execute_input":"2023-10-20T07:43:20.814033Z","iopub.status.idle":"2023-10-20T07:43:21.040723Z","shell.execute_reply.started":"2023-10-20T07:43:20.814007Z","shell.execute_reply":"2023-10-20T07:43:21.039719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_dicom(path):\n    dicom = pydicom.read_file(path)\n    data = dicom.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 data\n\n\ndef visualize_sample(\n    brats21id, \n    slice_i,\n    mgmt_value,\n    types=(\"FLAIR\", \"T1w\", \"T1wCE\", \"T2w\")\n):\n    plt.figure(figsize=(16, 5))\n    patient_path = os.path.join(\n        \"../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/\", \n        str(brats21id).zfill(5),\n    )\n    for i, t in enumerate(types, 1):\n        t_paths = sorted(\n            glob.glob(os.path.join(patient_path, t, \"*\")), \n            key=lambda x: int(x[:-4].split(\"-\")[-1]),\n        )\n        data = load_dicom(t_paths[int(len(t_paths) * slice_i)])\n        plt.subplot(1, 4, i)\n        plt.imshow(data, cmap=\"gray\")\n        plt.title(f\"{t}\", fontsize=16)\n        plt.axis(\"off\")\n\n    plt.suptitle(f\"MGMT_value: {mgmt_value}\", fontsize=16)\n    plt.show()\nfor i in random.sample(range(train_df.shape[0]), 5):\n    _brats21id = train_df.iloc[i][\"BraTS21ID\"]\n    _mgmt_value = train_df.iloc[i][\"MGMT_value\"]\n    visualize_sample(brats21id=_brats21id, mgmt_value=_mgmt_value, slice_i=0.5)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:43:21.042578Z","iopub.execute_input":"2023-10-20T07:43:21.042883Z","iopub.status.idle":"2023-10-20T07:43:24.551229Z","shell.execute_reply.started":"2023-10-20T07:43:21.042855Z","shell.execute_reply":"2023-10-20T07:43:24.550251Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from matplotlib import animation, rc\nrc('animation', html='jshtml')\n\n\ndef create_animation(ims):\n    fig = plt.figure(figsize=(6, 6))\n    plt.axis('off')\n    im = plt.imshow(ims[0], cmap=\"gray\")\n\n    def animate_func(i):\n        im.set_array(ims[i])\n        return [im]\n\n    return animation.FuncAnimation(fig, animate_func, frames = len(ims), interval = 1000//24)\ndef load_dicom_line(path):\n    t_paths = sorted(\n        glob.glob(os.path.join(path, \"*\")), \n        key=lambda x: int(x[:-4].split(\"-\")[-1]),\n    )\n    images = []\n    for filename in t_paths:\n        data = load_dicom(filename)\n        if data.max() == 0:\n            continue\n        images.append(data)\n        \n    return images","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:43:24.552510Z","iopub.execute_input":"2023-10-20T07:43:24.553639Z","iopub.status.idle":"2023-10-20T07:43:24.569569Z","shell.execute_reply.started":"2023-10-20T07:43:24.553599Z","shell.execute_reply":"2023-10-20T07:43:24.567727Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"images = load_dicom_line(\"../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000/FLAIR\")\ncreate_animation(images)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:43:24.572062Z","iopub.execute_input":"2023-10-20T07:43:24.573028Z","iopub.status.idle":"2023-10-20T07:43:45.644626Z","shell.execute_reply.started":"2023-10-20T07:43:24.572977Z","shell.execute_reply":"2023-10-20T07:43:45.643162Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"images = load_dicom_line(\"../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000/T2w\")\ncreate_animation(images)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:43:45.646750Z","iopub.execute_input":"2023-10-20T07:43:45.647303Z","iopub.status.idle":"2023-10-20T07:44:06.050305Z","shell.execute_reply.started":"2023-10-20T07:43:45.647252Z","shell.execute_reply":"2023-10-20T07:44:06.048886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":" Plotting 3D MRI scans","metadata":{}},{"cell_type":"code","source":"import tarfile\nimport plotly\nfrom plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:44:06.052136Z","iopub.execute_input":"2023-10-20T07:44:06.053073Z","iopub.status.idle":"2023-10-20T07:44:06.094587Z","shell.execute_reply.started":"2023-10-20T07:44:06.053018Z","shell.execute_reply":"2023-10-20T07:44:06.093213Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Define the extract_task1_files() function to extract files from tar files:","metadata":{}},{"cell_type":"markdown","source":"The first section contains a function and a helper function to extract files from a compressed file (tar) and save them to a specific folder:\n\nextract_task1_files(root=\"./data\"):This function takes root input (path to destination directory) and performs the extraction of files from the compressed file (tar) to the root directory.","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"def extract_task1_files(root=\"./data\"):\n    tar = tarfile.open(\"../input/brats-2021-task1/BraTS2021_Training_Data.tar\")\n    tar.extractall(root)\n    tar.close()","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:44:06.100391Z","iopub.execute_input":"2023-10-20T07:44:06.101370Z","iopub.status.idle":"2023-10-20T07:44:06.108406Z","shell.execute_reply.started":"2023-10-20T07:44:06.101315Z","shell.execute_reply":"2023-10-20T07:44:06.107098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"extract_task1_files()","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:44:06.110029Z","iopub.execute_input":"2023-10-20T07:44:06.111330Z","iopub.status.idle":"2023-10-20T07:45:38.267121Z","shell.execute_reply.started":"2023-10-20T07:44:06.111274Z","shell.execute_reply":"2023-10-20T07:45:38.266200Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Defining the ImageReader class for reading and processing images:\n","metadata":{}},{"cell_type":"markdown","source":"The second part contains an ImageReader class and a function for creating a Scatter3d object for the three-dimensional data display:\n**ImageReader**: This is a class that reads and processes image data from NIfTI files. This class has properties such as root (root path), img_size (image size), normalize (whether or not to normalize the image), and single_class (use a single layer or not). This class has methods such as read_file() (read and process an image file), and load_patient_scan() (load image data for a specific patient).\n","metadata":{}},{"cell_type":"code","source":"import nibabel as nib\nimport os\nimport albumentations as A\nimport numpy as np\n\n\nclass ImageReader:\n    def __init__(self, root:str, img_size:int=256, normalize:bool=False, single_class:bool=False):\n        pad_size = 256 if img_size > 256 else 224\n        self.resize = A.Compose(\n            [\n                A.PadIfNeeded(min_height=pad_size, min_width=pad_size, value=0),\n                A.Resize(img_size, img_size)\n            ]\n        )\n        self.normalize=normalize\n        self.single_class=single_class\n        self.root=root\n        \n    def read_file(self, path:str) -> dict:\n        scan_type = path.split('_')[-1]\n        raw_image = nib.load(path).get_fdata()\n        raw_mask = nib.load(path.replace(scan_type, 'seg.nii.gz')).get_fdata()\n        processed_frames, processed_masks = [], []\n        for frame_idx in range(raw_image.shape[2]):\n            frame = raw_image[:, :, frame_idx]\n            mask = raw_mask[:, :, frame_idx]\n            if self.normalize:\n                if frame.max() > 0:\n                    frame = frame/frame.max()\n                frame = frame.astype(np.float32)\n            else:\n                frame = frame.astype(np.uint8)\n            resized = self.resize(image=frame, mask=mask)\n            processed_frames.append(resized['image'])\n            processed_masks.append(1*(resized['mask'] > 0) if self.single_class else resized['mask'])\n        return {\n            'scan': np.stack(processed_frames, 0),\n            'segmentation': np.stack(processed_masks, 0),\n            'orig_shape': raw_image.shape\n        }\n    \n    def load_patient_scan(self, idx:int, scan_type:str='flair') -> dict:\n        patient_id = str(idx).zfill(5)\n        scan_filename = f'{self.root}/BraTS2021_{patient_id}/BraTS2021_{patient_id}_{scan_type}.nii.gz'\n        return self.read_file(scan_filename)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:45:38.268539Z","iopub.execute_input":"2023-10-20T07:45:38.269005Z","iopub.status.idle":"2023-10-20T07:45:39.510555Z","shell.execute_reply.started":"2023-10-20T07:45:38.268966Z","shell.execute_reply":"2023-10-20T07:45:39.509492Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Define the generate_3d_scatter() function to create Plotly's Scatter3d object:","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"import plotly.graph_objects as go\nimport numpy as np\n\n \n# generate_3d_scatter(): This is a function that creates a Plotly Scatter3d object to display \n# three-dimensional data. \n# This function takes parameters such as x, y, z (coordinate data arrays), colors (color arrays), \n# Size, Opacity, Scale, Hover \n#hover), and name (the name of the Scatter3d object).\ndef generate_3d_scatter(\n    x:np.array, y:np.array, z:np.array, colors:np.array,\n    size:int=3, opacity:float=0.2, scale:str='Teal',\n    hover:str='skip', name:str='MRI'\n) -> go.Scatter3d:\n    return go.Scatter3d(\n        x=x, y=y, z=z,\n        mode='markers', hoverinfo=hover,\n        marker = dict(\n            size=size, opacity=opacity,\n            color=colors, colorscale=scale\n        ),\n        name=name\n    )\n\n\nclass ImageViewer3d():\n# The constructor (__init__) of the ImageViewer3d class takes parameters like reader \n# (ImageReader subject), mri_downsample (reduced sample rate of MRI images), \n# and mri_colorscale (palette for MRI images).\n    def __init__(self, reader:ImageReader, mri_downsample:int=10, mri_colorscale:str='Ice', voxel_size:float=0.1) -> None:\n        self.reader = reader\n        self.mri_downsample = mri_downsample\n        self.mri_colorscale = mri_colorscale\n        self.voxel_size = voxel_size\n        \n# The load_clean_mri() method is used to load MRI image data after cleaning. \n# This method takes in image (numpy array of image) and orig_dim (original size) \n# of the image). It calculates the x, y, z coordinates of points in MRI images with larger values \n#0, use a mri_downsample reduced sample rate, and return a dictionary containing information about the coordinates \n# and the color of the points.\n    def load_clean_mri(self, image:np.array, orig_dim:int) -> dict:\n        shape_offset = image.shape[1]/orig_dim\n        z, x, y = (image > 0).nonzero()\n        # only (1/mri_downsample) is sampled for the resulting image\n        x, y, z = x[::self.mri_downsample], y[::self.mri_downsample], z[::self.mri_downsample]\n        colors = image[z, x, y]\n        return dict(x=x/shape_offset, y=y/shape_offset, z=z, colors=colors)\n    \n# The load_tumor_segmentation() method is used to load tumor segmentation data.\n# This method takes in image (numpy array of the segment) and orig_dim (original size)\n# of the image). It calculates the x, y, z coordinates of points in the segment with corresponding values \n# For tumor layers, use different reduced sample ratios (1/1, 1/3, 1/5) for each layer, and pay \n# about a dictionary containing information about the coordinates and colors of points.\n    def load_tumor_segmentation(self, image:np.array, orig_dim:int) -> dict:\n        tumors = {}\n        shape_offset = image.shape[1]/orig_dim\n        # 1/1, 1/3 and 1/5 pixels for tumor tissue classes 1(core), 2(invaded) and 4(enhancing)\n        sampling = {\n            1: 1, 2: 3, 4: 5\n        }\n        for class_idx in sampling:\n            z, x, y = (image == class_idx).nonzero()\n            x, y, z = x[::sampling[class_idx]], y[::sampling[class_idx]], z[::sampling[class_idx]]\n            tumors[class_idx] = dict(\n                x=x/shape_offset, y=y/shape_offset, z=z,\n                colors=class_idx/4\n            )\n        return tumors\n\n    \n# The collect_patient_data() method is used to collect a patient's data, \n# includes cleaned MRI imaging data and tumor segmentation data. \n# This method receives scanned input (object from method load_patient_scan() of\n# ImageReader class). It uses methods load_clean_mri() and load_tumor_segmentation() \n# to create image and segment data, and return a list of Scatter3d objects \n# and the total score.\n    def collect_patient_data(self, scan:dict) -> tuple:\n        clean_mri = self.load_clean_mri(scan['scan'], scan['orig_shape'][0])\n        tumors = self.load_tumor_segmentation(scan['segmentation'], scan['orig_shape'][0])\n        \n        voxel_volume = self.voxel_size ** 3\n        markers_created = clean_mri['x'].shape[0] + sum(tumors[class_idx]['x'].shape[0] for class_idx in tumors)\n        \n        clean_mri_diem = clean_mri['x'].shape[0]\n        tumor1_diem = tumors[1]['x'].shape[0]\n        tumor2_diem = tumors[2]['x'].shape[0]\n        tumor4_diem = tumors[4]['x'].shape[0]\n        \n        clean_mri_tile = round(clean_mri_diem /markers_created*100, 2)\n        tumor1_tile = round(tumor1_diem /markers_created*100, 2)\n        tumor2_tile = round(tumor2_diem /markers_created*100, 2)\n        tumor4_tile = round(tumor4_diem /markers_created*100, 2)\n \n        clean_mri_kichthuoc = str(round(clean_mri_diem * voxel_volume, 2)) + ' cm^3'\n        tumor1_kichthuoc = str(round(tumor1_diem * voxel_volume, 2)) + ' cm^3'\n        tumor2_kichthuoc = str(round(tumor2_diem * voxel_volume, 2)) + ' cm^3'\n        tumor4_kichthuoc = str(round(tumor4_diem * voxel_volume, 2)) + ' cm^3'\n        \n        print('Brain MRI - clean MRI images:', clean_mri_diem ,'points,', clean_mri_tile ,'%',clean_mri_kichthuoc)\n        print('UT Project Core:', tumor1_diem , 'points,', tumor1_tile ,'%,', tumor1_kichthuoc)\n        print('Surrounding tissue is invaded by UT tumors:', tumor2_diem ,'points,', tumor2_tile ,'%,', tumor2_kichthuoc)\n        print('Gadolinium-containing UT:', tumor4_diem ,'points,', tumor4_tile,'%,', tumor4_kichthuoc)\n        \n        return [\n            generate_3d_scatter(**clean_mri, scale=self.mri_colorscale, opacity=0.3, hover='skip', name='Brain MRI - clean MRI images('+ clean_mri_kichthuoc +')'),\n            generate_3d_scatter(**tumors[1], opacity=0.8, hover='all', name='UT Project Core(' + tumor1_kichthuoc + ')'),\n            generate_3d_scatter(**tumors[2], opacity=0.4, hover='all', name='Surrounding tissue is invaded by UT tumors(' + tumor2_kichthuoc + ')'),\n            generate_3d_scatter(**tumors[4], opacity=0.4, hover='all', name='Gadolinium-containing UT(' + tumor4_kichthuoc + ')'),\n        ], markers_created\n \n\n# The get_3d_scan() method is used to obtain three-dimensional data of a patient and create arguments\n# corresponding Scatter3d chart icon. This method takes arguments such as patient_idx\n# (patient's sequence number), and scan_type (type of MRI scan). It uses the method \n#load_patient_scan() of the ImageReader class to load image data for specific patients.\n# It then uses the collect_patient_data() method to crawl the image and \n# segmentation of patients. Finally, it creates a Scatter3d chart object using \n# use Scatter3d objects from collected data and install properties and configurations \n# for the Scatter3d chart and return the graph object.\n    def get_3d_scan(self, patient_idx:int, scan_type:str='flair') -> go.Figure:\n        scan = self.reader.load_patient_scan(patient_idx, scan_type)\n        data, num_markers = self.collect_patient_data(scan)\n        fig = go.Figure(data=data)\n        fig.update_layout(\n            title=f\"[Patient id:{patient_idx}] brain MRI scan ({num_markers} points)\",\n            legend_title=\"Pixel class (click to enable/disable)\",\n            font=dict(\n                family=\"Courier New, monospace\",\n                size=14,\n            ),\n            margin=dict(\n                l=0,r=0,b=0,t=30\n            ),\n            legend=dict(itemsizing='constant')\n        )\n        return fig","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:45:39.512935Z","iopub.execute_input":"2023-10-20T07:45:39.513420Z","iopub.status.idle":"2023-10-20T07:45:39.670060Z","shell.execute_reply.started":"2023-10-20T07:45:39.513372Z","shell.execute_reply":"2023-10-20T07:45:39.668889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* load_tumor_segmentation(self, image:np.array, orig_dim:int) -> dict: This is a method in a class or object. It takes input as an array of numpy (image) representing an image, and an integer (orig_dim) representing the original size of the image before being processed. This method returns a dict containing information about the cancer segments on the image.\n* \n* tumors = {}: This is an initialized dictionary variable that stores information about cancer tumor segments.\n* \n* shape_offset = image.shape[1]/orig_dim: The shape_offset variable is calculated to determine the ratio between the actual size of the image and the original size. It is used to convert the coordinates of points in cancer segments.\n* \n* sampling: This is a dictionary containing sampling parameters for different layers of cancerous tissue. Specifically, it assigns the sampling step to each layer, i.e. the number of points sampled from each layer corresponds to 1 actual point.\n* \n* for class_idx in sampling: This is a loop through elements in the sampling dictionary. class_idx is the key of each element.\n* \n* (image == class_idx).nonzero(): This is a comparison to find all positions in the image whose value is equal to class_idx. .nonzero() returns indexes of locations that meet the condition.\n* \n* x[::sampling[class_idx]], y[::sampling[class_idx]], z[::sampling[class_idx]]: This is a way to sample from x, y, and z arrays following the sampling step defined by sampling[class_idx]. x[::sampling[class_idx]] only takes elements from x with the sampling step[class_idx].\n* \n* tumors[class_idx] = dict(...): This is a way to add an element to the tumors dictionary. The information about the cancer segment is stored in the form of a subdictionary with corresponding keys and values.\n* \n* clean_mri['x'].shape[0] + sum(tumors[class_idx]['x'].shape[0] for class_idx in tumors): This is an expression that calculates the total number of points used to create a 3D point cloud. It includes the number of points from clean_mri and the total number of points from all layers of cancerous tissue in the tumors.","metadata":{}},{"cell_type":"markdown","source":"Create ImageReader and ImageViewer3d objects:","metadata":{}},{"cell_type":"markdown","source":"Create two objects: reader and viewer. Create two objects: reader and viewer.\n\nThe reader object is created by instantiating the ImageReader class. This object is initialized with the following parameters:\n\n* root='./data': The path to the folder containing the image data.\n* img_size=128: The image size is changed to 128x128.\n* normalize=True: Normalize the image.\n* single_class=False: No classification into a single class applies.\n\nThe viewer object is created by instantiating the ImageViewer3d class. This object is initialized with the following parameters:\n\n* reader: The ImageReader object is passed to read image data.\n* mri_downsample=25: The sample reduction rate of MRI images is 25.\n* Reader and viewer objects are created for use in reading and displaying 3D image data.","metadata":{}},{"cell_type":"code","source":"reader = ImageReader('./data', img_size=128, normalize=True, single_class=False)\nviewer = ImageViewer3d(reader, mri_downsample=25)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:45:39.671327Z","iopub.execute_input":"2023-10-20T07:45:39.671686Z","iopub.status.idle":"2023-10-20T07:45:39.677469Z","shell.execute_reply.started":"2023-10-20T07:45:39.671631Z","shell.execute_reply":"2023-10-20T07:45:39.676499Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Use viewer.get_3d_scan() to get a 3D image of a specific patient:","metadata":{}},{"cell_type":"markdown","source":"The code creates a 3D graph of the MRI scan of the first patient (with patient_idx=0) and the scan type 't1'.\n\nFirst, viewer.get_3d_scan(0, 't1') is called to retrieve the go object. 3D chart figure. This method calls the reader subject's load_patient_scan method to read the MRI scan data of patients with patient_idx=0 and scan type 't1'. It then uses the viewer's collect_patient_data method to generate data for the 3D chart. Finally, the 3D chart is created using go. Figure and other data are infused.\n\nThen plotly.offline.iplot(fig) is used to display the 3D chart. The plotly.offline iplot method allows the chart to be displayed directly in a notebook or other interactive environment.","metadata":{}},{"cell_type":"markdown","source":"* generate_3d_scatter(**clean_mri, scale=self.mri_colorscale, opacity=0.3, hover='skip', name='Brain MRI')\n* => Create a 3D point cloud from clean_mri (clean MRI image). The cloud point will be scaled to the color determined by the mri_colorscale. The opacity (transparency) of the cloud is 0.3. When hovering over, it skips displaying detailed information. The point cloud name is \"Brain MRI\".\n\n* generate_3d_scatter(**tumors[1], opacity=0.8, hover='all', name='Necrotic tumor core')\n* => Create a 3D point cloud from tumors[1] (cancerous tumor cores). The cloud point will have an opacity of 0.8. When hovering over the mouse, detailed information will be displayed. The point cloud name is \"Necrotic tumor core\".\n\n* generate_3d_scatter(**tumors[2], opacity=0.4, hover='all', name='Peritumoral invaded tissue')\n* => Create a 3D point cloud from tumors[2] (surrounding tissue invaded by cancerous tumors). The cloud point will have an opacity of 0.4. When hovering over the mouse, detailed information will be displayed. The point cloud name is \"Peritumoral invaded tissue\".\n\n* generate_3d_scatter(**tumors[4], opacity=0.4, hover='all', name='GD-enhancing tumor')\n* => Create a 3D point cloud from tumors[4] (Gadolinium-containing cancer-enhanced tumors). The cloud point will have an opacity of 0.4. When hovering over the mouse, detailed information will be displayed. The point cloud name is \"GD-enhancing tumor\".\n ","metadata":{}},{"cell_type":"code","source":"fig = viewer.get_3d_scan(2, 't2')\nplotly.offline.iplot(fig)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:45:39.678632Z","iopub.execute_input":"2023-10-20T07:45:39.679633Z","iopub.status.idle":"2023-10-20T07:45:41.330545Z","shell.execute_reply.started":"2023-10-20T07:45:39.679597Z","shell.execute_reply":"2023-10-20T07:45:41.329620Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The code creates a 3D graph of the MRI scan of the 9th patient (with patient_idx=9) and the type of 'flair' scan.\n\n\n\nFirst, viewer.get_3d_scan(9, 'flair') is called to retrieve the go object. 3D chart figure. This method calls the reader subject's load_patient_scan method to read the MRI scan data of patients with patient_idx=9 and the 'flair' scan type. It then uses the viewer's collect_patient_data method to generate data for the 3D chart. Finally, the 3D chart is created using go. Figure and other data are infused.\n\n\nThen plotly.offline.iplot(fig) is used to display the 3D chart. The plotly.offline iplot method allows the chart to be displayed directly in a notebook or other interactive environment.","metadata":{}},{"cell_type":"code","source":"fig = viewer.get_3d_scan(5, 'flair')\nplotly.offline.iplot(fig)","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:45:41.331655Z","iopub.execute_input":"2023-10-20T07:45:41.332232Z","iopub.status.idle":"2023-10-20T07:45:41.622528Z","shell.execute_reply.started":"2023-10-20T07:45:41.332204Z","shell.execute_reply":"2023-10-20T07:45:41.621635Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3D RECONSTRUCTION","metadata":{}},{"cell_type":"code","source":"import os\nimport glob\nfrom tqdm import tqdm\nfrom random import randint\n\nimport numpy as np\nimport pydicom\n\nimport matplotlib.pyplot as plt\nfrom matplotlib import cm\nimport matplotlib.animation as anim\nimport matplotlib.patches as mpatches\nimport matplotlib.gridspec as gridspec\n\n\nimport imageio\nfrom skimage.transform import resize\nfrom skimage.util import montage\n\nfrom IPython.display import Image as show_gif\n\nimport warnings\nwarnings.simplefilter(\"ignore\")\nsample_id = '../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000'\npath_x = '../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000/T2w/*.dcm'\nstart = len('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000/T2w/Image-')\nend = len('.dcm')\npath_to_slices = sorted(glob.glob(path_x), key= lambda x: int(x[start:-end]))","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:45:41.623865Z","iopub.execute_input":"2023-10-20T07:45:41.624263Z","iopub.status.idle":"2023-10-20T07:45:41.637865Z","shell.execute_reply.started":"2023-10-20T07:45:41.624223Z","shell.execute_reply":"2023-10-20T07:45:41.636964Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class ImageToGIF:\n    \"\"\"Create GIF without saving image files.\"\"\"\n    def __init__(self,\n                 size=(500, 500), \n                 xy_text=(80, 30),\n                 dpi=100, \n                 cmap='CMRmap'):\n\n        self.fig = plt.figure()\n        self.fig.set_size_inches(size[0] / dpi, size[1] / dpi)\n        self.xy_text = xy_text\n        self.cmap = cmap\n        \n        self.ax = self.fig.add_axes([0, 0, 1, 1])\n        self.ax.set_xticks([])\n        self.ax.set_yticks([])\n        self.images = []\n \n    def add(self, image, label, with_mask=False):\n        plt.set_cmap(self.cmap)\n        plt_img = self.ax.imshow(image, animated=True)\n        plt_text = self.ax.text(*self.xy_text, label, color='red')\n        to_plot = [plt_img, plt_text]\n        self.images.append(to_plot)\n        plt.close()\n \n    def save(self, filename, fps):\n        animation = anim.ArtistAnimation(self.fig, self.images)\n        animation.save(filename, writer='imagemagick', fps=fps)\n        \n\nsample_data_gif = ImageToGIF()\nlabel = sample_id.replace('/', '.').split('.')[-2]\nfilename = f'{label}_3d_2d.gif'\n\nfor i in range(len(path_to_slices)):\n    image = pydicom.read_file(path_to_slices[i]).pixel_array\n    #mask = np.clip(np.rot90(sample_mask[i]), 0, 1)\n    sample_data_gif.add(image, label=f'{label}_{str(i)}')\n\n    \nsample_data_gif.save(filename, fps=15)\nshow_gif(filename, format='png')","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:45:41.639205Z","iopub.execute_input":"2023-10-20T07:45:41.639864Z","iopub.status.idle":"2023-10-20T07:47:20.992429Z","shell.execute_reply.started":"2023-10-20T07:45:41.639813Z","shell.execute_reply":"2023-10-20T07:47:20.990605Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Image3dToGIF3d:\n    \"\"\"\n    Displaying 3D images in 3d axes.\n    Parameters:\n        img_dim: shape of cube for resizing.\n        figsize: figure size for plotting in inches.\n    Step by step explanation - https://terbium.io/2017/12/matplotlib-3d/\n    \"\"\"\n    def __init__(self, \n                 img_dim: tuple = (55, 55, 55),\n                 figsize: tuple = (15, 10),\n                 binary: bool = False,\n                 normalizing: bool = True,\n                ):\n        \"\"\"Initialization.\"\"\"\n        self.img_dim = img_dim\n        print(img_dim)\n        self.figsize = figsize\n        self.binary = binary\n        self.normalizing = normalizing\n\n    def _explode(self, data: np.ndarray):\n        \"\"\"\n        Takes: array and return an array twice as large in each dimension,\n        with an extra space between each voxel.\n        \"\"\"\n        shape_arr = np.array(data.shape)\n        size = shape_arr[:3] * 2 - 1\n        exploded = np.zeros(np.concatenate([size, shape_arr[3:]]),\n                            dtype=data.dtype)\n        exploded[::2, ::2, ::2] = data\n        return exploded\n\n    def _expand_coordinates(self, indices: np.ndarray):\n        \"\"\" \n        Parameters:\n            indices: coordinats of array with only original values\n            (before explode transformaion)\n        \n        Returns:\n        The arrays of values each dimensions (x y z) as arguments needed \n        for the plt.figure.voxels functionto extend coordinates\n        for the rendering only colored voxels\"\"\"\n        x, y, z = indices\n        x[1::2, :, :] += 1\n        y[:, 1::2, :] += 1\n        z[:, :, 1::2] += 1\n        return x, y, z\n    \n    def _normalize(self, arr: np.ndarray):\n        \"\"\"Normilize image value between 0 and 1.\"\"\"\n        arr_min = np.min(arr)\n        return (arr - arr_min) / (np.max(arr) - arr_min)\n\n    \n    def _scale_by(self, arr: np.ndarray, factor: int = 2):\n        \"\"\"\n        Scale 3d Image to factor (guesstimated transformation).\n        Parameters:\n            arr: 3d image for scalling.\n            factor: factor for scalling.\n        \"\"\"\n        mean = np.mean(arr)\n        return (arr - mean) * factor + mean\n    \n    def get_transformed_data(self, data: np.ndarray):\n        \"\"\"Data transformation: normalization, scaling, resizing.\"\"\"\n        if self.binary:\n            resized_data = resize(data, self.img_dim, preserve_range=True)\n            return np.clip(resized_data.astype(np.uint8), 0, 1).astype(np.float32)\n            \n        norm_data = np.clip(self._normalize(data)-0.1, 0, 1) ** 0.4\n        scaled_data = np.clip(self._scale_by(norm_data) - 0.1, 0, 1)\n        resized_data = resize(scaled_data, self.img_dim, preserve_range=True)\n        \n        return resized_data\n    \n    def plot_cube(self,\n                  cube,\n                  title: str = '', \n                  init_angle: int = 0,\n                  make_gif: bool = False,\n                  path_to_save: str = 'filename.gif'\n                 ):\n        \"\"\"\n        Plot 3d data.\n        -> Take array \n        -> return an array twice as large in each dimension,\n        (with an extra space between each voxel)\n        -> expand coordinates of each voxel for core rendering (to remove gaps)\n        because additional fake voxels have been added\n        -> set each voxel’s transparency equal to its value.       \n        Parameters:\n            cube: 3d data\n            title: title for figure.\n            init_angle: angle for image plot (from 0-360).\n            make_gif: if True create gif from every 5th frames from 3d image plot.\n            path_to_save: path to save GIF file.\n            \"\"\"\n\n \n        if self.normalizing:\n            cube = self._normalize(cube)\n            \n        facecolors = cm.gist_stern(cube)          \n        facecolors[:,:,:,-1] = cube\n        facecolors = self._explode(facecolors)\n\n        filled = facecolors[:,:,:,-1] != 0\n        x, y, z = self._expand_coordinates(np.indices(np.array(filled.shape) + 1))\n\n        with plt.style.context(\"dark_background\"):\n\n            fig = plt.figure(figsize=self.figsize)\n            ax = fig.gca(projection='3d')\n\n            ax.view_init(30, init_angle)\n            ax.set_xlim(right = self.img_dim[0] * 2)\n            ax.set_ylim(top = self.img_dim[1] * 2)\n            ax.set_zlim(top = self.img_dim[2] * 2)\n            ax.set_title(title, fontsize=18, y=1.05)\n\n            ax.voxels(x, y, z, filled, facecolors=facecolors, shade=False)\n\n            if make_gif:\n                images = []\n                for angle in tqdm(range(0, 360, 5)):\n                    ax.view_init(30, angle)\n                    fname = str(angle) + '.png'\n\n                    plt.savefig(fname, dpi=120, format='png', bbox_inches='tight')\n                    images.append(imageio.imread(fname))\n                    #os.remove(fname)\n                imageio.mimsave(path_to_save, images)\n                plt.close()\n\n            else:\n                plt.show()","metadata":{"execution":{"iopub.status.busy":"2023-10-20T07:47:20.994260Z","iopub.execute_input":"2023-10-20T07:47:20.994570Z","iopub.status.idle":"2023-10-20T07:47:21.019882Z","shell.execute_reply.started":"2023-10-20T07:47:20.994541Z","shell.execute_reply":"2023-10-20T07:47:21.018994Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tensor = np.zeros((512, 512, len(path_to_slices)))\nfor i in range(len(path_to_slices)):\n    image = pydicom.read_file(path_to_slices[i]).pixel_array\n    tensor[:,:,i] = image\ntitle = sample_id.replace(\".\", \"/\").split(\"/\")[-1]\nfilename = title+\"_3d.gif\"\n\ndata_to_3dgif = Image3dToGIF3d(img_dim = (120, 120, 78))\ntransformed_data = data_to_3dgif.get_transformed_data(np.moveaxis(np.flipud(tensor), [0, 1, 2], [-1, -2, -3]))\ndata_to_3dgif.plot_cube(\n    transformed_data[:77, :100, :55],\n    title=title,\n    make_gif=True,\n    path_to_save=filename\n)\n\nshow_gif(filename, format='png')","metadata":{"execution":{"iopub.status.busy":"2023-10-20T10:35:13.609561Z","iopub.execute_input":"2023-10-20T10:35:13.610289Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"NUM = '00012'\n\npath_flair = f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{NUM}/FLAIR/*.dcm'\nstart_flair = len(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{NUM}/FLAIR/Image-')\nend_flair = len('.dcm')\npath_to_slices_flair = sorted(glob.glob(path_flair), key= lambda x: int(x[start_flair:-end_flair]))\n\npath_t1w = f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{NUM}/T1w/*.dcm'\nstart_t1w = len(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{NUM}/T1w/Image-')\nend_t1w = len('.dcm')\npath_to_slices_t1w = sorted(glob.glob(path_t1w), key= lambda x: int(x[start_t1w:-end_t1w]))\n\npath_t1wce = f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{NUM}/T1wCE/*.dcm'\nstart_t1wce = len(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{NUM}/T1wCE/Image-')\nend_t1wce = len('.dcm')\npath_to_slices_t1wce = sorted(glob.glob(path_t1wce), key= lambda x: int(x[start_t1wce:-end_t1wce]))\n\npath_t2w = f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{NUM}/T2w/*.dcm'\nstart_t2w = len(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{NUM}/T2w/Image-')\nend_t2w = len('.dcm')\npath_to_slices_t2w = sorted(glob.glob(path_t2w), key= lambda x: int(x[start_t2w:-end_t2w]))\n\n\ndef slices_dcm_to_tensor(pth: str) -> np.ndarray:\n    tensor = np.zeros((512, 512,len(pth)))\n    for i in range(len(pth)):\n        image = pydicom.read_file(pth[i]).pixel_array\n        tensor[:,:,i] = image\n    return tensor\nflair_arr = slices_dcm_to_tensor(path_to_slices_flair)\nt1w_arr = slices_dcm_to_tensor(path_to_slices_t1w)\nt1wce_arr = slices_dcm_to_tensor(path_to_slices_t1wce)\nt2w_arr = slices_dcm_to_tensor(path_to_slices_t2w)\n\nprint(flair_arr.shape, t1w_arr.shape, t1wce_arr.shape, t2w_arr.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig = plt.figure(figsize=(20, 10))\n\ngs = gridspec.GridSpec(nrows=2, ncols=4, height_ratios=[1, 1.5])\n\n#  Varying density along a streamline\nax0 = fig.add_subplot(gs[0, 0])\nflair = ax0.imshow(flair_arr[:,:,150], cmap='bone')\nax0.set_title(\"FLAIR\", fontsize=18, weight='bold', y=-0.2)\nfig.colorbar(flair)\n\n#  Varying density along a streamline\nax1 = fig.add_subplot(gs[0, 1])\nt1 = ax1.imshow(t1w_arr[:,:,15], cmap='bone')\nax1.set_title(\"T1\", fontsize=18, weight='bold', y=-0.2)\nfig.colorbar(t1)\n\n#  Varying density along a streamline\nax2 = fig.add_subplot(gs[0, 2])\nt1ce = ax2.imshow(t1wce_arr[:,:,160], cmap='bone')\nax2.set_title(\"T1 contrast\", fontsize=18, weight='bold', y=-0.2)\nfig.colorbar(t1ce)\n\n#  Varying density along a streamline\nax3 = fig.add_subplot(gs[0, 3])\nt2 = ax3.imshow(t2w_arr[:,:,175], cmap='bone')\nax3.set_title(\"T2\", fontsize=18, weight='bold', y=-0.2)\nfig.colorbar(t2)\n\nplt.show();","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\n","metadata":{}}]}