highlight audited YOLO label relationships
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Jens
2026-08-09 19:12:04 +02:00
parent 72b2cdaae4
commit fc18e72c7f
7 changed files with 171 additions and 5 deletions
@@ -1,7 +1,7 @@
{
"schema_version": 1,
"ledger_id": "reviewedexp6-ai-assisted-review-r2-full-corpus",
"reviewed_at": "2026-08-09T15:00:00+02:00",
"reviewed_at": "2026-08-09T19:10:57+02:00",
"reviewer": {
"id": "openai-codex",
"type": "ai_assistant",
@@ -78,12 +78,23 @@
"audit_sha256": "2a219f650376e77a927c1ca37f801ac9fe8c2b8406cab6cd149be07e0557af73",
"focused_contact_sheet_path": "/app/storage/operator-data/model-review/reviewedexp6-corpus/nested-pairs-review-r1/contact_sheet_001.png",
"focused_contact_sheet_sha256": "7c27fc66bb1978296efc53d3e9f78ae6aa27816d5893c77f44d8803e57a40ee5",
"relationship_highlight_summary_path": "/app/storage/operator-data/model-review/reviewedexp6-corpus/nested-pairs-review-r2/operator_yolo_label_qa_summary.json",
"relationship_highlight_summary_sha256": "e4e6edc5c7223334c4feca9e242ac6d3c345b633a476aa9356b44133dcffe619",
"relationship_highlight_contact_sheet_path": "/app/storage/operator-data/model-review/reviewedexp6-corpus/nested-pairs-review-r2/contact_sheet_001.png",
"relationship_highlight_contact_sheet_sha256": "2a6bdd542b8ac49bcd5ac4d9c84999982461463ddd3c2fe0dc7332ceeb7a5e60",
"exact_duplicate_pairs": 0,
"near_duplicate_pairs_iou_at_least_0_90": 0,
"possible_nested_pairs_containment_at_least_0_98": 43,
"flagged_tile_count": 36,
"highlighted_unique_label_rows": 86,
"highlight_legend": {
"exact_duplicate": "cyan",
"near_duplicate": "red",
"possible_nested": "magenta",
"unflagged": "yellow"
},
"share_of_rendered_labels": 0.000543,
"ai_visual_followup": "no systematic duplicate-label pattern observed; relationships mostly correspond to adjacent or complex building components",
"ai_visual_followup": "all 86 implicated label rows were index-highlighted; no cyan exact duplicates or red near duplicates exist, while the 43 magenta nesting pairs are dispersed across dense reference AOIs and predominantly show larger GRB envelopes around smaller building components rather than one systematic duplicate-label pattern",
"action": "retain for experimental analysis, require human adjudication before release, and perform no automatic rewrite or exclusion"
}
}
+30
View File
@@ -12565,3 +12565,33 @@ Open:
on a dedicated 384 px contact sheet. Visual follow-up found complex/adjacent
building components rather than a systematic duplicate-label pattern.
- Targeted auditor and renderer tests passed: 9 tests.
## 2026-08-09 - Index-bound relationship highlighting
### Improved
- Connected the relationship-audit manifest to the visual label renderer so
exact duplicates, near duplicates and possible nesting receive distinct
colors and every implicated YOLO row is labelled with its source index.
- Generated a new immutable 36-tile focused sheet on Tower. It renders 17,253
valid labels, highlights all 86 rows participating in the 43 nesting pairs
and contains zero invalid label rows.
- AI-assisted inspection confirmed that the focused corpus contains only
magenta nesting candidates; no exact or high-IoU duplicate category is
present. The cases remain dispersed dense-GRB review candidates rather than
a safe deterministic rewrite set.
- Retained every source file and label unchanged. Excluding entire flagged
tiles would remove 17,167 unflagged labels and is therefore not justified.
### Verified
- Targeted relationship-auditor, renderer and Sprint 167 regression suite:
10 tests passed.
- Scoped Ruff check, Ruff format check, Python compilation and Git whitespace
check passed.
### Remaining limitation
- This is explicit AI-assisted triage, not human adjudication. It improves the
evidence and review precision but does not unlock governed production
training or checkpoint promotion.
+2
View File
@@ -1157,6 +1157,8 @@ This file now starts with the current implementation status. Older preparation/b
legitimately.
- [x] Persist a deterministic label-relationship audit and a focused 36-tile
contact sheet; AI follow-up found no systematic duplicate-label pattern.
- [x] Bind the relationship audit back to exact label-row indices and render
all 86 implicated rows with type-specific highlighting for focused review.
- [ ] Convert the AI-assisted ledger into no stronger claim than experimental
triage; a real human must independently review and sign the frozen artifacts
before the governed training wrapper may unlock.
@@ -117,3 +117,15 @@ AI-assisted inspection found no systematic duplicate-label pattern: the
relationships predominantly represent adjacent or complex building components
in dense GRB contexts. All tiles remain available for experimental analysis,
while the exact 43 pairs stay visible for human release adjudication.
A second focused render now binds the relationship manifest back to the exact
YOLO label-row indices. It highlights all 86 implicated rows: cyan for exact
duplicates, red for near duplicates and magenta for possible nesting. The sheet
contains only magenta relationship marks, confirming visually and
machine-readably that there are no exact or high-IoU duplicates in this set.
The nesting cases are geographically dispersed and commonly combine a larger
GRB object envelope with a smaller component. They are not deterministic
rewrite candidates. Excluding the 36 complete tiles would also discard 17,167
unflagged labels, so no automatic tile exclusion or label mutation was made.
The checksum-bound highlighted sheet and summary are recorded in the AI review
ledger.
+4 -1
View File
@@ -9,7 +9,10 @@ the script neither reads protected test data nor promotes a model.
`render_operator_yolo_label_qa_contact_sheets.py` paginates complete visual
reviews with `--tiles-per-sheet` (default `64`). This keeps large corpora
inspectable while preserving deterministic tile selection, ordering, label
accounting and stable `contact_sheet_001.png` naming for the first page.
accounting and stable `contact_sheet_001.png` naming for the first page. When
the input is a relationship-audit manifest, exact duplicates are highlighted
cyan, near duplicates red and possible nesting magenta; every highlighted box
also shows its zero-based label-row index. Unflagged labels remain yellow.
`audit_yolo_label_relationships.py` performs a read-only, same-class audit of
exact duplicate, high-IoU and possible-containment pairs inside YOLO label
@@ -250,6 +250,7 @@ def draw_tile_card(
invalid_label_count: int,
missing_label_file: bool,
low_visual_variance: bool,
relationship_highlights: dict[int, str],
) -> Image.Image:
header_height = 44
card = Image.new(
@@ -277,10 +278,17 @@ def draw_tile_card(
subtitle_parts.append(f"invalid:{invalid_label_count}")
if low_visual_variance:
subtitle_parts.append("low-variance")
if relationship_highlights:
subtitle_parts.append(f"highlighted:{len(relationship_highlights)}")
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)
for box in boxes:
highlight_styles = {
"exact_duplicate": ((0, 220, 255), 5),
"near_duplicate": ((255, 55, 55), 5),
"possible_nested": ((255, 0, 220), 5),
}
for box_index, box in enumerate(boxes):
x_center = box["center_x"] * thumb_size
y_center = box["center_y"] * thumb_size + header_height
width = box["width"] * thumb_size
@@ -291,11 +299,65 @@ def draw_tile_card(
bottom = min(thumb_size + header_height - 1, y_center + height / 2)
if right < left or bottom < top:
continue
draw.rectangle((left, top, right, bottom), outline=(255, 214, 10), width=3)
relationship = relationship_highlights.get(box_index)
color, stroke_width = highlight_styles.get(relationship, ((255, 214, 10), 3))
draw.rectangle((left, top, right, bottom), outline=color, width=stroke_width)
if relationship:
draw.rectangle(
(left, top, min(right, left + 30), min(bottom, top + 12)),
fill=(20, 31, 44),
)
draw.text(
(left + 2, top + 1),
f"#{box_index}",
fill=color,
font=font,
)
return card
def build_relationship_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,
}
highlights: dict[str, dict[int, str]] = {}
flagged_tiles = summary.get("flagged_tiles") or []
if not isinstance(flagged_tiles, list):
return highlights
for tile in flagged_tiles:
if not isinstance(tile, dict) or not tile.get("label_path"):
continue
tile_highlights = highlights.setdefault(str(tile["label_path"]), {})
relationships = tile.get("relationships") or []
if not isinstance(relationships, list):
continue
for relationship in relationships:
if not isinstance(relationship, dict):
continue
relationship_type = str(relationship.get("relationship") or "")
if relationship_type not in priorities:
continue
for field in ("first_index", "second_index"):
index = relationship.get(field)
if not isinstance(index, int) or index < 0:
continue
existing = tile_highlights.get(index)
if (
existing is None
or priorities[relationship_type] > priorities[existing]
):
tile_highlights[index] = relationship_type
return highlights
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()
@@ -344,6 +406,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)
for tile in selected_tiles:
image_path = resolve_path(tile.get("image_path"), summary_path)
@@ -351,6 +415,8 @@ 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)
invalid_label_count += tile_invalid_count
valid_label_count += len(boxes)
if missing_label_file:
@@ -375,6 +441,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,
)
)
rendered = True
@@ -393,6 +460,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),
"rendered": rendered,
}
)
@@ -435,6 +503,7 @@ 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,
"relationship_highlight_count": relationship_highlight_count,
"blank_range_threshold": args.blank_range_threshold,
"contact_sheets": contact_sheets,
"selected_tiles": selected_report_tiles,
@@ -456,6 +525,7 @@ 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']}",
"",
"## Contact Sheets",
"",
@@ -2,6 +2,7 @@ import pytest
from scripts.render_operator_yolo_label_qa_contact_sheets import (
CONTACT_SHEET_NAME_TEMPLATE,
build_relationship_highlights,
filter_tiles_by_samples,
)
@@ -34,3 +35,40 @@ def test_filter_tiles_by_samples_without_filter_preserves_tiles() -> None:
def test_contact_sheet_name_template_is_stable_and_one_indexed() -> None:
assert CONTACT_SHEET_NAME_TEMPLATE.format(index=1) == "contact_sheet_001.png"
assert CONTACT_SHEET_NAME_TEMPLATE.format(index=12) == "contact_sheet_012.png"
def test_relationship_highlights_bind_indices_and_preserve_highest_priority() -> None:
summary = {
"flagged_tiles": [
{
"label_path": "/data/tile.txt",
"relationships": [
{
"relationship": "possible_nested",
"first_index": 2,
"second_index": 4,
},
{
"relationship": "exact_duplicate",
"first_index": 2,
"second_index": 5,
},
{
"relationship": "near_duplicate",
"first_index": 6,
"second_index": 7,
},
],
}
]
}
assert build_relationship_highlights(summary) == {
"/data/tile.txt": {
2: "exact_duplicate",
4: "possible_nested",
5: "exact_duplicate",
6: "near_duplicate",
7: "near_duplicate",
}
}