承接上一篇滤镜初识,本文将介绍第一种滤镜:颜色滤镜。
颜色滤镜
颜色滤镜即调色滤镜,也是最常见的滤镜,任何通过调节图像像素值的亮度、对比度、饱和度、色相等等方法,得到的不同于原图像颜色的效果,都统称为颜色滤镜。
我们来做一个颜色增强滤镜,以此说明,方便大家更好的理解。
如下图所示,我们对一副图在PS中进行饱和度调整(饱和度提高41):
那么,我们在这个过程中,算法实际上做了对一副图S进行饱和度+41的操作,我们使用这个操作对其他图进行处理,那么这个操作就是一个颜色增强滤镜。
我们编程实现这个滤镜操作:
- 1,PS饱和度调节算法原理
假设原图像素P,RGB颜色空间对应颜色分量为R/G/B,饱和度调整参数为saturation,范围[-100,100];
原理伪代码如下:
- 2,C语言实现
int f_Stauration(unsigned char* srcData, int width, int height, int stride, int saturation)
{
unsigned char* pSrc = srcData;
int r, g, b, rgbMin, rgbMax;
int k = saturation / 100.0f * 128;
int alpha = 0;
for(int j = 0; j < height; j++)
{
for(int i = 0; i < width; i++)
{
r = pSrc[2];
g = pSrc[1];
b = pSrc[0];
rgbMin = MIN2(MIN2(r, g), b);
rgbMax = MAX2(MAX2(r, g), b);
int delta = (rgbMax - rgbMin);
int value = (rgbMax + rgbMin);
if(delta ==0)
{
pSrc += 4;
continue;
}
int L = value >> 1;
int S = L < 128 ? (delta << 7) / value : (delta << 7) / (510 - value);
if(k >= 0)
{
alpha = k + S >= 128 ? S : 128 - k;
alpha = 128 * 128 / alpha - 128;
}
else
alpha = k;
r = r + ((r - L) * alpha >> 7);
g = g + ((g - L) * alpha >> 7);
b = b + ((b - L) * alpha >> 7);
pSrc[0] = CLIP3(b, 0, 255);
pSrc[1] = CLIP3(g, 0, 255);
pSrc[2] = CLIP3(r, 0, 255);
pSrc += 4;
}
}
return 0;
}
- 3,颜色增强滤镜实现
int f_ColorFilter(unsigned char* srcData, int width, int height, int stride)
{
return f_Saturation(srcData, width, height, stride, 41);
}
效果图如下:
上述过程实际上就是一个完整的制作颜色滤镜的过程,大家可以了解到什么是颜色滤镜,我们通过PS模拟滤镜效果,然后编程实现这个滤镜效果,用这个算法程序去处理其他的用户照片,这个算法程序就叫做一个滤镜算法,这里分类为颜色滤镜算法。
了解了颜色滤镜之后,我们来讲解一下LUT滤镜。
LUT滤镜
LUT是Look Up Table的缩写,俗称为颜色查找表。颜色查找表有1D LUT、2D LUT、3D LUT三种。
- 1D LUT
1D LUT只能控制gamma值、RGB平衡(灰阶)和白场(white point),形如下图一个256x1大小的1D LUT。
这张图中记录了0-255的像素映射值,我们通过查表的方式来获取对应RGB值的映射值,由于一个RGB对应一个输出,每个输出之间是相互独立的,因此,这种1D LUT只能实现gamma值,线性对比度亮度调节等等简单的功能,下面是使用1D LUT进行亮度调节的效果:
1D LUT也有着很多应用,比如Instagram app,它的滤镜实现中,就使用了很多1D LUT,有兴趣的可以去了解一下。
- 2D LUT
2D LUT可以控制颜色饱和度和亮度,在完整的色彩空间中进行线性缩放,但是也无法控制颜色的非线性变换,它与1D LUT的区别就是采用了二维坐标来记录颜色的映射关系。
我们以图层混合为例,2D LUT的一种样例图如下:
这种2D LUT的左上角为黑色,右下角为白色,大小为256x256,分别对应图像像素的RGB值范围,使用中假设像素值为M,那么对应到LUT样例图中M行M列的位置,该位置的映射值就是像素M对应的效果值。我们使用该样例图来实现正片叠底的图层混合效果如下:
上面所述这种2D LUT目前在图像类的手机app中非常常见,比如美图秀秀等,对于一些图层混合,我们不需要使用复杂的程序计算,也不需要使用3D LUT来占用更多的资源,直接使用2D LUT,即可满足实时处理的速度需求和资源空间节省的需求。
- 3D LUT
3D LUT非常适合用于精确的颜色校准工作,因为它们能够处理所有的显示校准的问题,从简单的gamma值、颜色范围和追踪错误,到修正高级的非线性属性、颜色串扰(去耦)、色相、饱和度、亮度等。
RGB 可以表示的颜色数量为 256*256*256 = 16,777,216,如果要记录每种颜色的映射结果,那么颜色表需要 一千六百多万条记录,这显然无法应用到实际的工程中。为了简化起见,Lev Zelensky发表了一个基准颜色表,将每相近的 4 种颜色采用一条记录存储,这样颜色表只需要64 * 64 * 64 = 262,144 条记录。这个表如下:
目前这种LUT的应用最为广泛,因为他可以记录各种非线性的颜色变换,对于颜色滤镜中的图像处理而言,可以大大简化程序逻辑与算法复杂度。
这种LUT的大小为512X512,分为64个大小为64X64的颜色方格,它的颜色关系如下:
假设像素P的三个分量为R/G/B
对于每个小方格,X方向代表R像素值,Y方向代表了G像素值,该方格的位置代表了B像素值。
查找原理
举例说明,对于像素P(200,0,100),R=200,G=0,B=100,P在LUT中的位置为D:
我们首先根据B=100来确定我们要找的R和G是在哪一个小方格中,我们设定左上角的第一个小方格为0,从左到右,从上到下依次排列64个小方格,对应的B值依次为i*4,范围正好在0-255之内,实际上是0-252,最大值63*4=252,那么,根据B计算出我们要找的是哪个方格,现在B=100,B/4=25,因此,在第26个小方格,也就是LUT的第四行第二列。
我们在第26个小方格中寻找最终的位置D,R=200,我们在该小方格水平方向上计算,200/4=50,因此,D位置的X坐标为50,同理,G=0,那么Y坐标为0;
这样我们就找到了像素P(200,0,100)在LUT中的位置:第四行第二列的小方格中,坐标(50,0);
于是,P在LUT中映射后的结果值就是D对应的RGB值;
上述过程就是LUT的颜色映射过程,即查表过程。
我们使用C语言来实现LUT的查表如下:
int f_Filter512(unsigned char* srcData, int width ,int height, int stride, unsigned char*Map)
{
int i, j, r, g, b, offset, pos, nx, ny, k;
unsigned char* pSrc = srcData;
offset = stride - (width << 2);
for(j = 0; j < height; j++)
{
for(i = 0; i < width; i++)
{
b = pSrc[0];
g = pSrc[1];
r = pSrc[2];
k = (b >> 2);
nx = (int)(r >> 2) + ((k - ((k >> 3) << 3)) << 6);
ny = (int)(((b >> 5) << 6) + (g >> 2));
pos = (nx << 2) + (ny << 11);
pSrc[0] = Map[pos];
pSrc[1] = Map[pos + 1];
pSrc[2] = Map[pos + 2];
pSrc += 4;
}
pSrc += offset;
}
return 0;
};
该代码中,Map数据即为512X512的LUT数据,此处格式统一为32位BGRA数据。
滤镜效果图举例如下:
512X512大小的LUT使用方法总结如下:
- 使用PS调色或者使用程序编程实现所需调色效果;
- 使用1中的PS动作操作或者是程序算法处理Fig.7中的LUT样例图,得到滤镜LUT;
- 使用本文提供的C代码接口对其他图像进行LUT调色;
3D LUT滤镜实际使用中并非指定的某一种,也可以根据自己需求,在正确的理论基础之上,创造更多的LUT样例图,目前,我们经常看到的如下几种:
每一种的查表原理都是类似的,大家可以自行推导即可,这里提醒一点,512X512大小的LUT是不需要插值处理的,因为每种颜色屏蔽了4位之后,实际上并不影响我们的感官效果,如果LUT大小小于了这个尺寸,那么,在使用过程中是需要进行插值运算才能达到不影响感官的效果的,这一点请注意一下。
上面就是本节内容,希望大家能对颜色滤镜有个清晰的认识!
本人QQ:1358009172