|
|
|
|
|
|
|
|
|
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 |
|
from cityscapesscripts.evaluation.evalInstanceLevelSemanticLabeling import \ |
|
CArgs |
|
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"') |
|
|
|
CSEval.setInstanceLabels(args) |
|
|
|
gt_instances = getGtInstances( |
|
groundtruth_list, args, backend_args=backend_args) |
|
|
|
matches = matchGtWithPreds(prediction_list, groundtruth_list, gt_instances, |
|
args, backend_args) |
|
if dump_matches: |
|
CSEval.writeDict2JSON(matches, 'matches.json') |
|
|
|
apScores = CSEval.evaluateMatches(matches, args) |
|
|
|
avgDict = CSEval.computeAverages(apScores, args) |
|
|
|
resDict = CSEval.prepareJSONDataForResults(avgDict, apScores, args) |
|
if args.JSONOutput: |
|
|
|
path = os.path.dirname(args.exportFile) |
|
CSEval.ensurePath(path) |
|
|
|
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): |
|
|
|
gt_image = readGTImage(gt, backend_args) |
|
pred_info = readPredInfo(pred) |
|
|
|
unfiltered_instances = gt_instances[gt] |
|
cur_gt_instances_orig = CSEval.filterGtInstances( |
|
unfiltered_instances, args) |
|
|
|
|
|
(cur_gt_instances, |
|
cur_pred_instances) = CSEval.assignGt2Preds(cur_gt_instances_orig, |
|
gt_image, pred_info, args) |
|
|
|
|
|
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]) |
|
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 (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) |
|
|
|
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: |
|
|
|
img_bytes = get(image_name, backend_args=backend_args) |
|
imgNp = mmcv.imfrombytes(img_bytes, flag='unchanged', backend='pillow') |
|
|
|
|
|
instances: dict = {} |
|
for label in labels: |
|
instances[label.name] = [] |
|
|
|
|
|
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 |
|
|