Balance YOLO label QA coverage
This commit is contained in:
@@ -120,6 +120,33 @@ def tile_sort_key(tile: dict[str, Any]) -> tuple[int, str, str, int, int]:
|
||||
)
|
||||
|
||||
|
||||
def balanced_tiles_by_sample(tiles: list[dict[str, Any]], limit: int) -> list[dict[str, Any]]:
|
||||
if limit <= 0 or not tiles:
|
||||
return []
|
||||
grouped: dict[str, list[dict[str, Any]]] = {}
|
||||
for tile in tiles:
|
||||
grouped.setdefault(str(tile.get("sample_slug") or "unknown"), []).append(tile)
|
||||
for sample_tiles in grouped.values():
|
||||
sample_tiles.sort(key=tile_sort_key)
|
||||
sample_order = sorted(grouped, key=lambda slug: tile_sort_key(grouped[slug][0]))
|
||||
|
||||
selected: list[dict[str, Any]] = []
|
||||
depth = 0
|
||||
while len(selected) < limit:
|
||||
added = False
|
||||
for slug in sample_order:
|
||||
sample_tiles = grouped[slug]
|
||||
if depth < len(sample_tiles):
|
||||
selected.append(sample_tiles[depth])
|
||||
added = True
|
||||
if len(selected) == limit:
|
||||
break
|
||||
if not added:
|
||||
break
|
||||
depth += 1
|
||||
return selected
|
||||
|
||||
|
||||
def select_tiles(tiles: list[dict[str, Any]], max_tiles: int) -> list[dict[str, Any]]:
|
||||
if max_tiles <= 0:
|
||||
raise ValueError("max_tiles must be positive")
|
||||
@@ -135,7 +162,8 @@ def select_tiles(tiles: list[dict[str, Any]], max_tiles: int) -> list[dict[str,
|
||||
)
|
||||
|
||||
negative_slots = min(len(negatives), max(1, max_tiles // 5)) if negatives and max_tiles > 1 else 0
|
||||
selected = positives[: max_tiles - negative_slots] + negatives[:negative_slots]
|
||||
selected = balanced_tiles_by_sample(positives, max_tiles - negative_slots)
|
||||
selected.extend(balanced_tiles_by_sample(negatives, negative_slots))
|
||||
|
||||
if len(selected) < max_tiles:
|
||||
selected_ids = {id(tile) for tile in selected}
|
||||
@@ -302,6 +330,12 @@ def build_report(summary: dict[str, Any], summary_path: Path, args: argparse.Nam
|
||||
"columns": args.columns,
|
||||
"thumb_size": args.thumb_size,
|
||||
"selected_tile_count": len(selected_tiles),
|
||||
"selected_sample_count": len(
|
||||
{str(tile.get("sample_slug") or "unknown") for tile in selected_tiles}
|
||||
),
|
||||
"selected_sample_slugs": sorted(
|
||||
{str(tile.get("sample_slug") or "unknown") for tile in selected_tiles}
|
||||
),
|
||||
"rendered_tile_count": len(rendered_cards),
|
||||
"missing_image_count": missing_image_count,
|
||||
"missing_label_file_count": missing_label_file_count,
|
||||
@@ -322,6 +356,7 @@ def write_markdown(report: dict[str, Any], output_dir: Path) -> None:
|
||||
"",
|
||||
f"- status: `{report['status']}`",
|
||||
f"- selected tiles: {report['selected_tile_count']}",
|
||||
f"- selected source samples: {report['selected_sample_count']}",
|
||||
f"- rendered tiles: {report['rendered_tile_count']}",
|
||||
f"- missing images: {report['missing_image_count']}",
|
||||
f"- missing label files: {report['missing_label_file_count']}",
|
||||
|
||||
Reference in New Issue
Block a user