Skip to content

Commit 458bd23

Browse files
gachiemchiepnmanovic
authored andcommitted
Adding dump for VOC instance mask. (#859)
* Add mask instance dumper * Fix bug * Merge mask instance into mask * Merge the change into mask * Create MaskColorizer * Add dump method
1 parent f57586a commit 458bd23

File tree

2 files changed

+93
-37
lines changed

2 files changed

+93
-37
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Format selection is possible after clicking on the Upload annotation / Dump anno
4242
| [YOLO](https://pjreddie.com/darknet/yolo/) | X | X |
4343
| [MS COCO Object Detection](http://cocodataset.org/#format-data) | X | X |
4444
| PNG mask | X | |
45+
| PNG instance mask | X | |
4546
| [TFrecord](https://www.tensorflow.org/tutorials/load_data/tf_records) | X | X |
4647
| [MOT](https://motchallenge.net/) | X | X |
4748
| [LabelMe](http://labelme.csail.mit.edu/Release3.0) | X | X |

cvat/apps/annotation/mask.py

Lines changed: 92 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,91 @@
66
"name": "MASK",
77
"dumpers": [
88
{
9-
"display_name": "{name} {format} {version}",
9+
"display_name": "{name} (by class) {format} {version}",
1010
"format": "ZIP",
1111
"version": "1.0",
12-
"handler": "dump"
12+
"handler": "dump_by_class"
13+
},
14+
{
15+
"display_name": "{name} (by instance) {format} {version}",
16+
"format": "ZIP",
17+
"version": "1.0",
18+
"handler": "dump_by_instance"
1319
},
1420
],
1521
"loaders": [
1622
],
1723
}
1824

19-
def dump(file_object, annotations):
20-
from zipfile import ZipFile
21-
import numpy as np
22-
import os
23-
from pycocotools import mask as maskUtils
24-
import matplotlib.image
25-
import io
26-
from collections import OrderedDict
27-
28-
# RGB format, (0, 0, 0) used for background
29-
def genearte_pascal_colormap(size=256):
30-
colormap = np.zeros((size, 3), dtype=int)
31-
ind = np.arange(size, dtype=int)
32-
33-
for shift in reversed(range(8)):
34-
for channel in range(3):
35-
colormap[:, channel] |= ((ind >> channel) & 1) << shift
36-
ind >>= 3
37-
38-
return colormap
25+
MASK_BY_CLASS = 0
26+
MASK_BY_INSTANCE = 1
3927

40-
def convert_box_to_polygon(points):
28+
def convert_box_to_polygon(shape):
4129
xtl = shape.points[0]
4230
ytl = shape.points[1]
4331
xbr = shape.points[2]
4432
ybr = shape.points[3]
4533

4634
return [xtl, ytl, xbr, ytl, xbr, ybr, xtl, ybr]
4735

48-
colormap = genearte_pascal_colormap()
49-
labels = [label[1]["name"] for label in annotations.meta["task"]["labels"] if label[1]["name"] != 'background']
50-
labels.insert(0, 'background')
51-
label_colors = OrderedDict((label, colormap[idx]) for idx, label in enumerate(labels))
36+
def create_mask_colorizer(annotations, colorize_type):
37+
import numpy as np
38+
from collections import OrderedDict
39+
40+
class MaskColorizer:
41+
42+
def __init__(self, annotations, colorize_type):
43+
44+
if colorize_type == MASK_BY_CLASS:
45+
self.colors = self.gen_class_mask_colors(annotations)
46+
elif colorize_type == MASK_BY_INSTANCE:
47+
self.colors = self.gen_instance_mask_colors()
48+
49+
def generate_pascal_colormap(self, size=256):
50+
# RGB format, (0, 0, 0) used for background
51+
colormap = np.zeros((size, 3), dtype=int)
52+
ind = np.arange(size, dtype=int)
53+
54+
for shift in reversed(range(8)):
55+
for channel in range(3):
56+
colormap[:, channel] |= ((ind >> channel) & 1) << shift
57+
ind >>= 3
5258

53-
with ZipFile(file_object, "w") as output_zip:
59+
return colormap
60+
61+
def gen_class_mask_colors(self, annotations):
62+
colormap = self.generate_pascal_colormap()
63+
labels = [label[1]["name"] for label in annotations.meta["task"]["labels"] if label[1]["name"] != 'background']
64+
labels.insert(0, 'background')
65+
label_colors = OrderedDict((label, colormap[idx]) for idx, label in enumerate(labels))
66+
67+
return label_colors
68+
69+
def gen_instance_mask_colors(self):
70+
colormap = self.generate_pascal_colormap()
71+
# The first color is black
72+
instance_colors = OrderedDict((idx, colormap[idx]) for idx in range(len(colormap)))
73+
74+
return instance_colors
75+
76+
return MaskColorizer(annotations, colorize_type)
77+
78+
def dump(file_object, annotations, colorize_type):
79+
80+
from zipfile import ZipFile, ZIP_STORED
81+
import numpy as np
82+
import os
83+
from pycocotools import mask as maskUtils
84+
import matplotlib.image
85+
import io
86+
87+
colorizer = create_mask_colorizer(annotations, colorize_type=colorize_type)
88+
if colorize_type == MASK_BY_CLASS:
89+
save_dir = "SegmentationClass"
90+
elif colorize_type == MASK_BY_INSTANCE:
91+
save_dir = "SegmentationObject"
92+
93+
with ZipFile(file_object, "w", ZIP_STORED) as output_zip:
5494
for frame_annotation in annotations.group_by_frame():
5595
image_name = frame_annotation.name
5696
annotation_name = "{}.png".format(os.path.splitext(os.path.basename(image_name))[0])
@@ -63,18 +103,33 @@ def convert_box_to_polygon(points):
63103
if not shapes:
64104
continue
65105
shapes = sorted(shapes, key=lambda x: int(x.z_order))
66-
img = np.zeros((height, width, 3))
67-
buf = io.BytesIO()
68-
for shape in shapes:
69-
points = shape.points if shape.type != 'rectangle' else convert_box_to_polygon(shape.points)
106+
img_mask = np.zeros((height, width, 3))
107+
buf_mask = io.BytesIO()
108+
for shape_index, shape in enumerate(shapes):
109+
points = shape.points if shape.type != 'rectangle' else convert_box_to_polygon(shape)
70110
rles = maskUtils.frPyObjects([points], height, width)
71111
rle = maskUtils.merge(rles)
72112
mask = maskUtils.decode(rle)
73-
color = label_colors[shape.label] / 255
74113
idx = (mask > 0)
75-
img[idx] = color
114+
# get corresponding color
115+
if colorize_type == MASK_BY_CLASS:
116+
color = colorizer.colors[shape.label] / 255
117+
elif colorize_type == MASK_BY_INSTANCE:
118+
color = colorizer.colors[shape_index+1] / 255
119+
120+
img_mask[idx] = color
76121

77-
matplotlib.image.imsave(buf, img, format='png')
78-
output_zip.writestr(annotation_name, buf.getvalue())
79-
labels = '\n'.join('{}:{}'.format(label, ','.join(str(i) for i in color)) for label, color in label_colors.items())
122+
# write mask
123+
matplotlib.image.imsave(buf_mask, img_mask, format='png')
124+
output_zip.writestr(os.path.join(save_dir, annotation_name), buf_mask.getvalue())
125+
# Store color map for each class
126+
labels = '\n'.join('{}:{}'.format(label, ','.join(str(i) for i in color)) for label, color in colorizer.colors.items())
80127
output_zip.writestr('colormap.txt', labels)
128+
129+
def dump_by_class(file_object, annotations):
130+
131+
return dump(file_object, annotations, MASK_BY_CLASS)
132+
133+
def dump_by_instance(file_object, annotations):
134+
135+
return dump(file_object, annotations, MASK_BY_INSTANCE)

0 commit comments

Comments
 (0)