{"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":"<p style='text-align: right;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 0.8em; font-weight: 300;\">Image source: www.freepik.com</span></p>\n\n![](https://i.ibb.co/fXHNQtH/cover1-01.jpg)\n\n<p style='text-align: center;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 2.5em; font-weight: 300;\">RSNA 3D CLAHE Voxels + TPU 3D Augmentations</span></p>\n<br>\n\n<span style=\"color: #1e009c; font-family: Segoe UI; font-size: 1.8em; font-weight: 300;\">Overview</span>\n<br>\n<br>\n&ensp;✔️&ensp;Align, Crop & Filter Voxels<br>\n&ensp;✔️&ensp;Normalization & Contrast Enhancement Across the 3D Volume<br>\n&ensp;✔️&ensp;Resample Voxels to size 64 x 256 x 256 for Optimal Training<br>\n&ensp;✔️&ensp;Efficient 3D Augmentations for TPU & GPU<br>\n&emsp;&emsp;&emsp;✔️&ensp;Flip<br>\n&emsp;&emsp;&emsp;✔️&ensp;Gamma<br>\n&emsp;&emsp;&emsp;✔️&ensp;Brightness<br>\n&emsp;&emsp;&emsp;✔️&ensp;Contrast<br>\n&emsp;&emsp;&emsp;✔️&ensp;Cutout<br>\n&emsp;&emsp;&emsp;✔️&ensp;Random Resized Crop<br>\n&emsp;&emsp;&emsp;✔️&ensp;Rotate<br>\n&emsp;&emsp;&emsp;✔️&ensp;Blur<br>\n&ensp;✔️&ensp;Interactive Visualizations with Weights & Biases<br>\n\n<br>\n\nWe resize and sample the competetion dataset to 64x256x256 (in the current notebook version). This size seems to be an arbitrary size to train on TPUs with a large batch size and to train larger model architectures as well without OOM issues.\n<br>\n<br>\n\nThe augmentations are carefully applied so the transformed image characteristics are uniform throughout the voxel, also preserving the geometrical alignment between slices to minimize discontinuities.\n<br>\n<br>\n\nPrior to augmentations, we align planes as the MRI plane type (Axial, Coronal, and Sagittal) is not consistent among patients or MRI scan types (FLAIR, T1w, T1wCE, T2w) and since it is better to train models using MRI voxels that are consistent in terms of plane type. \n<br>\n<br>\n\nTranspose and rotate operations are used to appropriately align the scans across planes. The scans are then normalized and cropped to remove excess space around the ROI. Some frames with less information are also filtered out. Then, we resize and resample the scans to the size 64 x 256 x 256 and we make sure to keep the plane type consistant (axial) across all the scans.\n<br>\n<br>\n\nContrast enhancement is done across the 3D volume with CLAHE.\n\n<br>\n<br>\n<br>\n\n<p style='text-align: left;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 1.1em; font-weight: 600;\"> 🏷️ TPU 3D-CNN Training Notebook</span></p>\n\n&emsp;&emsp;[[TPU] RSNA Keras 3D CNN Voxel Train 🏷️🏂](https://www.kaggle.com/sreevishnudamodaran/tpu-rsna-keras-3d-cnn-voxel-train)<br>\n\n<p style='text-align: left;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 1.1em; font-weight: 600;\"> 🏷️ Dataset with Processed 64 x 256 x 256 Voxels</span></p>\n\n&emsp;&emsp;[RSNA Processed Voxels 64x256x256](https://www.kaggle.com/sreevishnudamodaran/rsna-processed-voxels-64x256x256)\n\n<p style='text-align: left;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 1.1em; font-weight: 600;\"> 🏷️ Dataset with Processed 64 x 256 x 256 Voxels with CLAHE</span></p>\n\n&emsp;&emsp;[RSNA Processed Voxels 64x256x256 CLAHE](https://www.kaggle.com/sreevishnudamodaran/rsna-processed-voxels-64x256x256-clahe)\n\n<br>\n<span style=\"color: #111111; font-family: Segoe UI; font-size: 1.7em; font-weight: 300;\">References:</span>\n<p style='text-align: left;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 1.1em; font-weight: 600;\"> - Thanks to <a href=\"https://www.kaggle.com/ren4yu\">@ren4yu</a> for the notebook <a href=\"https://www.kaggle.com/ren4yu/normalized-voxels-align-planes-and-crop\">Normalized Voxels: Align Planes and Crop</a>, which details the aligning, normalizing, croping of voxels</span></p>\n\n<p style='text-align: left;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 1.1em; font-weight: 600;\"> - <a href=\"https://github.com/VincentStimper/mclahe\">Multidimensional Contrast Limited Adaptive Histogram Equalization (MCLAHE)</a></span></p>\n\n<p style='text-align: left;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 1.1em; font-weight: 600;\"> - GIF visualizations are adapted from the <a href=\"https://www.kaggle.com/ayuraj/brain-tumor-eda-and-interactive-viz-with-w-b\">Brain Tumor EDA and Interactive Viz with W&B</a> by <a href=\"https://www.kaggle.com/ayuraj\">@ayuraj</a></span></p>\n\n\n<br>\n<br>\n<span style=\"float:center;\"><a href=\"https://www.kaggle.com/sreevishnudamodaran\"><img style=\"padding: 5px;\" border=\"0\" alt=\"Ask Me Something\" src=\"https://img.shields.io/badge/Ask%20me-something-7a43bc.svg?style=for-the-badge&logo=kaggle\" width=\"160\" height=\"20\"></a><br>\n    <img style=\"padding: 5px;\" border=\"0\" alt=\"Ask Me Something\" src=\"https://img.shields.io/badge/Please-Upvote%20If%20you%20like%20this-16a5e9?style=for-the-badge&logo=kaggle\" width=\"250\" height=\"20\"></span>\n<br>","metadata":{}},{"cell_type":"code","source":"!pip install --upgrade https://github.com/VincentStimper/mclahe/archive/numpy.zip -q","metadata":{"execution":{"iopub.status.busy":"2021-09-12T15:06:16.045201Z","iopub.execute_input":"2021-09-12T15:06:16.045582Z","iopub.status.idle":"2021-09-12T15:06:25.502890Z","shell.execute_reply.started":"2021-09-12T15:06:16.045502Z","shell.execute_reply":"2021-09-12T15:06:25.501782Z"},"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport math\nfrom pathlib import Path\nimport glob\nimport math\n\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport pydicom\nimport mclahe as mc\n\nfrom tqdm.notebook import tqdm\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as ticker\nfrom matplotlib import rcParams\nfrom matplotlib.animation import FuncAnimation\nmatplotlib.rcParams['animation.html'] = 'jshtml'\n\nimport tensorflow as tf\nimport tensorflow.keras.backend as K\nimport tensorflow_addons as tfa\nfrom IPython.display import IFrame\nfrom IPython.core.display import display, HTML\nimport imageio","metadata":{"execution":{"iopub.status.busy":"2021-09-12T15:06:25.504627Z","iopub.execute_input":"2021-09-12T15:06:25.504984Z","iopub.status.idle":"2021-09-12T15:06:30.510079Z","shell.execute_reply.started":"2021-09-12T15:06:25.504933Z","shell.execute_reply":"2021-09-12T15:06:30.509012Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #1e009c; font-family: Segoe UI; font-size: 1.8em; font-weight: 300;\">Process & Create Voxels</span>","metadata":{}},{"cell_type":"code","source":"# https://www.kaggle.com/ren4yu/normalized-voxels-align-planes-and-crop\n# https://www.kaggle.com/arnabs007/part-1-rsna-miccai-btrc-understanding-the-data\n# https://www.kaggle.com/davidbroberts/determining-mr-image-planes\ndef get_image_plane(data):\n    x1, y1, _, x2, y2, _ = [round(j) for j in data.ImageOrientationPatient]\n    cords = [x1, y1, x2, y2]\n\n    if cords == [1, 0, 0, 0]:\n        return 'Coronal'\n    elif cords == [1, 0, 0, 1]:\n        return 'Axial'\n    elif cords == [0, 1, 0, 0]:\n        return 'Sagittal'\n    else:\n        return 'Unknown'\n    \ndef get_voxel(study_id, scan_type):\n    imgs = []\n    dcm_dir = data_root.joinpath(DATASET, study_id, scan_type)\n    dcm_paths = sorted(dcm_dir.glob(\"*.dcm\"), key=lambda x: int(x.stem.split(\"-\")[-1]))\n    positions = []\n    \n    for dcm_path in dcm_paths:\n        img = pydicom.dcmread(str(dcm_path))\n        imgs.append(img.pixel_array)\n        positions.append(img.ImagePositionPatient)\n        \n    plane = get_image_plane(img)\n    voxel = np.stack(imgs)\n    \n    # reorder planes if needed and rotate voxel\n    if plane == \"Coronal\":\n        if positions[0][1] < positions[-1][1]:\n            voxel = voxel[::-1]\n            print(f\"{study_id} {scan_type} {plane} reordered\")\n        voxel = voxel.transpose((1, 0, 2))\n    elif plane == \"Sagittal\":\n        if positions[0][0] < positions[-1][0]:\n            voxel = voxel[::-1]\n            print(f\"{study_id} {scan_type} {plane} reordered\")\n        voxel = voxel.transpose((1, 2, 0))\n        voxel = np.rot90(voxel, 2, axes=(1, 2))\n    elif plane == \"Axial\":\n        if positions[0][2] > positions[-1][2]:\n            voxel = voxel[::-1]\n            print(f\"{study_id} {scan_type} {plane} reordered\")\n        voxel = np.rot90(voxel, 2)\n    else:\n        raise ValueError(f\"Unknown plane {plane}\")\n    return voxel, plane\n\ndef normalize_contrast(voxel):\n    if voxel.sum() == 0:\n        return voxel\n    voxel = voxel - np.min(voxel)\n    voxel = voxel / np.max(voxel)\n    voxel = (voxel * 255).astype(np.uint8)\n    return voxel\n\ndef crop_voxel(voxel):\n    if voxel.sum() == 0:\n        return voxel\n    keep = (voxel.mean(axis=(0, 1)) > 0)\n    voxel = voxel[:, :, keep]\n    keep = (voxel.mean(axis=(0, 2)) > 0)\n    voxel = voxel[:, keep, :]\n    keep = (voxel.mean(axis=(1, 2)) > 0)\n    voxel = voxel[keep, :, :]\n    return voxel","metadata":{"execution":{"iopub.status.busy":"2021-09-12T15:06:30.513101Z","iopub.execute_input":"2021-09-12T15:06:30.513469Z","iopub.status.idle":"2021-09-12T15:06:30.529405Z","shell.execute_reply.started":"2021-09-12T15:06:30.513425Z","shell.execute_reply":"2021-09-12T15:06:30.528500Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #111111; font-family: Segoe UI; font-size: 1.7em; font-weight: 300;\">Re-sample & Filter Voxels</span>\n\nSome of the leading and the trailing images from each voxel are removed by applying a filter based on the standard deviation of the voxel mean values.\n\nWe also resample the voxels to the same axial MRI plane orientation throughout the data.\n\nSampling planes is done along the longest axis while resizing to reduce degradation.","metadata":{}},{"cell_type":"code","source":"def filter_voxel(voxel, filter_thr):\n    voxel_mean = voxel.mean(axis=(1, 2))\n    keep = (voxel_mean > voxel_mean.std()*filter_thr)\n    voxel = voxel[keep, :, :]\n    return voxel\n\ndef resize_voxel(voxel, sz=(64, 256, 256)):\n    output = np.zeros((sz[0], sz[1], sz[2]), dtype=np.uint8)\n    if np.argmax(voxel.shape) == 0:\n        for i, s in enumerate(np.linspace(0, voxel.shape[0] - 1, num=sz[0])):\n            sampled = voxel[int(s), :, :]\n            output[i, :, :] = cv2.resize(sampled, (sz[2], sz[1]), cv2.INTER_CUBIC)\n    elif np.argmax(voxel.shape) == 1:\n        for i, s in enumerate(np.linspace(0, voxel.shape[1] - 1, num=sz[1])):\n            sampled = voxel[:, int(s), :]\n            output[:, i, :] = cv2.resize(sampled, (sz[2], sz[0]), cv2.INTER_CUBIC)\n    elif np.argmax(voxel.shape) == 2:\n        for i, s in enumerate(np.linspace(0, voxel.shape[2] - 1, num=sz[2])):\n            sampled = voxel[:, :, int(s)]\n            output[:, :, i] = cv2.resize(sampled, (sz[1], sz[0]), cv2.INTER_CUBIC)\n    return output","metadata":{"execution":{"iopub.status.busy":"2021-09-12T15:06:30.531602Z","iopub.execute_input":"2021-09-12T15:06:30.532406Z","iopub.status.idle":"2021-09-12T15:06:30.545500Z","shell.execute_reply.started":"2021-09-12T15:06:30.532366Z","shell.execute_reply":"2021-09-12T15:06:30.544728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #111111; font-family: Segoe UI; font-size: 1.7em; font-weight: 300;\">3D CLAHE</span>\n\nWe use the Multidimensional Contrast Limited Adaptive Histogram Equalization (MCLAHE) implementation for contrast enhancement across arbitrary number of dimensions, to apply CLAHE across our 3D volume.\n\nRepo: https://github.com/VincentStimper/mclahe\n\nV. Stimper, S. Bauer, R. Ernstorfer, B. Schölkopf and R. P. Xian, \"Multidimensional Contrast Limited Adaptive Histogram Equalization,\" in IEEE Access, vol. 7, pp. 165437-165447, 2019.\n\nWe perform the MCLAHE operation on GPU with TF for a slight performance boost.","metadata":{}},{"cell_type":"code","source":"def clahe_3d(voxel):\n    voxel = mc.mclahe(voxel, kernel_size=[8,32,32],\n              n_bins=128,\n              clip_limit=0.01,\n              adaptive_hist_range=False)\n    return (voxel*255.).astype(np.uint8).clip(0, 255)\n\n@tf.function(input_signature=[tf.TensorSpec(None, tf.uint8)])\ndef tf_clahe_3d(voxel):\n    voxel = tf.numpy_function(clahe_3d, [voxel], tf.uint8)\n    return voxel","metadata":{"execution":{"iopub.status.busy":"2021-09-12T15:06:30.546794Z","iopub.execute_input":"2021-09-12T15:06:30.547132Z","iopub.status.idle":"2021-09-12T15:06:30.556393Z","shell.execute_reply.started":"2021-09-12T15:06:30.547098Z","shell.execute_reply":"2021-09-12T15:06:30.555553Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DATASET = 'train'\nscan_types = ['FLAIR','T1w','T1wCE','T2w']\ndata_root = Path(\"../input/rsna-miccai-brain-tumor-radiogenomic-classification\")\n\nvoxel_dir = '/kaggle/working/voxels'\nos.makedirs(voxel_dir, exist_ok=True)\n\nfilter_thr = 0.4 # voxel_mean > voxel_std_deviation*filter_thr\n\navgs = []\n\nfor study_path in tqdm(list(data_root.joinpath(DATASET).glob(\"*\"))):\n    study_id = study_path.name\n        \n    if study_id in ['00109', '00123', '00709']:\n        continue\n\n    for i, scan_type in enumerate(scan_types):\n        voxel, plane = get_voxel(study_id, scan_type)\n        voxel = normalize_contrast(voxel)\n        voxel = crop_voxel(voxel)\n        voxel = filter_voxel(voxel, filter_thr)\n        voxel = resize_voxel(voxel, sz=(64, 256, 256))\n        \n        voxel = tf_clahe_3d(voxel)\n        \n        os.makedirs(f'{voxel_dir}/{scan_type}', exist_ok=True)\n        \n        with open(f'{voxel_dir}/{scan_type}/{study_id}.npy', 'wb') as f:\n#             np.save(f, voxel)\n            np.save(f, voxel.numpy())","metadata":{"execution":{"iopub.status.busy":"2021-09-12T15:06:30.557621Z","iopub.execute_input":"2021-09-12T15:06:30.558139Z","iopub.status.idle":"2021-09-12T16:40:09.750535Z","shell.execute_reply.started":"2021-09-12T15:06:30.558105Z","shell.execute_reply":"2021-09-12T16:40:09.749709Z"},"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !zip -rq rsna-voxels-64x256x256-aligned-clahe.zip $voxel_dir/*","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-09-12T16:40:09.751867Z","iopub.execute_input":"2021-09-12T16:40:09.752353Z","iopub.status.idle":"2021-09-12T16:40:09.756016Z","shell.execute_reply.started":"2021-09-12T16:40:09.752313Z","shell.execute_reply":"2021-09-12T16:40:09.755199Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# from kaggle_secrets import UserSecretsClient\n# from google.cloud import storage\n\n# def upload_blob(bucket_name, source_file_name, destination_blob_name):\n#     \"\"\"Uploads a file to the bucket. https://cloud.google.com/storage/docs/ \"\"\"\n#     bucket = storage_client.get_bucket(bucket_name)\n#     blob = bucket.blob(destination_blob_name)\n#     blob.upload_from_filename(source_file_name)\n#     print('File {} uploaded to {}.'.format(\n#         source_file_name,\n#         destination_blob_name))\n\n# user_secrets = UserSecretsClient()\n# gcp_project_id = user_secrets.get_secret(\"gcp_project_id\")\n# bucket_name = user_secrets.get_secret(\"bucket_name\")\n# storage_client = storage.Client(project=gcp_project_id)\n# upload_blob(bucket_name, 'rsna-voxels-64x256x256-aligned-clahe.zip', 'rsna-voxels-64x256x256-aligned-clahe.zip')","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-09-12T16:40:09.757951Z","iopub.execute_input":"2021-09-12T16:40:09.758416Z","iopub.status.idle":"2021-09-12T16:40:09.775727Z","shell.execute_reply.started":"2021-09-12T16:40:09.758378Z","shell.execute_reply":"2021-09-12T16:40:09.774809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# !rm -r $voxel_dir ./rsna-voxels-64x256x256-aligned-clahe.zip","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-09-12T16:40:09.777125Z","iopub.execute_input":"2021-09-12T16:40:09.777521Z","iopub.status.idle":"2021-09-12T16:40:09.789660Z","shell.execute_reply.started":"2021-09-12T16:40:09.777484Z","shell.execute_reply":"2021-09-12T16:40:09.788875Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #111111; font-family: Segoe UI; font-size: 1.7em; font-weight: 300;\">Visualize Samples</span>\n\nThe generated datasets are here:\n\n&emsp;[RSNA Processed Voxels 64x256x256](https://www.kaggle.com/sreevishnudamodaran/rsna-processed-voxels-64x256x256)<br>\n\n&emsp;[RSNA Processed Voxels 64x256x256 CLAHE](https://www.kaggle.com/sreevishnudamodaran/rsna-processed-voxels-64x256x256-clahe)\n\nLet's read it directly.","metadata":{}},{"cell_type":"code","source":"def show_animation(images, normalized=True):\n    fig = plt.figure(figsize=(4, 4))\n    plt.axis('off')\n    if normalized:\n        im = plt.imshow(images[0], cmap='gray', vmin=0.0, vmax=1.0)\n    else:\n        im = plt.imshow(images[0], cmap='gray', vmin=0.0, vmax=255.0)\n    def animate(i):\n        im.set_array(images[i])\n        # return the artists set\n        return [im]\n    display(FuncAnimation(fig, animate, frames=len(images),\n                                               interval=20))\n    plt.close()\n\nvoxels = np.load('../input/rsna-processed-voxels-64x256x256-clahe/voxels/FLAIR/00046.npy')\nshow_animation([voxel/255. for voxel in voxels])","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:36:04.880369Z","iopub.execute_input":"2021-09-12T17:36:04.880754Z","iopub.status.idle":"2021-09-12T17:36:06.563851Z","shell.execute_reply.started":"2021-09-12T17:36:04.880714Z","shell.execute_reply":"2021-09-12T17:36:06.562856Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"voxels = np.load('../input/rsna-processed-voxels-64x256x256-clahe/voxels/FLAIR/00046.npy')\n\nsqrt = math.ceil(math.sqrt(voxels.shape[0]))\nfig = plt.figure(figsize=(11, 11))\nfig.suptitle('Axial Plane', x=0.5, y=0.98, size=25)\n\nfor idx in range(voxels.shape[0]):\n    ax = fig.add_subplot(int(sqrt), int(sqrt), idx+1)\n    ax.imshow(voxels[idx, : , :], cmap='gray')\n    ax.axes.xaxis.set_visible(False)\n    ax.axes.yaxis.set_visible(False)\nfig.tight_layout()\nplt.savefig(f'samples.png')\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:37:00.410669Z","iopub.execute_input":"2021-09-12T17:37:00.411278Z","iopub.status.idle":"2021-09-12T17:37:04.622182Z","shell.execute_reply.started":"2021-09-12T17:37:00.411230Z","shell.execute_reply":"2021-09-12T17:37:04.615431Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #1e009c; font-family: Segoe UI; font-size: 1.8em; font-weight: 300;\">Interactive Visualizations with Wandb</span>\n\nLet's use Weights & Biases Log Tables and Logging to interactively visualize our voxels. ","metadata":{}},{"cell_type":"code","source":"!pip install wandb --upgrade -q\n\n## Supress wandb logs since we are not going to train in this notebook\nos.environ[\"WANDB_SILENT\"] = \"true\"\n\nimport wandb\nfrom kaggle_secrets import UserSecretsClient\n\nuser_secrets = UserSecretsClient()\nkey = user_secrets.get_secret(\"wandb_key\")\nwandb.login(key=key)","metadata":{"execution":{"iopub.status.busy":"2021-09-12T16:51:52.883428Z","iopub.execute_input":"2021-09-12T16:51:52.883772Z","iopub.status.idle":"2021-09-12T16:52:03.895191Z","shell.execute_reply.started":"2021-09-12T16:51:52.883740Z","shell.execute_reply":"2021-09-12T16:52:03.894349Z"},"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset = '/kaggle/input/rsna-processed-voxels-64x256x256'\ntrain_voxels = sorted(glob.glob(f\"{dataset}/voxels/*/*.npy\"))\n\ndf_train = pd.DataFrame(train_voxels, columns=['voxel_paths'])\ndf_train['BraTS21ID'] = df_train.voxel_paths.map(lambda path:path.split('/')[-1].strip('.npy'))\ndf_train['scan_type'] = df_train.voxel_paths.map(lambda path:path.split('/')[-2])\n\ndf_train_labels = pd.read_csv('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train_labels.csv',\n                             dtype={'BraTS21ID':np.object,\n                                   'MGMT_value':np.int32})\ndf_train = df_train.set_index('BraTS21ID').join(df_train_labels.set_index('BraTS21ID'), on='BraTS21ID', how='left')\ndf_train = df_train.reset_index()\ndf_train.to_csv(\"/kaggle/working/train_df_meta.csv\")\ndf_train.head(3)","metadata":{"execution":{"iopub.status.busy":"2021-09-12T16:52:03.896855Z","iopub.execute_input":"2021-09-12T16:52:03.897205Z","iopub.status.idle":"2021-09-12T16:52:04.993603Z","shell.execute_reply.started":"2021-09-12T16:52:03.897165Z","shell.execute_reply":"2021-09-12T16:52:04.992630Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"run = wandb.init(project='RSNA-public-viz', job_type='augmentations-viz',\n                name='samples')\nos.makedirs('gifs/', exist_ok=True)\n\nfor i, path in tqdm(enumerate(train_voxels[:16]), total=16):\n    voxel = np.load(path)\n    gifs = np.expand_dims(voxel, axis=-1).astype('uint8')\n    imageio.mimsave(f'gifs/voxel_{i}.gif', gifs)    \n\nwandb.log({'images': [wandb.Image(f'gifs/voxel_{i}.gif') for i in range(16)]})\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2021-09-12T16:52:04.995377Z","iopub.execute_input":"2021-09-12T16:52:04.995747Z","iopub.status.idle":"2021-09-12T16:52:24.958707Z","shell.execute_reply.started":"2021-09-12T16:52:04.995708Z","shell.execute_reply":"2021-09-12T16:52:24.957792Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(HTML(f'<a href={run.get_url()} style=\"font-family: Segoe UI;font-size: 1.5em; font-weight: 300;\">View the Full Dashboard Here 🔎</a>'))\nprint()\ndisplay(HTML(f\"<div style='width: 780px; height: 500px; padding: 0; overflow: hidden;'><iframe src='{run.get_url()}', width=1010, height=650, style='-ms-zoom: 0.75; -moz-transform: scale(0.75); -moz-transform-origin: 0 0; -o-transform: scale(0.75); -o-transform-origin: 0 0; -webkit-transform: scale(0.75); -webkit-transform-origin: 0 0;'></iframe></div>\"))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-09-12T16:56:29.599290Z","iopub.execute_input":"2021-09-12T16:56:29.599648Z","iopub.status.idle":"2021-09-12T16:56:29.613854Z","shell.execute_reply.started":"2021-09-12T16:56:29.599613Z","shell.execute_reply":"2021-09-12T16:56:29.612784Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"run = wandb.init(project='RSNA-public-viz', name='voxels-processed')\nvoxels_table = wandb.Table(columns=['patient_id', 'MGMT_value',\n                                    'FLAIR', 'T1w', 'T1wCE', 'T2w'])\n\nos.makedirs('sample_gifs/', exist_ok=True)\nscan_types = ['FLAIR','T1w','T1wCE','T2w']\n\nfor patient_id in tqdm(df_train.BraTS21ID.unique()[:4]):\n    voxel_paths = df_train.loc[df_train.BraTS21ID==patient_id, 'voxel_paths']\n    label = df_train.loc[df_train.BraTS21ID==patient_id, 'MGMT_value'].values[0]\n    for path in voxel_paths:\n        voxels = np.load(path)\n        scan_type = path.split('/')[-2]\n        gifs = voxels.clip(0, 255).astype('uint8')\n        os.makedirs(f'sample_gifs/{scan_type}', exist_ok=True)\n        imageio.mimsave(f'sample_gifs/{scan_type}/{patient_id}.gif', gifs)\n\n    voxels_table.add_row(patient_id, label,\n             *[wandb.Image(f'sample_gifs/{scan_type}/{patient_id}.gif') for scan_type in scan_types])\n\n\nwandb.log({'voxels_processed_dataset': voxels_table})\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2021-09-12T16:58:24.285786Z","iopub.execute_input":"2021-09-12T16:58:24.286131Z","iopub.status.idle":"2021-09-12T16:58:45.003476Z","shell.execute_reply.started":"2021-09-12T16:58:24.286102Z","shell.execute_reply":"2021-09-12T16:58:45.002453Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<p style='text-align: left;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 1.6em; font-weight: 300;\"><a href=\"https://wandb.ai/sreevishnu-damodaran/RSNA-public-viz/runs/2s0rbc66\">View the Full Dashboard Here 🔎</a></span></p>\n<br>\n\n![](https://i.ibb.co/wz3d8Tz/gif-tab12.gif)","metadata":{}},{"cell_type":"markdown","source":"<span style=\"color: #1e009c; font-family: Segoe UI; font-size: 1.8em; font-weight: 300;\">Read & Use Voxels Directly in Tensorflow</span>\n\nWe can directly use the voxels without tfrecord conversion. We have to use the `tf.io.read_file` and `tf.io.decode_raw` methods in tensorflow to read the 3D voxels as there are no methods to decode '.npy' files specifically in tensorflow yet.\n\nThe final tensor is obtained by removing the header values computed in the `get_npy_header_cnt` function below.","metadata":{}},{"cell_type":"code","source":"dataset = '/kaggle/input/rsna-processed-voxels-64x256x256'\ninput_dims = (64, 256, 256)\nvoxel_dtype = tf.uint8\n\ndef get_npy_header_cnt(sample_voxel, dtype=tf.uint8):\n    img = np.load(sample_voxel)\n    tf_img = tf.io.read_file(sample_voxel)\n    tf_img = tf.io.decode_raw(tf_img, tf.uint8)\n    return tf_img.shape[0]-int(np.prod(img.shape))\n\n# Some sample voxel\nnpy_header_size = get_npy_header_cnt(f'{dataset}/voxels/FLAIR/00830.npy',\n                                     voxel_dtype)","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:01:45.598447Z","iopub.execute_input":"2021-09-12T17:01:45.598785Z","iopub.status.idle":"2021-09-12T17:01:45.792527Z","shell.execute_reply.started":"2021-09-12T17:01:45.598755Z","shell.execute_reply":"2021-09-12T17:01:45.791680Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_voxels = sorted(glob.glob(f\"{dataset}/voxels/*/*.npy\"))\n\ndf_train = pd.DataFrame(train_voxels, columns=['voxel_paths'])\ndf_train['BraTS21ID'] = df_train.voxel_paths.map(lambda path:path.split('/')[-1].strip('.npy'))\ndf_train['scan_type'] = df_train.voxel_paths.map(lambda path:path.split('/')[-2])\n\nfor cnt, path in enumerate(df_train.voxel_paths[:32]):\n    voxel = tf.io.read_file(path)\n    voxel = tf.io.decode_raw(voxel, voxel_dtype)\n    voxel = voxel[npy_header_size:]\n    voxel = tf.cast(voxel, tf.float32)/255.0\n    voxel = tf.reshape(voxel, input_dims)\n    voxel = tf.expand_dims(voxel, axis=-1)\n    if cnt==0:\n        voxels = tf.expand_dims(voxel, axis=0)\n    else:\n        voxels = tf.concat([voxels, tf.expand_dims(voxel, axis=0)], axis=0)","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:01:49.303028Z","iopub.execute_input":"2021-09-12T17:01:49.303360Z","iopub.status.idle":"2021-09-12T17:01:51.395586Z","shell.execute_reply.started":"2021-09-12T17:01:49.303330Z","shell.execute_reply":"2021-09-12T17:01:51.394739Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #1e009c; font-family: Segoe UI; font-size: 1.8em; font-weight: 300;\">TPU Augmentations</span>\n\n- Maximum and minimum values or delta values can be specifed for most of the augmenations below and the parameters for the operation are randomly choosen from this range for each voxel.\n\n- Even though, the parameters are picked randomly, we ensure the same augmentation is applied across all frames in a voxel to preserve the planar alignment and to preserve the image characteristics across frames.\n\n- There is also a provision added to specify the probability for which each augmenation gets applied to the voxels from each batch.\n\n- Since we use the voxel size 64x256x256 in the current version of this notebook, the augmentations that does geometrical transformations are applied with to the Y and the Z axis i.e to the axial MRI slices. This is done so because, the XY and the XZ planes are small (64x256) and they have very less information. For instance, applying the cutout operations to the XY and the XZ planes would drastically reduce the little information by creating holes. The same goes for random crop transformations. It would also not be possible to apply rotations like yaw and pitch as the XY and XZ planes are small compared to the YZ plane (256, 256) so we just apply roll (YZ rotation).\n\n- To perform some of the transformations below, I have used some functions for image processing from `tf.image` and `tfa.image` modules. Some of those functions inherently transform 4D images (3D with channel), as their designated inputs are often image batches. This property is carefully used in some places to apply transformations to the 3D voxel as a whole.\n\n<br>\n\n<span style=\"color: #111111; font-family: Segoe UI; font-size: 1.7em; font-weight: 300;\">Light Augmentations</span>","metadata":{}},{"cell_type":"code","source":"FLIP = 1.0 # @params: probability\nCONTRAST = (0.5, 1.5, 1.0) # @params: (minval, maxval, probability)\nBRIGHTNESS = (0.1, 1.0) # @params: (delta, probability)\nGAMMA = (0.5, 1.5, 1.0) # @params: (minval, maxval, probability)\n\nseed = 53\n\ndef augment(voxel):\n    aug_seed = tf.random.uniform((2,), minval=1, maxval=9999, dtype=tf.int32)\n    if tf.random.uniform(()) < FLIP:\n        if tf.random.uniform(()) < 0.5:\n            voxel = tf.image.flip_up_down(voxel)\n        else:\n            voxel = tf.image.flip_left_right(voxel)\n\n    if tf.random.uniform(()) < BRIGHTNESS[1]:\n        voxel = tf.image.adjust_brightness(voxel, tf.random.uniform((), minval=0.0,\n                                                                maxval=BRIGHTNESS[0],\n                                                                seed=seed))\n    if tf.random.uniform(()) < CONTRAST[2]:\n        voxel = tf.image.adjust_contrast(voxel, tf.random.uniform((), minval=CONTRAST[0],\n                                                              maxval=CONTRAST[1],\n                                                              seed=seed))\n    if tf.random.uniform(()) < GAMMA[2]:\n        voxel = tf.image.adjust_gamma(voxel, tf.random.uniform((), minval=GAMMA[0],\n                                                           maxval=GAMMA[1],\n                                                           seed=seed))\n    voxel = tf.where(tf.math.is_nan(voxel), tf.zeros_like(voxel), voxel)\n    return voxel\n\nos.makedirs('gifs/', exist_ok=True)\nrun = wandb.init(project='RSNA-public-viz', job_type='augmentations-viz',\n                name='light-augmentations')\n\nfor i, voxel in tqdm(enumerate(voxels), total=voxels.shape[0]):\n    gifs = (augment(voxel)*255.).numpy().clip(0, 255).astype('uint8')\n    imageio.mimsave(f'gifs/voxel_{i}.gif', gifs)  \n\nwandb.log({'images': [wandb.Image(f'gifs/voxel_{i}.gif') for i in range(voxels.shape[0])]})\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:02:15.965109Z","iopub.execute_input":"2021-09-12T17:02:15.965450Z","iopub.status.idle":"2021-09-12T17:02:41.634938Z","shell.execute_reply.started":"2021-09-12T17:02:15.965420Z","shell.execute_reply":"2021-09-12T17:02:41.634023Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(HTML(f'<a href={run.get_url()} style=\"font-family: Segoe UI;font-size: 1.5em; font-weight: 300;\">View the Full Dashboard Here 🔎</a>'))\nprint()\ndisplay(HTML(f\"<div style='width: 780px; height: 500px; padding: 0; overflow: hidden;'><iframe src='{run.get_url()}', width=1010, height=650, style='-ms-zoom: 0.75; -moz-transform: scale(0.75); -moz-transform-origin: 0 0; -o-transform: scale(0.75); -o-transform-origin: 0 0; -webkit-transform: scale(0.75); -webkit-transform-origin: 0 0;'></iframe></div>\"))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-09-12T17:06:10.264107Z","iopub.execute_input":"2021-09-12T17:06:10.264451Z","iopub.status.idle":"2021-09-12T17:06:10.279100Z","shell.execute_reply.started":"2021-09-12T17:06:10.264416Z","shell.execute_reply":"2021-09-12T17:06:10.277745Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #111111; font-family: Segoe UI; font-size: 1.7em; font-weight: 300;\">Random Cutout</span>","metadata":{}},{"cell_type":"code","source":"CUTOUT = ((10, 10), 20, 1.0) # @params: ((mask_dim0, mask_dim1), max_num_holes, probability)\n\ndef random_cutout3D(voxel, mask_shape=(10, 10), num_holes=20, p=0.5):\n    if tf.random.uniform(()) < p:\n        voxel_shape = voxel.shape\n        assert voxel_shape[1] >= mask_shape[0]\n        assert voxel_shape[2] >= mask_shape[1]\n\n        holes = tf.random.uniform((), minval=1, maxval=num_holes,\n                                  dtype=tf.int32)\n        mask_size = tf.constant([mask_shape[0], mask_shape[1]])\n        mask = tf.Variable((lambda : tf.ones(voxel_shape)),\n                           trainable=False)\n        \n        for i in tf.range(holes):\n            x = tf.random.uniform((), minval=0, maxval=voxel_shape[2],\n                                      dtype=tf.int32)\n            y = tf.random.uniform((), minval=0, maxval=voxel_shape[1],\n                                      dtype=tf.int32)\n            mask_endx = tf.add(x, mask_size[1])\n            mask_endy = tf.add(y, mask_size[0])\n            mask[:, x:mask_endx,\n                 y:mask_endy, :].assign(tf.zeros_like(mask[:, x:mask_endx,\n                                                        y:mask_endy, :]))\n        voxel = tf.multiply(voxel, mask)\n        mask.assign(tf.ones(voxel_shape))\n    return voxel\n\ndef augment(voxel):\n    voxel = random_cutout3D(voxel, mask_shape=CUTOUT[0],\n                          num_holes=CUTOUT[1], p=CUTOUT[2])\n    voxel = tf.where(tf.math.is_nan(voxel), tf.zeros_like(voxel), voxel)\n    return voxel\n\nrun = wandb.init(project='RSNA-public-viz', job_type='augmentations-viz',\n                name='cutout-augmentations')\nos.makedirs('gifs/', exist_ok=True)\n\nfor i, voxel in tqdm(enumerate(voxels), total=voxels.shape[0]):\n    gifs = (augment(voxel)*255.).numpy().clip(0, 255).astype('uint8')\n    imageio.mimsave(f'gifs/voxel_{i}.gif', gifs)    \n\nwandb.log({'images': [wandb.Image(f'gifs/voxel_{i}.gif') for i in range(voxels.shape[0])]})\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:06:15.879513Z","iopub.execute_input":"2021-09-12T17:06:15.879955Z","iopub.status.idle":"2021-09-12T17:06:41.319231Z","shell.execute_reply.started":"2021-09-12T17:06:15.879916Z","shell.execute_reply":"2021-09-12T17:06:41.318266Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(HTML(f'<a href={run.get_url()} style=\"font-family: Segoe UI;font-size: 1.5em; font-weight: 300;\">View the Full Dashboard Here 🔎</a>'))\nprint()\ndisplay(HTML(f\"<div style='width: 780px; height: 500px; padding: 0; overflow: hidden;'><iframe src='{run.get_url()}', width=1010, height=650, style='-ms-zoom: 0.75; -moz-transform: scale(0.75); -moz-transform-origin: 0 0; -o-transform: scale(0.75); -o-transform-origin: 0 0; -webkit-transform: scale(0.75); -webkit-transform-origin: 0 0;'></iframe></div>\"))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-09-12T17:09:03.297532Z","iopub.execute_input":"2021-09-12T17:09:03.297915Z","iopub.status.idle":"2021-09-12T17:09:03.311836Z","shell.execute_reply.started":"2021-09-12T17:09:03.297873Z","shell.execute_reply":"2021-09-12T17:09:03.310806Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #111111; font-family: Segoe UI; font-size: 1.7em; font-weight: 300;\">Random Rotate</span>","metadata":{}},{"cell_type":"code","source":"ROTATE = (20, 1.0) # @params: (maxangle, probability)\n\ndef random_rotate3D(voxel, limit=90, p=0.5):\n    if tf.random.uniform(()) < p:\n        angle = tf.random.uniform((), minval=-limit, maxval=limit, dtype=tf.int32)\n        voxel = tfa.image.rotate(voxel, tf.cast(angle, tf.float32)*(math.pi/180),\n                               interpolation='nearest', fill_mode='constant',\n                                 fill_value=0.0)\n    return voxel\n\ndef augment(voxel):\n    voxel = random_rotate3D(voxel, limit=ROTATE[0], p=ROTATE[1])\n    voxel = tf.where(tf.math.is_nan(voxel), tf.zeros_like(voxel), voxel)\n    return voxel\n\nrun = wandb.init(project='RSNA-public-viz', job_type='augmentations-viz',\n                name='rotate-augmentations')\nos.makedirs('gifs/', exist_ok=True)\n\nfor i, voxel in tqdm(enumerate(voxels), total=voxels.shape[0]):\n    gifs = (augment(voxel)*255.).numpy().clip(0, 255).astype('uint8')\n    imageio.mimsave(f'gifs/voxel_{i}.gif', gifs)    \n\nwandb.log({'images': [wandb.Image(f'gifs/voxel_{i}.gif') for i in range(voxels.shape[0])]})\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:09:40.666671Z","iopub.execute_input":"2021-09-12T17:09:40.667171Z","iopub.status.idle":"2021-09-12T17:10:05.445320Z","shell.execute_reply.started":"2021-09-12T17:09:40.667126Z","shell.execute_reply":"2021-09-12T17:10:05.444399Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(HTML(f'<a href={run.get_url()} style=\"font-family: Segoe UI;font-size: 1.5em; font-weight: 300;\">View the Full Dashboard Here 🔎</a>'))\nprint()\ndisplay(HTML(f\"<div style='width: 780px; height: 500px; padding: 0; overflow: hidden;'><iframe src='{run.get_url()}', width=1010, height=650, style='-ms-zoom: 0.75; -moz-transform: scale(0.75); -moz-transform-origin: 0 0; -o-transform: scale(0.75); -o-transform-origin: 0 0; -webkit-transform: scale(0.75); -webkit-transform-origin: 0 0;'></iframe></div>\"))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-09-12T17:12:22.998234Z","iopub.execute_input":"2021-09-12T17:12:22.998577Z","iopub.status.idle":"2021-09-12T17:12:23.013911Z","shell.execute_reply.started":"2021-09-12T17:12:22.998544Z","shell.execute_reply":"2021-09-12T17:12:23.012787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #111111; font-family: Segoe UI; font-size: 1.7em; font-weight: 300;\">Random Resized Crop</span>","metadata":{}},{"cell_type":"code","source":"RANDOM_CROP = (180, 180, 1.0) # @params: (min_width, min_height, probability)\n\ndef random_resized_crop3D(voxel, min_width, min_height, p=0.5):\n    if tf.random.uniform(()) < p:\n        voxel_shape = voxel.shape\n        assert voxel_shape[1] >= min_height\n        assert voxel_shape[2] >= min_width\n        \n        width = tf.random.uniform((), minval=min_width, maxval=voxel_shape[2],\n                                  dtype=tf.int32)\n        height = tf.random.uniform((), minval=min_height, maxval=voxel_shape[1],\n                                   dtype=tf.int32)\n        x = tf.random.uniform((), minval=0, maxval=voxel_shape[2] - width,\n                                  dtype=tf.int32)\n        y = tf.random.uniform((), minval=0, maxval=voxel_shape[1] - height,\n                                  dtype=tf.int32)\n        voxel = voxel[:, y:y+height, x:x+width, :]\n        voxel = tf.image.resize(voxel, voxel_shape[1:3], method='lanczos5')\n    return voxel\n\ndef augment(voxel):\n    voxel = random_resized_crop3D(voxel, RANDOM_CROP[0], RANDOM_CROP[1],\n                              p=RANDOM_CROP[2])\n    voxel = tf.where(tf.math.is_nan(voxel), tf.zeros_like(voxel), voxel)\n    return voxel\n\n\nrun = wandb.init(project='RSNA-public-viz', job_type='augmentations-viz',\n                name='random-crop-augmentations')\nos.makedirs('gifs/', exist_ok=True)\n\nfor i, voxel in tqdm(enumerate(voxels), total=voxels.shape[0]):\n    gifs = (augment(voxel)*255.).numpy().clip(0, 255).astype('uint8')\n    imageio.mimsave(f'gifs/voxel_{i}.gif', gifs)    \n\nwandb.log({'images': [wandb.Image(f'gifs/voxel_{i}.gif') for i in range(voxels.shape[0])]})\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:15:21.936206Z","iopub.execute_input":"2021-09-12T17:15:21.936538Z","iopub.status.idle":"2021-09-12T17:15:51.555677Z","shell.execute_reply.started":"2021-09-12T17:15:21.936507Z","shell.execute_reply":"2021-09-12T17:15:51.554761Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(HTML(f'<a href={run.get_url()} style=\"font-family: Segoe UI;font-size: 1.5em; font-weight: 300;\">View the Full Dashboard Here 🔎</a>'))\nprint()\ndisplay(HTML(f\"<div style='width: 780px; height: 500px; padding: 0; overflow: hidden;'><iframe src='{run.get_url()}', width=1010, height=650, style='-ms-zoom: 0.75; -moz-transform: scale(0.75); -moz-transform-origin: 0 0; -o-transform: scale(0.75); -o-transform-origin: 0 0; -webkit-transform: scale(0.75); -webkit-transform-origin: 0 0;'></iframe></div>\"))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-09-12T17:18:02.608961Z","iopub.execute_input":"2021-09-12T17:18:02.609325Z","iopub.status.idle":"2021-09-12T17:18:02.624604Z","shell.execute_reply.started":"2021-09-12T17:18:02.609287Z","shell.execute_reply":"2021-09-12T17:18:02.623469Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #111111; font-family: Segoe UI; font-size: 1.7em; font-weight: 300;\">Random Blur</span>","metadata":{}},{"cell_type":"code","source":"from tensorflow_addons.image import utils as img_utils\n\nBLUR = ([3, 3], 50, 1.0) # @params: ((filter_dim0, filter_dim1), max_sigma, probability)\n\ndef _get_gaussian_kernel(sigma, filter_shape):\n    \"\"\"Compute 1D Gaussian kernel.\"\"\"\n    x = tf.range(-filter_shape // 2 + 1, filter_shape // 2 + 1)\n    x = tf.cast(x ** 2, sigma.dtype)\n    x = tf.nn.softmax(-x / (2.0 * (sigma ** 2)))\n    return x\n\ndef random_gaussian_blur3D(voxel, filter_shape=[5, 5], max_sigma=3, p=0.5):\n    if tf.random.uniform(()) < p:\n        sigma = tf.random.uniform((), minval=3, maxval=max_sigma,\n                          dtype=tf.int32)\n        filter_shape = tf.constant(filter_shape)\n        channels = voxel.shape[-1]\n        sigma = tf.cast(sigma, voxel.dtype)\n        gaussian_kernel_x = _get_gaussian_kernel(sigma, filter_shape[1])\n        gaussian_kernel_x = gaussian_kernel_x[tf.newaxis, :]\n        gaussian_kernel_y = _get_gaussian_kernel(sigma, filter_shape[0])        \n        gaussian_kernel_y = gaussian_kernel_y[:, tf.newaxis]\n        gaussian_kernel_2d = tf.matmul(gaussian_kernel_y, gaussian_kernel_x)\n        gaussian_kernel_2d = gaussian_kernel_2d[:, :, tf.newaxis, tf.newaxis]\n        gaussian_kernel_2d = tf.tile(gaussian_kernel_2d,\n                                     tf.constant([1, 1, channels, 1]))\n        voxel = tf.nn.depthwise_conv2d(input=voxel,\n                                       filter=gaussian_kernel_2d,\n                                       strides=(1, 1, 1, 1),\n                                       padding=\"SAME\",\n                                       )\n        voxel = tf.cast(voxel, voxel.dtype)\n    return voxel\n\ndef augment(voxel):\n    voxel = random_gaussian_blur3D(voxel, filter_shape=BLUR[0],\n                                   max_sigma=BLUR[1], p=BLUR[2])\n    voxel = tf.where(tf.math.is_nan(voxel),\n                     tf.zeros_like(voxel), voxel)\n    return voxel\n\nrun = wandb.init(project='RSNA-public-viz', job_type='augmentations-viz',\n                name='blur-augmentations')\nos.makedirs('gifs/', exist_ok=True)\n\nfor i, voxel in tqdm(enumerate(voxels), total=voxels.shape[0]):\n    gifs = (augment(voxel)*255.).numpy().clip(0, 255).astype('uint8')\n    imageio.mimsave(f'gifs/voxel_{i}.gif', gifs)    \n\nwandb.log({'images': [wandb.Image(f'gifs/voxel_{i}.gif') for i in range(voxels.shape[0])]})\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:18:08.358136Z","iopub.execute_input":"2021-09-12T17:18:08.358481Z","iopub.status.idle":"2021-09-12T17:18:39.748439Z","shell.execute_reply.started":"2021-09-12T17:18:08.358448Z","shell.execute_reply":"2021-09-12T17:18:39.747381Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(HTML(f'<a href={run.get_url()} style=\"font-family: Segoe UI;font-size: 1.5em; font-weight: 300;\">View the Full Dashboard Here 🔎</a>'))\nprint()\ndisplay(HTML(f\"<div style='width: 780px; height: 500px; padding: 0; overflow: hidden;'><iframe src='{run.get_url()}', width=1010, height=650, style='-ms-zoom: 0.75; -moz-transform: scale(0.75); -moz-transform-origin: 0 0; -o-transform: scale(0.75); -o-transform-origin: 0 0; -webkit-transform: scale(0.75); -webkit-transform-origin: 0 0;'></iframe></div>\"))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-09-12T17:20:06.408568Z","iopub.execute_input":"2021-09-12T17:20:06.408938Z","iopub.status.idle":"2021-09-12T17:20:06.424232Z","shell.execute_reply.started":"2021-09-12T17:20:06.408905Z","shell.execute_reply":"2021-09-12T17:20:06.422949Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<span style=\"color: #1e009c; font-family: Segoe UI; font-size: 1.8em; font-weight: 300;\">🛰️ Putting It All Together</span>","metadata":{}},{"cell_type":"code","source":"seed = 42\ndataset = '/kaggle/input/rsna-processed-voxels-64x256x256-clahe'\ninput_dims = (64, 256, 256)\nvoxel_dtype = tf.uint8\n\nFLIP = 0.5 # @params: probability\nCONTRAST = (0.7, 1.3, 0.4) # @params: (minval, maxval, probability)\nBRIGHTNESS = (0.2, 0.4) # @params: (delta, probability)\nGAMMA = (0.8, 1.2, 0.4) # @params: (minval, maxval, probability)\nROTATE = (20, 0.3) # @params: (maxangle, probability)\nRANDOM_CROP = (180, 180, 0.3) # @params: (min_width, min_height, probability)\nCUTOUT = ((12, 12), 15, 0.3) # @params: ((mask_dim0, mask_dim1), max_num_holes, probability)\nBLUR = ([3, 3], 50, 0.4) # @params: ((filter_dim0, filter_dim1), max_sigma, probability)\n\ndef build_decoder(with_labels=True, target_size=(64, 256, 256), ext='npy'):\n    def decode(path):\n        if ext == 'npy':\n            voxel = tf.io.read_file(path)\n            voxel = tf.io.decode_raw(voxel, voxel_dtype)\n            voxel = voxel[npy_header_size:]\n        else:\n            raise ValueError(\"voxel extension not supported\")\n        \n        voxel = tf.cast(voxel, tf.float32)/255.0\n        voxel = tf.reshape(voxel, target_size)\n        voxel = tf.expand_dims(voxel, axis=-1)\n\n        return voxel\n    \n    def decode_with_labels(path, label):\n        return decode(path), label\n    \n    return decode_with_labels if with_labels else decode\n\ndef build_augmenter(with_labels=True):\n    '''\n    Performing stateless contrast and brightness tranformations with the same seed\n    to ensure the same tranformation is applied to every voxel slice.\n    ''' \n    def augment(voxel):\n        aug_seed = tf.random.uniform((2,), minval=1, maxval=9999, dtype=tf.int32)\n        if tf.random.uniform(()) < FLIP:\n            if tf.random.uniform(()) < 0.5:\n                voxel = tf.image.flip_up_down(voxel)\n            else:\n                voxel = tf.image.flip_left_right(voxel)\n                \n        if tf.random.uniform(()) < BRIGHTNESS[1]:\n            voxel = tf.image.adjust_brightness(voxel, tf.random.uniform((), minval=0.0,\n                                                                    maxval=BRIGHTNESS[0],\n                                                                    seed=seed))\n        if tf.random.uniform(()) < CONTRAST[2]:\n            voxel = tf.image.adjust_contrast(voxel, tf.random.uniform((), minval=CONTRAST[0],\n                                                                  maxval=CONTRAST[1],\n                                                                  seed=seed))\n        if tf.random.uniform(()) < GAMMA[2]:\n            voxel = tf.image.adjust_gamma(voxel, tf.random.uniform((), minval=GAMMA[0],\n                                                               maxval=GAMMA[1],\n                                                               seed=seed))\n        voxel = random_rotate3D(voxel, limit=ROTATE[0], p=ROTATE[1])\n        voxel = random_resized_crop3D(voxel, RANDOM_CROP[0], RANDOM_CROP[1],\n                                  p=RANDOM_CROP[2])\n        voxel = random_gaussian_blur3D(voxel, filter_shape=BLUR[0],\n                                       max_sigma=BLUR[1], p=BLUR[2])\n        voxel = random_cutout3D(voxel, mask_shape=CUTOUT[0],\n                          num_holes=CUTOUT[1], p=CUTOUT[2])\n        \n        voxel = tf.where(tf.math.is_nan(voxel), tf.zeros_like(voxel), voxel)\n        voxel = tf.cast(voxel, tf.float32)\n        return voxel\n    \n    def augment_with_labels(voxel, label):\n        return augment(voxel), label\n    \n    return augment_with_labels if with_labels else augment\n\ndef build_dataset(paths, labels=None, bsize=128, cache=True,\n                  decode_fn=None, augment_fn=None,\n                  augment=True, repeat=True, shuffle=1024,\n                  seed=None, cache_dir=\"\"):\n    \n    if cache_dir != \"\" and cache is True:\n        os.makedirs(cache_dir, exist_ok=True)\n    \n    if decode_fn is None:\n        decode_fn = build_decoder(labels is not None)\n    \n    if augment_fn is None:\n        augment_fn = build_augmenter(labels is not None)\n    \n    AUTO = tf.data.experimental.AUTOTUNE\n    slices = paths if labels is None else (paths, labels)\n    dset = tf.data.Dataset.from_tensor_slices(slices)\n    dset = dset.map(decode_fn, num_parallel_calls=AUTO)\n    dset = dset.cache(cache_dir) if cache else ds\n    \n    ## Map the functions to perform Augmentations\n    dset = dset.map(augment_fn, num_parallel_calls=AUTO) if augment else dset\n    dset = dset.repeat() if repeat else dset\n    dset = dset.shuffle(shuffle, seed=seed) if shuffle else dset\n    dset = dset.batch(bsize, drop_remainder=True).prefetch(AUTO)\n    return dset","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:21:38.064941Z","iopub.execute_input":"2021-09-12T17:21:38.065264Z","iopub.status.idle":"2021-09-12T17:21:38.088735Z","shell.execute_reply.started":"2021-09-12T17:21:38.065235Z","shell.execute_reply":"2021-09-12T17:21:38.087786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_cols = 'MGMT_value'\ntrain_paths = df_train.voxel_paths.values\ndecoder = build_decoder(with_labels=False, target_size=input_dims, ext='npy')\n\ntrain_dataset = build_dataset(\n    train_paths, bsize=32, decode_fn=decoder,\n    augment=True, repeat=False, shuffle=True\n)\n\ntrain_voxels = next(iter(train_dataset))\n\nrun = wandb.init(project='RSNA-public-viz', job_type='augmentations-viz',\n                name='all-augmentations')\nos.makedirs('gifs/', exist_ok=True)\n\nfor i, voxel in tqdm(enumerate(train_voxels), total=train_voxels.shape[0]):\n    gifs = (voxel*255.).numpy().clip(0, 255).astype('uint8')\n    imageio.mimsave(f'gifs/voxel_{i}.gif', gifs)    \n\nwandb.log({'images': [wandb.Image(f'gifs/voxel_{i}.gif') for i in range(voxels.shape[0])]})\nrun.finish()","metadata":{"execution":{"iopub.status.busy":"2021-09-12T17:28:45.266299Z","iopub.execute_input":"2021-09-12T17:28:45.266636Z","iopub.status.idle":"2021-09-12T17:29:24.251106Z","shell.execute_reply.started":"2021-09-12T17:28:45.266605Z","shell.execute_reply":"2021-09-12T17:29:24.249917Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"display(HTML(f'<a href={run.get_url()} style=\"font-family: Segoe UI;font-size: 1.5em; font-weight: 300;\">View the Full Dashboard Here 🔎</a>'))\nprint()\ndisplay(HTML(f\"<div style='width: 780px; height: 500px; padding: 0; overflow: hidden;'><iframe src='{run.get_url()}', width=1010, height=650, style='-ms-zoom: 0.75; -moz-transform: scale(0.75); -moz-transform-origin: 0 0; -o-transform: scale(0.75); -o-transform-origin: 0 0; -webkit-transform: scale(0.75); -webkit-transform-origin: 0 0;'></iframe></div>\"))","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-09-12T17:32:19.637720Z","iopub.execute_input":"2021-09-12T17:32:19.638081Z","iopub.status.idle":"2021-09-12T17:32:19.651421Z","shell.execute_reply.started":"2021-09-12T17:32:19.638048Z","shell.execute_reply":"2021-09-12T17:32:19.650313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Training Notebook:\n\n[[TPU] RSNA Keras 3D CNN Voxel Train 🏷️🏂](https://www.kaggle.com/sreevishnudamodaran/tpu-rsna-keras-3d-cnn-voxel-train)<br>","metadata":{}},{"cell_type":"code","source":"!find ./wandb -name \"*.gif\" -type f -delete\n!find ./sample_gifs -name \"*.gif\" -type f -delete\n!rm -r ./voxels","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2021-09-12T17:37:51.435723Z","iopub.execute_input":"2021-09-12T17:37:51.436085Z","iopub.status.idle":"2021-09-12T17:37:53.772710Z","shell.execute_reply.started":"2021-09-12T17:37:51.436053Z","shell.execute_reply":"2021-09-12T17:37:53.771546Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<p style='text-align: center;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 2.0em; font-weight: 300; letter-spacing:3px\">HAVE A GREAT DAY !</span></p>\n\n<p style='text-align: center;'><span style=\"color: #000508; font-family: Segoe UI; font-size: 1.2em; font-weight: 300;\">Let me know if you have any suggestions!</span></p>","metadata":{}}]}