灰度直方圖是圖像中像素灰度集的一種統(tǒng)計(jì)反應(yīng)。它能夠描述圖像中灰度的分布情況,直觀地展現(xiàn)出圖像中灰度所占多少。直方圖橫軸表示像素的灰度范圍(比如說 0~255),縱軸表示的是像素的數(shù)量或者密度。亮暗、對(duì)比度、圖像中的內(nèi)容不同,直方圖的表現(xiàn)也會(huì)不同。本文主要參考《岡薩雷斯》一書。

1.直方圖均衡
有的圖像的灰度分布不均勻,出現(xiàn)過亮過暗,或者對(duì)比度過低的情況,這樣的圖像細(xì)節(jié)不明顯,在肉眼觀察時(shí)會(huì)丟失一些信息。這時(shí)可以使用直方圖均衡技術(shù)對(duì)圖像進(jìn)行變換,變成肉眼易于分辨的細(xì)節(jié)分明的圖像。

要對(duì)直方圖進(jìn)行均衡,首先要通過統(tǒng)計(jì)得到原圖像的直方圖,然后通過下面這個(gè)神奇的公式,對(duì)灰度值進(jìn)行變換。其中 r 是輸入像素的灰度,函數(shù) T 表示一種變換,s 是輸出像素的灰度,pr 是原圖像灰度的PDF(概率密度函數(shù))。至于這個(gè)公式怎么來的,《岡薩雷斯》一書上貌似并沒有講清楚,但其實(shí)可以通過直覺來理解。

圖像是離散的,所以實(shí)際中使用的是離散形式

那么使用上面的公式,就可以將直方圖變換成這個(gè)樣子,這樣的圖像一般具有比較好的細(xì)節(jié)表現(xiàn)。

舉個(gè)書上的栗子就很好理解了
2.直方圖匹配(規(guī)定化)
一般來說,直方圖均衡能夠自動(dòng)地確定變換函數(shù),且輸出結(jié)果比較好,當(dāng)時(shí)需要自動(dòng)增強(qiáng)時(shí)是一種好方法。但有的情況下,使用直方圖均衡并不是最好的辦法。有時(shí)候我們可以指定特定的直方圖,而不是均勻分布的直方圖,并讓原圖像的直方圖變換成我們指定的形式。這個(gè)過程稱為直方圖匹配或者直方圖規(guī)定化。
在推導(dǎo)過程中,直方圖規(guī)定化的過程如下:
1.對(duì)原圖像進(jìn)行直方圖均衡。和上面一樣。

2.對(duì)事先規(guī)定的直方圖也進(jìn)行均衡。z為最終輸出圖像像素的灰度值。

3.那么從數(shù)學(xué)上可以得到反變換函數(shù)。對(duì)均衡后的圖像進(jìn)行反變換就可以得到直方圖規(guī)定化的結(jié)果了。

我這里做個(gè)圖解釋一下

同樣的,寫成離散形式。



同樣的,上例子
3.代碼實(shí)現(xiàn)
感覺OpenCV在直方圖處理這方面并不怎么走心。這里使用的是另一篇博客的類封裝和算法實(shí)現(xiàn)。
直方圖規(guī)定化中要注意兩點(diǎn):
實(shí)際操作中不會(huì)進(jìn)行兩次均衡化。在推導(dǎo)中發(fā)現(xiàn),假如sk 規(guī)定化后的對(duì)應(yīng)灰度是zm的話,需要滿足的條件是sk的累積概率和zm的累積概率是最接近的。所以可以根據(jù)計(jì)算累計(jì)密度的差值來進(jìn)行映射。
手動(dòng)輸入一個(gè)直方圖比較困難,這里使用一個(gè)參考圖像來進(jìn)行實(shí)現(xiàn)。參考圖像的直方圖就是我們指定的直方圖。
/********************************************************************
* Created by 楊幫杰 on 11/10/18
* Right to use this code in any way you want without
* warranty, support or any guarantee of it working
* E-mail: yangbangjie1998@qq.com
* Association: SCAU 華南農(nóng)業(yè)大學(xué)
********************************************************************/
#include <iostream>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/calib3d.hpp>
#define IMAGE1_PATH "/home/jacob/圖片/1.png"
#define IMAGE2_PATH "/home/jacob/圖片/2.png"
#define IMAGE3_PATH "/home/jacob/圖片/3.png"
using namespace std;
using namespace cv;
class Histogram1D
{
private:
int histSize[1]; // 項(xiàng)的數(shù)量
float hranges[2]; // 統(tǒng)計(jì)像素的最大值和最小值
const float* ranges[1];
int channels[1]; // 僅計(jì)算一個(gè)通道
public:
Histogram1D()
{
// 準(zhǔn)備1D直方圖的參數(shù)
histSize[0] = 256;
hranges[0] = 0.0f;
hranges[1] = 255.0f;
ranges[0] = hranges;
channels[0] = 0;
}
Mat getHistogram(const Mat &image)
{
Mat hist;
// 計(jì)算直方圖
calcHist(&image ,// 要計(jì)算圖像的
1, // 只計(jì)算一幅圖像的直方圖
channels, // 通道數(shù)量
Mat(), // 不使用掩碼
hist, // 存放直方圖
1, // 1D直方圖
histSize, // 統(tǒng)計(jì)的灰度的個(gè)數(shù)
ranges); // 灰度值的范圍
return hist;
}
Mat getHistogramImage(const Mat &image)
{
Mat hist = getHistogram(image);
//查找最大值用于歸一化
double maxVal = 0;
minMaxLoc(hist, NULL, &maxVal);
//繪制直方圖的圖像
Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));
// 設(shè)置最高點(diǎn)為最大值的90%
double hpt = 0.9 * histSize[0];
//每個(gè)條目繪制一條垂直線
for (int h = 0; h < histSize[0]; h++)
{
//直方圖的元素類型為32位浮點(diǎn)數(shù)
float binVal = hist.at<float>(h);
int intensity = static_cast<int>(binVal * hpt / maxVal);
line(histImg, Point(h, histSize[0]),
Point(h, histSize[0] - intensity), Scalar::all(0));
}
return histImg;
}
};
/**
* @brief EqualizeImage 對(duì)灰度圖像進(jìn)行直方圖均衡化
* @param src 輸入圖像
* @param dst 均衡化后的圖像
*/
void EqualizeImage(const Mat &src, Mat &dst)
{
Histogram1D hist1D;
Mat hist = hist1D.getHistogram(src);
hist /= (src.rows * src.cols); // 對(duì)得到的灰度直方圖進(jìn)行歸一化,得到密度(0~1)
float cdf[256] = {0}; // 灰度的累積概率
Mat lut(1, 256, CV_8U); // 創(chuàng)建用于灰度變換的查找表
for (int i = 0; i < 256; i++)
{
// 計(jì)算灰度級(jí)的累積概率
if (i == 0)
cdf[i] = hist.at<float>(i);
else
cdf[i] = cdf[i - 1] + hist.at<float>(i);
lut.at<uchar>(i) = static_cast<uchar>(255 * cdf[i]); // 創(chuàng)建灰度的查找表
}
LUT(src, lut, dst); // 應(yīng)用查找表,進(jìn)行灰度變化,得到均衡化后的圖像
}
/**
* @brief HistSpecify 對(duì)灰度圖像進(jìn)行直方圖規(guī)定化
* @param src 輸入圖像
* @param ref 參考圖像,解析參考圖像的直方圖并用于規(guī)定化
* @param result 直方圖規(guī)定化后的圖像
* @note 手動(dòng)設(shè)置一個(gè)直方圖并用于規(guī)定化比較麻煩,這里使用一個(gè)參考圖像來進(jìn)行
*/
void HistSpecify(const Mat &src, const Mat &ref, Mat &result)
{
Histogram1D hist1D;
Mat src_hist = hist1D.getHistogram(src);
Mat dst_hist = hist1D.getHistogram(ref);
float src_cdf[256] = { 0 };
float dst_cdf[256] = { 0 };
// 直方圖進(jìn)行歸一化處理
src_hist /= (src.rows * src.cols);
dst_hist /= (ref.rows * ref.cols);
// 計(jì)算原始直方圖和規(guī)定直方圖的累積概率
for (int i = 0; i < 256; i++)
{
if (i == 0)
{
src_cdf[i] = src_hist.at<float>(i);
dst_cdf[i] = dst_hist.at<float>(i);
}
else
{
src_cdf[i] = src_cdf[i - 1] + src_hist.at<float>(i);
dst_cdf[i] = dst_cdf[i - 1] + dst_hist.at<float>(i);
}
}
// 累積概率的差值
float diff_cdf[256][256];
for (int i = 0; i < 256; i++)
for (int j = 0; j < 256; j++)
diff_cdf[i][j] = fabs(src_cdf[i] - dst_cdf[j]);
// 構(gòu)建灰度級(jí)映射表
Mat lut(1, 256, CV_8U);
for (int i = 0; i < 256; i++)
{
// 查找源灰度級(jí)為i的映射灰度
// 和i的累積概率差值最小的規(guī)定化灰度
float min = diff_cdf[i][0];
int index = 0;
for (int j = 1; j < 256; j++)
{
if (min > diff_cdf[i][j])
{
min = diff_cdf[i][j];
index = j;
}
}
lut.at<uchar>(i) = static_cast<uchar>(index);
}
// 應(yīng)用查找表,做直方圖規(guī)定化
LUT(src, lut, result);
}
int main()
{
/****************顯示圖像的直方圖******************/
Histogram1D hist1;
Mat img1 = imread(IMAGE1_PATH);
Mat histImg1 = hist1.getHistogramImage(img1);
imshow("Image1", img1);
imshow("Histogram1", histImg1);
/*****************直方圖均衡*********************/
Mat equImg = Mat::zeros(img1.rows, img1.cols, img1.type());
EqualizeImage(img1, equImg);
Histogram1D hist2;
Mat histImg2 = hist2.getHistogramImage(equImg);
imshow("Equalized Image1", equImg);
imshow("Histogram2", histImg2);
/*****************直方圖規(guī)定化*******************/
Mat img2 = imread(IMAGE2_PATH);
Mat img3 = imread(IMAGE3_PATH);
Mat specifyImg = Mat::zeros(img2.rows, img2.cols, img2.type());
HistSpecify(img2, img3, specifyImg);
Histogram1D hist3;
Mat histImg3 = hist3.getHistogramImage(img2);
Histogram1D hist4;
Mat histImg4 = hist4.getHistogramImage(img3);
Histogram1D hist5;
Mat histImg5 = hist5.getHistogramImage(specifyImg);
imshow("Image2", img2);
imshow("Histogram3", histImg3);
imshow("Image3", img3);
imshow("Histogram4", histImg4);
imshow("Specify Image", specifyImg);
imshow("Histogram5", histImg5);
waitKey();
return 0;
}




References:
《數(shù)字圖像處理》 —— 岡薩雷斯
圖像處理基礎(chǔ)(8):圖像的灰度直方圖、直方圖均衡化、直方圖規(guī)定化(匹配)






