秦浩的“不络葛” - 从RGB到Lab色彩空间的转换
从RGB到Lab色彩空间的转换 
虽然若干年前就看过了关于色彩空间的介绍,但是直到今天才自己动手写代码做这件事情。虽然网络上已经有很多现成的例子,但是一则仅仅适用于浮点型的数据,另一方面,在实现上也有一些尚可优化之处。

色彩模型除了最常见的RGB以外,还有HSB、YCbCr、XYZ、Lab等。HSB一般仅仅作为图像处理过程中的临时模式,YCbCr常常用于图像的压缩处理,而XYZ则严格按照人眼对光信号的敏感度进行分布。

这里将要稍作讨论的便是Lab模型。网络上诸多的介绍都说Lab是基于XYZ的,故人们一般也只能找到XYZ和Lab之间的转换,而RGB到Lab的转换只能使用XYZ作为中间模式间接进行。可惜的是,这种现状源于误解。而在图像处理软件中(比如Photoshop),往往采用一个更为简单的算法。

我们可以先观察RGB到XYZ的转换:
[X,Y,Z] = [M] * [R,G,B]

其中M为一3x3矩阵:
[M] = [0.4125, 0.3576, 0.1805;
0.2126, 0.7152, 0.0722;
0.0193, 0.1192, 0.9505],

RGB是经过Gamma校正的色彩分量:R=g(r),G=g(g),B=g(b)。
其中rgb为原始的色彩分量。

g是Gamma校正函数:
当 x < 0.018 时,g(x) = 4.5318 * x
当 x >= 0.018 时,g(x) = 1.099 * d^0.45 - 0.099

rgb以及RGB的取值范围则均为[0,1)。计算完成后,XYZ的取值范围则有所变化,分别是:[0, 0.9506),[0, 1),[0, 1.0890)。


以及XYZ到Lab的转换:
L = 116 * f(Y1) - 16
a = 500 * (f(X1) - f(Y1))
b = 200 * (f(Y1) - f(Z1))

其中f是一个类似Gamma函数的校正函数:
当 x > 0.008856 时,f(x) = x^(1/3)
当 x <= 0.008856 时,f(x) = ( 7.787 * x ) + ( 16 / 116 )
X1、Y1、Z1分别是线性归一化之后的XYZ值,也就是说,它们的取值范围都是[0, 1)。此外,函数f的值域也和自变量一样都是[0, 1)。

计算完成后,L的取值范围[0, 100),而a和b则约为[-169, +169)和[-160, +160)。


在观察这些貌似复杂的变换之前,我们必须确定的一个假设是:在图像处理软件中,非RGB色彩数据的绝对值并不重要,重要的是他们能够尽可能准确的还原成RGB图像以显示在屏幕等相关设备上。这个假设是我们的简化得以成立的理由。

上面的从XYZ到Lab的转换乍一看起来很奇怪,但若是仔细观察,不难发现L与Y1只是一个简单的同区间映射关系,这个映射其实可有可无(如果进行了映射反而必定导致色阶丢失)。

这样,我们取得的第一个简化是:L = Y1

接下来接着看a和b的映射过程。大家不难发现,a和b其实是一个色差信号(跟Cb和Cr的性质差不多)。至于它们的转换系数500和200,大家可以完全忘记,因为他们的值域并不符合8位整数值的表达需要。我们将会稍后计算出合适的因数,使得a和b都处在[0, 255]的范围内。

因为XYZ必须归一化转为X1Y1Z1,那么我们其实可以在转换矩阵M中作出这个修改,令每行乘以一个系数以使得每行各数之和为1:
[M1] = [0.4339, 0.3762 0.1899;
0.2126, 0.7152, 0.0722;
0.0177, 0.1095, 0.8728]

于是乎,我们得出一个半成品:
L = Y1 = 0.2126 * R + 0.7152 * G + 0.0722 * B
a = Fa * (X1 - Y1) + Da
b = Fb * (Y1 - Z1) + Db
其中的Fx是调整值域用的系数,Dx是一个正数,用来消除a和b的负值。Fx和Dx的选取必须令a和b满足值域在[0, 255]上的分布。

接下来我们来确定Fx和Dx的值。通过M1我们很容易计算出X1-Y1的值域(极端情况)为[-86.784, +86.784),而Y1-Z1的值域则为[-204.9536, +204.9536)。于是乎,Fa的值为1.4749,Fb的值为0.6245;Da和Db则都是128。

这时,代入M1有:
L = Y1 = 0.2126 * R + 0.7152 * G + 0.0722 * B
a = 1.4749 * (0.2213 * R - 0.3390 * G + 0.1177 * B) + 128
b = 0.6245 * (0.1949 * R + 0.6057 * G - 0.8006 * B) + 128
其中RGB和Lab的取值范围都是[0,255]。

最后的一点工作是算法的优化。我们可以将这个方程组转换成常整数乘法与移位的方式(相当于使用定点数)。为了方便阅读,我仍然将移位写为除法。

所以我们的最终结果为:
L = Y1 = (13933 * R + 46871 * G + 4732 * B) div 2^16
a = 377 * (14503 * R - 22218 * G + 7714 * B) div 2^24 + 128
b = 160 * (12773 * R + 39695 * G - 52468 * B) div 2^24 + 128


至于逆变换则可以用类似的方法推导出来:
设L1=L,a1=(a-128)*174,b1=(b-128)*410,有:
R = L1 + (a1 * 100922 + b1 * 17790) div 2^23
G = L1 - (a1 * 30176 + b1 * 1481) div 2^23
B = L1 + (a1 * 1740 - b1 * 37719) div 2^23
其中RGB和Lab的取值范围都是[0,255],再经过逆Gamma函数取得原始的rgb


以上的算法在Delphi中编译通过。经测试,运算得出的直方图与图片观感和我手头的Photoshop CS的结果非常相似,但也有一些幅度上的差别,且容以后慢慢细察。

当初为了寻觅一个简单的RGB直接转Lab算法而找遍网络皆不得,万不得已只好自力更生。其间虽费时一日,幸好也算略有所得。暂记于此,以利后人。其间或许难免错漏之处,还望达人不吝指点。:SUN_GLASSES:

专业!:SMOKE:

liuchang 
一头雾水的头像是哪一个?

秦浩 
向你推荐:HAPPY: 或者:OO: 或者:XD:

才才 
建议以后这种文章发表到专业刊物上,别在博客上显摆了—让我看了很气馁—啥也不懂!:CRYIN:

秦浩 
本来就是发在这里骗骗外行的哈:PACMAN2:

Yvette 
强烈同意CL,发在这里很占地方,费我眼球:SEE_NO_EVIL:

秦浩 
楼上的好歹你也是学数学的:PACMAN:

york  
你好,你的算法是有问题的,“运算得出的直方图与图片观感和我手头的Photoshop CS的结果非常相似”可能值得商榷,如果有可能,请联系我,我们可以讨论一下

秦浩 
愿闻其详!已经给你邮件了:SUCK:

微星  
没有弄明白,请教个问题:
下面RGB2Lab公式中的R,G,B是经过gamma矫正后的还是原RGB?如何Gamma矫正?你上述矫正的判断是在xyz空间的公式.
{
L = Y1 = (13933 * R + 46871 * G + 4732 * B) div 2^16
a = 377 * (14503 * R - 22218 * G + 7714 * B) div 2^24 + 128
b = 160 * (12773 * R + 39695 * G - 52468 * B) div 2^24 + 128
}


至于逆变换则公式
{
设L1=L,a1=(a-128)*174,b1=(b-128)*410,有:
R = L1 + (a1 * 100922 + b1 * 17790) div 2^23
G = L1 - (a1 * 30176 + b1 * 1481) div 2^23
B = L1 + (a1 * 1740 - b1 * 37719) div 2^23
}
其中RGB和Lab的取值范围都是[0,255],再经过逆Gamma函数取得原始的rgb????,如何逆Gama?
我按上式写完两个函数,都没Gamma处理经过变换,再反变换结果图像被破坏了.

秦浩 
已经给你回信寄去源代码了。请注意查收:SUN_GLASSES:

Muscle  
您好,刚刚研究了您的推导过程,可否发一份delphi或者c++的算法实现给我,希望与博主交流,我的邮箱786219564@qq.com

WWW 
感觉很有问题。首先那个M矩阵就有问题,我猜可能是用在sRGB空间中的,但是正确的是需要经过转置的。

菲菲  
div 2^16是什么意思啊?怎么计算呢?


秦浩 
div 2^16 其实就是右移16位的意思:SKULL:

patriot  
你好,用你的算法正反转换两遍,转回的图像黑色部分颜色出错啊,不知能否告知如何修改?如果有可能,请联系我,谢谢!QQ:26514577,mail: apatriot@163.com

秦浩 
不知楼上能否把错误的大小告知一二?能提供原图更好。

patriot  
这个页面好像传不了图,能加qq或者mail联系么?

patriot  
这个页面好像传不了图,能加qq或者mail联系么?

forgot163  
按照楼主的方法写了程序,用Matlab实现的,发现结果似乎不对,L通道的最大值/最小值都是0;a/b通道的最小值都是128;我有一点不明白,在最终的公式中使用的R、G、B分量是经过矫正的,还是原图的RGB分量?

我的QQ:513322698 ,希望能与楼主讨论一下! 可以的话,楼主能不能把您的程序发给我,我看一下,也可能是我程序写错了!求指教!谢谢了!

wguo  
你好,刚研究你的推导过程,可否将c实现发给我,希望与博主进行交流。

yulink  
还是没太懂哈,
能给一份源程序吗?
455010562@qq.com

zhmn 
膜拜求源代码281444@126.com

zhmn 
不好意思打错了 是膜拜求源代码281444114@qq.com

罗娇  
师兄,您好!之前看到的转载的文章,按照你的实现了,鉴于编程能力有限,实现的效果还是没有OpenCV库函数的效果好。今天再次google发现真神原来在此,可以发一份您的源码?最好可以是c/c++的源码,无限感谢。luojiao1101@163.com

罗娇  
师兄,您好!之前看到的转载的文章,按照你的实现了,鉴于编程能力有限,实现的效果还是没有OpenCV库函数的效果好。今天再次google发现真神原来在此,可以发一份您的源码?最好可以是c/c++的源码,无限感谢。luojiao1101@163.com

zhanghziyuan3.com  
能否发一份delphi代码谢谢!!!
zhangzhiyuan303@163.com

评论 

发表评论

填写下面的表单来发表您的评论。









插入标签:


:-_-: :ANGRY: :ANIME: :PERSPIRATION: :BIG_LAUGH: :BIG_SMILE: ^_^bbb :BLUSHED: :BOWING: :CHILLOUT: :CONDUCTOR: :CRYIN: :CURSOR_STAB: :DOG_FACE: :DROOL: :EVIL_SMILE: :EYE_CLOSE: ;) ;-) :FAINT: :GROWLER: :HAPPY: :HEY: :JUGGLING: :MEDITATE: :BIGGER: :MUAHAHAH: :NORMAL: :OO: :OOPS: :PACMAN: :PACMAN2: :PAIN: :PANIC: :PARANOID: :PAT: :PIRATE: :PLOTTING: :SAD: :SCARED: :SEE_NO_EVIL: :SHOOT: :SKULL: :SMILE: :SMITTEN: :SMOKE: :SORRY: :SPEACHLESS: :SPEAK_NO_EVIL: :SUCK: :SUN_GLASSES: @_@ :SURPRISED: :SWEET: :) :-) :TEETHS: :THINKIN: :TONGUE: :UUUUUUU: :WISPER: :WOOO: :WOW: :WTF: :XD: