检测色卡饱和度和色彩偏差

简介

  本篇讲解如何测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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值