失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > OpenCV-实现直方图均衡化(对比cv::equalizeHist)

OpenCV-实现直方图均衡化(对比cv::equalizeHist)

时间:2023-01-11 14:07:28

相关推荐

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;}

测试效果

图1灰度原图

其直方图如下:

图2直方图

图3均衡化后灰度图

两个函数运行时间都在0.001s以下,其均衡化后直方图略有差异,但都实现了均衡效果。

自定义函数:

图4自定义函数均衡化后直方图

官方函数:

图5官方函数均衡化后直方图

对彩色图的三通道分别进行直方图均衡化,实现彩色均衡效果:

图6彩色原图

图7彩色均衡化效果

如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~

如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

如果觉得《OpenCV-实现直方图均衡化(对比cv::equalizeHist)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。