Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
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