{"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;\"> MÔ HÌNH HÓA HÌNH HỌC 3D</center>\n<p><center style=\"color:#949494; font-family: consolas; font-size: 20px;\">CHUYỂN ẢNH 2D THÀNH 3D DƯỚI DẠNG ĐÁM MÂY ĐIỂM</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>MỤC LỤC</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":"* SEGMENT_CLASSES: Đây là một từ điển định nghĩa các lớp phân đoạn (segment) có thể xuất hiện trong dữ liệu. Mỗi lớp được đại diện bằng một số nguyên và có một nhãn mô tả tương ứng.\n* VOLUME_SLICES và VOLUME_START_AT: Đây là các thông số liên quan đến xác định các lát cắt (slice) được sử dụng từ một khối dữ liệu 3D. VOLUME_SLICES cho biết số lát cắt được chọn từ mỗi khối dữ liệu 3D, trong trường hợp này là 100 lát cắt. VOLUME_START_AT xác định lát cắt đầu tiên của mỗi khối dữ liệu 3D mà chúng ta quan tâm, trong trường hợp này là lát cắt thứ 22.\n* IMG_SIZE: Kích thước ảnh đầu ra mong muốn, trong trường hợp này là 128x128 pixel.\n* Hai đoạn mã tiếp theo liên quan đến việc giải nén dữ liệu từ tệp nén (tarfile). Đầu tiên, đoạn mã mở tệp nén chứa dữ liệu huấn luyện (BraTS2021_Training_Data.tar) và giải nén toàn bộ nội dung vào thư mục ./BraTS2021_Training_Data. Sau đó, đoạn mã mở tệp nén chứa một tập tin ảnh mẫu (BraTS2021_00621.tar) và giải nén nó vào thư mục ./sample_img.","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\nimport glob\nimport PIL\nimport shutil\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom skimage import data\nfrom skimage.util import montage \nimport skimage.transform as skTrans\nfrom skimage.transform import rotate\nfrom skimage.transform import resize\nfrom PIL import Image, ImageOps  \n\n# neural imaging\nimport nilearn as nl\nimport nibabel as nib\nimport nilearn.plotting as nlplt\n\n\n# ml libs\nimport keras\nimport keras.backend as K\nfrom keras.callbacks import CSVLogger\nimport tensorflow as tf\nfrom tensorflow.keras.utils import plot_model\nfrom sklearn.preprocessing import MinMaxScaler\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report\nfrom tensorflow.keras.models import *\nfrom tensorflow.keras.layers import *\nfrom tensorflow.keras.optimizers import *\nfrom tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau, EarlyStopping, TensorBoard\nfrom tensorflow.keras.layers.experimental import preprocessing\n\n\n# Make numpy printouts easier to read.\nnp.set_printoptions(precision=3, suppress=True)\n# DEFINE seg-areas  \nSEGMENT_CLASSES = {\n    0 : 'NOT tumor',   \n    1 : 'NECROTIC/CORE', # or NON-ENHANCING tumor CORE - RED\n    2 : 'EDEMA',  # Green\n    3 : 'ENHANCING' # original 4 -> converted into 3 later, Yellow\n}\n\n# there are 155 slices per volume\n# to start at 5 and use 145 slices means we will skip the first 5 and last 5 \nVOLUME_SLICES = 100 \nVOLUME_START_AT = 22 # first slice of volume that we will include\n\nIMG_SIZE=128\nimport tarfile\nfile = tarfile.open('../input/brats-2021-task1/BraTS2021_Training_Data.tar')\n\nfile.extractall('./BraTS2021_Training_Data')\nfile.close()\nfile = tarfile.open('../input/brats-2021-task1/BraTS2021_00621.tar')\n\nfile.extractall('./sample_img')\nfile.close()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"file = tarfile.open('../input/brats-2021-task1/BraTS2021_00621.tar')\n\nfile.extractall('./sample_img')\nfile.close()\n\nnSample = os.listdir('./sample_img')\nnSample","metadata":{"execution":{"iopub.status.busy":"2023-06-11T08:52:31.055633Z","iopub.execute_input":"2023-06-11T08:52:31.056034Z","iopub.status.idle":"2023-06-11T08:52:31.091610Z","shell.execute_reply.started":"2023-06-11T08:52:31.055995Z","shell.execute_reply":"2023-06-11T08:52:31.090146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TRAIN_DATASET_PATH = './BraTS2021_Training_Data/'\nnSample = os.listdir(TRAIN_DATASET_PATH + 'BraTS2021_01261')\nnSample","metadata":{"execution":{"iopub.status.busy":"2023-06-11T08:52:31.093338Z","iopub.execute_input":"2023-06-11T08:52:31.094400Z","iopub.status.idle":"2023-06-11T08:52:31.103433Z","shell.execute_reply.started":"2023-06-11T08:52:31.094354Z","shell.execute_reply":"2023-06-11T08:52:31.102252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_image_flair=nib.load(TRAIN_DATASET_PATH + 'BraTS2021_01261/BraTS2021_01261_flair.nii.gz').get_fdata()\ntest_image_t1=nib.load(TRAIN_DATASET_PATH + 'BraTS2021_01261/BraTS2021_01261_t1.nii.gz').get_fdata()\ntest_image_t1ce=nib.load(TRAIN_DATASET_PATH + 'BraTS2021_01261/BraTS2021_01261_t1ce.nii.gz').get_fdata()\ntest_image_t2=nib.load(TRAIN_DATASET_PATH + 'BraTS2021_01261/BraTS2021_01261_t2.nii.gz').get_fdata()\ntest_mask=nib.load(TRAIN_DATASET_PATH + 'BraTS2021_01261/BraTS2021_01261_seg.nii.gz').get_fdata()","metadata":{"execution":{"iopub.status.busy":"2023-06-11T08:52:31.107893Z","iopub.execute_input":"2023-06-11T08:52:31.108900Z","iopub.status.idle":"2023-06-11T08:52:31.636225Z","shell.execute_reply.started":"2023-06-11T08:52:31.108857Z","shell.execute_reply":"2023-06-11T08:52:31.634747Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, (ax1, ax2, ax3, ax4, ax5) = plt.subplots(1,5, figsize = (20, 10))\nslice_w = 25\nax1.imshow(test_image_flair[:,:,test_image_flair.shape[0]//2-slice_w], cmap = 'gray')\nax1.set_title('Image flair')\nax2.imshow(test_image_t1[:,:,test_image_t1.shape[0]//2-slice_w], cmap = 'gray')\nax2.set_title('Image t1')\nax3.imshow(test_image_t1ce[:,:,test_image_t1ce.shape[0]//2-slice_w], cmap = 'gray')\nax3.set_title('Image t1ce')\nax4.imshow(test_image_t2[:,:,test_image_t2.shape[0]//2-slice_w], cmap = 'gray')\nax4.set_title('Image t2')\nax5.imshow(test_mask[:,:,test_mask.shape[0]//2-slice_w])\nax5.set_title('Mask')","metadata":{"execution":{"iopub.status.busy":"2023-06-11T08:52:31.637896Z","iopub.execute_input":"2023-06-11T08:52:31.638686Z","iopub.status.idle":"2023-06-11T08:52:32.593034Z","shell.execute_reply.started":"2023-06-11T08:52:31.638641Z","shell.execute_reply":"2023-06-11T08:52:32.591963Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, ax1 = plt.subplots(1, 1, figsize = (15,15))\nax1.imshow(rotate(montage(test_image_t1[50:-50,:,:]), 90, resize=True), cmap ='gray')","metadata":{"execution":{"iopub.status.busy":"2023-06-11T08:52:32.594543Z","iopub.execute_input":"2023-06-11T08:52:32.595694Z","iopub.status.idle":"2023-06-11T08:52:34.176237Z","shell.execute_reply.started":"2023-06-11T08:52:32.595647Z","shell.execute_reply":"2023-06-11T08:52:34.175051Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, ax1 = plt.subplots(1, 1, figsize = (15,15))\nax1.imshow(rotate(montage(test_mask[60:-60,:,:]), 90, resize=True), cmap ='gray')","metadata":{"execution":{"iopub.status.busy":"2023-06-11T08:52:34.178105Z","iopub.execute_input":"2023-06-11T08:52:34.178446Z","iopub.status.idle":"2023-06-11T08:52:35.585986Z","shell.execute_reply.started":"2023-06-11T08:52:34.178413Z","shell.execute_reply":"2023-06-11T08:52:35.584502Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"niimg = nl.image.load_img(TRAIN_DATASET_PATH + 'BraTS2021_01261/BraTS2021_01261_flair.nii.gz')\nnimask = nl.image.load_img(TRAIN_DATASET_PATH + 'BraTS2021_01261/BraTS2021_01261_seg.nii.gz')\n\nfig, axes = plt.subplots(nrows=4, figsize=(30, 40))\n\n\nnlplt.plot_anat(niimg,\n                title='BraTS18_Training_001_flair.nii plot_anat',\n                axes=axes[0])\n\nnlplt.plot_epi(niimg,\n               title='BraTS18_Training_001_flair.nii plot_epi',\n               axes=axes[1])\n\nnlplt.plot_img(niimg,\n               title='BraTS18_Training_001_flair.nii plot_img',\n               axes=axes[2])\n\nnlplt.plot_roi(nimask, \n               title='BraTS18_Training_001_flair.nii with mask plot_roi',\n               bg_img=niimg, \n               axes=axes[3], cmap='Paired')\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-06-11T08:52:35.587492Z","iopub.execute_input":"2023-06-11T08:52:35.587858Z","iopub.status.idle":"2023-06-11T08:52:49.226617Z","shell.execute_reply.started":"2023-06-11T08:52:35.587823Z","shell.execute_reply":"2023-06-11T08:52:49.225258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **NÃO**","metadata":{}},{"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-06-11T17:09:33.558638Z","iopub.execute_input":"2023-06-11T17:09:33.559273Z","iopub.status.idle":"2023-06-11T17:09:33.607655Z","shell.execute_reply.started":"2023-06-11T17:09:33.559219Z","shell.execute_reply":"2023-06-11T17:09:33.605694Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Định nghĩa hàm extract_task1_files() để giải nén các tệp tin từ tệp tar:","metadata":{}},{"cell_type":"markdown","source":"Phần đầu tiên chứa một hàm và một hàm trợ giúp để giải nén tệp tin từ một tệp tin nén (tar) và lưu chúng vào một thư mục cụ thể:\n\nextract_task1_files(root=\"./data\"): Hàm này nhận đầu vào là root (đường dẫn đến thư mục đích) và thực hiện việc giải nén các tệp tin từ tệp tin nén (tar) vào thư mục root.","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-06-11T17:09:35.260793Z","iopub.execute_input":"2023-06-11T17:09:35.261224Z","iopub.status.idle":"2023-06-11T17:09:35.270054Z","shell.execute_reply.started":"2023-06-11T17:09:35.261186Z","shell.execute_reply":"2023-06-11T17:09:35.268067Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"extract_task1_files()","metadata":{"execution":{"iopub.status.busy":"2023-06-11T17:09:36.873926Z","iopub.execute_input":"2023-06-11T17:09:36.874356Z","iopub.status.idle":"2023-06-11T17:11:55.567753Z","shell.execute_reply.started":"2023-06-11T17:09:36.874319Z","shell.execute_reply":"2023-06-11T17:11:55.565185Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Định nghĩa lớp ImageReader để đọc và xử lý hình ảnh:\n","metadata":{}},{"cell_type":"markdown","source":"Phần thứ hai chứa một lớp ImageReader và một hàm để tạo một đối tượng Scatter3d cho việc hiển thị dữ liệu ba chiều:\n\n**ImageReader**: Đây là một lớp có chức năng đọc và xử lý dữ liệu hình ảnh từ các tệp tin NIfTI. Lớp này có các thuộc tính như root (đường dẫn gốc), img_size (kích thước ảnh), normalize (có chuẩn hóa hình ảnh hay không), và single_class (sử dụng một lớp duy nhất hay không). Lớp này có các phương thức như read_file() (đọc và xử lý một tệp tin hình ảnh), và load_patient_scan() (tải dữ liệu hình ảnh cho một bệnh nhân cụ thể).\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-06-11T17:11:55.571493Z","iopub.execute_input":"2023-06-11T17:11:55.571972Z","iopub.status.idle":"2023-06-11T17:11:59.117703Z","shell.execute_reply.started":"2023-06-11T17:11:55.571921Z","shell.execute_reply":"2023-06-11T17:11:59.116398Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Định nghĩa hàm generate_3d_scatter() để tạo đối tượng Scatter3d của Plotly:","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(): Đây là một hàm tạo một đối tượng Scatter3d của Plotly để hiển thị \n# dữ liệu ba chiều. \n# Hàm này nhận các thông số như x, y, z (các mảng dữ liệu tọa độ), colors (mảng màu sắc), \n# size (kích thước điểm), opacity (độ mờ), scale (bảng màu), hover (thông tin hiển thị khi \n# di chuột), và name (tên của đối tượng Scatter3d).\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#     Hàm khởi tạo (__init__) của lớp ImageViewer3d nhận các tham số như reader \n#     (đối tượng ImageReader), mri_downsample (tỉ lệ mẫu giảm của hình ảnh MRI), \n#     và mri_colorscale (bảng màu cho hình ảnh MRI).\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# Phương thức load_clean_mri() được sử dụng để tải dữ liệu hình ảnh MRI sau khi đã làm sạch. \n# Phương thức này nhận đầu vào là image (mảng numpy của hình ảnh) và orig_dim (kích thước gốc \n# của hình ảnh). Nó tính toán tọa độ x, y, z của các điểm trong hình ảnh MRI có giá trị lớn hơn \n# 0, sử dụng tỉ lệ mẫu giảm mri_downsample, và trả về một từ điển chứa thông tin về các tọa độ \n# và màu sắc của các điểm.\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# Phương thức load_tumor_segmentation() được sử dụng để tải dữ liệu phân-segmentation của khối u.\n# Phương thức này nhận đầu vào là image (mảng numpy của phân đoạn) và orig_dim (kích thước gốc\n# của hình ảnh). Nó tính toán tọa độ x, y, z của các điểm trong phân đoạn có giá trị tương ứng \n# với các lớp khối u, sử dụng các tỉ lệ mẫu giảm khác nhau (1/1, 1/3, 1/5) cho từng lớp, và trả \n# về một từ điển chứa thông tin về các tọa độ và màu sắc của các điểm.\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# Hàm collect_patient_data trong đoạn mã trên được sử dụng để thu thập dữ liệu từ bệnh nhân và \n# tính toán các thông số liên quan đến kích thước và phân bố của các phần trong ảnh MRI.\n# Đầu tiên, hàm này tải ảnh MRI sạch (clean_mri) và thông tin phân đoạn của các loại u án UT \n# (tumors) từ đối số scan. Ảnh MRI sạch và thông tin phân đoạn được lưu trữ trong các biến \n# clean_mri và tumors tương ứng.\n# Tiếp theo, kích thước của một voxel được tính bằng cách lấy lũy thừa ba của kích thước voxel \n# (self.voxel_size). Biến voxel_volume lưu trữ kích thước voxel.\n# Biến markers_created được tính bằng tổng số điểm trong ảnh MRI sạch và tất cả các phân đoạn u\n# án UT trong tumors. Điều này đại diện cho tổng số điểm được tạo ra để hiển thị các phần trên \n# hình ảnh 3D.\n# Tiếp theo, số lượng điểm trong từng phần (ảnh MRI sạch, lõi u án UT, mô xung quanh bị xâm lấn \n# bởi u án UT, u án UT tăng cường) được tính bằng cách lấy kích thước của mỗi mảng dữ liệu từ \n# clean_mri và tumors và sử dụng thuộc tính .shape[0].\n# Sau đó, tỉ lệ phần trăm số điểm của từng phần được tính bằng cách chia số điểm của phần đó \n# cho markers_created và nhân với 100. Kết quả được lưu trong các biến clean_mri_tile, \n# tumor1_tile, tumor2_tile, và tumor4_tile.\n# Tiếp theo, kích thước của từng phần được tính bằng cách nhân số điểm trong phần đó với \n# voxel_volume và được lưu trữ trong các biến clean_mri_kichthuoc, tumor1_kichthuoc, \n# tumor2_kichthuoc, và tumor4_kichthuoc.\n# Sau đó, thông tin về số điểm và kích thước của từng phần được in ra màn hình sử dụng \n# câu lệnh print.\n# Cuối cùng, hàm trả về một danh sách chứa các đối tượng generate_3d_scatter tương ứng \n# với các phần và số lượng markers_created. Các đối tượng generate_3d_scatter này được \n# sử dụng để hiển thị các phần trên hình ảnh 3D.\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 - hình ảnh MRI sạch:', clean_mri_diem ,'points,', clean_mri_tile ,'%',clean_mri_kichthuoc)\n        print('Lõi u án UT:', tumor1_diem , 'points,', tumor1_tile ,'%,', tumor1_kichthuoc)\n        print('Mô xung quanh bị xâm lấn bởi u án UT:', tumor2_diem ,'points,', tumor2_tile ,'%,', tumor2_kichthuoc)\n        print('U án UT tăng cường bằng chất có chứa Gadolinium:', 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 - hình ảnh MRI sạch('+ clean_mri_kichthuoc +')'),\n            generate_3d_scatter(**tumors[1], opacity=0.8, hover='all', name='Lõi u án UT(' + tumor1_kichthuoc + ')'),\n            generate_3d_scatter(**tumors[2], opacity=0.4, hover='all', name='Mô xung quanh bị xâm lấn bởi u án UT(' + tumor2_kichthuoc + ')'),\n            generate_3d_scatter(**tumors[4], opacity=0.4, hover='all', name='U án UT tăng cường bằng chất có chứa Gadolinium(' + tumor4_kichthuoc + ')'),\n        ], markers_created\n \n\n# Phương thức get_3d_scan() được sử dụng để lấy dữ liệu ba chiều của một bệnh nhân và tạo đối \n# tượng biểu đồ Scatter3d tương ứng. Phương thức này nhận các đối số như patient_idx \n# (số thứ tự của bệnh nhân), và scan_type (loại quét MRI). Nó sử dụng phương thức \n# load_patient_scan() của lớp ImageReader để tải dữ liệu hình ảnh cho bệnh nhân cụ thể.\n# Sau đó, nó sử dụng phương thức collect_patient_data() để thu thập dữ liệu hình ảnh và \n# phân đoạn của bệnh nhân. Cuối cùng, nó tạo một đối tượng biểu đồ Scatter3d bằng cách sử \n# dụng các đối tượng Scatter3d từ dữ liệu thu thập và cài đặt các thuộc tính và cấu hình \n# cho biểu đồ Scatter3d và trả về đối tượng biểu đồ.\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-06-11T17:11:59.123109Z","iopub.execute_input":"2023-06-11T17:11:59.123576Z","iopub.status.idle":"2023-06-11T17:11:59.327048Z","shell.execute_reply.started":"2023-06-11T17:11:59.123530Z","shell.execute_reply":"2023-06-11T17:11:59.325590Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* load_tumor_segmentation(self, image:np.array, orig_dim:int) -> dict: Đây là một phương thức trong một lớp hoặc đối tượng. Nó nhận đầu vào là một mảng numpy (image) đại diện cho một hình ảnh, và một số nguyên (orig_dim) đại diện cho kích thước gốc của hình ảnh trước khi được xử lý. Phương thức này trả về một từ điển (dict) chứa thông tin về các phân đoạn u án ung thư trên hình ảnh.\n* \n* tumors = {}: Đây là một biến từ điển được khởi tạo để lưu trữ thông tin về các phân đoạn u án ung thư.\n* \n* shape_offset = image.shape[1]/orig_dim: Biến shape_offset được tính toán để xác định tỷ lệ giữa kích thước thật của hình ảnh và kích thước gốc ban đầu. Nó được sử dụng để chuyển đổi tọa độ của các điểm trong các phân đoạn ung thư.\n* \n* sampling: Đây là một từ điển chứa các thông số mẫu lấy mẫu cho các lớp mô ung thư khác nhau. Cụ thể, nó chỉ định bước mẫu lấy mẫu cho mỗi lớp, tức là số lượng điểm được lấy mẫu từ mỗi lớp tương ứng với 1 điểm thực tế.\n* \n* for class_idx in sampling:: Đây là một vòng lặp qua các phần tử trong từ điển sampling. class_idx là khóa của từng phần tử.\n* \n* (image == class_idx).nonzero(): Đây là một phép so sánh để tìm tất cả các vị trí trong image mà giá trị bằng class_idx. .nonzero() trả về các chỉ mục của các vị trí thỏa mãn điều kiện.\n* \n* x[::sampling[class_idx]], y[::sampling[class_idx]], z[::sampling[class_idx]]: Đây là một cách để lấy mẫu từ các mảng x, y, và z theo bước mẫu lấy mẫu được xác định bởi sampling[class_idx]. x[::sampling[class_idx]] chỉ lấy các phần tử từ x với bước sampling[class_idx].\n* \n* tumors[class_idx] = dict(...): Đây là một cách để thêm một phần tử vào từ điển tumors. Các thông tin về phân đoạn ung thư được lưu trữ dưới dạng một từ điển con với các khóa và giá trị tương ứng.\n* \n* clean_mri['x'].shape[0] + sum(tumors[class_idx]['x'].shape[0] for class_idx in tumors): Đây là một biểu thức tính toán tổng số lượng điểm dùng để tạo đám mây điểm 3D. Nó bao gồm số lượng điểm từ clean_mri và tổng số lượng điểm từ tất cả các lớp mô ung thư trong tumors.","metadata":{}},{"cell_type":"markdown","source":"Tạo đối tượng ImageReader và ImageViewer3d:\n","metadata":{}},{"cell_type":"markdown","source":"Tạo hai đối tượng: reader và viewer.\n\nĐối tượng reader được tạo bằng cách khởi tạo lớp ImageReader. Đối tượng này được khởi tạo với các tham số sau:\n\n* root='./data': Đường dẫn đến thư mục chứa dữ liệu hình ảnh.\n* img_size=128: Kích thước ảnh được thay đổi thành 128x128.\n* normalize=True: Chuẩn hóa ảnh.\n* single_class=False: Không áp dụng phân loại thành một lớp duy nhất.\n\nĐối tượng viewer được tạo bằng cách khởi tạo lớp ImageViewer3d. Đối tượng này được khởi tạo với các tham số sau:\n\n* reader: Đối tượng ImageReader được truyền vào để đọc dữ liệu hình ảnh.\n* mri_downsample=25: Tỉ lệ mẫu giảm của hình ảnh MRI là 25.\n* Các đối tượng reader và viewer được tạo ra để sử dụng trong việc đọc và hiển thị dữ liệu hình ảnh 3D.","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-06-11T17:11:59.330952Z","iopub.execute_input":"2023-06-11T17:11:59.331380Z","iopub.status.idle":"2023-06-11T17:11:59.339072Z","shell.execute_reply.started":"2023-06-11T17:11:59.331340Z","shell.execute_reply":"2023-06-11T17:11:59.337259Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Sử dụng viewer.get_3d_scan() để lấy hình ảnh 3D của bệnh nhân cụ thể:","metadata":{}},{"cell_type":"markdown","source":"Đoạn mã tạo ra một biểu đồ 3D của quét MRI của bệnh nhân đầu tiên (với patient_idx=0) và loại quét 't1'.\n\nĐầu tiên, viewer.get_3d_scan(0, 't1') được gọi để lấy đối tượng go.Figure biểu đồ 3D. Phương thức này gọi phương thức load_patient_scan của đối tượng reader để đọc dữ liệu quét MRI của bệnh nhân có patient_idx=0 và loại quét 't1'. Sau đó, nó sử dụng phương thức collect_patient_data của viewer để tạo dữ liệu cho biểu đồ 3D. Cuối cùng, biểu đồ 3D được tạo bằng cách sử dụng go.Figure và các dữ liệu được truyền vào.\n\nSau đó, plotly.offline.iplot(fig) được sử dụng để hiển thị biểu đồ 3D. Phương thức iplot của plotly.offline cho phép hiển thị biểu đồ trực tiếp trong notebook hoặc môi trường tương tác khác.","metadata":{}},{"cell_type":"markdown","source":"* generate_3d_scatter(**clean_mri, scale=self.mri_colorscale, opacity=0.3, hover='skip', name='Brain MRI')\n*     => Tạo đám mây điểm 3D từ clean_mri (hình ảnh MRI sạch). Điểm đám mây sẽ được tỉ lệ với màu sắc được xác định bởi mri_colorscale. Độ mờ (độ trong suốt) của đám mây là 0.3. Khi di chuột qua, sẽ bỏ qua hiển thị thông tin chi tiết. Tên đám mây điểm là \"Brain MRI\".\n\n* generate_3d_scatter(**tumors[1], opacity=0.8, hover='all', name='Necrotic tumor core')\n*     => Tạo đám mây điểm 3D từ tumors[1] (lõi u án ung thư). Điểm đám mây sẽ có độ mờ là 0.8. Khi di chuột qua, sẽ hiển thị thông tin chi tiết. Tên đám mây điểm là \"Necrotic tumor core\".\n\n* generate_3d_scatter(**tumors[2], opacity=0.4, hover='all', name='Peritumoral invaded tissue')\n*     => Tạo đám mây điểm 3D từ tumors[2] (mô xung quanh bị xâm lấn bởi u án ung thư). Điểm đám mây sẽ có độ mờ là 0.4. Khi di chuột qua, sẽ hiển thị thông tin chi tiết. Tên đám mây điểm là \"Peritumoral invaded tissue\".\n\n* generate_3d_scatter(**tumors[4], opacity=0.4, hover='all', name='GD-enhancing tumor')\n*     => Tạo đám mây điểm 3D từ tumors[4] (u án ung thư tăng cường bằng chất có chứa Gadolinium). Điểm đám mây sẽ có độ mờ là 0.4. Khi di chuột qua, sẽ hiển thị thông tin chi tiết. Tên đám mây điểm là \"GD-enhancing tumor\".\n ","metadata":{}},{"cell_type":"code","source":"fig = viewer.get_3d_scan(0, 't1')\nplotly.offline.iplot(fig)","metadata":{"execution":{"iopub.status.busy":"2023-06-11T17:11:59.340965Z","iopub.execute_input":"2023-06-11T17:11:59.341450Z","iopub.status.idle":"2023-06-11T17:12:01.902830Z","shell.execute_reply.started":"2023-06-11T17:11:59.341407Z","shell.execute_reply":"2023-06-11T17:12:01.901381Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"t1, t2, t1ce, flair, seg","metadata":{}},{"cell_type":"code","source":"fig = viewer.get_3d_scan(9, 't1ce')\nplotly.offline.iplot(fig)","metadata":{"execution":{"iopub.status.busy":"2023-06-11T17:12:01.904470Z","iopub.execute_input":"2023-06-11T17:12:01.904994Z","iopub.status.idle":"2023-06-11T17:12:02.734182Z","shell.execute_reply.started":"2023-06-11T17:12:01.904942Z","shell.execute_reply":"2023-06-11T17:12:02.732746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":" patient_idx là chỉ số của bệnh nhân trong tập dữ liệu và scan_type là loại quét MRI (ví dụ: 'flair', 't1', 't2', 't1ce').","metadata":{}},{"cell_type":"markdown","source":"Đoạn mã tạo ra một biểu đồ 3D của quét MRI của bệnh nhân thứ 9 (với patient_idx=9) và loại quét 'flair'.\n\nĐầu tiên, viewer.get_3d_scan(9, 'flair') được gọi để lấy đối tượng go.Figure biểu đồ 3D. Phương thức này gọi phương thức load_patient_scan của đối tượng reader để đọc dữ liệu quét MRI của bệnh nhân có patient_idx=9 và loại quét 'flair'. Sau đó, nó sử dụng phương thức collect_patient_data của viewer để tạo dữ liệu cho biểu đồ 3D. Cuối cùng, biểu đồ 3D được tạo bằng cách sử dụng go.Figure và các dữ liệu được truyền vào.\n\nSau đó, plotly.offline.iplot(fig) được sử dụng để hiển thị biểu đồ 3D. Phương thức iplot của plotly.offline cho phép hiển thị biểu đồ trực tiếp trong notebook hoặc môi trường tương tác khác.","metadata":{}},{"cell_type":"code","source":"fig = viewer.get_3d_scan(9, 'flair')\nplotly.offline.iplot(fig)","metadata":{"execution":{"iopub.status.busy":"2023-06-11T17:12:02.736645Z","iopub.execute_input":"2023-06-11T17:12:02.737172Z","iopub.status.idle":"2023-06-11T17:12:03.160350Z","shell.execute_reply.started":"2023-06-11T17:12:02.737117Z","shell.execute_reply":"2023-06-11T17:12:03.159177Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\nimg_id = \"00009\"\nimport matplotlib.pyplot as plt\nfor i, nii in enumerate([f'/kaggle/working/data/BraTS2021_{img_id}/BraTS2021_{img_id}_{s_type}.nii.gz' for s_type in [\"flair\", \"t1\", \"t1ce\", \"t2\", \"seg\"]]):\n    # PLOTTING\n    image = nib.load(nii).get_fdata()\n    slices = image.shape[-1]\n    rows = int(np.ceil((slices/2)/10))\n    plt.figure(figsize=(20, rows*2))\n    plt.suptitle(f\"\\n\\n\\n{nii.rsplit('_', 1)[-1].split('.', 1)[0]} SCAN\\n\".upper(), fontsize=18, fontweight=\"bold\")\n    for j in range(0, slices, 2):\n        plt.subplot(rows, 10, 1+j//2)\n        plt.axis(False)\n        plt.imshow(image[:, :, j], cmap=\"bone\")\n    plt.tight_layout()\n    plt.show()\n","metadata":{"execution":{"iopub.status.busy":"2023-06-11T17:14:17.179783Z","iopub.execute_input":"2023-06-11T17:14:17.180315Z","iopub.status.idle":"2023-06-11T17:14:38.169658Z","shell.execute_reply.started":"2023-06-11T17:14:17.180270Z","shell.execute_reply":"2023-06-11T17:14:38.168021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# <div style=\"box-shadow: rgba(240, 46, 170, 0.4) -5px 5px inset, rgba(240, 46, 170, 0.3) -10px 10px inset, rgba(240, 46, 170, 0.2) -15px 15px inset, rgba(240, 46, 170, 0.1) -20px 20px inset, rgba(240, 46, 170, 0.05) -25px 25px inset; padding:20px; font-size:30px; font-family: consolas; display:fill; border-radius:15px; color: rgba(240, 46, 170, 0.7)\"> <b> ༼⁠ ⁠つ⁠ ⁠◕⁠‿⁠◕⁠ ⁠༽⁠つ Thank You!</b></div>\n\n\n","metadata":{}},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}