CoolFace
Apppublic

coreml-community/ControlNet-v1-1-Annotators-cpu

sourceHugging Facemitupdated 2y agoView on Hugging Face
15likes
panoptic_evaluation.py200 linesDownload Raw Back to evaluation
1# Copyright (c) Facebook, Inc. and its affiliates.2import contextlib3import io4import itertools5import json6import logging7import numpy as np8import os9import tempfile10from collections import OrderedDict11from typing import Optional12from PIL import Image13from tabulate import tabulate14 15from annotator.oneformer.detectron2.data import MetadataCatalog16from annotator.oneformer.detectron2.utils import comm17from annotator.oneformer.detectron2.utils.file_io import PathManager18 19from .evaluator import DatasetEvaluator20 21logger = logging.getLogger(__name__)22 23 24class COCOPanopticEvaluator(DatasetEvaluator):25    """26    Evaluate Panoptic Quality metrics on COCO using PanopticAPI.27    It saves panoptic segmentation prediction in `output_dir`28 29    It contains a synchronize call and has to be called from all workers.30    """31 32    def __init__(self, dataset_name: str, output_dir: Optional[str] = None):33        """34        Args:35            dataset_name: name of the dataset36            output_dir: output directory to save results for evaluation.37        """38        self._metadata = MetadataCatalog.get(dataset_name)39        self._thing_contiguous_id_to_dataset_id = {40            v: k for k, v in self._metadata.thing_dataset_id_to_contiguous_id.items()41        }42        self._stuff_contiguous_id_to_dataset_id = {43            v: k for k, v in self._metadata.stuff_dataset_id_to_contiguous_id.items()44        }45 46        self._output_dir = output_dir47        if self._output_dir is not None:48            PathManager.mkdirs(self._output_dir)49 50    def reset(self):51        self._predictions = []52 53    def _convert_category_id(self, segment_info):54        isthing = segment_info.pop("isthing", None)55        if isthing is None:56            # the model produces panoptic category id directly. No more conversion needed57            return segment_info58        if isthing is True:59            segment_info["category_id"] = self._thing_contiguous_id_to_dataset_id[60                segment_info["category_id"]61            ]62        else:63            segment_info["category_id"] = self._stuff_contiguous_id_to_dataset_id[64                segment_info["category_id"]65            ]66        return segment_info67 68    def process(self, inputs, outputs):69        from panopticapi.utils import id2rgb70 71        for input, output in zip(inputs, outputs):72            panoptic_img, segments_info = output["panoptic_seg"]73            panoptic_img = panoptic_img.cpu().numpy()74            if segments_info is None:75                # If "segments_info" is None, we assume "panoptic_img" is a76                # H*W int32 image storing the panoptic_id in the format of77                # category_id * label_divisor + instance_id. We reserve -1 for78                # VOID label, and add 1 to panoptic_img since the official79                # evaluation script uses 0 for VOID label.80                label_divisor = self._metadata.label_divisor81                segments_info = []82                for panoptic_label in np.unique(panoptic_img):83                    if panoptic_label == -1:84                        # VOID region.85                        continue86                    pred_class = panoptic_label // label_divisor87                    isthing = (88                        pred_class in self._metadata.thing_dataset_id_to_contiguous_id.values()89                    )90                    segments_info.append(91                        {92                            "id": int(panoptic_label) + 1,93                            "category_id": int(pred_class),94                            "isthing": bool(isthing),95                        }96                    )97                # Official evaluation script uses 0 for VOID label.98                panoptic_img += 199 100            file_name = os.path.basename(input["file_name"])101            file_name_png = os.path.splitext(file_name)[0] + ".png"102            with io.BytesIO() as out:103                Image.fromarray(id2rgb(panoptic_img)).save(out, format="PNG")104                segments_info = [self._convert_category_id(x) for x in segments_info]105                self._predictions.append(106                    {107                        "image_id": input["image_id"],108                        "file_name": file_name_png,109                        "png_string": out.getvalue(),110                        "segments_info": segments_info,111                    }112                )113 114    def evaluate(self):115        comm.synchronize()116 117        self._predictions = comm.gather(self._predictions)118        self._predictions = list(itertools.chain(*self._predictions))119        if not comm.is_main_process():120            return121 122        # PanopticApi requires local files123        gt_json = PathManager.get_local_path(self._metadata.panoptic_json)124        gt_folder = PathManager.get_local_path(self._metadata.panoptic_root)125 126        with tempfile.TemporaryDirectory(prefix="panoptic_eval") as pred_dir:127            logger.info("Writing all panoptic predictions to {} ...".format(pred_dir))128            for p in self._predictions:129                with open(os.path.join(pred_dir, p["file_name"]), "wb") as f:130                    f.write(p.pop("png_string"))131 132            with open(gt_json, "r") as f:133                json_data = json.load(f)134            json_data["annotations"] = self._predictions135 136            output_dir = self._output_dir or pred_dir137            predictions_json = os.path.join(output_dir, "predictions.json")138            with PathManager.open(predictions_json, "w") as f:139                f.write(json.dumps(json_data))140 141            from panopticapi.evaluation import pq_compute142 143            with contextlib.redirect_stdout(io.StringIO()):144                pq_res = pq_compute(145                    gt_json,146                    PathManager.get_local_path(predictions_json),147                    gt_folder=gt_folder,148                    pred_folder=pred_dir,149                )150 151        res = {}152        res["PQ"] = 100 * pq_res["All"]["pq"]153        res["SQ"] = 100 * pq_res["All"]["sq"]154        res["RQ"] = 100 * pq_res["All"]["rq"]155        res["PQ_th"] = 100 * pq_res["Things"]["pq"]156        res["SQ_th"] = 100 * pq_res["Things"]["sq"]157        res["RQ_th"] = 100 * pq_res["Things"]["rq"]158        res["PQ_st"] = 100 * pq_res["Stuff"]["pq"]159        res["SQ_st"] = 100 * pq_res["Stuff"]["sq"]160        res["RQ_st"] = 100 * pq_res["Stuff"]["rq"]161 162        results = OrderedDict({"panoptic_seg": res})163        _print_panoptic_results(pq_res)164 165        return results166 167 168def _print_panoptic_results(pq_res):169    headers = ["", "PQ", "SQ", "RQ", "#categories"]170    data = []171    for name in ["All", "Things", "Stuff"]:172        row = [name] + [pq_res[name][k] * 100 for k in ["pq", "sq", "rq"]] + [pq_res[name]["n"]]173        data.append(row)174    table = tabulate(175        data, headers=headers, tablefmt="pipe", floatfmt=".3f", stralign="center", numalign="center"176    )177    logger.info("Panoptic Evaluation Results:\n" + table)178 179 180if __name__ == "__main__":181    from annotator.oneformer.detectron2.utils.logger import setup_logger182 183    logger = setup_logger()184    import argparse185 186    parser = argparse.ArgumentParser()187    parser.add_argument("--gt-json")188    parser.add_argument("--gt-dir")189    parser.add_argument("--pred-json")190    parser.add_argument("--pred-dir")191    args = parser.parse_args()192 193    from panopticapi.evaluation import pq_compute194 195    with contextlib.redirect_stdout(io.StringIO()):196        pq_res = pq_compute(197            args.gt_json, args.pred_json, gt_folder=args.gt_dir, pred_folder=args.pred_dir198        )199        _print_panoptic_results(pq_res)200