1. 程式人生 > >用C++(OpenCV)自己實現彩色直方圖均衡化

用C++(OpenCV)自己實現彩色直方圖均衡化

對於直方圖均衡化,我的理解是一個影象原來的畫素集中在一個灰度範圍裡,我們對這種圖不敏感,因為其灰度值集中,變化不明顯。如果我們把這些畫素點的灰度範圍擴大,影象就會顯得比較清晰。直方圖均衡化就是通過一個對映函式,把原影象素點的灰度值對映為一個新的值,使得影象的畫素點灰度值在全部灰度範圍內比較均勻分佈。

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

第一步是統計每個灰度值出現的次數。

第二步是計算累積函式。

第三步是根據對映函式,建立查詢表。

第四步是根據查詢表計算新的畫素值。

OpenCV自帶的函式cvEqualizeHist可以實現直方圖均衡化,我參考OpenCV該函式的原始碼,自己寫了一個myEqualizeHist函式來實現。

另外,為了對均衡化的結果進行理性的分析,我還自己寫了一個畫直方圖的函式。

原理是我先統計好每個灰度值的畫素點個數,然後呼叫opencv裡的cvRectangle函式,對於每一個灰度值(0-255)都畫出一個對應高度的小矩形,矩形的高度就是該灰度值對應的畫素點的個數。

程式碼如下:

#include <highgui.h>   
#include <cv.h> 
#include <stdio.h>
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;
}