Filme Generativo
Produz um MP4 de 45–120 s, 1920×1080, 30 fps no qual cada quadro é desenhado em código (pycairo + Pillow) e cada nota é sintetizada (numpy/scipy). Não há filmagens de arquivo, imagens de IA ou música licenciada. O filme de referência é SŪTRA (civilização indiana, 87 s). É estilizado como uma prova de impressão risográfica, e um fio vermelho percorre dez capítulos em uma grade de batidas de 96 BPM.
A qualidade vem de três coisas. Siga todas:
- Uma ideia por capítulo, não uma imagem por capítulo. Cada cena mostra um mecanismo (uma grade urbana sendo percorrida, uma trança de recitação, um fractal crescendo, uma trajetória sendo voada). Não ilustre um substantivo.
- Uma grade para imagem e som. Tudo é cronometrado em batidas. A música é escrita primeiro, e cada corte, estalo e flash cai em um evento musical.
- Uma direção de arte forte e específica que não é "explicativa corporativa": cromo de prova de impressão, tintas mal registradas, scripts e numerais nativos, legendas de máquina de escrever e transições escalonadas.
0 · Configuração (uma vez por sessão)
pip install --break-system-packages pycairo fonttools # numpy, scipy, Pillow geralmente presentes
which ffmpeg && python3 -c "from PIL import features; print('raqm', features.check('raqm'))"
Raqm deve ser True, porque fornece modelagem de scripts complexos para Devanagari, Árabe, Tamil e scripts semelhantes. Crie um diretório de trabalho. Se a pasta desta habilidade tiver scripts/, copie engine.py e audio_kit.py para o diretório de trabalho. Caso contrário, escreva-os a partir do Apêndice A e B literalmente. Eles são testados, então não os reescreva. As fontes baixam no primeiro uso do repositório google/fonts GitHub, que o sandbox permite.
Configure uma lista de tarefas: conceito → música → cenas → quadros de teste → renderização → QA → entrega.
1 · Conceito (antes de qualquer código)
Pesquise. Pesquise na web cada data, número, nome e alegação que aparecerá na tela. Prefixe datas incertas com "c.". Rotule diagramas como "esquemático" ou "não está em escala". Mantenha os fatos poucos e precisos, com legendas de 12 palavras ou menos.
Objeto de linha condutora. Encontre um dispositivo visual que apareça em cada capítulo e dê ao filme seu título. Em SŪTRA era um fio vermelho que caminha pelas ruas, torna-se uma onda sonora, desenha o kolam e depois voa a trajetória lunar. Outros exemplos:
- História oceânica: uma única crista de onda.
- Café: um gotejamento.
- Computação: um único bit.
- Uma empresa: o gesto central de seu produto.
Título. Escolha uma palavra, idealmente no próprio idioma do assunto, e dê a ela uma definição de dicionário na tela: sūtra (n.) 1. um fio 2. uma regra 3. aquilo que mantém as coisas juntas.
Capítulos. Use 6–10, cada um construído em torno de uma ideia visual distinta. Inspire-se neste repertório:
- Um diagrama que se constrói sozinho: uma grade urbana, um mapa de rotas, uma órbita ou uma árvore genealógica de scripts.
- Números como arte: um triângulo de Pascal/Sierpiński, π se espiralando, contadores binários ou objetos em série de duplicação.
- Recursão ou fractais: um templo de templos, ou uma árvore de árvores.
- Um osciloscópio da trilha sonora real (
wave_at(f)), que é ótimo para capítulos sobre fala ou música. - Um desenho de linha contínua: um kolam ou curva de espelho, um retrato de linha contínua ou uma assinatura.
- Uma colagem cinética de palavras, como palavras emprestadas, slogans ou nomes de lugares carimbados na batida.
- Uma contagem regressiva em numerais nativos, então um lançamento ou evento, depois um impacto.
- Um encerramento: o desfecho amarra todos os capítulos como contas no fio condutor e depois retorna à imagem de abertura.
Teste cada capítulo: se pudesse ser substituído por uma foto de arquivo, repense-o.
Paleta. Use uma cor de papel, uma cor noturna e 5 tintas nomeadas de acordo com os materiais da própria cultura. SŪTRA usou índigo, sindoor, haldi, rani e mor (pavão). Outros exemplos:
- Japão: sumi, shu vermelhão, ai índigo, kincha ouro, matcha.
- México: cochonilha, milho, azul talavera, jade, calêndula.
- Espaço: vantablack, laranja sódio, vermelho hidrogênio-alfa, teal oxigênio.
Alterne capítulos claros e escuros, e faça o capítulo de pausa recolorir a cada compasso.
Sistema tipográfico. Use 4–5 famílias. Escolha uma face condensada de exibição para palavras principais, script(s) nativo(s) para o assunto, uma face mono para legendas e rótulos, e uma serifa itálica suave para linhas poéticas. Use numerais nativos para o ano atual ou contador. Caminhos úteis do google/fonts (get_font(name, path, axes)):
| função | caminho | eixos |
|---|---|---|
| exibição | ofl/anton/Anton-Regular.ttf | – |
| mono | ofl/spacemono/SpaceMono-Regular.ttf, …-Bold.ttf | – |
| serifa itálica | ofl/fraunces/Fraunces-Italic[SOFT,WONK,opsz,wght].ttf | [144,380,100,1] |
| serifa pesada | ofl/fraunces/Fraunces[SOFT,WONK,opsz,wght].ttf | [144,900,100,1] |
| Devanagari | ofl/tirodevanagarisanskrit/TiroDevanagariSanskrit-Regular.ttf | – |
| Árabe | ofl/notonaskharabic/NotoNaskhArabic[wght].ttf | [700] |
| Tamil / Bengali / Tailandês | ofl/notosanstamil/NotoSansTamil[wdth,wght].ttf (mesmo padrão) | [700,100] (ordem wght, wdth = da fonte) |
| CJK | ofl/notoserifjp/NotoSerifJP[wght].ttf | [700] |
| Brahmi | ofl/notosansbrahmi/NotoSansBrahmi-Regular.ttf | – |
Verifique a ordem dos eixos com font_axes(name). Se um caminho retornar 404, navegue em https://github.com/google/fonts/tree/main/ofl/<family> com WebFetch.
Dispositivos não convencionais. Escolha vários:
- HUD de prova de impressão: marcas de registro, amostras de tinta, um rótulo "PLACA 03 / 08 · NOME", um número de capítulo em um sistema numeral antigo, um ano atual em numerais nativos e um fio de progresso com contas de capítulo.
- Mau registro riso na tipografia principal (
riso()). - Preenchimentos de meio-tom.
- Palavras gigantes verticais.
- Texto disposto em círculo (um carimbo ou selo).
- Legendas de máquina de escrever.
- Transições escalonadas ou em íris em duas tintas.
- Gagueiras de glitch nas costuras, acompanhadas por gagueiras de áudio.
- Um flash branco na pausa.
- Grão de filme.
Nunca:
- Um cartão de título centralizado que desvanece.
- Gradientes, brilhos ou flares de lente como decoração.
- Emojis ou clip-art.
- Desenhos de personagens protegidos por direitos autorais, logotipos ou rostos de pessoas reais.
- Bandeiras nacionais usadas como decoração.
- Imagens estereotipadas.
2 · Linha do tempo em batidas
Use 90–110 BPM. Um capítulo tem 8–16 batidas. 96 BPM × 136 batidas ≈ 87 s. Escreva uma tabela primeiro, como a de SŪTRA:
| batidas | capítulo | imagem | música |
|---|---|---|---|
| 0–12 | introdução | um ponto pulsa, estica-se em uma linha, letras do título aparecem uma por batida | drone cresce, sino em cada letra, riser |
| 12–28 | 1 | grade urbana se monta; fio caminha pelas ruas | groove de tambor manual + melodia dedilhada |
| 28–40 | 2 (respiração) | osciloscópio do áudio + trança de palavras | drone, canto, esparso |
| 40–52 | 3 | roda giratória, letras carimbam, árvore genealógica | bateria mais movimentada, riser, gagueira em 51.5 |
| 52–64 | 4 | trilhas de estrelas aceleram, dígitos espiralam | pad + arpejo de sino, grande riser, flash |
| 64–80 | PAUSA | glifo herói gigante, fractal cresce 4 linhas/batida, paleta troca por compasso | kick, clap, sub grave, refrão, boom |
| 80–92 | 5 | estrutura recursiva cresce por profundidade | meio tempo; tihai cai em 92 = corte |
| 92–108 | 6 | desenho de linha contínua, depois colagem de palavras | groove suave, depois groove completo |
| 108–120 | final | contagem regressiva em numerais nativos → trajetória → impacto de pouso | sinos, estrondo, riser, boom |
| 120–136 | desfecho | contas no fio, título retorna, ponto | canto + pad + melodia, fade |
A curva de energia segue: introdução silenciosa → construção → respiração → construção → pausa na ideia mais surpreendente → meio tempo → impacto final → encerramento silencioso.
3 · Música primeiro — audio.py
from audio_kit import *
T = Track(bpm=96, beats=136, root=138.59, scale="raga_bhairavi") # root Hz; SCALES tem muitas culturas
T.drone_bed(0, 132, level=lambda b: 0.16 if 12 <= b < 120 else 0.3)
T.hand_drum(12, 28) # padrão keherwa-like
T.melody(12, [(2, 4, 1), (3, 5, 1.2, 6), (4, 7, 2.2)]) # (batida, grau, dur, deslizar para grau)
T.riser_into(64, 4); T.at("fx", boom(), 64, 0.55) # na pausa
T.groove(64, 80); T.stutter(51.5)
T.tihai(92) # frase 3× caindo exatamente na batida 92
T.mixdown("music.wav", fade_beats=(133, 136)) # também escreve env.npy / wave.npy
- Os canais são drone, perc, kick, bass, mel, fx e pad. Use
T.at(bus, signal, beat, gain, pan)para qualquer one-shot. - Instrumentos:
drone, pluck (buzz=0 para limpo), bell, voice, pad, sub, kick, clap, hat, drum_tone, drum_bass, drum_slap, drum_muted, riser, boom, rumble. - Combine a escala e os instrumentos com a cultura. Por exemplo: hijaz + tambor de moldura + dedilhado oud-ish (
buzz=0.8); in_sen + dedilhado koto-ish (buzz=0) + sinos; doriano + drone + dedilhados limpos. - Cada evento visual ganha um som. Transições ganham risers, pausas ganham um boom mais flash, cortes caem em tihai ou hits de kick, e letras aparecendo ganham sinos.
- Verifique o resultado:
- Loudness:
ffmpeg -i music.wav -af ebur128=framelog=quiet -f null -deve mostrar cerca de −16 a −14 LUFS. - Espectrograma:
ffmpeg -i music.wav -lavfi showspectrumpic=s=1600x500:scale=log:fscale=log spec.png, então Leia a imagem e verifique as formas das seções.
- Loudness:
4 · Cenas — 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")
# pré-calcular geometria no nível do módulo com um rng semeado (determinístico entre processos workers)
def s_intro(ctx, b, f): # b = batida (float), f = quadro
bg(ctx, NIGHT)
riso(ctx, lambda c: text(ctx, "TÍTULO", "anton", 400, c, W/2, 700, ax=.5, sc=back(ph(b, 4, .5))),
PAPER, INK2, dark=True)
return PAPER # cor de primeiro plano para o HUD
def hud(ctx, b, fg): reg_marks(ctx, fg, (INK1, INK2, INK3))
SCENES = [(0, s_intro), (12, s_ch1)] # (batida inicial, fn)
run(SCENES, 136, bpm=96, name="meufilme", hud=hud,
wipes=[(12, INK2, INK3)], # (batida, c1, c2[, iris_wipe])
glitches=[(51.5, 52.15)], flashes=[(64, WHITE, .5)], fade_out=134.6)
Regras das cenas:
- Sem estado. Cada valor é uma função de
b, para que os quadros possam ser renderizados em qualquer ordem entre processos. - Entradas usam
back(ph(b, start, 0.4))para estalos,eo()para deslizes eeio()para movimentos de câmera e desenhos de linha. Espace itens por 0.2–1 batida. - Vida da batida vem de
pulse(b)(1→0 a cada batida) para tamanho de salto e anel,env_at(f)para loudness ewave_at(f)para o osciloscópio. - Desenhe linhas ao vivo. Use
path_partial(ctx, pts, cumlen(pts), frac)e coloque uma conta brilhante na ponta retornada. - Composição.
- Mantenha um elemento herói, um diagrama de suporte e uma legenda por cena.
- Mantenha margens de 110 px. Cantos do HUD e a faixa inferior (y ≈ 960–1040) são reservados. Legendas vão em y ≈ 975 começando em x ≈ 560, ou na zona vazia da cena.
- Tipos grandes são grandes: glifos heróis de 300–800 px. Legendas são mono de 18–22 px.
- Contraste. Alterne cenas de papel e noturnas. Use
riso(..., dark=True)em fundos escuros. - Use
fit_size()para palavras longas em colagens etext_circle()para selos. - O HUD pode mostrar nome do capítulo, número da placa, numerais nativos via um mapa de dígitos e um fio de progresso. Quando a pasta da habilidade tem
examples/sutra/film.py, seuhud()e cenas são a referência completa; seuexamples/sutra/contact_sheet.pngmostra o visual finalizado.
5 · Quadros de teste → folha de contato → correção (não pule)
python3 film.py test 2 6 10 17 25 34 45 58 66 75 86 96 104 110 114 118 125 134
Leia sheet.png e verifique:
- Texto sobreposto ou cortado.
- Caixas tofu ou glifos ausentes. Execute
missing_glyphs(font, string)para cada fonte × string. Por exemplo, Fraunces carece de → π ∞ ṅ, e Tiro Devanagari carece de → ≈ π. - Legendas ilegíveis no tamanho de miniatura.
- Quadros em branco no início dos capítulos.
- Fatos errados ou erros de digitação.
- Batidas onde nada se move.
Itere até que cada quadro possa ser um pôster. Geralmente leva 2–3 rodadas.
6 · Renderizar, QA, entregar
nohup python3 film.py render > render.log 2>&1 &, então consulte com loops Bashsleep(timeout ≤ 590 s cada). Renderiza em paralelo em todos os núcleos. A velocidade aproximada é de 2–10 fps total, dependendo da complexidade da cena; SŪTRA levou cerca de 20 min em 2 núcleos.- As saídas são
name_full.mp4(CRF 18, qualidade total) ename.mp4, uma recompressão em duas passagens mantida abaixo de 29 MB para caber no limite de upload de 30 MB do chat. - QA:
ffprobe -count_framespara confirmar a contagem de quadros e duração.ffmpeg -i name_full.mp4 -vf "fps=1/3.5,scale=384:216,tile=5x5" -frames:v 1 final.png, então Leia.- Verifique 2–3 quadros em tamanho real, incluindo um após a compressão.
- Baixar: envie
name.mp4com SendUserFile (display: "render"); o cartão permite que o usuário reproduza e baixe. Mencione também o arquivo de qualidade total. Se a sessão estiver vinculada ao computador do usuário, ofereça para salvarname_full.mp4em uma de suas pastas viadevice_commit_files, ou envie se estiver abaixo de 30 MB. - Para re-renderizar apenas uma seção corrigida, execute
python3 film.py chunk <f0> <f1> part.mp4para os quadros afetados, então concatene e multiplexe as peças com ffmpeg comorun()faz.
Mensagem final ao usuário
Dê o título do filme e uma linha sobre seu conceito, os capítulos como uma lista compacta, o que é a música e quaisquer aproximações (datas "c.", esquemas). Não recapitule as etapas.
Apêndice A — engine.py
"""engine.py — kit de ferramentas de filme generativo estilo impressão (pycairo + Pillow/raqm + ffmpeg).
Um script de filme faz: from engine import *
em seguida define cenas como funções scene(ctx, b, f) -> fg_color (b = batida, f = quadro)
e chama run(SCENES, TOTAL_BEATS, bpm=..., wipes=..., hud=...) no final.
CLI do script de filme:
python film.py test 4 12.5 30 # renderiza PNGs nessas batidas + folha de contato (sheet.png)
python film.py render # renderização paralela completa -> film_full.mp4 + film.mp4 (<29 MB)
"""
import math, os, sys, subprocess, shutil
import numpy
…
[truncado para tradução]