Files
reTraining/scripts/capture_all_screenshots.py
T

314 lines
14 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Automated Screenshot Capture Engine
Captures 21 high-resolution screenshots (1440x900) of all pages, modal dialogues,
interactive states, and progress workflows across the Dataset Enrichment & Retraining app.
"""
import json
import os
import sys
import time
from pathlib import Path
from playwright.sync_api import sync_playwright
BASE_DIR = Path(__file__).resolve().parent.parent
BASE_URL = os.environ.get("BASE_URL", "http://localhost:5174")
OUTPUT_DIR = os.environ.get("SCREENSHOTS_DIR", str(BASE_DIR / "screenshots"))
os.makedirs(OUTPUT_DIR, exist_ok=True)
def wait_page(page, ms=500):
page.wait_for_load_state("domcontentloaded")
try:
page.evaluate("document.fonts.ready")
except Exception:
pass
page.wait_for_timeout(ms)
def run():
with sync_playwright() as p:
browser = p.chromium.launch(
headless=True,
args=[
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu",
"--window-size=1440,900"
]
)
context = browser.new_context(
viewport={"width": 1440, "height": 900},
device_scale_factor=1,
color_scheme="dark"
)
# 1. Projects Page
print("[1/21] Capturing 01_projects_page.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects")
page.wait_for_selector(".card-grid, .page-head")
wait_page(page)
page.screenshot(path=os.path.join(OUTPUT_DIR, "01_projects_page.png"))
page.close()
# 2. Project Create Modal
print("[2/21] Capturing 02_project_create_modal.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects")
page.wait_for_selector("button:has-text('New project')")
page.click("button:has-text('New project')")
page.wait_for_selector("form.form-panel, input#np-name")
wait_page(page)
page.screenshot(path=os.path.join(OUTPUT_DIR, "02_project_create_modal.png"))
page.close()
# 3. Library Video Archive
print("[3/21] Capturing 03_library_video_archive.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5")
page.wait_for_selector("table.video-table, nav.date-list")
wait_page(page)
page.screenshot(path=os.path.join(OUTPUT_DIR, "03_library_video_archive.png"))
page.close()
# 4. Trim Page
print("[4/21] Capturing 04_trim_page.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/trim/2026-08-17%2F2026-08-17_09-57-27-503912.mp4")
page.wait_for_selector(".trim-player, input#trim-start")
wait_page(page, 700)
page.screenshot(path=os.path.join(OUTPUT_DIR, "04_trim_page.png"))
page.close()
# 5. Batches Page
print("[5/21] Capturing 05_batches_page.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/batches")
page.wait_for_selector("table.video-table tbody tr")
wait_page(page)
page.screenshot(path=os.path.join(OUTPUT_DIR, "05_batches_page.png"))
page.close()
# 6. Batches SAM3 Auto-Annotate Modal
print("[6/21] Capturing 06_batches_sam3_auto_annotate_modal.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/batches")
page.wait_for_selector("table.video-table tbody tr button:has-text('Auto-annotate')")
first_btn = page.locator("table.video-table tbody tr button:has-text('Auto-annotate')").first
first_btn.click()
page.wait_for_selector("div:has-text('Select Engine to Auto-annotate')")
wait_page(page, 300)
sam3_card = page.locator("div:has-text('SAM3 Zero-Shot')").first
sam3_card.click()
page.wait_for_selector("button:has-text('Start Auto-Annotation'), input[type='range']")
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "06_batches_sam3_auto_annotate_modal.png"))
page.close()
# 7. Batches Mass Auto-Annotate Modal
print("[7/21] Capturing 07_batches_mass_auto_annotate_modal.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/batches")
page.wait_for_selector("button:has-text('Auto-Annotate All Batches')")
page.click("button:has-text('Auto-Annotate All Batches')")
page.wait_for_selector("h3:has-text('Mass auto-annotate'), button:has-text('Run Preview')")
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "07_batches_mass_auto_annotate_modal.png"))
page.close()
# 8. Review & Annotation Canvas
print("[8/21] Capturing 08_review_annotation_canvas.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/review?batch=66")
page.wait_for_selector(".canvas-wrap img, .filmstrip")
wait_page(page, 800)
page.screenshot(path=os.path.join(OUTPUT_DIR, "08_review_annotation_canvas.png"))
page.close()
# 9. Review Filmstrip Quick Reclass Bar
print("[9/21] Capturing 09_review_filmstrip_quick_reclass.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/review?batch=66")
page.wait_for_selector(".canvas-wrap img, .filmstrip")
wait_page(page, 500)
shapes = page.locator("button.shape-pick, ul.shape-list li button")
if shapes.count() > 0:
shapes.first.click()
else:
page.click("button:has-text('Select')")
page.wait_for_timeout(200)
select_all_btn = page.locator("button:has-text('Select all')")
if select_all_btn.count() > 0:
select_all_btn.click()
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "09_review_filmstrip_quick_reclass.png"))
page.close()
# 10. Review Triage Crop Grid
print("[10/21] Capturing 10_review_triage_crop_grid.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/data-prep?batches=66")
page.wait_for_selector("h2:has-text('Check the boundary')")
page.wait_for_selector("svg circle, button img[src*='crops']")
wait_page(page, 400)
page.evaluate("document.querySelector('main.roboflow-main').scrollTop = 1350")
wait_page(page, 600)
page.screenshot(path=os.path.join(OUTPUT_DIR, "10_review_triage_crop_grid.png"))
page.close()
# 11. Review Triage Scatter Plot
print("[11/21] Capturing 11_review_triage_scatter_plot.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/data-prep?batches=66")
page.wait_for_selector("h2:has-text('Check the boundary')")
page.wait_for_selector("svg circle, button img[src*='crops']")
wait_page(page, 400)
page.evaluate("document.querySelector('main.roboflow-main').scrollTop = 680")
wait_page(page, 600)
page.screenshot(path=os.path.join(OUTPUT_DIR, "11_review_triage_scatter_plot.png"))
page.close()
# 12. Review Exemplar Pool Panel
print("[12/21] Capturing 12_review_exemplar_pool_panel.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/review?batch=66")
page.wait_for_selector(".canvas-wrap svg, .filmstrip")
wait_page(page, 600)
canvas = page.locator(".canvas-wrap svg")
box = canvas.bounding_box()
if box:
cx = box["x"] + box["width"] * 0.35
cy = box["y"] + box["height"] * 0.35
page.mouse.move(cx, cy)
page.mouse.down()
page.mouse.move(cx + 120, cy + 90, steps=5)
page.mouse.up()
page.wait_for_selector(".exemplar-panel", timeout=5000)
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "12_review_exemplar_pool_panel.png"))
page.close()
# 13. Data Prep Quality Outliers
print("[13/21] Capturing 13_data_prep_quality_outliers.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/data-prep?batches=66")
page.wait_for_selector("h1:has-text('Data Prep'), h2:has-text('Outlier filter')")
page.evaluate("document.querySelector('main.roboflow-main').scrollTop = 0")
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "13_data_prep_quality_outliers.png"))
page.close()
# 14. Data Prep Augmentation Panel
print("[14/21] Capturing 14_data_prep_augmentation_panel.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/data-prep?batches=66")
page.wait_for_selector("h2:has-text('Augmentation')")
fine_tune_btn = page.locator("button:has-text('Fine-tune individual settings')")
if fine_tune_btn.count() > 0:
fine_tune_btn.click()
page.evaluate("document.querySelector('main.roboflow-main').scrollTop = 420")
wait_page(page, 400)
page.screenshot(path=os.path.join(OUTPUT_DIR, "14_data_prep_augmentation_panel.png"))
page.close()
# 15. Data Prep Merge Target Modal
print("[15/21] Capturing 15_data_prep_merge_target_modal.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/data-prep?batches=66")
page.wait_for_selector("button:has-text('Confirm merge')")
page.evaluate("document.querySelector('main.roboflow-main').scrollTop = 2600")
wait_page(page, 300)
page.click("button:has-text('Confirm merge')")
page.wait_for_selector("div:has-text('Merge Target'), input[name='merge-target']")
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "15_data_prep_merge_target_modal.png"))
page.close()
# 16. Datasets Page
print("[16/21] Capturing 16_datasets_page.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/datasets")
page.wait_for_selector(".panel, h1:has-text('Datasets')")
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "16_datasets_page.png"))
page.close()
# 17. Models & Training Page
print("[17/21] Capturing 17_models_training_page.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/models")
page.wait_for_selector("h2:has-text('Train Model'), .video-table.metrics")
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "17_models_training_page.png"))
page.close()
# 18. Counting Bench Page
print("[18/21] Capturing 18_counting_bench_page.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/counting-bench")
page.wait_for_selector("table.video-table, .stat-value")
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "18_counting_bench_page.png"))
page.close()
# 19. Live Count Page
print("[19/21] Capturing 19_live_count_page.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/projects/5/live-count")
page.wait_for_selector("h1:has-text('Live counting test'), input[type='range']")
wait_page(page, 500)
page.screenshot(path=os.path.join(OUTPUT_DIR, "19_live_count_page.png"))
page.close()
# 20. SAM3 Playground
print("[20/21] Capturing 20_sam3_playground_page.png...")
page = context.new_page()
page.goto(f"{BASE_URL}/#/sam3-playground")
page.wait_for_selector("h1:has-text('SAM3 Global Playground'), textarea, input[type='file']")
sample_img = BASE_DIR / "data" / "projects" / "sack" / "batches" / "66" / "frames" / "000001.jpg"
if sample_img.exists():
file_input = page.locator("input[type='file']").first
file_input.set_input_files(str(sample_img))
wait_page(page, 600)
page.screenshot(path=os.path.join(OUTPUT_DIR, "20_sam3_playground_page.png"))
page.close()
# 21. Workflow Progress States
print("[21/21] Capturing 21_workflow_progress_states.png...")
page = context.new_page()
page.route("**/api/jobs*", lambda route: route.fulfill(
status=200,
content_type="application/json",
body=json.dumps({
"jobs": [{
"id": 1600,
"project_id": 5,
"type": "train",
"status": "running",
"progress": 32,
"total": 50,
"log": [
"[04:47:44] Fine-tuning model.pt for 50 epoch(s) (batch=64, imgsz=640, device=0)",
"[04:47:44] Augmentation: aggressive — degrees=10, fliplr=0.5, flipud=0.1, mosaic=1",
"[04:47:44] Image cache: disk (RAM cache would need ~17.1 GB, 28.1 GB free)",
"[04:48:12] Epoch 30/50: loss=0.0312, mAP50=0.912, precision=0.884, recall=0.862",
"[04:48:25] Epoch 31/50: loss=0.0298, mAP50=0.924, precision=0.891, recall=0.871",
"[04:48:38] Epoch 32/50: loss=0.0285, mAP50=0.931, precision=0.898, recall=0.877"
]
}]
})
))
page.goto(f"{BASE_URL}/#/projects/5/models")
page.wait_for_selector('.panel:has-text("Training Job #1600 (running)")')
wait_page(page, 400)
page.screenshot(path=os.path.join(OUTPUT_DIR, "21_workflow_progress_states.png"))
page.close()
browser.close()
print("ALL 21 SCREENSHOTS CAPTURED SUCCESSFULLY!")
if __name__ == "__main__":
run()