Report blank YOLO label QA tiles
This commit is contained in:
@@ -31,6 +31,12 @@ def parse_args() -> argparse.Namespace:
|
||||
parser.add_argument("--max-tiles", type=int, default=24, help="Maximum selected tiles to render")
|
||||
parser.add_argument("--columns", type=int, default=4, help="Contact-sheet columns")
|
||||
parser.add_argument("--thumb-size", type=int, default=256, help="Rendered tile thumbnail size in pixels")
|
||||
parser.add_argument(
|
||||
"--blank-range-threshold",
|
||||
type=int,
|
||||
default=3,
|
||||
help="Mark rendered images with grayscale max-min range at or below this value as low variance.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
@@ -146,6 +152,7 @@ def draw_tile_card(
|
||||
thumb_size: int,
|
||||
invalid_label_count: int,
|
||||
missing_label_file: bool,
|
||||
low_visual_variance: bool,
|
||||
) -> Image.Image:
|
||||
header_height = 44
|
||||
card = Image.new("RGB", (thumb_size, thumb_size + header_height), color=(245, 247, 250))
|
||||
@@ -163,6 +170,8 @@ def draw_tile_card(
|
||||
subtitle_parts.append("missing-label-file")
|
||||
if invalid_label_count:
|
||||
subtitle_parts.append(f"invalid:{invalid_label_count}")
|
||||
if low_visual_variance:
|
||||
subtitle_parts.append("low-variance")
|
||||
draw.text((6, 6), title[:44], fill=(255, 255, 255), font=font)
|
||||
draw.text((6, 24), " | ".join(subtitle_parts)[:52], fill=(191, 219, 254), font=font)
|
||||
|
||||
@@ -180,6 +189,12 @@ def draw_tile_card(
|
||||
return card
|
||||
|
||||
|
||||
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()
|
||||
return (max_value - min_value) <= blank_range_threshold
|
||||
|
||||
|
||||
def build_contact_sheet(cards: list[Image.Image], columns: int, output_path: Path) -> None:
|
||||
if not cards:
|
||||
return
|
||||
@@ -216,6 +231,7 @@ def build_report(summary: dict[str, Any], summary_path: Path, args: argparse.Nam
|
||||
missing_label_file_count = 0
|
||||
invalid_label_count = 0
|
||||
valid_label_count = 0
|
||||
low_visual_variance_tile_count = 0
|
||||
|
||||
for tile in selected_tiles:
|
||||
image_path = resolve_path(tile.get("image_path"), summary_path)
|
||||
@@ -227,9 +243,13 @@ def build_report(summary: dict[str, Any], summary_path: Path, args: argparse.Nam
|
||||
missing_label_file_count += 1
|
||||
|
||||
rendered = False
|
||||
low_visual_variance = False
|
||||
if image_path is None or not image_path.exists():
|
||||
missing_image_count += 1
|
||||
else:
|
||||
low_visual_variance = image_has_low_visual_variance(image_path, args.blank_range_threshold)
|
||||
if low_visual_variance:
|
||||
low_visual_variance_tile_count += 1
|
||||
rendered_cards.append(
|
||||
draw_tile_card(
|
||||
image_path=image_path,
|
||||
@@ -238,6 +258,7 @@ def build_report(summary: dict[str, Any], summary_path: Path, args: argparse.Nam
|
||||
thumb_size=args.thumb_size,
|
||||
invalid_label_count=tile_invalid_count,
|
||||
missing_label_file=missing_label_file,
|
||||
low_visual_variance=low_visual_variance,
|
||||
)
|
||||
)
|
||||
rendered = True
|
||||
@@ -255,6 +276,7 @@ def build_report(summary: dict[str, Any], summary_path: Path, args: argparse.Nam
|
||||
"valid_label_count": len(boxes),
|
||||
"invalid_label_count": tile_invalid_count,
|
||||
"missing_label_file": missing_label_file,
|
||||
"low_visual_variance": low_visual_variance,
|
||||
"rendered": rendered,
|
||||
}
|
||||
)
|
||||
@@ -285,6 +307,8 @@ def build_report(summary: dict[str, Any], summary_path: Path, args: argparse.Nam
|
||||
"missing_label_file_count": missing_label_file_count,
|
||||
"invalid_label_count": invalid_label_count,
|
||||
"valid_label_count": valid_label_count,
|
||||
"low_visual_variance_tile_count": low_visual_variance_tile_count,
|
||||
"blank_range_threshold": args.blank_range_threshold,
|
||||
"contact_sheets": contact_sheets,
|
||||
"selected_tiles": selected_report_tiles,
|
||||
},
|
||||
@@ -302,6 +326,7 @@ def write_markdown(report: dict[str, Any], output_dir: Path) -> None:
|
||||
f"- missing images: {report['missing_image_count']}",
|
||||
f"- missing label files: {report['missing_label_file_count']}",
|
||||
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']}",
|
||||
"",
|
||||
"## Contact Sheets",
|
||||
@@ -323,6 +348,8 @@ def write_markdown(report: dict[str, Any], output_dir: Path) -> None:
|
||||
flags.append("missing-label-file")
|
||||
if tile["invalid_label_count"]:
|
||||
flags.append(f"invalid:{tile['invalid_label_count']}")
|
||||
if tile["low_visual_variance"]:
|
||||
flags.append("low-variance")
|
||||
flag_text = ", ".join(flags) if flags else "ok"
|
||||
lines.append(
|
||||
"- "
|
||||
|
||||
Reference in New Issue
Block a user