颜色空间系列代码下载链接:http://files.cnblogs.com/Imageshop/ImageInfo.rar (同文章同步更新)

在几个常用的颜色空间中,LAB颜色空间是除了RGB外,最常用的一种之一,不同于RGB色彩空间,Lab 颜色被设计来接近人类视觉。它致力于感知均匀性,它的 L 分量密切匹配人类亮度感知。因此可以被用来通过修改 a 和 b 分量的输色阶来做精确的颜色平衡,或使用 L 分量来调整亮度对比。这些变换在 RGB 或 CMYK 中是困难或不可能的,它们建模物理设备的输出,而不是人类视觉感知。

关于CIELAB颜色空间的更多原理说明,可见:http://en.wikipedia.org/wiki/Lab_color_space

本文研究的重点是RGB和LAB之间的快速转换过程。

首先,RGB和LAB之间没有直接的转换公式,其必须用通道XYZ颜色空间作为中间层,关于RGB和XYZ颜色空间的转换及优化,详见颜色空间系列1。

XYZ------>LAB转换公式如下:一般情况下我们认为Yn,Xn,Zn都为1。

其中

在上述表达式中,X,Y,Z及t变量的取值范围都是[0,1],对应的L分量的取值范围为[0,100],A和B分量都为[-127,127],因此,如果把L拉升至[0,255],把A,B位移至于[0,255],就可以同RGB颜色空间表达为同一个范围了。即使这样映射后,一般来说,LAB各分量的结果仍为浮点数,这个和RGB不同,但是在很多情况下,为了速度计效率,我们这需结果的取整部分,得到类似于RGB空间的布局。因此,对这类结果的优化更有实际意义。

关于这样的优化,OpenCv已经做了非常好的工作,各位看客也可以先看看OpenCv的代码,本文未直接沿用其优化,但本文的算法更简单明了,在保证结果无明显变化的同时,速度和效率都有30%以上的提升。

第一步,我们来看看f(t)这个函数的优化,f(t)是个分段函数,如果直接在函数体中判断,会多一些跳转和比较语句,不利于CPU的流水线工作,因此,我考虑的第一步是是否能用查表法来做。

在颜色空间系列1文章中,我们知道,转换后的XYZ值得范围是[0,255],而这里的t值范围为[0,1],把if t>(6/29)^3这个算法映射到[0,255],则为 if t>2.26 ,因为XYZ都为整数,即此条件和if t>2等价,可见这里会出现一些漏判点;考虑2.26这个数字的特点,如果我们在把这个结果放大4倍,即XYZ范围为[0,1020],则判断条件随之升级为if t>9.04,取整if t>9,则漏判现象大为减少。这是提的第一点。

接着上面,这样的话我们就定义一个查找表,查找表大小应该和XYZ的域相同的,即上面的1020(我更喜欢1024),对于表中的元素值,为求速度,当然必须为int 类型,

也就是说,需要把计算出来的小数值放大一定倍数。这里不多说,见下面的代码:

    for (I = 0; I < 1024; I++){if (I > Threshold)LabTab[I] = (int)(Math.Pow((float)I / 1020, 1.0F / 3) * (1 << Shift) + 0.5 );elseLabTab[I] = (int)((29 * 29.0 * I / (6 * 6 * 3 * 1020) + 4.0 / 29) * (1 << Shift) + 0.5 );}

C#语言是强类型语言,一定要注意运算式中各变量的类型,比如上式中的1.0F/3,我常常写成1/3(这个的运算结果为0),结果往往是总觉得程序写得没问题,但运行效果就是不对,找半天BUG也找不到。

I / 1020的目的还是把值映射到[0,1]范围的。 表达式最后的+0.5是因为(int)强制类型转换时向下取整的,+0.5则为四舍五入的效果。显然,这是我们需要的。

OK,有了这个查找表,下面的过程就简单了,对于A,B分量,就是进行简单的乘法、移位及加法,而对于L分量,必须有一个放大的过程,而这个过程我们应该直接从其系数入手,如下所示:

   const int ScaleLC = (int)(16 * 2.55 * (1 << Shift) + 0.5);const int ScaleLT = (int)(116 * 2.55 + 0.5);

2.55即为放大倍数,注意116这个数字,由于,其后的 f(x)已经进行了放大,该数字就不能再放大了。

通过以上分析,一个简单的而有高效转换算法就有了:

    public static unsafe void ToLAB(byte* From, byte* To, int Length = 1){if (Length < 1) return;byte* End = From + Length * 3;int X, Y, Z, L, A, B;byte Red, Green, Blue;while (From != End){Blue = *From; Green = *(From + 1); Red = *(From + 2);X = (Blue * LABXBI + Green * LABXGI + Red * LABXRI + HalfShiftValue) >> (Shift - 2);  //RGB->XYZ放大四倍后的结果Y = (Blue * LABYBI + Green * LABYGI + Red * LABYRI + HalfShiftValue) >> (Shift - 2);Z = (Blue * LABZBI + Green * LABZGI + Red * LABZRI + HalfShiftValue) >> (Shift - 2);X = LabTab[X];          // 进行查表Y = LabTab[Y];Z = LabTab[Z];L = ((ScaleLT * Y - ScaleLC + HalfShiftValue) >>Shift);A = ((500 * (X - Y) + HalfShiftValue) >> Shift) + 128;B = ((200 * (Y - Z) + HalfShiftValue) >> Shift) + 128;*To = (byte)L;          // 不要把直接计算的代码放在这里,会降低速度*(To + 1) = (byte)A;    // 无需判断是否存在溢出,因为测试过整个RGB空间的所有颜色值,无颜色存在溢出*(To + 2) = (byte)B;From += 3;To += 3;}}

再来看看反转的过程,即LAB-XYZ的算法,理论公式如下:

其中:

      注意,我这里说的转换有个前期条件,即LAB的数据是用类似于RGB空间的布局表达的,也就是说LAB各元素为byte类型。

我曾自己的研究过这些算法,如果完全像上面那样靠整数乘法及移位来实现,主要的难度是t^3这个表达式的计算结果会超出int类型的表达范围,而如果用64位的long类型,在目前32位机器依旧占主流配置的情况下,速度会下降很多。因此,我最后的研究还是以空间换时间的方法来实现。具体分析如下:

观察上式分析,Y的值只于L有关,而L由于我们的限定,只能取[0,255]这256个值,因此建立一个256个元素的查找表即可,而X及Z的值分别于L及A/B有关,需要建立256*256个元素的查找表即可,大约占用0.25MB的内存。查找表的建立如下:

for (I = 0; I < 256; I++){T = I * Div116 + Add16;if (T > ThresoldF)Y = T * T * T;elseY = MulT * (T - Sub4Div29);TabY[I] = (int)(Y * 255 + 0.5);      // 映射到[0,255]for (J = 0; J < 256; J++){X = T + Div500 * (J - 128);if (X > ThresoldF)X = X * X * X;elseX = MulT * (X - Sub4Div29);TabX[Index] = (int)(X * 255 + 0.5);Z = T - Div200 * (J - 128);if (Z > ThresoldF)Z = Z * Z * Z;elseZ = MulT * (Z - Sub4Div29);TabZ[Index] = (int)(Z * 255 + 0.5);Index++;}}

最终的LAB-RGB转换算法如下:

 public static unsafe void ToRGB(byte* From, byte* To, int Length = 1){if (Length < 1) return;byte* End = From + Length * 3;int L, A, B, X, Y, Z;int Blue, Green, Red;while (From != End){L = *(From); A = *(From + 1); B = *(From + 2);X = TabX[L * 256 + A];      // *256编译后会自动优化为移位的Y = TabY[L];Z = TabZ[L * 256 + B];Blue = (X * LABBXI + Y * LABBYI + Z * LABBZI + HalfShiftValue) >> Shift;  Green = (X * LABGXI + Y * LABGYI + Z * LABGZI + HalfShiftValue) >> Shift;Red = (X * LABRXI + Y * LABRYI + Z * LABRZI + HalfShiftValue) >> Shift;if (Red > 255) Red = 255; else if (Red < 0) Red = 0;if (Green > 255) Green = 255; else if (Green < 0) Green = 0;            // 需要有这个判断if (Blue > 255) Blue = 255; else if (Blue < 0) Blue = 0;*(To) = (byte)Blue;*(To + 1) = (byte)Green;*(To + 2) = (byte)Red;From += 3;To += 3;}}

通过以上的分析,可以看出,这个转换的过程代码很简单,清晰,而且效率不菲,对一副4000*3000的数码照片进行RGB->LAB,然后再LAB->RGB算法本体的时间只有250ms。

还有几个优化的地方就是我的所有的查找表都不是用的C#的数组,而是直接分配内存,这是因为C#的数组在很多情况下会有一个判断是否越界的汇编码,而用非托管内存则不会。

比如,以下是用非托管内存的数组访问的反汇编:

  static int* TabX = (int*)Marshal.AllocHGlobal(256 * 256 * 4);    // 这是原始的定义

                X = TabX[L * 256 + A];      // *256编译后会自动优化为移位的
00000037  mov         eax,edi
00000039  shl         eax,8          // 看到这里的移位没有
0000003c  add         eax,edx
0000003e  mov         edx,dword ptr ds:[005A1F0Ch]
00000044  mov         eax,dword ptr [edx+eax*4]
00000047  mov         dword ptr [ebp-14h],eax 

而用C#的数组方式生产的汇编如下:

static int[] TabX = new int[256 * 256];     // 这是原始的定义

                X = TabX[L * 256 + A];      // *256编译后会自动优化为移位的
0000003c  mov         eax,edi
0000003e  shl         eax,8
00000041  add         eax,edx
00000043  mov         edx,dword ptr ds:[02A27C68h]   
00000049  cmp         eax,dword ptr [edx+4]   // 多出这两句代码
0000004c  jae         00000133
00000052  mov         eax,dword ptr [edx+eax*4+8]
00000056  mov         dword ptr [ebp-14h],eax 

其实还有很多细节上的优化的东西,比如语句的顺序的讲究,有的时候就是调换下不同行的语句,程序的执行效率就有很多的不同,这主要是编译器的优化不同造成的,比如适当的顺序会让编译器选择某个常用变量为寄存器变量。 还比如有人喜欢用下面的代码

  *To++ = (byte)L;*To++ = (byte)A;*To++ = (byte)B;

来代替:

 *To = (byte)L;          *(To + 1) = (byte)A;    *(To + 2) = (byte)B;To += 3;

虽然代码看上去简洁了,可你执行后就知道速度反而慢了,为什么,我想我会在适当时候写一些关于C#优化方面的粗浅文章在对此进行解释吧。

最后附上一些处理的效果,还是拿系列1文章中那些崇洋的新贵门来做实验吧:

原图:

转换后的综合图像:

L通道:

A通道:

B通道:

同样的道理,上述快速算法如果进行多次转换,必然也存在精度上的损失。

LAB空间在以后的肤色检测文章中还会有提到。

'*********************************************************************

  转载请保留以下信息:

  作者: laviewpbt

  时间:2013.2.2   11点于家中

  QQ:33184777

  E-Mail : laviewpbt@sina.com

颜色空间系列2: RGB和CIELAB颜色空间的转换及优化算法相关推荐

  1. RGB和YCbCr颜色空间的转换及优化算法

    RGB和YCbCr颜色空间转换和优化 转载于颜色空间系列3: RGB和YCbCr颜色空间的转换及优化算法 在常用的几种颜色空间中,YCbCr颜色空间在学术论文中出现的频率是相当高的,常用于肤色检测等等 ...

  2. RGB和CIELAB颜色空间转换及偏色检测

    RGB转为CIELAB 首先RGB是不可以直接转为CIELAB颜色空间的,RGB需要先转为CIEXYZ颜色空间,然后再由CIEXYZ颜色空间转为CIELAB颜色空间.关于这2个颜色空间的互转,主要参考 ...

  3. MATLAB实战系列(十一)-多种群遗传算法的函数优化算法(附MATLAB代码)

    前言: 本篇博文参考,智能优化算法书籍<MATLAB智能算法30个案例分析(第2版)>,今天要与大家分享的智能算法是多种群遗传算法. 本地MATLAB环境部署 因为后面要介绍的多种群遗传算 ...

  4. 学习黑盒优化算法CMA和RandomSearch,借助阿里达摩院MindOpt的RABBO榜单【系列2/4】

    1 黑盒优化的概念 上一篇<新手一步步学习黑盒优化算法,借助达摩院MindOpt的RABBO榜单[系列1/4]>中给大家介绍了黑盒优化的概念.和怎么借助阿里达摩院的RABBO"动 ...

  5. RGB颜色空间和CIELab颜色空间互换(matlab代码)

    来源:http://www.mathworks.com/matlabcentral/fileexchange/24009-rgb2lab/content/RGB2Lab.m和http://www.ma ...

  6. RGB与Lab颜色空间互相转换

    RGB与Lab颜色空间互相转换 1.Lab颜色空间 同RGB颜色空间相比(见博客<光与色的故事--颜色空间浅析>),Lab是一种不常用的色彩空间.它是在1931年国际照明委员会(CIE)制 ...

  7. 在CIELab颜色空间下使用八方向Sobel算子实现边缘检测

    参考河北师范大学硕士学位论文--基于八方向Sobel算子的边缘检测算法研究. 由于自己实现滤波器运算,计算速度很慢,以后有能力再进行改进. 算子定义如下: 算法思路: 1.将RGB图像转化为CIELa ...

  8. RGB与Lab颜色空间互相转换 持续更新中

    RGB与Lab颜色空间互相转换 1.Lab颜色空间 同RGB颜色空间相比(见博客<光与色的故事–颜色空间浅析>),Lab是一种不常用的色彩空间.它是在1931年国际照明委员会(CIE)制定 ...

  9. Python实现RGB和Lab颜色空间互转

    Python实现RGB和Lab颜色空间互转 https://github.com/rubund/debian-home-assistant/blob/1a3e8f7e4b9ddec60a4380e14 ...

最新文章

  1. 信号传递的时机与顺序
  2. linux怎么查看内核定义的结构体,Linux如何查找一个结构体的原始定义
  3. Fescar TC-beigin流程
  4. 数据:以太坊2.0合约24小时新增2.04万ETH
  5. Java 多线程的创建
  6. 怎样从Mysql官网下载linux版本的mysql安装包
  7. 真相 | 14 岁编程神童谎言坐实,除了谴责我们该反思什么?
  8. 算法笔记_面试_0.刷leetcode_基础知识范围
  9. sqlmap指定cookie_Sqlmap Cookie注入 教程
  10. 拯救者Y7000在CentOS7上面无法打开网卡 Ath10k (QCA9377)
  11. Linux下编译链接动态库符号问题
  12. x264去方块滤波函数解析(二)
  13. XDMCP远程连接Linux桌面(lightdm) ps -ef |grep lightdm
  14. Linux系统检测工具
  15. 【英语语法入门】第04讲 代词的主格和宾格
  16. AcWing 1022 宠物小精灵之收服
  17. Soul瞬间发布长视频教程
  18. 电子体温计方案温度传感器的解析
  19. [喜闻乐见]期末尻♂总结
  20. python:HTTPX 库的快速开始

热门文章

  1. 机器人图形变变变_幼儿园大班数学《图形变变变》教案
  2. 《炬丰科技-半导体工艺》--技术资料合集24
  3. java栈实现--顺序栈
  4. Java栈——操作数栈
  5. 计算机任务管理器无法响应,简单几步解win10任务管理器打不开提示无响应的方法...
  6. New Concept book two words
  7. 射频功放OIP3,IIP3,IM3,IMD3几个指标的具体计算
  8. (阿里云笔记)轻量应用服务器控制台界面的使用
  9. 用什么软件可以检测苹果耳机芯片_如何辨别是不是络达1562的芯片
  10. OpenCV基于Python霍夫圆检测—标准霍夫圆检测