initial version of random angle strokes

This commit is contained in:
ykcheungab
2026-10-03 20:41:30 +08:00
parent 1675ccb4af
commit 59f48d6d06
7 changed files with 695 additions and 22 deletions
+98 -6
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import argparse
import json
import math
import os
import shutil
import subprocess
@@ -177,6 +178,8 @@ def _run_blender_script(script: str, blend: Path, extra_args: list[str]) -> int:
str(blender),
"-b",
str(blend),
"--python-exit-code",
"1",
"-P",
str(script_path),
"--",
@@ -467,6 +470,77 @@ def cmd_texture(revision_dir: Path, args: argparse.Namespace) -> int:
return 0
def cmd_binary_preview(revision_dir: Path, args: argparse.Namespace) -> int:
"""Convert existing colour textures to a bounded binary camera-footprint trial."""
root = repo_root(revision_dir)
paths = RevisionPaths.from_revision_dir(revision_dir)
errors = validate_revision_metadata(paths)
if errors:
for message in errors:
print(f"ERROR: {message}", file=sys.stderr)
return 1
if not math.isfinite(args.camera_width_fraction) or args.camera_width_fraction <= 0:
print("ERROR: --camera-width-fraction must be finite and positive", file=sys.stderr)
return 2
texture_dir = resolve_texture_output_dir(root, paths, None)
job_name = "_texture_views_job.json" if args.views else "_texture_job.json"
job_path = (args.source_job or texture_dir / job_name).resolve()
if not job_path.is_file():
print(f"ERROR: colour texture job missing: {job_path}", file=sys.stderr)
return 3
try:
job = json.loads(job_path.read_text(encoding="utf-8"))
expected_schema = (
"orisys-sensor-design/texture-job-multiview-v1"
if args.views else "orisys-sensor-design/texture-job-v3"
)
if job.get("schema") != expected_schema:
raise ValueError(f"expected {expected_schema}, got {job.get('schema')!r}")
if Path(job["revision_dir"]).resolve() != paths.root.resolve():
raise ValueError("source job belongs to a different revision")
if Path(job["source_blend"]).resolve() != paths.source_blend.resolve():
raise ValueError("source job was built from a different Blender scene")
entries = job["pairs" if args.views else "outputs"]
source_ids = [str(entry["id" if args.views else "uv_map"]) for entry in entries]
if (not source_ids or len(source_ids) != len(set(source_ids))
or any(not source_id or not all(
char.isalnum() or char in "_-" for char in source_id
) for source_id in source_ids)):
raise ValueError("source job needs distinct, filename-safe output IDs")
except (OSError, ValueError, KeyError, TypeError) as exc:
print(f"ERROR: invalid colour texture job {job_path}: {exc}", file=sys.stderr)
return 2
output_dir = (args.output_dir or job_path.parent / "binary-preview").resolve()
targets = [output_dir / f"{source_id}_camera_compensated_width.png"
for source_id in source_ids]
if any(path.exists() for path in targets):
print("ERROR: binary preview target already exists; choose a fresh --output-dir",
file=sys.stderr)
return 2
code = _run_blender_script(
"binary_pattern_from_color.py", paths.source_blend,
["--job", str(job_path), "--output-dir", str(output_dir),
"--modes", "camera_compensated_width",
"--camera-width-fraction", str(args.camera_width_fraction)],
)
if code != 0:
return code
missing = [path for path in targets if not path.is_file() or path.stat().st_size == 0]
if missing:
for path in missing:
print(f"ERROR: binary preview missing or empty: {path}", file=sys.stderr)
return 3
if console.get_level() == console.QUIET:
console.artifact_paths(targets)
else:
console.report(f"OK: {len(targets)} binary preview PNG(s) in {output_dir}")
for path in targets:
console.report(f" {path}")
return 0
def cmd_texture_views(revision_dir: Path, args: argparse.Namespace) -> int:
"""Generate one owned texture per production view.
@@ -1357,7 +1431,6 @@ def _run_track_stage(
def cmd_deform(revision_dir: Path, args: argparse.Namespace) -> int:
"""Deform the sensing surface through the declared x/y/z sweep."""
root = repo_root(revision_dir)
paths = RevisionPaths.from_revision_dir(revision_dir)
errors = validate_revision_metadata(paths)
if errors:
@@ -1374,7 +1447,6 @@ def cmd_deform(revision_dir: Path, args: argparse.Namespace) -> int:
def cmd_render(revision_dir: Path, args: argparse.Namespace) -> int:
"""Render what the sensor camera sees for every deformed frame."""
root = repo_root(revision_dir)
paths = RevisionPaths.from_revision_dir(revision_dir)
errors = validate_revision_metadata(paths)
if errors:
@@ -1392,7 +1464,6 @@ def cmd_render(revision_dir: Path, args: argparse.Namespace) -> int:
def cmd_track(revision_dir: Path, args: argparse.Namespace) -> int:
"""Estimate DIS optical flow against the zero-displacement reference."""
root = repo_root(revision_dir)
paths = RevisionPaths.from_revision_dir(revision_dir)
errors = validate_revision_metadata(paths)
if errors:
@@ -1426,7 +1497,6 @@ def cmd_track(revision_dir: Path, args: argparse.Namespace) -> int:
def cmd_sensitivity(revision_dir: Path, args: argparse.Namespace) -> int:
"""Run the probe bench end to end and write the sensitivity report."""
root = repo_root(revision_dir)
paths = RevisionPaths.from_revision_dir(revision_dir)
errors = validate_revision_metadata(paths)
if errors:
@@ -1490,7 +1560,6 @@ def cmd_sensitivity(revision_dir: Path, args: argparse.Namespace) -> int:
def cmd_optimise(revision_dir: Path, args: argparse.Namespace) -> int:
"""Search patch-pattern parameters for the best measured sensitivity."""
root = repo_root(revision_dir)
paths = RevisionPaths.from_revision_dir(revision_dir)
errors = validate_revision_metadata(paths)
if errors:
@@ -1740,7 +1809,6 @@ def _compare_stage_runner(paths, args):
def cmd_compare(revision_dir: Path, args: argparse.Namespace) -> int:
"""Run one pattern under several estimator settings and plot the comparison."""
root = repo_root(revision_dir)
paths = RevisionPaths.from_revision_dir(revision_dir)
errors = validate_revision_metadata(paths)
if errors:
@@ -2112,6 +2180,28 @@ def main(argv: list[str] | None = None) -> None:
),
)
binary_p = sub.add_parser(
"binary-preview",
help="Make bounded black/white PNG trials from existing colour textures",
)
binary_p.add_argument("revision_dir", type=Path)
binary_p.add_argument(
"--views", action="store_true",
help="Read the texture-views job instead of the single-UV texture job",
)
binary_p.add_argument(
"--source-job", type=Path, default=None,
help="Existing colour texture job (default: revision build/textures job)",
)
binary_p.add_argument(
"--output-dir", type=Path, default=None,
help="Fresh output directory (default: binary-preview beside the colour job)",
)
binary_p.add_argument(
"--camera-width-fraction", type=float, default=0.5,
help="Stroke width / projected camera-cell span (default: 0.5; width 0.11–0.28 mm)",
)
manifest_p = sub.add_parser("manifest", help="Write release manifest with file hashes")
manifest_p.add_argument("revision_dir", type=Path)
manifest_p.add_argument(
@@ -2505,6 +2595,8 @@ def main(argv: list[str] | None = None) -> None:
code = cmd_texture(args.revision_dir, args)
elif args.command == "texture-views":
code = cmd_texture_views(args.revision_dir, args)
elif args.command == "binary-preview":
code = cmd_binary_preview(args.revision_dir, args)
elif args.command == "inspect":
code = cmd_inspect(args.revision_dir, args)
elif args.command == "preview":