Как использовать предварительное умножение во время свертки изображения для решения проблемы с альфа-кровотечением?

Я пытаюсь применить размытие ящика к прозрачному изображению, и я получаю "темный ореол" по краям.

Jerry Huxtable имеет короткое упоминание о проблеме, и очень хорошая демонстрация, показывающая проблему:

enter image description here

Но я, для жизни меня, не могу понять, как "пре-умноженная альфа" может решить проблему. Теперь для очень простого примера. У меня есть изображение 3x3, содержащее один красный и один зеленый пиксель:

enter image description here

В действительности остальные пиксели прозрачны:

enter image description here

Теперь мы применим к изображению 3x3 Box Blur. Для простоты мы вычислим только новое значение центрального пикселя. Способ размытия ящика заключается в том, что, поскольку у нас есть 9-позиционный квадрат (3x3, называемый ядром), мы берем 1/9-е из каждого пикселя в ядре и добавляем его:

enter image description here

Итак,

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

В этом очень упрощенном примере вычисления становятся очень простыми:

finalRed =   1/9 * 255
finalGreen = 1/9 * 255
finalBlue =  0
finalAlpha = 1/9*255 + 1/9*255

Это дает мне окончательное значение цвета:

finalRed =   28
finalGreen = 28
finalBlue =  0
finalAlpha = 56 (22.2%)

enter image description here

Этот цвет слишком темный. Когда я выполняю размытие ящика 3px на том же 3x3 пиксельном изображении в Photoshop, я получаю то, что ожидаю:

enter image description here

Чем яснее отображается над белым:

enter image description here


В реальности я выполняю размытие ящика на растровом изображении, содержащем прозрачный текст, и текст получает темную темноту вокруг полос:

enter image description here

Я начинаю с GDI + Bitmap, который находится в формате PixelFormat32bppARGB


Как использовать "предварительно умноженную альфу" при применении ядра свертки 3x3?

Любой ответ должен включать новую forumla, так как:

final = 1/9*(pixel1+pixel2+pixel3...+pixel9)

Получает неверный ответ.


Изменить: Более простой пример:

Я буду выполнять эту математику с цветовыми и альфа-значениями в диапазоне 0..1:

enter image description here

Я собираюсь применить фильтр свертки размытия ящика к среднему пикселю:

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)

Что дает довольно прозрачный темно-зеленый цвет.

enter image description here

Это не то, что я ожидаю увидеть. И в сравнении Photoshop Box Blur:

enter image description here

Если я предполагаю, что (0, 0.33, 0, 0.33) предварительно умножается на альфу и не размножается, я получаю:

(0, 1, 0, 0.33)

<Т411 >

Что выглядит правильно для моего непрозрачного примера; но я не знаю, что делать, когда я начинаю привлекать частично прозрачные пиксели.

См. также

Ответ 1

tkerwin уже предоставил правильный ответ, но, похоже, нуждается в дальнейшем объяснении.

Математика, которую вы показали в своем вопросе, абсолютно правильная, вплоть до конца. Именно там вам не хватает шага - результаты все еще находятся в предмножеством альфа-режима и должны быть "unmultiplied" обратно в формат PixelFormat32bppARGB. Противоположностью умножения является деление, таким образом:

finalRed = finalRed * 255 / finalAlpha;
finalGreen = finalGreen * 255 / finalAlpha;
finalBlue = finalBlue * 255 / finalAlpha;

Вы выразили озабоченность тем, что разделение может создать результат, который находится вне пределов досягаемости, но этого не произойдет. Если вы проследите математику, вы заметите, что значения красного, зеленого и синего не могут быть больше альфа-значения из-за шага предварительного умножения. Если вы использовали более сложный фильтр, чем простое размытие ящика, это может быть возможностью, но это было бы так, даже если вы не использовали альфа! Правильный ответ заключается в том, чтобы зажать результат, превратив отрицательные числа в 0 и ничего больше 255 на 255.

Ответ 2

Следуя советам по вашей ссылке, вы предварительно размножаетесь до размытия и не домножения после размытия. В вашем примере предварительное умножение фактически ничего не делает, поскольку нет полупрозрачных пикселей. Вы сделали размытие, тогда вам нужно, чтобы вы не премноделились, делая (при условии, что нормализованные значения цвета от 0 до 1):

RGB' = RGB/A  (if A is > 0)
A' = A

Это даст вам не-умноженное размытое окончательное изображение.

Ответ 3

Оба ответа здесь кажутся неправильными.

Используйте правильный режим смешивания, который в соответствии с Porter Duff:

FG.RGB + (1.0 - FG.Alpha)*BG.RGB

Не уверен, откуда остальные ответы, но вау.

Альфа-кодирование диктует операцию over.