¿Cómo usar pre-multiplicado durante la convolución de la imagen para resolver el problema de sangrado alfa?
-
27-10-2019 - |
Pregunta
Estoy tratando de aplicar un borde de caja a una imagen transparente, y estoy obteniendo un "halo oscuro" alrededor de los bordes.
Jerry Huxtable tiene una breve mención del problema, y una muy buena demostración que muestra el problema sucede:
Pero yo, por mi vida, no puedo entender cómo "alfa previamente multiplicado"Puede solucionar el problema. Ahora para un ejemplo muy simple. Tengo una imagen 3x3, que contiene un píxel rojo y un verde:
En realidad, los píxeles restantes son transparentes:
Ahora aplicaremos un desenfoque de caja de 3x3 a la imagen. Por simplicidades, solo calcularemos el nuevo valor del píxel central. La forma en que funciona un borde de caja es que, dado que tenemos un cuadrado de 9 posición (3x3, llamado núcleo), tomamos 1/9 de cada píxeles en el núcleo, y lo agregamos:
Asi que
finalRed = 1/9 * red1 + 1/9 * red2 + 1/9 * red3+ ... + 1/9 * red9
finalGreen = 1/9*green1 + 1/9*green2 + 1/9*green3+ ... + 1/9*green9
finalBlue = 1/9* blue1 + 1/9* blue2 + 1/9* blue3+ ... + 1/9* blue9
finalAlpha = 1/9*alpha1 + 1/9*alpha2 + 1/9*alpha3+ ... + 1/9*alpha9
En este ejemplo muy simplificado, los cálculos se vuelven muy simples:
finalRed = 1/9 * 255
finalGreen = 1/9 * 255
finalBlue = 0
finalAlpha = 1/9*255 + 1/9*255
Esto me da un valor de color final de:
finalRed = 28
finalGreen = 28
finalBlue = 0
finalAlpha = 56 (22.2%)
Este color es demasiado oscuro. Cuando realizo un desenfoque de 3px en la misma imagen de 3x3 píxeles en Photoshop, obtengo lo que espero:
Que es más claro cuando se muestra sobre blanco:
En realidad, estoy realizando un cuadro desenfoque en un mapa de bits que contiene texto transparente, y el texto gana el oscuro y revelador alrededor de las franjas:
Estoy empezando con un mapa de bits GDI+ que está en PixelFormat32bppARGB
formato
¿Cómo uso el "alfa pre-multiplicado" al aplicar el núcleo de convolución 3x3?
Cualquier respuesta tendrá que incluir nueva Forumla, ya que:
final = 1/9*(pixel1+pixel2+pixel3...+pixel9)
Me está dando la respuesta incorrecta.
Editar: Un ejemplo más simple es:
Realizaré esta matemática con color y valores alfa en el rango de 0..1:
Voy a aplicar el filtro de convolución blur de caja al píxel medio:
ARGB'
= 1/9 * (0,1,0,1) + 1/9 * (0,0,0,0) + 1/9 * (0,0,0,0) +
1/9 * (0,1,0,1) + 1/9 * (0,0,0,0) + 1/9 * (0,0,0,0) +
1/9 * (0,1,0,1) + 1/9 * (0,0,0,0) + 1/9 * (0,0,0,0);
= (0, 0.11, 0, 0.11) + (0,0,0,0) + (0,0,0,0) +
(0, 0.11, 0, 0.11) + (0,0,0,0) + (0,0,0,0) +
(0, 0.11, 0, 0.11) + (0,0,0,0) + (0,0,0,0)
= (0, 0.33, 0, 0.33)
Que da un verde oscuro bastante transparente.
Que no es lo que espero ver. Y en comparación, la caja de la caja de Photoshop es:
Si asumo (0, 0.33, 0, 0.33)
es alfa pre-multiplicado, y sin múltiples, obtengo:
(0, 1, 0, 0.33)
Que se ve bien para mi ejemplo de todo opaco; Pero no sé qué hacer cuando empiezo a involucrar píxeles parcialmente transparentes.
Ver también
Solución
Tkerwin tiene ya proporcioné la respuesta correcta, pero parece necesitar más explicación.
Las matemáticas que ha mostrado en su pregunta son absolutamente correctas, hasta el final. Es allí donde te estás perdiendo un paso: los resultados todavía están en un modo alfa pre -multiplicado, y deben estar "sin múltiples" al formato PixAlformat32BPParGB. Lo contrario de una multiplicada es una división, por lo tanto:
finalRed = finalRed * 255 / finalAlpha;
finalGreen = finalGreen * 255 / finalAlpha;
finalBlue = finalBlue * 255 / finalAlpha;
Has expresado la preocupación de que la división podría crear un resultado que esté muy fuera de rango, pero eso no sucederá. Si rastrea las matemáticas, notará que los valores rojos, verdes y azules no pueden ser mayores que el valor alfa, debido al paso previo a la multiplicación. Si estuviera usando un filtro más complicado que un simple desenfoque de caja, podría ser una posibilidad, ¡pero ese sería el caso incluso si no usara Alpha! La respuesta correcta es sujetar el resultado, convirtiendo los números negativos en 0 y cualquier cosa mayor que 255 en 255.
Otros consejos
Siguiendo el consejo en su enlace, pre-multiplicado antes de desenfoque y sin ser multiplicado después de desenfoque. En su ejemplo, el pre-multiplicador en realidad no hace nada, ya que no hay píxeles semitransparentes. Hiciste el desenfoque, luego necesitas que no te presentes (suponiendo valores de color normalizados de 0 a 1):
RGB' = RGB/A (if A is > 0)
A' = A
Esto le dará una imagen final borrosa no múltiple.
Ambas respuestas parecen mal aquí.
Use el modo de mezcla adecuado, que según Porter Duff es:
FG.RGB + (1.0 - FG.Alpha)*BG.RGB
No estoy seguro de dónde provienen el resto de las respuestas, pero wow.
La codificación alfa dicta la operación excesiva.