OpenCV-实现直方图均衡化(对比cv::equalizeHist)
Posted 翟天保Steven
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV-实现直方图均衡化(对比cv::equalizeHist)相关的知识,希望对你有一定的参考价值。
作者:Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
实现原理
通过图像数据的直方图,可以快速判断图像的亮度和质量。而直方图均衡化就是通过图像变换使得直方图均匀分布,起到对比度增强的效果。在图像处理的课本中,针对离散形式的图像数据,最常用的一种方法就是累计概率分布。首先统计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以下,其均衡化后直方图略有差异,但都实现了均衡效果。
自定义函数:
官方函数:
对彩色图的三通道分别进行直方图均衡化,实现彩色均衡效果:
如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~
如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!
以上是关于OpenCV-实现直方图均衡化(对比cv::equalizeHist)的主要内容,如果未能解决你的问题,请参考以下文章
(转)OpenCV图像增强算法实现(直方图均衡化拉普拉斯LogGamma)