물 영상의 검증된 3D 대표 시제품과 새 장면 설계 작성
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""기존 실행기로 세 대표 컷의 PNG/MP4를 만든다. 전 컷 제작은 하지 않는다."""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
from PIL import Image, ImageChops
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
BASE = Path('projects/water-hydrogen-bond-surface-tension/stages/08-scenes/r2')
|
||||
BLENDER = 'C:/Program Files/Blender Foundation/Blender 5.2/blender.exe'
|
||||
PYTHON = 'C:/Users/baram/anaconda3/python.exe'
|
||||
COMPOSITOR = Path('C:/git/ShortVideoGenerator/projects/water-hydrogen-bond-surface-tension/renderer/node_modules/@remotion/compositor-win32-x64-msvc')
|
||||
SOURCE = (BASE/'generated-source/preview.blend').as_posix()
|
||||
ARTIFACT = (BASE/'artifact.json').as_posix()
|
||||
PROJECT = 'projects/water-hydrogen-bond-surface-tension/project.json'
|
||||
PLAN = [('c07','BondPair','PairA',1017,1219,1219),('c16','Interface','WaterCutawayBody',2806,3050,3050),('c18','DropletStretch','Droplet',3172,3372,3372)]
|
||||
|
||||
|
||||
def run(args, records):
|
||||
result=subprocess.run(args,cwd=ROOT,text=True,encoding='utf-8',capture_output=True)
|
||||
records.append(dict(command=args,exit_code=result.returncode))
|
||||
(ROOT/BASE/'cli-commands.json').write_text(json.dumps(records,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
if result.returncode:
|
||||
print(result.stdout,result.stderr,flush=True)
|
||||
raise RuntimeError('실행 실패; 명령 기록과 기존 시도 보존')
|
||||
parsed=json.loads(result.stdout)
|
||||
records[-1]['source_sha256']=parsed['scene_file']['sha256']
|
||||
records[-1]['scene_name']=parsed.get('scene_name')
|
||||
if 'media' in parsed:
|
||||
records[-1]['media']=parsed['media']
|
||||
records[-1]['evidence_path']=(Path(parsed['media']['path']).parent/'evidence.json').as_posix()
|
||||
(ROOT/BASE/'cli-commands.json').write_text(json.dumps(records,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print('EXIT_0',args[5],parsed.get('scene_name'),flush=True)
|
||||
return parsed
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
parser=argparse.ArgumentParser()
|
||||
parser.add_argument('--kind',choices=['image','video'],required=True)
|
||||
args=parser.parse_args()
|
||||
record_path=ROOT/BASE/'cli-commands.json'
|
||||
records=json.loads(record_path.read_text(encoding='utf-8')) if record_path.exists() else []
|
||||
for record in records:
|
||||
record.pop('stdout',None)
|
||||
record.pop('stderr',None)
|
||||
artifact=json.loads((ROOT/ARTIFACT).read_text(encoding='utf-8'))
|
||||
for cut,scene,obj,start,end,boundary in PLAN:
|
||||
assert not any(s['id']==cut+'-'+args.kind for s in artifact['payload']['preview']['samples']), '이미 반영한 sample을 같은 버전에서 재생성하지 않는다'
|
||||
common=[PYTHON,'-B','-X','utf8','tools/blender/run.py']
|
||||
inspect=run(common+['inspect','--blender',BLENDER,'--source',SOURCE,'--scene',scene,'--require-object',obj],records)
|
||||
artifact['payload']['preview']['scene_file']=inspect['scene_file']
|
||||
(ROOT/ARTIFACT).write_text(json.dumps(artifact,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
options=['--frame',str(boundary)] if args.kind=='image' else ['--start',str(start),'--end',str(end)]
|
||||
rendered=run(common+['render','--mode','preview','--project',PROJECT,'--artifact',ARTIFACT,'--output-revision','2','--blender',BLENDER,'--source',SOURCE,'--scene',scene,'--require-object',obj,'--cut-id',cut,'--ffmpeg',str(COMPOSITOR/'ffmpeg.exe'),'--ffprobe',str(COMPOSITOR/'ffprobe.exe')]+options,records)
|
||||
# 실행기의 실제 증거만 미디어 메타데이터로 사용한다.
|
||||
evidence=rendered
|
||||
if args.kind=='video':
|
||||
frames=[ROOT/Path(evidence['media']['path']).parent/f'frame-{f:08}.png' for f in (start,start+(end-start)//2,end-1)]
|
||||
pixels=[Image.open(f).convert('RGB') for f in frames]
|
||||
if not (ImageChops.difference(pixels[0],pixels[1]).getbbox() and ImageChops.difference(pixels[0],pixels[2]).getbbox()):
|
||||
(ROOT/Path(evidence['media']['path']).parent/'visual-failure.json').write_text(json.dumps(dict(error='실제 렌더 프레임이 정지함; 미디어 반영 금지',logical_frames=[start,start+(end-start)//2,end-1]),ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
raise RuntimeError('실제 렌더 프레임이 정지함; 미디어 반영 금지')
|
||||
artifact['payload']['preview']['samples'].append(dict(id=cut+'-'+args.kind,cut_ids=[cut],media=evidence['media']))
|
||||
(ROOT/ARTIFACT).write_text(json.dumps(artifact,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
||||
print('READY',cut,args.kind,evidence['media']['path'],flush=True)
|
||||
Reference in New Issue
Block a user