openCV直方图均衡化

1、什么是直方图均衡化

直方图是对图像像素的统计分布,它统计了每个像素(0到L-1)的数量。直方图均衡化就是将原始的直方图拉伸,使之均匀分布在全部灰度范围内,从而增强图像的对比度。直方图均衡化的中心思想是把原始图像的的灰度直方图从比较集中的某个区域变成在全部灰度范围内的均匀分布。

在一幅图像中,明亮图像的直方图倾向于灰度级高的一侧,灰暗图像的直方图倾向于灰度级低的一侧,如果一副图像占有全部可能的灰度级并且分布均匀,则这样的图像有高对比度和多变的灰度色调。直方图均衡化这种方法通常用来增加图像的局部对比度。所以这种方法对于图像前景和背景都太亮或者太暗的情况非常有用,使目标区域从背景脱离出来。

 讲到这里不得不引入累计分布函数 CDF,其定义为:

     对于连续函数,所有小于等于a的值,其出现概率的和。F(a) = P(x<=a)。

     针对于灰度级从[0,L-1]的图像,则是统计各个灰度级的累计分布概率,灰度值k(0<k<L-1)的累计概率是P(0), P(1),P(2).....P(K)之和。

2、实现过程

STEP:
1、统计直方图个个灰度级出现的次数
2、累计归一化的直方图,
3、重新计算像素值

我们来看看C代码(简化版)是如何一步一步实现的:

for(int i=0;i<height;i++){
	for(int j=0;j<width;j++){
		//s[i][j] 为像素值
		n[ s[i][j] ]++;
	}
}

for(i=0;i<L;i++){
	//p[i]表示灰度级为i的像素在整图像中出现的概率,
	p[i] = n[i] / (width*height);
}

//step 2 
for(i=0;i<L;i++){
	for(k=0;k<=i; k++){
		//c[]存储的是累计的归一化直方图
		c[i] += p[k];
	}
}

//step 3
//set the new pixel
for(int i=0;i<height;i++){
	for(int j=0;j<width;j++){
		//max is the max value of s[i][j]
		s[i][j] = c[ s[i][j] ] *(max-min) + min;
	}
}

示意图如下:

左边是原始像素值,Ni是统计的某个灰度级数量,Pi是出现的概率 ,sumPi是累计的归一化概率,最后是重新分配的像素值。


3、matlab代码实现

matlab实现:

%读入图像并进行直方图绘制
% ZhangFL  at SWPU 2017-07-03
%
clear;
img = imread('test.tif');
[height,width] = size(img);%return (rows,cols)


numberPix = zeros(1, 256);
for i=1:height
    for j=1:width
        numberPix( img(i,j) + 1 ) = numberPix( img(i,j)+1 ) + 1;%像素值有0,所以+1
    end
end

p = zeros(1,256);
for i=1:256
    p(i) = numberPix(i) / (width * height);
end;

    
pSum = zeros(1,256);
for i=1:256
    if i == 1
        pSum(i) = p(i);
    else
        pSum(i) = pSum(i-1) + p(i);
    end;
end
%累计分布取整
newPix = uint8(pSum.*255 + 0.5);%change double to int 
for i=1:height
    for j=1:width
        imgNew(i,j) = newPix( img(i,j) );
    end
end

subplot(2,2,1),imshow(img),title('原图像');
subplot(2,2,2),imhist(img),title('原图像直方图');
subplot(2,2,3),imshow(imgNew),title('直方图均衡化图像');
subplot(2,2,4),imhist(imgNew),title('新的直方图');

结果图:


4、openCV代码实现

首先对于灰度图,我们可以直接处理,或者将彩色图作为灰度图读入处理,前提是我们只想得到灰度图。由于直方图均衡化中原始图像和目标图像都必须是单通道,因此,如果我们想要处理彩色图,需要先将三通道分离,对其分别进行均衡化操作,然后再合成三通道图像。

这里补充说下,openCV 中函数:IplImage* cvLoadImage( const char* filename, int flags=CV_LOAD_IMAGE_COLOR );

        采用IplImage *img = cvLoadImage(picName)默认值是CV_LOAD_IMAGE_COLOR  读取无论原始图像的通道数是多少,都将被转换为3个通道读入。

所以我们如果想按灰度图读入应该采用:IplImage *img = cvLoadImage(picName,CV_LOAD_IMAGE_GRAYSCALE); 之前由于忽略了这一点,明明将图像分离三通道,并且分别保存了下来,然后再读取,结果通道数还是3,纠结了好久,,,这就是没有加flag的祸害啊。所以,最好还是加上flag.


首先openCV没有直方图直接显示的函数,所以我们需要创建直方图来自定义绘图,函数如下:

/*
	 width:直方图宽度     height:直方图高度     scale:
*/
IplImage* showImageHistogram(IplImage** image, int width, int height ,int scale){

	int dims = 1;
	int histSize = 256;
	float frange[] = { 0, 255 };
	float* ranges[] = { frange };
	//创建一个直方图     CV_HIST_ARRAY多维密集数组
	CvHistogram*  hist = cvCreateHist(dims, &histSize, CV_HIST_ARRAY, ranges);
	//根据输入图像计算直方图
	cvCalcHist(image, hist);


	//绘制直方图区域
	IplImage* histImage = cvCreateImage(cvSize(width*scale, height), IPL_DEPTH_8U, 1);
	//直方图背景区域置位白色
	cvRectangle(histImage, cvPoint(0, 0), cvPoint(histImage->width, histImage->height), CV_RGB(255,255,255), CV_FILLED);
	//获取最大值
	float maxHistValue = 0;
	cvGetMinMaxHistValue(hist, NULL, &maxHistValue, NULL,NULL);
	//绘制各个灰度级的直方图
	for (int i = 0; i < width; i++){
		float value = cvQueryHistValue_1D(hist, i);
		int drawHeight = cvRound((value / maxHistValue) * height);
		cvRectangle(histImage, cvPoint(i*scale, height-1), cvPoint((i+1)*scale -1, height-drawHeight), cvScalar(i, 0, 0, 0), CV_FILLED);
	}
	return histImage;

}

直接处理灰度图:

void histGrayChange(){
	const char* picName = "test.tif";//test.tif lenaRGB.tif
	//采用IplImage *img = cvLoadImage(picName)默认值是CV_LOAD_IMAGE_COLOR  读取无论原始图像的通道数是多少,都将被转换为3个通道读入。
	//IplImage *img = cvLoadImage(picName);

	//******以灰度图像读入,强制转换为单通道*****
	IplImage *img = cvLoadImage(picName,CV_LOAD_IMAGE_GRAYSCALE);
	if (img == NULL){
		cout << "Load File Failed." << endl;
	}
	cout << "ChannelL:" << img->nChannels;


	IplImage* imgDst = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	//直方图均衡化
	cvEqualizeHist(img, imgDst);
	
	cvNamedWindow("Origin", CV_WINDOW_AUTOSIZE);
	cvShowImage("Origin", img);

	cvNamedWindow("Result", CV_WINDOW_AUTOSIZE);
	cvShowImage("Result", imgDst);

	//
	int histImageWidth = 255;
	int histImageHeight = 150;
	int histImageScale = 2;
	IplImage *histImage1 = showImageHistogram(&img, histImageWidth, histImageHeight, histImageScale);
	cvNamedWindow("Hist1", CV_WINDOW_AUTOSIZE);
	cvShowImage("Hist1", histImage1);

	IplImage *histImage2 = showImageHistogram(&imgDst, histImageWidth, histImageHeight, histImageScale);
	cvNamedWindow("Hist2", CV_WINDOW_AUTOSIZE);
	cvShowImage("Hist2", histImage2);


	cvWaitKey();

	cvDestroyWindow("Origin"); cvReleaseImage(&img);
	cvDestroyWindow("Result"); cvReleaseImage(&imgDst);

}
showImageHistogram()函数是直方图绘制函数,后面会讲到。


彩色图直方图均衡化:

void histColorChange(){
	const char* picName = "lenaRGB.tif";
	//采用IplImage *img = cvLoadImage(picName)默认值是CV_LOAD_IMAGE_COLOR  读取无论原始图像的通道数是多少,都将被转换为3个通道读入。
	//IplImage *img = cvLoadImage(picName,CV_LOAD_IMAGE_GRAYSCALE);
	IplImage *img = cvLoadImage(picName);
	if (img == NULL){
		cout << "Load File Failed." << endl;
	}
	IplImage* imgChannel[4] = { NULL, NULL, NULL, NULL };
	IplImage* imgDst = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3);
		
	//创建单通道图像
	for (int i = 0; i < img->nChannels; i++){
		imgChannel[i] = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	}
	//图像三通道分离
	cvSplit(img, imgChannel[0], imgChannel[1], imgChannel[2], imgChannel[3]);//BGRA

	for (int i = 0; i < img->nChannels; i++){
		//直方图均衡化中原始图像和目标图像都必须是单通道  
		cvEqualizeHist(imgChannel[i], imgChannel[i]);
	}
	//通道组合
	cvMerge(imgChannel[0], imgChannel[1], imgChannel[2], NULL, imgDst);
	
	cvNamedWindow("Origin", CV_WINDOW_AUTOSIZE);
	cvShowImage("Origin",img);

	//直方图均衡化
	cvNamedWindow("Hist", CV_WINDOW_AUTOSIZE);
	cvShowImage("Hist", imgDst);

	cvWaitKey();

}




参考:直方图均衡化详解及编程实现(matlab)

           灰度之直方图均衡化 (openCV)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值