123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- # Copyright (c) OpenMMLab. All rights reserved.
- # Copyright (c) https://github.com/mcordts/cityscapesScripts
- # A wrapper of `cityscapesscripts` which supports loading groundtruth
- # image from `backend_args`.
- import json
- import os
- import sys
- from pathlib import Path
- from typing import Optional, Union
- import mmcv
- import numpy as np
- from mmengine.fileio import get
- try:
- import cityscapesscripts.evaluation.evalInstanceLevelSemanticLabeling as CSEval # noqa: E501
- from cityscapesscripts.evaluation.evalInstanceLevelSemanticLabeling import \
- CArgs # noqa: E501
- from cityscapesscripts.evaluation.instance import Instance
- from cityscapesscripts.helpers.csHelpers import (id2label, labels,
- writeDict2JSON)
- HAS_CITYSCAPESAPI = True
- except ImportError:
- CArgs = object
- HAS_CITYSCAPESAPI = False
- def evaluateImgLists(prediction_list: list,
- groundtruth_list: list,
- args: CArgs,
- backend_args: Optional[dict] = None,
- dump_matches: bool = False) -> dict:
- """A wrapper of obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling.evaluateImgLists``. Support loading
- groundtruth image from file backend.
- Args:
- prediction_list (list): A list of prediction txt file.
- groundtruth_list (list): A list of groundtruth image file.
- args (CArgs): A global object setting in
- obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling``
- backend_args (dict, optional): Arguments to instantiate the
- preifx of uri corresponding backend. Defaults to None.
- dump_matches (bool): whether dump matches.json. Defaults to False.
- Returns:
- dict: The computed metric.
- """
- if not HAS_CITYSCAPESAPI:
- raise RuntimeError('Failed to import `cityscapesscripts`.'
- 'Please try to install official '
- 'cityscapesscripts by '
- '"pip install cityscapesscripts"')
- # determine labels of interest
- CSEval.setInstanceLabels(args)
- # get dictionary of all ground truth instances
- gt_instances = getGtInstances(
- groundtruth_list, args, backend_args=backend_args)
- # match predictions and ground truth
- matches = matchGtWithPreds(prediction_list, groundtruth_list, gt_instances,
- args, backend_args)
- if dump_matches:
- CSEval.writeDict2JSON(matches, 'matches.json')
- # evaluate matches
- apScores = CSEval.evaluateMatches(matches, args)
- # averages
- avgDict = CSEval.computeAverages(apScores, args)
- # result dict
- resDict = CSEval.prepareJSONDataForResults(avgDict, apScores, args)
- if args.JSONOutput:
- # create output folder if necessary
- path = os.path.dirname(args.exportFile)
- CSEval.ensurePath(path)
- # Write APs to JSON
- CSEval.writeDict2JSON(resDict, args.exportFile)
- CSEval.printResults(avgDict, args)
- return resDict
- def matchGtWithPreds(prediction_list: list,
- groundtruth_list: list,
- gt_instances: dict,
- args: CArgs,
- backend_args=None):
- """A wrapper of obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling.matchGtWithPreds``. Support loading
- groundtruth image from file backend.
- Args:
- prediction_list (list): A list of prediction txt file.
- groundtruth_list (list): A list of groundtruth image file.
- gt_instances (dict): Groundtruth dict.
- args (CArgs): A global object setting in
- obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling``
- backend_args (dict, optional): Arguments to instantiate the
- preifx of uri corresponding backend. Defaults to None.
- Returns:
- dict: The processed prediction and groundtruth result.
- """
- if not HAS_CITYSCAPESAPI:
- raise RuntimeError('Failed to import `cityscapesscripts`.'
- 'Please try to install official '
- 'cityscapesscripts by '
- '"pip install cityscapesscripts"')
- matches: dict = dict()
- if not args.quiet:
- print(f'Matching {len(prediction_list)} pairs of images...')
- count = 0
- for (pred, gt) in zip(prediction_list, groundtruth_list):
- # Read input files
- gt_image = readGTImage(gt, backend_args)
- pred_info = readPredInfo(pred)
- # Get and filter ground truth instances
- unfiltered_instances = gt_instances[gt]
- cur_gt_instances_orig = CSEval.filterGtInstances(
- unfiltered_instances, args)
- # Try to assign all predictions
- (cur_gt_instances,
- cur_pred_instances) = CSEval.assignGt2Preds(cur_gt_instances_orig,
- gt_image, pred_info, args)
- # append to global dict
- matches[gt] = {}
- matches[gt]['groundTruth'] = cur_gt_instances
- matches[gt]['prediction'] = cur_pred_instances
- count += 1
- if not args.quiet:
- print(f'\rImages Processed: {count}', end=' ')
- sys.stdout.flush()
- if not args.quiet:
- print('')
- return matches
- def readGTImage(image_file: Union[str, Path],
- backend_args: Optional[dict] = None) -> np.ndarray:
- """Read an image from path.
- Same as obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling.readGTImage``, but support loading
- groundtruth image from file backend.
- Args:
- image_file (str or Path): Either a str or pathlib.Path.
- backend_args (dict, optional): Instantiates the corresponding file
- backend. It may contain `backend` key to specify the file
- backend. If it contains, the file backend corresponding to this
- value will be used and initialized with the remaining values,
- otherwise the corresponding file backend will be selected
- based on the prefix of the file path. Defaults to None.
- Returns:
- np.ndarray: The groundtruth image.
- """
- img_bytes = get(image_file, backend_args=backend_args)
- img = mmcv.imfrombytes(img_bytes, flag='unchanged', backend='pillow')
- return img
- def readPredInfo(prediction_file: str) -> dict:
- """A wrapper of obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling.readPredInfo``.
- Args:
- prediction_file (str): The prediction txt file.
- Returns:
- dict: The processed prediction results.
- """
- if not HAS_CITYSCAPESAPI:
- raise RuntimeError('Failed to import `cityscapesscripts`.'
- 'Please try to install official '
- 'cityscapesscripts by '
- '"pip install cityscapesscripts"')
- printError = CSEval.printError
- predInfo = {}
- if (not os.path.isfile(prediction_file)):
- printError(f"Infofile '{prediction_file}' "
- 'for the predictions not found.')
- with open(prediction_file) as f:
- for line in f:
- splittedLine = line.split(' ')
- if len(splittedLine) != 3:
- printError('Invalid prediction file. Expected content: '
- 'relPathPrediction1 labelIDPrediction1 '
- 'confidencePrediction1')
- if os.path.isabs(splittedLine[0]):
- printError('Invalid prediction file. First entry in each '
- 'line must be a relative path.')
- filename = os.path.join(
- os.path.dirname(prediction_file), splittedLine[0])
- imageInfo = {}
- imageInfo['labelID'] = int(float(splittedLine[1]))
- imageInfo['conf'] = float(splittedLine[2]) # type: ignore
- predInfo[filename] = imageInfo
- return predInfo
- def getGtInstances(groundtruth_list: list,
- args: CArgs,
- backend_args: Optional[dict] = None) -> dict:
- """A wrapper of obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling.getGtInstances``. Support loading
- groundtruth image from file backend.
- Args:
- groundtruth_list (list): A list of groundtruth image file.
- args (CArgs): A global object setting in
- obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling``
- backend_args (dict, optional): Arguments to instantiate the
- preifx of uri corresponding backend. Defaults to None.
- Returns:
- dict: The computed metric.
- """
- if not HAS_CITYSCAPESAPI:
- raise RuntimeError('Failed to import `cityscapesscripts`.'
- 'Please try to install official '
- 'cityscapesscripts by '
- '"pip install cityscapesscripts"')
- # if there is a global statistics json, then load it
- if (os.path.isfile(args.gtInstancesFile)):
- if not args.quiet:
- print('Loading ground truth instances from JSON.')
- with open(args.gtInstancesFile) as json_file:
- gt_instances = json.load(json_file)
- # otherwise create it
- else:
- if (not args.quiet):
- print('Creating ground truth instances from png files.')
- gt_instances = instances2dict(
- groundtruth_list, args, backend_args=backend_args)
- writeDict2JSON(gt_instances, args.gtInstancesFile)
- return gt_instances
- def instances2dict(image_list: list,
- args: CArgs,
- backend_args: Optional[dict] = None) -> dict:
- """A wrapper of obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling.instances2dict``. Support loading
- groundtruth image from file backend.
- Args:
- image_list (list): A list of image file.
- args (CArgs): A global object setting in
- obj:``cityscapesscripts.evaluation.
- evalInstanceLevelSemanticLabeling``
- backend_args (dict, optional): Arguments to instantiate the
- preifx of uri corresponding backend. Defaults to None.
- Returns:
- dict: The processed groundtruth results.
- """
- if not HAS_CITYSCAPESAPI:
- raise RuntimeError('Failed to import `cityscapesscripts`.'
- 'Please try to install official '
- 'cityscapesscripts by '
- '"pip install cityscapesscripts"')
- imgCount = 0
- instanceDict = {}
- if not isinstance(image_list, list):
- image_list = [image_list]
- if not args.quiet:
- print(f'Processing {len(image_list)} images...')
- for image_name in image_list:
- # Load image
- img_bytes = get(image_name, backend_args=backend_args)
- imgNp = mmcv.imfrombytes(img_bytes, flag='unchanged', backend='pillow')
- # Initialize label categories
- instances: dict = {}
- for label in labels:
- instances[label.name] = []
- # Loop through all instance ids in instance image
- for instanceId in np.unique(imgNp):
- instanceObj = Instance(imgNp, instanceId)
- instances[id2label[instanceObj.labelID].name].append(
- instanceObj.toDict())
- instanceDict[image_name] = instances
- imgCount += 1
- if not args.quiet:
- print(f'\rImages Processed: {imgCount}', end=' ')
- sys.stdout.flush()
- return instanceDict
|