expand AI-assisted review to full building corpus
This commit is contained in:
+49
-36
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"ledger_id": "reviewedexp6-ai-assisted-review-r1",
|
"ledger_id": "reviewedexp6-ai-assisted-review-r2-full-corpus",
|
||||||
"reviewed_at": "2026-08-09T14:00:00+02:00",
|
"reviewed_at": "2026-08-09T15:00:00+02:00",
|
||||||
"reviewer": {
|
"reviewer": {
|
||||||
"id": "openai-codex",
|
"id": "openai-codex",
|
||||||
"type": "ai_assistant",
|
"type": "ai_assistant",
|
||||||
@@ -10,16 +10,21 @@
|
|||||||
"status": "complete_ai_assisted_experimental_only",
|
"status": "complete_ai_assisted_experimental_only",
|
||||||
"explicit_limitations": [
|
"explicit_limitations": [
|
||||||
"This ledger is not human review and must never satisfy a human-review release gate.",
|
"This ledger is not human review and must never satisfy a human-review release gate.",
|
||||||
"Decisions cover the 64 rendered samples on the contact sheet, not every label instance in the corpus.",
|
"Decisions cover all 252 rendered tiles, but visual review still does not adjudicate every individual label as human ground truth.",
|
||||||
"Visible roof geometry can be displaced from authoritative GRB ground-level footprints.",
|
"Visible roof geometry can be displaced from authoritative GRB ground-level footprints.",
|
||||||
"Accepted samples are eligible only for experimental training and non-protected validation."
|
"Accepted samples are eligible only for experimental training and non-protected validation."
|
||||||
],
|
],
|
||||||
"artifacts": {
|
"artifacts": {
|
||||||
"contact_sheet_path": "/app/storage/operator-data/model-review/reviewedexp6-corpus/visual-review/contact_sheet_001.png",
|
"review_summary_path": "/app/storage/operator-data/model-review/reviewedexp6-corpus/full-ai-review-r1/operator_yolo_label_qa_summary.json",
|
||||||
"contact_sheet_sha256": "411ee541dcd53b59abc1c12f733f164b28478a1398ab2d50be8c728b11c8fdc4",
|
"review_summary_sha256": "467e5c3c489c42e42872a63fbb0bba7c613824631598d86ea6324b22c007f75a",
|
||||||
"review_summary_path": "/app/storage/operator-data/model-review/reviewedexp6-corpus/visual-review/operator_yolo_label_qa_summary.json",
|
"contact_sheets": [
|
||||||
"rendered_tile_count": 64,
|
{"path":"/app/storage/operator-data/model-review/reviewedexp6-corpus/full-ai-review-r1/contact_sheet_001.png","sha256":"688b97a6e82e6b906b7966ed3e2b43685786e89d50549188bfdcc3837d042eb2","tile_count":64},
|
||||||
"rendered_label_count": 21611,
|
{"path":"/app/storage/operator-data/model-review/reviewedexp6-corpus/full-ai-review-r1/contact_sheet_002.png","sha256":"87f1945df3324dc100e08febd3e45f291a2d709ddebd9c627b082f30fd8f2d26","tile_count":64},
|
||||||
|
{"path":"/app/storage/operator-data/model-review/reviewedexp6-corpus/full-ai-review-r1/contact_sheet_003.png","sha256":"1873ab25b8ee843abe28d00329fe28998a7b39f9fdbcd8f8acfb58cc576cdf10","tile_count":64},
|
||||||
|
{"path":"/app/storage/operator-data/model-review/reviewedexp6-corpus/full-ai-review-r1/contact_sheet_004.png","sha256":"e64c252aac08792a76a62fe734728e7c81ba6a649ad2a1cd2051b4df1f95be98","tile_count":60}
|
||||||
|
],
|
||||||
|
"rendered_tile_count": 252,
|
||||||
|
"rendered_label_count": 79192,
|
||||||
"selected_sample_count": 28
|
"selected_sample_count": 28
|
||||||
},
|
},
|
||||||
"visual_criteria": [
|
"visual_criteria": [
|
||||||
@@ -30,34 +35,34 @@
|
|||||||
"dense roof-versus-ground-footprint disagreement is retained as an explicit limitation"
|
"dense roof-versus-ground-footprint disagreement is retained as an explicit limitation"
|
||||||
],
|
],
|
||||||
"decisions": [
|
"decisions": [
|
||||||
{"sample_slug":"arendonk_center","sampled_tiles":2,"sampled_labels":1163,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"arendonk_center","sampled_tiles":9,"sampled_labels":4172,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"balen","sampled_tiles":2,"sampled_labels":794,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"balen","sampled_tiles":9,"sampled_labels":2658,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"beerse_center","sampled_tiles":2,"sampled_labels":1158,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"beerse_center","sampled_tiles":9,"sampled_labels":4303,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"dessel_center","sampled_tiles":2,"sampled_labels":841,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"dessel_center","sampled_tiles":9,"sampled_labels":3405,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"dessel_heide","sampled_tiles":2,"sampled_labels":68,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
{"sample_slug":"dessel_heide","sampled_tiles":9,"sampled_labels":192,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"geel","sampled_tiles":2,"sampled_labels":1244,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"geel","sampled_tiles":9,"sampled_labels":4615,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"geel_bel","sampled_tiles":2,"sampled_labels":62,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
{"sample_slug":"geel_bel","sampled_tiles":9,"sampled_labels":168,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"grobbendonk_center","sampled_tiles":2,"sampled_labels":1008,"split":"val","category":"reference_aoi","decision":"non_protected_validation_only"},
|
{"sample_slug":"grobbendonk_center","sampled_tiles":9,"sampled_labels":2793,"split":"val","category":"reference_aoi","decision":"non_protected_validation_only"},
|
||||||
{"sample_slug":"herentals","sampled_tiles":2,"sampled_labels":1512,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"herentals","sampled_tiles":9,"sampled_labels":5087,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"herenthout_bos","sampled_tiles":2,"sampled_labels":199,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
{"sample_slug":"herenthout_bos","sampled_tiles":9,"sampled_labels":520,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"hoogstraten_center","sampled_tiles":2,"sampled_labels":1063,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"hoogstraten_center","sampled_tiles":9,"sampled_labels":3761,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"hulshout_center","sampled_tiles":2,"sampled_labels":895,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"hulshout_center","sampled_tiles":9,"sampled_labels":3606,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"kasterlee_bos","sampled_tiles":2,"sampled_labels":72,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
{"sample_slug":"kasterlee_bos","sampled_tiles":9,"sampled_labels":137,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"kasterlee_center","sampled_tiles":2,"sampled_labels":983,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"kasterlee_center","sampled_tiles":9,"sampled_labels":4022,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"laakdal_center","sampled_tiles":2,"sampled_labels":156,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible_with_dense_alignment_limitation"},
|
{"sample_slug":"laakdal_center","sampled_tiles":9,"sampled_labels":287,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible_with_dense_alignment_limitation"},
|
||||||
{"sample_slug":"lille_center","sampled_tiles":2,"sampled_labels":961,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"lille_center","sampled_tiles":9,"sampled_labels":3949,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"lommel_heide","sampled_tiles":6,"sampled_labels":0,"split":"train","category":"pure_empty_negative","decision":"experimental_training_eligible"},
|
{"sample_slug":"lommel_heide","sampled_tiles":9,"sampled_labels":0,"split":"train","category":"pure_empty_negative","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"meerhout_bos","sampled_tiles":2,"sampled_labels":111,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
{"sample_slug":"meerhout_bos","sampled_tiles":9,"sampled_labels":206,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"meerhout_center","sampled_tiles":2,"sampled_labels":1016,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"meerhout_center","sampled_tiles":9,"sampled_labels":4002,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"mol","sampled_tiles":2,"sampled_labels":951,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"mol","sampled_tiles":9,"sampled_labels":3666,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"nijlen_center","sampled_tiles":2,"sampled_labels":1309,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"nijlen_center","sampled_tiles":9,"sampled_labels":4900,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"olen_center","sampled_tiles":2,"sampled_labels":971,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"olen_center","sampled_tiles":9,"sampled_labels":3955,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"oud_turnhout_center","sampled_tiles":2,"sampled_labels":1535,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible_with_dense_alignment_limitation"},
|
{"sample_slug":"oud_turnhout_center","sampled_tiles":9,"sampled_labels":5492,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible_with_dense_alignment_limitation"},
|
||||||
{"sample_slug":"postel_bos","sampled_tiles":6,"sampled_labels":0,"split":"train","category":"pure_empty_negative","decision":"experimental_training_eligible"},
|
{"sample_slug":"postel_bos","sampled_tiles":9,"sampled_labels":0,"split":"train","category":"pure_empty_negative","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"ravels_bos","sampled_tiles":2,"sampled_labels":22,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
{"sample_slug":"ravels_bos","sampled_tiles":9,"sampled_labels":55,"split":"train","category":"sparse_building_context","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"rijkevorsel_center","sampled_tiles":2,"sampled_labels":1153,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"rijkevorsel_center","sampled_tiles":9,"sampled_labels":4231,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"vorselaar_center","sampled_tiles":2,"sampled_labels":930,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
{"sample_slug":"vorselaar_center","sampled_tiles":9,"sampled_labels":3858,"split":"train","category":"reference_aoi","decision":"experimental_training_eligible"},
|
||||||
{"sample_slug":"vosselaar_center","sampled_tiles":2,"sampled_labels":1434,"split":"val","category":"reference_aoi","decision":"non_protected_validation_only"}
|
{"sample_slug":"vosselaar_center","sampled_tiles":9,"sampled_labels":5152,"split":"val","category":"reference_aoi","decision":"non_protected_validation_only"}
|
||||||
],
|
],
|
||||||
"accounting": {
|
"accounting": {
|
||||||
"accepted_experimental_training_samples": 26,
|
"accepted_experimental_training_samples": 26,
|
||||||
@@ -67,5 +72,13 @@
|
|||||||
"sparse_context_samples": 6,
|
"sparse_context_samples": 6,
|
||||||
"human_review_complete": false,
|
"human_review_complete": false,
|
||||||
"production_release_eligible": false
|
"production_release_eligible": false
|
||||||
|
},
|
||||||
|
"nested_box_audit": {
|
||||||
|
"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,
|
||||||
|
"share_of_rendered_labels": 0.000543,
|
||||||
|
"action": "requires_human_adjudication_before_release; no automatic rewrite or exclusion"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12549,3 +12549,16 @@ Open:
|
|||||||
review and therefore does not accept this AI ledger as release evidence.
|
review and therefore does not accept this AI ledger as release evidence.
|
||||||
- Did not add an override, fixture claim or synthetic reviewer identity. No
|
- Did not add an override, fixture claim or synthetic reviewer identity. No
|
||||||
model training, activation or production redeploy followed from this review.
|
model training, activation or production redeploy followed from this review.
|
||||||
|
|
||||||
|
### Full-corpus extension
|
||||||
|
|
||||||
|
- Added deterministic contact-sheet pagination and rendered all 252 retained
|
||||||
|
tiles across four pages. Accounting covers all 79,192 labels with zero
|
||||||
|
missing images/files, invalid rows or low-variance tiles.
|
||||||
|
- Inspected every page at original resolution. Pure-empty tiles remain visibly
|
||||||
|
empty; sparse contexts remain distinct; dense tiles retain the documented
|
||||||
|
roof-versus-ground-footprint limitation.
|
||||||
|
- A read-only same-class box probe found zero exact duplicates and zero pairs
|
||||||
|
at IoU >= 0.90. It flagged 43 possible containment pairs across 36 tiles for
|
||||||
|
human adjudication. No source label was modified or silently excluded.
|
||||||
|
- Targeted renderer tests passed: 6 tests.
|
||||||
|
|||||||
@@ -1150,6 +1150,11 @@ This file now starts with the current implementation status. Older preparation/b
|
|||||||
- [x] Complete an explicitly AI-assisted per-AOI review of the 64-tile
|
- [x] Complete an explicitly AI-assisted per-AOI review of the 64-tile
|
||||||
reviewedexp6 contact sheet, retaining reviewer type, artifact hash, split
|
reviewedexp6 contact sheet, retaining reviewer type, artifact hash, split
|
||||||
role, sample accounting and roof/ground-footprint limitations.
|
role, sample accounting and roof/ground-footprint limitations.
|
||||||
|
- [x] Extend AI-assisted review to all 252 tiles and 79,192 labels through
|
||||||
|
deterministic four-page contact-sheet rendering with complete accounting.
|
||||||
|
- [ ] Human-adjudicate the 43 possible nested same-class box pairs in 36 tiles;
|
||||||
|
do not rewrite or exclude them automatically because GRB objects can overlap
|
||||||
|
legitimately.
|
||||||
- [ ] Convert the AI-assisted ledger into no stronger claim than experimental
|
- [ ] Convert the AI-assisted ledger into no stronger claim than experimental
|
||||||
triage; a real human must independently review and sign the frozen artifacts
|
triage; a real human must independently review and sign the frozen artifacts
|
||||||
before the governed training wrapper may unlock.
|
before the governed training wrapper may unlock.
|
||||||
|
|||||||
@@ -103,3 +103,11 @@ It explicitly cannot satisfy the human-review release gate. The production
|
|||||||
training wrapper reproduced that boundary and no bypass was added. The exact
|
training wrapper reproduced that boundary and no bypass was added. The exact
|
||||||
ledger is retained in
|
ledger is retained in
|
||||||
`artifacts/evidence/accuracy/model-training/20260809-reviewedexp6-ai-assisted-review-ledger.json`.
|
`artifacts/evidence/accuracy/model-training/20260809-reviewedexp6-ai-assisted-review-ledger.json`.
|
||||||
|
|
||||||
|
The renderer was subsequently paginated and the review expanded from the
|
||||||
|
64-tile sample to all 252 retained tiles. Four immutable pages now account for
|
||||||
|
all 79,192 labels with zero missing images, missing label files, invalid rows
|
||||||
|
or low-variance tiles. No exact or IoU>=0.90 duplicate box pair was found. A
|
||||||
|
separate containment probe identified 43 potentially nested pairs across 36
|
||||||
|
tiles (0.0543% relative to rendered labels); these remain human-adjudication
|
||||||
|
candidates and were neither rewritten nor automatically excluded.
|
||||||
|
|||||||
@@ -6,6 +6,11 @@ model and dataset hashes, standard Ultralytics detection metrics and a separate
|
|||||||
pure-background detection count. The output claim is validation ranking only;
|
pure-background detection count. The output claim is validation ranking only;
|
||||||
the script neither reads protected test data nor promotes a model.
|
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.
|
||||||
|
|
||||||
Setup-, import-, demo- en maintenance-scripts voor GeoIntel.
|
Setup-, import-, demo- en maintenance-scripts voor GeoIntel.
|
||||||
|
|
||||||
## WALOUS source provisioning
|
## WALOUS source provisioning
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from PIL import Image, ImageDraw, ImageFont
|
|||||||
|
|
||||||
JSON_NAME = "operator_yolo_label_qa_summary.json"
|
JSON_NAME = "operator_yolo_label_qa_summary.json"
|
||||||
MARKDOWN_NAME = "operator_yolo_label_qa_contact_sheet.md"
|
MARKDOWN_NAME = "operator_yolo_label_qa_contact_sheet.md"
|
||||||
CONTACT_SHEET_NAME = "contact_sheet_001.png"
|
CONTACT_SHEET_NAME_TEMPLATE = "contact_sheet_{index:03d}.png"
|
||||||
|
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
@@ -37,6 +37,12 @@ def parse_args() -> argparse.Namespace:
|
|||||||
)
|
)
|
||||||
parser.add_argument("--columns", type=int, default=4, help="Contact-sheet columns")
|
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("--thumb-size", type=int, default=256, help="Rendered tile thumbnail size in pixels")
|
||||||
|
parser.add_argument(
|
||||||
|
"--tiles-per-sheet",
|
||||||
|
type=int,
|
||||||
|
default=64,
|
||||||
|
help="Maximum rendered cards per contact-sheet page. Default: 64.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--blank-range-threshold",
|
"--blank-range-threshold",
|
||||||
type=int,
|
type=int,
|
||||||
@@ -329,16 +335,17 @@ def build_report(summary: dict[str, Any], summary_path: Path, args: argparse.Nam
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
contact_sheets = []
|
if args.tiles_per_sheet <= 0:
|
||||||
if rendered_cards:
|
raise ValueError("tiles_per_sheet must be positive")
|
||||||
contact_sheets.append(
|
contact_sheets = [
|
||||||
{
|
{
|
||||||
"path": CONTACT_SHEET_NAME,
|
"path": CONTACT_SHEET_NAME_TEMPLATE.format(index=(start // args.tiles_per_sheet) + 1),
|
||||||
"tile_count": len(rendered_cards),
|
"tile_count": len(rendered_cards[start : start + args.tiles_per_sheet]),
|
||||||
"columns": args.columns,
|
"columns": args.columns,
|
||||||
"thumb_size": args.thumb_size,
|
"thumb_size": args.thumb_size,
|
||||||
}
|
}
|
||||||
)
|
for start in range(0, len(rendered_cards), args.tiles_per_sheet)
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
{
|
{
|
||||||
@@ -350,6 +357,7 @@ def build_report(summary: dict[str, Any], summary_path: Path, args: argparse.Nam
|
|||||||
"requested_sample_slugs": sorted(set(args.sample_slug)),
|
"requested_sample_slugs": sorted(set(args.sample_slug)),
|
||||||
"columns": args.columns,
|
"columns": args.columns,
|
||||||
"thumb_size": args.thumb_size,
|
"thumb_size": args.thumb_size,
|
||||||
|
"tiles_per_sheet": args.tiles_per_sheet,
|
||||||
"selected_tile_count": len(selected_tiles),
|
"selected_tile_count": len(selected_tiles),
|
||||||
"selected_sample_count": len(
|
"selected_sample_count": len(
|
||||||
{str(tile.get("sample_slug") or "unknown") for tile in selected_tiles}
|
{str(tile.get("sample_slug") or "unknown") for tile in selected_tiles}
|
||||||
@@ -424,8 +432,10 @@ def main() -> int:
|
|||||||
|
|
||||||
summary = load_json(summary_path)
|
summary = load_json(summary_path)
|
||||||
report, cards = build_report(summary, summary_path, args)
|
report, cards = build_report(summary, summary_path, args)
|
||||||
if cards:
|
for page_index, start in enumerate(range(0, len(cards), args.tiles_per_sheet), start=1):
|
||||||
build_contact_sheet(cards, args.columns, output_dir / CONTACT_SHEET_NAME)
|
page_cards = cards[start : start + args.tiles_per_sheet]
|
||||||
|
page_name = CONTACT_SHEET_NAME_TEMPLATE.format(index=page_index)
|
||||||
|
build_contact_sheet(page_cards, args.columns, output_dir / page_name)
|
||||||
|
|
||||||
(output_dir / JSON_NAME).write_text(json.dumps(report, indent=2, sort_keys=True), encoding="utf-8")
|
(output_dir / JSON_NAME).write_text(json.dumps(report, indent=2, sort_keys=True), encoding="utf-8")
|
||||||
write_markdown(report, output_dir)
|
write_markdown(report, output_dir)
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from scripts.render_operator_yolo_label_qa_contact_sheets import filter_tiles_by_samples
|
from scripts.render_operator_yolo_label_qa_contact_sheets import (
|
||||||
|
CONTACT_SHEET_NAME_TEMPLATE,
|
||||||
|
filter_tiles_by_samples,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
TILES = [
|
TILES = [
|
||||||
@@ -24,3 +27,8 @@ def test_filter_tiles_by_samples_rejects_missing_sample() -> None:
|
|||||||
|
|
||||||
def test_filter_tiles_by_samples_without_filter_preserves_tiles() -> None:
|
def test_filter_tiles_by_samples_without_filter_preserves_tiles() -> None:
|
||||||
assert filter_tiles_by_samples(TILES, []) is TILES
|
assert filter_tiles_by_samples(TILES, []) is TILES
|
||||||
|
|
||||||
|
|
||||||
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user