Skip to content

Commit 0d39b59

Browse files
committed
VOC attributes
1 parent f8410b7 commit 0d39b59

File tree

3 files changed

+116
-40
lines changed

3 files changed

+116
-40
lines changed

superannotate/input_converters/converters/voc_converters/voc_strategies.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ def __str__(self):
3333
return '{} object'.format(self.name)
3434

3535
def to_sa_format(self):
36-
sa_classes, sa_jsons, sa_masks = self.conversion_algorithm(
37-
self.export_root
36+
sa_classes, sa_jsons = self.conversion_algorithm(
37+
self.export_root, self.output_dir
3838
)
3939
dump_output(self.output_dir, self.platform, sa_classes, sa_jsons)
4040

@@ -43,7 +43,3 @@ def to_sa_format(self):
4343
for file in all_files:
4444
if '___save.png' not in str(file.name):
4545
(self.output_dir / file.name).unlink()
46-
47-
for sa_mask_name, sa_mask_value in sa_masks.items():
48-
sa_mask_value = sa_mask_value[:, :, ::-1]
49-
cv2.imwrite(str(self.output_dir / sa_mask_name), sa_mask_value)

superannotate/input_converters/converters/voc_converters/voc_to_sa_pixel.py

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ def _generate_polygons(object_mask_path, class_mask_path):
2626
if unique_color in (0, 220):
2727
continue
2828

29-
# class_color = class_mask[object_mask == unique_color][0]
3029
mask = np.zeros_like(object_mask)
3130
mask[object_mask == unique_color] = 255
3231
contours, _ = cv2.findContours(
@@ -63,7 +62,6 @@ def _iou(bbox1, bbox2):
6362
def _generate_instances(polygon_instances, voc_instances, bluemask_colors):
6463
instances = []
6564
i = 0
66-
# for polygon, color_id in polygon_instances:
6765
for polygon in polygon_instances:
6866
ious = []
6967
bbox_poly = [
@@ -72,16 +70,18 @@ def _generate_instances(polygon_instances, voc_instances, bluemask_colors):
7270
max(polygon[::2]),
7371
max(polygon[1::2])
7472
]
75-
for class_name, bbox in voc_instances:
73+
for _, bbox in voc_instances:
7674
ious.append(_iou(bbox_poly, bbox))
7775
ind = np.argmax(ious)
76+
class_name = list(voc_instances[ind][0].keys())[0]
77+
attributes = voc_instances[ind][0][class_name]
7878
instances.append(
7979
{
80-
"className": voc_instances[ind][0],
81-
# "classId": color_id,
80+
"className": class_name,
8281
"polygon": polygon,
8382
"bbox": voc_instances[ind][1],
84-
"blue_color": bluemask_colors[i]
83+
"blue_color": bluemask_colors[i],
84+
'classAttributes': attributes
8585
}
8686
)
8787
i += 1
@@ -95,31 +95,65 @@ def _get_voc_instances_from_xml(file_path):
9595
voc_instances = []
9696
for instance in instances:
9797
class_name = instance.find("name").text
98+
class_attributes = []
99+
for attr in ['pose', 'occluded', 'difficult', 'truncated']:
100+
attr_value = instance.find(attr)
101+
if attr_value is not None:
102+
class_attributes.append(
103+
{
104+
'name': attr_value.text,
105+
'groupName': attr
106+
}
107+
)
108+
98109
bbox = instance.find("bndbox")
99110
bbox = [
100111
float(bbox.find(x).text) for x in ["xmin", "ymin", "xmax", "ymax"]
101112
]
102-
voc_instances.append((class_name, bbox))
113+
voc_instances.append(({class_name: class_attributes}, bbox))
103114
return voc_instances
104115

105116

106-
def _create_classes(classes):
117+
def _create_classes(voc_instance):
118+
classes = {}
119+
for instance in voc_instance:
120+
for class_, value in instance.items():
121+
if class_ not in classes:
122+
classes[class_] = {}
123+
124+
for attr in value:
125+
if attr['groupName'] in classes[class_]:
126+
classes[class_][attr['groupName']].append(attr['name'])
127+
else:
128+
classes[class_][attr['groupName']] = [attr['name']]
129+
107130
sa_classes = []
108-
# for class_, id_ in classes.items():
109-
for class_ in set(classes):
131+
for class_ in classes:
110132
color = np.random.choice(range(256), size=3)
111133
hexcolor = "#%02x%02x%02x" % tuple(color)
134+
attribute_groups = []
135+
for attr_group, value in classes[class_].items():
136+
attributes = []
137+
for attr in set(value):
138+
attributes.append({'name': attr})
139+
140+
attribute_groups.append(
141+
{
142+
'name': attr_group,
143+
'is_multiselect': 0,
144+
'attributes': attributes
145+
}
146+
)
112147
sa_class = {
113-
# "id": id_,
114148
"name": class_,
115149
"color": hexcolor,
116-
"attribute_groups": []
150+
"attribute_groups": attribute_groups
117151
}
118152
sa_classes.append(sa_class)
119153
return sa_classes
120154

121155

122-
def voc_instance_segmentation_to_sa_pixel(voc_root):
156+
def voc_instance_segmentation_to_sa_pixel(voc_root, output_dir):
123157
classes = []
124158
object_masks_dir = voc_root / 'SegmentationObject'
125159
class_masks_dir = voc_root / 'SegmentationClass'
@@ -135,6 +169,9 @@ def voc_instance_segmentation_to_sa_pixel(voc_root):
135169
voc_instances = _get_voc_instances_from_xml(
136170
annotation_dir / filename.name
137171
)
172+
for class_, _ in voc_instances:
173+
classes.append(class_)
174+
138175
maped_instances = _generate_instances(
139176
polygon_instances, voc_instances, bluemask_colors
140177
)
@@ -152,13 +189,12 @@ def voc_instance_segmentation_to_sa_pixel(voc_root):
152189
'visible': True,
153190
}
154191
sa_loader.append(sa_polygon)
155-
classes.append(instance['className'])
156192

157193
sa_file_name = os.path.splitext(filename.name)[0] + ".jpg___pixel.json"
158194
sa_jsons[sa_file_name] = sa_loader
159195

160196
sa_mask_name = os.path.splitext(filename.name)[0] + ".jpg___save.png"
161-
sa_masks[sa_mask_name] = sa_mask
197+
cv2.imwrite(str(output_dir / sa_mask_name), sa_mask[:, :, ::-1])
162198

163199
classes = _create_classes(classes)
164-
return (classes, sa_jsons, sa_masks)
200+
return (classes, sa_jsons)

superannotate/input_converters/converters/voc_converters/voc_to_sa_vector.py

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,19 @@ def _generate_instances(polygon_instances, voc_instances):
6969
max(temp[::2]),
7070
max(temp[1::2])
7171
]
72-
for class_name, bbox in voc_instances:
72+
for _, bbox in voc_instances:
7373
ious.append(_iou(bbox_poly, bbox))
7474
ind = np.argmax(ious)
7575
for poly in polygon:
76+
class_name = list(voc_instances[ind][0].keys())[0]
77+
attributes = voc_instances[ind][0][class_name]
7678
instances.append(
7779
{
78-
'className': voc_instances[ind][0],
80+
'className': class_name,
7981
'polygon': poly,
8082
'bbox': voc_instances[ind][1],
81-
'groupId': group_id
83+
'groupId': group_id,
84+
'classAttributes': attributes
8285
}
8386
)
8487
return instances
@@ -91,26 +94,65 @@ def _get_voc_instances_from_xml(file_path):
9194
voc_instances = []
9295
for instance in instances:
9396
class_name = instance.find("name").text
97+
class_attributes = []
98+
for attr in ['pose', 'occluded', 'difficult', 'truncated']:
99+
attr_value = instance.find(attr)
100+
if attr_value is not None:
101+
class_attributes.append(
102+
{
103+
'name': attr_value.text,
104+
'groupName': attr
105+
}
106+
)
107+
94108
bbox = instance.find("bndbox")
95109
bbox = [
96110
float(bbox.find(x).text) for x in ["xmin", "ymin", "xmax", "ymax"]
97111
]
98-
voc_instances.append((class_name, bbox))
112+
voc_instances.append(({class_name: class_attributes}, bbox))
99113
return voc_instances
100114

101115

102-
def _create_classes(classes):
116+
def _create_classes(voc_instance):
117+
classes = {}
118+
for instance in voc_instance:
119+
for class_, value in instance.items():
120+
if class_ not in classes:
121+
classes[class_] = {}
122+
123+
for attr in value:
124+
if attr['groupName'] in classes[class_]:
125+
classes[class_][attr['groupName']].append(attr['name'])
126+
else:
127+
classes[class_][attr['groupName']] = [attr['name']]
128+
103129
sa_classes = []
104-
for class_ in set(classes):
130+
for class_ in classes:
105131
color = np.random.choice(range(256), size=3)
106132
hexcolor = "#%02x%02x%02x" % tuple(color)
107-
sa_class = {"name": class_, "color": hexcolor, "attribute_groups": []}
133+
attribute_groups = []
134+
for attr_group, value in classes[class_].items():
135+
attributes = []
136+
for attr in set(value):
137+
attributes.append({'name': attr})
138+
139+
attribute_groups.append(
140+
{
141+
'name': attr_group,
142+
'is_multiselect': 0,
143+
'attributes': attributes
144+
}
145+
)
146+
sa_class = {
147+
"name": class_,
148+
"color": hexcolor,
149+
"attribute_groups": attribute_groups
150+
}
108151
sa_classes.append(sa_class)
109152
return sa_classes
110153

111154

112-
def voc_instance_segmentation_to_sa_vector(voc_root):
113-
# classes = {}
155+
def voc_instance_segmentation_to_sa_vector(voc_root, output_dir):
114156
classes = []
115157
object_masks_dir = voc_root / 'SegmentationObject'
116158
class_masks_dir = voc_root / 'SegmentationClass'
@@ -125,32 +167,33 @@ def voc_instance_segmentation_to_sa_vector(voc_root):
125167
voc_instances = _get_voc_instances_from_xml(
126168
annotation_dir / filename.name
127169
)
170+
for class_, _ in voc_instances:
171+
classes.append(class_)
172+
128173
maped_instances = _generate_instances(polygon_instances, voc_instances)
129174
sa_loader = []
130175
for instance in maped_instances:
131176
sa_polygon = {
132177
'type': 'polygon',
133178
'points': instance["polygon"],
134179
'className': instance["className"],
135-
'attributes': [],
180+
'attributes': instance['classAttributes'],
136181
'probability': 100,
137182
'locked': False,
138183
'visible': True,
139184
'groupId': instance['groupId']
140185
}
141186
sa_loader.append(sa_polygon)
142187

143-
classes.append(instance["className"])
144-
145188
sa_file_name = os.path.splitext(filename.name
146189
)[0] + ".jpg___objects.json"
147190
sa_jsons[sa_file_name] = sa_loader
148191

149192
classes = _create_classes(classes)
150-
return (classes, sa_jsons, None)
193+
return (classes, sa_jsons)
151194

152195

153-
def voc_object_detection_to_sa_vector(voc_root):
196+
def voc_object_detection_to_sa_vector(voc_root, output_dir):
154197
classes = []
155198
annotation_dir = voc_root / "Annotations"
156199
files_list = annotation_dir.glob('*')
@@ -160,15 +203,16 @@ def voc_object_detection_to_sa_vector(voc_root):
160203
annotation_dir / filename.name
161204
)
162205
sa_loader = []
163-
for class_name, bbox in voc_instances:
164-
classes.append(class_name)
206+
for class_, bbox in voc_instances:
207+
class_name = list(class_.keys())[0]
208+
classes.append(class_)
165209

166210
sa_bbox = {
167211
'type': "bbox",
168212
'className': class_name,
169213
'probability': 100,
170214
'points': [],
171-
'attributes': [],
215+
'attributes': class_[class_name],
172216
'points':
173217
{
174218
"x1": bbox[0],
@@ -184,4 +228,4 @@ def voc_object_detection_to_sa_vector(voc_root):
184228
sa_jsons[sa_file_name] = sa_loader
185229

186230
classes = _create_classes(classes)
187-
return (classes, sa_jsons, None)
231+
return (classes, sa_jsons)

0 commit comments

Comments
 (0)