Как использовать предварительное умножение во время свертки изображения для решения проблемы альфа-кровотечения?
-
27-10-2019 - |
Вопрос
я пытаюсь применить размытие прямоугольника к прозрачному изображению, и у меня получается "темный ореол" по краям.
Джерри Хакстейбл содержит краткое упоминание о проблеме, и очень хорошая демонстрация , показывающая , что проблема возникла:
Но я, хоть убей, не могу понять, как это произошло ".предварительно умноженный альфа-код" могу решить проблему.Теперь перейдем к очень простому примеру.у меня есть изображение размером 3x3, содержащее один красный и один зеленый пиксель:
На самом деле остальные пиксели прозрачны:
Теперь мы применим к изображению прямоугольное размытие размером 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%)
Этот цвет слишком темный.Когда я выполняю размытие прямоугольника размером 3 пикселя на том же изображении размером 3x3 пикселя в Photoshop, я получаю то, что ожидаю:
Который становится четче при отображении поверх белого:
На самом деле я выполняю размытие прямоугольника на растровом изображении, содержащем прозрачный текст, и текст приобретает характерный темный оттенок по краям:
я начинаю с растрового изображения GDI +, которое находится в 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 - это:
Если я предполагаю, что (0, 0.33, 0, 0.33)
является предварительно умноженной альфой, и, не умножая ее, я получаю:
(0, 1, 0, 0.33)
Что выглядит правильно для моего полностью непрозрачного примера;но я не знаю, что делать, когда я начинаю использовать частично прозрачные пиксели.
Смотрите также
Решение
у ткервина есть уже предоставлен правильный ответ, но это, похоже, нуждается в дальнейшем объяснении.
Математика, которую вы показали в своем вопросе, абсолютно верна, вплоть до самого конца.Именно здесь вы пропускаете шаг - результаты все еще находятся в предварительно умноженном альфа-режиме и должны быть "неумножены" обратно в формат PixelFormat32bppARGB.Противоположностью умножению является деление, таким образом:
finalRed = finalRed * 255 / finalAlpha;
finalGreen = finalGreen * 255 / finalAlpha;
finalBlue = finalBlue * 255 / finalAlpha;
Вы выразили обеспокоенность тем, что разрыв может привести к результату, который сильно выходит за рамки допустимого, но этого не произойдет.Если вы проследите за математикой, то заметите, что значения красного, зеленого и синего не могут быть больше альфа-значения из-за шага предварительного умножения.Если бы вы использовали более сложный фильтр, чем простое размытие поля, это могло бы быть возможно, но это имело бы место, даже если бы вы не использовали alpha!Правильный ответ - зафиксировать результат, превратив отрицательные числа в 0, а все, что больше 255, - в 255.
Другие советы
Следуя совету в вашей ссылке, вы предварительно умножаете перед размытием и отменяете предварительное умножение после размытия.В вашем примере предварительное умножение фактически ничего не делает, поскольку полупрозрачных пикселей нет.Вы сделали размытие, затем вам нужно отменить предварительное умножение (при условии, что нормализованные значения цвета от 0 до 1):
RGB' = RGB/A (if A is > 0)
A' = A
В результате вы получите размытое конечное изображение без предварительного умножения.
Оба ответа здесь кажутся неправильными.
Используйте правильный режим наложения, который, по словам Портера Даффа, является:
FG.RGB + (1.0 - FG.Alpha)*BG.RGB
Не уверен, откуда берутся остальные ответы, но ничего себе.
Альфа-кодировка диктует операцию перезаписи.