用C++(OpenCV)自己实现彩色直方图均衡化

对于直方图均衡化,我的理解是一个图像原来的像素集中在一个灰度范围里,我们对这种图不敏感,因为其灰度值集中,变化不明显。如果我们把这些像素点的灰度范围扩大,图像就会显得比较清晰。直方图均衡化就是通过一个映射函数,把原图像素点的灰度值映射为一个新的值,使得图像的像素点灰度值在全部灰度范围内比较均匀分布。


直方图均衡化主要有四步:

第一步是统计每个灰度值出现的次数。

第二步是计算累积函数。

第三步是根据映射函数,建立查找表。

第四步是根据查找表计算新的像素值。


OpenCV自带的函数cvEqualizeHist可以实现直方图均衡化,我参考OpenCV该函数的源代码,自己写了一个myEqualizeHist函数来实现。


另外,为了对均衡化的结果进行理性的分析,我还自己写了一个画直方图的函数。

原理是我先统计好每个灰度值的像素点个数,然后调用opencv里的cvRectangle函数,对于每一个灰度值(0-255)都画出一个对应高度的小矩形,矩形的高度就是该灰度值对应的像素点的个数。


代码如下:

#include    
#include  
#include 
using namespace cv;
using namespace std;

IplImage* myDrawHistogram(int* hist_cal)
{
	//找出直方图中最大值,以便进行归一化
	int hist_max = 0;
	for(int i = 0; i < 256; i++)
	{
		if(hist_cal[i] > hist_max)
			hist_max = hist_cal[i];
	}

	//创建一幅空白的图像,用来显示直方图
	IplImage* hist_image = cvCreateImage(cvSize(256 * 3, 64 * 3), 8, 1);
	cvZero(hist_image);

	for(int i = 0; i < 256; i++)
	{
		CvPoint p0 = cvPoint(i * 3, 0);
		CvPoint p1 = cvPoint((i + 1) * 3, cvRound(((hist_max - hist_cal[i]) * 64) / hist_max * 3));
        cvRectangle(hist_image, p0, p1, cvScalar(255, 255, 255), -1, 8, 0); 
	}
	return hist_image;
}

int p[256];
int dst_p[256];
void myEqualizeHist(CvArr* srcarr, CvArr* dstarr)
{
	CvMat sstub;
	CvMat dstub;
	CvMat *src = cvGetMat(srcarr, &sstub);//convert CvArr to CvMat
	CvMat *dst = cvGetMat(dstarr, &dstub);

	CvSize size = cvGetMatSize(src);
	
	int x, y;
	const int hist_size = 256;
	//int p[hist_size];//p数组长度为图像的灰度等级(一般为256)
	fill(p, p + hist_size, 0);//初始化为0

	//扫描图像的每一个像素点,像素值为k则hist[k]++
	for(y = 0; y < size.height; y++)
	{
		const uchar* sptr = src->data.ptr + src->step * y;
		for(x = 0; x < size.width; x++)
		{
			p[sptr[x]]++;//相当于[x][y]这个点对应的像素值++
		}
	}

	int c[hist_size];
	c[0] = p[0];
	//累积函数
	for(int i = 1; i < hist_size; i++)
	{
		c[i] = c[i - 1] + p[i];
	}

	uchar lut[hist_size];
	//根据映射函数,建立look up table
	for(int i = 0; i < hist_size; i++)
	{
		int val = cvRound(c[i] * (255.f / (size.width * size.height)));
		lut[i] = CV_CAST_8U(val);//像素值i映射之后值为lut[i]
	}

	//根据look up table,改变图像像素值
	for(y = 0; y < size.height; y++)
	{
		const uchar* sptr = src->data.ptr + src->step * y;
		uchar* dptr = dst->data.ptr + dst->step * y;
		for(x = 0; x < size.width; x++)
		{
			dptr[x] = lut[sptr[x]];
		}
	}

	//计算直方图均衡化之后的图像的像素分布
	//int dst_p[hist_size];
	fill(dst_p, dst_p + hist_size, 0);
	for(y = 0; y < size.height; y++)
	{
		const uchar* dst_sptr = dst->data.ptr + dst->step * y;
		for(x = 0; x < size.width; x++)
		{
			dst_p[dst_sptr[x]]++;//相当于[x][y]这个点对应的像素值++
		}
	}
	
}

int main()
{
	IplImage* image = cvLoadImage("D:\\lena.jpg", 1);
	cvShowImage("原图像", image); 
	IplImage* redImage = cvCreateImage(cvGetSize(image), image->depth, 1);
    IplImage* greenImage = cvCreateImage(cvGetSize(image), image->depth, 1);
    IplImage* blueImage = cvCreateImage(cvGetSize(image), image->depth, 1);
	cvSplit(image, blueImage, greenImage, redImage, NULL);

	myEqualizeHist(redImage, redImage);
	cvShowImage("对图像进行直方均衡化之前的R直方图", myDrawHistogram(p));
	cvShowImage("对图像进行直方均衡化之后的R直方图", myDrawHistogram(dst_p));
	myEqualizeHist(greenImage, greenImage);
	cvShowImage("对图像进行直方均衡化之前的G直方图", myDrawHistogram(p));
	cvShowImage("对图像进行直方均衡化之后的G直方图", myDrawHistogram(dst_p));
	myEqualizeHist(blueImage, blueImage);
	cvShowImage("对图像进行直方均衡化之前的B直方图", myDrawHistogram(p));
	cvShowImage("对图像进行直方均衡化之后的B直方图", myDrawHistogram(dst_p));

	cvMerge(blueImage, greenImage, redImage, NULL, image);
	cvShowImage("自己实现的直方图均衡化", image);
    waitKey(0);
    return 0;
}

用C++(OpenCV)自己实现彩色直方图均衡化_第1张图片用C++(OpenCV)自己实现彩色直方图均衡化_第2张图片用C++(OpenCV)自己实现彩色直方图均衡化_第3张图片用C++(OpenCV)自己实现彩色直方图均衡化_第4张图片用C++(OpenCV)自己实现彩色直方图均衡化_第5张图片用C++(OpenCV)自己实现彩色直方图均衡化_第6张图片用C++(OpenCV)自己实现彩色直方图均衡化_第7张图片用C++(OpenCV)自己实现彩色直方图均衡化_第8张图片

你可能感兴趣的:(数字信号处理)