圖像直方圖是反映一個圖像像素分佈的統計表,其實橫座標表明了圖像像素的種類,能夠是灰度的,也能夠是彩色的。縱座標表明了每一種顏色值在圖像中的像素總數或者佔全部像素個數的百分比。算法
圖像是由像素構成,由於反映像素分佈的直方圖每每能夠做爲圖像一個很重要的特徵。在實際工程中,圖像直方圖在特徵提取、圖像匹配等方面都有很好的應用。數組
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;
}
|