我正在尝试为图像(在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)点,我忘了标记它)。
这意味着在开始看到任何实际差异之前,您必须将饱和度降低至.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调整滤波器不同。如果有人碰巧知道“标准”或通常的处理方式,请分享!