{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Kidney Segmentation Starter Notebook"},{"metadata":{},"cell_type":"markdown","source":"# Anatomies"},{"metadata":{},"cell_type":"markdown","source":"## Kidney Anatomy\n\n<img align='left' width=600, heigh=800, src='https://image.noelshack.com/fichiers/2020/47/3/1605712133-cross-section-kidney-blood-ves.png'>"},{"metadata":{},"cell_type":"markdown","source":"## Glomerulus anatomy\n\n<img align='left' width=600, heigh=800, src='https://upload.wikimedia.org/wikipedia/commons/thumb/6/69/Renal_corpuscle-en.svg/600px-Renal_corpuscle-en.svg.png'>"},{"metadata":{},"cell_type":"markdown","source":"For more medical information, read this awesome [Raphael Bourgade Notebook](https://www.kaggle.com/raphaelbourgade/all-the-medical-data-you-need-to-know)"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\nfrom tqdm import tqdm\n\nimport tifffile as tiff\nimport matplotlib.pyplot as plt\n\nfrom numpy import savez_compressed\nfrom numpy import load\n\nimport cv2","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Prepare Pandas Dataframes"},{"metadata":{"trusted":true},"cell_type":"code","source":"main_path = '../input/hubmap-kidney-segmentation/'\ntrain_path = main_path + 'train/'","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"# train data\ndf = pd.read_csv(main_path + 'train.csv')\n\n# patient data\ndf_info = pd.read_csv(main_path + '/HuBMAP-20-dataset_information.csv')\n\n# intersection of train data with patient data\ntrain_ds = df_info[df_info.image_file.isin((df.id + '.tiff').values)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_masks = pd.read_csv('../input/hubmap-kidney-segmentation/train.csv').set_index('id')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Image Class"},{"metadata":{},"cell_type":"markdown","source":"Images are split into 256x256 tiles and same as the masks.<br>\nEach patient kidney images/masks are saved into 'mask/img_id.npz'<br>\nThat way we take care of the Kaggle RAM to not go over the limit.<br>\nThanks to the [iafoss notebook](https://www.kaggle.com/iafoss/256x256-images), a notebook that shows how to split the images into 256x256 tiles."},{"metadata":{"trusted":true},"cell_type":"code","source":"class Image():\n    '''\n        Image\n    '''\n    def __init__(self, ds, df_mask, path):\n        self.image_file_name = ds['image_file']\n        self.width_pixels = ds['width_pixels']\n        self.height_pixels = ds['height_pixels']\n        self.path = path\n        \n        self.idx = self.image_file_name.split('.tiff')[0]\n        self.df_mask = df_mask\n\n        self.path_mask = path.split('train/')[0] + 'train.csv'\n        self.df_masks = pd.read_csv(self.path_mask).set_index('id')\n        \n        # size of tiles\n        self.sz = 256\n        # reduce the original images by 4 times\n        self.reduce = 4\n        \n        # save images and masks\n        self.save_images_masks()\n        \n    def enc2mask(self, encs, shape):\n        '''\n            Encode 2 Mask\n        '''\n        img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n        for m,enc in enumerate(encs):\n            if isinstance(enc,np.float) and np.isnan(enc): continue\n            s = enc.split()\n            for i in range(len(s)//2):\n                start = int(s[2*i]) - 1\n                length = int(s[2*i+1])\n                img[start:start+length] = 1 + m\n                \n        return img.reshape(shape).T\n\n    def mask2enc(self, mask, n=1):\n        '''\n            Mask 2 Encoding\n        '''\n        pixels = mask.T.flatten()\n        encs = []\n        for i in range(1,n+1):\n            p = (pixels == i).astype(np.int8)\n            if p.sum() == 0: encs.append(np.nan)\n            else:\n                p = np.concatenate([[0], p, [0]])\n                runs = np.where(p[1:] != p[:-1])[0] + 1\n                runs[1::2] -= runs[::2]\n                encs.append(' '.join(str(x) for x in runs))\n                \n        return encs\n    \n    def add_padding(self, img, mask):\n        shape = img.shape\n        reduce, sz = self.reduce, self.sz\n        pad0 = (reduce*sz - shape[0]%(reduce*sz))%(reduce*sz)\n        pad1 = (reduce*sz - shape[1]%(reduce*sz))%(reduce*sz)\n        img = np.pad(img,[[pad0//2,pad0-pad0//2],[pad1//2,pad1-pad1//2],[0,0]],\n                    constant_values=0)\n        mask = np.pad(mask,[[pad0//2,pad0-pad0//2],[pad1//2,pad1-pad1//2]],\n                    constant_values=0)\n        \n        return img, mask\n    \n    def split_into_tiles(self, img, mask):\n        reduce, sz = self.reduce, self.sz\n        img = cv2.resize(img,(img.shape[1]//reduce,img.shape[0]//reduce),\n                         interpolation = cv2.INTER_AREA)\n        img = img.reshape(img.shape[0]//sz,sz,img.shape[1]//sz,sz,3)\n        img = img.transpose(0,2,1,3,4).reshape(-1,sz,sz,3)\n\n        mask = cv2.resize(mask,(mask.shape[1]//reduce,mask.shape[0]//reduce),\n                          interpolation = cv2.INTER_NEAREST)\n        mask = mask.reshape(mask.shape[0]//sz,sz,mask.shape[1]//sz,sz)\n        mask = mask.transpose(0,2,1,3).reshape(-1,sz,sz)\n        \n        return img, mask\n    \n    def save_images_masks(self):\n        idx = self.idx\n        image_path = self.path + self.df_masks[self.df_masks.index == idx].index[0] + '.tiff'\n        encs = self.df_masks[self.df_masks.index == idx].encoding\n        \n        img = tiff.imread(image_path)\n        if len(img.shape) == 5:img = np.transpose(img.squeeze(), (1,2,0))\n        # read image and generate the mask\n        mask = self.enc2mask(encs,(img.shape[1],img.shape[0]))\n        \n        img, mask = self.add_padding(img, mask)\n        img, mask = self.split_into_tiles(img, mask)\n        \n        savez_compressed('img_'+ idx +'.npz', img)\n        savez_compressed('mask_'+ idx +'.npz', mask)\n        \n    def get_images(self):\n        img_path = 'img_' + self.idx + '.npz'\n        dict_data = load(img_path)\n        return dict_data['arr_0']\n    \n    def get_masks(self):\n        msk_path = 'mask_' + self.idx + '.npz'\n        dict_data = load(msk_path)\n        return dict_data['arr_0']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Patient Class"},{"metadata":{"trusted":true},"cell_type":"code","source":"class Glomerulus():\n    '''\n        Glomerulus\n    '''\n    def __init__(self, ds):\n        self.type = ds['type']\n        self.id = ds['id']\n        self.geometry = ds['geometry']\n        self.properties = ds['properties']\n\nclass Anatomical():\n    '''\n        Anatomical Structures\n    '''\n    def __init__(self, ds):\n        self.type = ds['type']\n        self.id = ds['id']\n        self.geometry = ds['geometry']\n        self.properties = ds['properties']\n\nclass Patient():\n    '''\n        Contains the main class\n    '''\n    def __init__(self, ds, ds_masks, path):\n        self.patient_number = ds['patient_number']\n        self.race = ds['race']\n        self.ethnicity = ds['ethnicity']\n        self.sex = ds['sex']\n        self.age = ds['age']\n        self.weight_kilograms = ds['weight_kilograms']\n        self.height_centimeters = ds['height_centimeters']\n        self.bmi_kg = ds['bmi_kg/m^2']\n        self.laterality = ds['laterality']\n        self.percent_cortex = ds['percent_cortex']\n        self.percent_medulla = ds['percent_medulla']\n        \n        self.image = Image(ds, df_masks, path)\n        \n        self.anatomical_structures_segmention_file = ds['anatomical_structures_segmention_file']\n        self.glomerulus_segmentation_file = ds['glomerulus_segmentation_file']\n        \n        self.anatomicals = []\n        ds_anatomicalies = pd.read_json(path  + self.anatomical_structures_segmention_file)\n        for i in range(len(ds_anatomicalies)):\n            self.anatomicals.append(Anatomical(ds_anatomicalies.iloc[i]))\n            \n        self.glomeruluses = []\n        ds_glomerulus = pd.read_json(path + self.glomerulus_segmentation_file)\n        for i in range(len(ds_glomerulus)):\n            self.glomeruluses.append(Glomerulus(ds_glomerulus.iloc[i]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df_masks = pd.read_csv('../input/hubmap-kidney-segmentation/train.csv').set_index('id')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"patients = [Patient(train_ds.iloc[i], df_masks, train_path) for i in tqdm(range(len(train_ds)), desc=\"Creating images/masks\", ncols=100, bar_format='{l_bar}{bar}|')]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Display a Patient Kidney Image/Mask tiles"},{"metadata":{"trusted":true},"cell_type":"code","source":"# load a patient images\nimages = patients[1].image.get_images()\nimages.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"f, axarr = plt.subplots(1,10, figsize=(20,20))\nfor i in range(400,410):\n    axarr[i - 400].imshow(images[i])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# load a patient masks\nmasks = patients[1].image.get_masks()\nmasks.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"f, axarr = plt.subplots(1,10, figsize=(20,20))\nfor i in range(400,410):\n    axarr[i - 400].imshow(masks[i])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Conclusion"},{"metadata":{},"cell_type":"markdown","source":"Using 256x256 and numpy .npz temp files for images/masks, we could use images as features and masks as labels.<br>\nOne of the easiest, machine learning starting architecture to look into is the U-Net model."}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}