简介
本篇讲解如何测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/25、包含亮度的色彩偏差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