Percepción del sujeto MMD para gradación de color (realzar el personaje)
0. Conclusión primero
- Para que un video de escenario MMD "realce al personaje", hay dos caminos:
- Modelado de zona de luz (recomendado, limpio): Un gradiente radial/suave elíptico centrado en el personaje — dentro de la elipse, el personaje se ilumina/calienta/agudiza/brilla; fuera, el fondo se oscurece/enfría/desatura/desenfoca ligeramente; el borde de transición es muy amplio, casi no se ve el círculo.
- Separación con máscara dura (límite más alto pero sucio): Se toma una placa de fondo con diferencia de fotogramas para obtener una máscara del personaje, y luego se gradúa por separado el personaje y el fondo para recomponerlos. La dificultad es que la sombra proyectada (cast shadow) se mueve con el personaje y está conectada a él, por lo que la máscara arrastra grandes áreas de sombra → aparecen manchas brillantes; si se erosiona forzadamente, se dañan alas y plumas.
- Solo para el producto final, elige la opción 1; cuando sea posible renderizar desde Blender/MMD una capa de personaje con canal alfa, elige la opción 2 (máxima calidad).
1. Materiales y herramientas
| Elemento | Ruta |
|---|---|
| Video fuente | D:\mmd.mp4\0002-4956.mkv (completo 165.166s) / subtítulos\0001-4956 - copia.mkv (truncado a ~137s) |
| Scripts de gradación | subtítulos\build\grade_ffmpeg.py (zona de luz/luz suave), grade_final5s.py (parámetros del producto final 5s), color_grade.py (enmascaramiento por diferencia de fotogramas, versión antigua), gothic_art.py (gradación gótica para fotogramas estáticos) |
| Scripts de verificación | avoid_layout.py (análisis de ocupación), avoid_side2.py (ocupación+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, sin cv2/aprendizaje profundo) |
2. Ruta A: Modelado de zona de luz (enfoque predeterminado de este proyecto)
2.1 Generar 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 del personaje (en composición vertical 2560x1080 aproximadamente aquí; para otro video, verificar con un fotograma primero)
rx,ry = 0.245,0.47 # semiancho/semialto (proporción); demasiado pequeño se nota el círculo, demasiado grande no hay separación
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 es más suave
Image.fromarray((mask*255).astype(np.uint8)).save("_mask_final.png")
Referencia de productos: subtítulos\salida\pruebas_gradación\_mask_final.png, subtítulos\salida\muestra_subtítulos\_mask_final.png.
2.2 Composición con ffmpeg (personaje realzado + fondo oscurecido)
Idea: Versión del personaje graduada + versión del fondo graduada, combinadas con la máscara como canal alfa:
[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 de arriba>" -map "[v]" -t 30 `
-c:v libx264 -preset medium -crf 16 -pix_fmt yuv420p OUT.mp4
Nota: Con
-loop 1 -i mask.png, la máscara es un flujo estático; después deoverlay, recuerda limitar la duración con-t. Significado de parámetros: bg brightness negativo = oscurecer, saturation<1 = desaturar,bm>0= fondo azulado (frío),gblur= ligero desenfoque; subj incluye brightness/saturación/colores cálidos/blend screenpara brillo.
2.3 Conjuntos de parámetros medidos (2560×1080, escenario MMD violeta)
| Perfil | bg brightness | bg sat | subj gain | subj sat | bloom | vignette | Efecto |
|---|---|---|---|---|---|---|---|
| A Natural | −0.035 | 0.82 | +0.030 | 1.08 | 0.20 | 0.30 | Realza ligeramente al personaje, casi no cambia la atmósfera |
| B Mejorado | −0.075 | 0.70 | +0.055 | 1.16 | 0.34 | 0.42 | Separación clara, el borde de la zona de luz apenas visible |
| Dirección final | −0.085 | 0.68 | +0.050 | 1.15 | 0.30 | 0.38 | Luz suave muy amplia, recomendado como base para todo el video |
Prueba parámetros en 5s, y una vez satisfecho, aplícalos directamente a 30s/todo el video (ffmpeg procesa todo de una vez, más rápido que fotograma por fotograma en Python).
3. Ruta B: Máscara dura (cuando se necesita una máscara más limpia)
3.1 Placa de fondo
- Extrae fotogramas de muestra de 1/2s de todo el video, redimensiona a 1280×540, mediana píxel a píxel = placa de fondo
bg_half.npy. - Comando/revisión:
subtítulos\build\color_grade.py, funciónbuild_background(). - Problema conocido: Cuando el personaje permanece en el centro durante mucho tiempo, la mediana se contamina con el personaje (ciertos píxeles en el cuerpo del personaje son a menudo del personaje) → la diferencia de fotogramas da valores pequeños en el torso → la máscara tiene agujeros. Usa
fill_holeso bbox como respaldo.
3.2 Máscara por diferencia de fotogramas (resolución 1/4, más rápida)
m = |frame_q - bg_q|.max(axis=2) > 38
m = MaxFilter(5) (dilatar) → componente conectada más grande → opcional: rellenar agujeros/desenfocar
- Se usa para análisis de ocupación/evitación (ver avoid-subtitles); para gradación, es necesario añadir una restricción de "similitud con el personaje", de lo contrario se mezclan sombras y manchas de luz.
3.3 Modos de fallo conocidos (medidos en este proyecto)
- Contaminación por sombra: La sombra del personaje se mueve con él, la máscara se vuelve contigua → aparecen manchas brillantes/bloques de color alrededor del personaje tras la gradación. Solución: No usar diferencia de fotogramas como máscara precisa; o aceptar "iluminar también la sombra" (muy extraño), o volver al modelado de zona de luz.
- Contaminación de fondo por mediana: Agujeros en la máscara donde el personaje está quieto → el torso no se gradúa, apareciendo manchas. Solución: Rellenar agujeros/usar bbox.
- Bandeado de 8 bits: Al ajustar mucho el color, aparecen bandas en el cielo/gradientes → añadir 1–3 niveles de granulado (
ffmpeg noise=alls=6:allf=t+u) o salida a 10 bits/HEVC. - Contraste de subtítulos: Al oscurecer el fondo, los textos blancos se ven más claros; si se oscurece demasiado, el personaje también se oscurece, por lo que hay que realzarlo o añadir bloom.
4. Gradaciones estilizadas (gótica/neón, para fotogramas estáticos)
- Gótico rojo-negro (ya descartado por el usuario, solo para archivo):
subtítulos\build\gothic_art.py, funcióngothic_grade(): oscurecer 0.42–0.52, desaturar 0.30–0.42, tonos medios violeta/rojo, viñeta fuerte, niebla. - Paleta de colores temática (la que el usuario finalmente quiere): blanco/hueso como principal + lavanda/violeta/rosa fucsia brillo, fondo oscuro a violeta-negro; ver constantes en
gothic_art.py(BLOOD/LAV/PINK/VIOLET). - Versión neón:
neon_wide.py(dibuja neón directamente con ASS, sin necesidad de gradación). - Aplicación rápida de color: Con la tríada
eq+colorbalance+vignettese puede convertir el escenario violeta a cualquier tono:- Azul frío:
colorbalance=bs=0.10:bm=0.12(desplazar a azul), desaturar; - Fuego cálido:
colorbalance=rs=0.10:rm=0.08,eq=saturation=1.1.
- Azul frío:
5. Lista de verificación
- Tomar 3–5 fotogramas: ¿Es natural la separación entre brillo del personaje y fondo? ¿Hay un círculo evidente en el borde de la zona de luz?
- Primeros planos/primerísimos planos: ¿La cara está sobreexpuesta? ¿El tono de piel está alterado?
- ¿Hay negros muertos/bandeado en las sombras (8 bits)?
- Vista con subtítulos: ¿El texto blanco es lo suficientemente claro?
- Verificar con
ffprobeque duración/fotogramas por segundo sean correctos; para salida a 10 bits usar-pix_fmt yuv420p10le -c:v libx265
6. Recursos
- Parámetros y comparación del producto final de 5s:
subtítulos\salida\pruebas_gradación\(A Natural/B Mejorado/Dirección final + videos de comparación) - Base de 30s:
subtítulos\salida\S4S6_30s\_base_30s.mp4(solo ligeramente oscurecido, sin gradación completa) - Direcciones probadas anteriormente pero descartadas: Gótico (
subtítulos\salida\diseño_subtítulos\G1…G3), FLARE, Neón (subtítulos\salida\muestra_subtítulos) — se archivan como referencia de parámetros.