{"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":"# 0. Introduction\n\nWelcome to the competition, '<a href=\"https://www.kaggle.com/c/rsna-miccai-brain-tumor-radiogenomic-classification/overview\">RSNA-MICCAI Brain Tumor Radiogenomic Classification</a>'.  \nAlso, welcome to this source code.  \nThis source code is constructed for the following goals.  \n* Providing the converting functions from DICOM (DCM) to Numpy Array (npy).  \n* Visualizing the converted array in 2D and 3D plot.  \n  \nTry this source code and upvote if you like it!  \n\nI hope always good luck to you.","metadata":{}},{"cell_type":"markdown","source":"# 1. Preparation\nBefore converting the given DICOM dataset, we will prepare some of the python packages and define some of the python custom functions.  \nNote that, DICOM is a format for storing medical data, and NumPy is a format for storing arrays in Python.\n\n* DICOM: https://www.dicomstandard.org/\n* Numpy: https://numpy.org/","metadata":{}},{"cell_type":"code","source":"\"\"\" Step 1.\nImport the python packages \"\"\"\n\nimport os, glob, shutil\nimport numpy as np\nimport matplotlib.pyplot as plt # for 2D plot\n\nfrom tqdm import tqdm # for confirming loop progress\nfrom pydicom import dcmread # for reading the given dicom data\nfrom skimage import measure # for 3D plot (1)\nfrom mpl_toolkits.mplot3d.art3d import Poly3DCollection # for 3D plot (2)","metadata":{"execution":{"iopub.status.busy":"2021-08-27T15:58:10.781453Z","iopub.execute_input":"2021-08-27T15:58:10.782264Z","iopub.status.idle":"2021-08-27T15:58:12.216641Z","shell.execute_reply.started":"2021-08-27T15:58:10.782124Z","shell.execute_reply":"2021-08-27T15:58:12.215458Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\" Step 2.\nDefine the custom functions for achieving the goal. \"\"\"\n\ndef sorted_list(path): \n    \n    \"\"\" function for getting list of files or directories. \"\"\"\n    \n    tmplist = glob.glob(path) # finding all files or directories and listing them.\n    tmplist.sort() # sorting the found list\n    \n    return tmplist\n\ndef make_dir(path, refresh=False):\n    \n    \"\"\" function for making directory (to save results). \"\"\"\n    \n    try: os.mkdir(path)\n    except: \n        if(refresh): \n            shutil.rmtree(path)\n            os.mkdir(path)\n    \ndef dicom_volume(path): \n    \n    \"\"\" function for getting DICOM volumes and convert to numpy array. \"\"\"\n    \n    list_dcm = sorted_list(path=os.path.join(path, '*.dcm')) # getting all slice as a list\n    list_index = []\n    for path_dcm in list_dcm:\n        list_index.append(int(path_dcm.split('/')[-1].replace('Image-', '').replace('.dcm', ''))) # parsing and adding the index of slice\n    list_index.sort() # sort the index\n    \n    list_arr = [] # array storage\n    for idx_dcm in list_index:\n        ds = dcmread(os.path.join(path, 'Image-%d.dcm' %(idx_dcm))) # getting slice information via single DICOM file.\n        arr = ds.pixel_array # extracting numpy array from DICOM file\n        list_arr.append(arr) # stacking to the array storage\n    \n    return np.asarray(list_arr) # converting as numpy array\n\ndef plot_2d(volume, index=0, title=''): \n    \n    \"\"\" function for plotting the DICOM slice. \"\"\"\n    \n    fig = plt.figure(figsize=(10, 10))\n    ax = fig.add_subplot(111)\n    ax.set_title(title)\n    ax.imshow(volume[index, :, :])\n\n    plt.show()\n    \ndef plot_3d(volume, threshold=0, title=''): \n    \n    \"\"\" function for plotting the DICOM volume. \"\"\"\n    \n    p = volume.transpose(2,1,0)\n    verts, faces, normals, values = measure.marching_cubes_lewiner(p, threshold)\n    fig = plt.figure(figsize=(10, 10))\n    ax = fig.add_subplot(111, projection='3d')\n    ax.set_title(title)\n    mesh = Poly3DCollection(verts[faces], alpha=0.1)\n    face_color = [0.5, 0.5, 1]\n    mesh.set_facecolor(face_color)\n    ax.add_collection3d(mesh)\n    ax.set_xlim(0, p.shape[0])\n    ax.set_ylim(0, p.shape[1])\n    ax.set_zlim(0, p.shape[2])\n\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-08-27T15:58:12.218352Z","iopub.execute_input":"2021-08-27T15:58:12.218662Z","iopub.status.idle":"2021-08-27T15:58:12.235444Z","shell.execute_reply.started":"2021-08-27T15:58:12.218632Z","shell.execute_reply":"2021-08-27T15:58:12.233851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Conversion\nWe have finished to convert the given DICOM dataset to Numpy array.  \nNow, take a converting process.\n\nWe can learn the detailed information of MRI in <a href=\"https://en.wikipedia.org/wiki/MRI_sequence\">wikipedia</a> and summarized information is following.  \n* Fluid Attenuated Inversion Recovery (FLAIR)  \n* T1-weighted pre-contrast (T1w)  \n* T1-weighted post-contrast (T1Gd)  \n* T2-weighted (T2)  ","metadata":{}},{"cell_type":"code","source":"\"\"\" Step 1.\nTake a look the list of files given in this competition. \"\"\"\n\nsorted_list(path=os.path.join('../input/rsna-miccai-brain-tumor-radiogenomic-classification', '*'))","metadata":{"execution":{"iopub.status.busy":"2021-08-27T15:58:12.237683Z","iopub.execute_input":"2021-08-27T15:58:12.238081Z","iopub.status.idle":"2021-08-27T15:58:12.262311Z","shell.execute_reply.started":"2021-08-27T15:58:12.238046Z","shell.execute_reply":"2021-08-27T15:58:12.261067Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\" Step 2.\nThe CSV file will not be DICOM data.\nThus, we need to search inside of the train or test directory. \"\"\"\n\nsorted_list(path=os.path.join('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train', '*'))[:10]","metadata":{"execution":{"iopub.status.busy":"2021-08-27T15:58:12.264490Z","iopub.execute_input":"2021-08-27T15:58:12.264880Z","iopub.status.idle":"2021-08-27T15:58:12.325199Z","shell.execute_reply.started":"2021-08-27T15:58:12.264836Z","shell.execute_reply":"2021-08-27T15:58:12.324068Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\" Step 3.\nLook inside a single ID that is supposed to be a DICOM sample. \nFour types of directory are shown. \"\"\"\n\nsorted_list(path=os.path.join('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000', '*'))","metadata":{"execution":{"iopub.status.busy":"2021-08-27T15:58:12.326640Z","iopub.execute_input":"2021-08-27T15:58:12.326964Z","iopub.status.idle":"2021-08-27T15:58:12.338053Z","shell.execute_reply.started":"2021-08-27T15:58:12.326934Z","shell.execute_reply":"2021-08-27T15:58:12.336617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\" Step 4.\nLook inside once more a single type among the four DICOM type. \nThere are several DICOM (DCM formatted) files are existing. \"\"\"\n\nsorted_list(path=os.path.join('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000/FLAIR', '*'))[:10]","metadata":{"execution":{"iopub.status.busy":"2021-08-27T15:58:12.339353Z","iopub.execute_input":"2021-08-27T15:58:12.339827Z","iopub.status.idle":"2021-08-27T15:58:12.378840Z","shell.execute_reply.started":"2021-08-27T15:58:12.339781Z","shell.execute_reply":"2021-08-27T15:58:12.377898Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\" Step 5\nNow, we can convert each type of DICOM for each ID. \"\"\"\n\nflair = dicom_volume(path='../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000/FLAIR')\nt1w = dicom_volume(path='../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000/T1w')\nt1wCE = dicom_volume(path='../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000/T1wCE')\nt2w = dicom_volume(path='../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/00000/T2w')\n\nprint(\"Converted Array\")\nprint(\"FLAIR :\", flair.shape)\nprint(\"T1w   :\", t1w.shape)\nprint(\"T1wCE :\", t1wCE.shape)\nprint(\"T2w   :\", t2w.shape)","metadata":{"execution":{"iopub.status.busy":"2021-08-27T15:58:12.380282Z","iopub.execute_input":"2021-08-27T15:58:12.380612Z","iopub.status.idle":"2021-08-27T15:58:22.476669Z","shell.execute_reply.started":"2021-08-27T15:58:12.380583Z","shell.execute_reply":"2021-08-27T15:58:22.475739Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Visualization\nWe conduct the visualization task to confirm the DICOM file has been converted as numpy correctly.  ","metadata":{}},{"cell_type":"code","source":"\"\"\" Step 1\n2D plot for each DICOM type. \"\"\"\n\nplot_2d(flair, index=int(flair.shape[0]/2), title=\"FLAIR\")\nplot_2d(t1w, index=int(t1w.shape[0]/2), title=\"T1w\")\nplot_2d(t1wCE, index=int(t1wCE.shape[0]/2), title=\"T1wCE\")\nplot_2d(t2w, index=int(t2w.shape[0]/2), title=\"T2w\")","metadata":{"execution":{"iopub.status.busy":"2021-08-27T15:58:22.478526Z","iopub.execute_input":"2021-08-27T15:58:22.479075Z","iopub.status.idle":"2021-08-27T15:58:23.506169Z","shell.execute_reply.started":"2021-08-27T15:58:22.479009Z","shell.execute_reply":"2021-08-27T15:58:23.505144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\" Step 2\n3D plot for each DICOM type. \"\"\"\n\nplot_3d(flair, title=\"FLAIR\")\nplot_3d(t1w, title=\"T1w\")\nplot_3d(t1wCE, title=\"T1wCE\")\nplot_3d(t2w, title=\"T2w\")","metadata":{"execution":{"iopub.status.busy":"2021-08-27T15:58:23.507866Z","iopub.execute_input":"2021-08-27T15:58:23.508408Z","iopub.status.idle":"2021-08-27T16:02:58.220098Z","shell.execute_reply.started":"2021-08-27T15:58:23.508200Z","shell.execute_reply":"2021-08-27T16:02:58.218913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4. Full Conversion\nWe can get fully converted dataset via following procedure.","metadata":{}},{"cell_type":"code","source":"save_root = 'converted'\nmake_dir(path=save_root, refresh=True)\n\nfor category in ['train', 'test']:\n    make_dir(path=os.path.join(save_root, category), refresh=False)\n    list_id = sorted_list(path='../input/rsna-miccai-brain-tumor-radiogenomic-classification/%s/*' %(category))\n    \n    print(\"Convert %s-set\" %(category))\n    for path_id in tqdm(list_id):\n        flair = dicom_volume(path='%s/FLAIR' %(path_id))\n        t1w = dicom_volume(path='%s/T1w' %(path_id))\n        t1wCE = dicom_volume(path='%s/T1wCE' %(path_id))\n        t2w = dicom_volume(path='%s/T2w' %(path_id))\n        \n        save_path = os.path.join(save_root, category,path_id.split('/')[-1])\n        np.savez_compressed(save_path, flair=flair, t1w=t1w, t1wCE=t1wCE, t2w=t2w)","metadata":{"execution":{"iopub.status.busy":"2021-08-27T16:02:58.221543Z","iopub.execute_input":"2021-08-27T16:02:58.221957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5. Summarizing\nNow we have finished transforming our dataset to participate in this contest.  \nA fully converted dataset is further provided in <a href=\"https://www.kaggle.com/yeonghyeon/rsnamiccai-btrc2021\">RSNA-MICCAI BTRC2021</a>.  \n\nIf you like this source code, please upvote.  \nI hope you get the results you want.  ","metadata":{}},{"cell_type":"code","source":"file_npz = np.load('./converted/train/00000.npz') # loading npz file\n\nnpy_flair = file_npz['flair'] # extracting flair array from loaded npz file\nnpy_t1w = file_npz['t1w']\nnpy_t1wCE = file_npz['t1wCE']\nnpy_t2w = file_npz['t2w']\n\nprint(\"Loaded Array\")\nprint(\"FLAIR :\", flair.shape)\nprint(\"T1w   :\", t1w.shape)\nprint(\"T1wCE :\", t1wCE.shape)\nprint(\"T2w   :\", t2w.shape)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}