"""실제 영상의 시작·중간·끝 렌더를 모바일 크기로 나란히 보는 검사 보조물.""" import hashlib import json from pathlib import Path from PIL import Image, ImageChops, ImageStat ROOT=Path(__file__).resolve().parents[3] BASE=ROOT/'projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r3' artifact=json.loads((BASE/'artifact.json').read_text(encoding='utf-8')) results=[] for sample in artifact['payload']['preview']['samples']: if sample['media']['kind']!='video': continue folder=(ROOT/sample['media']['path']).parent evidence=json.loads((folder/'evidence.json').read_text(encoding='utf-8')) frames=evidence['logical_frames'] selection=[frames[0],frames[len(frames)//2],frames[-1]] originals=[Image.open(folder/f'frame-{frame:08}.png').convert('RGB') for frame in selection] differences=[ImageChops.difference(originals[0],image) for image in originals[1:]] assert all(diff.getbbox() for diff in differences), '렌더 픽셀이 동일한 정지 클립' sheet=Image.new('RGB',(1080,640)) for index,image in enumerate(originals): sheet.paste(image.resize((360,640),Image.Resampling.LANCZOS),(index*360,0)) path=BASE/'generated-source'/f'mobile-{sample["cut_ids"][0]}.png' sheet.save(path) results.append(dict(cut_id=sample['cut_ids'][0],logical_frames=selection,pixel_sha256=[hashlib.sha256(image.tobytes()).hexdigest() for image in originals],mean_absolute_pixel_change=[sum(ImageStat.Stat(diff).mean)/3 for diff in differences],mobile_view_path=path.relative_to(ROOT).as_posix(),mobile_view_sha256=hashlib.sha256(path.read_bytes()).hexdigest(),visual_review='not_checked')) assert len(results)==3, '세 대표 영상이 아직 없음' (BASE/'motion-check.json').write_text(json.dumps(dict(result='GREEN',source_sha256=artifact['payload']['preview']['scene_file']['sha256'],views=results),ensure_ascii=False,indent=2)+'\n',encoding='utf-8') print('MOTION_GREEN: 세 영상의 실제 시작/중간/끝 RGB 픽셀 변화')