C ++:调整图像的HSL而不剪裁

时间:2017-12-26 03:48:18

标签: c++ image hsl

我正在尝试为图像编程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(.2)

此函数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,则函数看起来像:

f(x) = x ^ log_1/2(.97)

(点(0.5,0.97),我忘了标记它。)

这意味着在开始看到任何实际差异之前,你必须将饱和度调低到.001。我用这个测试图像遇到了这个问题:

原始(饱和度= 0.5):

饱和度= 0.001

饱和度= 0.0

此时我被困住了。 有没有办法调整饱和度和亮度而不会丢失细节或剪裁?也许还有另一种可以使用的弯曲方法?是否有一种我无法找到的标准方法? 提前谢谢。

使用Desmos在线图表计算器制作的图表。

1 个答案:

答案 0 :(得分:0)

好吧,我仍然不知道任何“标准”的实现方法,但我发现三角曲线即使对于强烈饱和的图像也能很好地工作。

使用两个函数a*sin(x*PI) where {0 <= x <= 0.5}1+(a-1)*sin(x*PI) where {.5 < x <= 1.0},可以创建如下曲线:

初始饱和度(a)= 0.2:

Trig 0.2

初始饱和度= 0.97:

Trig 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调整滤波器不同。如果有人碰巧知道“标准”或通常的处理方式,请分享!