{"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":"[![image.png](https://i.postimg.cc/3NS5z6RB/image.png)](https://postimg.cc/V0CVCKXr)\n\nFew days back I watched a show on Netflix named, [The Surgeon's Cut](https://www.youtube.com/watch?v=Fft5igeEIEM) and episode 2 intrigued me. This episode features Dr. Alfredo Quiñones-Hinojosa, one of the leading neurosurgeon who's expertise lies in removing complex brain tumors. \n\nOne of the line he said in the episode which really gripped me and I quote, \"The brain wasn't meant to be opened. It's not natural what we do. It's us defying nature. Opening the skull is scared and then cutting through the dura, the delicate membrane that is like the final gateway into a secret temple [brain].\"\n\nIf you think about it this competition enables us to help reduce the sugeries required to perform an biopsy to determine if there is a tumor or not. What excites me the most is the fact that we humans are capable of course correcting the natural progression of our body.\n\nMRI gives us an indirect way to look into our brain to find the defects and remove them hopefully to extend a human life. If we can develop algorightms that can improve the survival rate from less than a year to atleast two years, in my opinion it's giving a human being opportunities to connect with his/her family, do what's necesary for them and leave behind a legacy. \n\n------------------------------------------------------------------------------------------------------------------\n\nIn this kernel, I want to perform some basic EDA and use [Weights and Biases](https://wandb.ai/site) to visualize the data interactively. The idea is to understand the best way to present the data to make it more intuitive for everyone to get started with this competition. \n\n","metadata":{}},{"cell_type":"markdown","source":"# 🌍 Imports and Setup","metadata":{}},{"cell_type":"code","source":"!pip install -q pydicom","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-08-15T19:36:11.509895Z","iopub.execute_input":"2021-08-15T19:36:11.510683Z","iopub.status.idle":"2021-08-15T19:36:20.281471Z","shell.execute_reply.started":"2021-08-15T19:36:11.510554Z","shell.execute_reply":"2021-08-15T19:36:20.280592Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport re \nimport glob\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nimport seaborn as sns\nfrom tqdm import tqdm\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\n# Pydicom related imports\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport SimpleITK as sitk\n\n\n# Deep learning packages\nimport tensorflow as tf\n\n# For gif creation\nimport imageio","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:36:54.716007Z","iopub.execute_input":"2021-08-15T19:36:54.716660Z","iopub.status.idle":"2021-08-15T19:36:55.252839Z","shell.execute_reply.started":"2021-08-15T19:36:54.716600Z","shell.execute_reply":"2021-08-15T19:36:55.251590Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<img src=\"https://i.imgur.com/gb6B4ig.png\" width=\"400\" alt=\"Weights & Biases\" />\n\nWeights & Biases (W&B) is a set of machine learning tools that helps you build better models faster. **Kaggle competitions require fast-paced model development and evaluation**. There are a lot of components: exploring the training data, training different models, combining trained models in different combinations (ensembling), and so on.\n\n> ⏳ Lots of components = lots of places to go wrong = lots of time spent debugging \n\nW&B can be useful for Kaggle competition with it's lightweight and interoperable tools:\n\n* quickly track experiments,<br>\n* version and iterate on datasets, <br>\n* evaluate model performance,<br>\n* reproduce models,<br>\n* visualize results and spot regressions,<br>\n* and share findings with colleagues.\n\nTo learn more about Weights and Biases check out this [kernel](https://www.kaggle.com/ayuraj/experiment-tracking-with-weights-and-biases).","metadata":{}},{"cell_type":"code","source":"# W&B for experiment tracking\nimport wandb\nwandb.login()","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:36:57.719602Z","iopub.execute_input":"2021-08-15T19:36:57.719958Z","iopub.status.idle":"2021-08-15T19:37:16.006739Z","shell.execute_reply.started":"2021-08-15T19:36:57.719929Z","shell.execute_reply":"2021-08-15T19:37:16.005553Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> You can learn more about using W&B in this introduction kernel, [Experiment Tracking with Weights and Biases](https://www.kaggle.com/ayuraj/experiment-tracking-with-weights-and-biases).","metadata":{}},{"cell_type":"code","source":"CONFIG = {'IMG_SIZE': 224, \n          'NUM_FRAMES': 14,\n          'competition': 'rsna-miccai-brain', \n          '_wandb_kernel': 'ayut'}","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-08-15T19:37:30.633980Z","iopub.execute_input":"2021-08-15T19:37:30.634329Z","iopub.status.idle":"2021-08-15T19:37:30.639661Z","shell.execute_reply.started":"2021-08-15T19:37:30.634298Z","shell.execute_reply":"2021-08-15T19:37:30.638673Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 🌱 Training Data\n\nThe dataset consist of 585 patients and are given by unique id,`BraTS21ID`. You can also consider them independent cases. \n\nEach case consists of four structural multi-parametric MRI (mpMRI) scans. These scans are - \n\n* Fluid Attenuated Inversion Recovery (FLAIR) <br>\n* T1-weighted pre-contrast (T1w)\n* T1-weighted post-contrast (T1Gd)\n* T2-weighted (T2)\n\nEach sub-folder contains multiple scans in Dicom format with name - `Image-X.dcm`. Here X increases from 1 to N. **Each scan can be considered a slice (plane) of the brain**. We will investigate this further. \n\nGlioblastoma is a life-threatning brain tumor which is caused by the presence of a specific genetic sequence in the tumor known as MGMT promoter methylation. **Each independent case (patient) is labeled with `MGMT_value`. 1 corresponds to the presence of MGMT promoter and 0 corresponds to absense.**","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train_labels.csv')\nprint('Number of rows: ', len(train_df))\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:37:31.542524Z","iopub.execute_input":"2021-08-15T19:37:31.544849Z","iopub.status.idle":"2021-08-15T19:37:31.590965Z","shell.execute_reply.started":"2021-08-15T19:37:31.544752Z","shell.execute_reply":"2021-08-15T19:37:31.589599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(16, 5))\nax = sns.countplot(data=train_df, y=\"MGMT_value\")\nax.set_title(\"Distribution of Labels\");\nprint(train_df.MGMT_value.value_counts())","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:37:32.263625Z","iopub.execute_input":"2021-08-15T19:37:32.264451Z","iopub.status.idle":"2021-08-15T19:37:32.453966Z","shell.execute_reply.started":"2021-08-15T19:37:32.264404Z","shell.execute_reply":"2021-08-15T19:37:32.452671Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> The dataset is not entirely balanced but the imbalance is not severe in the patient-level. ","metadata":{}},{"cell_type":"markdown","source":"### Let's look at the number of files (`.dcm`) we have.","metadata":{}},{"cell_type":"code","source":"filenames = glob.glob('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/*/*/*')\nprint(f'Total number of files: {len(filenames)}')","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:37:34.223577Z","iopub.execute_input":"2021-08-15T19:37:34.223998Z","iopub.status.idle":"2021-08-15T19:38:26.657514Z","shell.execute_reply.started":"2021-08-15T19:37:34.223964Z","shell.execute_reply":"2021-08-15T19:38:26.655889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Let's look at the distribution of files per scan types. ","metadata":{}},{"cell_type":"code","source":"label_dict = {\n    'FLAIR': [],\n    'T1w': [],\n    'T1wCE': [],\n    'T2w': []\n}\n\nfor filename in tqdm(filenames):\n    scan = filename.split('/')[-2]\n    if scan=='FLAIR':\n        label_dict['FLAIR'].append(filename)\n    elif scan=='T1w':\n        label_dict['T1w'].append(filename)\n    elif scan=='T1wCE':\n        label_dict['T1wCE'].append(filename)\n    else:\n        label_dict['T2w'].append(filename)\n        \nprint('Size of FLAIR scan: {}, T1w scan: {}, T1wCE scan: {}, T2w scan: {}'.format(len(label_dict['FLAIR']), \n                                                                                  len(label_dict['T1w']),\n                                                                                  len(label_dict['T1wCE']),\n                                                                                  len(label_dict['T2w'])))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-08-15T19:39:17.333235Z","iopub.execute_input":"2021-08-15T19:39:17.333800Z","iopub.status.idle":"2021-08-15T19:39:17.848903Z","shell.execute_reply.started":"2021-08-15T19:39:17.333766Z","shell.execute_reply":"2021-08-15T19:39:17.847867Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"run = wandb.init(project='brain-tumor-viz', config=CONFIG)\ndata = [['FLAIR', 74248], ['T1w', 77627], ['T1wCE', 96766], ['T2w', 100000]]\ntable = wandb.Table(data=data, columns = [\"Scan Type\", \"Num Files\"])\nwandb.log({\"my_bar_chart_id\" : wandb.plot.bar(table, \"Scan Type\", \"Num Files\",\n                               title=\"Scan Types vs Number of Dicom files\")})\nrun.finish()","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-07-30T12:47:36.95484Z","iopub.execute_input":"2021-07-30T12:47:36.955187Z","iopub.status.idle":"2021-07-30T12:47:50.251875Z","shell.execute_reply.started":"2021-07-30T12:47:36.955157Z","shell.execute_reply":"2021-07-30T12:47:50.250987Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### [Interactive W&B Bar Chart $\\rightarrow$](https://wandb.ai/ayush-thakur/brain-tumor-viz/runs/3se9o0ot)\n\n![img](https://i.ibb.co/QNjHyQd/W-B-Chart-7-16-2021-3-49-04-AM.png)","metadata":{}},{"cell_type":"markdown","source":"# 🌊 MRI and Scan Types\n\nCredit: The below text is derived from this excellent post titled, [Magnetic Resonance Imaging (MRI) of the Brain and Spine: Basics](https://case.edu/med/neurology/NR/MRI%20Basics.htm)\n\n> Magnetic resonance imaging (MRI) is one of the most commonly used tests in neurology and neurosurgery. MRI provides exquisite detail of brain, spinal cord and vascular anatomy, and has the advantage of being able to visualize anatomy in all three planes: axial, sagittal and coronal (see the example image below). \n\n![img](https://i.ibb.co/S0wCMqc/image.png)\n\nA powerful magnetic field is employed to align the protons that are normally randomly oriented within the water nuclei of the tissue being examined (in this case brain). This alignment (or magnetization) is next perturbed or disrupted by introduction of an external Radio Frequency (RF) energy. \n\nWhen the nuclei relaxes it emits RF signals (energy) which are measured. Fourier Transform is used to convert the frequency information contained in the signal from each location in the imaged plane to corresponding intensity levels, which are then displayed as shades of gray in a matrix arrangement of pixels. \n\n**By varying the sequence of RF pulses applied & collected, different types of images are created.**\n\n* Repetition Time (TR) is the amount of time between successive pulse sequences applied to the same slice. <br>\n* Time to Echo (TE) is the time between the delivery of the RF pulse and the receipt of the echo signal. \n\n**T1, T2 and FLAIR are different MRI Imaging Sequence that has pros and cons over each other.**\n\nCheck out this [discussion post](https://www.kaggle.com/c/rsna-miccai-brain-tumor-radiogenomic-classification/discussion/252843) by [dlbsabu](https://www.kaggle.com/dlbsabu) that explains the differences between different scan types.\n\nThis image compares different MRI Imaging sequences really well:\n\n![img](https://i.ibb.co/5vZ4CgH/image.png)\n\n*Note that FLAIR is very sensitive to pathology and makes the differentiation between CSF and an abnormality much easier. Also note that CSF stands for Cerebrospinal Fluid.*","metadata":{}},{"cell_type":"markdown","source":"### Read DICOM File","metadata":{}},{"cell_type":"code","source":"# Reference: https://www.kaggle.com/xhlulu/siim-covid-19-convert-to-jpg-256px\ndef ReadMRI(path, voi_lut = True, fix_monochrome = True):\n    # Original from: https://www.kaggle.com/raddar/convert-dicom-to-np-array-the-correct-way\n    dicom = pydicom.read_file(path)\n    \n    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to \n    # \"human-friendly\" view\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n               \n    # depending on this value, X-ray may look inverted - fix that:\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n        \n    data = data - np.min(data)\n    data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n        \n    return data","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:39:27.578609Z","iopub.execute_input":"2021-08-15T19:39:27.579229Z","iopub.status.idle":"2021-08-15T19:39:27.586035Z","shell.execute_reply.started":"2021-08-15T19:39:27.579176Z","shell.execute_reply":"2021-08-15T19:39:27.585034Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Let's quickly test the function to read the DICOM file.","metadata":{}},{"cell_type":"code","source":"data = ReadMRI(filenames[11])\nprint('Shape of data: ', data.shape)\n\nplt.figure(figsize=(5, 5))\nplt.imshow(data, cmap='gray');","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:39:30.881171Z","iopub.execute_input":"2021-08-15T19:39:30.881748Z","iopub.status.idle":"2021-08-15T19:39:31.137895Z","shell.execute_reply.started":"2021-08-15T19:39:30.881698Z","shell.execute_reply":"2021-08-15T19:39:31.136687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> Each MRI image has a resolution of 512x512 pixels. \n\n> There are some images without any useful content, i.e, there's no brainy stuff in the image.","metadata":{}},{"cell_type":"markdown","source":"# 🐋 Animate MRI Scan\n\nAs mentioned earlier, each subfolder within individual case corresponds to a MRI Imaging Sequence. We will use Weights and Biases image logging feature to log all the images in a  subfolder and will use a slider to animate the images to get a sense of 3D reconstruction of brain. \n\nWhy am I using Weights and Biases for this? Well I could animate with just two lines of code and I can keep a log of all the scans that I animate to compare later. ","metadata":{}},{"cell_type":"code","source":"patient_ids = os.listdir('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train')\nIDX = np.random.choice(len(patient_ids))\n\npatient_id = patient_ids[IDX]\nprint(f'Patient ID: {patient_id}')","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:39:41.339853Z","iopub.execute_input":"2021-08-15T19:39:41.340405Z","iopub.status.idle":"2021-08-15T19:39:41.349539Z","shell.execute_reply.started":"2021-08-15T19:39:41.340367Z","shell.execute_reply":"2021-08-15T19:39:41.348445Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# https://stackoverflow.com/a/2669120/7636462\ndef sorted_nicely(l): \n    \"\"\" Sort the given iterable in the way that humans expect.\"\"\" \n    convert = lambda text: int(text) if text.isdigit() else text \n    alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] \n    return sorted(l, key = alphanum_key)","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:39:43.160995Z","iopub.execute_input":"2021-08-15T19:39:43.161563Z","iopub.status.idle":"2021-08-15T19:39:43.168235Z","shell.execute_reply.started":"2021-08-15T19:39:43.161526Z","shell.execute_reply":"2021-08-15T19:39:43.166990Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Let's animate FLAIR MRI Sequence","metadata":{}},{"cell_type":"code","source":"flair_data = []\nflair_filenames = os.listdir(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{patient_id}/FLAIR/')\nflair_filenames = sorted_nicely(flair_filenames)\nfor filename in flair_filenames:\n    flair_data.append(ReadMRI(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{patient_id}/FLAIR/{filename}'))\n    \nprint(f'Number of FLAIR images for the patient: {len(flair_data)}')","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:39:48.263721Z","iopub.execute_input":"2021-08-15T19:39:48.264138Z","iopub.status.idle":"2021-08-15T19:39:48.745229Z","shell.execute_reply.started":"2021-08-15T19:39:48.264102Z","shell.execute_reply":"2021-08-15T19:39:48.744183Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"run = wandb.init(project='brain-tumor-viz', config=CONFIG)\nwandb.log({f\"flair_{patient_id}\": [wandb.Image(image) for image in flair_data]})\nrun.finish()","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-07-30T12:48:47.997976Z","iopub.execute_input":"2021-07-30T12:48:47.998347Z","iopub.status.idle":"2021-07-30T12:49:01.019851Z","shell.execute_reply.started":"2021-07-30T12:48:47.998317Z","shell.execute_reply":"2021-07-30T12:49:01.01853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### [Check out the run page $\\rightarrow$](https://wandb.ai/ayush-thakur/brain-tumor-viz/runs/35x6onk7)\n[![Animation.gif](https://i.postimg.cc/pXfL1VYV/Animation.gif)](https://postimg.cc/SJK4nhvH)","metadata":{}},{"cell_type":"markdown","source":"### Let's visualize different scan types for multiple patients with brain tumor.","metadata":{}},{"cell_type":"code","source":"train_df_1 = train_df.loc[train_df.MGMT_value==1].reset_index(drop=True)\nprint(f'Number of patients with brain tumor: {len(train_df_1)}')\n\nIMG_2_LOG = 20\ntrain_df_1_sampled = train_df_1.sample(n=IMG_2_LOG).reset_index(drop=True)\nprint(f'Number of sampled patients: {len(train_df_1_sampled)}')\n\n# Let's log the sampled dataframe to W&B Tables\nsampled_data_at = wandb.Table(dataframe=train_df_1_sampled)\n\nrun = wandb.init(project='brain-tumor-viz', config=CONFIG)\nwandb.log({f\"Sampled Dataframe\": sampled_data_at})\nrun.finish()","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-07-30T12:49:05.326615Z","iopub.execute_input":"2021-07-30T12:49:05.327041Z","iopub.status.idle":"2021-07-30T12:49:18.310966Z","shell.execute_reply.started":"2021-07-30T12:49:05.327002Z","shell.execute_reply":"2021-07-30T12:49:18.309965Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_patient_id(patient_id):\n    if patient_id < 10:\n        return '0000'+str(patient_id)\n    elif patient_id >= 10 and patient_id < 100:\n        return '000'+str(patient_id)\n    elif patient_id >=100 and patient_id < 1000:\n        return '00'+str(patient_id)\n    else:\n        return '0'+str(patient_id)","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:40:01.039447Z","iopub.execute_input":"2021-08-15T19:40:01.039833Z","iopub.status.idle":"2021-08-15T19:40:01.047056Z","shell.execute_reply.started":"2021-08-15T19:40:01.039800Z","shell.execute_reply":"2021-08-15T19:40:01.045757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for i in tqdm(range(len(train_df_1_sampled))):\n    # Get Patient ID and directory name\n    row = train_df_1_sampled.loc[i]\n    patient_id = get_patient_id(row.BraTS21ID)\n    \n    # Initialize a W&B run to log images\n    run = wandb.init(project='brain-tumor-viz', config=CONFIG, name=f'{patient_id}')\n\n    for key in label_dict.keys():\n        if os.path.isdir(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{patient_id}/{key}'):\n            _filenames = os.listdir(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{patient_id}/{key}')\n            _filenames = sorted_nicely(_filenames)\n            for filename in _filenames:\n                mri_data = ReadMRI(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{patient_id}/{key}/{filename}')\n                # Log MRI data sequentially\n                wandb.log({f'{key}': [wandb.Image(mri_data)]})\n\n    # Close W&B run associated to that patient id.\n    run.finish()","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-07-30T13:18:52.867666Z","iopub.execute_input":"2021-07-30T13:18:52.868068Z","iopub.status.idle":"2021-07-30T13:30:01.059672Z","shell.execute_reply.started":"2021-07-30T13:18:52.868036Z","shell.execute_reply":"2021-07-30T13:30:01.058084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### [Check out the report to interact with the logged images $\\rightarrow$](https://wandb.ai/ayush-thakur/brain-tumor-viz/reports/Visualize-MRI-Image-Sequence---Vmlldzo4NTcwMDk)\n[![Animation2.gif](https://i.postimg.cc/wjZcSX9s/Animation2.gif)](https://postimg.cc/0zGwSJM8)","metadata":{}},{"cell_type":"markdown","source":"# ❄️ Create GIF using Imageio\n\nGoing forward, I am going to use the dataset created by [Jonathan Besomi](https://www.kaggle.com/jonathanbesomi). Many thanks to him for creating this. You can find the data [here](https://www.kaggle.com/jonathanbesomi/rsna-miccai-png).","metadata":{}},{"cell_type":"code","source":"processed_data_path = '../input/rsna-miccai-png/train'","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:40:07.439277Z","iopub.execute_input":"2021-08-15T19:40:07.439850Z","iopub.status.idle":"2021-08-15T19:40:07.444357Z","shell.execute_reply.started":"2021-08-15T19:40:07.439813Z","shell.execute_reply":"2021-08-15T19:40:07.443544Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# randomly select few samples for which we will create gifs using Imageio\nsample_patients = np.random.choice(os.listdir(processed_data_path), 32)\nsample_patient_paths = [f'{processed_data_path}/{sample}/T2w/' for sample in sample_patients]","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:40:08.263626Z","iopub.execute_input":"2021-08-15T19:40:08.264012Z","iopub.status.idle":"2021-08-15T19:40:08.304143Z","shell.execute_reply.started":"2021-08-15T19:40:08.263971Z","shell.execute_reply":"2021-08-15T19:40:08.303009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def decode_image(image):\n    # convert the compressed string to a 3D uint8 tensor\n    image = tf.image.decode_png(image, channels=1)\n    # Normalize image\n    image = tf.image.convert_image_dtype(image, dtype=tf.float32)\n    \n    return image\n\ndef parse_frames(dirname, window=False):\n    # get MRI images file paths for given patient \n    paths = glob.glob(dirname+'/*.png')\n    # Sort the images to get sequential imaging\n    paths = sorted_nicely(paths)\n    \n    if window:\n        # randomly select a window of images to be used as sequence\n        start = tf.random.uniform((1,), maxval=len(paths)-CONFIG['NUM_FRAMES'], dtype=tf.int32)\n\n        paths = tf.slice(paths, start, [CONFIG['NUM_FRAMES']])\n    else:\n        paths = tf.convert_to_tensor(paths)\n        \n\n    def get_frames(path):\n        # Load image\n        image = tf.io.read_file(path)\n        image = decode_image(image)\n        # Resize image\n        image = tf.image.resize(image, (CONFIG['IMG_SIZE'], CONFIG['IMG_SIZE']))\n        \n        return image\n\n    mri_images = tf.map_fn(fn=get_frames, elems=paths, fn_output_signature=tf.float32)\n    \n    return mri_images","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:40:11.343372Z","iopub.execute_input":"2021-08-15T19:40:11.343811Z","iopub.status.idle":"2021-08-15T19:40:11.354043Z","shell.execute_reply.started":"2021-08-15T19:40:11.343773Z","shell.execute_reply":"2021-08-15T19:40:11.352947Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"frames = []\nfor sample_path in tqdm(sample_patient_paths):\n    frames.append(parse_frames(sample_path))","metadata":{"execution":{"iopub.status.busy":"2021-08-15T19:40:12.974651Z","iopub.execute_input":"2021-08-15T19:40:12.975189Z","iopub.status.idle":"2021-08-15T19:40:48.477438Z","shell.execute_reply.started":"2021-08-15T19:40:12.975144Z","shell.execute_reply":"2021-08-15T19:40:48.476077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"run = wandb.init(project='brain-tumor-viz', config=CONFIG, job_type='create-gifs')\n\nos.makedirs('tmp-gifs/', exist_ok=True)\nfor i, frame in tqdm(enumerate(frames)):\n    imageio.mimsave(f'tmp-gifs/out_{i}.gif', (frame*255).numpy().astype('uint8'))    \n\nwandb.log({'examples': [wandb.Image(f'tmp-gifs/out_{i}.gif') \n                        for i in range(len(frames))]})\n    \nrun.finish()","metadata":{"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-07-30T13:33:15.87722Z","iopub.execute_input":"2021-07-30T13:33:15.877631Z","iopub.status.idle":"2021-07-30T13:33:51.45628Z","shell.execute_reply.started":"2021-07-30T13:33:15.877599Z","shell.execute_reply":"2021-07-30T13:33:51.455494Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### [Check out the W&B Run page here $\\rightarrow$](https://wandb.ai/ayush-thakur/brain-tumor-viz/runs/2nrw339f)\n\n![img](https://i.imgur.com/olT8gQK.gif)","metadata":{}},{"cell_type":"markdown","source":"# 🎉🎉 Visualizing dataset interactively with W&B Tables 🎆🎆\n\nW&B Tables let you to log, query, and analyze data interactively. This can help you understand your dataset, visualize model predictions, and share insights in a central dashboard. \n\nThe code cell below logs the entire dataset of this competition as GIFs (every MRI sequence) along with patient id and ground truth. \n\n### Why should you use W&B Tables?\n\n* It is suited for quick EDA. \n\n* It helps understand the data better with few lines of code. Here's a [quick colab notebook](http://wandb.me/tables-quickstart).\n\n* It lets you see the \"actual\" data in it's entirety. With matplotlib based visualization you will have to plot everything in batches and it not very scalable. \n\n* You can filter, sort and group data which can help answer some fundamental questions. \n\n* It is well suited to visualize model predictions and compare models on example level. You can check out this [Kaggle kernel](https://www.kaggle.com/ayuraj/better-data-understanding-with-w-b-tables) to learn more about model prediction visualization. \n\nRead more about Tables [here](https://wandb.ai/wandb/posts/reports/Announcing-W-B-Tables-Iterate-on-Your-Data--Vmlldzo4NTMxNDU).","metadata":{}},{"cell_type":"code","source":"# Initialize a W&B run to log images\nrun = wandb.init(project='brain-tumor-viz', config=CONFIG, name='viz-dataset-tables') # W&B Code 1\n\ndata_at = wandb.Table(columns=['patient_id', 'target', 'FLAIR', 'T1w', 'T1wCE', 'T2w']) # W&B Code 2\n\nfor i in tqdm(range(len(train_df))):\n    os.makedirs('tables-gif', exist_ok=True)\n    \n    row = train_df.loc[i]\n    patient_id = get_patient_id(row.BraTS21ID)\n    c = 0\n    missed_keys = []\n    for j, key in enumerate(label_dict.keys()):\n        if os.path.isdir(f'../input/rsna-miccai-png/train/{patient_id}/{key}'):\n            _frames = parse_frames(f'../input/rsna-miccai-png/train/{patient_id}/{key}')\n            imageio.mimsave(f'tables-gif/out_{patient_id}_{j}.gif', (_frames*255).numpy().astype('uint8'))\n            c += 1\n        else:\n            missed_keys.append(key)\n    \n    if c==4:\n        data_at.add_data(patient_id,                                            \n                         row.MGMT_value,\n                         wandb.Image(f'tables-gif/out_{patient_id}_0.gif'),\n                         wandb.Image(f'tables-gif/out_{patient_id}_1.gif'),\n                         wandb.Image(f'tables-gif/out_{patient_id}_2.gif'),\n                         wandb.Image(f'tables-gif/out_{patient_id}_3.gif')) # W&B Code 3\n    else:\n        print(f'Patient Id: {patient_id} is missing these MRI sequences: {missed_keys}')\n        data_tuple = ()\n        for j, key in enumerate(label_dict.keys()):\n            if key not in missed_keys:\n                data_tuple += (wandb.Image(f'tables-gif/out_{patient_id}_{j}.gif'),)\n            else:\n                data_tuple += (None,)\n        \n        data_at.add_data(patient_id, \n                         row.MGMT_value,\n                         *data_tuple)\n\nwandb.log({'MRI Sequencing Dataset': data_at}) # W&B Code 4\nwandb.finish() # W&B Code 5","metadata":{"execution":{"iopub.status.busy":"2021-07-30T13:35:07.257062Z","iopub.execute_input":"2021-07-30T13:35:07.257999Z","iopub.status.idle":"2021-07-30T14:45:28.112464Z","shell.execute_reply.started":"2021-07-30T13:35:07.257941Z","shell.execute_reply":"2021-07-30T14:45:28.111563Z"},"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualize entire dataset interactively\n\n#### [Check out the table here $\\rightarrow$](http://wandb.me/brain-tumor-tables)\n\n[![Animation7414e590d7929f38.gif](https://s6.gifyu.com/images/Animation7414e590d7929f38.gif)](http://wandb.me/brain-tumor-tables)","metadata":{}},{"cell_type":"markdown","source":"# Connecting Voxel Spaces and Resampling\n\nThis section is based on the amazing work done by [Michael Beregov](https://www.kaggle.com/boojum) in his [kernel](https://www.kaggle.com/boojum/connecting-voxel-spaces).","metadata":{"_kg_hide-input":true}},{"cell_type":"code","source":"TRAIN_PATH = '../input/rsna-miccai-brain-tumor-radiogenomic-classification/train'\nfolders = os.listdir(train_data)\nfolder = folders[0]","metadata":{"execution":{"iopub.status.busy":"2021-08-15T20:34:07.105130Z","iopub.execute_input":"2021-08-15T20:34:07.105469Z","iopub.status.idle":"2021-08-15T20:34:07.110951Z","shell.execute_reply.started":"2021-08-15T20:34:07.105439Z","shell.execute_reply":"2021-08-15T20:34:07.109833Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def read_dicom(file_id, scan_type):\n    filenamesDICOM = reader.GetGDCMSeriesFileNames(f'{TRAIN_PATH}/{file_id}/{scan_type}')\n    reader.SetFileNames(filenamesDICOM)\n    file = reader.Execute()\n    return file\n\ndef resample(image, ref_image):\n    resampler = sitk.ResampleImageFilter()\n    resampler.SetReferenceImage(ref_image)\n    resampler.SetInterpolator(sitk.sitkLinear)\n    \n    resampler.SetTransform(sitk.AffineTransform(image.GetDimension()))\n\n    resampler.SetOutputSpacing(ref_image.GetSpacing())\n\n    resampler.SetSize(ref_image.GetSize())\n\n    resampler.SetOutputDirection(ref_image.GetDirection())\n\n    resampler.SetOutputOrigin(ref_image.GetOrigin())\n\n    resampler.SetDefaultPixelValue(image.GetPixelIDValue())\n\n    resamped_image = resampler.Execute(image)\n    \n    return resamped_image\n\ndef normalize(data):\n    return (data - np.min(data)) / (np.max(data) - np.min(data))","metadata":{"execution":{"iopub.status.busy":"2021-08-15T20:37:37.376307Z","iopub.execute_input":"2021-08-15T20:37:37.376842Z","iopub.status.idle":"2021-08-15T20:37:37.386610Z","shell.execute_reply.started":"2021-08-15T20:37:37.376796Z","shell.execute_reply":"2021-08-15T20:37:37.384709Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"reader = sitk.ImageSeriesReader()\nreader.LoadPrivateTagsOn()\nfilenamesDICOM = reader.GetGDCMSeriesFileNames(f'{train_path}/{folder}/T1w')\nreader.SetFileNames(filenamesDICOM)\nt1_reference = reader.Execute()","metadata":{"execution":{"iopub.status.busy":"2021-08-15T20:10:32.593416Z","iopub.execute_input":"2021-08-15T20:10:32.594016Z","iopub.status.idle":"2021-08-15T20:10:33.510597Z","shell.execute_reply.started":"2021-08-15T20:10:32.593966Z","shell.execute_reply":"2021-08-15T20:10:33.509420Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"reader = sitk.ImageSeriesReader()\nreader.LoadPrivateTagsOn()\n\n# read FLAIR scan\nflair = read_dicom(folder, 'FLAIR')\n\n# read T1wCE scan\nt1wce = read_dicom(folder, 'T1wCE')\n\n# read T2w scan\nt2 = read_dicom(folder, 'T2w')\n\n# resampled\nflair_resampled = resample(flair, t1_reference)\nflair_resampled = normalize(sitk.GetArrayFromImage(flair_resampled))\n\nt1wce_resampled = resample(t1wce, t1_reference)\nt1wce_resampled = normalize(sitk.GetArrayFromImage(t1wce_resampled))\n\nt2_resampled = resample(t2, t1_reference)\nt2_resampled = normalize(sitk.GetArrayFromImage(t2_resampled))\n\nstacked = np.stack([t1wce_resampled, t2_resampled, flair_resampled])\n\nto_rgb = stacked[:,100,:,:].transpose(1,2,0)\nim = Image.fromarray((to_rgb * 255).astype(np.uint8))","metadata":{"execution":{"iopub.status.busy":"2021-08-15T20:48:51.771125Z","iopub.execute_input":"2021-08-15T20:48:51.771543Z","iopub.status.idle":"2021-08-15T20:48:56.794291Z","shell.execute_reply.started":"2021-08-15T20:48:51.771501Z","shell.execute_reply":"2021-08-15T20:48:56.792776Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"run = wandb.init(project='brain-tumor-viz', config=CONFIG)\n\nfor i in range(stacked.shape[1]):\n    to_rgb = stacked[:,i,:,:].transpose(1,2,0)\n    im = Image.fromarray((to_rgb * 255).astype(np.uint8))\n    wandb.log({f\"stacked_{folder}\": [wandb.Image(im)]})\n    \nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2021-08-15T20:56:37.216867Z","iopub.execute_input":"2021-08-15T20:56:37.217221Z","iopub.status.idle":"2021-08-15T20:56:57.122446Z","shell.execute_reply.started":"2021-08-15T20:56:37.217192Z","shell.execute_reply":"2021-08-15T20:56:57.121079Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# WORK IN PROGRESS","metadata":{}}]}