Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render actual SchDoc and compare to a trusted Altium page PNG, without alignment.
|
||||
|
||||
Images must represent the same sheet viewport. No registration, recoloring or
|
||||
cropping hides coordinate differences. Altium PDF exports can be rasterized to
|
||||
PNG with pdftoppm first. This tool does not manufacture reference images.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'backend'))
|
||||
import cairosvg
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from app.parser.schdoc_parser import parse_schdoc
|
||||
from app.render import svg_document
|
||||
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__)
|
||||
p.add_argument('schematic',type=Path)
|
||||
p.add_argument('--reference',type=Path)
|
||||
p.add_argument('--output',type=Path,default=Path('validation-output'))
|
||||
p.add_argument('--width',type=int,default=2000)
|
||||
p.add_argument('--max-mae',type=float,default=2.0,help='Mean absolute RGB error threshold, 0..255')
|
||||
p.add_argument('--max-changed-fraction',type=float,default=.01)
|
||||
p.add_argument('--pixel-tolerance',type=int,default=12)
|
||||
args=p.parse_args(); args.output.mkdir(parents=True,exist_ok=True)
|
||||
raw=args.schematic.read_bytes(); doc=parse_schdoc(raw,args.schematic.name,'validation')
|
||||
svg=svg_document(doc); (args.output/'drawing.svg').write_bytes(svg)
|
||||
width=args.width; height=max(1,round(width*doc.sheet.bounds.height/doc.sheet.bounds.width))
|
||||
if args.reference:
|
||||
with Image.open(args.reference) as ref: width,height=ref.size
|
||||
expected_ratio=doc.sheet.bounds.width/doc.sheet.bounds.height
|
||||
if abs(width/height/expected_ratio-1)>.005:
|
||||
p.error('Reference aspect ratio differs from original sheet; export the same viewport')
|
||||
png=cairosvg.svg2png(bytestring=svg,output_width=width,output_height=height)
|
||||
(args.output/'rendered.png').write_bytes(png)
|
||||
report={'source_sha256':hashlib.sha256(raw).hexdigest(),'record_count':doc.metadata['record_count'],
|
||||
'component_count':len(doc.components),'primitive_count':len(doc.primitives),
|
||||
'diagnostics':[d.model_dump() for d in doc.diagnostics],
|
||||
'reference_compared':bool(args.reference),'fidelity_verified':False}
|
||||
passed=True
|
||||
if args.reference:
|
||||
reference=Image.open(args.reference).convert('RGB'); actual=Image.open(args.output/'rendered.png').convert('RGB')
|
||||
a=np.array(actual,dtype=np.int16); b=np.array(reference,dtype=np.int16); delta=np.abs(a-b)
|
||||
mae=float(delta.mean()); changed=float((delta.max(axis=2)>args.pixel_tolerance).mean())
|
||||
heat=np.zeros_like(a,dtype=np.uint8); heat[:,:,0]=np.clip(delta.max(axis=2)*4,0,255).astype(np.uint8)
|
||||
Image.fromarray(heat).save(args.output/'difference.png')
|
||||
Image.blend(reference,actual,.5).save(args.output/'overlay.png')
|
||||
passed=mae<=args.max_mae and changed<=args.max_changed_fraction
|
||||
report.update(reference_sha256=hashlib.sha256(args.reference.read_bytes()).hexdigest(),mae=mae,
|
||||
changed_pixel_fraction=changed,thresholds={'max_mae':args.max_mae,'max_changed_fraction':args.max_changed_fraction,'pixel_tolerance':args.pixel_tolerance},
|
||||
regression_passed=passed)
|
||||
(args.output/'report.json').write_text(json.dumps(report,indent=2))
|
||||
print(json.dumps({k:v for k,v in report.items() if k!='diagnostics'},indent=2))
|
||||
return 0 if passed else 1
|
||||
|
||||
if __name__=='__main__': sys.exit(main())
|
||||
مرجع در شماره جدید
مسدود کردن کاربر