Skip to content

Commit

Permalink
Fixed regression with converting complex polygons to coco (#969)
Browse files Browse the repository at this point in the history
  • Loading branch information
JBWilkie authored Nov 21, 2024
1 parent f9eed67 commit 1562449
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 49 deletions.
86 changes: 58 additions & 28 deletions darwin/exporter/formats/coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import numpy as np
import orjson as json
from upolygon import draw_polygon, rle_encode

import darwin.datatypes as dt
from darwin.utils import convert_polygons_to_sequences
Expand Down Expand Up @@ -175,35 +176,64 @@ def _build_annotation(
) -> Optional[Dict[str, Any]]:
annotation_type = annotation.annotation_class.annotation_type
if annotation_type == "polygon":
sequences = convert_polygons_to_sequences(
annotation.data["paths"], rounding=False
)
x_coords = [s[0::2] for s in sequences]
y_coords = [s[1::2] for s in sequences]
min_x = np.min([np.min(x_coord) for x_coord in x_coords])
min_y = np.min([np.min(y_coord) for y_coord in y_coords])
max_x = np.max([np.max(x_coord) for x_coord in x_coords])
max_y = np.max([np.max(y_coord) for y_coord in y_coords])
w = max_x - min_x
h = max_y - min_y
# Compute the area of the polygon
poly_area = np.sum(
[
_polygon_area(x_coord, y_coord)
for x_coord, y_coord in zip(x_coords, y_coords)
]
)
if len(annotation.data["paths"]) == 1:
sequences = convert_polygons_to_sequences(
annotation.data["paths"], rounding=False
)
x_coords = [s[0::2] for s in sequences]
y_coords = [s[1::2] for s in sequences]
min_x = np.min([np.min(x_coord) for x_coord in x_coords])
min_y = np.min([np.min(y_coord) for y_coord in y_coords])
max_x = np.max([np.max(x_coord) for x_coord in x_coords])
max_y = np.max([np.max(y_coord) for y_coord in y_coords])
w = max_x - min_x
h = max_y - min_y
# Compute the area of the polygon
poly_area = np.sum(
[
_polygon_area(x_coord, y_coord)
for x_coord, y_coord in zip(x_coords, y_coords)
]
)

return {
"id": annotation_id,
"image_id": _build_image_id(annotation_file),
"category_id": categories[annotation.annotation_class.name],
"segmentation": sequences,
"area": poly_area,
"bbox": [min_x, min_y, w, h],
"iscrowd": 0,
"extra": _build_extra(annotation),
}
return {
"id": annotation_id,
"image_id": _build_image_id(annotation_file),
"category_id": categories[annotation.annotation_class.name],
"segmentation": sequences,
"area": poly_area,
"bbox": [min_x, min_y, w, h],
"iscrowd": 0,
"extra": _build_extra(annotation),
}
elif len(annotation.data["paths"]) > 1:
mask = np.zeros((annotation_file.image_height, annotation_file.image_width))
sequences = convert_polygons_to_sequences(annotation.data["paths"])
draw_polygon(mask, sequences, 1)
counts = rle_encode(mask)

x_coords = [s[0::2] for s in sequences]
y_coords = [s[1::2] for s in sequences]
min_x = np.min([np.min(x_coord) for x_coord in x_coords])
min_y = np.min([np.min(y_coord) for y_coord in y_coords])
max_x = np.max([np.max(x_coord) for x_coord in x_coords])
max_y = np.max([np.max(y_coord) for y_coord in y_coords])
w = max_x - min_x + 1
h = max_y - min_y + 1

return {
"id": annotation_id,
"image_id": _build_image_id(annotation_file),
"category_id": categories[annotation.annotation_class.name],
"segmentation": {
"counts": counts,
"size": [annotation_file.image_height, annotation_file.image_width],
},
"area": 0,
"bbox": [min_x, min_y, w, h],
"iscrowd": 1,
"extra": _build_extra(annotation),
}
elif annotation_type == "tag":
pass
elif annotation_type == "bounding_box":
Expand Down
28 changes: 27 additions & 1 deletion e2e_tests/data/convert/coco/from/base_annotation.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@
"schema_ref": "https://darwin-public.s3.eu-west-1.amazonaws.com/darwin_json/2.0/schema.json",
"item": {
"name": "",
"path": "/"
"path": "/",
"slots": [
{
"type": "image",
"slot_name": "0",
"width": 1080,
"height": 1920
}
]
},
"annotations": [
{
Expand Down Expand Up @@ -59,6 +67,24 @@
"x": 0.0,
"y": 1.0
}
],
[
{
"x": 1.0,
"y": 0.0
},
{
"x": 1.0,
"y": 1.0
},
{
"x": 0.0,
"y": 1.0
},
{
"x": 0.0,
"y": 0.0
}
]
]
}
Expand Down
39 changes: 20 additions & 19 deletions e2e_tests/data/convert/coco/to/output.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"license": 0,
"file_name": "",
"coco_url": "n/a",
"height": null,
"width": null,
"height": 1920,
"width": 1080,
"date_captured": "",
"flickr_url": "n/a",
"darwin_url": null,
Expand Down Expand Up @@ -60,26 +60,27 @@
"id": 3,
"image_id": 2043925204,
"category_id": 3961009249,
"segmentation": [
[
0.0,
0.0,
1.0,
0.0,
1.0,
1.0,
0.0,
1.0
"segmentation": {
"counts": [
0,
2,
1918,
2,
2071678
],
"size": [
1920,
1080
]
],
"area": 1.0,
},
"area": 0,
"bbox": [
0.0,
0.0,
1.0,
1.0
0,
0,
2,
2
],
"iscrowd": 0,
"iscrowd": 1,
"extra": {}
}
],
Expand Down
32 changes: 31 additions & 1 deletion tests/darwin/exporter/formats/export_coco_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ def annotation_file(self) -> dt.AnnotationFile:
filename="test.json",
annotation_classes=set(),
annotations=[],
image_height=1920,
image_width=1080,
)

def test_polygon_include_extras(self, annotation_file: dt.AnnotationFile):
polygon = dt.Annotation(
dt.AnnotationClass("polygon_class", "polygon"),
{"paths": [{"x": 1, "y": 1}, {"x": 2, "y": 2}, {"x": 1, "y": 2}]},
{"paths": [[{"x": 1, "y": 1}, {"x": 2, "y": 2}, {"x": 1, "y": 2}]]},
[dt.make_instance_id(1)],
)

Expand All @@ -29,6 +31,34 @@ def test_polygon_include_extras(self, annotation_file: dt.AnnotationFile):
"extra"
] == {"instance_id": 1}

def test_complex_polygon(self, annotation_file: dt.AnnotationFile):
polygon = dt.Annotation(
dt.AnnotationClass("polygon_class", "polygon"),
{
"paths": [
[{"x": 1, "y": 1}, {"x": 2, "y": 2}, {"x": 1, "y": 2}],
[{"x": 3, "y": 3}, {"x": 4, "y": 4}, {"x": 3, "y": 4}],
]
},
[],
)

categories = {"polygon_class": 1}

annotations = coco._build_annotation(annotation_file, 1, polygon, categories)
assert annotations["segmentation"]["counts"] == [
1921,
2,
1919,
1,
1920,
2,
1919,
1,
2065915,
]
assert annotations["segmentation"]["size"] == [1920, 1080]

def test_bounding_boxes_include_extras(self, annotation_file: dt.AnnotationFile):
bbox = dt.Annotation(
dt.AnnotationClass("bbox_class", "bounding_box"),
Expand Down

0 comments on commit 1562449

Please sign in to comment.