Skip to content

Commit eaaaab8

Browse files
support coco bbox format
1 parent cc6b174 commit eaaaab8

2 files changed

Lines changed: 57 additions & 10 deletions

File tree

src/deepforest/utilities.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -439,18 +439,24 @@ def read_coco(json_file):
439439
filenames = []
440440
labels = []
441441

442+
# Format reference: https://cocodataset.org/#format-data
442443
for annotation in coco_data["annotations"]:
443444
segmentation = annotation.get("segmentation")
444445
if not segmentation:
445-
continue
446-
# COCO polygons are usually a list of lists; take the first (assume "single part")
447-
segmentation_mask = segmentation[0]
448-
# Convert flat list to coordinate pairs
449-
pairs = [
450-
(segmentation_mask[i], segmentation_mask[i + 1])
451-
for i in range(0, len(segmentation_mask), 2)
452-
]
453-
polygon = shapely.geometry.Polygon(pairs)
446+
# COCO bbox format is [x, y, width, height]
447+
x, y, width, height = annotation["bbox"]
448+
# Shapely box format is [minx, miny, maxx, maxy]
449+
polygon = shapely.box(x, y, x + width, y + height)
450+
else:
451+
# COCO polygons are usually a list of lists; take the first (assume "single part")
452+
segmentation_mask = segmentation[0]
453+
# Convert flat list to coordinate pairs
454+
pairs = [
455+
(segmentation_mask[i], segmentation_mask[i + 1])
456+
for i in range(0, len(segmentation_mask), 2)
457+
]
458+
polygon = shapely.geometry.Polygon(pairs)
459+
454460
filenames.append(image_ids[annotation["image_id"]])
455461
polygons.append(polygon.wkt)
456462
cat_id = annotation.get("category_id")

tests/test_utilities.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ def test_image_to_geo_coordinates_polygons():
558558

559559

560560

561-
def test_read_coco_json(tmp_path):
561+
def test_read_coco_json_polygon(tmp_path):
562562
"""Test reading a COCO format JSON file"""
563563
# Create a sample COCO JSON structure
564564
coco_data = {
@@ -608,6 +608,47 @@ def test_read_coco_json(tmp_path):
608608
assert geom.is_valid
609609
assert isinstance(geom, shapely.geometry.Polygon)
610610

611+
def test_read_coco_json_bbox(tmp_path):
612+
"""Test reading a COCO format JSON file"""
613+
# Create a sample COCO JSON structure
614+
coco_data = {
615+
"images": [
616+
{"id": 1, "file_name": "OSBS_029.png"},
617+
{"id": 2, "file_name": "OSBS_029.tif"}
618+
],
619+
"categories": [
620+
{"id": 0, "name": "Tree"},
621+
{"id": 1, "name": "Bird"}
622+
],
623+
"annotations": [
624+
{
625+
"image_id": 1,
626+
"bbox": [0, 0, 10, 10], # x, y, width, height
627+
"category_id": 0
628+
},
629+
{
630+
"image_id": 2,
631+
"bbox": [5, 5, 10, 10],
632+
"category_id": 1
633+
}
634+
]
635+
}
636+
637+
# Write the sample JSON to a temporary file
638+
json_path = tmp_path / "annotations.json"
639+
with open(json_path, "w") as f:
640+
json.dump(coco_data, f)
641+
642+
# Read the file using our utility
643+
df = utilities.read_file(str(json_path), root_dir=os.path.dirname(get_data("OSBS_029.png")))
644+
645+
# Assert the dataframe has the expected structure
646+
assert df.shape[0] == 2 # Two annotations
647+
648+
# Check bboxes:
649+
expected_boxes = [geometry.box(0, 0, 10, 10), geometry.box(5, 5, 15, 15)]
650+
for geom, expected in zip(df.geometry, expected_boxes):
651+
assert geom.equals(expected)
611652

612653
def test_format_geometry_box():
613654
"""Test formatting box geometry from model predictions"""

0 commit comments

Comments
 (0)