Skip to content
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

1114 rle support for coco #1163

Merged
merged 60 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
e30d4a9
add test for generating annotation with mask
emSko May 2, 2024
cc5e72d
test for RLE format
emSko May 2, 2024
7f114cb
RLE decoding
emSko May 2, 2024
6913ecf
2 annotations with segmentation, one polygon one RLE
emSko May 2, 2024
4a068ff
binary mask to RL encoding
emSko May 3, 2024
dc1ce02
move rle encode decode functions to dataset/utils.py
emSko May 3, 2024
869204d
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 5, 2024
18a790e
typing chanches and doc strings for rle_to_mask and mask_to_rle funct…
emSko May 6, 2024
3ade7f0
fix order caused error with mask generation in coco_annotations_to_de…
emSko May 6, 2024
2727152
merge with upstream
emSko May 6, 2024
a941583
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 6, 2024
88a43cc
unit tests for rle_to_mask and mask_to_rle functions
emSko May 6, 2024
6b5dacd
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 6, 2024
a9f821c
Assertion error when number of pixel in RLE does not match the nuber …
emSko May 7, 2024
3618ac3
merge
emSko May 7, 2024
d59ec0f
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 7, 2024
aac1d88
assertion for valid mask in mask_to_rle function
emSko May 7, 2024
36a301e
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 7, 2024
649e273
fix the line lengths
emSko May 8, 2024
008711a
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 8, 2024
e4e66f8
faster mask to rle
emSko May 10, 2024
39477fe
Merge branch '1114_RLE_support_for_COCO' of github.com:emSko/supervis…
emSko May 10, 2024
7bd92b0
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 10, 2024
8c857dd
speed up rle to mask
emSko May 12, 2024
d292cbf
Merge branch '1114_RLE_support_for_COCO' of github.com:emSko/supervis…
emSko May 12, 2024
14b5734
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 12, 2024
d21c98a
docs updated; `rle_to_mask` and `mask_to_rle` added to `__init__.py`
SkalskiP May 13, 2024
9a1c112
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 13, 2024
59b273b
small refactor
SkalskiP May 13, 2024
cccacab
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 13, 2024
aab861c
test_coco_annotations_to_detections result matrices defined in place
emSko May 15, 2024
f08404e
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 15, 2024
d8679d9
automatic RLE for masks with holes or in multiple pieces
emSko May 16, 2024
eefa05c
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 16, 2024
1e8ee47
craete copies of mask in _has_holes and _mask_has_multiple_segments
emSko May 16, 2024
ce05a0c
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 16, 2024
3ee72e3
check for empty mask
emSko May 16, 2024
511f0ab
check for empty mask
emSko May 16, 2024
b3b7455
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 16, 2024
879ae94
test polygon mask when mask in single component and no holes
emSko May 17, 2024
ab775d9
Attempt to fix SegFault
LinasKo May 17, 2024
aad5e2a
merge seg fault fix
emSko May 17, 2024
0cd2c9d
documentation change for as_coco
emSko May 17, 2024
8d432c7
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 17, 2024
a40bf78
coco mock method refactoring
emSko May 20, 2024
2626263
move has_holes and mask_has_multiple_segments to detections utils
emSko May 20, 2024
179d00b
merge
emSko May 20, 2024
7ee84c4
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 20, 2024
68f07e1
tests for mask_has_holes
emSko May 20, 2024
f0fc76c
Merge branch '1114_RLE_support_for_COCO' of github.com:emSko/supervis…
emSko May 20, 2024
c26eb1a
unit tests for test_mask_has_multiple_segments
emSko May 20, 2024
dfac30c
change docu for mask_has_multiple_segments and mask_has_holes and add…
emSko May 20, 2024
7b42d83
Merge branch 'develop' into 1114_RLE_support_for_COCO
emSko May 20, 2024
ab55e81
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 20, 2024
6dbf3f6
fix for unit tests for test_mask_has_holes
emSko May 20, 2024
f271625
small refactor + docs improvements
SkalskiP May 21, 2024
996b0a3
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 21, 2024
1d43042
small refactor + docs improvements
SkalskiP May 21, 2024
c3855c9
small refactor + docs improvements
SkalskiP May 21, 2024
5840889
fix(pre_commit): 🎨 auto format pre-commit hooks
pre-commit-ci[bot] May 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/datasets.md → docs/datasets/core.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
comments: true
status: new
---

# Datasets
Expand Down
18 changes: 18 additions & 0 deletions docs/datasets/utils.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
comments: true
status: new
---

# Datasets Utils

<div class="md-typeset">
<h2><a href="#supervision.dataset.utils.rle_to_mask">rle_to_mask</a></h2>
</div>

:::supervision.dataset.utils.rle_to_mask

<div class="md-typeset">
<h2><a href="#supervision.dataset.utils.mask_to_rle">mask_to_rle</a></h2>
</div>

:::supervision.dataset.utils.mask_to_rle
12 changes: 12 additions & 0 deletions docs/detection/utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,15 @@ status: new
</div>

:::supervision.detection.utils.pad_boxes

<div class="md-typeset">
<h2><a href="#supervision.detection.utils.contains_holes">contains_holes</a></h2>
</div>

:::supervision.detection.utils.contains_holes

<div class="md-typeset">
<h2><a href="#supervision.detection.utils.contains_multiple_segments">contains_multiple_segments</a></h2>
</div>

:::supervision.detection.utils.contains_multiple_segments
4 changes: 3 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ nav:
- Detection Smoother: detection/tools/smoother.md
- Save Detections: detection/tools/save_detections.md
- Trackers: trackers.md
- Datasets: datasets.md
- Datasets:
- Core: datasets/core.md
- Utils: datasets/utils.md
- Utils:
- Video: utils/video.md
- Image: utils/image.md
Expand Down
3 changes: 3 additions & 0 deletions supervision/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
ClassificationDataset,
DetectionDataset,
)
from supervision.dataset.utils import mask_to_rle, rle_to_mask
from supervision.detection.annotate import BoxAnnotator
from supervision.detection.core import Detections
from supervision.detection.line_zone import LineZone, LineZoneAnnotator
Expand All @@ -48,6 +49,8 @@
box_non_max_suppression,
calculate_masks_centroids,
clip_boxes,
contains_holes,
contains_multiple_segments,
filter_polygons_by_area,
mask_iou_batch,
mask_non_max_suppression,
Expand Down
34 changes: 23 additions & 11 deletions supervision/dataset/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,12 @@ def split(
Tuple[DetectionDataset, DetectionDataset]: A tuple containing
the training and testing datasets.

Example:
Examples:
```python
import supervision as sv

ds = sv.DetectionDataset(...)
train_ds, test_ds = ds.split(split_ratio=0.7,
random_state=42, shuffle=True)
train_ds, test_ds = ds.split(split_ratio=0.7, random_state=42, shuffle=True)
len(train_ds), len(test_ds)
# (700, 300)
```
Expand Down Expand Up @@ -229,7 +228,7 @@ def from_pascal_voc(
DetectionDataset: A DetectionDataset instance containing
the loaded images and annotations.

Example:
Examples:
```python
import roboflow
from roboflow import Roboflow
Expand Down Expand Up @@ -286,7 +285,7 @@ def from_yolo(
DetectionDataset: A DetectionDataset instance
containing the loaded images and annotations.

Example:
Examples:
```python
import roboflow
from roboflow import Roboflow
Expand Down Expand Up @@ -391,7 +390,7 @@ def from_coco(
DetectionDataset: A DetectionDataset instance containing
the loaded images and annotations.

Example:
Examples:
```python
import roboflow
from roboflow import Roboflow
Expand Down Expand Up @@ -431,6 +430,20 @@ def as_coco(
Exports the dataset to COCO format. This method saves the
images and their corresponding annotations in COCO format.

!!! tip

The format of the mask is determined automatically based on its structure:

- If a mask contains multiple disconnected components or holes, it will be
saved using the Run-Length Encoding (RLE) format for efficient storage and
processing.
- If a mask consists of a single, contiguous region without any holes, it
will be encoded as a polygon, preserving the outline of the object.

This automatic selection ensures that the masks are stored in the most
appropriate and space-efficient format, complying with COCO dataset
standards.

Args:
images_directory_path (Optional[str]): The path to the directory
where the images should be saved.
Expand Down Expand Up @@ -482,7 +495,7 @@ def merge(cls, dataset_list: List[DetectionDataset]) -> DetectionDataset:
(DetectionDataset): A single `DetectionDataset` object containing
the merged data from the input list.

Example:
Examples:
```python
import supervision as sv

Expand Down Expand Up @@ -567,13 +580,12 @@ def split(
Tuple[ClassificationDataset, ClassificationDataset]: A tuple containing
the training and testing datasets.

Example:
Examples:
```python
import supervision as sv

cd = sv.ClassificationDataset(...)
train_cd,test_cd = cd.split(split_ratio=0.7,
random_state=42,shuffle=True)
train_cd,test_cd = cd.split(split_ratio=0.7, random_state=42,shuffle=True)
len(train_cd), len(test_cd)
# (700, 300)
```
Expand Down Expand Up @@ -635,7 +647,7 @@ def from_folder_structure(cls, root_directory_path: str) -> ClassificationDatase
Returns:
ClassificationDataset: The dataset.

Example:
Examples:
```python
import roboflow
from roboflow import Roboflow
Expand Down
73 changes: 49 additions & 24 deletions supervision/dataset/formats/coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@

import cv2
import numpy as np
import numpy.typing as npt

from supervision.dataset.utils import (
approximate_mask_with_polygons,
map_detections_class_id,
mask_to_rle,
rle_to_mask,
)
from supervision.detection.core import Detections
from supervision.detection.utils import polygon_to_mask
from supervision.detection.utils import (
contains_holes,
contains_multiple_segments,
polygon_to_mask,
)
from supervision.utils.file import read_json_file, save_json_file


Expand Down Expand Up @@ -57,13 +64,24 @@ def group_coco_annotations_by_image_id(
return annotations


def _polygons_to_masks(
polygons: List[np.ndarray], resolution_wh: Tuple[int, int]
) -> np.ndarray:
def coco_annotations_to_masks(
image_annotations: List[dict], resolution_wh: Tuple[int, int]
) -> npt.NDArray[np.bool_]:
return np.array(
[
polygon_to_mask(polygon=polygon, resolution_wh=resolution_wh)
for polygon in polygons
rle_to_mask(
rle=np.array(image_annotation["segmentation"]["counts"]),
resolution_wh=resolution_wh,
)
if image_annotation["iscrowd"]
else polygon_to_mask(
polygon=np.reshape(
np.asarray(image_annotation["segmentation"], dtype=np.int32),
(-1, 2),
),
resolution_wh=resolution_wh,
)
for image_annotation in image_annotations
],
dtype=bool,
)
Expand All @@ -83,13 +101,9 @@ def coco_annotations_to_detections(
xyxy[:, 2:4] += xyxy[:, 0:2]

if with_masks:
polygons = [
np.reshape(
np.asarray(image_annotation["segmentation"], dtype=np.int32), (-1, 2)
)
for image_annotation in image_annotations
]
mask = _polygons_to_masks(polygons=polygons, resolution_wh=resolution_wh)
mask = coco_annotations_to_masks(
image_annotations=image_annotations, resolution_wh=resolution_wh
)
return Detections(
class_id=np.asarray(class_ids, dtype=int), xyxy=xyxy, mask=mask
)
Expand All @@ -108,24 +122,35 @@ def detections_to_coco_annotations(
coco_annotations = []
for xyxy, mask, _, class_id, _, _ in detections:
box_width, box_height = xyxy[2] - xyxy[0], xyxy[3] - xyxy[1]
polygon = []
segmentation = []
iscrowd = 0
if mask is not None:
polygon = list(
approximate_mask_with_polygons(
mask=mask,
min_image_area_percentage=min_image_area_percentage,
max_image_area_percentage=max_image_area_percentage,
approximation_percentage=approximation_percentage,
)[0].flatten()
)
iscrowd = contains_holes(mask=mask) or contains_multiple_segments(mask=mask)

if iscrowd:
segmentation = {
"counts": mask_to_rle(mask=mask),
"size": list(mask.shape[:2]),
}
else:
segmentation = [
list(
approximate_mask_with_polygons(
mask=mask,
min_image_area_percentage=min_image_area_percentage,
max_image_area_percentage=max_image_area_percentage,
approximation_percentage=approximation_percentage,
)[0].flatten()
)
]
coco_annotation = {
"id": annotation_id,
"image_id": image_id,
"category_id": int(class_id),
"bbox": [xyxy[0], xyxy[1], box_width, box_height],
"area": box_width * box_height,
"segmentation": [polygon] if polygon else [],
"iscrowd": 0,
"segmentation": segmentation,
"iscrowd": iscrowd,
}
coco_annotations.append(coco_annotation)
annotation_id += 1
Expand Down