{"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":"# Reproducing the Sartotius paper results\n\nThis notebook aims at reproducing the [Sartorius paper on LIVECell](https://www.nature.com/articles/s41592-021-01249-6)\n\nIn their paper they have trained two different models using the LIVECell 2021 dataset:\n* An anchor-free model (CenterMask2)\n* An anchor-based model (ResNeSt)\n\nBoth architectures have been trained on the whole dataset,as well as on each individual cell type.\n\nGiven the impressive results the authors state on zero-shot learning I thought that we could use the pretrained model as is or as a starting point to do transfer learning.\n\nI chose to work with the CenterMask2 model and when trying to make predictions on the competition's data (without retraining) the results were very bad.\nHere I simply try to reproduce the stated performance by simply predicting on the LIVECell test set.\n\nI put here the performance for reference (source: https://github.com/sartorius-research/LIVECell/tree/main/model)\n\n<table class=\"tg\">\n  <tr>\n    <th class=\"tg-0pky\">Architecture</th>\n    <th class=\"tg-0pky\">Dataset</th>\n    <th class=\"tg-0pky\">Box mAP%</th>\n    <th class=\"tg-0pky\">Mask mAP%</th>\n    <th class=\"tg-0pky\">download</th>\n  </tr>\n  <tr>\n    <td rowspan=\"9\" class=\"tg-0pky\">Anchor free</td>\n    <td class=\"tg-0pky\">LIVECell</td>\n    <td class=\"tg-0pky\">48.45</td>\n    <td class=\"tg-0pky\">47.78</td>\n    <td class=\"tg-0lax\"><a href=\"https://github.com/sartorius-research/LIVECell/blob/main/model/anchor_free/livecell_config.yaml\">config</a> | <a href=\"http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/ALL/LIVECell_anchor_free_model.pth\">model </a> \n  </tr>\n  <tr>\n    <td class=\"tg-0pky\">A172</td>\n    <td class=\"tg-0pky\">31.49</td>\n    <td class=\"tg-0pky\">34.57</td>\n    <td class=\"tg-0lax\"><a href=\"https://github.com/sartorius-research/LIVECell/blob/main/model/anchor_free/a172_config.yaml\">config</a> | <a href=\"https://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/A172/LIVECell_anchor_free_a172_model.pth\">model </a> \n  </tr>\n   <tr>\n    <td class=\"tg-0pky\">BT-474</td>\n    <td class=\"tg-0pky\">42.12</td>\n    <td class=\"tg-0pky\">42.60</td>\n    <td class=\"tg-0lax\"><a href=\"https://github.com/sartorius-research/LIVECell/blob/main/model/anchor_free/bt474_config.yaml\">config</a> | <a href=\"https://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/BT474/LIVECell_anchor_free_bt474_model.pth \">model </a> \n  </tr>\n  <tr>\n    <td class=\"tg-0pky\">BV-2</td>\n    <td class=\"tg-0pky\">42.62</td>\n    <td class=\"tg-0pky\">45.69</td>\n    <td class=\"tg-0lax\"><a href=\"https://github.com/sartorius-research/LIVECell/blob/main/model/anchor_free/bv2_config.yaml\">config</a> | <a href=\"https://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/BV2/LIVECell_anchor_free_bv2_model.pth\">model </a> \n  </tr>\n   <tr>\n    <td class=\"tg-0pky\">Huh7</td>\n    <td class=\"tg-0pky\">42.44</td>\n    <td class=\"tg-0pky\">45.85</td>\n    <td class=\"tg-0lax\"><a href=\"https://github.com/sartorius-research/LIVECell/blob/main/model/anchor_free/huh7_config.yaml\">config</a> | <a href=\"https://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/HUH7/LIVECell_anchor_free_huh7_model.pth\">model </a> \n  </tr>\n  <tr>\n    <td class=\"tg-0pky\">MCF7</td>\n    <td class=\"tg-0pky\">36.53</td>\n    <td class=\"tg-0pky\">37.30 </td>\n    <td class=\"tg-0lax\"><a href=\"https://github.com/sartorius-research/LIVECell/blob/main/model/anchor_free/mcf7_config.yaml\">config</a> | <a href=\"https://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/MCF7/LIVECell_anchor_free_mcf7_model.pth\">model </a> \n  </tr>\n  <tr>\n    <td class=\"tg-0pky\">SH-SY5Y</td>\n    <td class=\"tg-0pky\">25.20</td>\n    <td class=\"tg-0pky\">23.91</td>\n    <td class=\"tg-0lax\"><a href=\"https://github.com/sartorius-research/LIVECell/blob/main/model/anchor_free/shsy5y_config.yaml\">config</a> | <a href=\"https://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/SHSY5Y/LIVECell_anchor_free_shsy5y_model.pth\">model </a>\n  </tr>\n  <tr>\n    <td class=\"tg-0pky\">SkBr3</td>\n    <td class=\"tg-0pky\">64.35</td>\n    <td class=\"tg-0pky\">65.85</td>\n    <td class=\"tg-0lax\"><a href=\"https://github.com/sartorius-research/LIVECell/blob/main/model/anchor_free/skbr3_config.yaml\">config</a> | <a href=\"https://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/SKBR3/LIVECell_anchor_free_skbr3_model.pth\">model </a>\n  </tr>\n  <tr>\n    <td class=\"tg-0pky\">SK-OV-3</td>\n    <td class=\"tg-0pky\">46.43</td>\n    <td class=\"tg-0pky\">49.39</td>\n    <td class=\"tg-0lax\"><a href=\"https://github.com/sartorius-research/LIVECell/blob/main/model/anchor_free/skov3_config.yaml\">config</a> | <a href=\"https://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/SKOV3/LIVECell_anchor_free_skov3_model.pth\">model </a>\n  </tr>\n</table>\n\nI am gonna be using the model trained on the whole dataset\n","metadata":{}},{"cell_type":"code","source":"import os\nimport json\nimport yaml\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nfrom collections import defaultdict","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-11-09T00:02:28.939107Z","iopub.execute_input":"2021-11-09T00:02:28.939606Z","iopub.status.idle":"2021-11-09T00:02:28.943587Z","shell.execute_reply.started":"2021-11-09T00:02:28.939566Z","shell.execute_reply":"2021-11-09T00:02:28.942899Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install 'git+https://github.com/facebookresearch/detectron2.git'","metadata":{"_kg_hide-output":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-08T23:45:16.768787Z","iopub.execute_input":"2021-11-08T23:45:16.769076Z","iopub.status.idle":"2021-11-08T23:48:16.055627Z","shell.execute_reply.started":"2021-11-08T23:45:16.769041Z","shell.execute_reply":"2021-11-08T23:48:16.054807Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import detectron2\ndetectron2.__version__","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-11-08T23:48:16.058861Z","iopub.execute_input":"2021-11-08T23:48:16.059105Z","iopub.status.idle":"2021-11-08T23:48:16.755727Z","shell.execute_reply.started":"2021-11-08T23:48:16.059080Z","shell.execute_reply":"2021-11-08T23:48:16.754913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prepare dataset for model evaluation\n\nWe are going to use the LIVECell test set to evaluate the pretrained model. We need to fix 2 things in the annotations so that we can use them:\n* Make the segmentations into a list of dicts rather than a dict of dict\n* Make the image paths relative to the competition's dataset folder","metadata":{}},{"cell_type":"code","source":"# sample TEST_SIZE images to save some GPU quota\nTEST_SIZE = 100  # I put the total size to be fair\n\nlivecell_test_annotations_path = \"/kaggle/input/sartorius-cell-instance-segmentation/LIVECell_dataset_2021/annotations/LIVECell/livecell_coco_test.json\"\n\nwith open(livecell_test_annotations_path) as infile:\n    livecell_test_annotations = json.load(infile)\n    \n\nimages = livecell_test_annotations[\"images\"]\nannotations = livecell_test_annotations[\"annotations\"]\n\n#index annotations by image_id\nannotations_by_image_id = defaultdict(list)\nfor v in annotations.values():\n    annotations_by_image_id[v[\"image_id\"]].append(v)\n\nimage_no = len(images)\nrandom_image_idxs = np.random.choice(image_no, TEST_SIZE, replace=False)\n\nprint(f\"Sampled {TEST_SIZE} images out of {image_no}\")\n\n\nsampled_images = []\nsampled_annotations = []\nfor image_idx in random_image_idxs:\n    image = images[image_idx]\n    \n    # fix image path to be relative to the competition's dataset folder\n    filename = image[\"file_name\"]\n    cell_type = filename.split(\"_\")[0]\n    image[\"file_name\"] = f\"LIVECell_dataset_2021/images/livecell_test_images/{cell_type}/{filename}\"\n    \n    image_id = image[\"id\"]\n    annotations = annotations_by_image_id[image_id]\n    sampled_images.append(image)\n    sampled_annotations.extend(annotations)\n    \nlivecell_test_annotations[\"images\"] = sampled_images\nlivecell_test_annotations[\"annotations\"] = sampled_annotations\n\n\nwith open('/kaggle/working/livecell_coco_test.json', 'w') as outfile:\n    json.dump(livecell_test_annotations, outfile)","metadata":{"execution":{"iopub.status.busy":"2021-11-09T00:04:20.898711Z","iopub.execute_input":"2021-11-09T00:04:20.898999Z","iopub.status.idle":"2021-11-09T00:04:35.949192Z","shell.execute_reply.started":"2021-11-09T00:04:20.898967Z","shell.execute_reply":"2021-11-09T00:04:35.948356Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prepare the model config and weights\n\n* Get the configuration from the LIVECell repo and fix the relative path of the ``_BASE_`` config\n* Download the pretrained model (from the link listed [here](https://github.com/sartorius-research/LIVECell/tree/main/model#usage))","metadata":{}},{"cell_type":"code","source":"# download the center mask config from the LIVECell github repo\n! wget -O /kaggle/working/centermask2_livecell_config.yaml \"https://raw.githubusercontent.com/sartorius-research/LIVECell/main/model/anchor_free/livecell_config.yaml\"\n! wget -O /kaggle/working/centermask2_base_vovnet_config.yaml \"https://raw.githubusercontent.com/sartorius-research/LIVECell/main/model/anchor_free/Base-CenterMask-VoVNet.yaml\" ","metadata":{"execution":{"iopub.status.busy":"2021-11-08T23:48:31.169162Z","iopub.execute_input":"2021-11-08T23:48:31.169594Z","iopub.status.idle":"2021-11-08T23:48:33.246337Z","shell.execute_reply.started":"2021-11-08T23:48:31.169554Z","shell.execute_reply":"2021-11-08T23:48:33.245567Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"WORKING_DIR = Path(\"/kaggle/working\")\nINITIAL_LIVECELL_CONFIG = WORKING_DIR / \"centermask2_livecell_config.yaml\"\nMODEL_CONFIG = WORKING_DIR / \"centermask2_livecell_config_local.yaml\"\nOUTPUT_DIR = WORKING_DIR / \"output\"\n\nos.makedirs(OUTPUT_DIR, exist_ok=True)\n\nwith open(INITIAL_LIVECELL_CONFIG, \"r\") as stream:\n    try:\n        config = yaml.safe_load(stream)\n    except yaml.YAMLError as exc:\n        print(exc)\n        \n        \nconfig[\"_BASE_\"] = str(WORKING_DIR / \"centermask2_base_vovnet_config.yaml\")\nconfig[\"OUTPUT_DIR\"] = str(OUTPUT_DIR)\nconfig[\"DATALOADER\"][\"NUM_WORKERS\"] = 2\nconfig[\"SOLVER\"][\"IMS_PER_BATCH\"] = 2\n\n# test phase choices, taken from the demo script of centermask2\nconfidence_threshold = 0.4\nconfig[\"MODEL\"][\"RETINANET\"][\"SCORE_THRESH_TEST\"] = confidence_threshold\nconfig[\"MODEL\"][\"ROI_HEADS\"][\"SCORE_THRESH_TEST\"] = confidence_threshold\nconfig[\"MODEL\"][\"FCOS\"][\"INFERENCE_TH_TEST\"] = confidence_threshold\n\nwith open(MODEL_CONFIG, 'w') as outfile:\n    yaml.dump(config, outfile, default_flow_style=False)","metadata":{"execution":{"iopub.status.busy":"2021-11-09T00:09:10.700404Z","iopub.execute_input":"2021-11-09T00:09:10.700682Z","iopub.status.idle":"2021-11-09T00:09:10.732253Z","shell.execute_reply.started":"2021-11-09T00:09:10.700651Z","shell.execute_reply":"2021-11-09T00:09:10.731564Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!wget -O /kaggle/working/centermask2_livecell_model.pth http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/models/Anchor_free/ALL/LIVECell_anchor_free_model.pth","metadata":{"execution":{"iopub.status.busy":"2021-11-08T23:48:33.279705Z","iopub.execute_input":"2021-11-08T23:48:33.280245Z","iopub.status.idle":"2021-11-08T23:49:10.911944Z","shell.execute_reply.started":"2021-11-08T23:48:33.280210Z","shell.execute_reply":"2021-11-08T23:49:10.910889Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Adapt CenterMask2 training script\n\nWe follow the instructions in the LIVECell repo:\n\n> Using a custom dataset such as LIVECell together with the detectron2 code base is done by first registering the dataset via the detectron2 python API. In practice this can be done adding the following code to the train_net.py file in the cloned centermask2 repo.\n\n=> For this one,I go and register the dataset directly in the ``main`` function of the ``train_net.py`` script\n\n> The original evaluation script available in the centermask and detectron2 repo is based on there being no more than 100 detections in an image. In our case we can have thousands of annotations and thus the AP evaluation will be off. We therefore provide coco_evaluation.py evaluation script in the code folder. \\\n> To use this script, go into the train_net.py file and remove (or comment out) the current import of COCOEvaluator. Then import COCOEvaluator for from the provided coco_evaluator.py file instead. This will result in AP evaluation supporting for up to 2000 instances in one image.\n\n=> For this one, I override the ``coco_evaluation.py`` file in centermask2 repo with the one from LIVECell\n","metadata":{}},{"cell_type":"code","source":"!git clone https://github.com/youngwanLEE/centermask2.git","metadata":{"execution":{"iopub.status.busy":"2021-11-08T23:49:10.914065Z","iopub.execute_input":"2021-11-08T23:49:10.914363Z","iopub.status.idle":"2021-11-08T23:49:12.473468Z","shell.execute_reply.started":"2021-11-08T23:49:10.914324Z","shell.execute_reply":"2021-11-08T23:49:12.472707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# download the evaluator code from LIVECell and replace the one in the centermask repo\n! wget -O /kaggle/working/centermask2/centermask/evaluation/coco_evaluation.py \"https://raw.githubusercontent.com/sartorius-research/LIVECell/main/code/coco_evaluation.py\"","metadata":{"execution":{"iopub.status.busy":"2021-11-08T23:49:12.476414Z","iopub.execute_input":"2021-11-08T23:49:12.476680Z","iopub.status.idle":"2021-11-08T23:49:13.500823Z","shell.execute_reply.started":"2021-11-08T23:49:12.476644Z","shell.execute_reply":"2021-11-08T23:49:13.500078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The LIVECell evalutation code throws an error. I have commented out the lines cuasing the error (it was for some additional metrics to be printed)\n\nTo load the file in the notebook use:\n\n``%load /kaggle/working/centermask2/centermask/evaluation/coco_evaluation.py``\n\nThen you can save the modified file with the ``%%writefile`` magic as bellow\n\nI used the same magic to modify the training script","metadata":{}},{"cell_type":"code","source":"%%writefile /kaggle/working/centermask2/centermask/evaluation/coco_evaluation.py\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n# Modified by Sangrok Lee and Youngwan Lee (ETRI), 2020. All Rights Reserved.\n# Modified by Christoffer Edlund (Sartorius), 2020. All Rights Reserved.\nimport types\nimport contextlib\nimport copy\nimport io\nimport itertools\nimport json\nimport logging\nimport numpy as np\nimport os\nimport pickle\nfrom collections import OrderedDict\nimport pycocotools.mask as mask_util\nimport torch\nfrom fvcore.common.file_io import PathManager\nfrom pycocotools.coco import COCO\nfrom pycocotools.cocoeval import COCOeval\nfrom tabulate import tabulate\n\nimport detectron2.utils.comm as comm\nfrom detectron2.data import MetadataCatalog\nfrom detectron2.data.datasets.coco import convert_to_coco_json\nfrom detectron2.structures import Boxes, BoxMode, pairwise_iou\nfrom detectron2.utils.logger import create_small_table\n\nfrom detectron2.evaluation.evaluator import DatasetEvaluator\n#from detectron2.evaluation.fast_eval_api import COCOeval_opt\n_use_fast_impl = True\ntry:\n    from fast_coco_eval import COCOeval_fast as COCOeval_opt\nexcept ImportError:\n    print(f\"Could not find fast coco implementation\")\n    _use_fast_impl = False\n\nclass COCOEvaluator(DatasetEvaluator):\n    \"\"\"\n    Evaluate object proposal, instance detection/segmentation, keypoint detection\n    outputs using COCO's metrics and APIs.\n    \"\"\"\n\n    def __init__(self, dataset_name, cfg, distributed, output_dir=None):\n        \"\"\"\n        Args:\n            dataset_name (str): name of the dataset to be evaluated.\n                It must have either the following corresponding metadata:\n                    \"json_file\": the path to the COCO format annotation\n                Or it must be in detectron2's standard dataset format\n                so it can be converted to COCO format automatically.\n            cfg (CfgNode): config instance\n            distributed (True): if True, will collect results from all ranks for evaluation.\n                Otherwise, will evaluate the results in the current process.\n            output_dir (str): optional, an output directory to dump all\n                results predicted on the dataset. The dump contains two files:\n                1. \"instance_predictions.pth\" a file in torch serialization\n                   format that contains all the raw original predictions.\n                2. \"coco_instances_results.json\" a json file in COCO's result\n                   format.\n        \"\"\"\n        print(\"__init__\")\n        self._tasks = self._tasks_from_config(cfg)\n        self._distributed = distributed\n        self._output_dir = output_dir\n\n        self._cpu_device = torch.device(\"cpu\")\n        self._logger = logging.getLogger(__name__)\n\n        self._metadata = MetadataCatalog.get(dataset_name)\n        if not hasattr(self._metadata, \"json_file\"):\n            self._logger.warning(\n                f\"json_file was not found in MetaDataCatalog for '{dataset_name}'.\"\n                \" Trying to convert it to COCO format ...\"\n            )\n\n            cache_path = os.path.join(output_dir, f\"{dataset_name}_coco_format.json\")\n            self._metadata.json_file = cache_path\n            convert_to_coco_json(dataset_name, cache_path)\n\n        json_file = PathManager.get_local_path(self._metadata.json_file)\n        with contextlib.redirect_stdout(io.StringIO()):\n            self._coco_api = COCO(json_file)\n\n        self._kpt_oks_sigmas = cfg.TEST.KEYPOINT_OKS_SIGMAS\n        # Test set json files do not contain annotations (evaluation must be\n        # performed using the COCO evaluation server).\n        self._do_evaluation = \"annotations\" in self._coco_api.dataset\n\n    def reset(self):\n        self._predictions = []\n\n    def _tasks_from_config(self, cfg):\n        \"\"\"\n        Returns:\n            tuple[str]: tasks that can be evaluated under the given configuration.\n        \"\"\"\n        print(\"_tasks_from_config\")\n        tasks = (\"bbox\",)\n        if cfg.MODEL.MASK_ON:\n            tasks = tasks + (\"segm\",)\n        if cfg.MODEL.KEYPOINT_ON:\n            tasks = tasks + (\"keypoints\",)\n        print(f\"tasks: {tasks}\")\n        return tasks\n\n    def process(self, inputs, outputs):\n        \"\"\"\n        Args:\n            inputs: the inputs to a COCO model (e.g., GeneralizedRCNN).\n                It is a list of dict. Each dict corresponds to an image and\n                contains keys like \"height\", \"width\", \"file_name\", \"image_id\".\n            outputs: the outputs of a COCO model. It is a list of dicts with key\n                \"instances\" that contains :class:`Instances`.\n        \"\"\"\n\n        for input, output in zip(inputs, outputs):\n            prediction = {\"image_id\": input[\"image_id\"]}\n\n            # TODO this is ugly\n            if \"instances\" in output:\n                instances = output[\"instances\"].to(self._cpu_device)\n                prediction[\"instances\"] = instances_to_coco_json(instances, input[\"image_id\"])\n            if \"proposals\" in output:\n                prediction[\"proposals\"] = output[\"proposals\"].to(self._cpu_device)\n            self._predictions.append(prediction)\n\n    def evaluate(self):\n\n        print(\"evaluate\")\n\n        if self._distributed:\n            comm.synchronize()\n            predictions = comm.gather(self._predictions, dst=0)\n            predictions = list(itertools.chain(*predictions))\n\n            if not comm.is_main_process():\n                return {}\n        else:\n            predictions = self._predictions\n\n        if len(predictions) == 0:\n            self._logger.warning(\"[COCOEvaluator] Did not receive valid predictions.\")\n            return {}\n\n        if self._output_dir:\n            PathManager.mkdirs(self._output_dir)\n            file_path = os.path.join(self._output_dir, \"instances_predictions.pth\")\n            with PathManager.open(file_path, \"wb\") as f:\n                torch.save(predictions, f)\n\n        self._results = OrderedDict()\n        if \"proposals\" in predictions[0]:\n            self._eval_box_proposals(predictions)\n        if \"instances\" in predictions[0]:\n            self._eval_predictions(set(self._tasks), predictions)\n        # Copy so the caller can do whatever with results\n        return copy.deepcopy(self._results)\n\n    def _eval_predictions(self, tasks, predictions):\n        \"\"\"\n        Evaluate predictions on the given tasks.\n        Fill self._results with the metrics of the tasks.\n        \"\"\"\n\n        print(\"_eval_predictions\")\n        print(f\"use_fast_impl: {_use_fast_impl}\")\n\n        self._logger.info(\"Preparing results for COCO format ...\")\n        coco_results = list(itertools.chain(*[x[\"instances\"] for x in predictions]))\n\n        # unmap the category ids for COCO\n        if hasattr(self._metadata, \"thing_dataset_id_to_contiguous_id\"):\n            reverse_id_mapping = {\n                v: k for k, v in self._metadata.thing_dataset_id_to_contiguous_id.items()\n            }\n            for result in coco_results:\n                category_id = result[\"category_id\"]\n                assert (\n                        category_id in reverse_id_mapping\n                ), \"A prediction has category_id={}, which is not available in the dataset.\".format(\n                    category_id\n                )\n                result[\"category_id\"] = reverse_id_mapping[category_id]\n\n        if self._output_dir:\n            file_path = os.path.join(self._output_dir, \"coco_instances_results.json\")\n            self._logger.info(\"Saving results to {}\".format(file_path))\n            with PathManager.open(file_path, \"w\") as f:\n                f.write(json.dumps(coco_results))\n                f.flush()\n\n        if not self._do_evaluation:\n            self._logger.info(\"Annotations are not available for evaluation.\")\n            return\n\n        self._logger.info(\"Evaluating predictions ...\")\n        for task in sorted(tasks):\n            coco_eval = (\n                _evaluate_predictions_on_coco(\n                    self._coco_api, coco_results, task, kpt_oks_sigmas=self._kpt_oks_sigmas\n                )\n                if len(coco_results) > 0\n                else None  # cocoapi does not handle empty results very well\n            )\n\n            res = self._derive_coco_results(\n                coco_eval, task, class_names=self._metadata.get(\"thing_classes\")\n            )\n            self._results[task] = res\n\n    def _eval_box_proposals(self, predictions):\n        \"\"\"\n        Evaluate the box proposals in predictions.\n        Fill self._results with the metrics for \"box_proposals\" task.\n        \"\"\"\n        print(\"_eval_box_proposals\")\n        if self._output_dir:\n            # Saving generated box proposals to file.\n            # Predicted box_proposals are in XYXY_ABS mode.\n            bbox_mode = BoxMode.XYXY_ABS.value\n            ids, boxes, objectness_logits = [], [], []\n            for prediction in predictions:\n                ids.append(prediction[\"image_id\"])\n                boxes.append(prediction[\"proposals\"].proposal_boxes.tensor.numpy())\n                objectness_logits.append(prediction[\"proposals\"].objectness_logits.numpy())\n\n            proposal_data = {\n                \"boxes\": boxes,\n                \"objectness_logits\": objectness_logits,\n                \"ids\": ids,\n                \"bbox_mode\": bbox_mode,\n            }\n            with PathManager.open(os.path.join(self._output_dir, \"box_proposals.pkl\"), \"wb\") as f:\n                pickle.dump(proposal_data, f)\n\n        if not self._do_evaluation:\n            self._logger.info(\"Annotations are not available for evaluation.\")\n            return\n\n        self._logger.info(\"Evaluating bbox proposals ...\")\n        res = {}\n        areas = {\"all\": \"\", \"small\": \"s\", \"medium\": \"m\", \"large\": \"l\"}\n        for limit in [100, 1000]:\n            for area, suffix in areas.items():\n                stats = _evaluate_box_proposals(predictions, self._coco_api, area=area, limit=limit)\n                key = \"AR{}@{:d}\".format(suffix, limit)\n                res[key] = float(stats[\"ar\"].item() * 100)\n        self._logger.info(\"Proposal metrics: \\n\" + create_small_table(res))\n        self._results[\"box_proposals\"] = res\n\n    def _derive_coco_results(self, coco_eval, iou_type, class_names=None):\n        \"\"\"\n        Derive the desired score numbers from summarized COCOeval.\n        Args:\n            coco_eval (None or COCOEval): None represents no predictions from model.\n            iou_type (str):\n            class_names (None or list[str]): if provided, will use it to predict\n                per-category AP.\n        Returns:\n            a dict of {metric name: score}\n        \"\"\"\n        print(\"_derive_coco_results\")\n\n        metrics = {\n            \"bbox\": [\"AP\", \"AP50\", \"AP75\", \"APs\", \"APm\", \"APl\"],\n            \"segm\": [\"AP\", \"AP50\", \"AP75\", \"APs\", \"APm\", \"APl\"],\n            \"keypoints\": [\"AP\", \"AP50\", \"AP75\", \"APm\", \"APl\"],\n        }[iou_type]\n\n        if coco_eval is None:\n            self._logger.warn(\"No predictions from the model!\")\n            return {metric: float(\"nan\") for metric in metrics}\n\n        # the standard metrics\n        results = {\n            metric: float(coco_eval.stats[idx] * 100 if coco_eval.stats[idx] >= 0 else \"nan\")\n            for idx, metric in enumerate(metrics)\n        }\n        self._logger.info(\n            \"Evaluation results for {}: \\n\".format(iou_type) + create_small_table(results)\n        )\n        if not np.isfinite(sum(results.values())):\n            self._logger.info(\"Note that some metrics cannot be computed.\")\n\n        if class_names is None or len(class_names) <= 1:\n            return results\n        # Compute per-category AP\n        # from https://github.com/facebookresearch/Detectron/blob/a6a835f5b8208c45d0dce217ce9bbda915f44df7/detectron/datasets/json_dataset_evaluator.py#L222-L252 # noqa\n        precisions = coco_eval.eval[\"precision\"]\n        # precision has dims (iou, recall, cls, area range, max dets)\n        assert len(class_names) == precisions.shape[2]\n\n        results_per_category = []\n        for idx, name in enumerate(class_names):\n            # area range index 0: all area ranges\n            # max dets index -1: typically 100 per image\n            precision = precisions[:, :, idx, 0, -1]\n            precision = precision[precision > -1]\n            ap = np.mean(precision) if precision.size else float(\"nan\")\n            results_per_category.append((\"{}\".format(name), float(ap * 100)))\n\n        # tabulate it\n        N_COLS = min(6, len(results_per_category) * 2)\n        results_flatten = list(itertools.chain(*results_per_category))\n        results_2d = itertools.zip_longest(*[results_flatten[i::N_COLS] for i in range(N_COLS)])\n        table = tabulate(\n            results_2d,\n            tablefmt=\"pipe\",\n            floatfmt=\".3f\",\n            headers=[\"category\", \"AP\"] * (N_COLS // 2),\n            numalign=\"left\",\n        )\n        self._logger.info(\"Per-category {} AP: \\n\".format(iou_type) + table)\n\n        results.update({\"AP-\" + name: ap for name, ap in results_per_category})\n        return results\n\n\ndef instances_to_coco_json(instances, img_id):\n    \"\"\"\n    Dump an \"Instances\" object to a COCO-format json that's used for evaluation.\n    Args:\n        instances (Instances):\n        img_id (int): the image id\n    Returns:\n        list[dict]: list of json annotations in COCO format.\n    \"\"\"\n\n    num_instance = len(instances)\n    if num_instance == 0:\n        return []\n\n    boxes = instances.pred_boxes.tensor.numpy()\n    boxes = BoxMode.convert(boxes, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS)\n    boxes = boxes.tolist()\n    scores = instances.scores.tolist()\n    classes = instances.pred_classes.tolist()\n\n    has_mask = instances.has(\"pred_masks\")\n    has_mask_scores = instances.has(\"mask_scores\")\n    if has_mask:\n        # use RLE to encode the masks, because they are too large and takes memory\n        # since this evaluator stores outputs of the entire dataset\n        rles = [\n            mask_util.encode(np.array(mask[:, :, None], order=\"F\", dtype=\"uint8\"))[0]\n            for mask in instances.pred_masks\n        ]\n        for rle in rles:\n            # \"counts\" is an array encoded by mask_util as a byte-stream. Python3's\n            # json writer which always produces strings cannot serialize a bytestream\n            # unless you decode it. Thankfully, utf-8 works out (which is also what\n            # the pycocotools/_mask.pyx does).\n            rle[\"counts\"] = rle[\"counts\"].decode(\"utf-8\")\n\n        if has_mask_scores:\n            mask_scores = instances.mask_scores.tolist()\n\n    has_keypoints = instances.has(\"pred_keypoints\")\n    if has_keypoints:\n        keypoints = instances.pred_keypoints\n\n    results = []\n    for k in range(num_instance):\n        result = {\n            \"image_id\": img_id,\n            \"category_id\": classes[k],\n            \"bbox\": boxes[k],\n            \"score\": scores[k],\n        }\n        if has_mask:\n            result[\"segmentation\"] = rles[k]\n            if has_mask_scores:\n                result[\"mask_score\"] = mask_scores[k]\n\n        if has_keypoints:\n            # In COCO annotations,\n            # keypoints coordinates are pixel indices.\n            # However our predictions are floating point coordinates.\n            # Therefore we subtract 0.5 to be consistent with the annotation format.\n            # This is the inverse of data loading logic in `datasets/coco.py`.\n            keypoints[k][:, :2] -= 0.5\n            result[\"keypoints\"] = keypoints[k].flatten().tolist()\n        results.append(result)\n    return results\n\n\n# inspired from Detectron:\n# https://github.com/facebookresearch/Detectron/blob/a6a835f5b8208c45d0dce217ce9bbda915f44df7/detectron/datasets/json_dataset_evaluator.py#L255 # noqa\ndef _evaluate_box_proposals(dataset_predictions, coco_api, thresholds=None, area=\"all\", limit=None):\n    \"\"\"\n    Evaluate detection proposal recall metrics. This function is a much\n    faster alternative to the official COCO API recall evaluation code. However,\n    it produces slightly different results.\n    \"\"\"\n#    print(\"_evaluate_box_proposals\")\n    # Record max overlap value for each gt box\n    # Return vector of overlap values\n    areas = {\n        \"all\": 0,\n        \"small\": 1,\n        \"medium\": 2,\n        \"large\": 3,\n        \"96-128\": 4,\n        \"128-256\": 5,\n        \"256-512\": 6,\n        \"512-inf\": 7,\n    }\n\n\n    area_ranges = [\n        [0 ** 2, 1e5 ** 2],  # all\n        [0 ** 2, 18 ** 2],  # small org: 0 - 32\n        [18 ** 2, 31 ** 2],  # medium org: 32 - 96\n        [31 ** 2, 1e5 ** 2],  # large org: 96 - 1e5\n        [31 ** 2, 128 ** 2],  # org: 96-128\n        [128 ** 2, 256 ** 2],  # 128-256\n        [256 ** 2, 512 ** 2],  # 256-512\n        [512 ** 2, 1e5 ** 2],\n    ]  # 512-inf\n\n    \"\"\"\n    area_ranges = [\n        [0 ** 2, 1e5 ** 2],  # all\n        [0 ** 2, 28 ** 2],  # small org: 0 - 32\n        [28 ** 2, 94 ** 2],  # medium org: 32 - 96\n        [94 ** 2, 1e5 ** 2],  # large org: 96 - 1e5 - our 64\n        [94 ** 2, 128 ** 2],  #  org: 96-128\n        [128 ** 2, 256 ** 2],  # 128-256\n        [256 ** 2, 512 ** 2],  # 256-512\n        [512 ** 2, 1e5 ** 2],\n    ]  # 512-inf\n    \"\"\"\n    assert area in areas, \"Unknown area range: {}\".format(area)\n    area_range = area_ranges[areas[area]]\n    gt_overlaps = []\n    num_pos = 0\n\n    for prediction_dict in dataset_predictions:\n        predictions = prediction_dict[\"proposals\"]\n\n        # sort predictions in descending order\n        # TODO maybe remove this and make it explicit in the documentation\n        inds = predictions.objectness_logits.sort(descending=True)[1]\n        predictions = predictions[inds]\n\n        ann_ids = coco_api.getAnnIds(imgIds=prediction_dict[\"image_id\"])\n        anno = coco_api.loadAnns(ann_ids)\n        gt_boxes = [\n            BoxMode.convert(obj[\"bbox\"], BoxMode.XYWH_ABS, BoxMode.XYXY_ABS)\n            for obj in anno\n            if obj[\"iscrowd\"] == 0\n        ]\n        gt_boxes = torch.as_tensor(gt_boxes).reshape(-1, 4)  # guard against no boxes\n        gt_boxes = Boxes(gt_boxes)\n        gt_areas = torch.as_tensor([obj[\"area\"] for obj in anno if obj[\"iscrowd\"] == 0])\n\n        if len(gt_boxes) == 0 or len(predictions) == 0:\n            continue\n\n        valid_gt_inds = (gt_areas >= area_range[0]) & (gt_areas <= area_range[1])\n        gt_boxes = gt_boxes[valid_gt_inds]\n\n        num_pos += len(gt_boxes)\n\n        if len(gt_boxes) == 0:\n            continue\n\n        if limit is not None and len(predictions) > limit:\n            predictions = predictions[:limit]\n\n        overlaps = pairwise_iou(predictions.proposal_boxes, gt_boxes)\n\n        _gt_overlaps = torch.zeros(len(gt_boxes))\n        for j in range(min(len(predictions), len(gt_boxes))):\n            # find which proposal box maximally covers each gt box\n            # and get the iou amount of coverage for each gt box\n            max_overlaps, argmax_overlaps = overlaps.max(dim=0)\n\n            # find which gt box is 'best' covered (i.e. 'best' = most iou)\n            gt_ovr, gt_ind = max_overlaps.max(dim=0)\n            assert gt_ovr >= 0\n            # find the proposal box that covers the best covered gt box\n            box_ind = argmax_overlaps[gt_ind]\n            # record the iou coverage of this gt box\n            _gt_overlaps[j] = overlaps[box_ind, gt_ind]\n            assert _gt_overlaps[j] == gt_ovr\n            # mark the proposal box and the gt box as used\n            overlaps[box_ind, :] = -1\n            overlaps[:, gt_ind] = -1\n\n        # append recorded iou coverage level\n        gt_overlaps.append(_gt_overlaps)\n    gt_overlaps = torch.cat(gt_overlaps, dim=0)\n    gt_overlaps, _ = torch.sort(gt_overlaps)\n\n    if thresholds is None:\n        step = 0.05\n        thresholds = torch.arange(0.5, 0.95 + 1e-5, step, dtype=torch.float32)\n    recalls = torch.zeros_like(thresholds)\n    # compute recall for each iou threshold\n    for i, t in enumerate(thresholds):\n        recalls[i] = (gt_overlaps >= t).float().sum() / float(num_pos)\n    # ar = 2 * np.trapz(recalls, thresholds)\n    ar = recalls.mean()\n    return {\n        \"ar\": ar,\n        \"recalls\": recalls,\n        \"thresholds\": thresholds,\n        \"gt_overlaps\": gt_overlaps,\n        \"num_pos\": num_pos,\n    }\n\n\ndef _evaluate_predictions_on_coco(coco_gt, coco_results, iou_type, kpt_oks_sigmas=None, use_fast_impl=False):\n    \"\"\"\n    Evaluate the coco results using COCOEval API.\n    \"\"\"\n#    print(\"_evaluate_predictions_on_coco\")\n    assert len(coco_results) > 0\n\n    #Insert this code to increase the number of detections possible /Christoffer :\n\n    def summarize_2(self, all_prec=False):\n            '''\n            Compute and display summary metrics for evaluation results.\n            Note this functin can *only* be applied on the default parameter setting\n            '''\n\n            print(\"In method\")\n            def _summarize(ap=1, iouThr=None, areaRng='all', maxDets=2000):\n                p = self.params\n                iStr = ' {:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}'\n                titleStr = 'Average Precision' if ap == 1 else 'Average Recall'\n                typeStr = '(AP)' if ap == 1 else '(AR)'\n                iouStr = '{:0.2f}:{:0.2f}'.format(p.iouThrs[0], p.iouThrs[-1]) \\\n                    if iouThr is None else '{:0.2f}'.format(iouThr)\n\n                aind = [i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng]\n                mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets]\n                if ap == 1:\n                    # dimension of precision: [TxRxKxAxM]\n                    s = self.eval['precision']\n                    # IoU\n                    if iouThr is not None:\n                        t = np.where(iouThr == p.iouThrs)[0]\n                        s = s[t]\n                    s = s[:, :, :, aind, mind]\n                else:\n                    # dimension of recall: [TxKxAxM]\n                    s = self.eval['recall']\n                    if iouThr is not None:\n                        t = np.where(iouThr == p.iouThrs)[0]\n                        s = s[t]\n                    s = s[:, :, aind, mind]\n                if len(s[s > -1]) == 0:\n                    mean_s = -1\n                else:\n                    mean_s = np.mean(s[s > -1])\n                print(iStr.format(titleStr, typeStr, iouStr, areaRng, maxDets, mean_s))\n                return mean_s\n\n            def _summarizeDets():\n\n                stats = np.zeros((12,))\n                stats[0] = _summarize(1, maxDets=self.params.maxDets[2])\n                stats[1] = _summarize(1, iouThr=.5, maxDets=self.params.maxDets[2])\n                stats[2] = _summarize(1, iouThr=.75, maxDets=self.params.maxDets[2])\n                stats[3] = _summarize(1, areaRng='small', maxDets=self.params.maxDets[2])\n                stats[4] = _summarize(1, areaRng='medium', maxDets=self.params.maxDets[2])\n                stats[5] = _summarize(1, areaRng='large', maxDets=self.params.maxDets[2])\n                stats[6] = _summarize(0, maxDets=self.params.maxDets[0])\n                stats[7] = _summarize(0, maxDets=self.params.maxDets[1])\n                stats[8] = _summarize(0, maxDets=self.params.maxDets[2])\n                stats[9] = _summarize(0, areaRng='small', maxDets=self.params.maxDets[2])\n                stats[10] = _summarize(0, areaRng='medium', maxDets=self.params.maxDets[2])\n                stats[11] = _summarize(0, areaRng='large', maxDets=self.params.maxDets[2])\n                return stats\n\n\n            def _summarizeKps():\n                stats = np.zeros((10,))\n                stats[0] = _summarize(1, maxDets=self.params.maxDets[2])\n                stats[1] = _summarize(1, maxDets=self.params.maxDets[2], iouThr=.5)\n                stats[2] = _summarize(1, maxDets=self.params.maxDets[2], iouThr=.75)\n                stats[3] = _summarize(1, maxDets=self.params.maxDets[2], areaRng='medium')\n                stats[4] = _summarize(1, maxDets=self.params.maxDets[2], areaRng='large')\n                stats[5] = _summarize(0, maxDets=self.params.maxDets[2])\n                stats[6] = _summarize(0, maxDets=self.params.maxDets[2], iouThr=.5)\n                stats[7] = _summarize(0, maxDets=self.params.maxDets[2], iouThr=.75)\n                stats[8] = _summarize(0, maxDets=self.params.maxDets[2], areaRng='medium')\n                stats[9] = _summarize(0, maxDets=self.params.maxDets[2], areaRng='large')\n                return stats\n\n            if not self.eval:\n                raise Exception('Please run accumulate() first')\n            iouType = self.params.iouType\n            if iouType == 'segm' or iouType == 'bbox':\n                summarize = _summarizeDets\n            elif iouType == 'keypoints':\n                summarize = _summarizeKps\n            self.stats = summarize()\n\n\n    if iou_type == \"segm\":\n        coco_results = copy.deepcopy(coco_results)\n        # When evaluating mask AP, if the results contain bbox, cocoapi will\n        # use the box area as the area of the instance, instead of the mask area.\n        # This leads to a different definition of small/medium/large.\n        # We remove the bbox field to let mask AP use mask area.\n        # We also replace `score` with `mask_score` when using mask scoring.\n        has_mask_scores = \"mask_score\" in coco_results[0]\n\n        for c in coco_results:\n            c.pop(\"bbox\", None)\n            if has_mask_scores:\n                c[\"score\"] = c[\"mask_score\"]\n                del c[\"mask_score\"]\n\n    coco_dt = coco_gt.loadRes(coco_results)\n    coco_eval = (COCOeval_opt if _use_fast_impl else COCOeval)(coco_gt, coco_dt, iou_type)\n    # Use the COCO default keypoint OKS sigmas unless overrides are specified\n    if kpt_oks_sigmas:\n        coco_eval.params.kpt_oks_sigmas = np.array(kpt_oks_sigmas)\n\n    if iou_type == \"keypoints\":\n        num_keypoints = len(coco_results[0][\"keypoints\"]) // 3\n        assert len(coco_eval.params.kpt_oks_sigmas) == num_keypoints, (\n            \"[COCOEvaluator] The length of cfg.TEST.KEYPOINT_OKS_SIGMAS (default: 17) \"\n            \"must be equal to the number of keypoints. However the prediction has {} \"\n            \"keypoints! For more information please refer to \"\n            \"http://cocodataset.org/#keypoints-eval.\".format(num_keypoints)\n        )\n\n    coco_eval.params.catIds = [1]\n    coco_eval.params.useCats = 0\n    coco_eval.params.maxDets = [100, 500, 2000]\n\n    coco_eval.params.areaRng = [[0 ** 2, 1e5 ** 2], [0 ** 2, 18 ** 2], [18 ** 2, 31 ** 2], [31 ** 2, 1e5 ** 2]]\n    coco_eval.params.areaRngLbl = ['all', 'small', 'medium', 'large']\n\n    print(f\"Size parameters: {coco_eval.params.areaRng}\")\n\n    coco_eval.summarize = types.MethodType(summarize_2, coco_eval)\n\n    coco_eval.evaluate()\n    coco_eval.accumulate()\n    coco_eval.summarize()\n\n#     \"\"\"\n#     Added code to produce precision and recall for all iou levels / Chris\n#     \"\"\"\n#     precisions = coco_eval.eval['precision']\n#     recalls = coco_eval.eval['recall']\n\n#     # IoU threshold | instances | Categories | areas | max dets\n#     pre_per_iou = [precisions[iou_idx, :, :, 0, -1].mean() for iou_idx in range(precisions.shape[0])]\n#     rec_pre_iou = [recalls[iou_idx, :, :, 0, -1].mean() for iou_idx in range(recalls.shape[0])]\n\n#     print(f\"Precision and Recall per iou: {coco_eval.params.iouThrs}\")\n#     print(np.round(np.array(pre_per_iou), 4))\n#     print(np.round(np.array(rec_pre_iou), 4))\n\n    return coco_eval\n","metadata":{"execution":{"iopub.status.busy":"2021-11-08T23:49:13.503401Z","iopub.execute_input":"2021-11-08T23:49:13.503692Z","iopub.status.idle":"2021-11-08T23:49:13.525466Z","shell.execute_reply.started":"2021-11-08T23:49:13.503655Z","shell.execute_reply":"2021-11-08T23:49:13.524602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%writefile centermask2/train_net.py\n# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved\n# Modified by Youngwan Lee (ETRI), 2020. All Rights Reserved.\nimport logging\nimport os\nfrom collections import OrderedDict\nimport torch\n\nimport detectron2.utils.comm as comm\nfrom detectron2.data import MetadataCatalog\nfrom detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, hooks, launch\nfrom detectron2.evaluation import (\n    # CityscapesInstanceEvaluator,\n    # CityscapesSemSegEvaluator,\n    # COCOEvaluator,\n    COCOPanopticEvaluator,\n    DatasetEvaluators,\n    LVISEvaluator,\n    PascalVOCDetectionEvaluator,\n    SemSegEvaluator,\n    verify_results,\n)\nfrom centermask.evaluation import (\n    COCOEvaluator,\n    CityscapesInstanceEvaluator,\n    CityscapesSemSegEvaluator\n)\nfrom detectron2.modeling import GeneralizedRCNNWithTTA\nfrom detectron2.checkpoint import DetectionCheckpointer\nfrom centermask.config import get_cfg\nfrom detectron2.data.datasets import register_coco_instances # add this import to register new datasets\n\n\nclass Trainer(DefaultTrainer):\n    \"\"\"\n    This is the same Trainer except that we rewrite the\n    `build_train_loader` method.\n    \"\"\"\n\n\n\n    @classmethod\n    def build_evaluator(cls, cfg, dataset_name, output_folder=None):\n        \"\"\"\n        Create evaluator(s) for a given dataset.\n        This uses the special metadata \"evaluator_type\" associated with each builtin dataset.\n        For your own dataset, you can simply create an evaluator manually in your\n        script and do not have to worry about the hacky if-else logic here.\n        \"\"\"\n        if output_folder is None:\n            output_folder = os.path.join(cfg.OUTPUT_DIR, \"inference\")\n        evaluator_list = []\n        evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type\n        if evaluator_type in [\"sem_seg\", \"coco_panoptic_seg\"]:\n            evaluator_list.append(\n                SemSegEvaluator(\n                    dataset_name,\n                    distributed=True,\n                    output_dir=output_folder,\n                )\n            )\n        if evaluator_type in [\"coco\", \"coco_panoptic_seg\"]:\n            evaluator_list.append(COCOEvaluator(dataset_name, cfg, True, output_dir=output_folder)) # changed constructor to match the one from LIVECell\n        if evaluator_type == \"coco_panoptic_seg\":\n            evaluator_list.append(COCOPanopticEvaluator(dataset_name, output_folder))\n        if evaluator_type == \"cityscapes_instance\":\n            assert (\n                torch.cuda.device_count() >= comm.get_rank()\n            ), \"CityscapesEvaluator currently do not work with multiple machines.\"\n            return CityscapesInstanceEvaluator(dataset_name)\n        if evaluator_type == \"cityscapes_sem_seg\":\n            assert (\n                torch.cuda.device_count() >= comm.get_rank()\n            ), \"CityscapesEvaluator currently do not work with multiple machines.\"\n            return CityscapesSemSegEvaluator(dataset_name)\n        elif evaluator_type == \"pascal_voc\":\n            return PascalVOCDetectionEvaluator(dataset_name)\n        elif evaluator_type == \"lvis\":\n            return LVISEvaluator(dataset_name, output_dir=output_folder)\n        if len(evaluator_list) == 0:\n            raise NotImplementedError(\n                \"no Evaluator for the dataset {} with the type {}\".format(\n                    dataset_name, evaluator_type\n                )\n            )\n        elif len(evaluator_list) == 1:\n            return evaluator_list[0]\n        return DatasetEvaluators(evaluator_list)\n\n    @classmethod\n    def test_with_TTA(cls, cfg, model):\n        logger = logging.getLogger(\"detectron2.trainer\")\n        # In the end of training, run an evaluation with TTA\n        # Only support some R-CNN models.\n        logger.info(\"Running inference with test-time augmentation ...\")\n        model = GeneralizedRCNNWithTTA(cfg, model)\n        evaluators = [\n            cls.build_evaluator(\n                cfg, name, output_folder=os.path.join(cfg.OUTPUT_DIR, \"inference_TTA\")\n            )\n            for name in cfg.DATASETS.TEST\n        ]\n        res = cls.test(cfg, model, evaluators)\n        res = OrderedDict({k + \"_TTA\": v for k, v in res.items()})\n        return res\n\n\n\ndef setup(args):\n    \"\"\"\n    Create configs and perform basic setups.\n    \"\"\"\n    cfg = get_cfg()\n    cfg.merge_from_file(args.config_file)\n    cfg.merge_from_list(args.opts)\n    cfg.freeze()\n    default_setup(cfg, args)\n    return cfg\n\n\ndef main(args):\n    register_coco_instances(\"TEST\", {}, '/kaggle/working/livecell_coco_test.json', '/kaggle/input/sartorius-cell-instance-segmentation')\n    cfg = setup(args)\n\n    if args.eval_only:\n        model = Trainer.build_model(cfg)\n        DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load(\n            cfg.MODEL.WEIGHTS, resume=args.resume\n        )\n        res = Trainer.test(cfg, model)\n        if cfg.TEST.AUG.ENABLED:\n            res.update(Trainer.test_with_TTA(cfg, model))\n        if comm.is_main_process():\n            verify_results(cfg, res)\n        return res\n\n    \"\"\"\n    If you'd like to do anything fancier than the standard training logic,\n    consider writing your own training loop or subclassing the trainer.\n    \"\"\"\n    trainer = Trainer(cfg)\n    trainer.resume_or_load(resume=args.resume)\n    if cfg.TEST.AUG.ENABLED:\n        trainer.register_hooks(\n            [hooks.EvalHook(0, lambda: trainer.test_with_TTA(cfg, trainer.model))]\n        )\n    return trainer.train()\n\n\nif __name__ == \"__main__\":\n    args = default_argument_parser().parse_args()\n    print(\"Command Line Args:\", args)\n    launch(\n        main,\n        args.num_gpus,\n        num_machines=args.num_machines,\n        machine_rank=args.machine_rank,\n        dist_url=args.dist_url,\n        args=(args,),\n    )\n","metadata":{"execution":{"iopub.status.busy":"2021-11-08T23:49:13.527016Z","iopub.execute_input":"2021-11-08T23:49:13.527280Z","iopub.status.idle":"2021-11-08T23:49:13.541735Z","shell.execute_reply.started":"2021-11-08T23:49:13.527246Z","shell.execute_reply":"2021-11-08T23:49:13.540969Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!python centermask2/train_net.py  --config-file centermask2_livecell_config_local.yaml --eval-only MODEL.WEIGHTS /kaggle/working/centermask2_livecell_model.pth","metadata":{"execution":{"iopub.status.busy":"2021-11-09T00:09:24.948873Z","iopub.execute_input":"2021-11-09T00:09:24.949570Z","iopub.status.idle":"2021-11-09T00:09:34.657113Z","shell.execute_reply.started":"2021-11-09T00:09:24.949534Z","shell.execute_reply":"2021-11-09T00:09:34.656196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!python centermask2/train_net.py --help","metadata":{"execution":{"iopub.status.busy":"2021-11-08T22:34:47.114024Z","iopub.execute_input":"2021-11-08T22:34:47.11443Z","iopub.status.idle":"2021-11-08T22:34:49.063663Z","shell.execute_reply.started":"2021-11-08T22:34:47.114395Z","shell.execute_reply":"2021-11-08T22:34:49.0628Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_predictor(config_path, confidence_threshold=0.4):\n    cfg = get_cfg()\n    cfg.merge_from_file(config_path)\n    cfg.MODEL.WEIGHTS = \"/kaggle/working/centermask2_livecell_model.pth\"\n    cfg.MODEL.RETINANET.SCORE_THRESH_TEST = confidence_threshold\n    cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = confidence_threshold\n    cfg.MODEL.FCOS.INFERENCE_TH_TEST = confidence_threshold\n    cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = confidence_threshold\n    cfg.freeze()\n    predictor = DefaultPredictor(cfg)\n    return predictor","metadata":{"execution":{"iopub.status.busy":"2021-11-09T00:09:50.243609Z","iopub.execute_input":"2021-11-09T00:09:50.243901Z","iopub.status.idle":"2021-11-09T00:09:50.249922Z","shell.execute_reply.started":"2021-11-09T00:09:50.243870Z","shell.execute_reply":"2021-11-09T00:09:50.249084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import sys\nCENTERMASK2_DIR=Path(\"/kaggle/working/centermask2/\")\nsys.path.append(str(CENTERMASK2_DIR))\n\nimport cv2\nimport matplotlib.pyplot as plt\n\nfrom detectron2.data.datasets import register_coco_instances\nfrom detectron2.data import MetadataCatalog, DatasetCatalog\nfrom detectron2.utils.visualizer import Visualizer, ColorMode\nfrom centermask.config import get_cfg\nfrom detectron2.engine import default_setup\nfrom detectron2.engine import DefaultPredictor\n\ndataDir=Path('../input/sartorius-cell-instance-segmentation/')\nregister_coco_instances(\"TEST\", {}, '/kaggle/working/livecell_coco_test.json', '/kaggle/input/sartorius-cell-instance-segmentation')\nmetadata = MetadataCatalog.get('TEST')\ntest_ds = DatasetCatalog.get('TEST')","metadata":{"execution":{"iopub.status.busy":"2021-11-09T00:09:56.282315Z","iopub.execute_input":"2021-11-09T00:09:56.282693Z","iopub.status.idle":"2021-11-09T00:09:56.330199Z","shell.execute_reply.started":"2021-11-09T00:09:56.282655Z","shell.execute_reply":"2021-11-09T00:09:56.328129Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictor = get_predictor(MODEL_CONFIG, confidence_threshold=0.4)\n\nimage_ids = [42, 69]\nfor i in range(10):\n    d = test_ds[i]\n    img = cv2.imread(d[\"file_name\"])\n\n    outputs = predictor(img)\n    pred_masks = outputs['instances'].to(\"cpu\")\n\n    visualizer_target = Visualizer(img[:, :, ::-1], metadata=metadata)\n    out_target = visualizer_target.draw_dataset_dict(d)\n    visualizer_pred = Visualizer(img[:, :, ::-1], metadata=metadata)\n    out_pred = visualizer_pred.draw_instance_predictions(pred_masks)\n\n    _, axs = plt.subplots(1,2, figsize=(40,15))\n    axs[0].imshow(out_target.get_image()[:, :, ::-1])\n    axs[1].imshow(out_pred.get_image()[:, :, ::-1])","metadata":{"execution":{"iopub.status.busy":"2021-11-09T00:10:01.367482Z","iopub.execute_input":"2021-11-09T00:10:01.367757Z","iopub.status.idle":"2021-11-09T00:10:32.729304Z","shell.execute_reply.started":"2021-11-09T00:10:01.367728Z","shell.execute_reply":"2021-11-09T00:10:32.728402Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}