简介
本篇讲解如何测24色卡的饱和度和色彩偏差。
实现原理
主要是模仿imatest来实现,详细资料请参考:http://www.imatest.com/docs/colorcheck/
具体做法:1、首先鼠标框选出色卡中24色所在位置,然后分别在对应色块中再取出小块矩形。这24个小矩形中色彩数据,就是之后用来处理计算的数据。
2、接着将24个小矩形的图像,转换为Lab格式。
3、饱和度计算公式:Chroma = 100% mean((a*i_meas2 + b*i_meas2)1/2 ) / mean((a*i_ideal2 + b*i_ideal2)1/2 ) ;
a*i_meas2:被计算的色块对应色块的平均a。
b*i_meas2:被计算的色块对应色块的平均b。
a*i_ideal2:作为计算模板色块对应的平均a。
b*i_ideal2:作为计算模板色块对应的平均b。
4、不包含亮度的色彩偏差Cab:
a*i_corr = 100 a*i_meas / Sat ; b*i_corr = 100 b*i_meas / Sat
ΔCi_corr = |Ci_corr – Ci_ideal | = ( (a*i_corr – a*i_ideal )2 + (b*i_corr – b*i_ideal )2 )1/2
5、包含亮度的色彩偏差Eab:
ΔE*ab = ( (L2*-L1*)2+ (a2*-a1*)2+ (b2*-b1*)2 )1/2; ΔC* = ( (a2*-a1*)2+ (b2*-b1*)2 )1/2
6、模板测卡对应的数据信息:http://xritephoto.com/documents/literature/en/ColorData-1p_EN.pdf
具体实现
框选出24色块数据
首先鼠标框选出一个矩形框,然后使用函数mccRectAddr,在矩形框中,分别选中24色块数据。
void mccRectAddr( Mat mat1, Mat mat2, int * rectAddr) {
int i, j;
for ( j= 0 ; j< 4 ; j++ ) {
for ( i= 0 ; i< 6 ; i++ ) {
ccm_rect[ i+ 6 * j] [ 0 ] = rectAddr[ 0 ] + ( rectAddr[ 2 ] - rectAddr[ 0 ] ) * ( 12 + 6 * i + 34 * i ) / 234 ;
ccm_rect[ i+ 6 * j] [ 1 ] = rectAddr[ 1 ] + ( rectAddr[ 3 ] - rectAddr[ 1 ] ) * ( 10 + 6 * j + 34 * j) / 151 ;
ccm_rect[ i+ 6 * j] [ 2 ] = rectAddr[ 0 ] + ( rectAddr[ 2 ] - rectAddr[ 0 ] ) * ( 22 + 6 * i + 34 * i) / 234 ;
ccm_rect[ i+ 6 * j] [ 3 ] = rectAddr[ 1 ] + ( rectAddr[ 3 ] - rectAddr[ 1 ] ) * ( 20 + 6 * j + 34 * j) / 151 ;
rectangle( mat1, Point( ccm_rect[ i+ 6 * j] [ 0 ] , ccm_rect[ i+ 6 * j] [ 1 ] ) , Point( ccm_rect[ i+ 6 * j] [ 2 ] , ccm_rect[ i+ 6 * j] [ 3 ] ) , Scalar( 255 , 0 , 0 ) , 2 ) ;
}
}
mccRoiGet( mat2) ;
}
注意,这里mat1和mat2都是需要被计算的色卡图片,rectAddr保存的书鼠标矩形框坐标信息。函数中,接着在mat1中,选中的24色块数据位置
分别画出小矩形到mat1中,用来显示。接着使用函数mccRoiGet将选中的24色块数据,在mat2使用ROI扣去出来。
void mccRoiGet( Mat mat1) {
char str[ 10 ] ;
int i= 0 , j= 0 ;
for ( i= 0 ; i< 4 ; i++ ) {
for ( j= 0 ; j< 6 ; j++ ) {
mccROI[ j+ i* 6 ] = mat1( cv:: Rect ( ccm_rect[ j+ 6 * i] [ 0 ] , ccm_rect[ j+ 6 * i] [ 1 ] ,
ccm_rect[ j+ 6 * i] [ 2 ] - ccm_rect[ j+ 6 * i] [ 0 ] , ccm_rect[ j+ 6 * i] [ 3 ] - ccm_rect[ j+ 6 * i] [ 1 ] ) ) ;
}
}
}
这样,选中的24色块数据,就分别保存到了mccROI[24]中了。
对应的效果演示如下:
Sat计算
void mccSat( ) {
IplImage pI_1;
CvScalar s;
int width, height;
int i, j, k;
double ab_meas= 0 , ab_idea= 0 ;
for ( k= 0 ; k< 18 ; k++ ) {
pI_1 = mccROI[ k] ;
cvCvtColor( & pI_1, & pI_1, CV_BGR2Lab) ;
width = mccROI[ k] .rows ;
height = mccROI[ k] .cols ;
for ( i= 0 ; i< width; i++ ) {
for ( j= 0 ; j< height; j++ ) {
s = cvGet2D( & pI_1, i, j) ;
if ( i== 0 && j== 0 ) {
mccLab[ k] [ 0 ] = s.val [ 0 ] ;
mccLab[ k] [ 1 ] = s.val [ 1 ] ;
mccLab[ k] [ 2 ] = s.val [ 2 ] ;
} else {
mccLab[ k] [ 0 ] = ( s.val [ 0 ] + mccLab[ k] [ 0 ] ) / 2 ;
mccLab[ k] [ 1 ] = ( s.val [ 1 ] + mccLab[ k] [ 1 ] ) / 2 ;
mccLab[ k] [ 2 ] = ( s.val [ 2 ] + mccLab[ k] [ 2 ] ) / 2 ;
}
}
}
mccLab[ k] [ 0 ] = mccLab[ k] [ 0 ] * 100 / 255 ;
mccLab[ k] [ 1 ] = mccLab[ k] [ 1 ] - 128 ;
mccLab[ k] [ 2 ] = mccLab[ k] [ 2 ] - 128 ;
ab_meas = sqrt ( ( mccLab[ k] [ 1 ] * mccLab[ k] [ 1 ] ) + ( mccLab[ k] [ 2 ] * mccLab[ k] [ 2 ] ) ) + ab_meas;
ab_idea = sqrt ( ( mccLabModel[ k] [ 1 ] * mccLabModel[ k] [ 1 ] ) + ( mccLabModel[ k] [ 2 ] * mccLabModel[ k] [ 2 ] ) ) + ab_idea;
cvCvtColor( & pI_1, & pI_1, CV_Lab2BGR) ;
}
ab_meas = ab_meas / 18 ;
ab_idea = ab_idea / 18 ;
Sat = ab_meas / ab_idea;
printf ( "Sat:%lf\n " , Sat) ;
}
将之前mccROI[24]中保存的色块数据分别进行处理。首先是将数据转换为Lab的格式。接着计算出色块L a b的平均值,保存到对应的mccLab中。
需要注意:
1、这里计算色彩相关,所以只需要处理前18块数据,后面6块灰阶图像不用处理。
2、在opencv中L a b,都被转换为0 - 255的数据存储。所以进行计算之前,需要将L a b数据先转换回来。
mccLab[ k] [ 0 ] = mccLab[ k] [ 0 ] * 100 / 255 ;
mccLab[ k] [ 1 ] = mccLab[ k] [ 1 ] - 128 ;
mccLab[ k] [ 2 ] = mccLab[ k] [ 2 ] - 128 ;
最后就是根据之前提到的计算饱和度公式,将饱和度计算出来,保存到Sat中。
Cab计算
void mccCab( ) {
IplImage pI_1;
CvScalar s;
int width, height;
int i, j, k;
double a_err= 0 , b_err= 0 ;
Cab_sum = 0 ;
Cab_max = 0 ;
for ( k= 0 ; k< 18 ; k++ ) {
pI_1 = mccROI[ k] ;
cvCvtColor( & pI_1, & pI_1, CV_BGR2Lab) ;
width = mccROI[ k] .rows ;
height = mccROI[ k] .cols ;
for ( i= 0 ; i< width; i++ ) {
for ( j= 0 ; j< height; j++ ) {
s = cvGet2D( & pI_1, i, j) ;
if ( i== 0 && j== 0 ) {
mccLab[ k] [ 0 ] = s.val [ 0 ] ;
mccLab[ k] [ 1 ] = s.val [ 1 ] ;
mccLab[ k] [ 2 ] = s.val [ 2 ] ;
} else {
mccLab[ k] [ 0 ] = ( s.val [ 0 ] + mccLab[ k] [ 0 ] ) / 2 ;
mccLab[ k] [ 1 ] = ( s.val [ 1 ] + mccLab[ k] [ 1 ] ) / 2 ;
mccLab[ k] [ 2 ] = ( s.val [ 2 ] + mccLab[ k] [ 2 ] ) / 2 ;
}
}
}
mccLab[ k] [ 0 ] = mccLab[ k] [ 0 ] * 100 / 255 ;
mccLab[ k] [ 1 ] = mccLab[ k] [ 1 ] - 128 ;
mccLab[ k] [ 2 ] = mccLab[ k] [ 2 ] - 128 ;
a_err = mccLab[ k] [ 1 ] / Sat;
b_err = mccLab[ k] [ 2 ] / Sat;
Cab[ k] = ( a_err - mccLabModel[ k] [ 1 ] ) * ( a_err - mccLabModel[ k] [ 1 ] ) + \
( b_err - mccLabModel[ k] [ 2 ] ) * ( b_err - mccLabModel[ k] [ 2 ] ) ;
Cab[ k] = sqrt ( Cab[ k] ) ;
if ( Cab_max < Cab[ k] ) {
Cab_max = Cab[ k] ;
}
cvCvtColor( & pI_1, & pI_1, CV_Lab2BGR) ;
Cab_sum = Cab_sum + Cab[ k] ;
printf ( "Cab[%d]:%lf\n " , k, Cab[ k] ) ;
}
Cab_sum = Cab_sum / 18 ;
printf ( "Cab_sum:%lf\n " , Cab_sum) ;
}
Eab计算
void mccEab( ) {
IplImage pI_1;
CvScalar s;
int width, height;
int i, j, k;
Eab_sum = 0 ;
Eab_max = 0 ;
for ( k= 0 ; k< 18 ; k++ ) {
pI_1 = mccROI[ k] ;
cvCvtColor( & pI_1, & pI_1, CV_BGR2Lab) ;
width = mccROI[ k] .rows ;
height = mccROI[ k] .cols ;
for ( i= 0 ; i< width; i++ ) {
for ( j= 0 ; j< height; j++ ) {
s = cvGet2D( & pI_1, i, j) ;
if ( i== 0 && j== 0 ) {
mccLab[ k] [ 0 ] = s.val [ 0 ] ;
mccLab[ k] [ 1 ] = s.val [ 1 ] ;
mccLab[ k] [ 2 ] = s.val [ 2 ] ;
} else {
mccLab[ k] [ 0 ] = ( s.val [ 0 ] + mccLab[ k] [ 0 ] ) / 2 ;
mccLab[ k] [ 1 ] = ( s.val [ 1 ] + mccLab[ k] [ 1 ] ) / 2 ;
mccLab[ k] [ 2 ] = ( s.val [ 2 ] + mccLab[ k] [ 2 ] ) / 2 ;
}
}
}
mccLab[ k] [ 0 ] = mccLab[ k] [ 0 ] * 100 / 255 ;
mccLab[ k] [ 1 ] = mccLab[ k] [ 1 ] - 128 ;
mccLab[ k] [ 2 ] = mccLab[ k] [ 2 ] - 128 ;
Eab[ k] = ( mccLab[ k] [ 0 ] - mccLabModel[ k] [ 0 ] ) * ( mccLab[ k] [ 0 ] - mccLabModel[ k] [ 0 ] ) + \
( mccLab[ k] [ 1 ] - mccLabModel[ k] [ 1 ] ) * ( mccLab[ k] [ 1 ] - mccLabModel[ k] [ 1 ] ) + \
( mccLab[ k] [ 2 ] - mccLabModel[ k] [ 2 ] ) * ( mccLab[ k] [ 2 ] - mccLabModel[ k] [ 2 ] ) ;
Eab[ k] = sqrt ( Eab[ k] ) ;
if ( Eab_sum== 0 ) {
Eab_sum = Eab[ k] ;
} else {
Eab_sum = ( Eab[ k] + Eab_sum) / 2 ;
}
if ( Eab_max < Eab[ k] ) {
Eab_max = Eab[ k] ;
}
printf ( "mccLab[%d]->Eab:%lf\n " , k, Eab[ k] ) ;
cvCvtColor( & pI_1, & pI_1, CV_Lab2BGR) ;
}
printf ( "Eab_sum:%lf\n " , Eab_sum) ;
}
最后在另一张空白图片上,将得到的Sat\Cab\Eab数据都显示出来。
sprintf ( showSat, "Sat:%.1lf" , Sat* 100 ) ;
sprintf ( showCab, "Cab: mean=%.1lf, max=%.1lf" , Cab_sum, Cab_max) ;
sprintf ( showEab, "Eab: mean=%.1lf, max=%.1lf" , Eab_sum, Eab_max) ;
cvPutText( & pI_barPic, showSat , cvPoint( 10 , 15 ) ,& font, CV_RGB( 255 , 0 , 0 ) ) ;
cvPutText( & pI_barPic, showCab , cvPoint( 10 , 30 ) ,& font, CV_RGB( 255 , 0 , 0 ) ) ;
cvPutText( & pI_barPic, showEab , cvPoint( 10 , 45 ) ,& font, CV_RGB( 255 , 0 , 0 ) ) ;
cv:: imshow ( barPic, src2) ;
结果发现,除了饱和度之外,Cab和Eab都和imatest有不小的差距。
对应的效果演示如下:
代码下载:http://download.csdn.net/detail/u011630458/8793681