首先,我们先了解什么是直方图:
http://baike.baidu.com/view/1164383.htm
其实我们从小就有接触直方图这东西了,好像以前数学考试很多题目都是通过直方分布图来求各种分布情况和概率
其实,在OPENCV,直方图也是一个分解图像的各方面的概率,如RGB,HSV等,让我们更好利用这些概率去处理一些东西
总之几个词吧:形象,方便
灰度直方图的定义
灰度直方图是灰度级的函数,描述图像中该灰度级的像素个数(或该灰度级像素出现的频率):其横坐标是灰度级,纵坐标表示图像中该灰度级出现的个数(频率)。
一维直方图的结构表示为
高维直方图可以理解为图像在每个维度上灰度级分布的直方图。
我们以前会把直方图更多地想象成一幅“图”,继而理解图中横坐标,纵坐标的意义。而在OpenCV中,应该更多把直方图看做“数据结构”来理解。
下面我们直接来讲应用了:
创建直方图 cvCreateHist()
- CvHistogram* cvCreateHist(
- int dims, //直方图维数
- int* sizes,//直方图维数尺寸
- int type, //直方图的表示格式
- float** ranges=NULL,//图中方块范围的数组
- int uniform=1 //归一化标识
- );
CvHistogram* cvCreateHist(
int dims, //直方图维数
int* sizes,//直方图维数尺寸
int type, //直方图的表示格式
float** ranges=NULL, //图中方块范围的数组
int uniform=1 //归一化标识
);
通过cvCreateHist()我们创建一个直方图数据结构。
计算图像直方图的函数CalcHist():
- void cvCalcHist(
- IplImage** image, //输入图像(也可用CvMat**)
- CvHistogram* hist, //直方图指针
- int accumulate=0, //累计标识。如果设置,则直方图在开始时不被清零。
- const CvArr* mask=NULL//操作 mask, 确定输入图像的哪个象素被计数
- );
void cvCalcHist(
IplImage** image, //输入图像(也可用CvMat**)
CvHistogram* hist, //直方图指针
int accumulate=0, //累计标识。如果设置,则直方图在开始时不被清零。
const CvArr* mask=NULL //操作 mask, 确定输入图像的哪个象素被计数
);
第一个参数 输入图像跟我们上面创建的直方图维数相关,一维要一个图像,二维要输入两个图像
通过这个CalcHist()函数将我们的图像和上面新建的直方图打上联系
具体参数如何赋值如何用,我们看一下下面的例子:
一、显示一维图像的直方图
既然这里的直方图是数据结构,那我们如何才能显示出我们以前常见的RGB分布直方图的“图”呢?
一般在构建直方图数据结构有这么三个连续步骤:
- cvCreateHist() //创建直方图
- cvCalcHist()//直方图,图像计算
- cvNormalizeHist()//归一化
cvCreateHist() //创建直方图
cvCalcHist()//直方图,图像计算
cvNormalizeHist()//归一化
- //自定义显示直方图函数 窗口名 源图像
- void myShowHist(constchar* name,IplImage * src)
- {
- char window[100]; //新窗口
- IplImage* gray_plane = cvCreateImage(cvGetSize(src),8,1);
- cvCvtColor(src,gray_plane,CV_BGR2GRAY);
- int hist_size = 256; //直方图尺寸
- int hist_height = 256;
- // float range[] = {0,255}; //灰度级的范围
- // float* ranges[]={range};
- //创建一维直方图,统计图像在[0 255]像素的均匀分布
- // CvHistogram* gray_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);//参数完整版
- CvHistogram* gray_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY);//省略默认版
- //计算灰度图像的一维直方图
- cvCalcHist(&gray_plane,gray_hist,0,0);
- //归一化直方图
- cvNormalizeHist(gray_hist,1.0);
- //扩展倍数
- int scale = 2;
- //创建一张一维直方图的“图”,横坐标为灰度级,纵坐标为像素个数(*scale)
- IplImage* hist_image = cvCreateImage(cvSize(hist_size*scale,hist_height),8,3);
- cvZero(hist_image);
- //统计直方图中的最大直方块(按回比例除乘)
- float max_value = 0;
- cvGetMinMaxHistValue(gray_hist, 0,&max_value,0,0);
- //分别将每个直方块的值绘制到图中
- for(int i=0;i<hist_size;i++)
- {
- float bin_val = cvQueryHistValue_1D(gray_hist,i);//像素i的概率
- int intensity = cvRound(bin_val*hist_height/max_value);//要绘制的高度
- cvRectangle(hist_image,
- cvPoint(i*scale,hist_height-1),
- cvPoint((i+1)*scale - 1, hist_height - intensity),
- CV_RGB(255,255,255));
- }
- cvNamedWindow( name, 1 );
- cvShowImage(name,src);
- sprintf(window, "%s_histogram", name);//新窗口
- cvShowImage( window, hist_image );
- }
- int main(int argc,char** argv )
- {
- IplImage * src= cvLoadImage("baboon.jpg");
- myShowHist("Source",src);
- cvWaitKey(0);
- return 0;
- }
//自定义显示直方图函数 窗口名 源图像
void myShowHist(const char* name,IplImage * src)
{
char window[100]; //新窗口
IplImage* gray_plane = cvCreateImage(cvGetSize(src),8,1);
cvCvtColor(src,gray_plane,CV_BGR2GRAY);
int hist_size = 256; //直方图尺寸
int hist_height = 256;
// float range[] = {0,255}; //灰度级的范围
// float* ranges[]={range};
//创建一维直方图,统计图像在[0 255]像素的均匀分布
// CvHistogram* gray_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);//参数完整版
CvHistogram* gray_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY);//省略默认版
//计算灰度图像的一维直方图
cvCalcHist(&gray_plane,gray_hist,0,0);
//归一化直方图
cvNormalizeHist(gray_hist,1.0);
//扩展倍数
int scale = 2;
//创建一张一维直方图的“图”,横坐标为灰度级,纵坐标为像素个数(*scale)
IplImage* hist_image = cvCreateImage(cvSize(hist_size*scale,hist_height),8,3);
cvZero(hist_image);
//统计直方图中的最大直方块(按回比例除乘)
float max_value = 0;
cvGetMinMaxHistValue(gray_hist, 0,&max_value,0,0);
//分别将每个直方块的值绘制到图中
for(int i=0;i<hist_size;i++)
{
float bin_val = cvQueryHistValue_1D(gray_hist,i); //像素i的概率
int intensity = cvRound(bin_val*hist_height/max_value); //要绘制的高度
cvRectangle(hist_image,
cvPoint(i*scale,hist_height-1),
cvPoint((i+1)*scale - 1, hist_height - intensity),
CV_RGB(255,255,255));
}
cvNamedWindow( name, 1 );
cvShowImage(name,src);
sprintf(window, "%s_histogram", name); //新窗口
cvShowImage( window, hist_image );
}
int main(int argc, char** argv )
{
IplImage * src= cvLoadImage("baboon.jpg");
myShowHist("Source",src);
cvWaitKey(0);
return 0;
}
效果如下:
以下我们就可以用这个写好的子函数方便处理很多东西了O(∩_∩)O哈哈~
二、直方图均衡化
直方图均衡化(Histogram Equalization)是直方图最典型的应用,是图像点运算的一种。
直方图均衡化是通过灰度变换将一幅图像转换为另一幅具有均衡直方图,即在每个灰度级上都具有接近相同的像素点数过程。
如下图是理想的单纯高斯分布映射的示意图:
OpenCV中的cvEqualizeHist()
- void cvEqualizeHist( const CvArr* src, CvArr* dst );
void cvEqualizeHist( const CvArr* src, CvArr* dst );
参数很明确简单,但是要注意下面:
此函数只能处理单通道的灰色图像,对于彩色图像,我们可以把每个信道先Split出来,分别均衡化,再Merge为彩色图像
- int main(int argc,char** argv )
- {
- IplImage * image= cvLoadImage("baboon.jpg");
- myShowHist("Source",image);
- IplImage* eqlimage=cvCreateImage(cvGetSize(image),image->depth,3);
- //分别均衡化每个信道
- 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);<SPAN style="WHITE-SPACE: pre"> </SPAN>//分解3通道
- <SPAN style="WHITE-SPACE: pre"> </SPAN>
- cvEqualizeHist(redImage,redImage);
- cvEqualizeHist(greenImage,greenImage);
- cvEqualizeHist(blueImage,blueImage);
- //均衡化后的图像
- cvMerge(blueImage,greenImage,redImage,NULL,eqlimage);//合并
- myShowHist("Equalized",eqlimage);
- cvWaitKey(0);
- return 0;
- }
int main(int argc, char** argv )
{
IplImage * image= cvLoadImage("baboon.jpg");
myShowHist("Source",image);
IplImage* eqlimage=cvCreateImage(cvGetSize(image),image->depth,3);
//分别均衡化每个信道
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); //分解3通道
cvEqualizeHist(redImage,redImage);
cvEqualizeHist(greenImage,greenImage);
cvEqualizeHist(blueImage,blueImage);
//均衡化后的图像
cvMerge(blueImage,greenImage,redImage,NULL,eqlimage);//合并
myShowHist("Equalized",eqlimage);
cvWaitKey(0);
return 0;
}
现象如下:
均衡前:
均衡后: