Repository: AliAmini93/MRI-MS-Plaques-Segmentation Branch: main Commit: 9ac8863a87a8 Files: 3 Total size: 523.4 KB Directory structure: gitextract_wx3lw4ny/ ├── LICENSE ├── MS_lesion_segmentation_3DAttUNet.ipynb └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2023 Ali Amini Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MS_lesion_segmentation_3DAttUNet.ipynb ================================================ { "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "zpPXSgJJP8vc", "outputId": "f0f5f0f4-c317-438a-da27-9923e613c99e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sun Apr 11 07:53:37 2021 \n", "+-----------------------------------------------------------------------------+\n", "| NVIDIA-SMI 460.67 Driver Version: 460.32.03 CUDA Version: 11.2 |\n", "|-------------------------------+----------------------+----------------------+\n", "| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n", "| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n", "| | | MIG M. |\n", "|===============================+======================+======================|\n", "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", "| N/A 41C P8 9W / 70W | 0MiB / 15109MiB | 0% Default |\n", "| | | N/A |\n", "+-------------------------------+----------------------+----------------------+\n", " \n", "+-----------------------------------------------------------------------------+\n", "| Processes: |\n", "| GPU GI CI PID Type Process name GPU Memory |\n", "| ID ID Usage |\n", "|=============================================================================|\n", "| No running processes found |\n", "+-----------------------------------------------------------------------------+\n" ] } ], "source": [ "!nvidia-smi" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "ArwHJBuNQn-L", "outputId": "36dd4ace-dab6-4908-e4fc-3f1dafbf6fac" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Mounted at /gdrive\n" ] } ], "source": [ "from google.colab import drive\n", "drive.mount('/gdrive')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "HpExRSMvQ0yQ" }, "outputs": [], "source": [ "!pip install numpy\n", "!pip install scikit-learn\n", "!pip install tensorflow-gpu\n", "!pip install scipy\n", "!pip install simpleitk\n", "!pip install grpcio\n", "!pip install git+https://github.com/aleju/imgaug.git" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "GigYbU1wQ2KP", "outputId": "7a25b562-0051-43fc-a3d9-07672153b01e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.4.1\n" ] } ], "source": [ "%matplotlib inline\n", "import nibabel as nib\n", "import tensorflow as tf\n", "import datetime\n", "import tensorflow as tf\n", "print(tf.__version__)\n", "import numpy as np\n", "import h5py\n", "import os\n", "import random\n", "from sklearn.utils import shuffle\n", "from tqdm import tqdm\n", "import math\n", "import SimpleITK as sitk # For loading the dataset\n", "import numpy as np # For data manipulation\n", "import glob # For populating the list of files\n", "import re # For parsing the filenames (to know their modality)\n", "from skimage.transform import resize # For resizing\n", "from scipy.ndimage import zoom # For resizing but not used for now\n", "import matplotlib.pyplot as plt\n", "from PIL import Image\n", "import imgaug.augmenters as iaa\n", "from sklearn.model_selection import KFold\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "CWY33UqLQ9Vh" }, "outputs": [], "source": [ "def read_img(img_path):\n", " \"\"\"\n", " Reads a .nii.gz image and returns as a numpy array.\n", " \"\"\"\n", " \n", " return sitk.GetArrayFromImage(sitk.ReadImage(img_path))\n", " \n", "def itensity_normalize_one_volume(volume):\n", " \"\"\"\n", " normalize the itensity of an nd volume based on the mean and std of nonzeor region.\n", " inputs:\n", " volume: the input nd volume.\n", " outputs:\n", " out: the normalized nd volume.\n", " \"\"\"\n", " pixels = volume[volume > 0]\n", " mean = pixels.mean()\n", " std = pixels.std()\n", " out = (volume - mean)/std\n", " out_random = np.zeros(volume.shape)\n", " out[volume == 0] = out_random[volume == 0]\n", " return out\n", " \n", "def get_none_zero_region(im, margin):\n", " \"\"\"\n", " get the bounding box of the non-zero region of an ND volume.\n", " \"\"\"\n", " input_shape = im.shape\n", " if(type(margin) is int ):\n", " margin = [margin]*len(input_shape)\n", " assert(len(input_shape) == len(margin))\n", " indxes = np.nonzero(im)\n", " idx_min = []\n", " idx_max = []\n", " for i in range(len(input_shape)):\n", " idx_min.append(indxes[i].min())\n", " idx_max.append(indxes[i].max())\n", "\n", " for i in range(len(input_shape)):\n", " idx_min[i] = max(idx_min[i] - margin[i], 0)\n", " idx_max[i] = min(idx_max[i] + margin[i], input_shape[i] - 1)\n", " return idx_min, idx_max\n", " \n", "def crop_ND_volume_with_bounding_box(volume, min_idx, max_idx):\n", " \"\"\"\n", " crop/extract a subregion form an nd image.\n", " \"\"\"\n", " dim = len(volume.shape)\n", " assert(dim >= 2 and dim <= 5)\n", " if(dim == 2):\n", " output = volume[np.ix_(range(min_idx[0], max_idx[0] + 1),\n", " range(min_idx[1], max_idx[1] + 1))]\n", " elif(dim == 3):\n", " output = volume[np.ix_(range(min_idx[0], max_idx[0] + 1),\n", " range(min_idx[1], max_idx[1] + 1),\n", " range(min_idx[2], max_idx[2] + 1))]\n", " elif(dim == 4):\n", " output = volume[np.ix_(range(min_idx[0], max_idx[0] + 1),\n", " range(min_idx[1], max_idx[1] + 1),\n", " range(min_idx[2], max_idx[2] + 1),\n", " range(min_idx[3], max_idx[3] + 1))]\n", " elif(dim == 5):\n", " output = volume[np.ix_(range(min_idx[0], max_idx[0] + 1),\n", " range(min_idx[1], max_idx[1] + 1),\n", " range(min_idx[2], max_idx[2] + 1),\n", " range(min_idx[3], max_idx[3] + 1),\n", " range(min_idx[4], max_idx[4] + 1))]\n", " else:\n", " raise ValueError(\"the dimension number shoud be 2 to 5\")\n", " return output\n", "\n", "def my_resize(img, shape, mode='constant', orig_shape=(155, 240, 240)):\n", " \"\"\"\n", " Wrapper for scipy.ndimage.zoom suited for MRI images.\n", " \"\"\"\n", " assert len(shape) == 3, \"Can not have more than 3 dimensions\"\n", " factors = (\n", " shape[0]/orig_shape[0],\n", " shape[1]/orig_shape[1], \n", " shape[2]/orig_shape[2]\n", " )\n", " \n", " # Resize to the given shape\n", " return zoom(img, factors, mode=mode)\n", "\n", "def get_random_roi_sampling_center(input_shape, output_shape, sample_mode='full', bounding_box = None):\n", " \"\"\"\n", " get a random coordinate representing the center of a roi for sampling\n", " inputs:\n", " input_shape: the shape of sampled volume\n", " output_shape: the desired roi shape\n", " sample_mode: 'valid': the entire roi should be inside the input volume\n", " 'full': only the roi centre should be inside the input volume\n", " bounding_box: the bounding box which the roi center should be limited to\n", " outputs:\n", " center: the output center coordinate of a roi\n", " \"\"\"\n", " center = []\n", " for i in range(len(input_shape)):\n", " if(sample_mode[i] == 'full'):\n", " if(bounding_box):\n", " x0 = bounding_box[i*2]; x1 = bounding_box[i*2 + 1]\n", " else:\n", " x0 = 0; x1 = input_shape[i]\n", " else:\n", " if(bounding_box):\n", " x0 = bounding_box[i*2] + int(output_shape[i]/2) \n", " x1 = bounding_box[i*2+1] - int(output_shape[i]/2) \n", " else:\n", " x0 = int(output_shape[i]/2) \n", " x1 = input_shape[i] - x0\n", " if(x1 <= x0):\n", " centeri = int((x0 + x1)/2)\n", " else:\n", " centeri = np.random.randint(x0, x1)\n", " center.append(centeri)\n", " return center\n", "def extract_roi_from_volume(volume, in_center, output_shape, fill = 'random'):\n", " \"\"\"\n", " extract a roi from a 3d volume\n", " inputs:\n", " volume: the input 3D volume\n", " in_center: the center of the roi\n", " output_shape: the size of the roi\n", " fill: 'random' or 'zero', the mode to fill roi region where is outside of the input volume\n", " outputs:\n", " output: the roi volume\n", " \"\"\"\n", " input_shape = volume.shape \n", " if(fill == 'random'):\n", " output = np.random.normal(0, 1, size = output_shape)\n", " else:\n", " output = np.zeros(output_shape)\n", " r0max = [int(x/2) for x in output_shape]\n", " r1max = [output_shape[i] - r0max[i] for i in range(len(r0max))]\n", " r0 = [min(r0max[i], in_center[i]) for i in range(len(r0max))]\n", " r1 = [min(r1max[i], input_shape[i] - in_center[i]) for i in range(len(r0max))]\n", " out_center = r0max\n", "\n", " output[np.ix_(range(out_center[0] - r0[0], out_center[0] + r1[0]),\n", " range(out_center[1] - r0[1], out_center[1] + r1[1]),\n", " range(out_center[2] - r0[2], out_center[2] + r1[2]))] = \\\n", " volume[np.ix_(range(in_center[0] - r0[0], in_center[0] + r1[0]),\n", " range(in_center[1] - r0[1], in_center[1] + r1[1]),\n", " range(in_center[2] - r0[2], in_center[2] + r1[2]))]\n", " return output\n", "\n", "def zero_padding_3d(volume,size=((29, 30),(11,12),(29,30))):\n", " x= np.pad(volume,pad_width=size,mode='constant', constant_values=0)\n", " return x \n", "\n", "def preprocess_label(img):\n", "\n", " \"\"\"\n", " pad\n", " \"\"\"\n", " # img = zero_padding_3d(img)\n", " \n", " \"\"\"\n", " Separates out the 1 labels from the segmentation\n", " \"\"\"\n", " ms_plk = img == 1\n", " \n", " return np.array([ms_plk], dtype=np.uint8)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "kLXOwRbmRBJY" }, "outputs": [], "source": [ "# Get a list of files for all modalities individually for train\n", "mprage = glob.glob('/gdrive/My Drive/main dataset ISBI 2015/training_final_v4/training/*/preprocessed/*mprage_pp.nii')\n", "mprage.sort()\n", "T2 = glob.glob('/gdrive/My Drive/main dataset ISBI 2015/training_final_v4/training/*/preprocessed/*t2_pp.nii')\n", "T2.sort()\n", "FLAIR= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/training_final_v4/training/*/preprocessed/*flair_pp.nii')\n", "FLAIR.sort()\n", "PD= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/training_final_v4/training/*/preprocessed/*pd_pp.nii')\n", "PD.sort()\n", "mask_1= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/training_final_v4/training/*/masks/*mask1_pp.nii') # Ground Truth rater1\n", "mask_1.sort()\n", "mask_2= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/training_final_v4/training/*/masks/*mask2_pp.nii') # Ground Truth rater2\n", "mask_2.sort()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "egvAraTaRExZ" }, "outputs": [], "source": [ "print(np.shape(mprage))\n", "print(np.shape(T2))\n", "print(np.shape(FLAIR))\n", "print(np.shape(PD))\n", "print(np.shape(mask_1))\n", "print(np.shape(mask_2))\n", "print(mprage)\n", "print(T2)\n", "print(FLAIR)\n", "print(PD)\n", "print(mask_1)\n", "print(mask_2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "vPNdiq6ERHbZ" }, "outputs": [], "source": [ "# Get a list of files for all modalities individually or test\n", "Test_mprage = glob.glob('/gdrive/My Drive/main dataset ISBI 2015/testdata_website/testdata_website/*/preprocessed/*mprage_pp.nii')\n", "Test_mprage.sort()\n", "Test_T2 = glob.glob('/gdrive/My Drive/main dataset ISBI 2015/testdata_website/testdata_website/*/preprocessed/*t2_pp.nii')\n", "Test_T2.sort()\n", "Test_FLAIR= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/testdata_website/testdata_website/*/preprocessed/*flair_pp.nii')\n", "Test_FLAIR.sort()\n", "Test_PD= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/testdata_website/testdata_website/*/preprocessed/*pd_pp.nii')\n", "Test_PD.sort()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ldpHC_g5RKhR" }, "outputs": [], "source": [ "print(np.shape(Test_mprage))\n", "print(np.shape(Test_T2))\n", "print(np.shape(Test_FLAIR))\n", "print(np.shape(Test_PD))\n", "print(Test_mprage)\n", "print(Test_T2)\n", "print(Test_FLAIR)\n", "print(Test_PD)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "9XZqfiTpRNT-" }, "outputs": [], "source": [ "\"\"\"\n", "Parse all the training filenames and create a dictionary for each patient with structure:\n", "\"\"\"\n", "pat = re.compile('.*_(\\w*)\\_pp\\.nii')\n", "\n", "data_paths = [{\n", " pat.findall(item)[0]:item\n", " for item in items\n", "}\n", "for items in list(zip(mprage, FLAIR, T2, PD, mask_1, mask_2))]\n", "print(data_paths)\n", "print(type(data_paths))\n", "print(np.shape(data_paths))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "kb5jMQg0RTBD" }, "outputs": [], "source": [ "n_d = 21\n", "input_shape= (2,240,240)\n", "input_shape_data = (2,181,240,240)\n", "output_channels = 1\n", "dataa = np.empty((1392,) + input_shape, dtype=np.float32)\n", "labels = np.empty((1392, output_channels) + input_shape[1:], dtype=np.float32)\n", "\n", "print(type(input_shape))\n", "print(np.shape(dataa))\n", "print(type(dataa))\n", "print(np.shape(labels))\n", "print(type((len(data_paths[:n_d]),)))\n", "print(type(labels))\n", "print(type(data_paths))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "fRaZOVdkRWbz" }, "outputs": [], "source": [ "# Parameters for the progress bar\n", "total = len(data_paths[:n_d])\n", "step = 25 / total\n", "k = 0\n", "count = 0\n", "m = 0\n", "counting = 0\n", "z=0\n", "for i, imgs in enumerate(data_paths[:n_d]):\n", " try:\n", " data=np.empty((1,) + input_shape_data, dtype=np.float32)\n", " label=np.empty((1, output_channels) + input_shape_data[1:], dtype=np.float32)\n", " data[0]= np.array([preprocess(read_img(imgs[m])) for m in ['flair','mprage']], dtype=np.float32)\n", " label[0]= preprocess_label(read_img(imgs['mask1']))[None, ...]\n", " for j in range(181):\n", " if not np.all(label[0][:,j,:,:] == np.zeros((1,240,240))):\n", " dataa[k]= data[0][:,j,:,:]\n", " labels[k]= label[0][:,j,:,:]\n", " k+=1 \n", " # Print the progress bar\n", " print('\\r' + f'Progress: '\n", " f\"[{'=' * int((i+1) * step) + ' ' * (24 - int((i+1) * step))}]\"\n", " f\"({math.ceil((i+1) * 100 / (total))} %)\",\n", " end='')\n", " except Exception as e:\n", " print(f'Something went wrong with {imgs[\"T1\"]}, skipping...\\n Exception:\\n{str(e)}')\n", " continue\n", "data=np.empty((1,) + input_shape_data, dtype=np.float32)\n", "label=np.empty((1, output_channels) + input_shape_data[1:], dtype=np.uint8) \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "a190HzH9RZGj" }, "outputs": [], "source": [ "dataaa = np.einsum('iCHW->iHWC', dataa)\n", "labelss = np.einsum('iCHW->iHWC', labels)\n", "print(np.shape(dataaa))\n", "print(np.shape(labelss))\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "UaCpJ4xRRb2Y" }, "outputs": [], "source": [ "from sklearn.model_selection import train_test_split\n", "x_train,x_val,y_train,y_val=train_test_split(dataaa,labelss,test_size=0.2,random_state=73)\n", "print(np.shape(x_train))\n", "print(np.shape(y_train))\n", "print(np.shape(x_val))\n", "print(np.shape(y_val))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "occ5s4DyRe4p" }, "outputs": [], "source": [ "%matplotlib inline\n", "fig, (ax1, ax2) = plt.subplots(1,2,figsize=(10.125,6.75))\n", "index =600\n", "ax1.imshow(x_train[index,:,:,1], cmap='gray')\n", "ax2.imshow(y_train[index,:,:,0], cmap='gray')\n", "fig, (ax1, ax2) = plt.subplots(1,2,figsize=(10.125,6.75))\n", "index =200\n", "ax1.imshow(x_val[index,:,:,1], cmap='gray')\n", "ax2.imshow(y_val[index,:,:,0], cmap='gray')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "S2O9qef7RhXt" }, "outputs": [], "source": [ "hf = h5py.File(\"/gdrive/My Drive/data_train_ISBI_just_ms_slice.hdf5\", \"w\")\n", "dset = hf.create_dataset(\"data_train_ISBI_just_ms_slice\", data=x_train)\n", "hf.close()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "VCcGS7JoRkuR" }, "outputs": [], "source": [ "hf = h5py.File(\"/gdrive/My Drive/label_train_ISBI_just_ms_slice.hdf5\", \"w\")\n", "dset = hf.create_dataset(\"label_train_ISBI_just_ms_slice\", data=y_train)\n", "hf.close()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Hhx9V_GcRnD5" }, "outputs": [], "source": [ "hf = h5py.File(\"/gdrive/My Drive/data_val_ISBI_just_ms_slice.hdf5\", \"w\")\n", "dset = hf.create_dataset(\"data_val_ISBI_just_ms_slice\", data=x_val)\n", "hf.close()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "03xFseJDRpht" }, "outputs": [], "source": [ "hf = h5py.File(\"/gdrive/My Drive/label_val_ISBI_just_ms_slice.hdf5\", \"w\")\n", "dset = hf.create_dataset(\"label_val_ISBI_just_ms_slice\", data=y_val)\n", "hf.close()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "uV43ciLgR1Hi" }, "outputs": [], "source": [ "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "def trainset_generator(batch_size):\n", " hf_x = h5py.File('/gdrive/My Drive/data_train_just_ms_slice.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_train_just_ms_slice.hdf5', 'r')\n", " data_len = hf_x['data_train_just_ms_slice'].shape[0]\n", " while True:\n", " index = list(range(data_len))\n", " ind = shuffle(index)\n", " for x in batch(ind, batch_size):\n", " data_x=[]\n", " data_y=[]\n", " for i in x:\n", " norm_input=[]\n", " for w in [0,1]:\n", " norm_modal= itensity_normalize_one_volume(hf_x['data_train_just_ms_slice'][i,:,:,w])\n", " norm_input.append(norm_modal)\n", " norm_input = np.einsum('CHW->HWC', norm_input)\n", " data_x.append(norm_input)\n", " data_y.append(hf_y['label_train_just_ms_slice'][i, :])\n", " # gt_train = [np.array(data_y),np.array(data_y)]\n", " yield(np.array(data_x),np.array(data_y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "f0yjnInIR4Gd" }, "outputs": [], "source": [ "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "def valset_generator(batch_size):\n", " hf_x = h5py.File('/gdrive/My Drive/data_val_just_ms_slice.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_val_just_ms_slice.hdf5', 'r')\n", " data_len = hf_x['data_val_just_ms_slice'].shape[0]\n", " while True:\n", " index = list(range(data_len))\n", " for x in batch(index, batch_size):\n", " data_x_val=[]\n", " data_y_val=[]\n", " for i in x:\n", " norm_input_val=[]\n", " for w in [0,1]:\n", " norm_modal_val= itensity_normalize_one_volume(hf_x['data_val_just_ms_slice'][i,:,:,w])\n", " norm_input_val.append(norm_modal_val)\n", " norm_input_val = np.einsum('CHW->HWC', norm_input_val)\n", " data_x_val.append(norm_input_val)\n", " data_y_val.append(hf_y['label_val_just_ms_slice'][i, :])\n", " # gt_val = [np.array(data_y_val),np.array(data_y_val)]\n", " yield(np.array(data_x_val),np.array(data_y_val))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JsMZX6TQR69m" }, "outputs": [], "source": [ "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "\n", "seq = iaa.Sequential([iaa.Fliplr(0.5),\n", " iaa.Flipud(0.5)\n", " # sometimes2(iaa.Rot90(1,True))\n", " ])\n", "def trainset_generator_augmentation(batch_size):\n", " hf_x = h5py.File('/gdrive/My Drive/data_train_ISBI_just_ms_slice.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_train_ISBI_just_ms_slice.hdf5', 'r')\n", " data_len = hf_x['data_train_ISBI_just_ms_slice'].shape[0]\n", " while True:\n", " index = list(range(data_len))\n", " ind = shuffle(index)\n", " for x in batch(ind, batch_size):\n", " data_x=[]\n", " data_y=[]\n", " for i in x:\n", " norm_input=[]\n", " for w in [0,1]:\n", " norm_modal= itensity_normalize_one_volume(hf_x['data_train_ISBI_just_ms_slice'][i,:,:,w])\n", " norm_input.append(norm_modal)\n", " norm_input = np.einsum('CHW->HWC', norm_input)\n", " data_x.append(norm_input)\n", " data_y.append(hf_y['label_train_ISBI_just_ms_slice'][i, :])\n", " images_for_aug = np.array(data_x)\n", " labels_for_aug= np.array(data_y).astype(np.int32)\n", " images_aug, segmaps_aug = seq(images=images_for_aug, segmentation_maps=labels_for_aug)\n", " yield(images_aug,segmaps_aug.astype(np.float32))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "pvYKwZW4SxTh" }, "outputs": [], "source": [ "#####for testing train generator 3D ISBI\n", "%matplotlib inline\n", "hf_x = h5py.File('/gdrive/My Drive/data_train_3D_ISBI_21sample.hdf5', 'r')\n", "hf_y = h5py.File('/gdrive/My Drive/label_train_3D_ISBI_21sample.hdf5', 'r')\n", "data_x=[]\n", "data_y=[]\n", "data_y1=[]\n", "data_y2=[]\n", "for i in range(0,3):\n", " norm_input=[]\n", " idx_min, idx_max=get_none_zero_region(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0],0)\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " roi_sampled= get_random_roi_sampling_center(np.shape(croped_zero), (128,128,128), sample_mode='valid',bounding_box=None)\n", " for w in [0,1,2,3]:\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,w], idx_min,idx_max)\n", " extracted_roi=extract_roi_from_volume(croped_zero,roi_sampled, (128,128,128), fill = 'zero')\n", " norm_modal=itensity_normalize_one_volume(extracted_roi)\n", " norm_input.append(norm_modal) \n", " norm_input = np.einsum('CDHW->HWDC', norm_input)\n", " croped_mask1=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " extract_roi_mask1= extract_roi_from_volume(croped_mask1,roi_sampled, (128,128,128), fill = 'zero')\n", " croped_mask2=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,1], idx_min,idx_max)\n", " extract_roi_mask2= extract_roi_from_volume(croped_mask2,roi_sampled, (128,128,128), fill = 'zero')\n", " extract_roi_mask= extract_roi_mask1 + extract_roi_mask2\n", " extract_roi_mask[extract_roi_mask<=1]=0\n", " extract_roi_mask[extract_roi_mask>1]= 1\n", " extract_roi_mask= np.expand_dims(extract_roi_mask,axis=0)\n", " extract_roi_mask= np.einsum('CDHW->HWDC', extract_roi_mask)\n", " extract_roi_mask1= np.expand_dims(extract_roi_mask1,axis=0)\n", " extract_roi_mask1= np.einsum('CDHW->HWDC', extract_roi_mask1)\n", " extract_roi_mask2= np.expand_dims(extract_roi_mask2,axis=0)\n", " extract_roi_mask2= np.einsum('CDHW->HWDC', extract_roi_mask2)\n", " data_x.append(norm_input)\n", " data_y.append(extract_roi_mask)\n", " data_y1.append(extract_roi_mask1)\n", " data_y2.append(extract_roi_mask2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 332 }, "id": "GONAtLk_TSJX", "outputId": "e497f1cd-7dd8-465d-e63b-48e64e18ad2a" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(3, 128, 128, 128, 4)\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 12, "metadata": { "tags": [] }, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "print(np.shape(data_x))\n", "fig, (ax1, ax2, ax3, ax4)= plt.subplots(1,4,figsize=(2*10.125,2*6.75))\n", "u=64\n", "ax1.imshow(np.array(data_x)[2,:,:,u,0], cmap='gray')\n", "ax2.imshow(np.array(data_y)[2,:,:,u,0], cmap='gray')\n", "ax3.imshow(np.array(data_y1)[2,:,:,u,0], cmap='gray')\n", "ax4.imshow(np.array(data_y2)[2,:,:,u,0], cmap='gray')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "wTZKhb7XJgMe" }, "outputs": [], "source": [ "\n", "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "def trainset_generator_3D_ISBI(batch_size):\n", " hf_x = h5py.File('/gdrive/My Drive/data_train_3D_ISBI_21sample.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_train_3D_ISBI_21sample.hdf5', 'r')\n", " data_len = hf_x['data_train_3D_ISBI_21sample.hdf5'].shape[0]\n", " while True:\n", " index = list(range(data_len))\n", " ind = shuffle(index)\n", " for x in batch(ind, batch_size):\n", " data_x=[]\n", " data_y=[]\n", " for i in x:\n", " norm_input=[]\n", " idx_min, idx_max=get_none_zero_region(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0],0)\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " roi_sampled= get_random_roi_sampling_center(np.shape(croped_zero), (128,128,128), sample_mode='valid',bounding_box=None)\n", " for w in range(0,4):\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,w], idx_min,idx_max)\n", " extracted_roi=extract_roi_from_volume(croped_zero,roi_sampled, (128,128,128), fill = 'zero')\n", " norm_modal=itensity_normalize_one_volume(extracted_roi)\n", " norm_input.append(norm_modal) \n", " norm_input = np.einsum('CDHW->HWDC', norm_input)\n", " croped=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " extract_roi_mask= extract_roi_from_volume(croped,roi_sampled, (128,128,128), fill = 'zero')\n", " extract_roi_mask= np.expand_dims(extract_roi_mask,axis=0)\n", " extract_roi_mask= np.einsum('CDHW->HWDC', extract_roi_mask)\n", " data_x.append(norm_input)\n", " data_y.append(extract_roi_mask)\n", " # gt_train = [np.array(data_y),np.array(data_y),np.array(data_y),np.array(data_y)]\n", " yield(np.array(data_x),np.array(data_y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "qrAoFn2fXF0v" }, "outputs": [], "source": [ "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "def trainset_generator_3D_ISBI_maj(batch_size):\n", " hf_x = h5py.File('/gdrive/My Drive/data_train_3D_ISBI_21sample.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_train_3D_ISBI_21sample.hdf5', 'r')\n", " data_len = hf_x['data_train_3D_ISBI_21sample.hdf5'].shape[0]\n", " while True:\n", " index = list(range(data_len))\n", " ind = shuffle(index)\n", " for x in batch(ind, batch_size):\n", " data_x=[]\n", " data_y=[]\n", " for i in x:\n", " norm_input=[]\n", " idx_min, idx_max=get_none_zero_region(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0],0)\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " roi_sampled= get_random_roi_sampling_center(np.shape(croped_zero), (128,128,128), sample_mode='valid',bounding_box=None)\n", " for w in range(0,4):\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,w], idx_min,idx_max)\n", " extracted_roi=extract_roi_from_volume(croped_zero,roi_sampled, (128,128,128), fill = 'zero')\n", " norm_modal=itensity_normalize_one_volume(extracted_roi)\n", " norm_input.append(norm_modal) \n", " norm_input = np.einsum('CDHW->HWDC', norm_input)\n", " croped_mask1=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " extract_roi_mask1= extract_roi_from_volume(croped_mask1,roi_sampled, (128,128,128), fill = 'zero')\n", " croped_mask2=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,1], idx_min,idx_max)\n", " extract_roi_mask2= extract_roi_from_volume(croped_mask2,roi_sampled, (128,128,128), fill = 'zero')\n", " extract_roi_mask= extract_roi_mask1 + extract_roi_mask2\n", " extract_roi_mask[extract_roi_mask<=1]=0\n", " extract_roi_mask[extract_roi_mask>1]= 1\n", " extract_roi_mask= np.expand_dims(extract_roi_mask,axis=0)\n", " extract_roi_mask= np.einsum('CDHW->HWDC', extract_roi_mask)\n", " data_x.append(norm_input)\n", " data_y.append(extract_roi_mask)\n", " # gt_train = [np.array(data_y),np.array(data_y),np.array(data_y),np.array(data_y)]\n", " yield(np.array(data_x),np.array(data_y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 526 }, "id": "4w1aCbarRtPy", "outputId": "f62c3600-e830-47a2-b4e3-6fa8cbb67f16" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "false\n", "here2\n", "false\n", "here2\n", "true\n", "here1\n", "true\n", "here1\n", "true\n", "here1\n", "true\n", "here1\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 18, "metadata": { "tags": [] }, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "#####for extract train patch 3D ISBI\n", "%matplotlib inline\n", "def crop_ND_volume_with_bounding_box(volume, min_idx, max_idx):\n", " \"\"\"\n", " crop/extract a subregion form an nd image.\n", " \"\"\"\n", " dim = len(volume.shape)\n", " channel_dim = volume.shape[-1]\n", " output = volume[np.ix_(range(min_idx[0], max_idx[0] + 1),\n", " range(min_idx[1], max_idx[1] + 1),\n", " range(min_idx[2], max_idx[2] + 1),\n", " range(channel_dim))]\n", " return output\n", "def get_random_roi_sampling_center(input_shape, output_shape, sample_mode='full', bounding_box = None):\n", " \"\"\"\n", " get a random coordinate representing the center of a roi for sampling\n", " inputs:\n", " input_shape: the shape of sampled volume\n", " output_shape: the desired roi shape\n", " sample_mode: 'valid': the entire roi should be inside the input volume\n", " 'full': only the roi centre should be inside the input volume\n", " bounding_box: the bounding box which the roi center should be limited to\n", " outputs:\n", " center: the output center coordinate of a roi\n", " \"\"\"\n", " center = []\n", " for i in range(len(input_shape)):\n", " if(sample_mode == 'full'):\n", " if(bounding_box):\n", " x0 = bounding_box[i*2]; x1 = bounding_box[i*2 + 1]\n", " else:\n", " x0 = 0; x1 = input_shape[i]\n", " else:\n", " if(bounding_box):\n", " x0 = bounding_box[i*2] + int(output_shape[i]/2) \n", " x1 = bounding_box[i*2+1] - int(output_shape[i]/2) \n", " else:\n", " x0 = int(output_shape[i]/2) \n", " x1 = input_shape[i] - x0\n", " if(x1 <= x0):\n", " centeri = int((x0 + x1)/2)\n", " else:\n", " centeri = np.random.randint(x0, x1)\n", " center.append(centeri)\n", " return center\n", "def get_random_roi_sampling_center_v2(croped_zero,croped_zero_mask,nonzero_center='true',mask_num=0):\n", " \"\"\"\n", " get a random coordinate representing the center of a roi for sampling\n", " two option is available:\n", " 1) get roi center that centered on nonzero voxels of mask.\n", " 2) get roi center that centered on nonzero voxels of brain region with a\n", " margin of border to include more brain region\n", " mask_num => 0=mask_1 and 1=mask_2\n", " \"\"\"\n", " if nonzero_center=='both':\n", " nonzero_center = random.choice(['true','false'])\n", " if nonzero_center=='true':\n", " mask_nonzero = np.array(croped_zero_mask[:,:,:,mask_num].nonzero())\n", " random_select = np.random.randint(0,mask_nonzero.shape[1])\n", " roi_index = mask_nonzero[:,random_select]\n", " if nonzero_center=='false':\n", " nonzero = np.array(croped_zero[:,:,:, 0].nonzero())\n", " random_select = np.random.randint(0,nonzero.shape[1])\n", " roi_index = nonzero[:,random_select]\n", " return roi_index\n", "def extract_roi_from_volume(volume, in_center, output_shape, fill = 'random'):\n", " \"\"\"\n", " extract a roi from a 3d volume\n", " inputs:\n", " volume: the input 3D volume\n", " in_center: the center of the roi\n", " output_shape: the size of the roi\n", " fill: 'random' or 'zero', the mode to fill roi region where is outside of the input volume\n", " outputs:\n", " output: the roi volume\n", " \"\"\"\n", " input_shape = volume.shape \n", " if(fill == 'random'):\n", " output = np.random.normal(0, 1, size = output_shape)\n", " else:\n", " output = np.zeros(output_shape)\n", " r0max = [int(x/2) for x in output_shape]\n", " r1max = [output_shape[i] - r0max[i] for i in range(len(r0max))]\n", " r0 = [min(r0max[i], in_center[i]) for i in range(len(r0max))]\n", " r1 = [min(r1max[i], input_shape[i] - in_center[i]) for i in range(len(r0max))]\n", " out_center = r0max\n", "\n", " output[np.ix_(range(out_center[0] - r0[0], out_center[0] + r1[0]),\n", " range(out_center[1] - r0[1], out_center[1] + r1[1]),\n", " range(out_center[2] - r0[2], out_center[2] + r1[2]))] = \\\n", " volume[np.ix_(range(in_center[0] - r0[0], in_center[0] + r1[0]),\n", " range(in_center[1] - r0[1], in_center[1] + r1[1]),\n", " range(in_center[2] - r0[2], in_center[2] + r1[2]))]\n", " return output\n", "hf_x = h5py.File('/gdrive/My Drive/data_train_3D_ISBI_21sample.hdf5', 'r')\n", "hf_y = h5py.File('/gdrive/My Drive/label_train_3D_ISBI_21sample.hdf5', 'r')\n", "data_x=[]\n", "data_y=[]\n", "data_len = hf_x['data_train_3D_ISBI_21sample.hdf5'].shape[0]\n", "for i in range(2):\n", " idx_min, idx_max=get_none_zero_region(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0],0)\n", " croped_zero = crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,:], idx_min,idx_max) \n", " croped_zero_mask = crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,:], idx_min,idx_max) \n", " idx_min_mask, idx_max_mask = get_none_zero_region(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0],0)\n", " for j in range(3):\n", " # roi_sampled = get_random_roi_sampling_center(np.shape(croped_zero[:,:,:,0]), (64,64,64), sample_mode='full',bounding_box=[idx_min_mask[0],idx_max_mask[0],idx_min_mask[1],idx_max_mask[1],idx_min_mask[2],idx_max_mask[2]])\n", " roi_sampled = get_random_roi_sampling_center_v2(croped_zero,croped_zero_mask,nonzero_center='both',mask_num=0)\n", " extracted_roi=[]\n", " extracted_roi_mask=[]\n", " for k in range(4):\n", " extracted_roi.append(extract_roi_from_volume(croped_zero[:,:,:,k],roi_sampled, (64,64,64), fill = 'zero'))\n", " for kk in range(2):\n", " extracted_roi_mask.append(extract_roi_from_volume(croped_zero_mask[:,:,:,kk],roi_sampled, (64,64,64), fill = 'zero'))\n", " extracted_roi = np.einsum('CDHW->DHWC', extracted_roi)\n", " extracted_roi_mask = np.einsum('CDHW->DHWC', extracted_roi_mask)\n", " data_x.append(extracted_roi)\n", " data_y.append(extracted_roi_mask)\n", "fig, (ax1, ax2, ax3, ax4)= plt.subplots(1,4,figsize=(2*10.125,2*6.75))\n", "n=0\n", "u=32\n", "ch=0\n", "ax1.imshow(np.array(data_x)[n,u,:,:,ch], cmap='gray')\n", "ax2.imshow(np.array(data_y)[n,u,:,:,0], cmap='gray')\n", "ax3.imshow(np.array(data_x)[n,u,:,:,ch], cmap='gray')\n", "ax4.imshow(np.array(data_y)[n,u,:,:,1], cmap='gray')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "AlIU9IydKpBG" }, "outputs": [], "source": [ "def crop_ND_volume_with_bounding_box(volume, min_idx, max_idx):\n", " \"\"\"\n", " crop/extract a subregion form an nd image.\n", " \"\"\"\n", " dim = len(volume.shape)\n", " channel_dim = volume.shape[-1]\n", " output = volume[np.ix_(range(min_idx[0], max_idx[0] + 1),\n", " range(min_idx[1], max_idx[1] + 1),\n", " range(min_idx[2], max_idx[2] + 1),\n", " range(channel_dim))]\n", " return output\n", "def get_random_roi_sampling_center_v2(croped_zero,croped_zero_mask,nonzero_center='both',mask_num=1):\n", " \"\"\"\n", " get a random coordinate representing the center of a roi for sampling\n", " two option is available:\n", " 1) get roi center that centered on nonzero voxels of mask.\n", " 2) get roi center that centered on nonzero voxels of brain region with a\n", " margin of border to include more brain region\n", " \"\"\"\n", " if nonzero_center=='both':\n", " nonzero_center = random.choice(['true','false'])\n", " if nonzero_center=='true':\n", " mask_nonzero = np.array(croped_zero_mask[:,:,:,mask_num].nonzero())\n", " random_select = np.random.randint(0,mask_nonzero.shape[1])\n", " roi_index = mask_nonzero[:,random_select]\n", " if nonzero_center=='false':\n", " nonzero = np.array(croped_zero[:,:,:, 0].nonzero())\n", " random_select = np.random.randint(0,nonzero.shape[1])\n", " roi_index = nonzero[:,random_select]\n", " return roi_index\n", "def extract_roi_from_volume(volume, in_center, output_shape, fill = 'random'):\n", " \"\"\"\n", " extract a roi from a 3d volume\n", " inputs:\n", " volume: the input 3D volume\n", " in_center: the center of the roi\n", " output_shape: the size of the roi\n", " fill: 'random' or 'zero', the mode to fill roi region where is outside of the input volume\n", " outputs:\n", " output: the roi volume\n", " \"\"\"\n", " input_shape = volume.shape \n", " if(fill == 'random'):\n", " output = np.random.normal(0, 1, size = output_shape)\n", " else:\n", " output = np.zeros(output_shape)\n", " r0max = [int(x/2) for x in output_shape]\n", " r1max = [output_shape[i] - r0max[i] for i in range(len(r0max))]\n", " r0 = [min(r0max[i], in_center[i]) for i in range(len(r0max))]\n", " r1 = [min(r1max[i], input_shape[i] - in_center[i]) for i in range(len(r0max))]\n", " out_center = r0max\n", "\n", " output[np.ix_(range(out_center[0] - r0[0], out_center[0] + r1[0]),\n", " range(out_center[1] - r0[1], out_center[1] + r1[1]),\n", " range(out_center[2] - r0[2], out_center[2] + r1[2]))] = \\\n", " volume[np.ix_(range(in_center[0] - r0[0], in_center[0] + r1[0]),\n", " range(in_center[1] - r0[1], in_center[1] + r1[1]),\n", " range(in_center[2] - r0[2], in_center[2] + r1[2]))]\n", " return output\n", "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "def trainset_generator_3D_ISBI_aug_random_patch_equal_prob(batch_size):\n", " hf_x = h5py.File('/gdrive/My Drive/data_train_3D_ISBI_21sample.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_train_3D_ISBI_21sample.hdf5', 'r')\n", " data_len = hf_x['data_train_3D_ISBI_21sample.hdf5'].shape[0]\n", " while True:\n", " index = list(range(data_len))\n", " ind = shuffle(index)\n", " for x in batch(ind, batch_size):\n", " data_x=[]\n", " data_y=[]\n", " for i in x:\n", " idx_min, idx_max=get_none_zero_region(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0],0)\n", " croped_zero = crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,:], idx_min,idx_max) \n", " croped_zero_mask = crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,:], idx_min,idx_max)\n", " roi_sampled = get_random_roi_sampling_center_v2(croped_zero,croped_zero_mask,nonzero_center='both',mask_num=1)\n", " norm_input=[]\n", " extracted_roi_mask=[] \n", " for w in range(4):\n", " extracted_roi = extract_roi_from_volume(croped_zero[:,:,:,w],roi_sampled, (64,64,64), fill = 'zero')\n", " norm_modal = itensity_normalize_one_volume(extracted_roi)\n", " norm_input.append(norm_modal) \n", " for ww in range(2):\n", " extracted_roi_mask.append(extract_roi_from_volume(croped_zero_mask[:,:,:,ww],roi_sampled, (64,64,64), fill = 'zero'))\n", " norm_input = np.einsum('CDHW->HWDC', norm_input)\n", " extract_roi_mask= np.einsum('CDHW->HWDC', extracted_roi_mask)\n", " extract_roi_mask= np.expand_dims(extract_roi_mask[:,:,:,1],axis=-1)\n", " xy = tf.concat([norm_input, extract_roi_mask], axis=-1)\n", " ###fliplr\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[1]),lambda: xy)\n", " ###flipud\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[0]),lambda: xy)\n", " # ###rot90 counter-clockwise\n", " # xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: np.rot90(xy,axes=(0,1)),lambda: xy)\n", " # ###rot90 clockwise\n", " # xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: np.rot90(xy,axes=(1,0)),lambda: xy)\n", " images_aug, segmaps_aug = tf.split(xy, [4, 1], axis=-1)\n", " data_x.append(images_aug.numpy())\n", " data_y.append(segmaps_aug.numpy())\n", " yield(np.array(data_x),np.array(data_y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "j1BPJA3bubZe" }, "outputs": [], "source": [ "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "def trainset_generator_3D_ISBI_aug(batch_size):\n", " hf_x = h5py.File('/gdrive/My Drive/data_train_3D_ISBI_21sample.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_train_3D_ISBI_21sample.hdf5', 'r')\n", " data_len = hf_x['data_train_3D_ISBI_21sample.hdf5'].shape[0]\n", " while True:\n", " index = list(range(data_len))\n", " ind = shuffle(index)\n", " for x in batch(ind, batch_size):\n", " data_x=[]\n", " data_y=[]\n", " for i in x:\n", " norm_input=[]\n", " idx_min, idx_max=get_none_zero_region(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0],0)\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " roi_sampled= get_random_roi_sampling_center(np.shape(croped_zero), (128,128,128), sample_mode='valid',bounding_box=None)\n", " for w in range(0,4):\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,w], idx_min,idx_max)\n", " extracted_roi=extract_roi_from_volume(croped_zero,roi_sampled, (128,128,128), fill = 'zero')\n", " norm_modal=itensity_normalize_one_volume(extracted_roi)\n", " norm_input.append(norm_modal) \n", " norm_input = np.einsum('CDHW->HWDC', norm_input)\n", " croped=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,1], idx_min,idx_max)\n", " extract_roi_mask= extract_roi_from_volume(croped,roi_sampled, (128,128,128), fill = 'zero')\n", " extract_roi_mask= np.expand_dims(extract_roi_mask,axis=0)\n", " extract_roi_mask= np.einsum('CDHW->HWDC', extract_roi_mask)\n", " xy = tf.concat([norm_input, extract_roi_mask], axis=-1)\n", " ###fliplr\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[1]),lambda: xy)\n", " ###flipud\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[0]),lambda: xy)\n", " images_aug, segmaps_aug = tf.split(xy, [4, 1], axis=-1)\n", " data_x.append(images_aug.numpy())\n", " data_y.append(segmaps_aug.numpy())\n", " yield(np.array(data_x),np.array(data_y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "n9iqKjIJvRI_" }, "outputs": [], "source": [ "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "def trainset_generator_3D_ISBI_aug_patch_wise(batch_size):\n", " hf_x = h5py.File('/gdrive/My Drive/data_train_3D_ISBI_420patch.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_train_3D_ISBI_420patch.hdf5', 'r')\n", " data_len = hf_x['data_train_3D_ISBI_420patch.hdf5'].shape[0]\n", " while True:\n", " index = list(range(data_len))\n", " ind = shuffle(index)\n", " for x in batch(ind, batch_size):\n", " data_x=[]\n", " data_y=[]\n", " for i in x:\n", " norm_input=[]\n", " for w in range(0,4):\n", " norm_modal=itensity_normalize_one_volume(hf_x['data_train_3D_ISBI_420patch.hdf5'][i,:,:,:,w])\n", " norm_input.append(norm_modal) \n", " norm_input = np.einsum('CDHW->HWDC', norm_input)\n", " expanded_mask= np.expand_dims(hf_y['label_train_3D_ISBI_420patch.hdf5'][i,:,:,:,1],axis=0)\n", " expanded_mask= np.einsum('CDHW->HWDC', expanded_mask)\n", " xy = tf.concat([norm_input, expanded_mask], axis=-1)\n", " ###fliplr\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[1]),lambda: xy)\n", " ###flipud\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[0]),lambda: xy)\n", " # ###rot90 counter-clockwise\n", " # xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: np.rot90(xy,axes=(0,1)),lambda: xy)\n", " # ###rot90 clockwise\n", " # xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: np.rot90(xy,axes=(1,0)),lambda: xy)\n", " images_aug, segmaps_aug = tf.split(xy, [4, 1], axis=-1)\n", " data_x.append(images_aug.numpy())\n", " data_y.append(segmaps_aug.numpy())\n", " # gt_train = [np.array(data_y),np.array(data_y),np.array(data_y),np.array(data_y)]\n", " yield(np.array(data_x),np.array(data_y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "4CN78XEOXGby" }, "outputs": [], "source": [ "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "def trainset_generator_3D_ISBI_maj_aug_kfold(batch_size,num_fold):\n", " hf_x = h5py.File('/gdrive/My Drive/data_train_3D_ISBI_21sample.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_train_3D_ISBI_21sample.hdf5', 'r')\n", " data_len = hf_x['data_train_3D_ISBI_21sample.hdf5'].shape[0]\n", " index = list(range(data_len))\n", " train_indices=[None]*5\n", " val_indices=[None]*5\n", " kf = KFold(n_splits=5,random_state=0,shuffle=True)\n", " for i, [train_index, val_index] in enumerate(kf.split(index)):\n", " train_indices[i], val_indices[i] = train_index, val_index\n", " while True:\n", " ind = shuffle(train_indices[num_fold])\n", " for x in batch(ind, batch_size):\n", " data_x=[]\n", " data_y=[]\n", " for i in x:\n", " norm_input=[]\n", " idx_min, idx_max=get_none_zero_region(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0],0)\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " roi_sampled= get_random_roi_sampling_center(np.shape(croped_zero), (128,128,128), sample_mode='valid',bounding_box=None)\n", " for w in range(0,4):\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,w], idx_min,idx_max)\n", " extracted_roi=extract_roi_from_volume(croped_zero,roi_sampled, (128,128,128), fill = 'zero')\n", " norm_modal=itensity_normalize_one_volume(extracted_roi)\n", " norm_input.append(norm_modal) \n", " norm_input = np.einsum('CDHW->HWDC', norm_input)\n", " croped_mask1=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " extract_roi_mask1= extract_roi_from_volume(croped_mask1,roi_sampled, (128,128,128), fill = 'zero')\n", " croped_mask2=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,1], idx_min,idx_max)\n", " extract_roi_mask2= extract_roi_from_volume(croped_mask2,roi_sampled, (128,128,128), fill = 'zero')\n", " extract_roi_mask= extract_roi_mask1 + extract_roi_mask2\n", " extract_roi_mask[extract_roi_mask<=1]=0\n", " extract_roi_mask[extract_roi_mask>1]= 1\n", " extract_roi_mask= np.expand_dims(extract_roi_mask,axis=0)\n", " extract_roi_mask= np.einsum('CDHW->HWDC', extract_roi_mask)\n", " xy = tf.concat([norm_input, extract_roi_mask], axis=-1)\n", " ###fliplr\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[1]),lambda: xy)\n", " ###flipud\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[0]),lambda: xy)\n", " images_aug, segmaps_aug = tf.split(xy, [4, 1], axis=-1)\n", " data_x.append(images_aug.numpy())\n", " data_y.append(segmaps_aug.numpy())\n", " # gt_train = [np.array(data_y),np.array(data_y),np.array(data_y),np.array(data_y)]\n", " yield(np.array(data_x),np.array(data_y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "_Ks-TLpTToRq" }, "outputs": [], "source": [ "def batch(iterable, n=1):\n", " l = len(iterable)\n", " for ndx in range(0, l, n):\n", " yield iterable[ndx:min(ndx + n, l)]\n", "def trainset_generator_3D_ISBI_maj_aug(batch_size):\n", " hf_x = h5py.File('/gdrive/My Drive/data_train_3D_ISBI_21sample.hdf5', 'r')\n", " hf_y = h5py.File('/gdrive/My Drive/label_train_3D_ISBI_21sample.hdf5', 'r')\n", " data_len = hf_x['data_train_3D_ISBI_21sample.hdf5'].shape[0]\n", " while True:\n", " index = list(range(data_len))\n", " ind = shuffle(index)\n", " for x in batch(ind, batch_size):\n", " data_x=[]\n", " data_y=[]\n", " for i in x:\n", " norm_input=[]\n", " idx_min, idx_max=get_none_zero_region(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0],0)\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " roi_sampled= get_random_roi_sampling_center(np.shape(croped_zero), (128,128,128), sample_mode='valid',bounding_box=None)\n", " for w in range(0,4):\n", " croped_zero=crop_ND_volume_with_bounding_box(hf_x['data_train_3D_ISBI_21sample.hdf5'][i,:,:,:,w], idx_min,idx_max)\n", " extracted_roi=extract_roi_from_volume(croped_zero,roi_sampled, (128,128,128), fill = 'zero')\n", " norm_modal=itensity_normalize_one_volume(extracted_roi)\n", " norm_input.append(norm_modal) \n", " norm_input = np.einsum('CDHW->HWDC', norm_input)\n", " croped_mask1=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,0], idx_min,idx_max)\n", " extract_roi_mask1= extract_roi_from_volume(croped_mask1,roi_sampled, (128,128,128), fill = 'zero')\n", " croped_mask2=crop_ND_volume_with_bounding_box(hf_y['label_train_3D_ISBI_21sample.hdf5'][i,:,:,:,1], idx_min,idx_max)\n", " extract_roi_mask2= extract_roi_from_volume(croped_mask2,roi_sampled, (128,128,128), fill = 'zero')\n", " extract_roi_mask= extract_roi_mask1 + extract_roi_mask2\n", " extract_roi_mask[extract_roi_mask<=0.5]=0\n", " extract_roi_mask[extract_roi_mask>0.5]= 1\n", " extract_roi_mask= np.expand_dims(extract_roi_mask,axis=0)\n", " extract_roi_mask= np.einsum('CDHW->HWDC', extract_roi_mask)\n", " xy = tf.concat([norm_input, extract_roi_mask], axis=-1)\n", " ###fliplr\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[1]),lambda: xy)\n", " ###flipud\n", " xy = tf.cond(tf.random.uniform(()) > 0.5,lambda: tf.reverse(xy, axis=[0]),lambda: xy)\n", " images_aug, segmaps_aug = tf.split(xy, [4, 1], axis=-1)\n", " data_x.append(images_aug.numpy())\n", " data_y.append(segmaps_aug.numpy())\n", " yield(np.array(data_x),np.array(data_y))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "9lk8A9T2R9RD", "outputId": "e8a67595-94a2-4030-c602-c3d526536016" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: \"functional_1\"\n", "_________________________________________________________________\n", "Layer (type) Output Shape Param # \n", "=================================================================\n", "input_1 (InputLayer) [(None, None, None, 4)] 0 \n", "_________________________________________________________________\n", "group_normalization (GroupNo (None, None, None, 4) 8 \n", "=================================================================\n", "Total params: 8\n", "Trainable params: 8\n", "Non-trainable params: 0\n", "_________________________________________________________________\n" ] } ], "source": [ "### GroupNormalization code from https://github.com/titu1994/keras-global-context-networks.git repository\n", "from keras.engine import Layer, InputSpec\n", "from keras import initializers\n", "from keras import regularizers\n", "from keras import constraints\n", "from keras import backend as K\n", "\n", "from keras.utils.generic_utils import get_custom_objects\n", "\n", "\n", "class GroupNormalization(Layer):\n", " \"\"\"Group normalization layer\n", " Group Normalization divides the channels into groups and computes within each group\n", " the mean and variance for normalization. GN's computation is independent of batch sizes,\n", " and its accuracy is stable in a wide range of batch sizes\n", " # Arguments\n", " groups: Integer, the number of groups for Group Normalization.\n", " axis: Integer, the axis that should be normalized\n", " (typically the features axis).\n", " For instance, after a `Conv2D` layer with\n", " `data_format=\"channels_first\"`,\n", " set `axis=1` in `BatchNormalization`.\n", " epsilon: Small float added to variance to avoid dividing by zero.\n", " center: If True, add offset of `beta` to normalized tensor.\n", " If False, `beta` is ignored.\n", " scale: If True, multiply by `gamma`.\n", " If False, `gamma` is not used.\n", " When the next layer is linear (also e.g. `nn.relu`),\n", " this can be disabled since the scaling\n", " will be done by the next layer.\n", " beta_initializer: Initializer for the beta weight.\n", " gamma_initializer: Initializer for the gamma weight.\n", " beta_regularizer: Optional regularizer for the beta weight.\n", " gamma_regularizer: Optional regularizer for the gamma weight.\n", " beta_constraint: Optional constraint for the beta weight.\n", " gamma_constraint: Optional constraint for the gamma weight.\n", " # Input shape\n", " Arbitrary. Use the keyword argument `input_shape`\n", " (tuple of integers, does not include the samples axis)\n", " when using this layer as the first layer in a model.\n", " # Output shape\n", " Same shape as input.\n", " # References\n", " - [Group Normalization](https://arxiv.org/abs/1803.08494)\n", " \"\"\"\n", "\n", " def __init__(self,\n", " groups=32,\n", " axis=-1,\n", " epsilon=1e-5,\n", " center=True,\n", " scale=True,\n", " beta_initializer='zeros',\n", " gamma_initializer='ones',\n", " beta_regularizer=None,\n", " gamma_regularizer=None,\n", " beta_constraint=None,\n", " gamma_constraint=None,\n", " **kwargs):\n", " super(GroupNormalization, self).__init__(**kwargs)\n", " self.supports_masking = True\n", " self.groups = groups\n", " self.axis = axis\n", " self.epsilon = epsilon\n", " self.center = center\n", " self.scale = scale\n", " self.beta_initializer = initializers.get(beta_initializer)\n", " self.gamma_initializer = initializers.get(gamma_initializer)\n", " self.beta_regularizer = regularizers.get(beta_regularizer)\n", " self.gamma_regularizer = regularizers.get(gamma_regularizer)\n", " self.beta_constraint = constraints.get(beta_constraint)\n", " self.gamma_constraint = constraints.get(gamma_constraint)\n", "\n", " def build(self, input_shape):\n", " dim = input_shape[self.axis]\n", "\n", " if dim is None:\n", " raise ValueError('Axis ' + str(self.axis) + ' of '\n", " 'input tensor should have a defined dimension '\n", " 'but the layer received an input with shape ' +\n", " str(input_shape) + '.')\n", "\n", " if dim < self.groups:\n", " raise ValueError('Number of groups (' + str(self.groups) + ') cannot be '\n", " 'more than the number of channels (' +\n", " str(dim) + ').')\n", "\n", " if dim % self.groups != 0:\n", " raise ValueError('Number of groups (' + str(self.groups) + ') must be a '\n", " 'multiple of the number of channels (' +\n", " str(dim) + ').')\n", "\n", " self.input_spec = InputSpec(ndim=len(input_shape),\n", " axes={self.axis: dim})\n", " shape = (dim,)\n", "\n", " if self.scale:\n", " self.gamma = self.add_weight(shape=shape,\n", " name='gamma',\n", " initializer=self.gamma_initializer,\n", " regularizer=self.gamma_regularizer,\n", " constraint=self.gamma_constraint)\n", " else:\n", " self.gamma = None\n", " if self.center:\n", " self.beta = self.add_weight(shape=shape,\n", " name='beta',\n", " initializer=self.beta_initializer,\n", " regularizer=self.beta_regularizer,\n", " constraint=self.beta_constraint)\n", " else:\n", " self.beta = None\n", " self.built = True\n", "\n", " def call(self, inputs, **kwargs):\n", " input_shape = K.int_shape(inputs)\n", " tensor_input_shape = K.shape(inputs)\n", "\n", " # Prepare broadcasting shape.\n", " reduction_axes = list(range(len(input_shape)))\n", " del reduction_axes[self.axis]\n", " broadcast_shape = [1] * len(input_shape)\n", " broadcast_shape[self.axis] = input_shape[self.axis] // self.groups\n", " broadcast_shape.insert(1, self.groups)\n", "\n", " reshape_group_shape = K.shape(inputs)\n", " group_axes = [reshape_group_shape[i] for i in range(len(input_shape))]\n", " group_axes[self.axis] = input_shape[self.axis] // self.groups\n", " group_axes.insert(1, self.groups)\n", "\n", " # reshape inputs to new group shape\n", " group_shape = [group_axes[0], self.groups] + group_axes[2:]\n", " group_shape = K.stack(group_shape)\n", " inputs = K.reshape(inputs, group_shape)\n", "\n", " group_reduction_axes = list(range(len(group_axes)))\n", " group_reduction_axes = group_reduction_axes[2:]\n", "\n", " mean = K.mean(inputs, axis=group_reduction_axes, keepdims=True)\n", " variance = K.var(inputs, axis=group_reduction_axes, keepdims=True)\n", "\n", " inputs = (inputs - mean) / (K.sqrt(variance + self.epsilon))\n", "\n", " # prepare broadcast shape\n", " inputs = K.reshape(inputs, group_shape)\n", " outputs = inputs\n", "\n", " # In this case we must explicitly broadcast all parameters.\n", " if self.scale:\n", " broadcast_gamma = K.reshape(self.gamma, broadcast_shape)\n", " outputs = outputs * broadcast_gamma\n", "\n", " if self.center:\n", " broadcast_beta = K.reshape(self.beta, broadcast_shape)\n", " outputs = outputs + broadcast_beta\n", "\n", " outputs = K.reshape(outputs, tensor_input_shape)\n", "\n", " return outputs\n", "\n", " def get_config(self):\n", " config = {\n", " 'groups': self.groups,\n", " 'axis': self.axis,\n", " 'epsilon': self.epsilon,\n", " 'center': self.center,\n", " 'scale': self.scale,\n", " 'beta_initializer': initializers.serialize(self.beta_initializer),\n", " 'gamma_initializer': initializers.serialize(self.gamma_initializer),\n", " 'beta_regularizer': regularizers.serialize(self.beta_regularizer),\n", " 'gamma_regularizer': regularizers.serialize(self.gamma_regularizer),\n", " 'beta_constraint': constraints.serialize(self.beta_constraint),\n", " 'gamma_constraint': constraints.serialize(self.gamma_constraint)\n", " }\n", " base_config = super(GroupNormalization, self).get_config()\n", " return dict(list(base_config.items()) + list(config.items()))\n", "\n", " def compute_output_shape(self, input_shape):\n", " return input_shape\n", "\n", "\n", "get_custom_objects().update({'GroupNormalization': GroupNormalization})\n", "\n", "\n", "if __name__ == '__main__':\n", " from keras.layers import Input\n", " from keras.models import Model\n", " ip = Input(shape=(None, None, 4))\n", " x = GroupNormalization(groups=2, axis=-1, epsilon=0.1)(ip)\n", " model = Model(ip, x)\n", " model.summary()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "MJSvzY30R_0o", "outputId": "5363847b-8048-4b92-8831-e8c84a275ecb" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: \"functional_3\"\n", "__________________________________________________________________________________________________\n", "Layer (type) Output Shape Param # Connected to \n", "==================================================================================================\n", "input_2 (InputLayer) [(None, 64, 64, 32)] 0 \n", "__________________________________________________________________________________________________\n", "conv2d (Conv2D) (None, 64, 64, 1) 32 input_2[0][0] \n", "__________________________________________________________________________________________________\n", "reshape_1 (Reshape) (None, 4096, 1) 0 conv2d[0][0] \n", "__________________________________________________________________________________________________\n", "reshape (Reshape) (None, 4096, 32) 0 input_2[0][0] \n", "__________________________________________________________________________________________________\n", "softmax (Softmax) (None, 4096, 1) 0 reshape_1[0][0] \n", "__________________________________________________________________________________________________\n", "dot (Dot) (None, 32, 1) 0 reshape[0][0] \n", " softmax[0][0] \n", "__________________________________________________________________________________________________\n", "reshape_2 (Reshape) (None, 1, 1, 32) 0 dot[0][0] \n", "__________________________________________________________________________________________________\n", "conv2d_1 (Conv2D) (None, 1, 1, 2) 64 reshape_2[0][0] \n", "__________________________________________________________________________________________________\n", "group_normalization_1 (GroupNor (None, 1, 1, 2) 4 conv2d_1[0][0] \n", "__________________________________________________________________________________________________\n", "activation (Activation) (None, 1, 1, 2) 0 group_normalization_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv2d_2 (Conv2D) (None, 1, 1, 32) 64 activation[0][0] \n", "__________________________________________________________________________________________________\n", "activation_1 (Activation) (None, 1, 1, 32) 0 conv2d_2[0][0] \n", "__________________________________________________________________________________________________\n", "add (Add) (None, 64, 64, 32) 0 input_2[0][0] \n", " activation_1[0][0] \n", "==================================================================================================\n", "Total params: 164\n", "Trainable params: 164\n", "Non-trainable params: 0\n", "__________________________________________________________________________________________________\n" ] } ], "source": [ "### global_context_block code from https://github.com/titu1994/keras-global-context-networks.git repository\n", "from keras.layers import Conv1D, Conv2D, Conv3D\n", "from keras.layers import Reshape\n", "from keras.layers import Activation\n", "from keras.layers import Softmax\n", "from keras.layers import Permute\n", "from keras.layers import add, dot\n", "\n", "from keras import backend as K\n", "\n", "def global_context_block(ip, reduction_ratio=16, transform_activation='linear'):\n", " \"\"\"\n", " Adds a Global Context attention block for self attention to the input tensor.\n", " Input tensor can be or rank 3 (temporal), 4 (spatial) or 5 (spatio-temporal).\n", "\n", " # Arguments:\n", " ip: input tensor\n", " intermediate_dim: The dimension of the intermediate representation. Can be\n", " `None` or a positive integer greater than 0. If `None`, computes the\n", " intermediate dimension as half of the input channel dimension.\n", " reduction_ratio: Reduces the input filters by this factor for the\n", " bottleneck block of the transform submodule. Node: the reduction\n", " ratio must be set such that it divides the input number of channels,\n", " transform_activation: activation function to apply to the output\n", " of the transform block. Can be any string activation function availahle\n", " to Keras.\n", "\n", " # Returns:\n", " a tensor of same shape as input\n", " \"\"\"\n", " channel_dim = 1 if K.image_data_format() == 'channels_first' else -1\n", " ip_shape = K.int_shape(ip)\n", "\n", " # check rank and calculate the input shape\n", " if len(ip_shape) == 3: # temporal / time series data\n", " rank = 3\n", " batchsize, dim1, channels = ip_shape\n", "\n", " elif len(ip_shape) == 4: # spatial / image data\n", " rank = 4\n", "\n", " if channel_dim == 1:\n", " batchsize, channels, dim1, dim2 = ip_shape\n", " else:\n", " batchsize, dim1, dim2, channels = ip_shape\n", "\n", " elif len(ip_shape) == 5: # spatio-temporal / Video or Voxel data\n", " rank = 5\n", "\n", " if channel_dim == 1:\n", " batchsize, channels, dim1, dim2, dim3 = ip_shape\n", " else:\n", " batchsize, dim1, dim2, dim3, channels = ip_shape\n", "\n", " else:\n", " raise ValueError('Input dimension has to be either 3 (temporal), 4 (spatial) or 5 (spatio-temporal)')\n", "\n", " if rank > 3:\n", " flat_spatial_dim = -1 if K.image_data_format() == 'channels_first' else 1\n", " else:\n", " flat_spatial_dim = 1\n", "\n", " \"\"\" Context Modelling Block \"\"\"\n", " # [B, ***, C] or [B, C, ***]\n", " input_flat = _spatial_flattenND(ip, rank)\n", " # [B, ..., C] or [B, C, ...]\n", " context = _convND(ip, rank, channels=1, kernel=1)\n", " # [B, ..., 1] or [B, 1, ...]\n", " context = _spatial_flattenND(context, rank)\n", " # [B, ***, 1] or [B, 1, ***]\n", " context = Softmax(axis=flat_spatial_dim)(context)\n", "\n", " # Compute context block outputs\n", " context = dot([input_flat, context], axes=flat_spatial_dim)\n", " # [B, C, 1]\n", " context = _spatial_expandND(context, rank)\n", " # [B, C, 1...] or [B, 1..., C]\n", "\n", " \"\"\" Transform block \"\"\"\n", " # Transform bottleneck\n", " # [B, C // R, 1...] or [B, 1..., C // R]\n", " transform = _convND(context, rank, channels // reduction_ratio, kernel=1)\n", " # Group normalization acts as Layer Normalization when groups = 1\n", " transform = GroupNormalization(groups=1, axis=channel_dim)(transform)\n", " transform = Activation('relu')(transform)\n", "\n", " # Transform output block\n", " # [B, C, 1...] or [B, 1..., C]\n", " transform = _convND(transform, rank, channels, kernel=1)\n", " transform = Activation(transform_activation)(transform)\n", "\n", " # apply context transform\n", " out = add([ip, transform])\n", "\n", " return out\n", "\n", "\n", "def _convND(ip, rank, channels, kernel=1):\n", " assert rank in [3, 4, 5], \"Rank of input must be 3, 4 or 5\"\n", "\n", " if rank == 3:\n", " x = Conv1D(channels, kernel, padding='same', use_bias=False, kernel_initializer='he_normal')(ip)\n", " elif rank == 4:\n", " x = Conv2D(channels, (kernel, kernel), padding='same', use_bias=False, kernel_initializer='he_normal')(ip)\n", " else:\n", " x = Conv3D(channels, (kernel, kernel, kernel), padding='same', use_bias=False, kernel_initializer='he_normal')(ip)\n", "\n", " return x\n", "\n", "\n", "def _spatial_flattenND(ip, rank):\n", " assert rank in [3, 4, 5], \"Rank of input must be 3, 4 or 5\"\n", "\n", " ip_shape = K.int_shape(ip)\n", " channel_dim = 1 if K.image_data_format() == 'channels_first' else -1\n", "\n", " if rank == 3:\n", " x = ip # identity op for rank 3\n", "\n", " elif rank == 4:\n", " if channel_dim == 1:\n", " # [C, D1, D2] -> [C, D1 * D2]\n", " shape = [ip_shape[1], ip_shape[2] * ip_shape[3]]\n", " else:\n", " # [D1, D2, C] -> [D1 * D2, C]\n", " shape = [ip_shape[1] * ip_shape[2], ip_shape[3]]\n", "\n", " x = Reshape(shape)(ip)\n", "\n", " else:\n", " if channel_dim == 1:\n", " # [C, D1, D2, D3] -> [C, D1 * D2 * D3]\n", " shape = [ip_shape[1], ip_shape[2] * ip_shape[3] * ip_shape[4]]\n", " else:\n", " # [D1, D2, D3, C] -> [D1 * D2 * D3, C]\n", " shape = [ip_shape[1] * ip_shape[2] * ip_shape[3], ip_shape[4]]\n", "\n", " x = Reshape(shape)(ip)\n", "\n", " return x\n", "\n", "\n", "def _spatial_expandND(ip, rank):\n", " assert rank in [3, 4, 5], \"Rank of input must be 3, 4 or 5\"\n", "\n", " channel_dim = 1 if K.image_data_format() == 'channels_first' else -1\n", "\n", " if rank == 3:\n", " x = Permute((2, 1))(ip) # identity op for rank 3\n", "\n", " elif rank == 4:\n", " if channel_dim == 1:\n", " # [C, D1, D2] -> [C, D1 * D2]\n", " shape = [-1, 1, 1]\n", " else:\n", " # [D1, D2, C] -> [D1 * D2, C]\n", " shape = [1, 1, -1]\n", "\n", " x = Reshape(shape)(ip)\n", "\n", " else:\n", " if channel_dim == 1:\n", " # [C, D1, D2, D3] -> [C, D1 * D2 * D3]\n", " shape = [-1, 1, 1, 1]\n", " else:\n", " # [D1, D2, D3, C] -> [D1 * D2 * D3, C]\n", " shape = [1, 1, 1, -1]\n", "\n", " x = Reshape(shape)(ip)\n", "\n", " return x\n", "\n", "\n", "if __name__ == '__main__':\n", " from keras.layers import Input\n", " from keras.models import Model\n", "\n", " ip = Input(shape=(64, 64, 32))\n", " x = global_context_block(ip, reduction_ratio=16)\n", "\n", " model = Model(ip, x)\n", "\n", " model.summary()\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "GzJec_bLSCrq", "outputId": "2be2b188-e030-4a4f-836f-3e2e2cc60279" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:`period` argument is deprecated. Please use `save_freq` to specify the frequency in number of batches seen.\n", "Model: \"model\"\n", "__________________________________________________________________________________________________\n", "Layer (type) Output Shape Param # Connected to \n", "==================================================================================================\n", "input_1 (InputLayer) [(None, 192, 192, 19 0 \n", "__________________________________________________________________________________________________\n", "conv1_1 (Conv3D) (None, 192, 192, 192 3488 input_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_1_bn (BatchNormalization) (None, 192, 192, 192 128 conv1_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_1_act (Activation) (None, 192, 192, 192 0 conv1_1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_2 (Conv3D) (None, 192, 192, 192 27680 conv1_1_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_2_bn (BatchNormalization) (None, 192, 192, 192 128 conv1_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_2_act (Activation) (None, 192, 192, 192 0 conv1_2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "max_pooling3d (MaxPooling3D) (None, 96, 96, 96, 3 0 conv1_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_1 (Conv3D) (None, 96, 96, 96, 6 55360 max_pooling3d[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_1_bn (BatchNormalization) (None, 96, 96, 96, 6 256 conv2_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_1_act (Activation) (None, 96, 96, 96, 6 0 conv2_1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_2 (Conv3D) (None, 96, 96, 96, 6 110656 conv2_1_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_2_bn (BatchNormalization) (None, 96, 96, 96, 6 256 conv2_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_2_act (Activation) (None, 96, 96, 96, 6 0 conv2_2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "max_pooling3d_1 (MaxPooling3D) (None, 48, 48, 48, 6 0 conv2_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_1 (Conv3D) (None, 48, 48, 48, 1 221312 max_pooling3d_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_1_bn (BatchNormalization) (None, 48, 48, 48, 1 512 conv3_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_1_act (Activation) (None, 48, 48, 48, 1 0 conv3_1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_2 (Conv3D) (None, 48, 48, 48, 1 442496 conv3_1_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_2_bn (BatchNormalization) (None, 48, 48, 48, 1 512 conv3_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_2_act (Activation) (None, 48, 48, 48, 1 0 conv3_2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "max_pooling3d_2 (MaxPooling3D) (None, 24, 24, 24, 1 0 conv3_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_1 (Conv3D) (None, 24, 24, 24, 2 884992 max_pooling3d_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_1_bn (BatchNormalization) (None, 24, 24, 24, 2 1024 conv4_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_1_act (Activation) (None, 24, 24, 24, 2 0 conv4_1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_2 (Conv3D) (None, 24, 24, 24, 2 1769728 conv4_1_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_2_bn (BatchNormalization) (None, 24, 24, 24, 2 1024 conv4_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_2_act (Activation) (None, 24, 24, 24, 2 0 conv4_2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "max_pooling3d_3 (MaxPooling3D) (None, 12, 12, 12, 2 0 conv4_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "center_1 (Conv3D) (None, 12, 12, 12, 5 3539456 max_pooling3d_3[0][0] \n", "__________________________________________________________________________________________________\n", "center_1_bn (BatchNormalization (None, 12, 12, 12, 5 2048 center_1[0][0] \n", "__________________________________________________________________________________________________\n", "center_1_act (Activation) (None, 12, 12, 12, 5 0 center_1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "center_2 (Conv3D) (None, 12, 12, 12, 5 7078400 center_1_act[0][0] \n", "__________________________________________________________________________________________________\n", "center_2_bn (BatchNormalization (None, 12, 12, 12, 5 2048 center_2[0][0] \n", "__________________________________________________________________________________________________\n", "center_2_act (Activation) (None, 12, 12, 12, 5 0 center_2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "g1_conv (Conv3D) (None, 12, 12, 12, 5 262656 center_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "g1_bn (BatchNormalization) (None, 12, 12, 12, 5 2048 g1_conv[0][0] \n", "__________________________________________________________________________________________________\n", "g1_act (Activation) (None, 12, 12, 12, 5 0 g1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv3d (Conv3D) (None, 12, 12, 12, 2 131328 g1_act[0][0] \n", "__________________________________________________________________________________________________\n", "g_up_1 (Conv3DTranspose) (None, 12, 12, 12, 2 1769728 conv3d[0][0] \n", "__________________________________________________________________________________________________\n", "xl_1 (Conv3D) (None, 12, 12, 12, 2 524544 conv4_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "add (Add) (None, 12, 12, 12, 2 0 g_up_1[0][0] \n", " xl_1[0][0] \n", "__________________________________________________________________________________________________\n", "activation (Activation) (None, 12, 12, 12, 2 0 add[0][0] \n", "__________________________________________________________________________________________________\n", "psi_1 (Conv3D) (None, 12, 12, 12, 1 257 activation[0][0] \n", "__________________________________________________________________________________________________\n", "activation_1 (Activation) (None, 12, 12, 12, 1 0 psi_1[0][0] \n", "__________________________________________________________________________________________________\n", "up_sampling3d (UpSampling3D) (None, 24, 24, 24, 1 0 activation_1[0][0] \n", "__________________________________________________________________________________________________\n", "psi_up_1 (Lambda) (None, 24, 24, 24, 2 0 up_sampling3d[0][0] \n", "__________________________________________________________________________________________________\n", "q_attn_1 (Multiply) (None, 24, 24, 24, 2 0 psi_up_1[0][0] \n", " conv4_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "q_attn_conv_1 (Conv3D) (None, 24, 24, 24, 2 65792 q_attn_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv3d_transpose (Conv3DTranspo (None, 24, 24, 24, 2 3539200 center_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "q_attn_bn_1 (BatchNormalization (None, 24, 24, 24, 2 1024 q_attn_conv_1[0][0] \n", "__________________________________________________________________________________________________\n", "up1 (Concatenate) (None, 24, 24, 24, 5 0 conv3d_transpose[0][0] \n", " q_attn_bn_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_d_1 (Conv3D) (None, 24, 24, 24, 2 3539200 up1[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_d_1_bn (BatchNormalizatio (None, 24, 24, 24, 2 1024 conv1_d_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_d_1_act (Activation) (None, 24, 24, 24, 2 0 conv1_d_1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_d_2 (Conv3D) (None, 24, 24, 24, 2 1769728 conv1_d_1_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_d_2_bn (BatchNormalizatio (None, 24, 24, 24, 2 1024 conv1_d_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv1_d_2_act (Activation) (None, 24, 24, 24, 2 0 conv1_d_2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "g2_conv (Conv3D) (None, 24, 24, 24, 2 65792 conv1_d_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "g2_bn (BatchNormalization) (None, 24, 24, 24, 2 1024 g2_conv[0][0] \n", "__________________________________________________________________________________________________\n", "g2_act (Activation) (None, 24, 24, 24, 2 0 g2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv3d_1 (Conv3D) (None, 24, 24, 24, 1 32896 g2_act[0][0] \n", "__________________________________________________________________________________________________\n", "g_up_2 (Conv3DTranspose) (None, 24, 24, 24, 1 442496 conv3d_1[0][0] \n", "__________________________________________________________________________________________________\n", "xl_2 (Conv3D) (None, 24, 24, 24, 1 131200 conv3_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "add_1 (Add) (None, 24, 24, 24, 1 0 g_up_2[0][0] \n", " xl_2[0][0] \n", "__________________________________________________________________________________________________\n", "activation_2 (Activation) (None, 24, 24, 24, 1 0 add_1[0][0] \n", "__________________________________________________________________________________________________\n", "psi_2 (Conv3D) (None, 24, 24, 24, 1 129 activation_2[0][0] \n", "__________________________________________________________________________________________________\n", "activation_3 (Activation) (None, 24, 24, 24, 1 0 psi_2[0][0] \n", "__________________________________________________________________________________________________\n", "up_sampling3d_1 (UpSampling3D) (None, 48, 48, 48, 1 0 activation_3[0][0] \n", "__________________________________________________________________________________________________\n", "psi_up_2 (Lambda) (None, 48, 48, 48, 1 0 up_sampling3d_1[0][0] \n", "__________________________________________________________________________________________________\n", "q_attn_2 (Multiply) (None, 48, 48, 48, 1 0 psi_up_2[0][0] \n", " conv3_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "q_attn_conv_2 (Conv3D) (None, 48, 48, 48, 1 16512 q_attn_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv3d_transpose_1 (Conv3DTrans (None, 48, 48, 48, 1 884864 conv1_d_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "q_attn_bn_2 (BatchNormalization (None, 48, 48, 48, 1 512 q_attn_conv_2[0][0] \n", "__________________________________________________________________________________________________\n", "up2 (Concatenate) (None, 48, 48, 48, 2 0 conv3d_transpose_1[0][0] \n", " q_attn_bn_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_d_1 (Conv3D) (None, 48, 48, 48, 1 884864 up2[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_d_1_bn (BatchNormalizatio (None, 48, 48, 48, 1 512 conv2_d_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_d_1_act (Activation) (None, 48, 48, 48, 1 0 conv2_d_1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_d_2 (Conv3D) (None, 48, 48, 48, 1 442496 conv2_d_1_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_d_2_bn (BatchNormalizatio (None, 48, 48, 48, 1 512 conv2_d_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv2_d_2_act (Activation) (None, 48, 48, 48, 1 0 conv2_d_2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "g3_conv (Conv3D) (None, 48, 48, 48, 1 16512 conv2_d_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "g3_bn (BatchNormalization) (None, 48, 48, 48, 1 512 g3_conv[0][0] \n", "__________________________________________________________________________________________________\n", "g3_act (Activation) (None, 48, 48, 48, 1 0 g3_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv3d_2 (Conv3D) (None, 48, 48, 48, 6 8256 g3_act[0][0] \n", "__________________________________________________________________________________________________\n", "g_up_3 (Conv3DTranspose) (None, 48, 48, 48, 6 110656 conv3d_2[0][0] \n", "__________________________________________________________________________________________________\n", "xl_3 (Conv3D) (None, 48, 48, 48, 6 32832 conv2_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "add_2 (Add) (None, 48, 48, 48, 6 0 g_up_3[0][0] \n", " xl_3[0][0] \n", "__________________________________________________________________________________________________\n", "activation_4 (Activation) (None, 48, 48, 48, 6 0 add_2[0][0] \n", "__________________________________________________________________________________________________\n", "psi_3 (Conv3D) (None, 48, 48, 48, 1 65 activation_4[0][0] \n", "__________________________________________________________________________________________________\n", "activation_5 (Activation) (None, 48, 48, 48, 1 0 psi_3[0][0] \n", "__________________________________________________________________________________________________\n", "up_sampling3d_2 (UpSampling3D) (None, 96, 96, 96, 1 0 activation_5[0][0] \n", "__________________________________________________________________________________________________\n", "psi_up_3 (Lambda) (None, 96, 96, 96, 6 0 up_sampling3d_2[0][0] \n", "__________________________________________________________________________________________________\n", "q_attn_3 (Multiply) (None, 96, 96, 96, 6 0 psi_up_3[0][0] \n", " conv2_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "q_attn_conv_3 (Conv3D) (None, 96, 96, 96, 6 4160 q_attn_3[0][0] \n", "__________________________________________________________________________________________________\n", "conv3d_transpose_2 (Conv3DTrans (None, 96, 96, 96, 6 221248 conv2_d_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "q_attn_bn_3 (BatchNormalization (None, 96, 96, 96, 6 256 q_attn_conv_3[0][0] \n", "__________________________________________________________________________________________________\n", "up3 (Concatenate) (None, 96, 96, 96, 1 0 conv3d_transpose_2[0][0] \n", " q_attn_bn_3[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_d_1 (Conv3D) (None, 96, 96, 96, 6 221248 up3[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_d_1_bn (BatchNormalizatio (None, 96, 96, 96, 6 256 conv3_d_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_d_1_act (Activation) (None, 96, 96, 96, 6 0 conv3_d_1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_d_2 (Conv3D) (None, 96, 96, 96, 6 110656 conv3_d_1_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_d_2_bn (BatchNormalizatio (None, 96, 96, 96, 6 256 conv3_d_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv3_d_2_act (Activation) (None, 96, 96, 96, 6 0 conv3_d_2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv3d_transpose_3 (Conv3DTrans (None, 192, 192, 192 55328 conv3_d_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "up4 (Concatenate) (None, 192, 192, 192 0 conv3d_transpose_3[0][0] \n", " conv1_2_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_d_1 (Conv3D) (None, 192, 192, 192 55328 up4[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_d_1_bn (BatchNormalizatio (None, 192, 192, 192 128 conv4_d_1[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_d_1_act (Activation) (None, 192, 192, 192 0 conv4_d_1_bn[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_d_2 (Conv3D) (None, 192, 192, 192 27680 conv4_d_1_act[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_d_2_bn (BatchNormalizatio (None, 192, 192, 192 128 conv4_d_2[0][0] \n", "__________________________________________________________________________________________________\n", "conv4_d_2_act (Activation) (None, 192, 192, 192 0 conv4_d_2_bn[0][0] \n", "__________________________________________________________________________________________________\n", "final (Conv3D) (None, 192, 192, 192 33 conv4_d_2_act[0][0] \n", "==================================================================================================\n", "Total params: 29,518,404\n", "Trainable params: 29,509,828\n", "Non-trainable params: 8,576\n", "__________________________________________________________________________________________________\n", "None\n" ] } ], "source": [ "import tensorflow as tf\n", "import tensorflow.keras.backend as K\n", "from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Conv2DTranspose, concatenate, BatchNormalization, Activation, add, AveragePooling2D, UpSampling2D, SeparableConvolution2D, Dropout, multiply, GlobalAveragePooling2D, Conv3D, Conv3DTranspose, MaxPooling3D, GlobalAveragePooling3D, UpSampling3D\n", "from tensorflow.keras.models import Model, model_from_json\n", "from tensorflow.keras.optimizers import Adam, SGD, Nadam, Adagrad, RMSprop\n", "from tensorflow.keras.activations import elu, sigmoid\n", "from tensorflow.keras.layers import LeakyReLU, Reshape, Dense, Multiply\n", "from tensorflow.keras.callbacks import ModelCheckpoint\n", "from tensorflow.keras.callbacks import LearningRateScheduler\n", "from tensorflow.keras.callbacks import EarlyStopping\n", "from tensorflow.keras.callbacks import ReduceLROnPlateau\n", "from tensorflow.keras.initializers import glorot_uniform\n", "from tensorflow.keras.utils import plot_model\n", "from tensorflow.keras.preprocessing.image import ImageDataGenerator\n", "from tensorflow.keras.callbacks import TensorBoard, CSVLogger\n", "from tensorflow.keras.losses import BinaryCrossentropy\n", "from tensorflow.keras.metrics import AUC\n", "from tensorflow.keras.regularizers import l2\n", "from tensorflow.keras.layers import Lambda\n", "input_shape = (64,64,64,4)\n", "input_shape_test= (192,192,192,4)\n", "seed_number = 1234\n", "l2 = l2(0.0001)\n", "ki = glorot_uniform(seed=seed_number)\n", "def recall_m(y_true, y_pred):\n", " y_true_f = K.flatten(y_true)\n", " y_pred_f = K.flatten(y_pred)\n", " true_positives = K.sum(K.round(K.clip(y_true_f* y_pred_f, 0, 1)))\n", " possible_positives = K.sum(K.round(K.clip(y_true_f, 0, 1)))\n", " recall = true_positives / (possible_positives + K.epsilon())\n", " return recall\n", "\n", "def precision_m(y_true, y_pred):\n", " y_true_f = K.flatten(y_true)\n", " y_pred_f = K.flatten(y_pred)\n", " true_positives = K.sum(K.round(K.clip(y_true_f * y_pred_f, 0, 1)))\n", " predicted_positives = K.sum(K.round(K.clip(y_pred_f, 0, 1)))\n", " precision = true_positives / (predicted_positives + K.epsilon())\n", " return precision\n", "\n", "def f1_m(y_true, y_pred):\n", " precision = precision_m(y_true, y_pred)\n", " recall = recall_m(y_true, y_pred)\n", " return 2*((precision*recall)/(precision+recall+K.epsilon()))\n", "\n", "\n", "def jaccard_distance(y_true, y_pred, smooth=1):\n", " \"\"\"Jaccard distance for semantic segmentation.\n", " Also known as the intersection-over-union loss.\n", " This loss is useful when you have unbalanced numbers of pixels within an image\n", " because it gives all classes equal weight. However, it is not the defacto\n", " standard for image segmentation.\n", " For example, assume you are trying to predict if\n", " each pixel is cat, dog, or background.\n", " You have 80% background pixels, 10% dog, and 10% cat.\n", " If the model predicts 100% background\n", " should it be be 80% right (as with categorical cross entropy)\n", " or 30% (with this loss)?\n", " The loss has been modified to have a smooth gradient as it converges on zero.\n", " This has been shifted so it converges on 0 and is smoothed to avoid exploding\n", " or disappearing gradient.\n", " Jaccard = (|X & Y|)/ (|X|+ |Y| - |X & Y|)\n", " = sum(|A*B|)/(sum(|A|)+sum(|B|)-sum(|A*B|))\n", " # Arguments\n", " y_true: The ground truth tensor.\n", " y_pred: The predicted tensor\n", " smooth: Smoothing factor. Default is 100.\n", " # Returns\n", " The Jaccard distance between the two tensors.\n", " # References\n", " - [What is a good evaluation measure for semantic segmentation?](\n", " http://www.bmva.org/bmvc/2013/Papers/paper0032/paper0032.pdf)\n", " \"\"\"\n", " threshold = 0.5\n", " y_true_f = K.flatten(y_true)\n", " y_pred_f = K.flatten(y_pred)\n", " y_pred_f = K.cast(K.greater_equal(K.clip(y_pred_f, 0, 1), threshold),'float32')\n", " intersection = K.sum(K.abs(y_true_f * y_pred_f), axis=-1)\n", " sum_ = K.sum(K.abs(y_true_f) + K.abs(y_pred_f), axis=-1)\n", " jac = (intersection + smooth) / (sum_ - intersection + smooth)\n", " return jac\n", "def dice_coef(y_true, y_pred):\n", " threshold = 0.5\n", " y_true_f = K.flatten(y_true)\n", " y_pred_f = K.flatten(y_pred)\n", " y_pred_f = K.cast(K.greater_equal(K.clip(y_pred_f, 0, 1), threshold),'float32')\n", " intersection = K.sum(y_true_f * y_pred_f)\n", " return (2. * intersection + K.epsilon()) / (K.sum(y_true_f) + K.sum(y_pred_f) + K.epsilon())\n", "def dice_coefficient(y_true, y_pred):\n", " smooth=1\n", " y_true_f = K.flatten(y_true)\n", " y_pred_f = K.flatten(y_pred)\n", " intersection = K.sum(K.abs(y_true_f * y_pred_f), axis=-1)\n", " return (2. * intersection + K.epsilon()) / (\n", " K.sum(K.square(y_true_f), -1) + K.sum(K.square(y_pred_f), -1) + K.epsilon())\n", "def dice_coefficient_correct(y_true, y_pred):\n", " y_true_f = K.flatten(y_true)\n", " y_pred_f = K.flatten(y_pred)\n", " intersection = K.sum(K.abs(y_true_f * y_pred_f), axis=-1)\n", " return (2. * intersection) / (K.sum(y_true_f) + K.sum(y_pred_f) + K.epsilon())\n", "def confusion(y_true, y_pred):\n", " smooth=1\n", " y_pred_f = K.flatten(y_pred)\n", " y_pred_pos = K.clip(y_pred_f, 0, 1)\n", " y_pred_neg = 1 - y_pred_pos\n", " y_pos = K.clip(y_true, 0, 1)\n", " y_neg = 1 - y_pos\n", " tp = K.sum(y_pos * y_pred_pos)\n", " fp = K.sum(y_neg * y_pred_pos)\n", " fn = K.sum(y_pos * y_pred_neg) \n", " prec = (tp + smooth)/(tp+fp+smooth)\n", " recall = (tp+smooth)/(tp+fn+smooth)\n", " return prec, recall\n", "\n", "def tp(y_true, y_pred):\n", " smooth = 1\n", " y_true_f = K.flatten(y_true)\n", " y_pred_f = K.flatten(y_pred)\n", " y_pred_pos = K.round(K.clip(y_pred_f, 0, 1))\n", " y_pos = K.round(K.clip(y_true_f, 0, 1))\n", " tp = (K.sum(y_pos * y_pred_pos) + smooth)/ (K.sum(y_pos) + smooth) \n", " return tp \n", "\n", "def tn(y_true, y_pred):\n", " smooth = 1\n", " y_true_f = K.flatten(y_true)\n", " y_pred_f = K.flatten(y_pred)\n", " y_pred_pos = K.round(K.clip(y_pred_f, 0, 1))\n", " y_pred_neg = 1 - y_pred_pos\n", " y_pos = K.round(K.clip(y_true_f, 0, 1))\n", " y_neg = 1 - y_pos \n", " tn = (K.sum(y_neg * y_pred_neg) + smooth) / (K.sum(y_neg) + smooth )\n", " return tn \n", "\n", "def tversky(y_true, y_pred):\n", " smooth = 1\n", " y_true_pos = K.flatten(y_true)\n", " y_pred_pos = K.flatten(y_pred)\n", " true_pos = K.sum(y_true_pos * y_pred_pos)\n", " false_neg = K.sum(y_true_pos * (1-y_pred_pos))\n", " false_pos = K.sum((1-y_true_pos)*y_pred_pos)\n", " alpha = 0.7\n", " return (true_pos + smooth)/(true_pos + alpha*false_neg + (1-alpha)*false_pos + smooth)\n", "\n", "def tversky_loss(y_true, y_pred):\n", " return 1 - tversky(y_true,y_pred)\n", "\n", "def focal_tversky(y_true,y_pred):\n", " pt_1 = tversky(y_true, y_pred)\n", " gamma = 0.75\n", " return K.pow((1-pt_1), gamma)\n", "def focal_l2(inputs,output, e=1e-8, weight_L2=0.1):\n", " \"\"\"\n", " loss(input_shape, inp, e=1e-8, weight_L2=0.1)\n", " ------------------------------------------------------\n", " Since keras does not allow custom loss functions to have arguments\n", " other than the true and predicted labels, this function acts as a wrapper\n", " that allows us to implement the custom loss used in the paper, involving\n", " outputs from multiple layers.\n", " L = - L + weight_L2 ∗ L\n", " - L is the dice loss between input and segmentation output.\n", " - L is the L2 loss between the output and the input.\n", "\n", " Parameters\n", " ----------\n", " `input_shape`: A 4-tuple, required\n", " The shape of an image as the tuple (c, H, W, D), where c is\n", " the no. of channels; H, W and D is the height, width and depth of the\n", " input image, respectively.\n", " `inp`: An keras.layers.Layer instance, required\n", " `e`: Float, optional\n", " A small epsilon term to add in the denominator to avoid dividing by\n", " zero and possible gradient explosion.\n", " `weight_L2`: A real number, optional\n", " The weight to be given to the L2 loss term in the loss function. Adjust to get best\n", " results for your task.\n", "\n", " -------\n", " loss_(y_true, y_pred): A custom keras loss function\n", " This function takes as input the predicted and ground labels, uses them\n", " to calculate the dice loss. Combined with the L + weight_L2 ∗ L\n", " - L is the dice loss between input and segmentation output.\n", " - L is the L2 loss between the output and the input.\n", "\n", " Parameters\n", " ----------\n", " `input_shape`: A 4-tuple, required\n", " The shape of an image as the tuple (c, H, W, D), where c is\n", " the no. of channels; H, W and D is the height, width and depth of the\n", " input image, respectively.\n", " `inp`: An keras.layers.Layer instance, required\n", " `e`: Float, optional\n", " A small epsilon term to add in the denominator to avoid dividing by\n", " zero and possible gradient explosion.\n", " `weight_L2`: A real number, optional\n", " The weight to be given to the L2 loss term in the loss function. Adjust to get best\n", " results for your task.\n", "\n", " -------\n", " loss_(y_true, y_pred): A custom keras loss function\n", " This function takes as input the predicted and ground labels, uses them\n", " to calculate the dice loss. Combined with the L]" ] }, "execution_count": 15, "metadata": { "tags": [] }, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "plt.plot(my_model.history['loss'])\n", "plt.plot(my_model.history['dice_coefficient_correct'])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "JdXlhtZaSgl0" }, "outputs": [], "source": [ "del model" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "LrtJzK1RSkkW" }, "outputs": [], "source": [ "from tensorflow.keras.models import load_model\n", "model_1.load_weights('/gdrive/MyDrive/nima_log/new_log_ISBI_data_3D/mozila_mohammadreza acount/Attention_Unet_3D/7/saved-model-3D-MS-250-0.8007-0.8398.hdf5')\n", "model_2.load_weights('/gdrive/My Drive/nima_log/new_log_ISBI_data_3D/chrome_mahmoodian acount/Attention_Unet_3D/7/saved-model-3D-MS-250-0.8054-0.8175.hdf5')\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "b-hlMTWCQr3D", "outputId": "6947871f-1151-4f34-d135-390812a93356" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "61\n" ] } ], "source": [ "###for testing on ISBI test dataset in 3D models part 1\n", "hf_x = h5py.File('/gdrive/My Drive/data_test_3D_ISBI_61sample.hdf5', 'r')\n", "data_len = hf_x['data_test_3D_ISBI_61sample.hdf5'].shape[0]\n", "print(data_len)\n", "num_test=61" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Kwx7av2UUUAh" }, "outputs": [], "source": [ "###for testing on ISBI test dataset in 3D models part 2(AttUnet version 192)\n", "##################\n", "Test_FLAIR= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/testdata_website/testdata_website/*/preprocessed/*flair_pp.nii')\n", "Test_FLAIR.sort()\n", "list_name=[]\n", "for i,name in enumerate(Test_FLAIR):\n", " _,x=name.rsplit('preprocessed/',1)\n", " y,_,_=x.rsplit('_',2)\n", " z=y+'_JHU.nii'\n", " list_name.append(z)\n", "##################\n", "def zero_padding_3d(volume,size=((29, 30),(11,12),(29,30))):\n", " x= np.pad(volume,pad_width=size,mode='constant', constant_values=0)\n", " return x\n", "#################\n", "for i in range(num_test):\n", " norm_input=[]\n", " for j in range(0,4):\n", " padding=zero_padding_3d(hf_x['data_test_3D_ISBI_61sample.hdf5'][i,:,:,:,j],size=((5, 6),(0,0),(5,6)))\n", " padding=padding[:,12:-13,:]\n", " norm_test= itensity_normalize_one_volume(padding)\n", " norm_input.append(norm_test)\n", " norm_input= np.einsum('CDHW->HWDC',norm_input)\n", " expanded= np.expand_dims(norm_input, axis=0)\n", " pred_1= model_1.predict(expanded)\n", " pred_2= model_2.predict(expanded)\n", " pred_1[pred_1>=0.8]= 1\n", " pred_1[pred_1<0.8]= 0\n", " pred_2[pred_2>=0.8]= 1\n", " pred_2[pred_2<0.8]= 0\n", " pred_final =pred_1 + pred_2\n", " pred_final[pred_final<=1.5]=0\n", " pred_final[pred_final>0.5]= 1\n", " pred_final= np.squeeze(pred_final)\n", " final_mask= np.einsum('HWD->DHW',pred_final)\n", " final_mask= final_mask[5:-6,:,5:-6]\n", " final_mask= zero_padding_3d(final_mask, size=((0,0),(12,13),(0,0)))\n", " final_mask= np.einsum('DHW->WHD',final_mask)\n", " loaded_img= nib.load(Test_FLAIR[i])\n", " img = nib.Nifti1Image(final_mask,affine=loaded_img.affine,header=loaded_img.header,extra=loaded_img.extra,file_map=loaded_img.file_map)\n", " img.to_filename(os.path.join('/gdrive/My Drive/test_Attunet_for_ISBI_site',list_name[i]))\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Zc4mYrCnQs8f" }, "outputs": [], "source": [ "###for testing on ISBI test dataset in 3D models part 2\n", "##################\n", "Test_FLAIR= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/testdata_website/testdata_website/*/preprocessed/*flair_pp.nii')\n", "Test_FLAIR.sort()\n", "list_name=[]\n", "for i,name in enumerate(Test_FLAIR):\n", " _,x=name.rsplit('preprocessed/',1)\n", " y,_,_=x.rsplit('_',2)\n", " z=y+'_JHU.nii'\n", " list_name.append(z)\n", "##################\n", "def zero_padding_3d(volume,size=((29, 30),(11,12),(29,30))):\n", " x= np.pad(volume,pad_width=size,mode='constant', constant_values=0)\n", " return x\n", "#################\n", "for i in range(num_test):\n", " norm_input=[]\n", " for j in range(0,4):\n", " padding=zero_padding_3d(hf_x['data_test_3D_ISBI_61sample.hdf5'][i,:,:,:,j],size=((37, 38),(19,20),(37,38)))\n", " norm_test= itensity_normalize_one_volume(padding)\n", " norm_input.append(norm_test)\n", " norm_input= np.einsum('CDHW->HWDC',norm_input)\n", " expanded= np.expand_dims(norm_input, axis=0)\n", " pred_1= model_1.predict(expanded)\n", " pred_2= model_2.predict(expanded)\n", " pred_1[pred_1>0.5]= 1\n", " pred_1[pred_1<=0.5]= 0\n", " pred_2[pred_2>0.5]= 1\n", " pred_2[pred_2<=0.5]= 0\n", " pred_final =pred_1 + pred_2\n", " pred_final[pred_final<=1]=0\n", " pred_final[pred_final>1]= 1\n", " final_mask= np.squeeze(pred_final)\n", " final_mask= np.einsum('HWD->DHW',final_mask)\n", " final_mask= final_mask[37:-38,19:-20,37:-38]\n", " final_mask= np.einsum('DHW->WHD',final_mask)\n", " loaded_img= nib.load(Test_FLAIR[i])\n", " img = nib.Nifti1Image(final_mask,affine=loaded_img.affine,header=loaded_img.header,extra=loaded_img.extra,file_map=loaded_img.file_map)\n", " img.to_filename(os.path.join('/gdrive/My Drive/test_unet_for_ISBI_site',list_name[i]))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "EuxdPXqYMDfS" }, "outputs": [], "source": [ "###for testing on ISBI test dataset in 3D models part 2(Unet 256 version)\n", "##################\n", "Test_FLAIR= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/testdata_website/testdata_website/*/preprocessed/*flair_pp.nii')\n", "Test_FLAIR.sort()\n", "list_name=[]\n", "for i,name in enumerate(Test_FLAIR):\n", " _,x=name.rsplit('preprocessed/',1)\n", " y,_,_=x.rsplit('_',2)\n", " z=y+'_JHU.nii'\n", " list_name.append(z)\n", "##################\n", "def zero_padding_3d(volume,size=((29, 30),(11,12),(29,30))):\n", " x= np.pad(volume,pad_width=size,mode='constant', constant_values=0)\n", " return x\n", "#################\n", "for i in range(num_test):\n", " norm_input=[]\n", " for j in range(0,4):\n", " padding=zero_padding_3d(hf_x['data_test_3D_ISBI_61sample.hdf5'][i,:,:,:,j],size=((37, 38),(19,20),(37,38)))\n", " norm_test= itensity_normalize_one_volume(padding)\n", " norm_input.append(norm_test)\n", " norm_input= np.einsum('CDHW->HWDC',norm_input)\n", " expanded= np.expand_dims(norm_input, axis=0)\n", " pred_1= model_1.predict(expanded)\n", " pred_2= model_2.predict(expanded)\n", " pred_1[pred_1>=0.8]= 1\n", " pred_1[pred_1<0.8]= 0\n", " pred_2[pred_2>=0.8]= 1\n", " pred_2[pred_2<0.8]= 0\n", " pred_final =pred_1 + pred_2\n", " norm_input=[]\n", " expanded=[]\n", " pred_1=[]\n", " pred_2=[]\n", " pred_final[pred_final<=1.5]=0\n", " pred_final[pred_final>0.5]= 1\n", " final_mask= np.squeeze(pred_final)\n", " pred_final=[]\n", " final_mask= np.einsum('HWD->DHW',final_mask)\n", " final_mask= final_mask[37:-38,19:-20,37:-38]\n", " final_mask= np.einsum('DHW->WHD',final_mask)\n", " loaded_img= nib.load(Test_FLAIR[i])\n", " img = nib.Nifti1Image(final_mask,affine=loaded_img.affine,header=loaded_img.header,extra=loaded_img.extra,file_map=loaded_img.file_map)\n", " img.to_filename(os.path.join('/gdrive/My Drive/test_unet_for_ISBI_site',list_name[i]))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ppXhmFIxYwMs" }, "outputs": [], "source": [ "###for testing on ISBI test dataset in 3D models part 2(nestedUnet version 192)\n", "##################\n", "Test_FLAIR= glob.glob('/gdrive/My Drive/main dataset ISBI 2015/testdata_website/testdata_website/*/preprocessed/*flair_pp.nii')\n", "Test_FLAIR.sort()\n", "list_name=[]\n", "for i,name in enumerate(Test_FLAIR):\n", " _,x=name.rsplit('preprocessed/',1)\n", " y,_,_=x.rsplit('_',2)\n", " z=y+'_JHU.nii'\n", " list_name.append(z)\n", "##################\n", "def zero_padding_3d(volume,size=((29, 30),(11,12),(29,30))):\n", " x= np.pad(volume,pad_width=size,mode='constant', constant_values=0)\n", " return x\n", "#################\n", "for i in range(num_test):\n", " norm_input=[]\n", " for j in range(0,4):\n", " padding=zero_padding_3d(hf_x['data_test_3D_ISBI_61sample.hdf5'][i,:,:,:,j],size=((5, 6),(0,0),(5,6)))\n", " padding=padding[:,12:-13,:]\n", " norm_test= itensity_normalize_one_volume(padding)\n", " norm_input.append(norm_test)\n", " norm_input= np.einsum('CDHW->HWDC',norm_input)\n", " expanded= np.expand_dims(norm_input, axis=0)\n", " pred_1= model_1.predict(expanded)\n", " pred_2= model_2.predict(expanded)\n", " pred_1[pred_1>=0.8]= 1\n", " pred_1[pred_1<0.8]= 0\n", " pred_2[pred_2>=0.8]= 1\n", " pred_2[pred_2<0.8]= 0\n", " pred_final =pred_1 + pred_2\n", " norm_input=[]\n", " expanded=[]\n", " pred_1=[]\n", " pred_2=[]\n", " pred_final[pred_final<=1.5]=0\n", " pred_final[pred_final>0.5]= 1\n", " final_mask= np.squeeze(pred_final)\n", " pred_final=[]\n", " final_mask= np.einsum('HWD->DHW',final_mask)\n", " final_mask= final_mask[5:-6,:,5:-6]\n", " final_mask= zero_padding_3d(final_mask, size=((0,0),(12,13),(0,0)))\n", " final_mask= np.einsum('DHW->WHD',final_mask)\n", " loaded_img= nib.load(Test_FLAIR[i])\n", " img = nib.Nifti1Image(final_mask,affine=loaded_img.affine,header=loaded_img.header,extra=loaded_img.extra,file_map=loaded_img.file_map)\n", " img.to_filename(os.path.join('/gdrive/My Drive/test_Nestunet_for_ISBI_site',list_name[i]))" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "BQRRpphwSoWC" }, "outputs": [], "source": [ "# K.set_learning_phase(0)\n", "model.evaluate_generator(valset_generator(1),steps=279,verbose=1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "obI0M-Z1SrAA" }, "outputs": [], "source": [ "# K.set_learning_phase(0)\n", "model.evaluate_generator(valset_generator(9),steps=31,verbose=1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "qp29muYqSt2r" }, "outputs": [], "source": [ "def check_preds(ypred, ytrue):\n", " smooth = np.finfo(float).eps\n", " pred = np.ndarray.flatten(np.clip(ypred,0,1))\n", " gt = np.ndarray.flatten(np.clip(ytrue,0,1))\n", " intersection = np.sum(pred * gt) \n", " union = np.sum(pred) + np.sum(gt) \n", " return np.round((2 * intersection + smooth)/(union + smooth),decimals=5)\n", "def auc(y_true, y_pred):\n", " smooth = 0.0000001\n", " y_pred_pos = np.round(np.clip(y_pred, 0, 1))\n", " y_pred_neg = 1 - y_pred_pos\n", " y_pos = np.round(np.clip(y_true, 0, 1))\n", " y_neg = 1 - y_pos\n", " tp = np.sum(y_pos * y_pred_pos)\n", " tn = np.sum(y_neg * y_pred_neg)\n", " fp = np.sum(y_neg * y_pred_pos)\n", " fn = np.sum(y_pos * y_pred_neg)\n", " tpr = (tp + smooth) / (tp + fn + smooth) #recall(sensitivity)\n", " tnr = (tn + smooth) / (tn + fp + smooth) #specificity\n", " prec = (tp + smooth) / (tp + fp + smooth) #precision\n", " fpr = (fp + smooth) / (fp + tn + smooth)\n", " return [tpr, tnr, prec, fpr]" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "YSGMYx1ASweD" }, "outputs": [], "source": [ "################################################################################\n", "###for validation\n", "hf_x_val = h5py.File('/gdrive/My Drive/data_val_ISBI_just_ms_slice.hdf5', 'r')\n", "hf_y_val = h5py.File('/gdrive/My Drive/label_val_ISBI_just_ms_slice.hdf5', 'r')\n", "hf_x_train = h5py.File('/gdrive/My Drive/data_train_ISBI_just_ms_slice.hdf5', 'r')\n", "hf_y_train = h5py.File('/gdrive/My Drive/label_train_ISBI_just_ms_slice.hdf5', 'r')\n", "print(np.shape(hf_x_val['data_val_ISBI_just_ms_slice'][0,:,:,:]))\n", "print(np.shape(hf_y_val['label_val_ISBI_just_ms_slice'][0,:,:,:]))\n", "print(np.shape(hf_x_train['data_train_ISBI_just_ms_slice'][0,:,:,:]))\n", "print(np.shape(hf_y_train['label_train_ISBI_just_ms_slice'][0,:,:,:]))\n", "num_val=279\n", "dsc = np.zeros((num_val,1))\n", "preds = np.zeros((num_val,240,240,1))\n", "gt = np.zeros((num_val,240,240,1))\n", "tpr = np.zeros_like(dsc)\n", "tn = np.zeros_like(dsc)\n", "prec = np.zeros_like(dsc)\n", "tnr = np.zeros_like(dsc)\n", "fpr = np.zeros_like(dsc)" ] } ], "metadata": { "accelerator": "GPU", "colab": { "provenance": [] }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" } }, "nbformat": 4, "nbformat_minor": 1 } ================================================ FILE: README.md ================================================ # Segmentation of Multiple Sclerosis Disease Plaques from MRI Images Using 3D Attention U-Net ## Overview This repository contains the implementation of a 3D Attention U-Net model for segmenting Multiple Sclerosis (MS) disease plaques from MRI images. The model is designed to accurately segment lesions and track their evolution over time, addressing the unique challenges presented by MS lesion segmentation in longitudinal MRI datasets. ## Dataset The model was trained and tested on the dataset from the *Longitudinal MS Lesion Segmentation Challenge*, conducted at the 2015 International Symposium on Biomedical Imaging in New York, NY. The challenge involved applying automatic lesion segmentation algorithms to MR neuroimaging data acquired at multiple time points from MS patients. ### Reference - Carass A, et al. Longitudinal multiple sclerosis lesion segmentation: Resource and challenge. Neuroimage. 2017;148:77-102. [Paper Link](https://doi.org/10.1016/j.neuroimage.2016.12.064) ### Challenge Details - The challenge focused on evaluating the segmentation accuracy of algorithms against manual segmentations and their ability to track lesion evolution. - Data included MR neuroimaging from multiple time points, providing a dynamic view of lesion development in MS patients. ## Model Performance The model achieved a performance metric of 92.871 on the test set, demonstrating its effectiveness in segmenting MS lesions with high accuracy. ## Model Overview The `attn_unet_3D` function defines an Attention U-Net model for 3D image data, particularly suited for tasks like volumetric image segmentation. This architecture is a variant of the standard U-Net model, augmented with attention gates to focus on salient features, enhancing the model's performance in applications like medical image analysis. ## Architecture Details ### Input Layer - **Input**: The model starts with an input layer that accepts 3D data. ### Downsampling Path (Encoder) - **Convolutional Blocks**: Each block in this path consists of two 3D convolutional layers with ReLU activation, followed by batch normalization. - **Number of Filters**: The number of filters doubles at each level (32, 64, 128, 256), allowing the network to capture more complex features. - **Pooling**: Each convolutional block is followed by a 3D Max Pooling layer to reduce the spatial dimensions. ### Center Block - **Convolution**: A central convolutional block with 512 filters processes the data at the lowest resolution. ### Attention Gates - **Function**: Attention gates are applied before each upsampling step. They aim to enhance relevant features and suppress irrelevant ones for better segmentation accuracy. - **Components**: These gates involve operations like convolutions, activations, and upsampling to generate a gating signal. ### Upsampling Path (Decoder) - **Transposed Convolutions**: The model uses 3D transposed convolutions to increase the spatial resolution of feature maps. - **Concatenation**: The transposed feature maps are concatenated with corresponding cropped feature maps from the downsampling path. - **Filters**: The number of filter halves with each upsampling step (256, 128, 64, 32). ### Output Layer - **Final Convolution**: A 3D convolutional layer with a single filter and a sigmoid activation function generates the final output, typically a segmented 3D image. ![image](https://github.com/user-attachments/assets/81436efc-f122-4f89-887d-52712f982802) ## Compilation Details ### Loss Function - **Focal Tversky Loss**: A custom loss function that is a variant of the Tversky loss, adapted to focus more on learning from difficult examples. It's particularly useful for dealing with class imbalance, which is common in medical image segmentation. ### Metric - **Dice Coefficient Correct**: This metric is likely a variant of the Dice coefficient, a common metric in image segmentation tasks, especially for evaluating performance in medical imaging. - ## Comparative Visualization of MRI Slice and Corresponding Segmentation Outputs ![image](https://github.com/AliAmini93/MRI-MS-Plaques-Segmentation/assets/96921261/a9607f7a-4ee6-4173-90d7-9bfefb0ae7c7)