作者:翟天保Steven
版權聲明:著作權歸作者所有,商業(yè)轉載請聯系作者獲得授權,非商業(yè)轉載請注明出處
實現原理
? ? ? ?通過圖像數據的直方圖,可以快速判斷圖像的亮度和質量,。而直方圖均衡化就是通過圖像變換使得直方圖均勻分布,起到對比度增強的效果,。在圖像處理的課本中,針對離散形式的圖像數據,最常用的一種方法就是累計概率分布,。首先統計0-255灰度值所占像素個數;再計算出像素個數與總像素的比,表示為出現的概率;從0開始進行累計概率分布,即從0慢慢累加各層概率值直到1;則均衡化圖像的灰度值=原灰度值所對應的累計概率*255,。
? ? ? ?基于上述原理,我自定義了一個簡單的直方圖均衡化函數EqualizeHist,并定義了直方圖簡易繪制函數drawHistImg,用來作直觀對比,。
功能函數代碼
// 直方圖均衡化
cv::Mat EqualizeHist(cv::Mat src)
{
cv::Mat h = cv::Mat::zeros(1, 256, CV_32FC1);
cv::Mat hs = cv::Mat::zeros(1, 256, CV_32FC1);
cv::Mat hp = cv::Mat::zeros(1, 256, CV_32FC1);
cv::Mat result = cv::Mat::zeros(src.size(), src.type());
int sum = 0;
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
h.at<float>(0, src.at <uchar>(i, j))++;
sum++;
}
}
for (int i = 0; i < 256; ++i)
{
hs.at<float>(0, i) = h.at<float>(0, i) / sum;
if (i == 0)
{
hp.at<float>(0, i) = hs.at<float>(0, i);
}
else {
hp.at<float>(0, i) = hp.at<float>(0, i - 1) + hs.at<float>(0, i);
}
}
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
result.at <uchar>(i, j) = uchar(round(255 * hp.at<float>(0, src.at<uchar>(i, j))));
}
}
return result;
}
// 繪制簡易直方圖
cv::Mat drawHistImg(cv::Mat &src)
{
cv::Mat hist = cv::Mat::zeros(1, 256, CV_32FC1);
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
hist.at<float>(0, src.at <uchar>(i, j))++;
}
}
cv::Mat histImage = cv::Mat::zeros(540, 1020, CV_8UC1);
const int bins = 255;
double maxValue;
cv::Point2i maxLoc;
cv::minMaxLoc(hist, 0, &maxValue, 0, &maxLoc);
int scale = 4;
int histHeight = 540;
for (int i = 0; i < bins; i++)
{
float binValue = (hist.at<float>(i));
int height = cvRound(binValue * histHeight / maxValue);
cv::rectangle(histImage, cv::Point(i * scale, histHeight),
cv::Point((i + 1) * scale-1, histHeight - height), cv::Scalar(255), -1);
}
return histImage;
}
函數原型
? ? ? ?官方OpenCV庫中也有自帶的直方圖均衡化函數:
void equalizeHist( InputArray src, OutputArray dst );
參數說明
- InputArray類型的src,輸入圖像,如Mat類型,。
- OutputArray類型的dst,輸出圖像。
C++測試代碼
#include <iostream>
#include <time.h>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
cv::Mat EqualizeHist(cv::Mat src);
cv::Mat drawHistImg(cv::Mat &hist);
int main()
{
cv::Mat src = imread("test.jpg",0);
cv::Mat src1 = imread("test.jpg");
clock_t start1, start2, end1,end2;
// 繪制原圖直方圖
cv::Mat hI = drawHistImg(src);
// 自定義直方圖均衡化
start1 = clock();
cv::Mat result1 = EqualizeHist(src);
end1 = clock();
double dif1 = (end1 - start1) / CLOCKS_PER_SEC;
cout << "time1:" << dif1 << endl;
// 繪制均衡化后直方圖
cv::Mat hrI = drawHistImg(result1);
// 官方直方圖均衡化函數
start2 = clock();
cv::Mat result2;
equalizeHist(src, result2);
end2 = clock();
double dif2 = (end2 - start2) / CLOCKS_PER_SEC;
cout << "time2:" << dif2 << endl;
// 繪制均衡化后直方圖
cv::Mat hr2I = drawHistImg(result2);
// 彩色直方圖均衡化,三通道分別作均衡再合并
vector<cv::Mat> rgb,rgb_;
cv::Mat r, g, b;
cv::split(src1, rgb);
equalizeHist(rgb[0], b);
equalizeHist(rgb[1], g);
equalizeHist(rgb[2], r);
rgb_.push_back(b);
rgb_.push_back(g);
rgb_.push_back(r);
cv::Mat src1_;
cv::merge(rgb_, src1_);
imshow("original", src1);
imshow("result", src1_);
waitKey(0);
return 0;
}
// 直方圖均衡化
cv::Mat EqualizeHist(cv::Mat src)
{
cv::Mat h = cv::Mat::zeros(1, 256, CV_32FC1);
cv::Mat hs = cv::Mat::zeros(1, 256, CV_32FC1);
cv::Mat hp = cv::Mat::zeros(1, 256, CV_32FC1);
cv::Mat result = cv::Mat::zeros(src.size(), src.type());
int sum = 0;
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
h.at<float>(0, src.at <uchar>(i, j))++;
sum++;
}
}
for (int i = 0; i < 256; ++i)
{
hs.at<float>(0, i) = h.at<float>(0, i) / sum;
if (i == 0)
{
hp.at<float>(0, i) = hs.at<float>(0, i);
}
else {
hp.at<float>(0, i) = hp.at<float>(0, i - 1) + hs.at<float>(0, i);
}
}
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
result.at <uchar>(i, j) = uchar(round(255 * hp.at<float>(0, src.at<uchar>(i, j))));
}
}
return result;
}
// 繪制簡易直方圖
cv::Mat drawHistImg(cv::Mat &src)
{
cv::Mat hist = cv::Mat::zeros(1, 256, CV_32FC1);
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
hist.at<float>(0, src.at <uchar>(i, j))++;
}
}
cv::Mat histImage = cv::Mat::zeros(540, 1020, CV_8UC1);
const int bins = 255;
double maxValue;
cv::Point2i maxLoc;
cv::minMaxLoc(hist, 0, &maxValue, 0, &maxLoc);
int scale = 4;
int histHeight = 540;
for (int i = 0; i < bins; i++)
{
float binValue = (hist.at<float>(i));
int height = cvRound(binValue * histHeight / maxValue);
cv::rectangle(histImage, cv::Point(i * scale, histHeight),
cv::Point((i + 1) * scale-1, histHeight - height), cv::Scalar(255), -1);
}
return histImage;
}
測試效果
? ? ? ?其直方圖如下:?
? ? ? ?兩個函數運行時間都在0.001s以下,其均衡化后直方圖略有差異,但都實現了均衡效果,。
? ? ? ?自定義函數:
? ? ? ? 官方函數:
? ? ? ? ?對彩色圖的三通道分別進行直方圖均衡化,實現彩色均衡效果:
? ? ? ?如果函數有什么可以改進完善的地方,非常歡迎大家指出,一同進步何樂而不為呢~
? ? ? ?如果文章幫助到你了,可以點個贊讓我知道,我會很快樂~加油!