MMD Gradação de Cor com Consciência de Sujeito (Destacar Pessoa)
0. Conclusão Primeiro
- Vídeos de palco MMD que querem "destacar a pessoa", dois caminhos:
- Modelagem de zona de luz (recomendado, limpo): Gradiente radial/elíptico suave centrado na pessoa — dentro da elipse, a pessoa fica mais clara/quente/nítida/brilhante; fora, o fundo escurece/esfria/dessatura/levemente desfoca; a transição da borda é muito larga, quase não se vê o "círculo".
- Separação rígida por máscara (potencial maior, mas sujo): Usa diferença de quadros com um fundo de referência para obter a máscara da pessoa, gradua pessoa e fundo separadamente e depois compõe. A dificuldade é que a sombra projetada (cast shadow) se move com a pessoa e se conecta a ela, fazendo a máscara incluir grandes áreas de sombra → aparecem manchas claras; corroer à força remove asas/penas.
- Apenas para vídeo finalizado escolha 1; se puder re-renderizar do Blender/MMD com camada de personagem com canal alfa, escolha 2 (maior qualidade).
1. Materiais e Ferramentas
| Item | Caminho |
|---|---|
| Vídeo fonte | D:\mmd.mp4\0002-4956.mkv (completo 165.166s) / 字幕\0001-4956 - 副本.mkv (truncado para ~137s) |
| Scripts de gradação | 字幕\build\grade_ffmpeg.py (zona de luz/suave), grade_final5s.py (parâmetros do produto final 5s), color_grade.py (máscara por diferença antiga), gothic_art.py (gravação gótica em quadro estático) |
| Scripts de validação | avoid_layout.py (análise de ocupação), avoid_side2.py (ocupação+bbox) |
| ffmpeg | C:\Users\35530\AppData\Local\Microsoft\WinGet\Packages\Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe\ffmpeg-9.0-full_build\bin\ffmpeg.exe |
| Python | C:\Users\35530\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\python\python.exe (PIL/numpy, sem cv2/deep learning) |
2. Rota A: Modelagem de Zona de Luz (Padrão deste Projeto)
2.1 Gerar Máscara de Luz Suave (PNG único)
import numpy as np
from PIL import Image
W,H = 2560,1080
yy,xx = np.mgrid[0:H,0:W].astype(np.float32)
cx,cy = 0.515,0.565 # centro da pessoa (2560x1080 vertical aprox. aqui; troque de vídeo, verifique com quadro)
rx,ry = 0.245,0.47 # meia largura/meia altura (proporção); muito pequeno vê-se o círculo, muito grande sem separação
q = ((xx-W*cx)/(W*rx))**2 + ((yy-H*cy)/(H*ry))**2
mask = np.clip(1.0-q,0,1)**0.55 # 0.5–0.7 é mais suave
Image.fromarray((mask*255).astype(np.uint8)).save("_mask_final.png")
Consulte produtos finais: 字幕\输出\调色测试\_mask_final.png, 字幕\输出\字幕小样\_mask_final.png.
2.2 Composição ffmpeg (Pessoa Elevada + Fundo Escurecido)
Ideia: Versão graduada da pessoa + versão graduada do fundo, combinar alfa com a máscara:
[0:v]split=2[sv][bv];
[sv]eq=brightness=0.050:contrast=1.06:saturation=1.15,
colorbalance=rs=0.02:bs=-0.02:rm=0.05:bm=-0.06:rh=0.02:bh=-0.02,
split=2[sm][sh];[sh]gblur=sigma=14[blur];
[sm][blur]blend=all_mode=screen:all_opacity=0.34[sub];
[sub]format=yuva420p[suba];
[1:v]format=gray[gm];[suba][gm]alphamerge[subj];
[bv]eq=brightness=-0.045:contrast=1.07:saturation=0.87,
colorbalance=rm=-0.03:bm=0.05,gblur=sigma=0.6[bg];
[bg][subj]overlay=0:0,format=yuv420p[v]
Comando:
& $ff -v error -y -ss 36.5 -t 30 -i src.mkv -loop 1 -i _mask_final.png `
-filter_complex "<graph acima>" -map "[v]" -t 30 `
-c:v libx264 -preset medium -crf 16 -pix_fmt yuv420p OUT.mp4
Nota: Com
-loop 1 -i mask.png, a máscara é um stream estático; apósoverlay, lembre-tpara limitar a duração. Significado dos parâmetros: bg brightness negativo = escurecer, saturation<1 = dessaturar,bm>0= fundo azulado (frio),gblur= leve desfoque; subj: brightness/saturação/cores quentes/blend screenpara brilho.
2.3 Conjuntos de Parâmetros Medidos (2560×1080, palco roxo MMD)
| Conjunto | bg brightness | bg sat | subj gain | subj sat | bloom | vignette | Efeito |
|---|---|---|---|---|---|---|---|
| A Natural | −0.035 | 0.82 | +0.030 | 1.08 | 0.20 | 0.30 | Leve destaque, quase não altera atmosfera |
| B Reforçado | −0.075 | 0.70 | +0.055 | 1.16 | 0.34 | 0.42 | Separação clara, borda da zona ligeiramente visível |
| Direção Final | −0.085 | 0.68 | +0.050 | 1.15 | 0.30 | 0.38 | Luz suave muito larga, recomendado para filme inteiro |
Teste parâmetros em 5s, se satisfatório aplique diretamente em 30s/vídeo inteiro (ffmpeg roda uma vez, mais rápido que Python quadro a quadro).
3. Rota B: Máscara Rígida (Quando Máscara Mais Limpa é Necessária)
3.1 Plano de Fundo
- Amostre quadros a cada 1/2s do vídeo inteiro, reduza para 1280×540, mediana pixel a pixel = plano de fundo
bg_half.npy. - Comando/revisão:
字幕\build\color_grade.pybuild_background(). - Problemas conhecidos: Se a pessoa fica parada no centro por muito tempo, a mediana é "contaminada" pela pessoa (certos pixels na pessoa aparecem frequentemente) → diferença de quadros pequena no tronco → máscara com buracos. Use
fill_holesou bbox como fallback.
3.2 Máscara por Diferença de Quadros (1/4 resolução é mais rápida)
m = |frame_q - bg_q|.max(axis=2) > 38
m = MaxFilter(5) (expandir) → maior componente conexo → opcional preencher buracos/desfocar borda
- Usado para análise de ocupação/desvio (veja avoid-subtitles); para gradação, precisa de restrição extra de "similaridade à pessoa", senão sombras/manchas claras se misturam.
3.3 Modos de Falha Conhecidos (Medidos neste Projeto)
- Contaminação por projeção: Sombra da pessoa se move com ela, máscara contínua → após gradação, aparecem manchas claras/blocos de cor ao redor da pessoa. Solução: não use diferença de quadros como matte preciso; ou aceite "iluminar junto com a sombra" (estranho), ou volte para modelagem de zona de luz.
- Contaminação do fundo mediano: Máscara tem buracos onde a pessoa parou → tronco não graduado, aparecendo manchado. Solução: preencher buracos/usar bbox.
- Bandeamento 8bit: Após grande ajuste de cor, céu/gradientes podem mostrar bandas → adicione 1–3 níveis de grão (ffmpeg
noise=alls=6:allf=t+u) ou saída 10bit/HEVC. - Contraste da legenda: Fundo escurecido torna texto branco mais claro; se escurecer demais e a pessoa também escurecer, precisa realçar a pessoa ou adicionar bloom.
4. Gravação Estilizada Gótica/Neón (Para Quadros Estáticos)
- Gótico vermelho-preto (já rejeitado pelo usuário, apenas arquivado):
字幕\build\gothic_art.pygothic_grade(): escurecer 0.42–0.52, dessaturar 0.30–0.42, tons médios roxo/vermelho, vinheta forte, névoa. - Paleta de tema (que o usuário finalmente queria): Branco/osso branco principal + lavanda/rosa magenta brilho, fundo escuro roxo-preto; veja constantes de gradação em
gothic_art.py(BLOOD/LAV/PINK/VIOLET). - Versão neón:
neon_wide.py(ASS desenha neón diretamente, sem gradação). - Aplicação rápida de tom: Três itens
eq+colorbalance+vignettepodem transformar palco roxo em qualquer tom:- Ciano frio:
colorbalance=bs=0.10:bm=0.12:bm...(desvio azul), dessaturar; - Fogo quente:
colorbalance=rs=0.10:rm=0.08,eq=saturation=1.1.
- Ciano frio:
5. Lista de Verificação
- Amostre 3–5 quadros: separação entre brilho da pessoa e fundo natural, borda da zona de luz sem círculo visível
- Close-up/primeiro plano: rosto superexposto? tom de pele alterado?
- Áreas escuras: preto morto/bandeamento (8bit)?
- Visualizar com legenda: texto branco claro o suficiente?
- Saída
ffprobeduração/framerate corretos; saída 10bit use-pix_fmt yuv420p10le -c:v libx265
6. Recursos
- Parâmetros e comparação do produto final de 5s:
字幕\输出\调色测试\(A natural/B reforçado/direção final + vídeo de comparação) - Base de 30s:
字幕\输出\S4S6_30s\_base_30s.mp4(apenas levemente escurecido, sem gradação completa) - Direções tentadas anteriormente mas rejeitadas: gótico (
字幕\输出\字幕设计\G1…G3), FLARE explosão, neón (字幕\输出\字幕小样) — arquivadas para referência de parâmetros.