{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<center><h1>Sartorius - Cell Instance Segmentation.</h1></center>\n\n![img](https://storage.googleapis.com/kaggle-competitions/kaggle/30201/logos/header.png)","metadata":{}},{"cell_type":"markdown","source":"### **Semantic Segmentation: each pixel of an image is linked to a class label.**\n\n![img](https://raw.githubusercontent.com/WaterKnight1998/SemTorch/develop/readme_images/semantic_segmentation.png)\n\n### **Instance Segmentation: is similar to semantic segmentation, but goes a bit deeper, it identifies , for each pixel, the object instance it belongs to.**\n\n![img](https://raw.githubusercontent.com/WaterKnight1998/SemTorch/develop/readme_images/instance_segmentation.png)","metadata":{}},{"cell_type":"markdown","source":"## Please don't forget to Upvote if like the work.\n\n### if you fork the notebook please try to upvote it too.\n**It keeps me motivated**\n\n","metadata":{}},{"cell_type":"code","source":"from fastai.vision.all import *\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport numpy as np\nfrom tqdm.notebook import tqdm\nimport seaborn as sns\nimport matplotlib.image as immg\nfrom joblib import Parallel, delayed\nimport PIL,cv2,gc,os,sys,torch","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:40.478217Z","iopub.execute_input":"2021-11-26T13:22:40.478862Z","iopub.status.idle":"2021-11-26T13:22:41.448906Z","shell.execute_reply.started":"2021-11-26T13:22:40.478759Z","shell.execute_reply":"2021-11-26T13:22:41.448165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"Path('/root/.cache/torch/hub/checkpoints/').mkdir(exist_ok=True, parents=True)\n!cp '../input/resnet34/resnet34.pth' '/root/.cache/torch/hub/checkpoints/resnet34-b627a593.pth' \n!cp \"../input/resnet50/resnet50.pth\" '/root/.cache/torch/hub/checkpoints/resnet50-0676ba61.pth'\n!cp '../input/resnet18/resnet18.pth' '/root/.cache/torch/hub/checkpoints/resnet18-f37072fd.pth' ","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:41.450401Z","iopub.execute_input":"2021-11-26T13:22:41.450646Z","iopub.status.idle":"2021-11-26T13:22:44.429924Z","shell.execute_reply.started":"2021-11-26T13:22:41.450611Z","shell.execute_reply":"2021-11-26T13:22:44.428935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data already broken down into 2x2 tiles to increase dataset, and faster training\n\nhttps://www.kaggle.com/robertlangdonvinci/sartorius-cell-segmentation-data-gen/notebook","metadata":{}},{"cell_type":"markdown","source":"### Since data is broked into 2x2 tile some tiles contain no masks we will clean them up.","metadata":{}},{"cell_type":"code","source":"path = Path('../input/sartoriuscellinstancesegmentationmaskpng')","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:44.432088Z","iopub.execute_input":"2021-11-26T13:22:44.432335Z","iopub.status.idle":"2021-11-26T13:22:44.435994Z","shell.execute_reply.started":"2021-11-26T13:22:44.432306Z","shell.execute_reply":"2021-11-26T13:22:44.435308Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def label_func(fn): return f\"/kaggle/input/sartoriuscellinstancesegmentationmaskpng/TrainMask2x2/{fn.stem}_mask.png\"","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:44.437522Z","iopub.execute_input":"2021-11-26T13:22:44.437959Z","iopub.status.idle":"2021-11-26T13:22:45.310405Z","shell.execute_reply.started":"2021-11-26T13:22:44.437903Z","shell.execute_reply":"2021-11-26T13:22:45.309429Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_files = get_image_files(path/'TrainImage2x2')","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:45.313587Z","iopub.execute_input":"2021-11-26T13:22:45.314377Z","iopub.status.idle":"2021-11-26T13:22:45.644334Z","shell.execute_reply.started":"2021-11-26T13:22:45.314258Z","shell.execute_reply":"2021-11-26T13:22:45.643597Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_files_clean = [] \nfor f in tqdm(img_files):\n    loc = label_func(f)\n    img = np.unique(np.array(Image.open(loc)))\n    if len(img)!=1:\n        img_files_clean.append(f)","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:45.645776Z","iopub.execute_input":"2021-11-26T13:22:45.646025Z","iopub.status.idle":"2021-11-26T13:22:50.874724Z","shell.execute_reply.started":"2021-11-26T13:22:45.64599Z","shell.execute_reply":"2021-11-26T13:22:50.873951Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(len(img_files))\nprint(len(img_files_clean))","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:50.87598Z","iopub.execute_input":"2021-11-26T13:22:50.876842Z","iopub.status.idle":"2021-11-26T13:22:50.884966Z","shell.execute_reply.started":"2021-11-26T13:22:50.876801Z","shell.execute_reply":"2021-11-26T13:22:50.884257Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_files = img_files_clean ","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:50.886171Z","iopub.execute_input":"2021-11-26T13:22:50.886929Z","iopub.status.idle":"2021-11-26T13:22:50.899543Z","shell.execute_reply.started":"2021-11-26T13:22:50.88689Z","shell.execute_reply":"2021-11-26T13:22:50.898814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"n = np.random.randint(0,100)\nimg = PIL.Image.open(img_files[n])\nmask = PIL.Image.open(label_func(img_files[n]))","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:50.900654Z","iopub.execute_input":"2021-11-26T13:22:50.900975Z","iopub.status.idle":"2021-11-26T13:22:50.917545Z","shell.execute_reply.started":"2021-11-26T13:22:50.900933Z","shell.execute_reply":"2021-11-26T13:22:50.916918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(1,figsize=(18,8))\nplt.subplot(121)\nplt.imshow(img)\nplt.title('raw image')\nplt.subplot(122)\nplt.imshow(img)\nplt.imshow(mask,alpha=0.5);\nplt.title('image + mask');","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:50.91859Z","iopub.execute_input":"2021-11-26T13:22:50.918899Z","iopub.status.idle":"2021-11-26T13:22:51.492395Z","shell.execute_reply.started":"2021-11-26T13:22:50.918863Z","shell.execute_reply":"2021-11-26T13:22:51.491245Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_path = Path('../input/sartoriuscellinstancesegmentationmaskpng/TrainImage2x2')","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:51.494898Z","iopub.execute_input":"2021-11-26T13:22:51.495768Z","iopub.status.idle":"2021-11-26T13:22:51.499734Z","shell.execute_reply.started":"2021-11-26T13:22:51.495717Z","shell.execute_reply":"2021-11-26T13:22:51.499125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_classes(fnames):\n    class_codes=[]\n    for i in tqdm(range(len(fnames))):\n        class_codes += list(np.unique(np.asarray(Image.open(label_func(fnames[i])))))\n    return np.array(list(set(class_codes)))","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:51.501288Z","iopub.execute_input":"2021-11-26T13:22:51.501808Z","iopub.status.idle":"2021-11-26T13:22:51.510124Z","shell.execute_reply.started":"2021-11-26T13:22:51.501772Z","shell.execute_reply":"2021-11-26T13:22:51.509255Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"codes = get_classes(img_files);codes","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:51.511517Z","iopub.execute_input":"2021-11-26T13:22:51.512103Z","iopub.status.idle":"2021-11-26T13:22:57.055665Z","shell.execute_reply.started":"2021-11-26T13:22:51.512063Z","shell.execute_reply":"2021-11-26T13:22:57.05498Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"mask.shape","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:57.057178Z","iopub.execute_input":"2021-11-26T13:22:57.057652Z","iopub.status.idle":"2021-11-26T13:22:57.063295Z","shell.execute_reply.started":"2021-11-26T13:22:57.057614Z","shell.execute_reply":"2021-11-26T13:22:57.062574Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_files_big = get_image_files('../input/sartorius-cell-instance-segmentation/train')","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:57.064675Z","iopub.execute_input":"2021-11-26T13:22:57.065149Z","iopub.status.idle":"2021-11-26T13:22:57.083359Z","shell.execute_reply.started":"2021-11-26T13:22:57.065101Z","shell.execute_reply":"2021-11-26T13:22:57.082687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Creating A dataloader","metadata":{}},{"cell_type":"code","source":"def label_func2(fn): \n    fn = Path(fn)\n    img = np.array(Image.open(f\"../input/sartoriuscellinstancesegmentationmaskpng/TrainMask2x2/{fn.stem}_mask.png\"))\n    img = img.clip(0,1)\n    return img","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:57.085372Z","iopub.execute_input":"2021-11-26T13:22:57.085549Z","iopub.status.idle":"2021-11-26T13:22:57.089845Z","shell.execute_reply.started":"2021-11-26T13:22:57.085527Z","shell.execute_reply":"2021-11-26T13:22:57.088871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def label_func_big(fn): \n    fn = Path(fn)\n    img = np.array(Image.open(f\"../input/cell-train-mask-big/{fn.stem}.png\"))\n    img = img.clip(0,1)\n    return img","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:57.091312Z","iopub.execute_input":"2021-11-26T13:22:57.09164Z","iopub.status.idle":"2021-11-26T13:22:57.098598Z","shell.execute_reply.started":"2021-11-26T13:22:57.091596Z","shell.execute_reply":"2021-11-26T13:22:57.097365Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dls = SegmentationDataLoaders.from_label_func(img_path, bs=12, \n                                                    fnames = img_files,\n                                                    label_func = label_func2, \n                                                    codes = [0,1])","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:25:54.866552Z","iopub.execute_input":"2021-11-26T13:25:54.867328Z","iopub.status.idle":"2021-11-26T13:25:54.920766Z","shell.execute_reply.started":"2021-11-26T13:25:54.867285Z","shell.execute_reply":"2021-11-26T13:25:54.920081Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dls.show_batch(max_n=8,figsize=(20,8))","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:25:55.852835Z","iopub.execute_input":"2021-11-26T13:25:55.853594Z","iopub.status.idle":"2021-11-26T13:25:56.781516Z","shell.execute_reply.started":"2021-11-26T13:25:55.853553Z","shell.execute_reply":"2021-11-26T13:25:56.780871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"len(dls.train_ds),len(dls.valid_ds)","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:59.56597Z","iopub.execute_input":"2021-11-26T13:22:59.566438Z","iopub.status.idle":"2021-11-26T13:22:59.572294Z","shell.execute_reply.started":"2021-11-26T13:22:59.566402Z","shell.execute_reply":"2021-11-26T13:22:59.571548Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"name2id = {v:k for k,v in enumerate(codes)}\nvoid_code = -1\n\ndef cell_mask_accuracy(input, target):\n    target = target.squeeze(1)\n    mask = target != void_code\n    return (input.argmax(dim=1)[mask]==target[mask]).float().mean()","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:59.573778Z","iopub.execute_input":"2021-11-26T13:22:59.574306Z","iopub.status.idle":"2021-11-26T13:22:59.58114Z","shell.execute_reply.started":"2021-11-26T13:22:59.57427Z","shell.execute_reply":"2021-11-26T13:22:59.580362Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"acc = cell_mask_accuracy","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:59.582662Z","iopub.execute_input":"2021-11-26T13:22:59.58322Z","iopub.status.idle":"2021-11-26T13:22:59.591939Z","shell.execute_reply.started":"2021-11-26T13:22:59.583182Z","shell.execute_reply":"2021-11-26T13:22:59.591258Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## IoU metrics","metadata":{}},{"cell_type":"code","source":"# https://forums.fast.ai/t/multi-class-semantic-segmentation-metrics-and-accuracy/74665/4\n# Return Jaccard index, or Intersection over Union (IoU) value\ndef IoU(preds:Tensor, targs:Tensor, eps:float=1e-8):\n    \"\"\"Computes the Jaccard loss, a.k.a the IoU loss.\n    Notes: [Batch size,Num classes,Height,Width]\n    Args:\n        targs: a tensor of shape [B, H, W] or [B, 1, H, W].\n        preds: a tensor of shape [B, C, H, W]. Corresponds to\n            the raw output or logits of the model. (prediction)\n        eps: added to the denominator for numerical stability.\n    Returns:\n        iou: the average class intersection over union value \n             for multi-class image segmentation\n    \"\"\"\n    num_classes = preds.shape[1]\n    \n    # Single class segmentation?\n    if num_classes == 1:\n        true_1_hot = torch.eye(num_classes + 1)[targs.squeeze(1)]\n        true_1_hot = true_1_hot.permute(0, 3, 1, 2).float()\n        true_1_hot_f = true_1_hot[:, 0:1, :, :]\n        true_1_hot_s = true_1_hot[:, 1:2, :, :]\n        true_1_hot = torch.cat([true_1_hot_s, true_1_hot_f], dim=1)\n        pos_prob = torch.sigmoid(preds)\n        neg_prob = 1 - pos_prob\n        probas = torch.cat([pos_prob, neg_prob], dim=1)\n        \n    # Multi-class segmentation\n    else:\n        # Convert target to one-hot encoding\n        # true_1_hot = torch.eye(num_classes)[torch.squeeze(targs,1)]\n        true_1_hot = torch.eye(num_classes)[targs.squeeze(1)]\n        \n        # Permute [B,H,W,C] to [B,C,H,W]\n        true_1_hot = true_1_hot.permute(0, 3, 1, 2).float()\n        \n        # Take softmax along class dimension; all class probs add to 1 (per pixel)\n        probas = F.softmax(preds, dim=1)\n        \n    true_1_hot = true_1_hot.type(preds.type())\n    \n    # Sum probabilities by class and across batch images\n    dims = (0,) + tuple(range(2, targs.ndimension()))\n    intersection = torch.sum(probas * true_1_hot, dims) # [class0,class1,class2,...]\n    cardinality = torch.sum(probas + true_1_hot, dims)  # [class0,class1,class2,...]\n    union = cardinality - intersection\n    iou = (intersection / (union + eps)).mean()   # find mean of class IoU values\n    return iou","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:22:59.59352Z","iopub.execute_input":"2021-11-26T13:22:59.593792Z","iopub.status.idle":"2021-11-26T13:22:59.606342Z","shell.execute_reply.started":"2021-11-26T13:22:59.593758Z","shell.execute_reply":"2021-11-26T13:22:59.605617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Creating a UNet Learner","metadata":{}},{"cell_type":"markdown","source":"**This module builds a dynamic U-Net from any backbone pretrained on ImageNet, automatically inferring the intermediate sizes.**\n\n***\n\n![dynamicUnet](https://fastai1.fast.ai/imgs/u-net-architecture.png)\n\n***\n\n**This is the original U-Net. The difference here is that the left part is a pretrained model.**\n\nhttps://fastai1.fast.ai/vision.models.unet.html","metadata":{}},{"cell_type":"code","source":"learn = unet_learner(dls, resnet34,  model_dir='/kaggle/working/',metrics=[acc,Dice(),IoU]).to_fp16()","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:26:03.003523Z","iopub.execute_input":"2021-11-26T13:26:03.00379Z","iopub.status.idle":"2021-11-26T13:26:04.830315Z","shell.execute_reply.started":"2021-11-26T13:26:03.003759Z","shell.execute_reply":"2021-11-26T13:26:04.829543Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* Launch a mock training to find a good learning rate","metadata":{}},{"cell_type":"code","source":"learn.lr_find()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:26:08.192862Z","iopub.execute_input":"2021-11-26T13:26:08.193248Z","iopub.status.idle":"2021-11-26T13:26:08.343711Z","shell.execute_reply.started":"2021-11-26T13:26:08.193211Z","shell.execute_reply":"2021-11-26T13:26:08.342925Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* Start your training","metadata":{}},{"cell_type":"code","source":"cb1 = SaveModelCallback(monitor='IoU',fname='best_model',comp=np.greater) # Callbacks\ncb2 = ReduceLROnPlateau(monitor='IoU', patience=1,factor=0.2)\nlearn.fit_one_cycle(2, 1e-3,cbs = [cb1,cb2])","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:26:12.880237Z","iopub.execute_input":"2021-11-26T13:26:12.880862Z","iopub.status.idle":"2021-11-26T13:29:16.41963Z","shell.execute_reply.started":"2021-11-26T13:26:12.880814Z","shell.execute_reply":"2021-11-26T13:29:16.41881Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dls_big = SegmentationDataLoaders.from_label_func(img_path, bs=4, \n                                              fnames = img_files_big,\n                                              label_func = label_func_big, \n                                              codes = [0,1])","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:29:41.924459Z","iopub.execute_input":"2021-11-26T13:29:41.925111Z","iopub.status.idle":"2021-11-26T13:29:41.997836Z","shell.execute_reply.started":"2021-11-26T13:29:41.925033Z","shell.execute_reply":"2021-11-26T13:29:41.996971Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.dls = dls_big","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:29:44.887591Z","iopub.execute_input":"2021-11-26T13:29:44.887844Z","iopub.status.idle":"2021-11-26T13:29:44.891513Z","shell.execute_reply.started":"2021-11-26T13:29:44.887815Z","shell.execute_reply":"2021-11-26T13:29:44.890656Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cb1 = SaveModelCallback(monitor='IoU',fname='best_model_big',comp=np.greater) # Callbacks\ncb2 = ReduceLROnPlateau(monitor='IoU', patience=1,factor=0.2)\nlearn.fit_one_cycle(1, 1e-5, cbs = [cb1,cb2])","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:30:19.153421Z","iopub.execute_input":"2021-11-26T13:30:19.153697Z","iopub.status.idle":"2021-11-26T13:31:48.6121Z","shell.execute_reply.started":"2021-11-26T13:30:19.153666Z","shell.execute_reply":"2021-11-26T13:31:48.611328Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.load('/kaggle/working/best_model_big');","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:38:55.093355Z","iopub.execute_input":"2021-11-26T13:38:55.093673Z","iopub.status.idle":"2021-11-26T13:38:55.225858Z","shell.execute_reply.started":"2021-11-26T13:38:55.093637Z","shell.execute_reply":"2021-11-26T13:38:55.225131Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"try:\n    learn.export('/kaggle/working/export.pkl')\nexcept:\n    pass","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:38:57.317745Z","iopub.execute_input":"2021-11-26T13:38:57.318306Z","iopub.status.idle":"2021-11-26T13:38:57.642476Z","shell.execute_reply.started":"2021-11-26T13:38:57.318266Z","shell.execute_reply":"2021-11-26T13:38:57.641734Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"learn.show_results(max_n = 8, figsize = (10,16) )","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:38:58.917689Z","iopub.execute_input":"2021-11-26T13:38:58.91862Z","iopub.status.idle":"2021-11-26T13:39:00.721598Z","shell.execute_reply.started":"2021-11-26T13:38:58.918578Z","shell.execute_reply":"2021-11-26T13:39:00.72098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## our top 3 losses","metadata":{}},{"cell_type":"code","source":"interp = SegmentationInterpretation.from_learner(learn)\ninterp.plot_top_losses(k=3)","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:01.102705Z","iopub.execute_input":"2021-11-26T13:39:01.103165Z","iopub.status.idle":"2021-11-26T13:39:23.182484Z","shell.execute_reply.started":"2021-11-26T13:39:01.103125Z","shell.execute_reply":"2021-11-26T13:39:23.181731Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Loading Submission files and predicting results","metadata":{}},{"cell_type":"code","source":"submission = pd.read_csv('../input/sartorius-cell-instance-segmentation/sample_submission.csv')\nsubmission.head()","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:23.184602Z","iopub.execute_input":"2021-11-26T13:39:23.185117Z","iopub.status.idle":"2021-11-26T13:39:23.227871Z","shell.execute_reply.started":"2021-11-26T13:39:23.185067Z","shell.execute_reply":"2021-11-26T13:39:23.227024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data_path = submission['id'].apply(lambda x:f'../input/sartorius-cell-instance-segmentation/test/{x}.png').tolist()","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:23.229423Z","iopub.execute_input":"2021-11-26T13:39:23.229747Z","iopub.status.idle":"2021-11-26T13:39:23.240808Z","shell.execute_reply.started":"2021-11-26T13:39:23.229711Z","shell.execute_reply":"2021-11-26T13:39:23.239935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"tst_dl = learn.dls.test_dl(test_data_path)\npreds = learn.get_preds(dl = tst_dl)[0]","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:23.243104Z","iopub.execute_input":"2021-11-26T13:39:23.243451Z","iopub.status.idle":"2021-11-26T13:39:25.341744Z","shell.execute_reply.started":"2021-11-26T13:39:23.243412Z","shell.execute_reply":"2021-11-26T13:39:25.340957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"prediction_masks = [x.argmax(axis=0) for x in preds]","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:25.343514Z","iopub.execute_input":"2021-11-26T13:39:25.343792Z","iopub.status.idle":"2021-11-26T13:39:25.560221Z","shell.execute_reply.started":"2021-11-26T13:39:25.343751Z","shell.execute_reply":"2021-11-26T13:39:25.559443Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## A look at test predictions","metadata":{}},{"cell_type":"code","source":"im_num = 2\nts_img = PIL.Image.open(test_data_path[im_num])\nts_mask = prediction_masks[im_num]","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:29.318343Z","iopub.execute_input":"2021-11-26T13:39:29.318911Z","iopub.status.idle":"2021-11-26T13:39:29.323585Z","shell.execute_reply.started":"2021-11-26T13:39:29.318874Z","shell.execute_reply":"2021-11-26T13:39:29.322657Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(1,figsize=(18,8))\nplt.subplot(121)\nplt.imshow(ts_img)\nplt.title('Test Image')\nplt.subplot(122)\nplt.imshow(ts_img)\nplt.imshow(ts_mask,alpha=0.5);\nplt.title('Test Image + Predicted Mask');","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:30.472607Z","iopub.execute_input":"2021-11-26T13:39:30.472882Z","iopub.status.idle":"2021-11-26T13:39:31.135537Z","shell.execute_reply.started":"2021-11-26T13:39:30.472851Z","shell.execute_reply":"2021-11-26T13:39:31.134864Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Converting predicted semantic masks to instance masks and then to run length encodings\n\n**Since fastai only provides semantic segmentation we will use a hack to convert it into intance segmentation masks**","metadata":{}},{"cell_type":"markdown","source":"**We will use an algorithm called connected components algorithm to convert semantic mask to instance mask**\n\n**Connected Component Labeling (CCL)** is a basic algorithm in image processing and an essential step in nearly every application dealing with object detection. It groups together pixels belonging to the same connected component\n\n![ccl](https://homepages.inf.ed.ac.uk/rbf/HIPR2/labelb.gif)","metadata":{}},{"cell_type":"code","source":"def CCL(img_arr):\n    img = img_arr\n    # Converting those pixels with values 1-127 to 0 and others to 1\n    #img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)[1]\n    # Applying cv2.connectedComponents() \n    num_labels, labels = cv2.connectedComponents(img)\n    # Map component labels to hue val, 0-179 is the hue range in OpenCV\n    label_hue = np.uint8(179*labels/np.max(labels))\n    blank_ch = 255*np.ones_like(label_hue)\n    labeled_img = cv2.merge([label_hue, blank_ch, blank_ch])\n    ret_lbl = labeled_img.copy()\n    return ret_lbl[:,:,0]","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:39.808629Z","iopub.execute_input":"2021-11-26T13:39:39.809208Z","iopub.status.idle":"2021-11-26T13:39:39.816653Z","shell.execute_reply.started":"2021-11-26T13:39:39.809167Z","shell.execute_reply":"2021-11-26T13:39:39.814097Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"is_mask = np.expand_dims(prediction_masks[im_num].numpy(),axis=-1).astype(np.uint8)\nis_img = CCL(is_mask)","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:40.522926Z","iopub.execute_input":"2021-11-26T13:39:40.523624Z","iopub.status.idle":"2021-11-26T13:39:40.552349Z","shell.execute_reply.started":"2021-11-26T13:39:40.523581Z","shell.execute_reply":"2021-11-26T13:39:40.551617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(1,figsize=(18,8))\nplt.subplot(131)\nplt.imshow(ts_img)\nplt.title('Test Image')\nplt.subplot(132)\nplt.imshow(is_img)\nplt.title('Instance Converted mask')\nplt.subplot(133)\nplt.imshow(ts_img)\nplt.imshow(is_mask,alpha=0.5);\nplt.title('Test Image upon Instance Converted Mask');","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:40.553758Z","iopub.execute_input":"2021-11-26T13:39:40.554012Z","iopub.status.idle":"2021-11-26T13:39:41.257797Z","shell.execute_reply.started":"2021-11-26T13:39:40.553977Z","shell.execute_reply":"2021-11-26T13:39:41.256988Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**See how CCL algorithm has colored each mask with a different color**","metadata":{}},{"cell_type":"code","source":"# From https://www.kaggle.com/stainsby/fast-tested-rle\ndef rle_decode(mask_rle, shape=(520, 704)):\n    '''\n    mask_rle: run-length as string formated (start length)\n    shape: (height,width) of array to return \n    Returns numpy array, 1 - mask, 0 - background\n\n    '''\n    s = mask_rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    img = np.zeros(shape[0]*shape[1], dtype=np.uint8)\n    for lo, hi in zip(starts, ends):\n        img[lo:hi] = 1\n    return img.reshape(shape)  # Needed to align to RLE direction\n\ndef rle_encode(img):\n    '''\n    img: numpy array, 1 - mask, 0 - background\n    Returns run length as string formated\n    '''\n    pixels = img.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:41.487672Z","iopub.execute_input":"2021-11-26T13:39:41.487909Z","iopub.status.idle":"2021-11-26T13:39:41.498093Z","shell.execute_reply.started":"2021-11-26T13:39:41.48788Z","shell.execute_reply":"2021-11-26T13:39:41.497237Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def convert_seg_ins(img_f):\n    lbl_img1 = img_f.copy()\n    grps = list(np.unique(lbl_img1))\n    grps.remove(0)\n    all_masks = []\n    shape = (520,704)\n    for g in grps:\n        a = np.where(((lbl_img1!=0)&(lbl_img1!=g)),np.zeros(shape),lbl_img1)\n        all_masks.append(a.clip(0,1))\n    all_masks = np.array(all_masks)\n    return all_masks","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:41.803316Z","iopub.execute_input":"2021-11-26T13:39:41.803674Z","iopub.status.idle":"2021-11-26T13:39:41.81036Z","shell.execute_reply.started":"2021-11-26T13:39:41.803641Z","shell.execute_reply":"2021-11-26T13:39:41.809356Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Writing masks to rle","metadata":{}},{"cell_type":"code","source":"sub_ids = submission['id'].values","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:42.358231Z","iopub.execute_input":"2021-11-26T13:39:42.358715Z","iopub.status.idle":"2021-11-26T13:39:42.362762Z","shell.execute_reply.started":"2021-11-26T13:39:42.358677Z","shell.execute_reply":"2021-11-26T13:39:42.362119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"res = []\nn = 0\nfor i in tqdm(range(len(prediction_masks))):\n    chk_mask = np.expand_dims(prediction_masks[i].numpy(),axis=-1).astype(np.uint8)\n    lbl_img = CCL(chk_mask)\n    pred_masks = convert_seg_ins(lbl_img)\n    for mask in pred_masks:\n        ts = np.unique(mask, return_counts=True)[1][1]\n        #removing blocks with very small areas\n        if ts>50:\n            res.append([sub_ids[i],rle_encode(mask)])","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:42.371933Z","iopub.execute_input":"2021-11-26T13:39:42.37227Z","iopub.status.idle":"2021-11-26T13:39:46.84544Z","shell.execute_reply.started":"2021-11-26T13:39:42.37224Z","shell.execute_reply":"2021-11-26T13:39:46.84436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_df = pd.DataFrame(res,columns=['id', 'predicted'])","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:46.84761Z","iopub.execute_input":"2021-11-26T13:39:46.84802Z","iopub.status.idle":"2021-11-26T13:39:46.858879Z","shell.execute_reply.started":"2021-11-26T13:39:46.847987Z","shell.execute_reply":"2021-11-26T13:39:46.858201Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:46.860414Z","iopub.execute_input":"2021-11-26T13:39:46.860883Z","iopub.status.idle":"2021-11-26T13:39:46.878692Z","shell.execute_reply.started":"2021-11-26T13:39:46.860847Z","shell.execute_reply":"2021-11-26T13:39:46.877937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_df.to_csv('submission.csv',index=False)","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:46.882391Z","iopub.execute_input":"2021-11-26T13:39:46.884186Z","iopub.status.idle":"2021-11-26T13:39:46.898303Z","shell.execute_reply.started":"2021-11-26T13:39:46.884147Z","shell.execute_reply":"2021-11-26T13:39:46.897216Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Please don't forget to Upvote if like the work.","metadata":{}},{"cell_type":"code","source":"sub_df['id'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-11-26T13:39:46.903442Z","iopub.execute_input":"2021-11-26T13:39:46.905526Z","iopub.status.idle":"2021-11-26T13:39:46.921835Z","shell.execute_reply.started":"2021-11-26T13:39:46.905486Z","shell.execute_reply":"2021-11-26T13:39:46.920574Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}