Skip to content

Commit

Permalink
Add function to create multiple annotations from the same image. Closes
Browse files Browse the repository at this point in the history
#29

The functionality is implemented in a new function `create_annotation_infos`
with a similar API as the already existing `create_annotation_info`.

I think it makes sense to have a dedicated function for this use-case.

An example is at `samples/multi-annotations`.
  • Loading branch information
timofurrer committed Apr 4, 2020
1 parent 207b4fa commit 70b9142
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 3 deletions.
20 changes: 20 additions & 0 deletions examples/multi-annotations/multi_annotation_infos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from pathlib import Path
import json

from PIL import Image
import numpy as np

from pycococreatortools import pycococreatortools

SAMPLE_IMAGE_PATH = Path(__file__).parent / "sample.png"
assert SAMPLE_IMAGE_PATH.exists()

raw_image_bitmask = Image.open(SAMPLE_IMAGE_PATH)
image_bitmask = np.asarray(raw_image_bitmask.convert("1")).astype(np.uint8)

annotation_infos = pycococreatortools.create_annotation_infos(
1, 1, {"id": 1}, image_bitmask, raw_image_bitmask.size, tolerance=2, connectivity=1
)

print(f"Found {len(annotation_infos)} annotations in the image:")
print(json.dumps(annotation_infos, indent=4))
Binary file added examples/multi-annotations/sample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 55 additions & 3 deletions pycococreatortools/pycococreatortools.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ def create_annotation_info(annotation_id, image_id, category_info, binary_mask,
if bounding_box is None:
bounding_box = mask.toBbox(binary_mask_encoded)

if category_info["is_crowd"]:
if "is_crowd" in category_info and category_info["is_crowd"]:
is_crowd = 1
segmentation = binary_mask_to_rle(binary_mask)
else :
else:
is_crowd = 0
segmentation = binary_mask_to_polygon(binary_mask, tolerance)
if not segmentation:
Expand All @@ -110,6 +110,58 @@ def create_annotation_info(annotation_id, image_id, category_info, binary_mask,
"segmentation": segmentation,
"width": binary_mask.shape[1],
"height": binary_mask.shape[0],
}
}

return annotation_info


def create_annotation_infos(
start_annotation_id, image_id, category_info, binary_mask,
image_size=None, tolerance=2, connectivity=None
):
"""Create multiple annotation infos for each connected component in the given binary mask
The same category is used for each annotation.
The annotation ids start from `start_annotation_id` and
are incremented for each annotation.
The `connectivity` argument can be used to specify the connectivity
for the labels according to:
https://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label
Limitations:
* Crowds are not supported
"""
if "is_crowd" in category_info and category_info["is_crowd"]:
raise NotImplementedError("Creating multiple crowd annotations from a single binary mask is not supported")

if image_size is not None:
binary_mask = resize_binary_mask(binary_mask, image_size)

# label connected components in binary mask image
label_image = measure.label(binary_mask, connectivity=connectivity)
region_props = measure.regionprops(label_image)

# create a binary mask image per region property
binary_masks = []
for region_bbox in (r.bbox for r in region_props):
region_binary_mask = np.zeros_like(binary_mask, dtype=np.bool)

# copy the region into the region binary mask
bbox_slice = (
slice(region_bbox[0], region_bbox[2]),
slice(region_bbox[1], region_bbox[3]),
)
region_binary_mask[bbox_slice] = binary_mask[bbox_slice]
binary_masks.append(region_binary_mask)

# create annotations for each binary mask
annotation_infos = []
for annotation_id, region_binary_mask in enumerate(binary_masks, start=start_annotation_id):
annotation_info = create_annotation_info(
annotation_id, image_id, category_info, region_binary_mask,
image_size=image_size, tolerance=tolerance
)
annotation_infos.append(annotation_info)

return annotation_infos

0 comments on commit 70b9142

Please sign in to comment.