图像直方图是反映一个图像像素分布的统计表,其实横坐标表明了图像像素的种类,能够是灰度的,也能够是彩色的。纵坐标表明了每一种颜色值在图像中的像素总数或者占全部像素个数的百分比。算法
图像是由像素构成,由于反映像素分布的直方图每每能够做为图像一个很重要的特征。在实际工程中,图像直方图在特征提取、图像匹配等方面都有很好的应用。数组
OpenCV中计算图像直方图像函数是calcHist,它的参数比较多,下面分析一下它的接口和用法。ide
1
|
void
calcHist(
const
Mat* images,
int
nimages,
const
int
* channels, InputArray mask, OutputArray hist,
int
dims,
const
int
*histSize,
const
float
** ranges,
bool
uniform=
true
,
bool
accumulate=
false
);
|
const Mat* images:为输入图像的指针。函数
int nimages:要计算直方图的图像的个数。此函数能够为多图像求直方图,咱们一般状况下都只做用于单一图像,因此一般nimages=1。spa
const int* channels:图像的通道,它是一个数组,若是是灰度图像则channels[1]={0};若是是彩色图像则channels[3]={0,1,2};若是是只是求彩色图像第2个通道的直方图,则channels[1]={1};3d
IuputArray mask:是一个遮罩图像用于肯定哪些点参与计算,实际应用中是个很好的参数,默认状况咱们都设置为一个空图像,即:Mat()。指针
OutArray hist:计算获得的直方图code
int dims:获得的直方图的维数,灰度图像为1维,彩色图像为3维。orm
const int* histSize:直方图横坐标的区间数。若是是10,则它会横坐标分为10份,而后统计每一个区间的像素点总和。blog
const float** ranges:这是一个二维数组,用来指出每一个区间的范围。
后面两个参数都有默认值,uniform参数代表直方图是否等距,最后一个参数与多图像下直方图的显示与存储有关。
下面咱们来计算一幅图像的灰度直方图,彩色直方图以及自定义的灰度分布图。
灰度直方图:
1
2
3
4
5
6
7
8
9
10
11
12
|
int
main()
{
Mat Image=imread(
"../cat.png"
);
cvtColor(Image,Image,CV_BGR2GRAY);
const
int
channels[1]={0};
const
int
histSize[1]={256};
float
hranges[2]={0,255};
const
float
* ranges[1]={hranges};
MatND hist;
calcHist(&Image,1,channels,Mat(),hist,1,histSize,ranges);
return
0;
}
|
彩色直方图:
1
2
3
4
5
6
7
8
9
10
11
|
int
main()
{
Mat Image=imread(
"../cat.png"
);
const
int
channels[3]={0,1,2};
const
int
histSize[3]={256,256,256};
float
hranges[2]={0,255};
const
float
* ranges[3]={hranges,hranges,hranges};
MatND hist;
calcHist(&Image,1,channels,Mat(),hist,3,histSize,ranges);
return
0;
}
|
不均匀直方图,咱们分别统计0-50,50-80,80-150,150-230,230-255区间的灰度分布:
1
2
3
4
5
6
7
8
9
10
11
12
|
int
main()
{
Mat Image=imread(
"../cat.png"
);
cvtColor(Image,Image,CV_BGR2GRAY);
const
int
channels[1]={0};
int
histSize[1]={5};
float
hranges[6]={0,50,80,150,230,255};
const
float
* ranges[1]={hranges};
MatND hist;
calcHist(&Image,1,channels,Mat(),hist,1,histSize,ranges,
false
);
return
0;
}
|
从上面的例子中咱们能够看出,直方图计算获得的其实是一个多维数组,这并不够直观,咱们但愿可以像在Excel中把相关数据经过表的形式表示出来。
下面经过划线函数来把一个灰度直方图显示出来:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
Mat getHistImg(
const
MatND& hist)
{
double
maxVal=0;
double
minVal=0;
//找到直方图中的最大值和最小值
minMaxLoc(hist,&minVal,&maxVal,0,0);
int
histSize=hist.rows;
Mat histImg(histSize,histSize,CV_8U,Scalar(255));
// 设置最大峰值为图像高度的90%
int
hpt=
static_cast
<
int
>(0.9*histSize);
for
(
int
h=0;h<histSize;h++)
{
float
binVal=hist.at<
float
>(h);
int
intensity=
static_cast
<
int
>(binVal*hpt/maxVal);
line(histImg,Point(h,histSize),Point(h,histSize-intensity),Scalar::all(0));
}
return
histImg;
}
|
直方图变换是图像处理中一个很重要的概念,图像直方图能够反映出图像对比度,明暗程度等特征,因此咱们能够利用直方图的变换进行图像画面的调节。
下面介绍两个简单的直方图变换函数:直方图拉伸与直方图均衡化。
若是图像的灰度在直方图上显示集中在某一个区间,则说明图像色彩单一,咱们能够将其扩展到更宽的灰度范围内让图像更有层次感。
变换函数:将图像的一种灰度值通过变换获得另外一个灰度。
直方图变换的核心就是变换函数,s=T(r),r是变换前的灰度值,s是变换后的灰度值,如要咱们想将[a,b]区间的灰度变换到[0,255]范围内,则变换函数是:T(r)=255*(r-a)/(b-a)。
咱们在OpenCV中建立这样一个变换函数:
1
2
3
4
5
6
7
8
9
10
11
12
|
// 建立一个1*256的矢量
Mat lut(1,256,CV_8U);
for
(
int
i=0;i<256;i++)
{
if
(lut.at<uchar>(i)<imin)
lut.at<uchar>(i)=0;
else
if
(lut.at<uchar>(i)>imax)
lut.at<uchar>(i)=255;
else
lut.at<uchar>(i)=
static_cast
<uchar>(
255.0*(i-imin)/(imax-imin)+0.5);
}
|
其中imax,imin是图像中的最小灰度与最大灰度。咱们能够从直方图中求出:
1
2
3
4
5
6
7
8
9
10
11
|
int
imax,imin;
for
(imin=0;imin<256;imin++)
{
if
(hist.at<uchar>(imin)>minValue)
break
;
}
for
(imax=255;imax>-1;imax--)
{
if
(hist.at<uchar>(imax)>minValue)
break
;
}
|
最后咱们应用OpenCV中的LUT函数,把变换应用在直方图上便可。
1
|
LUT(image,lut,result);
|
第二个参数就像一个查找表同样,将原图像中的灰度按表查找,而后把灰度值替换为表中对应的值。
有了上面灰度拉伸的例子就不难理解图像的直方图均衡了,直方图均衡化可让图像灰度分布更加均匀,让图像的对比度加强。
在OpenCV中直方图均衡不用像灰度拉伸那样先构造一个变换函数,它有直接对应的函数,固然你若是有兴趣也能够去尝试写一下变换函数均衡化的变换原理会稍复杂一些,在OpenCV这个系列里面,不会太多的介绍数字图像中的算法,之后有机会再专门来讨论。
1
2
3
4
5
6
7
8
9
|
int
main()
{
Mat Image=imread(
"../cat.png"
);
cvtColor(Image,Image,CV_BGR2GRAY);
Mat result;
equalizeHist(Image,result);
return
0;
}
|