제너레이티브 필름
45~120초, 1920×1080, 30fps MP4를 생성합니다. 모든 프레임은 코드로 그려지고(pycairo + Pillow) 모든 음은 합성됩니다(numpy/scipy). 스톡 푸티지, AI 이미지, 라이선스 음악은 없습니다. 참고 영화는 SŪTRA(인도 문명, 87초)입니다. 리소그래프 인쇄 증명 스타일이며, 96 BPM 비트 그리드 위에서 10개 챕터를 관통하는 빨간 실이 흐릅니다.
품질은 세 가지에 달려 있습니다. 세 가지를 모두 따르세요:
- 챕터마다 그림이 아닌 아이디어. 각 장면은 메커니즘을 보여줍니다(도시 격자 걷기, 낭송 브레이드, 프랙탈 성장, 궤적 비행). 명사를 설명하지 마세요.
- 사진과 소리를 위한 하나의 그리드. 모든 것은 비트 단위로 시간이 정해집니다. 음악이 먼저 작성되고, 모든 컷, 팝, 플래시는 음악 이벤트에 맞춰집니다.
- 강력하고 구체적인 아트 디렉션으로 '회사 설명 영상'이 아니어야 합니다: 인쇄 증명 크롬, 오정렬된 잉크, 현지 문자와 숫자, 타자기 캡션, 계단식 와이프.
0 · 설정(세션당 한 번)
pip install --break-system-packages pycairo fonttools # numpy, scipy, Pillow는 보통 설치되어 있음
which ffmpeg && python3 -c "from PIL import features; print('raqm', features.check('raqm'))"
Raqm이 True여야 합니다. 데바나가리, 아랍어, 타밀어 등 복잡한 문자 조합을 제공하기 때문입니다. 작업 디렉토리를 만드세요. 이 스킬 폴더에 scripts/가 있으면 engine.py와 audio_kit.py를 작업 디렉토리로 복사하세요. 그렇지 않으면 부록 A와 B를 그대로 작성하세요. 테스트된 코드이므로 다시 작성하지 마세요. 글꼴은 첫 사용 시 google/fonts GitHub 저장소에서 다운로드되며, 샌드박스가 허용합니다.
작업 목록을 설정하세요: 개념 → 음악 → 장면 → 테스트 프레임 → 렌더링 → QA → 전달.
1 · 개념(코드 작성 전)
리서치. 화면에 표시될 모든 날짜, 숫자, 이름, 주장을 웹 검색하세요. 불확실한 날짜 앞에는 "c."를 붙이세요. 다이어그램에는 "개략도" 또는 "축척 아님"이라고 표시하세요. 사실은 적고 정확하게 유지하며, 캡션은 12단어 이내로 하세요.
관통 객체. 모든 챕터에 등장하고 영화 제목이 되는 하나의 시각적 장치를 찾으세요. SŪTRA에서는 거리를 걷고, 음파가 되고, 콜람을 그리고, 달 궤적을 비행하는 빨간 실이었습니다. 다른 예:
- 바다 역사: 단일 파도의 마루.
- 커피: 한 방울.
- 컴퓨팅: 단일 비트.
- 회사: 제품의 핵심 제스처.
제목. 대상 언어로 된 한 단어를 선택하고, 화면에 사전 스타일의 설명을 표시하세요: sūtra (n.) 1. 실 2. 규칙 3. 사물을 함께 묶는 것.
챕터. 6~10개를 사용하고, 각각 뚜렷한 시각적 아이디어를 중심으로 구성하세요. 다음 레퍼토리에서 가져오세요:
- 스스로 구축되는 다이어그램: 도시 격자, 경로 지도, 궤도 또는 문자 계통도.
- 예술로서의 숫자: 파스칼/시에르핀스키 삼각형, 나선형으로 퍼져나가는 π, 이진 카운터, 또는 두 배로 늘어나는 계열의 객체들.
- 재귀 또는 프랙탈: 사원들의 사원, 또는 나무들의 나무.
- 실제 사운드트랙의 오실로스코프(
wave_at(f)): 음성이나 음악에 관한 챕터에 좋습니다. - 끊어지지 않는 선 그림: 콜람 또는 거울 곡선, 연속선 초상화, 또는 서명.
- 키네틱 단어 콜라주: 비트에 맞춰 찍히는 차용어, 슬로건, 또는 지명.
- 현지 숫자로 된 카운트다운, 이어서 발사 또는 이벤트, 그리고 히트.
- 북엔드: 아웃트로가 모든 챕터를 관통 실 위의 구슬로 연결하고, 시작 이미지로 돌아갑니다.
각 챕터를 테스트하세요: 스톡 사진으로 대체될 수 있다면 다시 생각하세요.
팔레트. 종이 색, 밤 색, 그리고 문화의 고유 재료 이름을 딴 5가지 잉크를 사용하세요. SŪTRA는 인디고, 신두르, 할디, 라니, 모르(공작)를 사용했습니다. 다른 예:
- 일본: 스미, 슈 주홍, 아이 인디고, 킨차 금, 말차.
- 멕시코: 코치닐, 옥수수, 탈라베라 블루, 옥, 메리골드.
- 우주: 반타블랙, 나트륨 오렌지, 수소-알파 레드, 산소 틸.
밝은 챕터와 어두운 챕터를 번갈아 배치하고, 드롭 챕터는 매 마디마다 색상을 변경하세요.
타입 시스템. 4~5개 패밀리를 사용하세요. 히어로 단어에는 좁은 디스플레이 서체, 주제에는 현지 문자, 캡션과 레이블에는 모노 서체, 시적인 줄에는 부드러운 이탤릭 세리프를 선택하세요. 실행 연도나 카운터에는 현지 숫자를 사용하세요. 유용한 google/fonts 경로(get_font(name, path, axes)):
| 역할 | 경로 | 축 |
|---|---|---|
| 디스플레이 | ofl/anton/Anton-Regular.ttf | – |
| 모노 | ofl/spacemono/SpaceMono-Regular.ttf, …-Bold.ttf | – |
| 이탤릭 세리프 | ofl/fraunces/Fraunces-Italic[SOFT,WONK,opsz,wght].ttf | [144,380,100,1] |
| 굵은 세리프 | ofl/fraunces/Fraunces[SOFT,WONK,opsz,wght].ttf | [144,900,100,1] |
| 데바나가리 | ofl/tirodevanagarisanskrit/TiroDevanagariSanskrit-Regular.ttf | – |
| 아랍어 | ofl/notonaskharabic/NotoNaskhArabic[wght].ttf | [700] |
| 타밀어 / 벵골어 / 태국어 | ofl/notosanstamil/NotoSansTamil[wdth,wght].ttf (동일 패턴) | [700,100] (wght, wdth 순서 = 폰트의 순서) |
| CJK | ofl/notoserifjp/NotoSerifJP[wght].ttf | [700] |
| 브라흐미 | ofl/notosansbrahmi/NotoSansBrahmi-Regular.ttf | – |
축 순서는 font_axes(name)로 확인하세요. 경로가 404면 WebFetch로 https://github.com/google/fonts/tree/main/ofl/<family>를 탐색하세요.
파격적인 장치. 몇 가지를 선택하세요:
- 인쇄 증명 HUD: 등록 마크, 잉크 견본, "PLATE 03 / 08 · NAME" 레이블, 고대 숫자 체계의 챕터 번호, 현지 숫자의 실행 연도, 챕터 구슬이 있는 진행 실.
- 히어로 타입의 리소 오정렬(
riso()). - 하프톤 채우기.
- 수직 거대 단어.
- 원 위에 설정된 텍스트(도장 또는 인장).
- 타자기 캡션.
- 두 가지 잉크로 된 계단식 또는 아이리스 와이프.
- 이음새에서의 글리치 스터터, 오디오 스터터와 일치.
- 드롭에서의 흰색 플래시.
- 필름 그레인.
절대 금지:
- 페이드되는 중앙 제목 카드.
- 장식으로서의 그라데이션, 글로우 또는 렌즈 플레어.
- 이모지 또는 클립아트.
- 저작권 있는 캐릭터, 로고, 실제 인물의 얼굴 그림.
- 장식으로 사용되는 국기.
- 고정관념 이미지.
2 · 비트 단위 타임라인
90–110 BPM을 사용하세요. 챕터는 8–16비트입니다. 96 BPM × 136비트 ≈ 87초. SŪTRA처럼 먼저 표를 작성하세요:
| 비트 | 챕터 | 그림 | 음악 |
|---|---|---|---|
| 0–12 | 인트로 | 점이 맥동하고, 선으로 늘어나며, 제목 글자가 비트마다 하나씩 나타남 | 드론 스웰, 각 글자마다 종소리, 라이저 |
| 12–28 | 1 | 도시 격자 조립; 실이 거리를 걷다 | 핸드드럼 그루브 + 플럭 멜로디 |
| 28–40 | 2(숨결) | 오디오 오실로스코프 + 단어 브레이드 | 드론, 성가, 드문드문 |
| 40–52 | 3 | 회전하는 바퀴, 글자가 찍힘, 계보 트리 | 바쁜 드럼, 라이저, 51.5에서 스터터 |
| 52–64 | 4 | 별자취 가속, 숫자 나선 | 패드 + 종 아르페지오, 큰 라이저, 플래시 |
| 64–80 | 드롭 | 거대한 히어로 글리프, 프랙탈이 마디당 4행 성장, 팔레트가 마디마다 변경 | 킥, 클랩, 서브 베이스, 훅, 붐 |
| 80–92 | 5 | 재귀 구조가 깊이에 따라 성장 | 하프타임; 티하이가 92에 착지 = 컷 |
| 92–108 | 6 | 한 줄 그림, 이어서 단어 콜라주 | 부드러운 그루브, 이어서 풀 그루브 |
| 108–120 | 피날레 | 현지 숫자 카운트다운 → 궤적 → 착지 히트 | 종, 럼블, 라이저, 붐 |
| 120–136 | 아웃트로 | 실 위의 구슬, 제목 재등장, 점 | 성가 + 패드 + 멜로디, 페이드 |
에너지 곡선: 조용한 인트로 → 빌드 → 숨결 → 빌드 → 가장 놀라운 아이디어로 드롭 → 하프타임 → 피날레 히트 → 조용한 북엔드.
3 · 음악 우선 — audio.py
from audio_kit import *
T = Track(bpm=96, beats=136, root=138.59, scale="raga_bhairavi") # root Hz; SCALES에 다양한 문화의 음계 있음
T.drone_bed(0, 132, level=lambda b: 0.16 if 12 <= b < 120 else 0.3)
T.hand_drum(12, 28) # 케헤르와 유사 기본 패턴
T.melody(12, [(2, 4, 1), (3, 5, 1.2, 6), (4, 7, 2.2)]) # (beat, degree, dur, glide-to degree)
T.riser_into(64, 4); T.at("fx", boom(), 64, 0.55) # 드롭으로
T.groove(64, 80); T.stutter(51.5)
T.tihai(92) # 3× 구절이 정확히 비트 92에 착지
T.mixdown("music.wav", fade_beats=(133, 136)) # env.npy / wave.npy도 기록
- 버스: drone, perc, kick, bass, mel, fx, pad. 원샷에는
T.at(bus, signal, beat, gain, pan)을 사용하세요. - 악기:
drone, pluck (buzz=0 for clean), bell, voice, pad, sub, kick, clap, hat, drum_tone, drum_bass, drum_slap, drum_muted, riser, boom, rumble. - 문화에 맞춰 음계와 악기를 매치하세요. 예: hijaz + 프레임 드럼 + oud-스타일 플럭(
buzz=0.8); in_sen + 코토-스타일 플럭(buzz=0) + 종; dorian + 드론 + 깨끗한 플럭. - 모든 시각적 이벤트에는 소리가 있습니다. 와이프에는 라이저, 드롭에는 붐과 플래시, 컷은 티하이나 킥 히트에 맞추고, 글자가 나타나면 종소리가 납니다.
- 결과 확인:
- 음량:
ffmpeg -i music.wav -af ebur128=framelog=quiet -f null -약 −16 ~ −14 LUFS. - 스펙트로그램:
ffmpeg -i music.wav -lavfi showspectrumpic=s=1600x500:scale=log:fscale=log spec.png, 이미지를 읽고 섹션 모양 확인.
- 음량:
4 · 장면 — film.py
from engine import *
PAPER, NIGHT, INK = hx("F1E6CF"), hx("0A0D26"), hx("14121C")
INK1, INK2, INK3 = hx("1B2A6B"), hx("EE3B1E"), hx("FFB21A")
get_font("anton", "ofl/anton/Anton-Regular.ttf")
get_font("mono", "ofl/spacemono/SpaceMono-Regular.ttf")
# 시드된 rng로 모듈 수준에서 기하학 미리 계산(작업자 프로세스 간 결정적)
def s_intro(ctx, b, f): # b = 비트(실수), f = 프레임
bg(ctx, NIGHT)
riso(ctx, lambda c: text(ctx, "TITLE", "anton", 400, c, W/2, 700, ax=.5, sc=back(ph(b, 4, .5))),
PAPER, INK2, dark=True)
return PAPER # HUD용 전경색
def hud(ctx, b, fg): reg_marks(ctx, fg, (INK1, INK2, INK3))
SCENES = [(0, s_intro), (12, s_ch1)] # (시작 비트, 함수)
run(SCENES, 136, bpm=96, name="myfilm", hud=hud,
wipes=[(12, INK2, INK3)], # (beat, c1, c2[, iris_wipe])
glitches=[(51.5, 52.15)], flashes=[(64, WHITE, .5)], fade_out=134.6)
장면 규칙:
- 무상태. 모든 값은
b의 함수이므로 프레임은 프로세스 간에 임의 순서로 렌더링될 수 있습니다. - 등장은 팝에
back(ph(b, start, 0.4)), 슬라이드에eo(), 카메라 이동과 선 그리기에eio()를 사용하세요. 항목을 0.2–1비트씩 지연시키세요. - 비트 생명은 바운스와 링 크기에
pulse(b)(각 비트마다 1→0), 음량에env_at(f), 오실로스코프에wave_at(f)에서 얻습니다. - 선을 실시간으로 그리세요.
path_partial(ctx, pts, cumlen(pts), frac)을 사용하고 반환된 끝점에 빛나는 구슬을 놓으세요. - 구성.
- 장면당 하나의 히어로 요소, 하나의 보조 다이어그램, 하나의 캡션을 유지하세요.
- 110px 여백을 유지하세요. HUD 모서리와 하단 밴드(y ≈ 960–1040)는 예약되어 있습니다. 캡션은 x ≈ 560에서 시작하는 y ≈ 975 또는 장면의 빈 영역에 배치하세요.
- 큰 타입은 큽니다: 300–800px 히어로 글리프. 캡션은 18–22px 모노입니다.
- 대비. 밝은 장면과 어두운 장면을 번갈아 사용하세요. 어두운 배경에는
riso(..., dark=True)를 사용하세요. - 콜라주의 긴 단어에는
fit_size()를, 도장에는text_circle()을 사용하세요. - HUD는 챕터 이름, 판 번호, 숫자 맵을 통한 현지 숫자, 진행 실을 표시할 수 있습니다. 스킬 폴더에
examples/sutra/film.py가 있으면 해당hud()와 장면이 전체 참조이며,examples/sutra/contact_sheet.png가 완성된 모습을 보여줍니다.
5 · 테스트 프레임 → 콘택트 시트 → 수정(건너뛰지 마세요)
python3 film.py test 2 6 10 17 25 34 45 58 66 75 86 96 104 110 114 118 125 134
sheet.png를 읽고 확인:
- 겹치거나 잘린 텍스트.
- 토후 상자 또는 누락된 글리프. 모든 글꼴 × 문자열에 대해
missing_glyphs(font, string)를 실행하세요. 예: Fraunces에는 → π ∞ ṅ 누락, Tiro Devanagari에는 → ≈ π 누락. - 축소판 크기에서 읽을 수 없는 캡션.
- 챕터 시작의 빈 프레임.
- 잘못된 사실 또는 오타.
- 아무것도 움직이지 않는 비트.
모든 프레임이 포스터가 될 수 있을 때까지 반복하세요. 보통 2–3라운드가 걸립니다.
6 · 렌더링, QA, 전달
nohup python3 film.py render > render.log 2>&1 &, 그런 다음 Bashsleep루프로 폴링(각각 제한 시간 ≤ 590초). 모든 코어에서 병렬로 렌더링됩니다. 대략적인 속도는 장면 복잡성에 따라 총 2–10fps입니다. SŪTRA는 2코어에서 약 20분이 걸렸습니다.- 출력:
name_full.mp4(CRF 18, 전체 품질) 및name.mp4(30MB 채팅 업로드 제한에 맞게 29MB 미만으로 유지되는 2패스 재인코딩). - QA:
ffprobe -count_frames로 프레임 수와 지속 시간 확인.ffmpeg -i name_full.mp4 -vf "fps=1/3.5,scale=384:216,tile=5x5" -frames:v 1 final.png, 그런 다음 읽기.- 압축 후를 포함하여 전체 크기 프레임 2–3개를 점검.
- 다운로드:
SendUserFile로name.mp4전송(display: "render"); 카드를 통해 사용자가 재생 및 다운로드할 수 있습니다. 전체 품질 파일도 언급하세요. 세션이 사용자 컴퓨터에 연결된 경우device_commit_files를 통해name_full.mp4를 사용자 폴더 중 하나에 저장하거나, 30MB 미만이면 전송하세요. - 수정된 섹션만 다시 렌더링하려면
python3 film.py chunk <f0> <f1> part.mp4를 실행하여 영향을 받는 프레임을 처리한 다음,run()이 하는 것처럼 ffmpeg로 조각을 연결하고 먹스하세요.
사용자에게 보내는 최종 메시지
영화 제목과 개념을 한 줄로, 챕터를 간결한 목록으로, 음악이 무엇인지, 그리고 모든 근사치("c." 날짜, 개략도)를 제공하세요. 단계를 요약하지 마세요.
부록 A — engine.py
"""engine.py — 제너레이티브 인쇄 스타일 필름 툴킷(pycairo + Pillow/raqm + ffmpeg).
필름 스크립트는: from engine import *
그런 다음 장면을 함수로 정의하고 scene(ctx, b, f) -> fg_color (b = 비트, f = 프레임)
하단에서 run(SCENES, TOTAL_BEATS, bpm=..., wipes=..., hud=...) 호출.
필름 스크립트의 CLI:
python film.py test 4 12.5 30 # 해당 비트의 PNG + 콘택트 시트(sheet.png) 렌더링
python film.py render # 전체 병렬 렌더링 -> film_full.mp4 + film.mp4 (<29MB)
"""
import math, os, sys, subprocess, shutil
import numpy
…
[번역을 위해 잘림]