图像灰度化

     图像灰度化就是彩色图像转化成为灰度图像,即剔除彩色图像中的彩色信息,只包含亮度信息。彩色图像中的每个像素的颜色有三个分量决定,而每个分量有255种值可取,这样一个像素点可以有1600多万的颜色的变化范围。而灰度图像是三个分量相同的一种特殊的彩色图像,其一个像素点的变化范围为255种,所以在数字图像处理中一般先将各种格式的图像转变成灰度图像以使后续的图像的计算量降低。灰度图像的描述与彩色图像一样仍然反映了整幅图像的整体和局部的色度和亮度等级的分布和特征。图像的灰度化处理常用一下几种方法:

RGB空间图像

     定义于RGB空间的彩色图,其每个像素点的色彩由R、G、B三个分量共同决定。每个分量在内存所占的位数共同决定了图像深度,即每个像素点所占的字节数。以常见的24深度彩色RGB图来说,其三个分量各占1个字节,这样每个分量可以取值为0~255,这样一个像素点可以有1600多万(255*255*255)的颜色的变化范围。对这样一幅彩色图来说,其对应的灰度图则是只有8位的图像深度(可认为它是RGB三个分量相等),这也说明了灰度图图像处理所需的计算量确实要少。不过需要注意的是,虽然丢失了一些颜色等级,但是从整幅图像的整体和局部的色彩以及亮度等级分布特征来看,灰度图描述与彩色图的描述是一致的。
     对于RGB图像进行灰度化,通俗点说就是对图像的RGB三个分量进行加权平均得到最终的灰度值。最常见的加权方法如下:  


      1)Gray=B;Gray=G;Gray=R

      2)Gray=max(B+G+R)

      3)Gray=(B+G+R)/3

      4)Gray= 0.072169B+ 0.715160G+ 0.212671R

      5)Gray= 0.11B+ 0.59G+ 0.3R

     这三种方法中,第一种为分量法,即用RGB三个分量的某一个分量作为该点的灰度值;第二种方法为最大值法,将彩色图像中的三分量亮度的最大值作为灰度图的灰度值。第三种方法将彩色图像中的三分量亮度求平均得到一个灰度图;后两种都是属于加权平均法,其中第四种是OpenCV开放库所采用的灰度权值,第五种为从人体生理学角度所提出的一种权值(人眼对绿色的敏感最高,对蓝色敏感最低)。

 3、代码实现

     本文旨在对整个实现原理及思路进行总结,因此以下基于OpenCv的基本函数实现这几种变化过程,至于位图以及其他形式的图像,在获取了图像原始数据后,处理方法都一样,仅需注意指针的位置操作即可。具体代码如下:



IplImage	*ColorImage;                        //定义相应的图像指针
	IplImage	*GrayImage1;                        //从1~5代表5中不同权值的结果
	IplImage	*GrayImage2;
	IplImage	*GrayImage3;
	IplImage	*GrayImage4;
	IplImage	*GrayImage5;
	IplImage	*GrayImage6;
	IplImage	*GrayImage7;

	ColorImage = cvLoadImage( "49138.jpg", -1 );    //读取图片
	if (ColorImage == NULL)
		return;	
	
	GrayImage1 = cvCreateImage(cvGetSize(ColorImage),8,1);
	GrayImage2 = cvCreateImage(cvGetSize(ColorImage),8,1);
	GrayImage3 = cvCreateImage(cvGetSize(ColorImage),8,1);
	GrayImage4 = cvCreateImage(cvGetSize(ColorImage),8,1);
	GrayImage5 = cvCreateImage(cvGetSize(ColorImage),8,1);
	GrayImage6 = cvCreateImage(cvGetSize(ColorImage),8,1);
	GrayImage7 = cvCreateImage(cvGetSize(ColorImage),8,1);

	CvMat* pGrayMat1 = NULL;         //定义与图像关联的数据指针
	CvMat* pGrayMat2 = NULL;
	CvMat* pGrayMat3 = NULL;
	CvMat* pGrayMat4 = NULL;
	CvMat* pGrayMat5 = NULL;
	CvMat* pGrayMat6 = NULL;
	CvMat* pGrayMat7 = NULL;

	pGrayMat1 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
	pGrayMat2 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
	pGrayMat3 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
	pGrayMat4 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
	pGrayMat5 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
	pGrayMat6 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);
	pGrayMat7 = cvCreateMat(ColorImage->height, ColorImage->width, CV_32FC1);

	BYTE data1;       //中间过程变量
	BYTE data2;
	BYTE data3;
	BYTE data4;
	BYTE data5;
	BYTE data6;
	BYTE data7;
	for(int j=0; j<ColorImage->height; j++)
	{
		for(int i=0; i<ColorImage->width; i++)
		{			
			data1 = (BYTE)ColorImage->imageData[j*ColorImage->widthStep + i*3];     //B分量
			data2 = (BYTE)ColorImage->imageData[j*ColorImage->widthStep + i*3 + 1]; //G分量
			data3 = (BYTE)ColorImage->imageData[j*ColorImage->widthStep + i*3 + 2]; //R分量
			data4 = max(data1, max(data2, data3));    //最大值
			data5 = (BYTE)((data1 + data2 + data3)/3);
			data6 = (BYTE)(0.072169*data1 + 0.715160*data2 + 0.212671*data3);
			data7 = (BYTE)(0.11*data1 + 0.59*data2 + 0.30*data3);
			cvmSet(pGrayMat1, j, i, data1);
			cvmSet(pGrayMat2, j, i, data2);
			cvmSet(pGrayMat3, j, i, data3);
			cvmSet(pGrayMat4, j, i, data4);
			cvmSet(pGrayMat5, j, i, data5);
			cvmSet(pGrayMat6, j, i, data6);
			cvmSet(pGrayMat7, j, i, data6);
		}
	}
	cvConvert(pGrayMat1, GrayImage1);
	cvConvert(pGrayMat2, GrayImage2);
	cvConvert(pGrayMat3, GrayImage3);
	cvConvert(pGrayMat4, GrayImage4);
	cvConvert(pGrayMat5, GrayImage5);
	cvConvert(pGrayMat6, GrayImage6);
	cvConvert(pGrayMat7, GrayImage7);

	cvNamedWindow( "ColorImage",CV_WINDOW_AUTOSIZE);
	cvNamedWindow( "GrayImage1",CV_WINDOW_AUTOSIZE);
	cvNamedWindow( "GrayImage2",CV_WINDOW_AUTOSIZE);
	cvNamedWindow( "GrayImage3",CV_WINDOW_AUTOSIZE);
	cvNamedWindow( "GrayImage4",CV_WINDOW_AUTOSIZE);
	cvNamedWindow( "GrayImage5",CV_WINDOW_AUTOSIZE);
	cvNamedWindow( "GrayImage6",CV_WINDOW_AUTOSIZE);
	cvNamedWindow( "GrayImage7",CV_WINDOW_AUTOSIZE);

	cvShowImage("ColorImage", ColorImage);
	cvShowImage("GrayImage1", GrayImage1);
	cvShowImage("GrayImage2", GrayImage2);
	cvShowImage("GrayImage3", GrayImage3);
	cvShowImage("GrayImage4", GrayImage4);
	cvShowImage("GrayImage5", GrayImage5);
	cvShowImage("GrayImage6", GrayImage6);
	cvShowImage("GrayImage7", GrayImage7);
		
	cvWaitKey(0);
	cvDestroyWindow("ColorImage");
	cvDestroyWindow("GrayImage1");
	cvDestroyWindow("GrayImage2");
	cvDestroyWindow("GrayImage3");
	cvDestroyWindow("GrayImage4");
	cvDestroyWindow("GrayImage5");
	cvDestroyWindow("GrayImage6");
	cvDestroyWindow("GrayImage7");
	
	cvReleaseImage(&ColorImage);
	cvReleaseImage(&GrayImage1);
	cvReleaseImage(&GrayImage2);
	cvReleaseImage(&GrayImage3);
	cvReleaseImage(&GrayImage4);
	cvReleaseImage(&GrayImage5);
	cvReleaseImage(&GrayImage6);
	cvReleaseImage(&GrayImage7);
	
	cvReleaseMat(&pGrayMat1);
	cvReleaseMat(&pGrayMat2);
	cvReleaseMat(&pGrayMat3);
	cvReleaseMat(&pGrayMat4);
	cvReleaseMat(&pGrayMat5);
	cvReleaseMat(&pGrayMat6);
	cvReleaseMat(&pGrayMat7);

      
     上文的代码实现中,以最后一种方法为例,用到了如下代码:

     Gray= (0.11* Blue + 0.59* Green + 0.30* Red);

     实际计算机处理时,这种方法已经很快了,但实际上还存在可以优化的余地。以上代码所采用的是浮点运算。而在图像处理中,速度就是生命,实时性往往是很重要的指标,这就要求我们在实现算法时必须考虑到代码的效率问题。所以有一个原则:在图像处理中,能不用浮点运算,就最好不要用!

     因此,上述代码可以等效的优化为:

     Gray = (30 * Red + 59 *Green + 11 * Blue) / 100;

     这样一改,可以有效避免浮点运算,因此可以提高代码的效率

     对这行代码还可以继续改进为如下:

     Gray= HiByte(77 * Red + 151 * Green + 28 * Blue);

     其中77,151,28分别除以256,即为上文的三个系数。

     同样的,还可以实现为:

     Gray= (77 * Red + 151 * Green + 28 * Blue) shr 8;

     这种方法实现了移位运算,避免了除法,效率上又有所提高。