| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						import glob | 
					
					
						
						| 
							 | 
						import logging | 
					
					
						
						| 
							 | 
						import numpy as np | 
					
					
						
						| 
							 | 
						import os | 
					
					
						
						| 
							 | 
						import tempfile | 
					
					
						
						| 
							 | 
						from collections import OrderedDict | 
					
					
						
						| 
							 | 
						import torch | 
					
					
						
						| 
							 | 
						from PIL import Image | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						from detectron2.data import MetadataCatalog | 
					
					
						
						| 
							 | 
						from detectron2.utils import comm | 
					
					
						
						| 
							 | 
						from detectron2.utils.file_io import PathManager | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						from .evaluator import DatasetEvaluator | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						class CityscapesEvaluator(DatasetEvaluator): | 
					
					
						
						| 
							 | 
						    """ | 
					
					
						
						| 
							 | 
						    Base class for evaluation using cityscapes API. | 
					
					
						
						| 
							 | 
						    """ | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def __init__(self, dataset_name): | 
					
					
						
						| 
							 | 
						        """ | 
					
					
						
						| 
							 | 
						        Args: | 
					
					
						
						| 
							 | 
						            dataset_name (str): the name of the dataset. | 
					
					
						
						| 
							 | 
						                It must have the following metadata associated with it: | 
					
					
						
						| 
							 | 
						                "thing_classes", "gt_dir". | 
					
					
						
						| 
							 | 
						        """ | 
					
					
						
						| 
							 | 
						        self._metadata = MetadataCatalog.get(dataset_name) | 
					
					
						
						| 
							 | 
						        self._cpu_device = torch.device("cpu") | 
					
					
						
						| 
							 | 
						        self._logger = logging.getLogger(__name__) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def reset(self): | 
					
					
						
						| 
							 | 
						        self._working_dir = tempfile.TemporaryDirectory(prefix="cityscapes_eval_") | 
					
					
						
						| 
							 | 
						        self._temp_dir = self._working_dir.name | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        assert ( | 
					
					
						
						| 
							 | 
						            comm.get_local_size() == comm.get_world_size() | 
					
					
						
						| 
							 | 
						        ), "CityscapesEvaluator currently do not work with multiple machines." | 
					
					
						
						| 
							 | 
						        self._temp_dir = comm.all_gather(self._temp_dir)[0] | 
					
					
						
						| 
							 | 
						        if self._temp_dir != self._working_dir.name: | 
					
					
						
						| 
							 | 
						            self._working_dir.cleanup() | 
					
					
						
						| 
							 | 
						        self._logger.info( | 
					
					
						
						| 
							 | 
						            "Writing cityscapes results to temporary directory {} ...".format(self._temp_dir) | 
					
					
						
						| 
							 | 
						        ) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						class CityscapesInstanceEvaluator(CityscapesEvaluator): | 
					
					
						
						| 
							 | 
						    """ | 
					
					
						
						| 
							 | 
						    Evaluate instance segmentation results on cityscapes dataset using cityscapes API. | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						    Note: | 
					
					
						
						| 
							 | 
						        * It does not work in multi-machine distributed training. | 
					
					
						
						| 
							 | 
						        * It contains a synchronization, therefore has to be used on all ranks. | 
					
					
						
						| 
							 | 
						        * Only the main process runs evaluation. | 
					
					
						
						| 
							 | 
						    """ | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def process(self, inputs, outputs): | 
					
					
						
						| 
							 | 
						        from cityscapesscripts.helpers.labels import name2label | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        for input, output in zip(inputs, outputs): | 
					
					
						
						| 
							 | 
						            file_name = input["file_name"] | 
					
					
						
						| 
							 | 
						            basename = os.path.splitext(os.path.basename(file_name))[0] | 
					
					
						
						| 
							 | 
						            pred_txt = os.path.join(self._temp_dir, basename + "_pred.txt") | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            if "instances" in output: | 
					
					
						
						| 
							 | 
						                output = output["instances"].to(self._cpu_device) | 
					
					
						
						| 
							 | 
						                num_instances = len(output) | 
					
					
						
						| 
							 | 
						                with open(pred_txt, "w") as fout: | 
					
					
						
						| 
							 | 
						                    for i in range(num_instances): | 
					
					
						
						| 
							 | 
						                        pred_class = output.pred_classes[i] | 
					
					
						
						| 
							 | 
						                        classes = self._metadata.stuff_classes[pred_class] | 
					
					
						
						| 
							 | 
						                        class_id = name2label[classes].id | 
					
					
						
						| 
							 | 
						                        score = output.scores[i] | 
					
					
						
						| 
							 | 
						                        mask = output.pred_masks[i].numpy().astype("uint8") | 
					
					
						
						| 
							 | 
						                        png_filename = os.path.join( | 
					
					
						
						| 
							 | 
						                            self._temp_dir, basename + "_{}_{}.png".format(i, classes) | 
					
					
						
						| 
							 | 
						                        ) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						                        Image.fromarray(mask * 255).save(png_filename) | 
					
					
						
						| 
							 | 
						                        fout.write( | 
					
					
						
						| 
							 | 
						                            "{} {} {}\n".format(os.path.basename(png_filename), class_id, score) | 
					
					
						
						| 
							 | 
						                        ) | 
					
					
						
						| 
							 | 
						            else: | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                with open(pred_txt, "w") as fout: | 
					
					
						
						| 
							 | 
						                    pass | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def evaluate(self): | 
					
					
						
						| 
							 | 
						        """ | 
					
					
						
						| 
							 | 
						        Returns: | 
					
					
						
						| 
							 | 
						            dict: has a key "segm", whose value is a dict of "AP" and "AP50". | 
					
					
						
						| 
							 | 
						        """ | 
					
					
						
						| 
							 | 
						        comm.synchronize() | 
					
					
						
						| 
							 | 
						        if comm.get_rank() > 0: | 
					
					
						
						| 
							 | 
						            return | 
					
					
						
						| 
							 | 
						        import cityscapesscripts.evaluation.evalInstanceLevelSemanticLabeling as cityscapes_eval | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        self._logger.info("Evaluating results under {} ...".format(self._temp_dir)) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        cityscapes_eval.args.predictionPath = os.path.abspath(self._temp_dir) | 
					
					
						
						| 
							 | 
						        cityscapes_eval.args.predictionWalk = None | 
					
					
						
						| 
							 | 
						        cityscapes_eval.args.JSONOutput = False | 
					
					
						
						| 
							 | 
						        cityscapes_eval.args.colorized = False | 
					
					
						
						| 
							 | 
						        cityscapes_eval.args.gtInstancesFile = os.path.join(self._temp_dir, "gtInstances.json") | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        gt_dir = PathManager.get_local_path(self._metadata.gt_dir) | 
					
					
						
						| 
							 | 
						        groundTruthImgList = glob.glob(os.path.join(gt_dir, "*", "*_gtFine_instanceIds.png")) | 
					
					
						
						| 
							 | 
						        assert len( | 
					
					
						
						| 
							 | 
						            groundTruthImgList | 
					
					
						
						| 
							 | 
						        ), "Cannot find any ground truth images to use for evaluation. Searched for: {}".format( | 
					
					
						
						| 
							 | 
						            cityscapes_eval.args.groundTruthSearch | 
					
					
						
						| 
							 | 
						        ) | 
					
					
						
						| 
							 | 
						        predictionImgList = [] | 
					
					
						
						| 
							 | 
						        for gt in groundTruthImgList: | 
					
					
						
						| 
							 | 
						            predictionImgList.append(cityscapes_eval.getPrediction(gt, cityscapes_eval.args)) | 
					
					
						
						| 
							 | 
						        results = cityscapes_eval.evaluateImgLists( | 
					
					
						
						| 
							 | 
						            predictionImgList, groundTruthImgList, cityscapes_eval.args | 
					
					
						
						| 
							 | 
						        )["averages"] | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        ret = OrderedDict() | 
					
					
						
						| 
							 | 
						        ret["segm"] = {"AP": results["allAp"] * 100, "AP50": results["allAp50%"] * 100} | 
					
					
						
						| 
							 | 
						        self._working_dir.cleanup() | 
					
					
						
						| 
							 | 
						        return ret | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						class CityscapesSemSegEvaluator(CityscapesEvaluator): | 
					
					
						
						| 
							 | 
						    """ | 
					
					
						
						| 
							 | 
						    Evaluate semantic segmentation results on cityscapes dataset using cityscapes API. | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						    Note: | 
					
					
						
						| 
							 | 
						        * It does not work in multi-machine distributed training. | 
					
					
						
						| 
							 | 
						        * It contains a synchronization, therefore has to be used on all ranks. | 
					
					
						
						| 
							 | 
						        * Only the main process runs evaluation. | 
					
					
						
						| 
							 | 
						    """ | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def process(self, inputs, outputs): | 
					
					
						
						| 
							 | 
						        from cityscapesscripts.helpers.labels import trainId2label | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        for input, output in zip(inputs, outputs): | 
					
					
						
						| 
							 | 
						            file_name = input["file_name"] | 
					
					
						
						| 
							 | 
						            basename = os.path.splitext(os.path.basename(file_name))[0] | 
					
					
						
						| 
							 | 
						            pred_filename = os.path.join(self._temp_dir, basename + "_pred.png") | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            output = output["sem_seg"].argmax(dim=0).to(self._cpu_device).numpy() | 
					
					
						
						| 
							 | 
						            pred = 255 * np.ones(output.shape, dtype=np.uint8) | 
					
					
						
						| 
							 | 
						            for train_id, label in trainId2label.items(): | 
					
					
						
						| 
							 | 
						                if label.ignoreInEval: | 
					
					
						
						| 
							 | 
						                    continue | 
					
					
						
						| 
							 | 
						                pred[output == train_id] = label.id | 
					
					
						
						| 
							 | 
						            Image.fromarray(pred).save(pred_filename) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    def evaluate(self): | 
					
					
						
						| 
							 | 
						        comm.synchronize() | 
					
					
						
						| 
							 | 
						        if comm.get_rank() > 0: | 
					
					
						
						| 
							 | 
						            return | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as cityscapes_eval | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        self._logger.info("Evaluating results under {} ...".format(self._temp_dir)) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        cityscapes_eval.args.predictionPath = os.path.abspath(self._temp_dir) | 
					
					
						
						| 
							 | 
						        cityscapes_eval.args.predictionWalk = None | 
					
					
						
						| 
							 | 
						        cityscapes_eval.args.JSONOutput = False | 
					
					
						
						| 
							 | 
						        cityscapes_eval.args.colorized = False | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        gt_dir = PathManager.get_local_path(self._metadata.gt_dir) | 
					
					
						
						| 
							 | 
						        groundTruthImgList = glob.glob(os.path.join(gt_dir, "*", "*_gtFine_labelIds.png")) | 
					
					
						
						| 
							 | 
						        assert len( | 
					
					
						
						| 
							 | 
						            groundTruthImgList | 
					
					
						
						| 
							 | 
						        ), "Cannot find any ground truth images to use for evaluation. Searched for: {}".format( | 
					
					
						
						| 
							 | 
						            cityscapes_eval.args.groundTruthSearch | 
					
					
						
						| 
							 | 
						        ) | 
					
					
						
						| 
							 | 
						        predictionImgList = [] | 
					
					
						
						| 
							 | 
						        for gt in groundTruthImgList: | 
					
					
						
						| 
							 | 
						            predictionImgList.append(cityscapes_eval.getPrediction(cityscapes_eval.args, gt)) | 
					
					
						
						| 
							 | 
						        results = cityscapes_eval.evaluateImgLists( | 
					
					
						
						| 
							 | 
						            predictionImgList, groundTruthImgList, cityscapes_eval.args | 
					
					
						
						| 
							 | 
						        ) | 
					
					
						
						| 
							 | 
						        ret = OrderedDict() | 
					
					
						
						| 
							 | 
						        ret["sem_seg"] = { | 
					
					
						
						| 
							 | 
						            "IoU": 100.0 * results["averageScoreClasses"], | 
					
					
						
						| 
							 | 
						            "iIoU": 100.0 * results["averageScoreInstClasses"], | 
					
					
						
						| 
							 | 
						            "IoU_sup": 100.0 * results["averageScoreCategories"], | 
					
					
						
						| 
							 | 
						            "iIoU_sup": 100.0 * results["averageScoreInstCategories"], | 
					
					
						
						| 
							 | 
						        } | 
					
					
						
						| 
							 | 
						        self._working_dir.cleanup() | 
					
					
						
						| 
							 | 
						        return ret | 
					
					
						
						| 
							 | 
						
 |