#!/usr/bin/env python3 """Run the placement stage against several existing split candidates. This is an offline regression matrix: it does not call the image API. Point the input map at generated candidate folders, then inspect each assembled.png and case-summary.json under --out-dir. """ import argparse, json, shutil, subprocess from pathlib import Path from PIL import Image import numpy as np def main(): ap = argparse.ArgumentParser() ap.add_argument('--reference', required=True) ap.add_argument('--genielabs-dir', required=True) ap.add_argument('--out-dir', required=True) ap.add_argument('--candidate', action='append', nargs=2, metavar=('NAME','PARTS_DIR'), required=True) args = ap.parse_args() root = Path(args.out_dir); root.mkdir(parents=True, exist_ok=True) py = '/Users/zhaoziyu/anaconda3/bin/python' ref = Image.open(args.reference).convert('RGBA') ref_rgb = np.asarray(ref)[:, :, :3].astype(np.float32) ref_fg = np.asarray(ref)[:, :, 3] > 128 rows = [] for name, source in args.candidate: case = root / name; parts = case / 'parts'; parts.mkdir(parents=True, exist_ok=True) for f in Path(source).glob('*.png'): shutil.copy2(f, parts / f.name) layout = case / 'layout.json' subprocess.run([py, str(Path(args.genielabs_dir)/'scripts/position_parts.py'), '--reference', args.reference, '--parts', str(parts), '--output', str(layout)], check=True) assembled = case / 'assembled.png' subprocess.run([py, str(Path(__file__).with_name('compose_preview.py')), '--reference', args.reference, '--parts', str(parts), '--layout', str(layout), '--output', str(assembled)], check=True) out = np.asarray(Image.open(assembled).convert('RGBA'))[:, :, :3].astype(np.float32) err = float(np.mean(np.abs(out[ref_fg] - ref_rgb[ref_fg]))) if ref_fg.any() else None data = json.loads(layout.read_text()) summary = {'name': name, 'parts': len(data.get('parts', {})), 'sift': sum(v.get('method') == 'sift' for v in data.get('parts', {}).values()), 'template': sum(v.get('method') == 'template' for v in data.get('parts', {}).values()), 'foreground_mae': round(err, 2) if err is not None else None, 'z_order': data.get('z_order', [])} (case/'case-summary.json').write_text(json.dumps(summary, ensure_ascii=False, indent=2)) rows.append(summary) (root/'case-report.json').write_text(json.dumps(rows, ensure_ascii=False, indent=2)) print(json.dumps(rows, ensure_ascii=False, indent=2)) if __name__ == '__main__': main()