{"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":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport cv2\nimport glob\nimport matplotlib.pyplot as plt\nimport numpy as np # linear algebra\nfrom operator import itemgetter\nimport os\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-08-10T21:37:10.065748Z","iopub.execute_input":"2021-08-10T21:37:10.066239Z","iopub.status.idle":"2021-08-10T21:37:10.072143Z","shell.execute_reply.started":"2021-08-10T21:37:10.066199Z","shell.execute_reply":"2021-08-10T21:37:10.071122Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_path = '/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification'","metadata":{"execution":{"iopub.status.busy":"2021-08-10T21:35:59.518645Z","iopub.execute_input":"2021-08-10T21:35:59.519154Z","iopub.status.idle":"2021-08-10T21:35:59.522634Z","shell.execute_reply.started":"2021-08-10T21:35:59.519123Z","shell.execute_reply":"2021-08-10T21:35:59.521816Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This is a simple notebook showing how to load the full Brain in PyTorch.  This means, reading in all of the images of a particular modality for a particular patient, putting them in order, and producing a single 3D tensor to be fed into a PyTorch model.  In order for this to work, all images of a particular modality for a particular patient must have the same shape.  This is the case for this dataset, but you may want to investigate it for yourself.\n\nSeveral of these helper functions came from or were inspired by this notebook: https://www.kaggle.com/furcifer/no-baseline-pytorch-cnn-for-mri?scriptVersionId=68186710\n","metadata":{}},{"cell_type":"code","source":"def _dicom2array(path, voi_lut=True, fix_monochrome=True, resize=False):\n    dicom = pydicom.read_file(path)\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n    #Normalize the data: subtract off the minimum, divide by the maximum, convert to 256 uint8\n    data = data - np.min(data)\n    data = data/np.max(data)\n    data = (data * 255).astype(np.uint8)\n    \n    #Resize images to target value\n    if resize:\n        data = cv2.resize(data, (256, 256))\n    return data","metadata":{"execution":{"iopub.status.busy":"2021-08-10T21:32:43.778891Z","iopub.execute_input":"2021-08-10T21:32:43.77926Z","iopub.status.idle":"2021-08-10T21:32:43.787031Z","shell.execute_reply.started":"2021-08-10T21:32:43.779231Z","shell.execute_reply":"2021-08-10T21:32:43.785981Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"If one looks at the raw images, one sees lots of blank space around the actually brain, which is not useful for classification.  The following function finds the edges of the brain and eliminates the surrounding blank space.  Note: this is circumscribing it, not eliminating *all* the blank space, there is still blank space in the corners because PyTorch needs a cuboid input.","metadata":{}},{"cell_type":"code","source":"def _circumscriber(img: np.array) -> np.array:\n    #First is vertical, second is horizontal, third is slices\n    vmin = 0\n    vlimit = img.shape[0]\n    hmin = 0\n    hlimit = img.shape[1]\n    \n    for i in range(vlimit):\n        if np.max(img[i, :, :]) == 0:\n            vmin += 1\n        else:\n            break\n    vmax = vmin + 1\n    for i in range(vmin+1, vlimit):\n        if np.max(img[i, :, :]) > 0:\n            vmax += 1\n        else:\n            break\n    \n    for j in range(hlimit):\n        if np.max(img[:, j, :]) == 0:\n            hmin += 1\n        else:\n            break\n    hmax = hmin + 1\n    for j in range(hmin+1, hlimit):\n        if np.max(img[:, j, :]) > 0:\n            hmax += 1\n        else:\n            break\n    return img[vmin:vmax, hmin:hmax, :]","metadata":{"execution":{"iopub.status.busy":"2021-08-10T21:32:52.783706Z","iopub.execute_input":"2021-08-10T21:32:52.784052Z","iopub.status.idle":"2021-08-10T21:32:52.793141Z","shell.execute_reply.started":"2021-08-10T21:32:52.784023Z","shell.execute_reply":"2021-08-10T21:32:52.791973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_FULL_brain(scan_id, split = 'train', modality='FLAIR'):\n    \"\"\"\n    send all of the images in the chosen modality, in order, as a single 3D np array\n    \"\"\"\n    if split != \"train\" and split != \"test\":\n        print('Please request a valid split: train or test.  Defaulting to train.')\n        split = \"train\"\n        \n    if modality != 'FLAIR' and modality != 'T1w' and modality != 'T1wCE' and modality != 'T2w':\n        print('Please select an appropriate modality: FLAIR, T1w, T1wCE, or T2w')\n        print('Defaulting to FLAIR')\n        modality = 'FLAIR'\n        \n    image = sorted(glob.glob(f'{train_path}/{split}/{scan_id}/{modality}/*.dcm'))\n    image_slice_locs = [pydicom.dcmread(im)[('0020', '1041')].value for im in image]\n    image_pairs = list(zip(image, image_slice_locs))\n    ordered_IP = sorted(image_pairs, key=itemgetter(1))\n    images = [f[0] for f in ordered_IP]\n    real_images = [_dicom2array(f) for f in images]\n    good_images = np.array([im for im in real_images if np.max(im) > 0]).T\n    final_image = _circumscriber(good_images)\n    return final_image","metadata":{"execution":{"iopub.status.busy":"2021-08-10T21:33:05.464602Z","iopub.execute_input":"2021-08-10T21:33:05.465012Z","iopub.status.idle":"2021-08-10T21:33:05.475237Z","shell.execute_reply.started":"2021-08-10T21:33:05.464978Z","shell.execute_reply":"2021-08-10T21:33:05.474049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_imgs(imgs, cols=4, size=7, is_rgb=True, title=\"\", cmap='gray', img_size=(512,512)):\n    rows = len(imgs)//cols + 1\n    fig = plt.figure(figsize=(cols*size, rows*size))\n    for i in range(4):\n        img = imgs[:,:,i]\n        fig.add_subplot(rows, cols, i+1)\n        plt.imshow(img, cmap=cmap)\n    plt.suptitle(title)\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2021-08-10T21:34:08.150269Z","iopub.execute_input":"2021-08-10T21:34:08.15072Z","iopub.status.idle":"2021-08-10T21:34:08.157526Z","shell.execute_reply.started":"2021-08-10T21:34:08.150684Z","shell.execute_reply":"2021-08-10T21:34:08.156707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"boxtest = load_FULL_brain('00000', split='train', modality='FLAIR')","metadata":{"execution":{"iopub.status.busy":"2021-08-10T21:36:10.469899Z","iopub.execute_input":"2021-08-10T21:36:10.470522Z","iopub.status.idle":"2021-08-10T21:36:17.724708Z","shell.execute_reply.started":"2021-08-10T21:36:10.470485Z","shell.execute_reply":"2021-08-10T21:36:17.723772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's have a look at four slices from the middle of patient 00000's FLAIR brain image.","metadata":{}},{"cell_type":"code","source":"plot_imgs(boxtest[:,:,100:104])","metadata":{"execution":{"iopub.status.busy":"2021-08-10T21:37:22.200269Z","iopub.execute_input":"2021-08-10T21:37:22.200773Z","iopub.status.idle":"2021-08-10T21:37:23.126761Z","shell.execute_reply.started":"2021-08-10T21:37:22.200736Z","shell.execute_reply":"2021-08-10T21:37:23.125764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class BrainLoader(Dataset):\n    def __init__(self, label_file, path, split, modality, val_split=0.25):\n        train_data = pd.read_csv(os.path.join(path, label_file))\n        self.labels = {}\n        self.path = path\n        brats = list(train_data['BraTS21ID'])\n        mgmt = list(train_data['MGMT_value'])\n        for b, m in zip(brats, mgmt):\n            self.labels[str(b).zfill(5)] = m\n            \n        self.split = split\n        self.modality = modality\n        \n        self.ids = [a.split('/')[-1] for a in sorted(glob.glob(path + f'/{split}/*'))]\n        stop = int(len(self.ids) * (1 - val_split))\n        if split == 'train':\n            self.ids = self.ids[:stop]\n        elif split == 'val':\n            self.ids = self.ids[stop:]\n            \n    def __len__(self):\n        return len(self.ids)\n    \n    def __getitem__(self, idx):\n        p_id = self.ids[idx]\n        imgs = load_FULL_brain(p_id, split=self.split, modality=self.modality)\n        transform = transforms.Compose([transforms.ToTensor()])\n        imgs = transform(imgs)\n        \n        if self.split != 'test':\n            label = torch.tensor(self.labels[p_id], dtype=torch.long)\n            return torch.tensor(imgs, dtype=torch.float32), label\n        return torch.tensor(imgs, dtype=torch.float32)","metadata":{"execution":{"iopub.status.busy":"2021-08-10T21:39:52.502842Z","iopub.execute_input":"2021-08-10T21:39:52.503216Z","iopub.status.idle":"2021-08-10T21:39:52.516434Z","shell.execute_reply.started":"2021-08-10T21:39:52.503178Z","shell.execute_reply":"2021-08-10T21:39:52.514938Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Finally, let's test out BrainLoader.","metadata":{}},{"cell_type":"code","source":"train_bs = 1\ntrain_dataset = BrainLoader('train_labels.csv', train_path, split='train', modality='T1w')\n\ntrain_loader = DataLoader(train_dataset, batch_size=train_bs, shuffle=True)\n\nfor img, label in train_loader:\n    print('Iteration')\n    print(img.shape)\n    print(img.min())\n    print(img.mean())\n    print(img.max())\n    print(label.shape)\n    break","metadata":{"execution":{"iopub.status.busy":"2021-08-10T21:46:46.589391Z","iopub.execute_input":"2021-08-10T21:46:46.589786Z","iopub.status.idle":"2021-08-10T21:46:48.463240Z","shell.execute_reply.started":"2021-08-10T21:46:46.589752Z","shell.execute_reply":"2021-08-10T21:46:48.462251Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This should give anyone intending to use 3D CNNs in PyTorch a head-start.","metadata":{}}]}