前言
生活中我们使用RGB颜色空间更多一些,但在计算机视觉中,尤其颜色识别相关的算法设计中,rgb,hsv,lab颜色空间混用是常用的方法。由于本人去年用过OPENMV,其IDE里有着极为方便使用的LAB阈值编辑器,所以选择将RGB565格式的图片转成LAB,再进行后续的处理。
关于凌瞳
15届全国大学生智能车竞赛即将到来,逐飞新出品了一款名为“凌瞳”的彩色摄像头,分辨率最高可达VGA(480*640),使用RT1064的CSI接口采集图像,帧率最高可达60。它支持RGB565,YUV422的图像格式。它的sensor型号成谜,逐飞的店主曾经委婉提及他们试用了十多款sensor才选定的这个scc8660。以我的好奇心当然是要搜一下的,不过百度谷歌都无果,在逐飞群里问了一下,没一个人理。时隔多日有人在群里问,这是逐飞自己编出来的名字吗?我随即附和,却没想到二石不能激起一层浪,罢了。
RGB转Lab颜色空间
RGB颜色空间不能直接转换为Lab颜色空间,需要借助XYZ颜色空间,把RGB颜色空间转换到XYZ颜色空间,之后再把XYZ颜色空间转换到Lab颜色空间。
关于RGB565
RGB565,顾名思义,16位数据,分别5,6,5位是R,G,B分量。
获取R,G,B
img_show[i][j]=scc8660_csi_image[i][j];
r=(img_show[i][j]&0Xf800)>>11;
g=(img_show[i][j]&0X07e0)>>5;
b=(img_show[i][j]&0X001f);
按理说数据高八位应该在数据低八位,上面的代码却没有执行高低八位换位的操作,是因为在摄像头的配置文件中,有这样一个数据,默认0改为1即可。
{SCC8660_DATA_FORMAT,1}//输出数据格式 0:RGB565 1:RGB565(字节交换) 2:YUV422(YUYV) 3:YUV422(UYVY)
RGB->XYZ
此处参考handspeaker的博客
XYZ->LAB
L
=
116
∗
f
(
Y
1
)
−
16
L = 116 * f(Y1) - 16
L=116∗f(Y1)−16
A
=
500
∗
(
f
(
X
/
X
n
)
−
f
(
Y
/
Y
n
)
)
A = 500 * (f(X/Xn) - f(Y/Yn))
A=500∗(f(X/Xn)−f(Y/Yn))
B
=
200
∗
(
f
(
Y
1
)
−
f
(
Z
1
)
)
B = 200 * (f(Y1) - f(Z1))
B=200∗(f(Y1)−f(Z1))
Xn,Yn,Zn一般默认是95.047,100.0,108.883。
代码实现
无任何优化的代码
inline float gamma(float x)
{return x>0.04045?pow((x+0.055f)/1.055f,2.4f):x/12.92;};
void RGBToLab(unsigned char*rgbImg,float*labImg)
{
float B=gamma(rgbImg[0]/255.0f);
float G=gamma(rgbImg[1]/255.0f);
float R=gamma(rgbImg[2]/255.0f);
float X=0.412453*R+0.357580*G+0.180423*B;
float Y=0.212671*R+0.715160*G+0.072169*B;
float Z=0.019334*R+0.119193*G+0.950227*B;
float X/=0.95047;
float Y/=1.0;
float Z/=1.08883;
float FX = X > 0.008856f ? pow(X,1.0f/3.0f) : (7.787f * X +0.137931f);
float FY = Y > 0.008856f ? pow(Y,1.0f/3.0f) : (7.787f * Y +0.137931f);
float FZ = Z > 0.008856f ? pow(Z,1.0f/3.0f) : (7.787f * Z +0.137931f);
labImg[0] = Y > 0.008856f ? (116.0f * FY - 16.0f) : (903.3f * Y);
labImg[1] = 500.f * (FX - FY);
labImg[2] = 200.f * (FY - FZ);
}
移植、优化和修改
可以看出handspeaker的代码是基于RGB888格式的,而凌瞳是RGB565,需要做一定的修改。另一方面,将gama和f(t)运算简化为查表运算,将浮点数转换为大整形,将乘除法转换为移位操作。具体代码如下:
static int LabTable[1024];
static int GamaTable1[32];
static int GamaTable2[64];//B有6位,特殊对待
void CreateTable()//建表
{
for (int I = 0; I < 1024; I++)
{
if (I > 9)
LabTable[I] = (int)(pow((float)I / 1024, 1.0F / 3.0F) * 1024 );
else
LabTable[I] = (int)(7.787F * I + 141.2 );
}
for (int J = 0; J < 32; J++)
{
float x = J/32.0F;
x = x>0.04045?pow((x+0.055f)/1.055f,2.4f):x/12.92;
GamaTable1[J] = (int)(x*1024);
}
for (int K = 0; K < 64; K++)
{
float y = K/64.0F;
y = y>0.04045?pow((y+0.055f)/1.055f,2.4f):y/12.92;
GamaTable2[K] = (int)(y*1024);
}
}
x=(455026*GamaTable1[r]+394489*GamaTable2[g]+199046*GamaTable1[b])>>20;
y=(223002*GamaTable1[r]+749900*GamaTable2[g]+75675*GamaTable1[b])>>20;
z=(18619*GamaTable1[r]+114786*GamaTable2[g]+915097*GamaTable1[b])>>20;
L = y > 9 ? (116 * LabTable[y] - 16384)>> 10: (903 * LabTable[y])>> 10;
A = (500 * (LabTable[x] - LabTable[y]))>> 10;
B = (200 * (LabTable[y] - LabTable[z]))>> 10;
总结
前期利用image2lcd软件进行图片的取模和显示,在dev c++上调试代码 ,可以一步步看到结果。调好的代码再移植到RT1064上,效率很高,一次成功。
优化部分已经做到了最佳,比未优化算法快上百倍,运行时间可以说是忽略不计,非常满意。