【发布时间】:2017-12-26 03:48:17
【问题描述】:
我正在尝试为图像(在 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 在线图形计算器制作的图表。
【问题讨论】:
-
或者说第一种方法(普通乘法)确实是“标准”的方法,意味着剪裁是正常的?