{"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":"```python\n# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n```","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"<br>","metadata":{}},{"cell_type":"markdown","source":"# Introduction","metadata":{}},{"cell_type":"markdown","source":"This notebook is a merging from the quoted notebook listed below based on my personal studying and digesting. Thanks a lot to these kindly contributors since their contributions really help me a lot!\n\n* <a href=\"https://www.kaggle.com/code/nikmarker/sartorius-starter-torch-mask-r-cnn-lb-0-273\" style=\"text-decoration:none\">**Sartorius - Starter Torch Mask R-CNN [LB=0.273]**</a>\n* <a href=\"https://www.kaggle.com/code/frozenwolf/sartorius-visualization-training-maskr-cnn\" style=\"text-decoration:none\">🧬 Sartorius : Visualization + Training MaskR-CNN</a>\n* \n* <a href=\"https://www.kaggle.com/code/frozenwolf/sartorius-visualization-training-u-net\" style=\"text-decoration:none\">🦠 Sartorius : Visualization + Training U-Net</a>\n* <a href=\"https://www.kaggle.com/code/shnakazawa/semantic-segmentation-with-pytorch-and-u-net/notebook\" style=\"text-decoration:none\">Semantic Segmentation with PyTorch and U-Net</a>\n\nAnd of course, as a noice in Kaggle, I will refernce most of the codes from the notebook above, rearrange and display them on my own way and style.\n\n\n<br>\nRead more about the Mask-RCNN paper <a href=\"https://arxiv.org/abs/1703.06870\" style=\"text-decoration:none\">here</a>.\n\n<br>\n<br>\n\nFor more about the Pytorch library, exploring <a href=\"https://pytorch.org/\" style=\"text-decoration:none\">here</a>. Especially,\n* For related materials in `torchvision.models`: <a href=\"https://pytorch.org/vision/0.11/models.html#object-detection-instance-segmentation-and-person-keypoint-detection\" style=\"text-decoration:none\">Object Detection, Instance Segmentation and Person Keypoint Detection</a>\n* For MaskRcnn source code: <a href=\"https://pytorch.org/vision/stable/_modules/torchvision/models/detection/mask_rcnn.html\" style=\"text-decoration:none\">torchvision.models.detection.mask_rcnn</a>\n* For FasterRcnn source code: <a href=\"https://pytorch.org/vision/0.11/_modules/torchvision/models/detection/faster_rcnn.html\" style=\"text-decoration:none\">torchvision.models.detection.faster_rcnn</a>\n* For **github** source code: <a href=\"https://github.com/pytorch/vision/blob/main/torchvision/models/detection/mask_rcnn.py\" style=\"text-decoration:none\">pytorch/vision/torchvision/models/detection/mask_rcnn.py</a>","metadata":{"execution":{"iopub.status.busy":"2023-04-19T08:10:21.292563Z","iopub.execute_input":"2023-04-19T08:10:21.293318Z","iopub.status.idle":"2023-04-19T08:10:21.305282Z","shell.execute_reply.started":"2023-04-19T08:10:21.293276Z","shell.execute_reply":"2023-04-19T08:10:21.303529Z"}}},{"cell_type":"markdown","source":"# **Import Libraries**","metadata":{}},{"cell_type":"code","source":"import os\nimport time\nimport random\nimport collections\nfrom pathlib import Path\nfrom tqdm.notebook import tqdm\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom sklearn.model_selection import KFold\n# from sklearn.model_selection import train_test_split\n\nimport cv2\nfrom PIL import Image\n# import albumentations as A\n# from albumentations.pytorch import ToTensorV2\n# from albumentations import HorizontalFlip, VerticalFlip, ShiftScaleRotate, Normalize, Resize, Compose, GaussNoise\n\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\n\nimport torchvision\n# from torchvision.transforms import ToPILImage\nfrom torchvision.transforms import functional as F\nfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictor\nfrom torchvision.models.detection.mask_rcnn import MaskRCNNPredictor\n\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:02.314739Z","iopub.execute_input":"2023-04-20T02:42:02.315926Z","iopub.status.idle":"2023-04-20T02:42:03.960072Z","shell.execute_reply.started":"2023-04-20T02:42:02.315856Z","shell.execute_reply":"2023-04-20T02:42:03.958908Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f'PyTorch version: {torch.__version__}')\nprint(f'CUDA avaliable: {torch.cuda.is_available()}')","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:03.961909Z","iopub.execute_input":"2023-04-20T02:42:03.963425Z","iopub.status.idle":"2023-04-20T02:42:03.994104Z","shell.execute_reply.started":"2023-04-20T02:42:03.963383Z","shell.execute_reply":"2023-04-20T02:42:03.993014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Fix Randomness**","metadata":{}},{"cell_type":"code","source":"# Fix randomness\n\ndef fix_all_seeds(seed):\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    # torch.backends.cudnn.deterministic = True\n    # torch.backends.cudnn.benchmark = True\n    \n    os.environ['PYTHONHASHSEED'] = str(seed)\n\n\n# Set seed\nfix_all_seeds(2023)","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:03.998465Z","iopub.execute_input":"2023-04-20T02:42:03.998799Z","iopub.status.idle":"2023-04-20T02:42:04.008204Z","shell.execute_reply.started":"2023-04-20T02:42:03.998768Z","shell.execute_reply":"2023-04-20T02:42:04.006815Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.getcwd()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.012380Z","iopub.execute_input":"2023-04-20T02:42:04.013235Z","iopub.status.idle":"2023-04-20T02:42:04.023378Z","shell.execute_reply.started":"2023-04-20T02:42:04.013193Z","shell.execute_reply":"2023-04-20T02:42:04.021853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Configurations**","metadata":{}},{"cell_type":"code","source":"# Directory setting\nDATA_DIR = '/kaggle/input/sartorius-cell-instance-segmentation/'\n\nTRAIN_CSV = DATA_DIR + 'train.csv'\nTRAIN_PATH = DATA_DIR + 'train/'\nTEST_PATH = DATA_DIR + 'test/'\n\nNORMALIZE = False                         # Normalize to resnet mean and std if True. Be set in transforms().\nRESNET_MEAN = (0.485, 0.456, 0.406)\nRESNET_STD = (0.229, 0.224, 0.225)\nIMG_SIZE = (224, 224)                     # size of resized image for model input\nHEIGHT = 520                              # height of the provided train image\nWIDTH = 704                               # width of the provided train image\n\nH_LAYER = 256\nBOX_DETECTIONS_PER_IMG = 790\nMIN_SCORE = 0.59\nMASK_THRESHOLD = 0.5   # Changes the confidence required for a pixel to be kept for a mask. Only used 0.5 till now.\n\n\nBATCH_SIZE = 2\nBATCH_SHUFFLE = True\nEPOCHS = 18\n\nUSE_SCHEDULER = False  # Use a StepLR scheduler if True. Not tried yet.\nMOMENTUM = 0.95\nLEARNING_RATE = 0.001\nWEIGHT_DECAY = 0.0005\n\n# CELL_TYPES  = {0: 'shsy5y', 1: 'astro', 2: 'cort'}\nCLASSES_NUM = 4        # 3 types of cell + 1 type as background\nFOLDS = 3              # Kfold cross-validation\n\n\n## Set device\n# device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') \nDEVICE = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')\nprint(f'Using {DEVICE} device')","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.025196Z","iopub.execute_input":"2023-04-20T02:42:04.025968Z","iopub.status.idle":"2023-04-20T02:42:04.037355Z","shell.execute_reply.started":"2023-04-20T02:42:04.025928Z","shell.execute_reply":"2023-04-20T02:42:04.035790Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# **Helper Functions and Classes**\n\n## Transformations Classes\n\nJust Horizontal and Vertical Flip for now.\n\nNormalization to Resnet's mean and std can be performed using the parameter `NORMALIZE` in the top cell. Haven't tested it yet.\n\nThe first 3 transformations come from <a href=\"https://www.kaggle.com/abhishek/maskrcnn-utils\" style=\"text-decoration:none\">this</a> utils package by Abishek, `VerticalFlip` is <a href=\"https://www.kaggle.com/code/nikmarker/sartorius-starter-torch-mask-r-cnn-lb-0-273\" style=\"text-decoration:none\">KONKOV's</a> adaption of HorizontalFlip, and `Normalize` is of KONKOV's own. And `Resize` is of my own.","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"# These are slight redefinitions of torch.transformation classes\n# The difference is that they handle the target and the mask\n# Copied from Abishek, added new ones\n\nclass Compose():\n    def __init__(self, transforms):\n        self.transforms = transforms\n    \n    \n    def __call__(self, image, target):\n        for t in self.transforms:\n            image, target = t(image, target)\n        \n        return image, target\n\n    \n    \n\nclass VerticalFlip():\n    def __init__(self, prob):\n        self.prob = prob\n    \n    \n    def __call__(self, image, target):\n        if random.random() < self.prob:                  # 实现 VerticalFlip 的概率小于初始化的 prob\n            height, weight = image.shape[-2:]\n            image = image.flip(-2)\n            bbox = target[\"boxes\"]\n            bbox[:, [1, 3]] = height - bbox[:, [3, 1]]   # 因为 image.flip(-2) 将图片 vertical flip 了，所以相应的 bbox 的范围要做这番调整。\n            target[\"boxes\"] = bbox\n            target[\"masks\"] = target[\"masks\"].flip(-2)\n        return image, target\n\n    \n    \n\nclass HorizontalFlip():\n    def __init__(self, prob):\n        self.prob = prob\n\n    def __call__(self, image, target):\n        if random.random() < self.prob:                # 实现 VerticalFlip 的概率小于初始化的 prob\n            height, width = image.shape[-2:]\n            image = image.flip(-1)\n            bbox = target[\"boxes\"]\n            bbox[:, [0, 2]] = width - bbox[:, [2, 0]]  # 因为 image.flip(-1) 将图片 horizontal flip 了，所以相应的 bbox 的范围要做这番调整。\n            target[\"boxes\"] = bbox\n            target[\"masks\"] = target[\"masks\"].flip(-1)\n        return image, target\n\n\n\nclass Normalize():\n    def __call__(self, image, target):\n        image = F.normalize(image, RESNET_MEAN, RESNET_STD)\n        return image, target\n\n\n\nclass ToTensor():\n    def __call__(self, image, target):\n        image = F.to_tensor(image)\n        return image, target","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.039376Z","iopub.execute_input":"2023-04-20T02:42:04.040017Z","iopub.status.idle":"2023-04-20T02:42:04.057233Z","shell.execute_reply.started":"2023-04-20T02:42:04.039974Z","shell.execute_reply":"2023-04-20T02:42:04.056132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Resize():\n    def __call__(self, image, target):\n        image = F.resize(image, IMG_SIZE)\n        return image, target","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.058970Z","iopub.execute_input":"2023-04-20T02:42:04.059501Z","iopub.status.idle":"2023-04-20T02:42:04.071024Z","shell.execute_reply.started":"2023-04-20T02:42:04.059461Z","shell.execute_reply":"2023-04-20T02:42:04.069959Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## transforms()","metadata":{}},{"cell_type":"code","source":"def transforms(train=True):\n    # transforms = [ToTensor(), Resize()]\n    transforms = [ToTensor()]\n    \n    if NORMALIZE:        # Set to False in Configurations, cause it can be achieved in params of MRcnn.\n        transforms.append(Normalize())\n    \n    # Data augmentation for training\n    if train: \n        transforms.append(HorizontalFlip(0.5))\n        transforms.append(VerticalFlip(0.5))\n\n    return Compose(transforms)","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.072749Z","iopub.execute_input":"2023-04-20T02:42:04.073376Z","iopub.status.idle":"2023-04-20T02:42:04.085268Z","shell.execute_reply.started":"2023-04-20T02:42:04.073329Z","shell.execute_reply":"2023-04-20T02:42:04.084220Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## show_gpu_memory()","metadata":{}},{"cell_type":"code","source":"def show_gpu_memory(device):\n    print(f\"Allocated GPU memory: {torch.cuda.memory_allocated(device) / 1024 / 1024:.2f} MB\")\n    print(f\"Cached GPU memory: {torch.cuda.memory_cached(device) / 1024 / 1024:.2f} MB\") \n\nshow_gpu_memory(DEVICE)","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.087088Z","iopub.execute_input":"2023-04-20T02:42:04.088065Z","iopub.status.idle":"2023-04-20T02:42:04.096290Z","shell.execute_reply.started":"2023-04-20T02:42:04.088037Z","shell.execute_reply":"2023-04-20T02:42:04.095125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"torch.cuda.get_device_properties(DEVICE)","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.101888Z","iopub.execute_input":"2023-04-20T02:42:04.102971Z","iopub.status.idle":"2023-04-20T02:42:04.118660Z","shell.execute_reply.started":"2023-04-20T02:42:04.102933Z","shell.execute_reply":"2023-04-20T02:42:04.117635Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## rle_decode()\n\nDecode the RLE (annotation) of an particular cell instance in an image to its correspongding mask.","metadata":{}},{"cell_type":"code","source":"def rle_decode(rle, img_shape, color=1):\n    \"\"\"Decode the RLE (annotation) of an particular cell instance in an image to its correspongding mask.\n\n    Args:\n        rle (str): mask with run length encoding.\n        img_shape ((int, int)): (height, width) of the image, also the shape of mask np.ndarray to return.\n        color (int): brightness of the mask pixel. Default to 1.\n\n    Returns:\n        np.ndarray: 1 - mask, 0 - background.\n    \"\"\"\n    rle_list = rle.split()\n    starts, lengths = [np.asarray(x, dtype=int) for x in (rle_list[0:][::2], rle_list[1:][::2])]\n    starts -= 1\n    ends = starts + lengths\n    \n    mask = np.zeros(img_shape[0] * img_shape[1], dtype=np.float32)\n    for lo, hi in zip(starts, ends):\n        mask[lo:hi] = color\n    \n    return mask.reshape(img_shape)","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.120008Z","iopub.execute_input":"2023-04-20T02:42:04.120679Z","iopub.status.idle":"2023-04-20T02:42:04.130412Z","shell.execute_reply.started":"2023-04-20T02:42:04.120643Z","shell.execute_reply":"2023-04-20T02:42:04.129533Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## build_image_mask()\n\nDecode RLEs (annotations) of all cell instance in an image into one mask image.","metadata":{}},{"cell_type":"code","source":"def build_image_mask(img, rle_list):\n    \"\"\"Decode RLEs (annotations) of all cell instance in an image into one mask image.\n    \n    Args:\n        img (np.ndarray): image with single channel or multiple channels.\n        rle_list (list of str): rles of all cell instances in an image as a list.\n    \n    Returns:\n        np.ndarray: 1 - mask, 0 - background.\n    \"\"\"\n    img_shape = img.shape\n    h = img_shape[0]\n    w = img_shape[1]\n    \n    mask = np.zeros((h, w))\n    for rle in rle_list:\n        mask += rle_decode(rle, (h, w))\n    mask = mask.clip(0,1)\n    \n    return mask","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.131939Z","iopub.execute_input":"2023-04-20T02:42:04.132762Z","iopub.status.idle":"2023-04-20T02:42:04.142119Z","shell.execute_reply.started":"2023-04-20T02:42:04.132723Z","shell.execute_reply":"2023-04-20T02:42:04.141010Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<br>","metadata":{}},{"cell_type":"markdown","source":"# **EDA (Exploratory Data Analysis)**","metadata":{}},{"cell_type":"markdown","source":"We will do some **EDA** (Exploratory Data Analysis) of the `train.csv` including **Visualization** of some images so that we can better understand our training dataset.","metadata":{}},{"cell_type":"markdown","source":"## Explore `train.csv`","metadata":{}},{"cell_type":"code","source":"df_train = pd.read_csv(TRAIN_CSV)\ndf_train.head().append(df_train.tail())","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.143649Z","iopub.execute_input":"2023-04-20T02:42:04.144142Z","iopub.status.idle":"2023-04-20T02:42:04.451830Z","shell.execute_reply.started":"2023-04-20T02:42:04.144102Z","shell.execute_reply":"2023-04-20T02:42:04.450617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.shape","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.454474Z","iopub.execute_input":"2023-04-20T02:42:04.455435Z","iopub.status.idle":"2023-04-20T02:42:04.463124Z","shell.execute_reply.started":"2023-04-20T02:42:04.455386Z","shell.execute_reply":"2023-04-20T02:42:04.461809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.dtypes","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.464968Z","iopub.execute_input":"2023-04-20T02:42:04.465811Z","iopub.status.idle":"2023-04-20T02:42:04.477658Z","shell.execute_reply.started":"2023-04-20T02:42:04.465766Z","shell.execute_reply":"2023-04-20T02:42:04.476379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Cell Type","metadata":{}},{"cell_type":"code","source":"df_train.cell_type.value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.479464Z","iopub.execute_input":"2023-04-20T02:42:04.480263Z","iopub.status.idle":"2023-04-20T02:42:04.494276Z","shell.execute_reply.started":"2023-04-20T02:42:04.480224Z","shell.execute_reply":"2023-04-20T02:42:04.493325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Annotation (RLE)\n\nThe segment of training data is provided with **Run length encoding (RLE)** in the `annotation` column.\n\n\nRLE is a lossless compression technique used to represent data that contains long sequences of repeated values or characters. <a href=\"https://www.kaggle.com/c/severstal-steel-defect-detection/discussion/102311\" style=\"text-decoration:none\">Check out this discussion for a better understanding</a>.\n\n\nThe current dataset has one segment per row.","metadata":{}},{"cell_type":"code","source":"# train.csv 中 id 列是图片编号，每一行的 annotation 都是该图片中的一个细胞实例的 mask 数据。\n# 若一张图片中有 395 个细胞实例的 mask，则这张图片会在表格中出现 395 行。\ndf_instances = df_train.groupby(['id']).agg({'annotation': 'count', 'cell_type': 'first'})\ndf_instances.head()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.495714Z","iopub.execute_input":"2023-04-20T02:42:04.496358Z","iopub.status.idle":"2023-04-20T02:42:04.524694Z","shell.execute_reply.started":"2023-04-20T02:42:04.496296Z","shell.execute_reply":"2023-04-20T02:42:04.523578Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_instances.shape","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.526489Z","iopub.execute_input":"2023-04-20T02:42:04.526865Z","iopub.status.idle":"2023-04-20T02:42:04.535254Z","shell.execute_reply.started":"2023-04-20T02:42:04.526827Z","shell.execute_reply":"2023-04-20T02:42:04.534099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 每一种细胞类型（一张图片只有一种细胞）它在一张图片中 instance segmentation mask\n# (亦即表中对应图片的 annotation 行数) 的总数的分布的分位数情况。\ndf_instances_pentiles = df_train.groupby(['id']).agg({'annotation': 'count', 'cell_type': 'first'})\ndf_instances_pentiles = df_instances_pentiles.groupby(\"cell_type\")[['annotation']]\\\n                                             .describe(percentiles=[0.1, 0.25, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99]).astype(int)\\\n                                             .T.droplevel(level=0).T.drop(['count', '50%', 'std'], axis=1)\ndf_instances_pentiles","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.536837Z","iopub.execute_input":"2023-04-20T02:42:04.537533Z","iopub.status.idle":"2023-04-20T02:42:04.591557Z","shell.execute_reply.started":"2023-04-20T02:42:04.537494Z","shell.execute_reply":"2023-04-20T02:42:04.590389Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"From above, we can see that the maximum number of instances per image is **790**, which is from the shsy5y type. So we can set the params `box_detections_per_img` in mrcnn model a little bigger than this.","metadata":{}},{"cell_type":"code","source":"# Trying with different strategies\ndf_instances_pentiles['90%'].to_dict()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.593074Z","iopub.execute_input":"2023-04-20T02:42:04.594227Z","iopub.status.idle":"2023-04-20T02:42:04.601181Z","shell.execute_reply.started":"2023-04-20T02:42:04.594184Z","shell.execute_reply":"2023-04-20T02:42:04.600065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Pixels Per Mask Per `cell_type`","metadata":{}},{"cell_type":"code","source":"df_train['n_pixels'] = df_train.annotation.apply(lambda x: np.sum([int(e) for e in x.split()[1:][::2]]))\n# 得到每一个 annotation 所描述的 mask 的面积（像素点个数）。\n\n# 各类型细胞的一个 annotation 对应的一个 instance segmentation mask 的像素点个数的分布的分位数情况。\ndf_pixels = df_train.groupby(\"cell_type\")[['n_pixels']].describe(percentiles=[0.02, 0.05, 0.1, 0.9, 0.95, 0.98])\\\n                    .astype(int).T.droplevel(level=0).T.drop(['count', '50%', 'std'], axis=1)\ndf_pixels","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:04.602849Z","iopub.execute_input":"2023-04-20T02:42:04.603788Z","iopub.status.idle":"2023-04-20T02:42:05.823619Z","shell.execute_reply.started":"2023-04-20T02:42:04.603744Z","shell.execute_reply":"2023-04-20T02:42:05.822590Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Image Shape and Format","metadata":{}},{"cell_type":"code","source":"df_train.width.unique()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:05.825599Z","iopub.execute_input":"2023-04-20T02:42:05.826005Z","iopub.status.idle":"2023-04-20T02:42:05.834416Z","shell.execute_reply.started":"2023-04-20T02:42:05.825965Z","shell.execute_reply":"2023-04-20T02:42:05.833077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.height.unique()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:05.836718Z","iopub.execute_input":"2023-04-20T02:42:05.837292Z","iopub.status.idle":"2023-04-20T02:42:05.848531Z","shell.execute_reply.started":"2023-04-20T02:42:05.837250Z","shell.execute_reply":"2023-04-20T02:42:05.847395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img_shapes = set()\nimg_exts = set()\nimg_paths = Path(TRAIN_PATH).glob(\"*\")\n\nbar = tqdm(img_paths, total=df_train.id.unique().shape[0])    # should not be: total=len(list(img_paths))\n\nfor img_path in bar:\n    img_bgr = cv2.imread(img_path.as_posix())                 # BGR mode\n    img_rgb = img_bgr[:, :, ::-1]                             # RGB mode\n    \n    img_shapes.add(img_rgb.shape)\n    img_exts.add(img_path.suffix)\nprint(f'Image shapes are {img_shapes}.')\nprint(f'Image extensions are {img_exts}.')","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:05.849996Z","iopub.execute_input":"2023-04-20T02:42:05.850719Z","iopub.status.idle":"2023-04-20T02:42:10.258069Z","shell.execute_reply.started":"2023-04-20T02:42:05.850681Z","shell.execute_reply":"2023-04-20T02:42:10.256897Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Plot Histogram of Pixel Values\n\nGenerating a pixel value histogram can aid in identifying outlier images, such as those containing entirely zero-valued pixels.\n\nAs the figure displayed below, the image signals appear to be quite uniform, which is characteristic of typical microscopy images. :)","metadata":{}},{"cell_type":"code","source":"img_paths = Path(TRAIN_PATH).glob(\"*\")\nbar = tqdm(img_paths, total=df_train.id.unique().shape[0])     # should not be: total=len(list(img_paths))\n\nplt.figure(figsize=(8,8))\nfor img_path in bar:\n    img_bgr = cv2.imread(img_path.as_posix())    # BGR mode\n    img_rgb = img_bgr[:, :, ::-1]                # RGB mode\n    \n    hist = cv2.calcHist([img_rgb], [0], None ,[256], [0,256])\n    plt.plot(hist)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:10.259843Z","iopub.execute_input":"2023-04-20T02:42:10.260263Z","iopub.status.idle":"2023-04-20T02:42:19.318314Z","shell.execute_reply.started":"2023-04-20T02:42:10.260221Z","shell.execute_reply":"2023-04-20T02:42:19.317106Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Display Some Images and Masks","metadata":{}},{"cell_type":"markdown","source":"### shsy5y","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(16, 8))\nimg_id = \"0030fd0e6378\"\nimg = cv2.imread(TRAIN_PATH + img_id + \".png\")         # BGR mode\nax1 = plt.subplot(121)\nax1.imshow(img)\nax1.set_title(\"Shsy5y Original Image (BGR mode)\")\n\nmask = build_image_mask(img, df_train[df_train.id == img_id].annotation.tolist())\nax2 = plt.subplot(122)\nax2.imshow(mask)\nax2.set_title(\"Shsy5y Mask\")\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:19.320000Z","iopub.execute_input":"2023-04-20T02:42:19.320664Z","iopub.status.idle":"2023-04-20T02:42:20.055703Z","shell.execute_reply.started":"2023-04-20T02:42:19.320620Z","shell.execute_reply":"2023-04-20T02:42:20.053553Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(16, 8))\nimg_id = \"0030fd0e6378\"\nimg = cv2.imread(TRAIN_PATH + img_id + \".png\")          # BGR mode\nplt.imshow(img)\nplt.title(\"Cort Original Image (BGR mode) and Mask\")\n\nmask = build_image_mask(img, df_train[df_train.id == img_id].annotation.tolist())\nplt.imshow(mask, alpha=0.2)\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:20.056778Z","iopub.execute_input":"2023-04-20T02:42:20.057194Z","iopub.status.idle":"2023-04-20T02:42:20.958158Z","shell.execute_reply.started":"2023-04-20T02:42:20.057151Z","shell.execute_reply":"2023-04-20T02:42:20.957142Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### astro","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(16, 8))\nimg_id = \"0140b3c8f445\"\nimg = cv2.imread(TRAIN_PATH + img_id + \".png\")         # BGR mode\nax1 = plt.subplot(121)\nax1.imshow(img)\nax1.set_title(\"Astro Original Image (BGR mode)\")\n\nmask = build_image_mask(img, df_train[df_train.id == img_id].annotation.tolist())\nax2 = plt.subplot(122)\nax2.imshow(mask)\nax2.set_title(\"Astro Mask\")\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:20.965079Z","iopub.execute_input":"2023-04-20T02:42:20.965777Z","iopub.status.idle":"2023-04-20T02:42:21.568140Z","shell.execute_reply.started":"2023-04-20T02:42:20.965734Z","shell.execute_reply":"2023-04-20T02:42:21.567232Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(16, 8))\nimg_id = \"0140b3c8f445\"\nimg = cv2.imread(TRAIN_PATH + img_id + \".png\")          # BGR mode\nplt.imshow(img)\nplt.title(\"Cort Original Image (BGR mode) and Mask\")\n\nmask = build_image_mask(img, df_train[df_train.id == img_id].annotation.tolist())\nplt.imshow(mask, alpha=0.05)\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:21.569862Z","iopub.execute_input":"2023-04-20T02:42:21.570561Z","iopub.status.idle":"2023-04-20T02:42:22.287214Z","shell.execute_reply.started":"2023-04-20T02:42:21.570511Z","shell.execute_reply":"2023-04-20T02:42:22.286267Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### cort","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(16, 8))\nimg_id = \"01ae5a43a2ab\"\nimg = cv2.imread(TRAIN_PATH + img_id + \".png\")         # BGR mode\nax1 = plt.subplot(121)\nax1.imshow(img)\nax1.set_title(\"Cort Original Image (BGR mode)\")\n\nmask = build_image_mask(img, df_train[df_train.id == img_id].annotation.tolist())\nax2 = plt.subplot(122)\nax2.imshow(mask)\nax2.set_title(\"Cort Mask\")\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:22.289000Z","iopub.execute_input":"2023-04-20T02:42:22.289733Z","iopub.status.idle":"2023-04-20T02:42:22.871245Z","shell.execute_reply.started":"2023-04-20T02:42:22.289693Z","shell.execute_reply":"2023-04-20T02:42:22.870268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(16, 8))\nimg_id = \"01ae5a43a2ab\"\nimg = cv2.imread(TRAIN_PATH + img_id + \".png\")          # BGR mode\nplt.imshow(img)\nplt.title(\"Cort Original Image (BGR mode) and Mask\")\n\nmask = build_image_mask(img, df_train[df_train.id == img_id].annotation.tolist())\nplt.imshow(mask, alpha=0.2)\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:22.873095Z","iopub.execute_input":"2023-04-20T02:42:22.874021Z","iopub.status.idle":"2023-04-20T02:42:23.664997Z","shell.execute_reply.started":"2023-04-20T02:42:22.873983Z","shell.execute_reply":"2023-04-20T02:42:23.663931Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"<br>","metadata":{}},{"cell_type":"markdown","source":"# Dataset and DataLoader\n\n## Define CellDataset()","metadata":{}},{"cell_type":"code","source":"class CellDataset(Dataset):\n    def __init__(self, df, train_path, transforms=None):\n        super().__init__()\n        self.df = df                    # df_train\n        self.train_path = train_path\n        self.transforms = transforms\n        self.img_info = collections.defaultdict(dict)\n        \n        ## Encoding cell_type by dict\n        ## result: {'shsy5y': 1, 'astro': 2, 'cort': 3}\n        cell_type_dict = dict.fromkeys(df_train.cell_type.unique())  # {'shsy5y': None, 'astro': None, 'cort': None}\n        v = 0\n        for k in cell_type_dict.keys():\n            v += 1\n            cell_type_dict[k] = v\n        \n        ## 同一个 id 的所有 annotation 汇总成一个 list，并保留对应的 cell type 信息。\n        df_info = df.groupby(\"id\").agg({\"annotation\": lambda x: list(x),\n                                        \"cell_type\": \"first\"}) \\\n                                  .reset_index()\n        for idx, row in df_info.iterrows():\n            self.img_info[idx] = {'image_id': row['id'],\n                                  'image_path': os.path.join(self.train_path, row['id'] + '.png'),\n                                  'annotations': row[\"annotation\"],\n                                  'cell_type' : cell_type_dict[row['cell_type']]}\n    \n    \n    \n    def get_box(self, instance_mask):\n        \"\"\"Get the bounding box of a given mask of an instance in an image\"\"\"\n        pos = np.where(instance_mask)\n        xmin = np.min(pos[1])\n        xmax = np.max(pos[1])\n        ymin = np.min(pos[0])\n        ymax = np.max(pos[0])\n        \n        return [xmin, ymin, xmax, ymax]\n    \n    \n    \n    def __getitem__(self, idx):\n        \"\"\"Get the image and target\"\"\"        \n        info = self.img_info[idx]\n        img_path = info[\"image_path\"]\n        img = Image.open(img_path).convert(\"RGB\")    # L mode -> RGB mode, also: 1 channel -> 3 channels\n        n_instances = len(info[\"annotations\"])\n        \n        masks = np.zeros((n_instances, HEIGHT, WIDTH), dtype=np.uint8)\n        boxes = []        \n        for i, annotation in enumerate(info[\"annotations\"]):\n            msk = rle_decode(annotation, (HEIGHT, WIDTH))            \n            # msk = Image.fromarray(msk)\n            # if self.should_resize:\n            #     msk = msk.resize((self.width, self.height), resample=Image.BILINEAR)\n            # msk = np.array(msk) > 0            \n            masks[i, :, :] = msk\n            boxes.append(self.get_box(msk))\n        \n        # dummy labels\n        labels = [info[\"cell_type\"] for _ in range(n_instances)]\n        \n        boxes = torch.as_tensor(boxes, dtype=torch.float32)\n        area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])    # area of each box\n        labels = torch.as_tensor(labels, dtype=torch.int64)\n        masks = torch.as_tensor(masks, dtype=torch.int8)\n        image_id = torch.tensor([idx])\n        iscrowd = torch.zeros((n_instances,), dtype=torch.int64)\n        \n        # The required target for the Mask R-CNN\n        target = {\"boxes\": boxes,\n                  'labels': labels,\n                  'masks': masks,\n                  'image_id': image_id,\n                  'area': area,\n                  'iscrowd': iscrowd}\n        \n        if self.transforms is not None:\n            img, target = self.transforms(img, target)\n        \n        return img, target\n    \n    \n    \n    def __len__(self):\n        return len(self.img_info)","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:23.666621Z","iopub.execute_input":"2023-04-20T02:42:23.667217Z","iopub.status.idle":"2023-04-20T02:42:23.692165Z","shell.execute_reply.started":"2023-04-20T02:42:23.667181Z","shell.execute_reply":"2023-04-20T02:42:23.690880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define DataLoader","metadata":{}},{"cell_type":"code","source":"dataset_train = CellDataset(df=df_train, train_path=TRAIN_PATH, transforms=transforms(train=True))\n\ndataloder_train = DataLoader(dataset_train, \n                             batch_size=BATCH_SIZE, \n                             shuffle=BATCH_SHUFFLE, \n                             # num_workers=2, \n                             collate_fn=lambda x: tuple(zip(*x))\n                             )","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:23.694184Z","iopub.execute_input":"2023-04-20T02:42:23.695463Z","iopub.status.idle":"2023-04-20T02:42:23.791124Z","shell.execute_reply.started":"2023-04-20T02:42:23.695408Z","shell.execute_reply":"2023-04-20T02:42:23.790038Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model and Training\n\n## Define Model","metadata":{}},{"cell_type":"code","source":"# os.environ['TORCH_HOME']='/kaggle/working/'\n\nos.mkdir(\"MyModelSave\")","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:23.792534Z","iopub.execute_input":"2023-04-20T02:42:23.793048Z","iopub.status.idle":"2023-04-20T02:42:23.798314Z","shell.execute_reply.started":"2023-04-20T02:42:23.793004Z","shell.execute_reply":"2023-04-20T02:42:23.797188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = torchvision.models.detection.maskrcnn_resnet50_fpn(pretrained=True, \n                                                           box_detections_per_img=BOX_DETECTIONS_PER_IMG,\n                                                           image_mean=RESNET_MEAN, \n                                                           image_std=RESNET_STD)    # model with normalization\n\n# Get the number of input features for FastRCNNPredictor, that is\n# the classification + bounding box regression layers for Faster R-CNN \nin_features = model.roi_heads.box_predictor.cls_score.in_features\n# Replace the pre-trained head with a new one that is consistent with our CLASSES_NUM\nmodel.roi_heads.box_predictor = FastRCNNPredictor(in_features, CLASSES_NUM)\n\n\n# Get the number of input features for the mask classifier\nin_features_mask = model.roi_heads.mask_predictor.conv5_mask.in_channels\n# Replace the mask predictor with a new one\nmodel.roi_heads.mask_predictor = MaskRCNNPredictor(in_features_mask, H_LAYER, CLASSES_NUM)","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:23.799855Z","iopub.execute_input":"2023-04-20T02:42:23.800643Z","iopub.status.idle":"2023-04-20T02:42:24.815264Z","shell.execute_reply.started":"2023-04-20T02:42:23.800605Z","shell.execute_reply":"2023-04-20T02:42:24.813796Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"n_batches = len(dataloder_train)\n\nloss_mask_list = np.zeros((EPOCHS, n_batches,))\nloss_total_list = np.zeros((EPOCHS, n_batches,))\n\nmodel.to(DEVICE)\nmodel.train();     # 句尾添加分号 ; 会抑制输出","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:24.816974Z","iopub.execute_input":"2023-04-20T02:42:24.817375Z","iopub.status.idle":"2023-04-20T02:42:27.708513Z","shell.execute_reply.started":"2023-04-20T02:42:24.817303Z","shell.execute_reply":"2023-04-20T02:42:27.707453Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n\nparams = [p for p in model.parameters() if p.requires_grad]\noptimizer = torch.optim.SGD(params, lr=LEARNING_RATE, momentum=MOMENTUM, weight_decay=WEIGHT_DECAY)\n\nlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.1)\n\nfor epoch in range(1, EPOCHS + 1):\n    print(\"=\"*40 + f\" Starting epoch {epoch} of {EPOCHS} \" + \"=\"*40)\n    \n    time_start = time.time()\n    loss_total_accum = 0.0\n    loss_mask_accum = 0.0\n    \n    for batch_idx, (images, targets) in enumerate(dataloder_train, 1):\n    \n        # Predict\n        images = list(image.to(DEVICE) for image in images)\n        targets = [{k: v.to(DEVICE) for k, v in t.items()} for t in targets]\n\n        loss_dict = model(images, targets)\n        loss_total = sum(loss for loss in loss_dict.values())\n        \n        # Backprop\n        optimizer.zero_grad()\n        loss_total.backward()\n        optimizer.step()\n        \n        # Logging\n        loss_total_accum += loss_total.item()\n        loss_mask = loss_dict['loss_mask'].item()\n        loss_mask_accum += loss_mask\n        \n        loss_mask_list[epoch - 1, batch_idx - 1] = loss_mask\n        loss_total_list[epoch - 1, batch_idx - 1] = loss_total.item()\n        \n        if (batch_idx % 50 == 0) or (batch_idx == n_batches):\n            print(f\"[Batch {batch_idx:3d} / {n_batches:3d}] Batch total loss: {loss_total.item():7.3f}. Batch mask loss: {loss_mask:7.3f}\")\n    \n    if USE_SCHEDULER:\n        lr_scheduler.step()\n    \n    # Average training loss at this epoch\n    loss_total_avg = loss_total_accum / n_batches\n    loss_mask_avg = loss_mask_accum / n_batches\n    \n    # Time consuming of this training epoch\n    elapsed = time.time() - time_start\n    \n    \n    torch.save(model.state_dict(), f\"./MyModelSave/pytorch_mrcnn-epoch{epoch}.bin\")\n    \n    print()\n    prefix = f\"[Epoch {epoch:2d} / {EPOCHS:2d}]\"\n    print(f\"{prefix} Epoch average mask loss : {loss_mask_avg:7.3f}\")\n    print(f\"{prefix} Epoch average total loss: {loss_total_avg:7.3f}.\")\n    print(f\"{prefix} time elapsed: [{elapsed:.0f} secs]\")\n    \n    \n    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 6), sharex=True)\n    ax1.set_ylim(0, 1)\n    ax1.plot(loss_mask_list[epoch-1])\n    ax1.set_ylabel(\"Mask Loss\")\n    \n    ax2.set_ylim(0, 3)\n    ax2.plot(loss_total_list[epoch-1])\n    ax2.set_ylabel(\"Total Loss\")\n    ax2.set_xlabel(\"Batches\")\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T02:42:27.709845Z","iopub.execute_input":"2023-04-20T02:42:27.710301Z","iopub.status.idle":"2023-04-20T04:48:43.005376Z","shell.execute_reply.started":"2023-04-20T02:42:27.710209Z","shell.execute_reply":"2023-04-20T04:48:43.004246Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prediction\n\n## Test Dataset and DataLoader","metadata":{}},{"cell_type":"code","source":"class CellTestDataset(Dataset):\n    def __init__(self, test_path, transforms=None):\n        self.transforms = transforms\n        self.image_dir = test_path\n        self.image_ids = [file_name[:-4] for file_name in os.listdir(self.image_dir)]\n        \n    \n    def __getitem__(self, idx):\n        image_id = self.image_ids[idx]\n        image_path = os.path.join(self.image_dir, image_id + \".png\")\n        image = Image.open(image_path).convert(\"RGB\")    # 实现将单通道的灰度图像转换为三通道的\n        \n        if self.transforms is not None:\n            image, _ = self.transforms(image=image, target=None)\n        \n        return {'image': image, 'image_id': image_id}\n    \n    \n    def __len__(self):\n        return len(self.image_ids)","metadata":{"execution":{"iopub.status.busy":"2023-04-20T04:48:43.007240Z","iopub.execute_input":"2023-04-20T04:48:43.008007Z","iopub.status.idle":"2023-04-20T04:48:43.017452Z","shell.execute_reply.started":"2023-04-20T04:48:43.007967Z","shell.execute_reply":"2023-04-20T04:48:43.016099Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dataset_test = CellTestDataset(TEST_PATH, transforms=transforms(train=False))\n# len(dataset_test)   # 3\ndataset_test[0]","metadata":{"execution":{"iopub.status.busy":"2023-04-20T04:48:43.019358Z","iopub.execute_input":"2023-04-20T04:48:43.019722Z","iopub.status.idle":"2023-04-20T04:48:43.070289Z","shell.execute_reply.started":"2023-04-20T04:48:43.019686Z","shell.execute_reply":"2023-04-20T04:48:43.069348Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Predict Testing Dataset","metadata":{}},{"cell_type":"code","source":"def rle_encoding(x):\n    dots = np.where(x.flatten() == 1)[0]\n    run_lengths = []\n    prev = -2\n    for b in dots:\n        if (b>prev+1): run_lengths.extend((b + 1, 0))\n        run_lengths[-1] += 1\n        prev = b\n    \n    return ' '.join(map(str, run_lengths))\n\n\n\n\n\ndef remove_overlapping_pixels(mask, other_masks):\n    for other_mask in other_masks:\n        if np.sum(np.logical_and(mask, other_mask)) > 0:\n            mask[np.logical_and(mask, other_mask)] = 0\n    \n    return mask","metadata":{"execution":{"iopub.status.busy":"2023-04-20T04:48:43.072045Z","iopub.execute_input":"2023-04-20T04:48:43.072427Z","iopub.status.idle":"2023-04-20T04:48:43.080830Z","shell.execute_reply.started":"2023-04-20T04:48:43.072381Z","shell.execute_reply":"2023-04-20T04:48:43.078713Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.eval();\n\nsubmission = []\nfor sample in dataset_test:\n    img = sample['image']\n    image_id = sample['image_id']\n    with torch.no_grad():\n        result = model([img.to(DEVICE)])[0]\n    \n    previous_masks = []\n    for i, mask in enumerate(result[\"masks\"]):\n        \n        # Filter-out low-scoring results. Not tried yet.\n        score = result[\"scores\"][i].cpu().item()\n        if score < MIN_SCORE:                   # MIN_SCORE = 0.59\n            continue\n        \n        mask = mask.cpu().numpy()\n        # Keep only highly likely pixels\n        binary_mask = mask > MASK_THRESHOLD    # MASK_THRESHOLD = 0.5\n        binary_mask = remove_overlapping_pixels(binary_mask, previous_masks)\n        previous_masks.append(binary_mask)\n        rle = rle_encoding(binary_mask)\n        submission.append((image_id, rle))\n    \n    plt.figure(figsize=(12,12))\n    ax1 = plt.subplot(121)\n    ax1.imshow(img.numpy().transpose((1,2,0)))\n    all_preds_masks = np.zeros((HEIGHT, WIDTH))\n    for mask in result['masks'].cpu().detach().numpy():\n        all_preds_masks = np.logical_or(all_preds_masks, mask[0] > MASK_THRESHOLD)\n    ax2 = plt.subplot(122)\n    ax2.imshow(img.numpy().transpose((1,2,0)))\n    ax2.imshow(all_preds_masks, alpha=0.2)\n    plt.tight_layout()\n    plt.show()\n    \n    \n    # Add empty prediction if no RLE was generated for this image\n    all_images_ids = [image_id for image_id, rle in submission]\n    if image_id not in all_images_ids:\n        submission.append((image_id, \"\"))","metadata":{"execution":{"iopub.status.busy":"2023-04-20T04:48:43.082481Z","iopub.execute_input":"2023-04-20T04:48:43.083692Z","iopub.status.idle":"2023-04-20T04:48:49.265006Z","shell.execute_reply.started":"2023-04-20T04:48:43.083650Z","shell.execute_reply":"2023-04-20T04:48:49.264044Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_sub = pd.DataFrame(submission, columns=['id', 'predicted'])\ndf_sub.to_csv(\"/kaggle/working/My_MRCNN.csv\", index=False)\ndf_sub.head()","metadata":{"execution":{"iopub.status.busy":"2023-04-20T04:48:49.266336Z","iopub.execute_input":"2023-04-20T04:48:49.267638Z","iopub.status.idle":"2023-04-20T04:48:49.286630Z","shell.execute_reply.started":"2023-04-20T04:48:49.267596Z","shell.execute_reply":"2023-04-20T04:48:49.285480Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}