HarborYuan commited on
Commit
1f8df14
1 Parent(s): acdf070

adapt for omg

Browse files
app/configs/m2_convl.py CHANGED
@@ -36,6 +36,7 @@ model = dict(
36
  sphere_cls=True,
37
  ov_classifier_name=f'{ov_model_name}_{ov_datasets_name}',
38
  logit=None,
 
39
  in_channels=[192, 384, 768, 1536], # pass to pixel_decoder inside
40
  strides=[4, 8, 16, 32],
41
  feat_channels=256,
 
36
  sphere_cls=True,
37
  ov_classifier_name=f'{ov_model_name}_{ov_datasets_name}',
38
  logit=None,
39
+ enable_box_query=True,
40
  in_channels=[192, 384, 768, 1536], # pass to pixel_decoder inside
41
  strides=[4, 8, 16, 32],
42
  feat_channels=256,
ext/cityscapes_scripts/createPanopticImgs.py DELETED
@@ -1,194 +0,0 @@
1
- #!/usr/bin/python
2
- #
3
- # Converts the *instanceIds.png annotations of the Cityscapes dataset
4
- # to COCO-style panoptic segmentation format (http://cocodataset.org/#format-data).
5
- # The convertion is working for 'fine' set of the annotations.
6
- #
7
- # By default with this tool uses IDs specified in labels.py. You can use flag
8
- # --use-train-id to get train ids for categories. 'ignoreInEval' categories are
9
- # removed during the conversion.
10
- #
11
- # In panoptic segmentation format image_id is used to match predictions and ground truth.
12
- # For cityscapes image_id has form <city>_123456_123456 and corresponds to the prefix
13
- # of cityscapes image files.
14
- #
15
-
16
- # python imports
17
- from __future__ import print_function, absolute_import, division, unicode_literals
18
- import os
19
- import glob
20
- import sys
21
- import argparse
22
- import json
23
- import numpy as np
24
-
25
- # Image processing
26
- from PIL import Image
27
-
28
- # cityscapes imports
29
- from ext.cityscapes_scripts.helpers.csHelpers import printError
30
- from ext.cityscapes_scripts.helpers.labels import id2label, labels
31
-
32
-
33
- import mmengine
34
-
35
-
36
- # The main method
37
- def convert2panoptic(cityscapesPath=None, outputFolder=None, useTrainId=False, setNames=["val", "train", "test"]):
38
- # Where to look for Cityscapes
39
- if cityscapesPath is None:
40
- if 'CITYSCAPES_DATASET' in os.environ:
41
- cityscapesPath = os.environ['CITYSCAPES_DATASET']
42
- else:
43
- cityscapesPath = 'data/cityscapes'
44
- cityscapesPath = os.path.join(cityscapesPath, "gtFine")
45
-
46
- if outputFolder is None:
47
- outputFolder = cityscapesPath.replace('gtFine', "annotations")
48
-
49
- mmengine.mkdir_or_exist(outputFolder)
50
-
51
- categories = []
52
- for label in labels:
53
- if label.ignoreInEval:
54
- continue
55
- categories.append({'id': int(label.trainId) if useTrainId else int(label.id),
56
- 'name': label.name,
57
- 'color': label.color,
58
- 'supercategory': label.category,
59
- 'isthing': 1 if label.hasInstances else 0})
60
-
61
- categories = sorted(categories, key=lambda x:x['id'])
62
-
63
- for setName in setNames:
64
- # how to search for all ground truth
65
- searchFine = os.path.join(cityscapesPath, setName, "*", "*_instanceIds.png")
66
- # search files
67
- filesFine = glob.glob(searchFine)
68
- filesFine.sort()
69
-
70
- files = filesFine
71
- # quit if we did not find anything
72
- if not files:
73
- printError(
74
- "Did not find any files for {} set using matching pattern {}. Please consult the README.".format(setName, searchFine)
75
- )
76
- # a bit verbose
77
- print("Converting {} annotation files for {} set.".format(len(files), setName))
78
-
79
- trainIfSuffix = "_trainId" if useTrainId else ""
80
- outputBaseFile = "cityscapes_panoptic_{}{}".format(setName, trainIfSuffix)
81
- outFile = os.path.join(outputFolder, "{}.json".format(outputBaseFile))
82
- print("Json file with the annotations in panoptic format will be saved in {}".format(outFile))
83
- panopticFolder = os.path.join(outputFolder, outputBaseFile)
84
- if not os.path.isdir(panopticFolder):
85
- print("Creating folder {} for panoptic segmentation PNGs".format(panopticFolder))
86
- os.mkdir(panopticFolder)
87
- print("Corresponding segmentations in .png format will be saved in {}".format(panopticFolder))
88
-
89
- images = []
90
- annotations = []
91
- for progress, f in enumerate(files):
92
-
93
- originalFormat = np.array(Image.open(f))
94
-
95
- fileName = os.path.basename(f)
96
- location = fileName.split('_')[0]
97
- imageId = fileName.replace("_gtFine_instanceIds.png", "")
98
- fileName = os.path.join(location, fileName)
99
- inputFileName = fileName.replace("_gtFine_instanceIds.png", "_leftImg8bit.png")
100
- outputFileName = fileName.replace("_gtFine_instanceIds.png", "_panoptic.png")
101
- # image entry, id for image is its filename without extension
102
- images.append({"id": imageId,
103
- "width": int(originalFormat.shape[1]),
104
- "height": int(originalFormat.shape[0]),
105
- "file_name": inputFileName})
106
-
107
- pan_format = np.zeros(
108
- (originalFormat.shape[0], originalFormat.shape[1], 3), dtype=np.uint8
109
- )
110
-
111
- segmentIds = np.unique(originalFormat)
112
- segmInfo = []
113
- for segmentId in segmentIds:
114
- if segmentId < 1000:
115
- semanticId = segmentId
116
- isCrowd = 1
117
- else:
118
- semanticId = segmentId // 1000
119
- isCrowd = 0
120
- labelInfo = id2label[semanticId]
121
- categoryId = labelInfo.trainId if useTrainId else labelInfo.id
122
- if labelInfo.ignoreInEval:
123
- continue
124
- if not labelInfo.hasInstances:
125
- isCrowd = 0
126
-
127
- mask = originalFormat == segmentId
128
- color = [segmentId % 256, segmentId // 256, segmentId // 256 // 256]
129
- pan_format[mask] = color
130
-
131
- area = np.sum(mask) # segment area computation
132
-
133
- # bbox computation for a segment
134
- hor = np.sum(mask, axis=0)
135
- hor_idx = np.nonzero(hor)[0]
136
- x = hor_idx[0]
137
- width = hor_idx[-1] - x + 1
138
- vert = np.sum(mask, axis=1)
139
- vert_idx = np.nonzero(vert)[0]
140
- y = vert_idx[0]
141
- height = vert_idx[-1] - y + 1
142
- bbox = [int(x), int(y), int(width), int(height)]
143
-
144
- segmInfo.append({"id": int(segmentId),
145
- "category_id": int(categoryId),
146
- "area": int(area),
147
- "bbox": bbox,
148
- "iscrowd": isCrowd})
149
-
150
- annotations.append({'image_id': imageId,
151
- 'file_name': outputFileName,
152
- "segments_info": segmInfo})
153
-
154
- mmengine.mkdir_or_exist(os.path.dirname(os.path.join(panopticFolder, outputFileName)))
155
- Image.fromarray(pan_format).save(os.path.join(panopticFolder, outputFileName))
156
-
157
- print("\rProgress: {:>3.2f} %".format((progress + 1) * 100 / len(files)), end=' ')
158
- sys.stdout.flush()
159
-
160
- print("\nSaving the json file {}".format(outFile))
161
- d = {'images': images,
162
- 'annotations': annotations,
163
- 'categories': categories}
164
- with open(outFile, 'w') as f:
165
- json.dump(d, f, sort_keys=True, indent=4)
166
-
167
-
168
- def main():
169
- parser = argparse.ArgumentParser()
170
- parser.add_argument("--dataset-folder",
171
- dest="cityscapesPath",
172
- help="path to the Cityscapes dataset 'gtFine' folder",
173
- default=None,
174
- type=str)
175
- parser.add_argument("--output-folder",
176
- dest="outputFolder",
177
- help="path to the output folder.",
178
- default=None,
179
- type=str)
180
- parser.add_argument("--use-train-id", default=True,action="store_true", dest="useTrainId")
181
- parser.add_argument("--set-names",
182
- dest="setNames",
183
- help="set names to which apply the function to",
184
- nargs='+',
185
- default=["val", "train"],
186
- type=str)
187
- args = parser.parse_args()
188
-
189
- convert2panoptic(args.cityscapesPath, args.outputFolder, args.useTrainId, args.setNames)
190
-
191
-
192
- # call the main
193
- if __name__ == "__main__":
194
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ext/cityscapes_scripts/helpers/__init__.py DELETED
@@ -1 +0,0 @@
1
- # empty
 
 
ext/cityscapes_scripts/helpers/annotation.py DELETED
@@ -1,441 +0,0 @@
1
- #!/usr/bin/python
2
- #
3
- # Classes to store, read, and write annotations
4
- #
5
-
6
- from __future__ import print_function, absolute_import, division
7
- import os
8
- import json
9
- import numpy as np
10
- from collections import namedtuple
11
-
12
- # get current date and time
13
- import datetime
14
- import locale
15
-
16
- from abc import ABCMeta, abstractmethod
17
- from .box3dImageTransform import Camera
18
-
19
- # A point in a polygon
20
- Point = namedtuple('Point', ['x', 'y'])
21
-
22
-
23
- class CsObjectType():
24
- """Type of an object"""
25
- POLY = 1 # polygon
26
- BBOX2D = 2 # bounding box
27
- BBOX3D = 3 # 3d bounding box
28
- IGNORE2D = 4 # 2d ignore region
29
-
30
-
31
- class CsObject:
32
- """Abstract base class for annotation objects"""
33
- __metaclass__ = ABCMeta
34
-
35
- def __init__(self, objType):
36
- self.objectType = objType
37
- # the label
38
- self.label = ""
39
-
40
- # If deleted or not
41
- self.deleted = 0
42
- # If verified or not
43
- self.verified = 0
44
- # The date string
45
- self.date = ""
46
- # The username
47
- self.user = ""
48
- # Draw the object
49
- # Not read from or written to JSON
50
- # Set to False if deleted object
51
- # Might be set to False by the application for other reasons
52
- self.draw = True
53
-
54
- @abstractmethod
55
- def __str__(self): pass
56
-
57
- @abstractmethod
58
- def fromJsonText(self, jsonText, objId=-1): pass
59
-
60
- @abstractmethod
61
- def toJsonText(self): pass
62
-
63
- def updateDate(self):
64
- try:
65
- locale.setlocale(locale.LC_ALL, 'en_US.utf8')
66
- except locale.Error:
67
- locale.setlocale(locale.LC_ALL, 'en_US')
68
- except locale.Error:
69
- locale.setlocale(locale.LC_ALL, 'us_us.utf8')
70
- except locale.Error:
71
- locale.setlocale(locale.LC_ALL, 'us_us')
72
- except Exception:
73
- pass
74
- self.date = datetime.datetime.now().strftime("%d-%b-%Y %H:%M:%S")
75
-
76
- # Mark the object as deleted
77
- def delete(self):
78
- self.deleted = 1
79
- self.draw = False
80
-
81
-
82
- class CsPoly(CsObject):
83
- """Class that contains the information of a single annotated object as polygon"""
84
-
85
- # Constructor
86
- def __init__(self):
87
- CsObject.__init__(self, CsObjectType.POLY)
88
- # the polygon as list of points
89
- self.polygon = []
90
- # the object ID
91
- self.id = -1
92
-
93
- def __str__(self):
94
- polyText = ""
95
- if self.polygon:
96
- if len(self.polygon) <= 4:
97
- for p in self.polygon:
98
- polyText += '({},{}) '.format(p.x, p.y)
99
- else:
100
- polyText += '({},{}) ({},{}) ... ({},{}) ({},{})'.format(
101
- self.polygon[0].x, self.polygon[0].y,
102
- self.polygon[1].x, self.polygon[1].y,
103
- self.polygon[-2].x, self.polygon[-2].y,
104
- self.polygon[-1].x, self.polygon[-1].y)
105
- else:
106
- polyText = "none"
107
- text = "Object: {} - {}".format(self.label, polyText)
108
- return text
109
-
110
- def fromJsonText(self, jsonText, objId=-1):
111
- self.id = objId
112
- self.label = str(jsonText['label'])
113
- self.polygon = [Point(p[0], p[1]) for p in jsonText['polygon']]
114
- if 'deleted' in jsonText.keys():
115
- self.deleted = jsonText['deleted']
116
- else:
117
- self.deleted = 0
118
- if 'verified' in jsonText.keys():
119
- self.verified = jsonText['verified']
120
- else:
121
- self.verified = 1
122
- if 'user' in jsonText.keys():
123
- self.user = jsonText['user']
124
- else:
125
- self.user = ''
126
- if 'date' in jsonText.keys():
127
- self.date = jsonText['date']
128
- else:
129
- self.date = ''
130
- if self.deleted == 1:
131
- self.draw = False
132
- else:
133
- self.draw = True
134
-
135
- def toJsonText(self):
136
- objDict = {}
137
- objDict['label'] = self.label
138
- objDict['id'] = self.id
139
- objDict['deleted'] = self.deleted
140
- objDict['verified'] = self.verified
141
- objDict['user'] = self.user
142
- objDict['date'] = self.date
143
- objDict['polygon'] = []
144
- for pt in self.polygon:
145
- objDict['polygon'].append([pt.x, pt.y])
146
-
147
- return objDict
148
-
149
-
150
- class CsBbox2d(CsObject):
151
- """Class that contains the information of a single annotated object as bounding box"""
152
-
153
- # Constructor
154
- def __init__(self):
155
- CsObject.__init__(self, CsObjectType.BBOX2D)
156
- # the polygon as list of points
157
- self.bbox_amodal_xywh = []
158
- self.bbox_modal_xywh = []
159
-
160
- # the ID of the corresponding object
161
- self.instanceId = -1
162
- # the label of the corresponding object
163
- self.label = ""
164
-
165
- def __str__(self):
166
- bboxAmodalText = ""
167
- bboxAmodalText += '[(x1: {}, y1: {}), (w: {}, h: {})]'.format(
168
- self.bbox_amodal_xywh[0], self.bbox_amodal_xywh[1], self.bbox_amodal_xywh[2], self.bbox_amodal_xywh[3])
169
-
170
- bboxModalText = ""
171
- bboxModalText += '[(x1: {}, y1: {}), (w: {}, h: {})]'.format(
172
- self.bbox_modal_xywh[0], self.bbox_modal_xywh[1], self.bbox_modal_xywh[2], self.bbox_modal_xywh[3])
173
-
174
- text = "Object: {}\n - Amodal {}\n - Modal {}".format(
175
- self.label, bboxAmodalText, bboxModalText)
176
- return text
177
-
178
- def setAmodalBox(self, bbox_amodal):
179
- # sets the amodal box if required
180
- self.bbox_amodal_xywh = [
181
- bbox_amodal[0],
182
- bbox_amodal[1],
183
- bbox_amodal[2] - bbox_amodal[0],
184
- bbox_amodal[3] - bbox_amodal[1]
185
- ]
186
-
187
- # access 2d boxes in [xmin, ymin, xmax, ymax] format
188
- @property
189
- def bbox_amodal(self):
190
- """Returns the 2d box as [xmin, ymin, xmax, ymax]"""
191
- return [
192
- self.bbox_amodal_xywh[0],
193
- self.bbox_amodal_xywh[1],
194
- self.bbox_amodal_xywh[0] + self.bbox_amodal_xywh[2],
195
- self.bbox_amodal_xywh[1] + self.bbox_amodal_xywh[3]
196
- ]
197
-
198
- @property
199
- def bbox_modal(self):
200
- """Returns the 2d box as [xmin, ymin, xmax, ymax]"""
201
- return [
202
- self.bbox_modal_xywh[0],
203
- self.bbox_modal_xywh[1],
204
- self.bbox_modal_xywh[0] + self.bbox_modal_xywh[2],
205
- self.bbox_modal_xywh[1] + self.bbox_modal_xywh[3]
206
- ]
207
-
208
- def fromJsonText(self, jsonText, objId=-1):
209
- # try to load from cityperson format
210
- if 'bbox' in jsonText.keys() and 'bboxVis' in jsonText.keys():
211
- self.bbox_amodal_xywh = jsonText['bbox']
212
- self.bbox_modal_xywh = jsonText['bboxVis']
213
- # both modal and amodal boxes are provided
214
- elif "modal" in jsonText.keys() and "amodal" in jsonText.keys():
215
- self.bbox_amodal_xywh = jsonText['amodal']
216
- self.bbox_modal_xywh = jsonText['modal']
217
- # only amodal boxes are provided
218
- else:
219
- self.bbox_modal_xywh = jsonText['amodal']
220
- self.bbox_amodal_xywh = jsonText['amodal']
221
-
222
- # load label and instanceId if available
223
- if 'label' in jsonText.keys() and 'instanceId' in jsonText.keys():
224
- self.label = str(jsonText['label'])
225
- self.instanceId = jsonText['instanceId']
226
-
227
- def toJsonText(self):
228
- objDict = {}
229
- objDict['label'] = self.label
230
- objDict['instanceId'] = self.instanceId
231
- objDict['modal'] = self.bbox_modal_xywh
232
- objDict['amodal'] = self.bbox_amodal_xywh
233
-
234
- return objDict
235
-
236
-
237
- class CsBbox3d(CsObject):
238
- """Class that contains the information of a single annotated object as 3D bounding box"""
239
-
240
- # Constructor
241
- def __init__(self):
242
- CsObject.__init__(self, CsObjectType.BBOX3D)
243
-
244
- self.bbox_2d = None
245
-
246
- self.center = []
247
- self.dims = []
248
- self.rotation = []
249
- self.instanceId = -1
250
- self.label = ""
251
- self.score = -1.
252
-
253
- def __str__(self):
254
- bbox2dText = str(self.bbox_2d)
255
-
256
- bbox3dText = ""
257
- bbox3dText += '\n - Center (x/y/z) [m]: {}/{}/{}'.format(
258
- self.center[0], self.center[1], self.center[2])
259
- bbox3dText += '\n - Dimensions (l/w/h) [m]: {}/{}/{}'.format(
260
- self.dims[0], self.dims[1], self.dims[2])
261
- bbox3dText += '\n - Rotation: {}/{}/{}/{}'.format(
262
- self.rotation[0], self.rotation[1], self.rotation[2], self.rotation[3])
263
-
264
- text = "Object: {}\n2D {}\n - 3D {}".format(
265
- self.label, bbox2dText, bbox3dText)
266
- return text
267
-
268
- def fromJsonText(self, jsonText, objId=-1):
269
- # load 2D box
270
- self.bbox_2d = CsBbox2d()
271
- self.bbox_2d.fromJsonText(jsonText['2d'])
272
-
273
- self.center = jsonText['3d']['center']
274
- self.dims = jsonText['3d']['dimensions']
275
- self.rotation = jsonText['3d']['rotation']
276
- self.label = jsonText['label']
277
- self.score = jsonText['score']
278
-
279
- if 'instanceId' in jsonText.keys():
280
- self.instanceId = jsonText['instanceId']
281
-
282
- def toJsonText(self):
283
- objDict = {}
284
- objDict['label'] = self.label
285
- objDict['instanceId'] = self.instanceId
286
- objDict['2d']['amodal'] = self.bbox_2d.bbox_amodal_xywh
287
- objDict['2d']['modal'] = self.bbox_2d.bbox_modal_xywh
288
- objDict['3d']['center'] = self.center
289
- objDict['3d']['dimensions'] = self.dims
290
- objDict['3d']['rotation'] = self.rotation
291
-
292
- return objDict
293
-
294
- @property
295
- def depth(self):
296
- # returns the BEV depth
297
- return np.sqrt(self.center[0]**2 + self.center[1]**2).astype(int)
298
-
299
-
300
- class CsIgnore2d(CsObject):
301
- """Class that contains the information of a single annotated 2d ignore region"""
302
-
303
- # Constructor
304
- def __init__(self):
305
- CsObject.__init__(self, CsObjectType.IGNORE2D)
306
-
307
- self.bbox_xywh = []
308
- self.label = ""
309
- self.instanceId = -1
310
-
311
- def __str__(self):
312
- bbox2dText = ""
313
- bbox2dText += 'Ignore Region: (x1: {}, y1: {}), (w: {}, h: {})'.format(
314
- self.bbox_xywh[0], self.bbox_xywh[1], self.bbox_xywh[2], self.bbox_xywh[3])
315
-
316
- return bbox2dText
317
-
318
- def fromJsonText(self, jsonText, objId=-1):
319
- self.bbox_xywh = jsonText['2d']
320
-
321
- if 'label' in jsonText.keys():
322
- self.label = jsonText['label']
323
-
324
- if 'instanceId' in jsonText.keys():
325
- self.instanceId = jsonText['instanceId']
326
-
327
- def toJsonText(self):
328
- objDict = {}
329
- objDict['label'] = self.label
330
- objDict['instanceId'] = self.instanceId
331
- objDict['2d'] = self.bbox_xywh
332
-
333
- return objDict
334
-
335
- @property
336
- def bbox(self):
337
- """Returns the 2d box as [xmin, ymin, xmax, ymax]"""
338
- return [
339
- self.bbox_xywh[0],
340
- self.bbox_xywh[1],
341
- self.bbox_xywh[0] + self.bbox_xywh[2],
342
- self.bbox_xywh[1] + self.bbox_xywh[3]
343
- ]
344
-
345
- # Extend api to be compatible to bbox2d
346
- @property
347
- def bbox_amodal_xywh(self):
348
- return self.bbox_xywh
349
-
350
- @property
351
- def bbox_modal_xywh(self):
352
- return self.bbox_xywh
353
-
354
-
355
- class Annotation:
356
- """The annotation of a whole image (doesn't support mixed annotations, i.e. combining CsPoly and CsBbox2d)"""
357
-
358
- # Constructor
359
- def __init__(self, objType=CsObjectType.POLY):
360
- # the width of that image and thus of the label image
361
- self.imgWidth = 0
362
- # the height of that image and thus of the label image
363
- self.imgHeight = 0
364
- # the list of objects
365
- self.objects = []
366
- # the camera calibration
367
- self.camera = None
368
- assert objType in CsObjectType.__dict__.values()
369
- self.objectType = objType
370
-
371
- def toJson(self):
372
- return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4)
373
-
374
- def fromJsonText(self, jsonText):
375
- jsonDict = json.loads(jsonText)
376
- self.imgWidth = int(jsonDict['imgWidth'])
377
- self.imgHeight = int(jsonDict['imgHeight'])
378
- self.objects = []
379
- # load objects
380
- if self.objectType != CsObjectType.IGNORE2D:
381
- for objId, objIn in enumerate(jsonDict['objects']):
382
- if self.objectType == CsObjectType.POLY:
383
- obj = CsPoly()
384
- elif self.objectType == CsObjectType.BBOX2D:
385
- obj = CsBbox2d()
386
- elif self.objectType == CsObjectType.BBOX3D:
387
- obj = CsBbox3d()
388
- obj.fromJsonText(objIn, objId)
389
- self.objects.append(obj)
390
-
391
- # load ignores
392
- if 'ignore' in jsonDict.keys():
393
- for ignoreId, ignoreIn in enumerate(jsonDict['ignore']):
394
- obj = CsIgnore2d()
395
- obj.fromJsonText(ignoreIn, ignoreId)
396
- self.objects.append(obj)
397
-
398
- # load camera calibration
399
- if 'sensor' in jsonDict.keys():
400
- self.camera = Camera(fx=jsonDict['sensor']['fx'],
401
- fy=jsonDict['sensor']['fy'],
402
- u0=jsonDict['sensor']['u0'],
403
- v0=jsonDict['sensor']['v0'],
404
- sensor_T_ISO_8855=jsonDict['sensor']['sensor_T_ISO_8855'])
405
-
406
- def toJsonText(self):
407
- jsonDict = {}
408
- jsonDict['imgWidth'] = self.imgWidth
409
- jsonDict['imgHeight'] = self.imgHeight
410
- jsonDict['objects'] = []
411
- for obj in self.objects:
412
- objDict = obj.toJsonText()
413
- jsonDict['objects'].append(objDict)
414
-
415
- return jsonDict
416
-
417
- # Read a json formatted polygon file and return the annotation
418
- def fromJsonFile(self, jsonFile):
419
- if not os.path.isfile(jsonFile):
420
- print('Given json file not found: {}'.format(jsonFile))
421
- return
422
- with open(jsonFile, 'r') as f:
423
- jsonText = f.read()
424
- self.fromJsonText(jsonText)
425
-
426
- def toJsonFile(self, jsonFile):
427
- with open(jsonFile, 'w') as f:
428
- f.write(self.toJson())
429
-
430
-
431
- # a dummy example
432
- if __name__ == "__main__":
433
- obj = CsPoly()
434
- obj.label = 'car'
435
- obj.polygon.append(Point(0, 0))
436
- obj.polygon.append(Point(1, 0))
437
- obj.polygon.append(Point(1, 1))
438
- obj.polygon.append(Point(0, 1))
439
-
440
- print(type(obj).__name__)
441
- print(obj)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ext/cityscapes_scripts/helpers/csHelpers.py DELETED
@@ -1,129 +0,0 @@
1
- #!/usr/bin/python
2
- #
3
- # Various helper methods and includes for Cityscapes
4
- #
5
-
6
- # Python imports
7
- from __future__ import print_function, absolute_import, division
8
- import os
9
- import sys
10
- import getopt
11
- import glob
12
- import math
13
- import json
14
- from collections import namedtuple
15
- import logging
16
- import traceback
17
-
18
- # Image processing
19
- from PIL import Image
20
- from PIL import ImageDraw
21
-
22
- # Numpy for datastructures
23
- import numpy as np
24
-
25
- # Cityscapes modules
26
- # from .annotation import Annotation
27
- from .labels import labels, name2label, id2label, trainId2label, category2labels
28
-
29
-
30
- def printError(message):
31
- """Print an error message and quit"""
32
- print('ERROR: ' + str(message))
33
- sys.exit(-1)
34
-
35
-
36
- class colors:
37
- """Class for colors"""
38
- RED = '\033[31;1m'
39
- GREEN = '\033[32;1m'
40
- YELLOW = '\033[33;1m'
41
- BLUE = '\033[34;1m'
42
- MAGENTA = '\033[35;1m'
43
- CYAN = '\033[36;1m'
44
- BOLD = '\033[1m'
45
- UNDERLINE = '\033[4m'
46
- ENDC = '\033[0m'
47
-
48
-
49
- def getColorEntry(val, args):
50
- """Colored value output if colorized flag is activated."""
51
-
52
- if not args.colorized:
53
- return ""
54
- if not isinstance(val, float) or math.isnan(val):
55
- return colors.ENDC
56
- if (val < .20):
57
- return colors.RED
58
- elif (val < .40):
59
- return colors.YELLOW
60
- elif (val < .60):
61
- return colors.BLUE
62
- elif (val < .80):
63
- return colors.CYAN
64
- else:
65
- return colors.GREEN
66
-
67
-
68
- # Cityscapes files have a typical filename structure
69
- # <city>_<sequenceNb>_<frameNb>_<type>[_<type2>].<ext>
70
- # This class contains the individual elements as members
71
- # For the sequence and frame number, the strings are returned, including leading zeros
72
- CsFile = namedtuple('csFile', ['city', 'sequenceNb', 'frameNb', 'type', 'type2', 'ext'])
73
-
74
-
75
- def getCsFileInfo(fileName):
76
- """Returns a CsFile object filled from the info in the given filename"""
77
- baseName = os.path.basename(fileName)
78
- parts = baseName.split('_')
79
- parts = parts[:-1] + parts[-1].split('.')
80
- if not parts:
81
- printError('Cannot parse given filename ({}). Does not seem to be a valid Cityscapes file.'.format(fileName))
82
- if len(parts) == 5:
83
- csFile = CsFile(*parts[:-1], type2="", ext=parts[-1])
84
- elif len(parts) == 6:
85
- csFile = CsFile(*parts)
86
- else:
87
- printError('Found {} part(s) in given filename ({}). Expected 5 or 6.'.format(len(parts), fileName))
88
-
89
- return csFile
90
-
91
-
92
- def getCoreImageFileName(filename):
93
- """Returns the part of Cityscapes filenames that is common to all data types
94
-
95
- e.g. for city_123456_123456_gtFine_polygons.json returns city_123456_123456
96
- """
97
- csFile = getCsFileInfo(filename)
98
- return "{}_{}_{}".format(csFile.city, csFile.sequenceNb, csFile.frameNb)
99
-
100
-
101
- def getDirectory(fileName):
102
- """Returns the directory name for the given filename
103
-
104
- e.g.
105
- fileName = "/foo/bar/foobar.txt"
106
- return value is "bar"
107
- Not much error checking though
108
- """
109
- dirName = os.path.dirname(fileName)
110
- return os.path.basename(dirName)
111
-
112
-
113
- def ensurePath(path):
114
- """Make sure that the given path exists"""
115
- if not path:
116
- return
117
- if not os.path.isdir(path):
118
- os.makedirs(path)
119
-
120
-
121
- def writeDict2JSON(dictName, fileName):
122
- """Write a dictionary as json file"""
123
- with open(fileName, 'w') as f:
124
- f.write(json.dumps(dictName, default=lambda o: o.__dict__, sort_keys=True, indent=4))
125
-
126
-
127
- # dummy main
128
- if __name__ == "__main__":
129
- printError("Only for include, not executable on its own.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ext/cityscapes_scripts/helpers/labels.py DELETED
@@ -1,182 +0,0 @@
1
- #!/usr/bin/python
2
- #
3
- # Cityscapes labels
4
- #
5
-
6
- from __future__ import print_function, absolute_import, division
7
- from collections import namedtuple
8
-
9
-
10
- #--------------------------------------------------------------------------------
11
- # Definitions
12
- #--------------------------------------------------------------------------------
13
-
14
- # a label and all meta information
15
- Label = namedtuple( 'Label' , [
16
-
17
- 'name' , # The identifier of this label, e.g. 'car', 'person', ... .
18
- # We use them to uniquely name a class
19
-
20
- 'id' , # An integer ID that is associated with this label.
21
- # The IDs are used to represent the label in ground truth images
22
- # An ID of -1 means that this label does not have an ID and thus
23
- # is ignored when creating ground truth images (e.g. license plate).
24
- # Do not modify these IDs, since exactly these IDs are expected by the
25
- # evaluation server.
26
-
27
- 'trainId' , # Feel free to modify these IDs as suitable for your method. Then create
28
- # ground truth images with train IDs, using the tools provided in the
29
- # 'preparation' folder. However, make sure to validate or submit results
30
- # to our evaluation server using the regular IDs above!
31
- # For trainIds, multiple labels might have the same ID. Then, these labels
32
- # are mapped to the same class in the ground truth images. For the inverse
33
- # mapping, we use the label that is defined first in the list below.
34
- # For example, mapping all void-type classes to the same ID in training,
35
- # might make sense for some approaches.
36
- # Max value is 255!
37
-
38
- 'category' , # The name of the category that this label belongs to
39
-
40
- 'categoryId' , # The ID of this category. Used to create ground truth images
41
- # on category level.
42
-
43
- 'hasInstances', # Whether this label distinguishes between single instances or not
44
-
45
- 'ignoreInEval', # Whether pixels having this class as ground truth label are ignored
46
- # during evaluations or not
47
-
48
- 'color' , # The color of this label
49
- ] )
50
-
51
-
52
- #--------------------------------------------------------------------------------
53
- # A list of all labels
54
- #--------------------------------------------------------------------------------
55
-
56
- # Please adapt the train IDs as appropriate for your approach.
57
- # Note that you might want to ignore labels with ID 255 during training.
58
- # Further note that the current train IDs are only a suggestion. You can use whatever you like.
59
- # Make sure to provide your results using the original IDs and not the training IDs.
60
- # Note that many IDs are ignored in evaluation and thus you never need to predict these!
61
-
62
- labels = [
63
- # name id trainId category catId hasInstances ignoreInEval color
64
- Label( 'unlabeled' , 0 , 255 , 'void' , 0 , False , True , ( 0, 0, 0) ),
65
- Label( 'ego vehicle' , 1 , 255 , 'void' , 0 , False , True , ( 0, 0, 0) ),
66
- Label( 'rectification border' , 2 , 255 , 'void' , 0 , False , True , ( 0, 0, 0) ),
67
- Label( 'out of roi' , 3 , 255 , 'void' , 0 , False , True , ( 0, 0, 0) ),
68
- Label( 'static' , 4 , 255 , 'void' , 0 , False , True , ( 0, 0, 0) ),
69
- Label( 'dynamic' , 5 , 255 , 'void' , 0 , False , True , (111, 74, 0) ),
70
- Label( 'ground' , 6 , 255 , 'void' , 0 , False , True , ( 81, 0, 81) ),
71
- Label( 'road' , 7 , 0 + 8, 'flat' , 1 , False , False , (128, 64,128) ),
72
- Label( 'sidewalk' , 8 , 1 + 8, 'flat' , 1 , False , False , (244, 35,232) ),
73
- Label( 'parking' , 9 , 255 , 'flat' , 1 , False , True , (250,170,160) ),
74
- Label( 'rail track' , 10 , 255 , 'flat' , 1 , False , True , (230,150,140) ),
75
- Label( 'building' , 11 , 2 + 8, 'construction' , 2 , False , False , ( 70, 70, 70) ),
76
- Label( 'wall' , 12 , 3 + 8, 'construction' , 2 , False , False , (102,102,156) ),
77
- Label( 'fence' , 13 , 4 + 8, 'construction' , 2 , False , False , (190,153,153) ),
78
- Label( 'guard rail' , 14 , 255 , 'construction' , 2 , False , True , (180,165,180) ),
79
- Label( 'bridge' , 15 , 255 , 'construction' , 2 , False , True , (150,100,100) ),
80
- Label( 'tunnel' , 16 , 255 , 'construction' , 2 , False , True , (150,120, 90) ),
81
- Label( 'pole' , 17 , 5 + 8, 'object' , 3 , False , False , (153,153,153) ),
82
- Label( 'polegroup' , 18 , 255 , 'object' , 3 , False , True , (153,153,153) ),
83
- Label( 'traffic light' , 19 , 6 + 8, 'object' , 3 , False , False , (250,170, 30) ),
84
- Label( 'traffic sign' , 20 , 7 + 8, 'object' , 3 , False , False , (220,220, 0) ),
85
- Label( 'vegetation' , 21 , 8 + 8, 'nature' , 4 , False , False , (107,142, 35) ),
86
- Label( 'terrain' , 22 , 9 + 8, 'nature' , 4 , False , False , (152,251,152) ),
87
- Label( 'sky' , 23 , 10 + 8, 'sky' , 5 , False , False , ( 70,130,180) ),
88
- Label( 'person' , 24 , 11 - 11 , 'human' , 6 , True , False , (220, 20, 60) ),
89
- Label( 'rider' , 25 , 12 - 11 , 'human' , 6 , True , False , (255, 0, 0) ),
90
- Label( 'car' , 26 , 13 - 11, 'vehicle' , 7 , True , False , ( 0, 0,142) ),
91
- Label( 'truck' , 27 , 14 - 11, 'vehicle' , 7 , True , False , ( 0, 0, 70) ),
92
- Label( 'bus' , 28 , 15 - 11, 'vehicle' , 7 , True , False , ( 0, 60,100) ),
93
- Label( 'caravan' , 29 , 255 , 'vehicle' , 7 , True , True , ( 0, 0, 90) ),
94
- Label( 'trailer' , 30 , 255 , 'vehicle' , 7 , True , True , ( 0, 0,110) ),
95
- Label( 'train' , 31 , 16 - 11, 'vehicle' , 7 , True , False , ( 0, 80,100) ),
96
- Label( 'motorcycle' , 32 , 17 - 11, 'vehicle' , 7 , True , False , ( 0, 0,230) ),
97
- Label( 'bicycle' , 33 , 18 - 11, 'vehicle' , 7 , True , False , (119, 11, 32) ),
98
- Label( 'license plate' , -1 , -1 , 'vehicle' , 7 , False , True , ( 0, 0,142) ),
99
- ]
100
-
101
-
102
- #--------------------------------------------------------------------------------
103
- # Create dictionaries for a fast lookup
104
- #--------------------------------------------------------------------------------
105
-
106
- # Please refer to the main method below for example usages!
107
-
108
- # name to label object
109
- name2label = { label.name : label for label in labels }
110
- # id to label object
111
- id2label = { label.id : label for label in labels }
112
- # trainId to label object
113
- trainId2label = { label.trainId : label for label in reversed(labels) }
114
- # category to list of label objects
115
- category2labels = {}
116
- for label in labels:
117
- category = label.category
118
- if category in category2labels:
119
- category2labels[category].append(label)
120
- else:
121
- category2labels[category] = [label]
122
-
123
- #--------------------------------------------------------------------------------
124
- # Assure single instance name
125
- #--------------------------------------------------------------------------------
126
-
127
- # returns the label name that describes a single instance (if possible)
128
- # e.g. input | output
129
- # ----------------------
130
- # car | car
131
- # cargroup | car
132
- # foo | None
133
- # foogroup | None
134
- # skygroup | None
135
- def assureSingleInstanceName( name ):
136
- # if the name is known, it is not a group
137
- if name in name2label:
138
- return name
139
- # test if the name actually denotes a group
140
- if not name.endswith("group"):
141
- return None
142
- # remove group
143
- name = name[:-len("group")]
144
- # test if the new name exists
145
- if not name in name2label:
146
- return None
147
- # test if the new name denotes a label that actually has instances
148
- if not name2label[name].hasInstances:
149
- return None
150
- # all good then
151
- return name
152
-
153
- #--------------------------------------------------------------------------------
154
- # Main for testing
155
- #--------------------------------------------------------------------------------
156
-
157
- # just a dummy main
158
- if __name__ == "__main__":
159
- # Print all the labels
160
- print("List of cityscapes labels:")
161
- print("")
162
- print(" {:>21} | {:>3} | {:>7} | {:>14} | {:>10} | {:>12} | {:>12}".format( 'name', 'id', 'trainId', 'category', 'categoryId', 'hasInstances', 'ignoreInEval' ))
163
- print(" " + ('-' * 98))
164
- for label in labels:
165
- print(" {:>21} | {:>3} | {:>7} | {:>14} | {:>10} | {:>12} | {:>12}".format( label.name, label.id, label.trainId, label.category, label.categoryId, label.hasInstances, label.ignoreInEval ))
166
- print("")
167
-
168
- print("Example usages:")
169
-
170
- # Map from name to label
171
- name = 'car'
172
- id = name2label[name].id
173
- print("ID of label '{name}': {id}".format( name=name, id=id ))
174
-
175
- # Map from ID to label
176
- category = id2label[id].category
177
- print("Category of label with ID '{id}': {category}".format( id=id, category=category ))
178
-
179
- # Map from trainID to label
180
- trainId = 0
181
- name = trainId2label[trainId].name
182
- print("Name of label with trainID '{id}': {name}".format( id=trainId, name=name ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ext/cityscapes_scripts/helpers/labels_cityPersons.py DELETED
@@ -1,61 +0,0 @@
1
- #!/usr/bin/python
2
- #
3
- # CityPersons (cp) labels
4
- #
5
-
6
- from __future__ import print_function, absolute_import, division
7
- from collections import namedtuple
8
-
9
-
10
- #--------------------------------------------------------------------------------
11
- # Definitions
12
- #--------------------------------------------------------------------------------
13
-
14
- # a label and all meta information
15
- LabelCp = namedtuple( 'LabelCp' , [
16
-
17
- 'name' , # The identifier of this label, e.g. 'pedestrian', 'rider', ... .
18
- # We use them to uniquely name a class
19
-
20
- 'id' , # An integer ID that is associated with this label.
21
- # The IDs are used to represent the label in ground truth
22
-
23
- 'hasInstances', # Whether this label distinguishes between single instances or not
24
-
25
- 'ignoreInEval', # Whether pixels having this class as ground truth label are ignored
26
- # during evaluations or not
27
-
28
- 'color' , # The color of this label
29
- ] )
30
-
31
-
32
- #--------------------------------------------------------------------------------
33
- # A list of all labels
34
- #--------------------------------------------------------------------------------
35
-
36
- # The 'ignore' label covers representations of humans, e.g. people on posters, reflections etc.
37
- # Each annotation includes both the full bounding box (bbox) as well as a bounding box covering the visible area (bboxVis).
38
- # The latter is obtained automatically from the segmentation masks.
39
-
40
- labelsCp = [
41
- # name id hasInstances ignoreInEval color
42
- LabelCp( 'ignore' , 0 , False , True , (250,170, 30) ),
43
- LabelCp( 'pedestrian' , 1 , True , False , (220, 20, 60) ),
44
- LabelCp( 'rider' , 2 , True , False , ( 0, 0,142) ),
45
- LabelCp( 'sitting person' , 3 , True , False , (107,142, 35) ),
46
- LabelCp( 'person (other)' , 4 , True , False , (190,153,153) ),
47
- LabelCp( 'person group' , 5 , False , True , (255, 0, 0) ),
48
- ]
49
-
50
-
51
- #--------------------------------------------------------------------------------
52
- # Create dictionaries for a fast lookup
53
- #--------------------------------------------------------------------------------
54
-
55
- # Please refer to the main method below for example usages!
56
-
57
- # name to label object
58
- name2labelCp = { label.name : label for label in labelsCp }
59
- # id to label object
60
- id2labelCp = { label.id : label for label in labelsCp }
61
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ext/cityscapes_scripts/helpers/version.py DELETED
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env python
2
-
3
- import os
4
-
5
- with open(os.path.join(os.path.dirname(__file__), '..', 'VERSION')) as f:
6
- version = f.read().strip()
7
-
8
- if __name__ == "__main__":
9
- print(version)
 
 
 
 
 
 
 
 
 
 
ext/davis2017/__init__.py DELETED
@@ -1,3 +0,0 @@
1
- from __future__ import absolute_import
2
-
3
- __version__ = '0.1.0'
 
 
 
 
ext/davis2017/davis.py DELETED
@@ -1,122 +0,0 @@
1
- import os
2
- from glob import glob
3
- from collections import defaultdict
4
- import numpy as np
5
- from PIL import Image
6
-
7
-
8
- class DAVIS(object):
9
- SUBSET_OPTIONS = ['train', 'val', 'test-dev', 'test-challenge']
10
- TASKS = ['semi-supervised', 'unsupervised']
11
- DATASET_WEB = 'https://davischallenge.org/davis2017/code.html'
12
- VOID_LABEL = 255
13
-
14
- def __init__(self, root, task='unsupervised', subset='val', sequences='all', resolution='480p', codalab=False):
15
- """
16
- Class to read the DAVIS dataset
17
- :param root: Path to the DAVIS folder that contains JPEGImages, Annotations, etc. folders.
18
- :param task: Task to load the annotations, choose between semi-supervised or unsupervised.
19
- :param subset: Set to load the annotations
20
- :param sequences: Sequences to consider, 'all' to use all the sequences in a set.
21
- :param resolution: Specify the resolution to use the dataset, choose between '480' and 'Full-Resolution'
22
- """
23
- if subset not in self.SUBSET_OPTIONS:
24
- raise ValueError(f'Subset should be in {self.SUBSET_OPTIONS}')
25
- if task not in self.TASKS:
26
- raise ValueError(f'The only tasks that are supported are {self.TASKS}')
27
-
28
- self.task = task
29
- self.subset = subset
30
- self.root = root
31
- self.img_path = os.path.join(self.root, 'JPEGImages', resolution)
32
- annotations_folder = 'Annotations' if task == 'semi-supervised' else 'Annotations_unsupervised'
33
- self.mask_path = os.path.join(self.root, annotations_folder, resolution)
34
- year = '2019' if task == 'unsupervised' and (subset == 'test-dev' or subset == 'test-challenge') else '2017'
35
- self.imagesets_path = os.path.join(self.root, 'ImageSets', year)
36
-
37
- self._check_directories()
38
-
39
- if sequences == 'all':
40
- with open(os.path.join(self.imagesets_path, f'{self.subset}.txt'), 'r') as f:
41
- tmp = f.readlines()
42
- sequences_names = [x.strip() for x in tmp]
43
- else:
44
- sequences_names = sequences if isinstance(sequences, list) else [sequences]
45
- self.sequences = defaultdict(dict)
46
-
47
- for seq in sequences_names:
48
- images = np.sort(glob(os.path.join(self.img_path, seq, '*.jpg'))).tolist()
49
- if len(images) == 0 and not codalab:
50
- raise FileNotFoundError(f'Images for sequence {seq} not found.')
51
- self.sequences[seq]['images'] = images
52
- masks = np.sort(glob(os.path.join(self.mask_path, seq, '*.png'))).tolist()
53
- masks.extend([-1] * (len(images) - len(masks)))
54
- self.sequences[seq]['masks'] = masks
55
-
56
- def _check_directories(self):
57
- if not os.path.exists(self.root):
58
- raise FileNotFoundError(f'DAVIS not found in the specified directory, download it from {self.DATASET_WEB}')
59
- if not os.path.exists(os.path.join(self.imagesets_path, f'{self.subset}.txt')):
60
- raise FileNotFoundError(f'Subset sequences list for {self.subset} not found, download the missing subset '
61
- f'for the {self.task} task from {self.DATASET_WEB}')
62
- if self.subset in ['train', 'val'] and not os.path.exists(self.mask_path):
63
- raise FileNotFoundError(f'Annotations folder for the {self.task} task not found, download it from {self.DATASET_WEB}')
64
-
65
- def get_frames(self, sequence):
66
- for img, msk in zip(self.sequences[sequence]['images'], self.sequences[sequence]['masks']):
67
- image = np.array(Image.open(img))
68
- mask = None if msk is None else np.array(Image.open(msk))
69
- yield image, mask
70
-
71
- def _get_all_elements(self, sequence, obj_type):
72
- obj = np.array(Image.open(self.sequences[sequence][obj_type][0]))
73
- all_objs = np.zeros((len(self.sequences[sequence][obj_type]), *obj.shape))
74
- obj_id = []
75
- for i, obj in enumerate(self.sequences[sequence][obj_type]):
76
- all_objs[i, ...] = np.array(Image.open(obj))
77
- obj_id.append(''.join(obj.split('/')[-1].split('.')[:-1]))
78
- return all_objs, obj_id
79
-
80
- def get_all_images(self, sequence):
81
- return self._get_all_elements(sequence, 'images')
82
-
83
- def get_all_masks(self, sequence, separate_objects_masks=False):
84
- masks, masks_id = self._get_all_elements(sequence, 'masks')
85
- masks_void = np.zeros_like(masks)
86
-
87
- # Separate void and object masks
88
- for i in range(masks.shape[0]):
89
- masks_void[i, ...] = masks[i, ...] == 255
90
- masks[i, masks[i, ...] == 255] = 0
91
-
92
- if separate_objects_masks:
93
- num_objects = int(np.max(masks[0, ...]))
94
- tmp = np.ones((num_objects, *masks.shape))
95
- tmp = tmp * np.arange(1, num_objects + 1)[:, None, None, None]
96
- masks = (tmp == masks[None, ...])
97
- masks = masks > 0
98
- return masks, masks_void, masks_id
99
-
100
- def get_sequences(self):
101
- for seq in self.sequences:
102
- yield seq
103
-
104
-
105
- if __name__ == '__main__':
106
- from matplotlib import pyplot as plt
107
-
108
- only_first_frame = True
109
- subsets = ['train', 'val']
110
-
111
- for s in subsets:
112
- dataset = DAVIS(root='/home/csergi/scratch2/Databases/DAVIS2017_private', subset=s)
113
- for seq in dataset.get_sequences():
114
- g = dataset.get_frames(seq)
115
- img, mask = next(g)
116
- plt.subplot(2, 1, 1)
117
- plt.title(seq)
118
- plt.imshow(img)
119
- plt.subplot(2, 1, 2)
120
- plt.imshow(mask)
121
- plt.show(block=True)
122
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ext/davis2017/evaluation.py DELETED
@@ -1,110 +0,0 @@
1
- import sys
2
- from tqdm import tqdm
3
- import warnings
4
- warnings.filterwarnings("ignore", category=RuntimeWarning)
5
-
6
- import numpy as np
7
- from ext.davis2017.davis import DAVIS
8
- from ext.davis2017.metrics import db_eval_boundary, db_eval_iou
9
- from ext.davis2017 import utils
10
- from ext.davis2017.results import Results
11
- from scipy.optimize import linear_sum_assignment
12
-
13
-
14
- class DAVISEvaluation(object):
15
- def __init__(self, davis_root, task, gt_set, sequences='all', codalab=False):
16
- """
17
- Class to evaluate DAVIS sequences from a certain set and for a certain task
18
- :param davis_root: Path to the DAVIS folder that contains JPEGImages, Annotations, etc. folders.
19
- :param task: Task to compute the evaluation, chose between semi-supervised or unsupervised.
20
- :param gt_set: Set to compute the evaluation
21
- :param sequences: Sequences to consider for the evaluation, 'all' to use all the sequences in a set.
22
- """
23
- self.davis_root = davis_root
24
- self.task = task
25
- self.dataset = DAVIS(root=davis_root, task=task, subset=gt_set, sequences=sequences, codalab=codalab)
26
-
27
- @staticmethod
28
- def _evaluate_semisupervised(all_gt_masks, all_res_masks, all_void_masks, metric):
29
- if all_res_masks.shape[0] > all_gt_masks.shape[0]:
30
- sys.stdout.write("\nIn your PNG files there is an index higher than the number of objects in the sequence!")
31
- sys.exit()
32
- elif all_res_masks.shape[0] < all_gt_masks.shape[0]:
33
- zero_padding = np.zeros((all_gt_masks.shape[0] - all_res_masks.shape[0], *all_res_masks.shape[1:]))
34
- all_res_masks = np.concatenate([all_res_masks, zero_padding], axis=0)
35
- j_metrics_res, f_metrics_res = np.zeros(all_gt_masks.shape[:2]), np.zeros(all_gt_masks.shape[:2])
36
- for ii in range(all_gt_masks.shape[0]):
37
- if 'J' in metric:
38
- j_metrics_res[ii, :] = db_eval_iou(all_gt_masks[ii, ...], all_res_masks[ii, ...], all_void_masks)
39
- if 'F' in metric:
40
- f_metrics_res[ii, :] = db_eval_boundary(all_gt_masks[ii, ...], all_res_masks[ii, ...], all_void_masks)
41
- return j_metrics_res, f_metrics_res
42
-
43
- @staticmethod
44
- def _evaluate_unsupervised(all_gt_masks, all_res_masks, all_void_masks, metric, max_n_proposals=20):
45
- if all_res_masks.shape[0] > max_n_proposals:
46
- sys.stdout.write(f"\nIn your PNG files there is an index higher than the maximum number ({max_n_proposals}) of proposals allowed!")
47
- sys.exit()
48
- elif all_res_masks.shape[0] < all_gt_masks.shape[0]:
49
- zero_padding = np.zeros((all_gt_masks.shape[0] - all_res_masks.shape[0], *all_res_masks.shape[1:]))
50
- all_res_masks = np.concatenate([all_res_masks, zero_padding], axis=0)
51
- j_metrics_res = np.zeros((all_res_masks.shape[0], all_gt_masks.shape[0], all_gt_masks.shape[1]))
52
- f_metrics_res = np.zeros((all_res_masks.shape[0], all_gt_masks.shape[0], all_gt_masks.shape[1]))
53
- for ii in range(all_gt_masks.shape[0]):
54
- for jj in range(all_res_masks.shape[0]):
55
- if 'J' in metric:
56
- j_metrics_res[jj, ii, :] = db_eval_iou(all_gt_masks[ii, ...], all_res_masks[jj, ...], all_void_masks)
57
- if 'F' in metric:
58
- f_metrics_res[jj, ii, :] = db_eval_boundary(all_gt_masks[ii, ...], all_res_masks[jj, ...], all_void_masks)
59
- if 'J' in metric and 'F' in metric:
60
- all_metrics = (np.mean(j_metrics_res, axis=2) + np.mean(f_metrics_res, axis=2)) / 2
61
- else:
62
- all_metrics = np.mean(j_metrics_res, axis=2) if 'J' in metric else np.mean(f_metrics_res, axis=2)
63
- row_ind, col_ind = linear_sum_assignment(-all_metrics)
64
- return j_metrics_res[row_ind, col_ind, :], f_metrics_res[row_ind, col_ind, :]
65
-
66
- def evaluate(self, res_path, metric=('J', 'F'), debug=False):
67
- metric = metric if isinstance(metric, tuple) or isinstance(metric, list) else [metric]
68
- if 'T' in metric:
69
- raise ValueError('Temporal metric not supported!')
70
- if 'J' not in metric and 'F' not in metric:
71
- raise ValueError('Metric possible values are J for IoU or F for Boundary')
72
-
73
- # Containers
74
- metrics_res = {}
75
- if 'J' in metric:
76
- metrics_res['J'] = {"M": [], "R": [], "D": [], "M_per_object": {}}
77
- if 'F' in metric:
78
- metrics_res['F'] = {"M": [], "R": [], "D": [], "M_per_object": {}}
79
-
80
- # Sweep all sequences
81
- results = Results(root_dir=res_path)
82
- for seq in tqdm(list(self.dataset.get_sequences())):
83
- all_gt_masks, all_void_masks, all_masks_id = self.dataset.get_all_masks(seq, True)
84
- if self.task == 'semi-supervised':
85
- all_gt_masks, all_masks_id = all_gt_masks[:, 1:-1, :, :], all_masks_id[1:-1]
86
- all_res_masks = results.read_masks(seq, all_masks_id)
87
- if self.task == 'unsupervised':
88
- j_metrics_res, f_metrics_res = self._evaluate_unsupervised(all_gt_masks, all_res_masks, all_void_masks, metric)
89
- elif self.task == 'semi-supervised':
90
- j_metrics_res, f_metrics_res = self._evaluate_semisupervised(all_gt_masks, all_res_masks, None, metric)
91
- for ii in range(all_gt_masks.shape[0]):
92
- seq_name = f'{seq}_{ii+1}'
93
- if 'J' in metric:
94
- [JM, JR, JD] = utils.db_statistics(j_metrics_res[ii])
95
- metrics_res['J']["M"].append(JM)
96
- metrics_res['J']["R"].append(JR)
97
- metrics_res['J']["D"].append(JD)
98
- metrics_res['J']["M_per_object"][seq_name] = JM
99
- if 'F' in metric:
100
- [FM, FR, FD] = utils.db_statistics(f_metrics_res[ii])
101
- metrics_res['F']["M"].append(FM)
102
- metrics_res['F']["R"].append(FR)
103
- metrics_res['F']["D"].append(FD)
104
- metrics_res['F']["M_per_object"][seq_name] = FM
105
-
106
- # Show progress
107
- if debug:
108
- sys.stdout.write(seq + '\n')
109
- sys.stdout.flush()
110
- return metrics_res
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ext/davis2017/metrics.py DELETED
@@ -1,197 +0,0 @@
1
- import math
2
- import numpy as np
3
- import cv2
4
-
5
-
6
- def db_eval_iou(annotation, segmentation, void_pixels=None):
7
- """ Compute region similarity as the Jaccard Index.
8
- Arguments:
9
- annotation (ndarray): binary annotation map.
10
- segmentation (ndarray): binary segmentation map.
11
- void_pixels (ndarray): optional mask with void pixels
12
-
13
- Return:
14
- jaccard (float): region similarity
15
- """
16
- assert annotation.shape == segmentation.shape, \
17
- f'Annotation({annotation.shape}) and segmentation:{segmentation.shape} dimensions do not match.'
18
- annotation = annotation.astype(bool)
19
- segmentation = segmentation.astype(bool)
20
-
21
- if void_pixels is not None:
22
- assert annotation.shape == void_pixels.shape, \
23
- f'Annotation({annotation.shape}) and void pixels:{void_pixels.shape} dimensions do not match.'
24
- void_pixels = void_pixels.astype(bool)
25
- else:
26
- void_pixels = np.zeros_like(segmentation)
27
-
28
- # Intersection between all sets
29
- inters = np.sum((segmentation & annotation) & np.logical_not(void_pixels), axis=(-2, -1))
30
- union = np.sum((segmentation | annotation) & np.logical_not(void_pixels), axis=(-2, -1))
31
-
32
- j = inters / union
33
- if j.ndim == 0:
34
- j = 1 if np.isclose(union, 0) else j
35
- else:
36
- j[np.isclose(union, 0)] = 1
37
- return j
38
-
39
-
40
- def db_eval_boundary(annotation, segmentation, void_pixels=None, bound_th=0.008):
41
- assert annotation.shape == segmentation.shape
42
- if void_pixels is not None:
43
- assert annotation.shape == void_pixels.shape
44
- if annotation.ndim == 3:
45
- n_frames = annotation.shape[0]
46
- f_res = np.zeros(n_frames)
47
- for frame_id in range(n_frames):
48
- void_pixels_frame = None if void_pixels is None else void_pixels[frame_id, :, :, ]
49
- f_res[frame_id] = f_measure(segmentation[frame_id, :, :, ], annotation[frame_id, :, :], void_pixels_frame, bound_th=bound_th)
50
- elif annotation.ndim == 2:
51
- f_res = f_measure(segmentation, annotation, void_pixels, bound_th=bound_th)
52
- else:
53
- raise ValueError(f'db_eval_boundary does not support tensors with {annotation.ndim} dimensions')
54
- return f_res
55
-
56
-
57
- def f_measure(foreground_mask, gt_mask, void_pixels=None, bound_th=0.008):
58
- """
59
- Compute mean,recall and decay from per-frame evaluation.
60
- Calculates precision/recall for boundaries between foreground_mask and
61
- gt_mask using morphological operators to speed it up.
62
-
63
- Arguments:
64
- foreground_mask (ndarray): binary segmentation image.
65
- gt_mask (ndarray): binary annotated image.
66
- void_pixels (ndarray): optional mask with void pixels
67
-
68
- Returns:
69
- F (float): boundaries F-measure
70
- """
71
- assert np.atleast_3d(foreground_mask).shape[2] == 1
72
- if void_pixels is not None:
73
- void_pixels = void_pixels.astype(bool)
74
- else:
75
- void_pixels = np.zeros_like(foreground_mask).astype(bool)
76
-
77
- bound_pix = bound_th if bound_th >= 1 else \
78
- np.ceil(bound_th * np.linalg.norm(foreground_mask.shape))
79
-
80
- # Get the pixel boundaries of both masks
81
- fg_boundary = _seg2bmap(foreground_mask * np.logical_not(void_pixels))
82
- gt_boundary = _seg2bmap(gt_mask * np.logical_not(void_pixels))
83
-
84
- from skimage.morphology import disk
85
-
86
- # fg_dil = binary_dilation(fg_boundary, disk(bound_pix))
87
- fg_dil = cv2.dilate(fg_boundary.astype(np.uint8), disk(bound_pix).astype(np.uint8))
88
- # gt_dil = binary_dilation(gt_boundary, disk(bound_pix))
89
- gt_dil = cv2.dilate(gt_boundary.astype(np.uint8), disk(bound_pix).astype(np.uint8))
90
-
91
- # Get the intersection
92
- gt_match = gt_boundary * fg_dil
93
- fg_match = fg_boundary * gt_dil
94
-
95
- # Area of the intersection
96
- n_fg = np.sum(fg_boundary)
97
- n_gt = np.sum(gt_boundary)
98
-
99
- # % Compute precision and recall
100
- if n_fg == 0 and n_gt > 0:
101
- precision = 1
102
- recall = 0
103
- elif n_fg > 0 and n_gt == 0:
104
- precision = 0
105
- recall = 1
106
- elif n_fg == 0 and n_gt == 0:
107
- precision = 1
108
- recall = 1
109
- else:
110
- precision = np.sum(fg_match) / float(n_fg)
111
- recall = np.sum(gt_match) / float(n_gt)
112
-
113
- # Compute F measure
114
- if precision + recall == 0:
115
- F = 0
116
- else:
117
- F = 2 * precision * recall / (precision + recall)
118
-
119
- return F
120
-
121
-
122
- def _seg2bmap(seg, width=None, height=None):
123
- """
124
- From a segmentation, compute a binary boundary map with 1 pixel wide
125
- boundaries. The boundary pixels are offset by 1/2 pixel towards the
126
- origin from the actual segment boundary.
127
- Arguments:
128
- seg : Segments labeled from 1..k.
129
- width : Width of desired bmap <= seg.shape[1]
130
- height : Height of desired bmap <= seg.shape[0]
131
- Returns:
132
- bmap (ndarray): Binary boundary map.
133
- David Martin <dmartin@eecs.berkeley.edu>
134
- January 2003
135
- """
136
-
137
- seg = seg.astype(bool)
138
- seg[seg > 0] = 1
139
-
140
- assert np.atleast_3d(seg).shape[2] == 1
141
-
142
- width = seg.shape[1] if width is None else width
143
- height = seg.shape[0] if height is None else height
144
-
145
- h, w = seg.shape[:2]
146
-
147
- ar1 = float(width) / float(height)
148
- ar2 = float(w) / float(h)
149
-
150
- assert not (
151
- width > w | height > h | abs(ar1 - ar2) > 0.01
152
- ), "Can" "t convert %dx%d seg to %dx%d bmap." % (w, h, width, height)
153
-
154
- e = np.zeros_like(seg)
155
- s = np.zeros_like(seg)
156
- se = np.zeros_like(seg)
157
-
158
- e[:, :-1] = seg[:, 1:]
159
- s[:-1, :] = seg[1:, :]
160
- se[:-1, :-1] = seg[1:, 1:]
161
-
162
- b = seg ^ e | seg ^ s | seg ^ se
163
- b[-1, :] = seg[-1, :] ^ e[-1, :]
164
- b[:, -1] = seg[:, -1] ^ s[:, -1]
165
- b[-1, -1] = 0
166
-
167
- if w == width and h == height:
168
- bmap = b
169
- else:
170
- bmap = np.zeros((height, width))
171
- for x in range(w):
172
- for y in range(h):
173
- if b[y, x]:
174
- j = 1 + math.floor((y - 1) + height / h)
175
- i = 1 + math.floor((x - 1) + width / h)
176
- bmap[j, i] = 1
177
-
178
- return bmap
179
-
180
-
181
- if __name__ == '__main__':
182
- from davis2017.davis import DAVIS
183
- from davis2017.results import Results
184
-
185
- dataset = DAVIS(root='input_dir/ref', subset='val', sequences='aerobatics')
186
- results = Results(root_dir='examples/osvos')
187
- # Test timing F measure
188
- for seq in dataset.get_sequences():
189
- all_gt_masks, _, all_masks_id = dataset.get_all_masks(seq, True)
190
- all_gt_masks, all_masks_id = all_gt_masks[:, 1:-1, :, :], all_masks_id[1:-1]
191
- all_res_masks = results.read_masks(seq, all_masks_id)
192
- f_metrics_res = np.zeros(all_gt_masks.shape[:2])
193
- for ii in range(all_gt_masks.shape[0]):
194
- f_metrics_res[ii, :] = db_eval_boundary(all_gt_masks[ii, ...], all_res_masks[ii, ...])
195
-
196
- # Run using to profile code: python -m cProfile -o f_measure.prof metrics.py
197
- # snakeviz f_measure.prof
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ext/davis2017/results.py DELETED
@@ -1,52 +0,0 @@
1
- import os
2
- import numpy as np
3
- from PIL import Image, ImagePalette
4
- import sys
5
-
6
-
7
- davis_palette = b'\x00\x00\x00\x80\x00\x00\x00\x80\x00\x80\x80\x00\x00\x00\x80\x80\x00\x80\x00\x80\x80\x80\x80\x80@\x00\x00\xc0\x00\x00@\x80\x00\xc0\x80\x00@\x00\x80\xc0\x00\x80@\x80\x80\xc0\x80\x80\x00@\x00\x80@\x00\x00\xc0\x00\x80\xc0\x00\x00@\x80\x80@\x80\x00\xc0\x80\x80\xc0\x80@@\x00\xc0@\x00@\xc0\x00\xc0\xc0\x00@@\x80\xc0@\x80@\xc0\x80\xc0\xc0\x80\x00\x00@\x80\x00@\x00\x80@\x80\x80@\x00\x00\xc0\x80\x00\xc0\x00\x80\xc0\x80\x80\xc0@\x00@\xc0\x00@@\x80@\xc0\x80@@\x00\xc0\xc0\x00\xc0@\x80\xc0\xc0\x80\xc0\x00@@\x80@@\x00\xc0@\x80\xc0@\x00@\xc0\x80@\xc0\x00\xc0\xc0\x80\xc0\xc0@@@\xc0@@@\xc0@\xc0\xc0@@@\xc0\xc0@\xc0@\xc0\xc0\xc0\xc0\xc0 \x00\x00\xa0\x00\x00 \x80\x00\xa0\x80\x00 \x00\x80\xa0\x00\x80 \x80\x80\xa0\x80\x80`\x00\x00\xe0\x00\x00`\x80\x00\xe0\x80\x00`\x00\x80\xe0\x00\x80`\x80\x80\xe0\x80\x80 @\x00\xa0@\x00 \xc0\x00\xa0\xc0\x00 @\x80\xa0@\x80 \xc0\x80\xa0\xc0\x80`@\x00\xe0@\x00`\xc0\x00\xe0\xc0\x00`@\x80\xe0@\x80`\xc0\x80\xe0\xc0\x80 \x00@\xa0\x00@ \x80@\xa0\x80@ \x00\xc0\xa0\x00\xc0 \x80\xc0\xa0\x80\xc0`\x00@\xe0\x00@`\x80@\xe0\x80@`\x00\xc0\xe0\x00\xc0`\x80\xc0\xe0\x80\xc0 @@\xa0@@ \xc0@\xa0\xc0@ @\xc0\xa0@\xc0 \xc0\xc0\xa0\xc0\xc0`@@\xe0@@`\xc0@\xe0\xc0@`@\xc0\xe0@\xc0`\xc0\xc0\xe0\xc0\xc0\x00 \x00\x80 \x00\x00\xa0\x00\x80\xa0\x00\x00 \x80\x80 \x80\x00\xa0\x80\x80\xa0\x80@ \x00\xc0 \x00@\xa0\x00\xc0\xa0\x00@ \x80\xc0 \x80@\xa0\x80\xc0\xa0\x80\x00`\x00\x80`\x00\x00\xe0\x00\x80\xe0\x00\x00`\x80\x80`\x80\x00\xe0\x80\x80\xe0\x80@`\x00\xc0`\x00@\xe0\x00\xc0\xe0\x00@`\x80\xc0`\x80@\xe0\x80\xc0\xe0\x80\x00 @\x80 @\x00\xa0@\x80\xa0@\x00 \xc0\x80 \xc0\x00\xa0\xc0\x80\xa0\xc0@ @\xc0 @@\xa0@\xc0\xa0@@ \xc0\xc0 \xc0@\xa0\xc0\xc0\xa0\xc0\x00`@\x80`@\x00\xe0@\x80\xe0@\x00`\xc0\x80`\xc0\x00\xe0\xc0\x80\xe0\xc0@`@\xc0`@@\xe0@\xc0\xe0@@`\xc0\xc0`\xc0@\xe0\xc0\xc0\xe0\xc0 \x00\xa0 \x00 \xa0\x00\xa0\xa0\x00 \x80\xa0 \x80 \xa0\x80\xa0\xa0\x80` \x00\xe0 \x00`\xa0\x00\xe0\xa0\x00` \x80\xe0 \x80`\xa0\x80\xe0\xa0\x80 `\x00\xa0`\x00 \xe0\x00\xa0\xe0\x00 `\x80\xa0`\x80 \xe0\x80\xa0\xe0\x80``\x00\xe0`\x00`\xe0\x00\xe0\xe0\x00``\x80\xe0`\x80`\xe0\x80\xe0\xe0\x80 @\xa0 @ \xa0@\xa0\xa0@ \xc0\xa0 \xc0 \xa0\xc0\xa0\xa0\xc0` @\xe0 @`\xa0@\xe0\xa0@` \xc0\xe0 \xc0`\xa0\xc0\xe0\xa0\xc0 `@\xa0`@ \xe0@\xa0\xe0@ `\xc0\xa0`\xc0 \xe0\xc0\xa0\xe0\xc0``@\xe0`@`\xe0@\xe0\xe0@``\xc0\xe0`\xc0`\xe0\xc0\xe0\xe0\xc0'
8
- mose_palette = b'\x00\x00\x00\xe4\x1a\x1c7~\xb8M\xafJ\x98N\xa3\xff\x7f\x00\xff\xff3\xa6V(\xf7\x81\xbf\x99\x99\x99f\xc2\xa5\xfc\x8db\x8d\xa0\xcb\xe7\x8a\xc3\xa6\xd8T\xff\xd9/\xe5\xc4\x94\xb3\xb3\xb3\x8d\xd3\xc7\xff\xff\xb3\xbe\xba\xda\xfb\x80r\x80\xb1\xd3\xfd\xb4b\xb3\xdei\xfc\xcd\xe5\xd9\xd9\xd9\xbc\x80\xbd\xcc\xeb\xc5\xff\xedo'
9
-
10
- class Results(object):
11
- def __init__(self, root_dir):
12
- self.root_dir = root_dir
13
-
14
- def _read_mask(self, sequence, frame_id):
15
- try:
16
- mask_path = os.path.join(self.root_dir, sequence, f'{frame_id}.png')
17
- # BUGFIX
18
- # There is a bug in the codebase
19
- # Here is a compensation.
20
- img = Image.open(mask_path)
21
- if img.mode != 'P':
22
- img_color = np.array(img)
23
- h, w, three = img_color.shape
24
- assert three == 3
25
-
26
- img_new = np.ones((h, w), dtype=np.uint8) * 255
27
- color_map_np = np.frombuffer(davis_palette, dtype=np.uint8).reshape(-1, 3).copy()
28
- for i in range(10):
29
- cur_color = color_map_np[i]
30
- mask = np.all(img_color == cur_color, axis=-1)
31
- img_new[mask] = i
32
- assert not np.all(img_new == 255).any()
33
- img = img_new
34
- # BUGFIX
35
- return np.array(img)
36
- except IOError as err:
37
- sys.stdout.write(sequence + " frame %s not found!\n" % frame_id)
38
- sys.stdout.write("The frames have to be indexed PNG files placed inside the corespondent sequence "
39
- "folder.\nThe indexes have to match with the initial frame.\n")
40
- sys.stderr.write("IOError: " + err.strerror + "\n")
41
- sys.exit()
42
-
43
- def read_masks(self, sequence, masks_id):
44
- mask_0 = self._read_mask(sequence, masks_id[0])
45
- masks = np.zeros((len(masks_id), *mask_0.shape))
46
- for ii, m in enumerate(masks_id):
47
- masks[ii, ...] = self._read_mask(sequence, m)
48
- num_objects = int(np.max(masks))
49
- tmp = np.ones((num_objects, *masks.shape))
50
- tmp = tmp * np.arange(1, num_objects + 1)[:, None, None, None]
51
- masks = (tmp == masks[None, ...]) > 0
52
- return masks
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ext/davis2017/utils.py DELETED
@@ -1,174 +0,0 @@
1
- import os
2
- import errno
3
- import numpy as np
4
- from PIL import Image
5
- import warnings
6
- from ext.davis2017.davis import DAVIS
7
-
8
-
9
- def _pascal_color_map(N=256, normalized=False):
10
- """
11
- Python implementation of the color map function for the PASCAL VOC data set.
12
- Official Matlab version can be found in the PASCAL VOC devkit
13
- http://host.robots.ox.ac.uk/pascal/VOC/voc2012/index.html#devkit
14
- """
15
-
16
- def bitget(byteval, idx):
17
- return (byteval & (1 << idx)) != 0
18
-
19
- dtype = 'float32' if normalized else 'uint8'
20
- cmap = np.zeros((N, 3), dtype=dtype)
21
- for i in range(N):
22
- r = g = b = 0
23
- c = i
24
- for j in range(8):
25
- r = r | (bitget(c, 0) << 7 - j)
26
- g = g | (bitget(c, 1) << 7 - j)
27
- b = b | (bitget(c, 2) << 7 - j)
28
- c = c >> 3
29
-
30
- cmap[i] = np.array([r, g, b])
31
-
32
- cmap = cmap / 255 if normalized else cmap
33
- return cmap
34
-
35
-
36
- def overlay_semantic_mask(im, ann, alpha=0.5, colors=None, contour_thickness=None):
37
- im, ann = np.asarray(im, dtype=np.uint8), np.asarray(ann, dtype=np.int)
38
- if im.shape[:-1] != ann.shape:
39
- raise ValueError('First two dimensions of `im` and `ann` must match')
40
- if im.shape[-1] != 3:
41
- raise ValueError('im must have three channels at the 3 dimension')
42
-
43
- colors = colors or _pascal_color_map()
44
- colors = np.asarray(colors, dtype=np.uint8)
45
-
46
- mask = colors[ann]
47
- fg = im * alpha + (1 - alpha) * mask
48
-
49
- img = im.copy()
50
- img[ann > 0] = fg[ann > 0]
51
-
52
- if contour_thickness: # pragma: no cover
53
- import cv2
54
- for obj_id in np.unique(ann[ann > 0]):
55
- contours = cv2.findContours((ann == obj_id).astype(
56
- np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]
57
- cv2.drawContours(img, contours[0], -1, colors[obj_id].tolist(),
58
- contour_thickness)
59
- return img
60
-
61
-
62
- def generate_obj_proposals(davis_root, subset, num_proposals, save_path):
63
- dataset = DAVIS(davis_root, subset=subset, codalab=True)
64
- for seq in dataset.get_sequences():
65
- save_dir = os.path.join(save_path, seq)
66
- if os.path.exists(save_dir):
67
- continue
68
- all_gt_masks, all_masks_id = dataset.get_all_masks(seq, True)
69
- img_size = all_gt_masks.shape[2:]
70
- num_rows = int(np.ceil(np.sqrt(num_proposals)))
71
- proposals = np.zeros((num_proposals, len(all_masks_id), *img_size))
72
- height_slices = np.floor(np.arange(0, img_size[0] + 1, img_size[0]/num_rows)).astype(np.uint).tolist()
73
- width_slices = np.floor(np.arange(0, img_size[1] + 1, img_size[1]/num_rows)).astype(np.uint).tolist()
74
- ii = 0
75
- prev_h, prev_w = 0, 0
76
- for h in height_slices[1:]:
77
- for w in width_slices[1:]:
78
- proposals[ii, :, prev_h:h, prev_w:w] = 1
79
- prev_w = w
80
- ii += 1
81
- if ii == num_proposals:
82
- break
83
- prev_h, prev_w = h, 0
84
- if ii == num_proposals:
85
- break
86
-
87
- os.makedirs(save_dir, exist_ok=True)
88
- for i, mask_id in enumerate(all_masks_id):
89
- mask = np.sum(proposals[:, i, ...] * np.arange(1, proposals.shape[0] + 1)[:, None, None], axis=0)
90
- save_mask(mask, os.path.join(save_dir, f'{mask_id}.png'))
91
-
92
-
93
- def generate_random_permutation_gt_obj_proposals(davis_root, subset, save_path):
94
- dataset = DAVIS(davis_root, subset=subset, codalab=True)
95
- for seq in dataset.get_sequences():
96
- gt_masks, all_masks_id = dataset.get_all_masks(seq, True)
97
- obj_swap = np.random.permutation(np.arange(gt_masks.shape[0]))
98
- gt_masks = gt_masks[obj_swap, ...]
99
- save_dir = os.path.join(save_path, seq)
100
- os.makedirs(save_dir, exist_ok=True)
101
- for i, mask_id in enumerate(all_masks_id):
102
- mask = np.sum(gt_masks[:, i, ...] * np.arange(1, gt_masks.shape[0] + 1)[:, None, None], axis=0)
103
- save_mask(mask, os.path.join(save_dir, f'{mask_id}.png'))
104
-
105
-
106
- def color_map(N=256, normalized=False):
107
- def bitget(byteval, idx):
108
- return ((byteval & (1 << idx)) != 0)
109
-
110
- dtype = 'float32' if normalized else 'uint8'
111
- cmap = np.zeros((N, 3), dtype=dtype)
112
- for i in range(N):
113
- r = g = b = 0
114
- c = i
115
- for j in range(8):
116
- r = r | (bitget(c, 0) << 7-j)
117
- g = g | (bitget(c, 1) << 7-j)
118
- b = b | (bitget(c, 2) << 7-j)
119
- c = c >> 3
120
-
121
- cmap[i] = np.array([r, g, b])
122
-
123
- cmap = cmap/255 if normalized else cmap
124
- return cmap
125
-
126
-
127
- def save_mask(mask, img_path):
128
- if np.max(mask) > 255:
129
- raise ValueError('Maximum id pixel value is 255')
130
- mask_img = Image.fromarray(mask.astype(np.uint8))
131
- mask_img.putpalette(color_map().flatten().tolist())
132
- mask_img.save(img_path)
133
-
134
-
135
- def db_statistics(per_frame_values):
136
- """ Compute mean,recall and decay from per-frame evaluation.
137
- Arguments:
138
- per_frame_values (ndarray): per-frame evaluation
139
-
140
- Returns:
141
- M,O,D (float,float,float):
142
- return evaluation statistics: mean,recall,decay.
143
- """
144
-
145
- # strip off nan values
146
- with warnings.catch_warnings():
147
- warnings.simplefilter("ignore", category=RuntimeWarning)
148
- M = np.nanmean(per_frame_values)
149
- O = np.nanmean(per_frame_values > 0.5)
150
-
151
- N_bins = 4
152
- ids = np.round(np.linspace(1, len(per_frame_values), N_bins + 1) + 1e-10) - 1
153
- ids = ids.astype(np.uint8)
154
-
155
- D_bins = [per_frame_values[ids[i]:ids[i + 1] + 1] for i in range(0, 4)]
156
-
157
- with warnings.catch_warnings():
158
- warnings.simplefilter("ignore", category=RuntimeWarning)
159
- D = np.nanmean(D_bins[0]) - np.nanmean(D_bins[3])
160
-
161
- return M, O, D
162
-
163
-
164
- def list_files(dir, extension=".png"):
165
- return [os.path.splitext(file_)[0] for file_ in os.listdir(dir) if file_.endswith(extension)]
166
-
167
-
168
- def force_symlink(file1, file2):
169
- try:
170
- os.symlink(file1, file2)
171
- except OSError as e:
172
- if e.errno == errno.EEXIST:
173
- os.remove(file2)
174
- os.symlink(file1, file2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
main.py CHANGED
@@ -107,7 +107,7 @@ def get_points_with_draw(image, img_state, evt: gr.SelectData):
107
  return image
108
 
109
 
110
- def segment_point(image, img_state):
111
  output_img = img_state.img
112
  h, w = output_img.shape[:2]
113
 
@@ -125,37 +125,47 @@ def segment_point(image, img_state):
125
  gt_instances = InstanceData(
126
  point_coords=selected_point,
127
  )
128
- pb_labels = torch.ones(len(gt_instances), dtype=torch.long, device=device)
129
- gt_instances.pb_labels = pb_labels
130
- batch_data_samples[0].gt_instances_collected = gt_instances
 
131
  batch_data_samples[0].set_metainfo(dict(batch_input_shape=(im_h, im_w)))
132
  batch_data_samples[0].set_metainfo(dict(img_shape=(h, w)))
133
  is_prompt = True
134
  else:
135
  batch_data_samples = [DetDataSample()]
 
136
  batch_data_samples[0].set_metainfo(dict(batch_input_shape=(im_h, im_w)))
137
  batch_data_samples[0].set_metainfo(dict(img_shape=(h, w)))
138
  is_prompt = False
139
  with torch.no_grad():
140
- masks, cls_pred = model.predict_with_point(img_tensor, batch_data_samples)
141
 
142
- assert len(masks) == 1
143
- masks = masks[0]
144
  if is_prompt:
145
  masks = masks[0, :h, :w]
146
- masks = masks > 0. # no sigmoid
147
  rgb_shape = tuple(list(masks.shape) + [3])
148
  color = np.zeros(rgb_shape, dtype=np.uint8)
149
  color[masks] = np.array([97, 217, 54])
150
  output_img = (output_img * 0.7 + color * 0.3).astype(np.uint8)
151
  output_img = Image.fromarray(output_img)
152
  else:
153
- output_img = visualizer._draw_panoptic_seg(
154
- output_img,
155
- masks['pan_results'].to('cpu').numpy(),
156
- classes=CocoPanopticDataset.METAINFO['classes'],
157
- palette=CocoPanopticDataset.METAINFO['palette']
158
- )
 
 
 
 
 
 
 
 
 
159
  return image, output_img
160
 
161
 
@@ -177,6 +187,12 @@ def register_point_mode():
177
 
178
  with gr.Row():
179
  with gr.Column():
 
 
 
 
 
 
180
  with gr.Row():
181
  with gr.Column():
182
  segment_btn = gr.Button("Segment", variant="primary")
@@ -209,7 +225,7 @@ def register_point_mode():
209
 
210
  segment_btn.click(
211
  segment_point,
212
- [img_p, img_state],
213
  [img_p, segm_p]
214
  )
215
 
 
107
  return image
108
 
109
 
110
+ def segment_point(image, img_state, mode):
111
  output_img = img_state.img
112
  h, w = output_img.shape[:2]
113
 
 
125
  gt_instances = InstanceData(
126
  point_coords=selected_point,
127
  )
128
+ pb_labels = torch.zeros(len(gt_instances), dtype=torch.long, device=device)
129
+ gt_instances.bp = pb_labels
130
+ batch_data_samples[0].gt_instances = gt_instances
131
+ batch_data_samples[0].data_tag = 'sam'
132
  batch_data_samples[0].set_metainfo(dict(batch_input_shape=(im_h, im_w)))
133
  batch_data_samples[0].set_metainfo(dict(img_shape=(h, w)))
134
  is_prompt = True
135
  else:
136
  batch_data_samples = [DetDataSample()]
137
+ batch_data_samples[0].data_tag = 'coco'
138
  batch_data_samples[0].set_metainfo(dict(batch_input_shape=(im_h, im_w)))
139
  batch_data_samples[0].set_metainfo(dict(img_shape=(h, w)))
140
  is_prompt = False
141
  with torch.no_grad():
142
+ results = model.predict(img_tensor, batch_data_samples, rescale=False)
143
 
144
+ masks = results[0]
 
145
  if is_prompt:
146
  masks = masks[0, :h, :w]
147
+ masks = masks > 0. # no sigmoid
148
  rgb_shape = tuple(list(masks.shape) + [3])
149
  color = np.zeros(rgb_shape, dtype=np.uint8)
150
  color[masks] = np.array([97, 217, 54])
151
  output_img = (output_img * 0.7 + color * 0.3).astype(np.uint8)
152
  output_img = Image.fromarray(output_img)
153
  else:
154
+ if mode == 'Panoptic Segmentation':
155
+ output_img = visualizer._draw_panoptic_seg(
156
+ output_img,
157
+ masks['pan_results'].to('cpu').numpy(),
158
+ classes=CocoPanopticDataset.METAINFO['classes'],
159
+ palette=CocoPanopticDataset.METAINFO['palette']
160
+ )
161
+ elif mode == 'Instance Segmentation':
162
+ masks['ins_results'] = masks['ins_results'][masks['ins_results'].scores > .2]
163
+ output_img = visualizer._draw_instances(
164
+ output_img,
165
+ masks['ins_results'].to('cpu').numpy(),
166
+ classes=CocoPanopticDataset.METAINFO['classes'],
167
+ palette=CocoPanopticDataset.METAINFO['palette']
168
+ )
169
  return image, output_img
170
 
171
 
 
187
 
188
  with gr.Row():
189
  with gr.Column():
190
+ mode = gr.Radio(
191
+ ["Panoptic Segmentation", "Instance Segmentation"],
192
+ label="Mode",
193
+ value="Panoptic Segmentation",
194
+ info="Please select the segmentation mode. (Ignored if provided with prompt.)"
195
+ )
196
  with gr.Row():
197
  with gr.Column():
198
  segment_btn = gr.Button("Segment", variant="primary")
 
225
 
226
  segment_btn.click(
227
  segment_point,
228
+ [img_p, img_state, mode],
229
  [img_p, segm_p]
230
  )
231
 
seg/models/detectors/mask2former_vid.py CHANGED
@@ -140,21 +140,23 @@ class Mask2formerVideo(SingleStageDetector):
140
  assert len(self.OVERLAPPING) == self.num_classes
141
  mask_cls_results = self.open_voc_inference(feats, mask_cls_results, mask_pred_results)
142
 
143
- if self.inference_sam:
144
- for idx, data_sample in enumerate(batch_data_samples):
145
- results = InstanceData()
146
- mask = mask_pred_results[idx]
147
- img_height, img_width = data_sample.metainfo['img_shape'][:2]
148
- mask = mask[:, :img_height, :img_width]
149
- ori_height, ori_width = data_sample.metainfo['ori_shape'][:2]
150
- mask = F.interpolate(
151
- mask[:, None],
152
- size=(ori_height, ori_width),
153
- mode='bilinear',
154
- align_corners=False)[:, 0]
155
- results.masks = mask.sigmoid() > 0.5
156
- data_sample.pred_instances = results
157
- return batch_data_samples
 
 
158
 
159
  if num_frames > 0:
160
  for frame_id in range(num_frames):
@@ -178,7 +180,7 @@ class Mask2formerVideo(SingleStageDetector):
178
  )
179
  results = self.add_pred_to_datasample(batch_data_samples, results_list)
180
 
181
- return results
182
 
183
  def add_pred_to_datasample(self, data_samples: SampleList,
184
  results_list: List[dict]) -> SampleList:
 
140
  assert len(self.OVERLAPPING) == self.num_classes
141
  mask_cls_results = self.open_voc_inference(feats, mask_cls_results, mask_pred_results)
142
 
143
+ if batch_data_samples[0].data_tag == 'sam':
144
+ return mask_pred_results.cpu().numpy()
145
+ # # if self.inference_sam:
146
+ # for idx, data_sample in enumerate(batch_data_samples):
147
+ # results = InstanceData()
148
+ # mask = mask_pred_results[idx]
149
+ # img_height, img_width = data_sample.metainfo['img_shape'][:2]
150
+ # mask = mask[:, :img_height, :img_width]
151
+ # ori_height, ori_width = data_sample.metainfo['ori_shape'][:2]
152
+ # mask = F.interpolate(
153
+ # mask[:, None],
154
+ # size=(ori_height, ori_width),
155
+ # mode='bilinear',
156
+ # align_corners=False)[:, 0]
157
+ # results.masks = mask.sigmoid() > 0.5
158
+ # data_sample.pred_instances = results
159
+ # return batch_data_samples
160
 
161
  if num_frames > 0:
162
  for frame_id in range(num_frames):
 
180
  )
181
  results = self.add_pred_to_datasample(batch_data_samples, results_list)
182
 
183
+ return results_list
184
 
185
  def add_pred_to_datasample(self, data_samples: SampleList,
186
  results_list: List[dict]) -> SampleList:
seg/models/utils/__init__.py CHANGED
@@ -2,6 +2,4 @@ from .video_gt_preprocess import preprocess_video_panoptic_gt
2
  from .mask_pool import mask_pool
3
  from .pan_seg_transform import INSTANCE_OFFSET_HB, mmpan2hbpan, mmgt2hbpan
4
  from .class_overlapping import calculate_class_overlapping
5
- from .online_pq_utils import cal_pq, IoUObj, NO_OBJ_ID
6
  from .no_obj import NO_OBJ
7
- from .offline_video_metrics import vpq_eval, stq
 
2
  from .mask_pool import mask_pool
3
  from .pan_seg_transform import INSTANCE_OFFSET_HB, mmpan2hbpan, mmgt2hbpan
4
  from .class_overlapping import calculate_class_overlapping
 
5
  from .no_obj import NO_OBJ
 
seg/models/utils/offline_video_metrics.py DELETED
@@ -1,114 +0,0 @@
1
- import numpy as np
2
-
3
- from seg.models.utils import NO_OBJ, INSTANCE_OFFSET_HB
4
-
5
-
6
- def vpq_eval(element, num_classes=-1, max_ins=INSTANCE_OFFSET_HB, ign_id=NO_OBJ):
7
- assert num_classes != -1
8
- import six
9
- pred_ids, gt_ids = element
10
- offset = 1e7 # 1e7 > 200 * max_ins
11
- assert offset > num_classes * max_ins
12
- num_cat = num_classes + 1
13
-
14
- iou_per_class = np.zeros(num_cat, dtype=np.float64)
15
- tp_per_class = np.zeros(num_cat, dtype=np.float64)
16
- fn_per_class = np.zeros(num_cat, dtype=np.float64)
17
- fp_per_class = np.zeros(num_cat, dtype=np.float64)
18
-
19
- def _ids_to_counts(id_array):
20
- ids, counts = np.unique(id_array, return_counts=True)
21
- return dict(six.moves.zip(ids, counts))
22
-
23
- pred_areas = _ids_to_counts(pred_ids)
24
- gt_areas = _ids_to_counts(gt_ids)
25
-
26
- void_id = ign_id * max_ins
27
- ign_ids = {
28
- gt_id for gt_id in six.iterkeys(gt_areas)
29
- if (gt_id // max_ins) == ign_id
30
- }
31
-
32
- int_ids = gt_ids.astype(np.uint64) * offset + pred_ids.astype(np.uint64)
33
- int_areas = _ids_to_counts(int_ids)
34
-
35
- def prediction_void_overlap(pred_id):
36
- void_int_id = void_id * offset + pred_id
37
- return int_areas.get(void_int_id, 0)
38
-
39
- def prediction_ignored_overlap(pred_id):
40
- total_ignored_overlap = 0
41
- for _ign_id in ign_ids:
42
- int_id = _ign_id * offset + pred_id
43
- total_ignored_overlap += int_areas.get(int_id, 0)
44
- return total_ignored_overlap
45
-
46
- gt_matched = set()
47
- pred_matched = set()
48
-
49
- for int_id, int_area in six.iteritems(int_areas):
50
- gt_id = int(int_id // offset)
51
- gt_cat = int(gt_id // max_ins)
52
- pred_id = int(int_id % offset)
53
- pred_cat = int(pred_id // max_ins)
54
- if gt_cat != pred_cat:
55
- continue
56
- union = (
57
- gt_areas[gt_id] + pred_areas[pred_id] - int_area -
58
- prediction_void_overlap(pred_id)
59
- )
60
- iou = int_area / union
61
- if iou > 0.5:
62
- tp_per_class[gt_cat] += 1
63
- iou_per_class[gt_cat] += iou
64
- gt_matched.add(gt_id)
65
- pred_matched.add(pred_id)
66
-
67
- for gt_id in six.iterkeys(gt_areas):
68
- if gt_id in gt_matched:
69
- continue
70
- cat_id = gt_id // max_ins
71
- if cat_id == ign_id:
72
- continue
73
- fn_per_class[cat_id] += 1
74
-
75
- for pred_id in six.iterkeys(pred_areas):
76
- if pred_id in pred_matched:
77
- continue
78
- if (prediction_ignored_overlap(pred_id) / pred_areas[pred_id]) > 0.5:
79
- continue
80
- cat = pred_id // max_ins
81
- fp_per_class[cat] += 1
82
-
83
- return iou_per_class, tp_per_class, fn_per_class, fp_per_class
84
-
85
-
86
- def stq(element, num_classes=19, max_ins=10000, ign_id=NO_OBJ, num_things=8, label_divisor=1e4, ins_divisor=1e7):
87
- y_pred, y_true = element
88
- y_true = y_true.astype(np.int64)
89
- y_pred = y_pred.astype(np.int64)
90
-
91
- # semantic eval
92
- semantic_label = y_true // max_ins
93
- semantic_prediction = y_pred // max_ins
94
- semantic_label = np.where(semantic_label != ign_id,
95
- semantic_label, num_classes)
96
- semantic_prediction = np.where(semantic_prediction != ign_id,
97
- semantic_prediction, num_classes)
98
- semantic_ids = np.reshape(semantic_label, [-1]) * label_divisor + np.reshape(semantic_prediction, [-1])
99
-
100
- # instance eval
101
- instance_label = y_true % max_ins
102
- label_mask = np.less(semantic_label, num_things)
103
- prediction_mask = np.less(semantic_label, num_things)
104
- is_crowd = np.logical_and(instance_label == 0, label_mask)
105
-
106
- label_mask = np.logical_and(label_mask, np.logical_not(is_crowd))
107
- prediction_mask = np.logical_and(prediction_mask, np.logical_not(is_crowd))
108
-
109
- seq_preds = y_pred[prediction_mask]
110
- seg_labels = y_true[label_mask]
111
-
112
- non_crowd_intersection = np.logical_and(label_mask, prediction_mask)
113
- intersection_ids = (y_true[non_crowd_intersection] * ins_divisor + y_pred[non_crowd_intersection])
114
- return semantic_ids, seq_preds, seg_labels, intersection_ids
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
seg/models/utils/online_pq_utils.py DELETED
@@ -1,73 +0,0 @@
1
- from seg.models.utils.no_obj import NO_OBJ
2
- from seg.models.utils.pan_seg_transform import INSTANCE_OFFSET_HB
3
- from panopticapi.evaluation import PQStat
4
-
5
- NO_OBJ_ID = NO_OBJ * INSTANCE_OFFSET_HB
6
-
7
-
8
- class IoUObj:
9
- def __init__(self, intersection: int = 0, union: int = 0):
10
- self.intersection = intersection
11
- self.union = union
12
-
13
- def __iadd__(self, other):
14
- self.intersection += other.intersection
15
- self.union += other.union
16
- return self
17
-
18
- def __isub__(self, other):
19
- self.intersection -= other.intersection
20
- self.union -= other.union
21
- return self
22
-
23
- def is_legal(self):
24
- return self.intersection >= 0 and self.union >= 0
25
-
26
- @property
27
- def iou(self):
28
- return self.intersection / self.union
29
-
30
-
31
- def cal_pq(global_intersection_info, classes):
32
- num_classes = len(classes)
33
- gt_matched = set()
34
- pred_matched = set()
35
-
36
- gt_all = set()
37
- pred_all = set()
38
-
39
- pq_stat = PQStat()
40
- for gt_id, pred_id in global_intersection_info:
41
- gt_cat = gt_id // INSTANCE_OFFSET_HB
42
- pred_cat = pred_id // INSTANCE_OFFSET_HB
43
- assert pred_cat < num_classes
44
- if global_intersection_info[gt_id, pred_id].union == 0:
45
- continue
46
- if gt_cat == NO_OBJ:
47
- continue
48
- gt_all.add(gt_id)
49
- pred_all.add(pred_id)
50
- if gt_cat != pred_cat:
51
- continue
52
- iou = global_intersection_info[gt_id, pred_id].iou
53
- if iou > 0.5:
54
- pq_stat[gt_cat].tp += 1
55
- pq_stat[gt_cat].iou += iou
56
- gt_matched.add(gt_id)
57
- pred_matched.add(pred_id)
58
-
59
- for gt_id in gt_all:
60
- gt_cat = gt_id // INSTANCE_OFFSET_HB
61
- if gt_id in gt_matched:
62
- continue
63
- pq_stat[gt_cat].fn += 1
64
-
65
- for pred_id in pred_all:
66
- pred_cat = pred_id // INSTANCE_OFFSET_HB
67
- if pred_id in pred_matched:
68
- continue
69
- if global_intersection_info[NO_OBJ_ID, pred_id].iou > 0.5:
70
- continue
71
- pq_stat[pred_cat].fp += 1
72
-
73
- return pq_stat