Как использовать предварительно умноженный во время свертки изображения для решения проблемы альфа-кровотечения?
Я пытаюсь применить размытие коробки к прозрачному изображению, и я получаю "темный ореол" по краям.
У Джерри Хакстейбла есть краткое упоминание о проблеме, и очень хорошая демонстрация, показывающая, что проблема возникает:
Но я, по жизни моей, не могу понять, как "предварительно умноженная альфа" может решить проблему. Теперь для очень простого примера. у меня есть изображение 3x3, содержащее один красный и один зеленый пиксель:
На самом деле оставшиеся пиксели прозрачны:
Теперь мы применим Box Blur 3x3 к изображению. Для простоты мы рассчитаем только новое значение центрального пикселя. Способ размывания прямоугольника заключается в том, что, поскольку у нас есть квадрат с 9 позициями (3x3, называемый ядром), мы берем 1/9 каждого пикселя в ядре и складываем его:
Так
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%)
Этот цвет слишком темный. Когда я выполняю размытие 3px Box на том же изображении 3x3 пикселя в Photoshop, я получаю то, что ожидаю:
Что яснее при отображении на белом фоне:
На самом деле я выполняю размытие окна на растровом изображении, содержащем прозрачный текст, и текст приобретает темную окраску по краям:
я начинаю с GDI+ Bitmap, который находится в PixelFormat32bppARGB
формат
Как использовать "предварительно умноженную альфу" при применении сверточного ядра 3x3?
Любой ответ должен будет включать новые форум, так как:
final = 1/9*(pixel1+pixel2+pixel3...+pixel9)
Получает мне неправильный ответ.
Изменить: более простой пример:
Я выполню эту математику со значениями цвета и альфа в диапазоне 0..1:
Я собираюсь применить фильтр свертки коробки размытия к среднему пикселю:
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)
Что дает довольно прозрачный темно-зеленый.
Что не то, что я ожидаю увидеть. И для сравнения Photoshop's Box Blur это:
Если я предполагаю (0, 0.33, 0, 0.33)
предварительно умножить альфа, и умножить его, я получаю:
(0, 1, 0, 0.33)
Который выглядит правильным для моего непрозрачного примера; но я не знаю, что делать, когда я начинаю использовать частично прозрачные пиксели.
Смотрите также
4 ответа
tkerwin уже дал правильный ответ, но, похоже, требуется дальнейшее объяснение.
Математика, которую вы показали в своем вопросе, абсолютно верна, вплоть до конца. Именно здесь вы пропускаете шаг - результаты все еще находятся в предварительно умноженном альфа-режиме и должны быть "умножены" обратно в формат PixelFormat32bppARGB. Противоположность умножения является делением, таким образом:
finalRed = finalRed * 255 / finalAlpha;
finalGreen = finalGreen * 255 / finalAlpha;
finalBlue = finalBlue * 255 / finalAlpha;
Вы выразили обеспокоенность тем, что разделение может создать результат, который сильно выходит за пределы диапазона, но этого не произойдет. Если вы проследите математику, вы заметите, что значения красного, зеленого и синего не могут быть больше значения альфа из-за шага предварительного умножения. Если бы вы использовали более сложный фильтр, чем простое размытие рамки, это могло бы быть возможно, но это было бы так, даже если вы не использовали альфа! Правильный ответ - зафиксировать результат, превратив отрицательные числа в 0 и все, что больше 255 в 255.
Следуя совету в вашей ссылке, вы предварительно умножаете перед размытием и не умножаете перед размытием. В вашем примере предварительное умножение фактически ничего не делает, так как нет полупрозрачных пикселей. Вы сделали размытие, затем вам нужно предварительно не умножать, выполняя (предполагая нормализованные значения цвета от 0 до 1):
RGB' = RGB/A (if A is > 0)
A' = A
Это даст вам предварительно не умноженное размытое конечное изображение.
Оба ответа здесь кажутся неправильными.
Используйте правильный режим смешивания, который в соответствии с Porter Duff:
FG.RGB + (1.0 - FG.Alpha)*BG.RGB
Не уверен, откуда остальные ответы, но вау.
Альфа-кодирование диктует операцию over.
Всем, кто в будущем будет читать эту замечательную ветку и пытаться отменить предварительное умножение цветов при многопроходном размытии — делайте это только один раз, на последнем проходе. Научился этому после 4 часов проб и ошибок.