Comment utiliser pré-multipliées au cours convolution d'image pour résoudre alpha problème fond perdu?
-
27-10-2019 - |
Question
Je suis en train d'appliquer une boîte flou à une image transparente, et que je reçois un « halo sombre » sur les bords.
Jerry Huxtable a une courte mention du problème , et une très bonne démonstration montrant le problème se produit:
Mais je, pour la vie de moi, ne peut pas comprendre comment « alpha prémultipliés » peut résoudre le problème. Maintenant, pour un exemple très simple. i ont une image de 3x3, contenant un rouge et un pixel vert:
En réalité, les pixels restants sont transparents:
Maintenant, nous allons appliquer un 3x3 Box Blur à l'image. Pour l'amour de simplicités, nous ne calculons la nouvelle valeur du pixel central. La façon dont un flou de boîte fonctionne est que, puisque nous avons un (appelé 3x3, le noyau) carré de position 9 nous prendre 1/9 chaque pixel dans le noyau, et l'ajouter jusqu'à:
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
Dans cet exemple très simplifié, les calculs deviennent très simples:
finalRed = 1/9 * 255
finalGreen = 1/9 * 255
finalBlue = 0
finalAlpha = 1/9*255 + 1/9*255
Cela me donne une valeur de couleur finale:
finalRed = 28
finalGreen = 28
finalBlue = 0
finalAlpha = 56 (22.2%)
Cette couleur est trop sombre. Lorsque j'effectue un 3px flou Box sur la même image de 3x3 pixels dans Photoshop, je reçois ce que je pense:
Ce qui est plus clair lors de l'affichage sur blanc:
En réalité, je joue une boîte flou sur un bitmap contenant du texte transparent, et le texte gagne le noir tell-tale autour des franges:
je commence avec un Bitmap GDI + qui est en format PixelFormat32bppARGB
Comment lors de l'application du noyau de convolution 3x3 utiliser "alpha prémultipliés"?
Toute réponse devra inclure de nouvelles forumla, depuis:
final = 1/9*(pixel1+pixel2+pixel3...+pixel9)
me faire la mauvaise réponse.
Modifier Un exemple plus simple est:
i va effectuer ce calcul avec des valeurs de couleur et alpha dans la gamme de 0..1:
Je vais appliquer la boîte filtre de convolution de flou au pixel milieu:
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)
Ce qui donne un vert foncé assez transparent.
Ce qui est pas ce que j'attends de voir. Et par comparaison Blur Box de Photoshop est:
Si je suppose (0, 0.33, 0, 0.33)
est l'alpha prémultipliés, et non se multiplient, je reçois:
(0, 1, 0, 0.33)
Ce qui semble bon pour mon exemple tout opaque; mais je ne sais pas quoi faire quand je commence à impliquer les pixels partiellement transparents.
Voir aussi
La solution
tkerwin a déjà fourni réponse correcte, mais il semble avoir besoin d'autres explications.
Les mathématiques que vous avez montré dans votre question est tout à fait correcte jusqu'à, jusqu'à la fin. Il est là que vous manque une étape - les résultats sont encore en mode alpha prémultipliés, et doit être « non multipliée » au format PixelFormat32bppARGB. L'inverse d'une multiplication est une division, donc:
finalRed = finalRed * 255 / finalAlpha;
finalGreen = finalGreen * 255 / finalAlpha;
finalBlue = finalBlue * 255 / finalAlpha;
Vous avez exprimé une préoccupation que le fossé pourrait créer un résultat qui est follement hors de portée, mais cela ne se produira pas. Si vous tracez le calcul, vous remarquerez que les valeurs de rouge, vert et bleu ne peut pas être supérieure à la valeur alpha, en raison de l'étape de pré-multiplication. Si vous utilisez un filtre plus compliqué qu'une simple boîte flou, il est peut-être une possibilité, mais ce serait le cas même si vous ne l'utilisez alpha! La réponse correcte consiste à serrer le résultat, en tournant les nombres négatifs en 0 et toute valeur supérieure à 255 255 en.
Autres conseils
En suivant les conseils de votre lien, vous prémultiplier avant le flou et non prémultiplier après le flou. Dans votre exemple, pré-multiplication ne fait rien, car il n'y a pas de pixels semi-transparents. Vous avez le flou, alors vous avez besoin vous non prémultiplier en faisant (en supposant des valeurs de couleur normalisées de 0 à 1):
RGB' = RGB/A (if A is > 0)
A' = A
vous obtiendrez une image finale floue non prémultipliés.
Les deux réponses apparaissent mal ici.
Utiliser le mode de fusion approprié, qui, selon Porter Duff est:
FG.RGB + (1.0 - FG.Alpha)*BG.RGB
Je ne sais pas où le reste des réponses viennent, mais wow.
L'alpha codant dicte le fonctionnement au-dessus de.