Spaces:
Sleeping
Sleeping
HarborYuan
commited on
Commit
•
1f8df14
1
Parent(s):
acdf070
adapt for omg
Browse files- app/configs/m2_convl.py +1 -0
- ext/cityscapes_scripts/createPanopticImgs.py +0 -194
- ext/cityscapes_scripts/helpers/__init__.py +0 -1
- ext/cityscapes_scripts/helpers/annotation.py +0 -441
- ext/cityscapes_scripts/helpers/csHelpers.py +0 -129
- ext/cityscapes_scripts/helpers/labels.py +0 -182
- ext/cityscapes_scripts/helpers/labels_cityPersons.py +0 -61
- ext/cityscapes_scripts/helpers/version.py +0 -9
- ext/davis2017/__init__.py +0 -3
- ext/davis2017/davis.py +0 -122
- ext/davis2017/evaluation.py +0 -110
- ext/davis2017/metrics.py +0 -197
- ext/davis2017/results.py +0 -52
- ext/davis2017/utils.py +0 -174
- main.py +31 -15
- seg/models/detectors/mask2former_vid.py +18 -16
- seg/models/utils/__init__.py +0 -2
- seg/models/utils/offline_video_metrics.py +0 -114
- seg/models/utils/online_pq_utils.py +0 -73
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 <[email protected]>
|
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.
|
129 |
-
gt_instances.
|
130 |
-
batch_data_samples[0].
|
|
|
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 |
-
|
141 |
|
142 |
-
|
143 |
-
masks = masks[0]
|
144 |
if is_prompt:
|
145 |
masks = masks[0, :h, :w]
|
146 |
-
masks = masks > 0.
|
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 |
-
|
154 |
-
output_img
|
155 |
-
|
156 |
-
|
157 |
-
|
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
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
|
|
|
|
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
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|