Skip to content

Adding dump for VOC instance mask. #859

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 19, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions cvat/apps/annotation/mask_instance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Copyright (C) 2019 Intel Corporation
#
# SPDX-License-Identifier: MIT

format_spec = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest adding this functionality to pascal_voc.py nearby as one extra dumper in the corresponding list.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhiltsov-max , @gachiemchiep , It is a great idea to join several parsers. I believe MASK and MASK INSTANCE are very similar. Thus it is better to extend "MASK" annotation format instead of creating a new one. Thus it will not be necessary to duplicate code.

Copy link
Contributor Author

@gachiemchiep gachiemchiep Nov 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhiltsov-max you're right. mask and mask instance are used in pascal voc so those parser should be merged into pascal_voc.py. Maybe it is a good idea to dump "bounding box" + "mask" + "mask instance" at the same time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhiltsov-max I merged the mask_instance and mask into mask.py.

"name": "MASK INSTANCE",
"dumpers": [
{
"display_name": "{name} {format} {version}",
"format": "ZIP",
"version": "1.0",
"handler": "dump"
},
],
"loaders": [
],
}

def dump(file_object, annotations):
from zipfile import ZipFile
import numpy as np
import os
from pycocotools import mask as maskUtils
import matplotlib.image
import io
from collections import OrderedDict

# RGB format, (0, 0, 0) used for background
def genearte_pascal_colormap(size=256):
colormap = np.zeros((size, 3), dtype=int)
ind = np.arange(size, dtype=int)

for shift in reversed(range(8)):
for channel in range(3):
colormap[:, channel] |= ((ind >> channel) & 1) << shift
ind >>= 3

return colormap

def convert_box_to_polygon(points):
xtl = shape.points[0]
ytl = shape.points[1]
xbr = shape.points[2]
ybr = shape.points[3]

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

colormap = genearte_pascal_colormap()
instance_colors = OrderedDict((idx, colormap[idx]) for idx in range(len(colormap)))

with ZipFile(file_object, "w") as output_zip:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding some compression may be not extraneous, check compression c-tor parameter, e.g. deflate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zhiltsov-max by default, the ZipFile use ZIP_STORED so it won't use any compression.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so I suggest adding it, or, at least, checking if it worth, as masks are png images, which do take some space.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the ZIP_STORED in the new commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check if ZIP_DEFLATED helps to reduce archive size, and add this parameter.

for frame_annotation in annotations.group_by_frame():
image_name = frame_annotation.name
annotation_name = "{}.png".format(os.path.splitext(os.path.basename(image_name))[0])
width = frame_annotation.width
height = frame_annotation.height

shapes = frame_annotation.labeled_shapes
# convert to mask only rectangles and polygons
shapes = [shape for shape in shapes if shape.type == 'rectangle' or shape.type == 'polygon']
if not shapes:
continue
shapes = sorted(shapes, key=lambda x: int(x.z_order))
img = np.zeros((height, width, 3))
buf = io.BytesIO()
for cnt, shape in enumerate(shapes):
points = shape.points if shape.type != 'rectangle' else convert_box_to_polygon(shape.points)
rles = maskUtils.frPyObjects([points], height, width)
rle = maskUtils.merge(rles)
mask = maskUtils.decode(rle)
# 0 is used background color. instance mask color started from 1
color = instance_colors[cnt+1] / 255
idx = (mask > 0)
img[idx] = color

matplotlib.image.imsave(buf, img, format='png')
output_zip.writestr(annotation_name, buf.getvalue())
labels = '\n'.join('{}:{}'.format(label, ','.join(str(i) for i in color)) for label, color in instance_colors.items())
output_zip.writestr('colormap.txt', labels)
1 change: 1 addition & 0 deletions cvat/apps/annotation/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
os.path.join(path_prefix, 'tfrecord.py'),
os.path.join(path_prefix, 'mot.py'),
os.path.join(path_prefix, 'labelme.py'),
os.path.join(path_prefix, 'mask_instance.py'),
)