Я пытаюсь запрограммировать фильтр Hue / Saturation / Lightness для изображения (в C ++). Преобразования RGB-> HSL работают нормально, но моя проблема возникает при учете того факта, что каждый пиксель имеет различную начальную насыщенность и яркость.
Для каждого пикселя я рассчитал исходный HSL и получил значения HSL в качестве входных данных фильтра (в диапазоне [0, 1]). Я хотел бы сделать 0,5 по умолчанию для всех (выходной сигнал совпадает с входным), 0,0 для насыщенности означает оттенки серого или полностью черный для яркости, а 1,0 означает полностью насыщенные или полностью белые. В отличие от насыщенности и яркости, часть оттенка проста: просто добавьте и оберните в диапазоне [0, 1].
Тогда возникает задача: как мы преобразуем значения насыщенности и яркости каждого пикселя на основе входных данных фильтра?
Одно решение, которое пришло на ум:
Out.H = Wrap(In.H + Filter.H - 0.5f, 0.0f, 1.0f);
Out.S = std::clamp(In.S * Filter.S * 2.0f, 0.0f, 1.0f);
Out.L = std::clamp(In.L * Filter.L * 2.0f, 0.0f, 1.0f);
куда Out
это выход, In
является исходным изображением, и Filter
это настройки фильтра. Но если исходное изображение сильно контрастирует с ним, первоначально светлые части изображения быстро обрезаются (фиксируются на сплошной 1,0f), теряя детализацию, в то время как более темные части могут быть едва заметны. Так что вместо этого я подумал, что, возможно, будут некоторые кривые, которые можно применить к нему, которые предотвратят это отсечение, при этом позволяя 0.5f вернуть исходное значение. Я подумал об использовании функции мощности и расчета необходимой мощности. Я придумал:
f(x) = x ^ log_1/2(a)
куда a
это значение на входном пикселе (значение функции при x = 0.5f). Например:
Эта функция, f(x) = x ^ log_1/2(0.2)
, проходит через (0,0), (0,5, 0,2) и (1, 1). Таким образом, если бы насыщенность входного пикселя составляла 0,2f, а входной сигнал насыщенности фильтра от пользователя был равен x, то выполнение этой функции сработало бы. Вам нужно заменить 0.2 в функции на то, какой будет насыщенность входного пикселя. Предположительно, то же самое можно сделать для легкости. Таким образом, код становится:
Out.H = Wrap(In.H + Filer.H - 0.5f, 0.0f, 1.0f);
Out.S = pow(In.S, log(Filter.S) / log(0.5f));
Out.L = pow(In.L, log(Filter.L) / log(0.5f));
Это хорошо работает для изображений со средней насыщенностью и яркостью. Но, если он изначально очень высок или очень низок, этот подход может означать, что функция сбрасывается очень близко к 0 или 1. Например, если насыщенность изначально равна 0,97, функция выглядит следующим образом:
(С точкой в (0.5, 0.97) я забыл отметить это).
Это означает, что вам придется уменьшить насыщенность до уровня, равного 0,001, прежде чем вы начнете видеть какую-либо фактическую разницу. Я столкнулся с этой проблемой с этим тестовым изображением:
Оригинал (насыщенность = 0,5):
Насыщенность = 0,001
Насыщенность = 0.0
На данный момент я застрял. Есть ли способ отрегулировать насыщенность и яркость, не теряя детали или отсечения? Возможно, есть другой метод изгиба, который можно использовать? Есть ли стандартный способ сделать это, что мне не удалось найти?
Заранее спасибо.
Графики сделаны с Desmos онлайн графический калькулятор.
Хорошо, я до сих пор не знаю ни одного «стандартного» способа достижения этой цели, но я обнаружил, что тригонометрические кривые работают относительно хорошо даже для сильно насыщенных изображений.
Используя две функции a*sin(x*PI) where {0 <= x <= 0.5}
а также 1+(a-1)*sin(x*PI) where {.5 < x <= 1.0}
можно создать кривые следующим образом:
Начальная насыщенность (a
) = 0,2:
Начальная насыщенность = 0,97:
Код:
Out.H = Wrap(In.H + Filter.H - 0.5f, 0.0f, 1.0f);
Out.S = (Filter.S <= 0.5f) ? (In.S * sin(Filter.S * PI)) : (1.0f - (1.0f - In.S) * sin(Filter.S * PI));
Out.L = (Filter.L <= 0.5f) ? (In.L * sin(Filter.L * PI)) : (1.0f - (1.0f - In.L) * sin(Filter.L * PI));
Понятно, что это не идеально, но на данный момент кажется достаточным, и позволяет намного чище регулировать изображение, чем функция мощности. Это также означает, что разные пиксели теряют и набирают насыщенность с разной скоростью, чем раньше, возможно, иначе, чем большинство фильтров регулировки HSL. Если кто-то знает «стандартный» или обычный способ обращения с вещами, пожалуйста, поделитесь!
Других решений пока нет …