11# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
22import glob
33import logging
4+ import numpy as np
45import os
56import tempfile
67from collections import OrderedDict
1617
1718class CityscapesEvaluator (DatasetEvaluator ):
1819 """
19- Evaluate instance segmentation results using cityscapes API.
20-
21- Note:
22- * It does not work in multi-machine distributed training.
23- * It contains a synchronization, therefore has to be used on all ranks.
24- * Only the main process runs evaluation.
20+ Base class for evaluation using cityscapes API.
2521 """
2622
2723 def __init__ (self , dataset_name ):
@@ -47,6 +43,17 @@ def reset(self):
4743 "Writing cityscapes results to temporary directory {} ..." .format (self ._temp_dir )
4844 )
4945
46+
47+ class CityscapesInstanceEvaluator (CityscapesEvaluator ):
48+ """
49+ Evaluate instance segmentation results using cityscapes API.
50+
51+ Note:
52+ * It does not work in multi-machine distributed training.
53+ * It contains a synchronization, therefore has to be used on all ranks.
54+ * Only the main process runs evaluation.
55+ """
56+
5057 def process (self , inputs , outputs ):
5158 from cityscapesscripts .helpers .labels import name2label
5259
@@ -110,3 +117,71 @@ def evaluate(self):
110117 ret ["segm" ] = {"AP" : results ["allAp" ] * 100 , "AP50" : results ["allAp50%" ] * 100 }
111118 self ._working_dir .cleanup ()
112119 return ret
120+
121+
122+ class CityscapesSemSegEvaluator (CityscapesEvaluator ):
123+ """
124+ Evaluate semantic segmentation results using cityscapes API.
125+
126+ Note:
127+ * It does not work in multi-machine distributed training.
128+ * It contains a synchronization, therefore has to be used on all ranks.
129+ * Only the main process runs evaluation.
130+ """
131+
132+ def process (self , inputs , outputs ):
133+ from cityscapesscripts .helpers .labels import trainId2label
134+
135+ for input , output in zip (inputs , outputs ):
136+ file_name = input ["file_name" ]
137+ basename = os .path .splitext (os .path .basename (file_name ))[0 ]
138+ pred_filename = os .path .join (self ._temp_dir , basename + "_pred.png" )
139+
140+ output = output ["sem_seg" ].argmax (dim = 0 ).to (self ._cpu_device ).numpy ()
141+ pred = 255 * np .ones (output .shape , dtype = np .uint8 )
142+ for train_id , label in trainId2label .items ():
143+ if label .ignoreInEval :
144+ continue
145+ pred [output == train_id ] = label .id
146+ Image .fromarray (pred ).save (pred_filename )
147+
148+ def evaluate (self ):
149+ comm .synchronize ()
150+ if comm .get_rank () > 0 :
151+ return
152+ # Load the Cityscapes eval script *after* setting the required env var,
153+ # since the script reads CITYSCAPES_DATASET into global variables at load time.
154+ import cityscapesscripts .evaluation .evalPixelLevelSemanticLabeling as cityscapes_eval
155+
156+ self ._logger .info ("Evaluating results under {} ..." .format (self ._temp_dir ))
157+
158+ # set some global states in cityscapes evaluation API, before evaluating
159+ cityscapes_eval .args .predictionPath = os .path .abspath (self ._temp_dir )
160+ cityscapes_eval .args .predictionWalk = None
161+ cityscapes_eval .args .JSONOutput = False
162+ cityscapes_eval .args .colorized = False
163+
164+ # These lines are adopted from
165+ # https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/evaluation/evalPixelLevelSemanticLabeling.py # noqa
166+ gt_dir = PathManager .get_local_path (self ._metadata .gt_dir )
167+ groundTruthImgList = glob .glob (os .path .join (gt_dir , "*" , "*_gtFine_labelIds.png" ))
168+ assert len (
169+ groundTruthImgList
170+ ), "Cannot find any ground truth images to use for evaluation. Searched for: {}" .format (
171+ cityscapes_eval .args .groundTruthSearch
172+ )
173+ predictionImgList = []
174+ for gt in groundTruthImgList :
175+ predictionImgList .append (cityscapes_eval .getPrediction (cityscapes_eval .args , gt ))
176+ results = cityscapes_eval .evaluateImgLists (
177+ predictionImgList , groundTruthImgList , cityscapes_eval .args
178+ )
179+ ret = OrderedDict ()
180+ ret ["sem_seg" ] = {
181+ "IoU" : 100.0 * results ["averageScoreClasses" ],
182+ "iIoU" : 100.0 * results ["averageScoreInstClasses" ],
183+ "IoU_sup" : 100.0 * results ["averageScoreCategories" ],
184+ "iIoU_sup" : 100.0 * results ["averageScoreInstCategories" ],
185+ }
186+ self ._working_dir .cleanup ()
187+ return ret
0 commit comments