|
4 | 4 | from typing import TYPE_CHECKING, Any |
5 | 5 |
|
6 | 6 | import cv2 |
| 7 | +import numpy as np |
7 | 8 | import polars as pl |
| 9 | +from loguru import logger |
8 | 10 | from pycocotools import mask |
9 | 11 |
|
10 | 12 | if TYPE_CHECKING: |
@@ -99,6 +101,29 @@ def check_group_file_correspondence(prepared_ldf: PreparedLDF) -> None: |
99 | 101 | f"To export multiple files (e.g. RGB, depth) per instance, export to Native format" |
100 | 102 | ) |
101 | 103 |
|
| 104 | + @staticmethod |
| 105 | + def exporter_specific_annotation_warning( |
| 106 | + prepared_ldf: PreparedLDF, supported_ann_types: list[str] |
| 107 | + ) -> None: |
| 108 | + df = prepared_ldf.processed_df |
| 109 | + |
| 110 | + present_task_types = ( |
| 111 | + df.select(pl.col("task_type").drop_nans().drop_nulls().unique()) |
| 112 | + .to_series() |
| 113 | + .to_list() |
| 114 | + ) |
| 115 | + |
| 116 | + unsupported = [ |
| 117 | + name |
| 118 | + for name in present_task_types |
| 119 | + if name not in supported_ann_types |
| 120 | + ] |
| 121 | + |
| 122 | + for name in unsupported: |
| 123 | + logger.warning( |
| 124 | + f"Found unsupported annotation type '{name}'; skipping this annotation type." |
| 125 | + ) |
| 126 | + |
102 | 127 | @staticmethod |
103 | 128 | def split_of_group(prepared_ldf: PreparedLDF, group_id: Any) -> str: |
104 | 129 | split = next( |
@@ -137,7 +162,7 @@ def create_zip_output( |
137 | 162 |
|
138 | 163 | @staticmethod |
139 | 164 | def get_single_skeleton( |
140 | | - allow_keypoints: bool, skeletons: dict |
| 165 | + allow_keypoints: bool, skeletons: dict[str, Any] | None = None |
141 | 166 | ) -> tuple[list[str], list[list[int]]]: |
142 | 167 | """Returns (labels, skeleton_edges_1_based) for the single |
143 | 168 | skeleton. |
@@ -178,6 +203,45 @@ def rle_to_yolo_polygon(rle: str, height: int, width: int) -> list: |
178 | 203 |
|
179 | 204 | return polygons |
180 | 205 |
|
| 206 | + @staticmethod |
| 207 | + def _bbox_from_poly( |
| 208 | + coords: list[float], |
| 209 | + ) -> tuple[float, float, float, float]: |
| 210 | + xs = coords[0::2] |
| 211 | + ys = coords[1::2] |
| 212 | + x_min, x_max = min(xs), max(xs) |
| 213 | + y_min, y_max = min(ys), max(ys) |
| 214 | + return x_min, y_min, (x_max - x_min), (y_max - y_min) |
| 215 | + |
| 216 | + @staticmethod |
| 217 | + def _iou_xywh( |
| 218 | + a: tuple[float, float, float, float], |
| 219 | + b: tuple[float, float, float, float], |
| 220 | + ) -> float: |
| 221 | + ax, ay, aw, ah = a |
| 222 | + bx, by, bw, bh = b |
| 223 | + ax2, ay2 = ax + aw, ay + ah |
| 224 | + bx2, by2 = bx + bw, by + bh |
| 225 | + inter_w = max(0.0, min(ax2, bx2) - max(ax, bx)) |
| 226 | + inter_h = max(0.0, min(ay2, by2) - max(ay, by)) |
| 227 | + inter = inter_w * inter_h |
| 228 | + if inter <= 0.0: |
| 229 | + return 0.0 |
| 230 | + union = aw * ah + bw * bh - inter |
| 231 | + return inter / union if union > 0.0 else 0.0 |
| 232 | + |
| 233 | + @staticmethod |
| 234 | + def decode_rle_with_pycoco(ann: dict[str, Any]) -> np.ndarray: |
| 235 | + h = int(ann["height"]) |
| 236 | + w = int(ann["width"]) |
| 237 | + counts = ann["counts"] |
| 238 | + |
| 239 | + # pycocotools expects an RLE object with 'size' and 'counts' |
| 240 | + rle = {"size": [h, w], "counts": counts.encode("utf-8")} |
| 241 | + |
| 242 | + m = mask.decode(rle) # type: ignore[arg-type] |
| 243 | + return np.array(m, dtype=np.uint8, order="C") |
| 244 | + |
181 | 245 | def _normalize( |
182 | 246 | self, xs: list[float], ys: list[float], w: float, h: float |
183 | 247 | ) -> list[float]: |
|
0 commit comments