audit YOLO geometry and overlapping validation rows
This commit is contained in:
@@ -287,6 +287,9 @@ def draw_tile_card(
|
||||
"exact_duplicate": ((0, 220, 255), 5),
|
||||
"near_duplicate": ((255, 55, 55), 5),
|
||||
"possible_nested": ((255, 0, 220), 5),
|
||||
"extreme_aspect_ratio": ((255, 128, 0), 5),
|
||||
"small_dimension": ((80, 160, 255), 5),
|
||||
"tile_edge": ((57, 255, 20), 5),
|
||||
}
|
||||
for box_index, box in enumerate(boxes):
|
||||
x_center = box["center_x"] * thumb_size
|
||||
@@ -317,14 +320,17 @@ def draw_tile_card(
|
||||
return card
|
||||
|
||||
|
||||
def build_relationship_highlights(
|
||||
def build_label_highlights(
|
||||
summary: dict[str, Any],
|
||||
) -> dict[str, dict[int, str]]:
|
||||
"""Map audited label rows to their highest-priority visual warning."""
|
||||
priorities = {
|
||||
"possible_nested": 1,
|
||||
"near_duplicate": 2,
|
||||
"exact_duplicate": 3,
|
||||
"tile_edge": 1,
|
||||
"small_dimension": 2,
|
||||
"extreme_aspect_ratio": 3,
|
||||
"possible_nested": 4,
|
||||
"near_duplicate": 5,
|
||||
"exact_duplicate": 6,
|
||||
}
|
||||
highlights: dict[str, dict[int, str]] = {}
|
||||
flagged_tiles = summary.get("flagged_tiles") or []
|
||||
@@ -355,9 +361,30 @@ def build_relationship_highlights(
|
||||
):
|
||||
tile_highlights[index] = relationship_type
|
||||
|
||||
outliers = tile.get("outliers") or []
|
||||
if not isinstance(outliers, list):
|
||||
continue
|
||||
for outlier in outliers:
|
||||
if not isinstance(outlier, dict):
|
||||
continue
|
||||
category = str(outlier.get("category") or "")
|
||||
index = outlier.get("index")
|
||||
if category not in priorities or not isinstance(index, int) or index < 0:
|
||||
continue
|
||||
existing = tile_highlights.get(index)
|
||||
if existing is None or priorities[category] > priorities[existing]:
|
||||
tile_highlights[index] = category
|
||||
|
||||
return highlights
|
||||
|
||||
|
||||
def build_relationship_highlights(
|
||||
summary: dict[str, Any],
|
||||
) -> dict[str, dict[int, str]]:
|
||||
"""Backward-compatible name for callers using relationship manifests."""
|
||||
return build_label_highlights(summary)
|
||||
|
||||
|
||||
def image_has_low_visual_variance(image_path: Path, blank_range_threshold: int) -> bool:
|
||||
image = Image.open(image_path).convert("L")
|
||||
min_value, max_value = image.getextrema()
|
||||
@@ -406,8 +433,8 @@ def build_report(
|
||||
invalid_label_count = 0
|
||||
valid_label_count = 0
|
||||
low_visual_variance_tile_count = 0
|
||||
relationship_highlight_count = 0
|
||||
relationship_highlights = build_relationship_highlights(summary)
|
||||
highlight_category_counts: dict[str, int] = {}
|
||||
label_highlights = build_label_highlights(summary)
|
||||
|
||||
for tile in selected_tiles:
|
||||
image_path = resolve_path(tile.get("image_path"), summary_path)
|
||||
@@ -415,8 +442,11 @@ def build_report(
|
||||
boxes, tile_invalid_count, missing_label_file = parse_yolo_label_file(
|
||||
label_path
|
||||
)
|
||||
tile_relationship_highlights = relationship_highlights.get(str(label_path), {})
|
||||
relationship_highlight_count += len(tile_relationship_highlights)
|
||||
tile_label_highlights = label_highlights.get(str(label_path), {})
|
||||
for category in tile_label_highlights.values():
|
||||
highlight_category_counts[category] = (
|
||||
highlight_category_counts.get(category, 0) + 1
|
||||
)
|
||||
invalid_label_count += tile_invalid_count
|
||||
valid_label_count += len(boxes)
|
||||
if missing_label_file:
|
||||
@@ -441,7 +471,7 @@ def build_report(
|
||||
invalid_label_count=tile_invalid_count,
|
||||
missing_label_file=missing_label_file,
|
||||
low_visual_variance=low_visual_variance,
|
||||
relationship_highlights=tile_relationship_highlights,
|
||||
relationship_highlights=tile_label_highlights,
|
||||
)
|
||||
)
|
||||
rendered = True
|
||||
@@ -460,7 +490,7 @@ def build_report(
|
||||
"invalid_label_count": tile_invalid_count,
|
||||
"missing_label_file": missing_label_file,
|
||||
"low_visual_variance": low_visual_variance,
|
||||
"relationship_highlight_count": len(tile_relationship_highlights),
|
||||
"highlighted_label_count": len(tile_label_highlights),
|
||||
"rendered": rendered,
|
||||
}
|
||||
)
|
||||
@@ -478,6 +508,14 @@ def build_report(
|
||||
}
|
||||
for start in range(0, len(rendered_cards), args.tiles_per_sheet)
|
||||
]
|
||||
relationship_highlight_count = sum(
|
||||
highlight_category_counts.get(category, 0)
|
||||
for category in ("exact_duplicate", "near_duplicate", "possible_nested")
|
||||
)
|
||||
outlier_highlight_count = sum(
|
||||
highlight_category_counts.get(category, 0)
|
||||
for category in ("extreme_aspect_ratio", "small_dimension", "tile_edge")
|
||||
)
|
||||
|
||||
return (
|
||||
{
|
||||
@@ -503,7 +541,12 @@ def build_report(
|
||||
"invalid_label_count": invalid_label_count,
|
||||
"valid_label_count": valid_label_count,
|
||||
"low_visual_variance_tile_count": low_visual_variance_tile_count,
|
||||
"highlighted_label_count": sum(highlight_category_counts.values()),
|
||||
"relationship_highlight_count": relationship_highlight_count,
|
||||
"outlier_highlight_count": outlier_highlight_count,
|
||||
"highlight_category_counts": dict(
|
||||
sorted(highlight_category_counts.items())
|
||||
),
|
||||
"blank_range_threshold": args.blank_range_threshold,
|
||||
"contact_sheets": contact_sheets,
|
||||
"selected_tiles": selected_report_tiles,
|
||||
@@ -525,7 +568,8 @@ def write_markdown(report: dict[str, Any], output_dir: Path) -> None:
|
||||
f"- invalid label rows: {report['invalid_label_count']}",
|
||||
f"- low-variance rendered tiles: {report['low_visual_variance_tile_count']}",
|
||||
f"- valid labels rendered: {report['valid_label_count']}",
|
||||
f"- relationship-highlighted labels: {report['relationship_highlight_count']}",
|
||||
f"- highlighted labels: {report['highlighted_label_count']}",
|
||||
f"- highlight categories: `{report['highlight_category_counts']}`",
|
||||
"",
|
||||
"## Contact Sheets",
|
||||
"",
|
||||
|
||||
Reference in New Issue
Block a user