若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105843052
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究
红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)
上一篇:《OpenCV开发笔记(四十七):红胖子8分钟带你深入了解直方图(图文并茂+浅显易懂+程序源码)》
下一篇:《OpenCV开发笔记(四十九):红胖子8分钟带你深入了解轮廓识别(图文并茂+浅显易懂+程序源码)》
红胖子来也!!!
在之前接触的相机录像中,遇到过白平衡,白平衡其实就是调整整个界面的亮度平衡,达到一个对比度合适的过程,原理其实就是直方图均衡化,对像素每个点进行处理,根据直方图对每个级别的像素值进行调整,达到一个整体合适值。
本章实现直方图均衡化。
关于直方图的具体概念,如未深入理解熟悉,笔者建议一定要理解清楚,理解直方图请查看博文:
《OpenCV开发笔记(四十七):红胖子8分钟带你深入了解直方图(图文并茂+浅显易懂+程序源码)》
相机和图像传感器一般都可以适应场景中自然产生的对比度,使其达到一个 合适的对比度范围,此时就有两种情况,一种是相机和图像传感器本身已经做了该部分工作达到很好的效果,另一种是没有达到很好的效果,图像处理上再对其进行一次对比度拉伸的处理,是原本变化不明显的地方变得增强。
(注意:可以各种彩色向量都可以做直方图处理,此处以灰度图举例)
int grayScale[256] = {0}; // 每个灰度级所占像素的
double grayPro[256] = {0}; // 每个灰度级所占像素比例: 为 该像素出现的次数/总像素
// 步骤一:计算灰度级中每个像素在整幅图像中的个数;灰度级8位为256级别0~255
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
grayScale[srcMat.at(row, col)]++;
}
}
// 步骤二:计算每个像素在整幅图像的概率分布,pixSum代表所有像素点
int pixSum = srcMat.rows * srcMat.cols;
for(int index = 0; index < 256; index++)
{
// 计算概率:每级概率 = 每级像素点的数量 / 总像素点
grayPro[index] = (float)grayScale[index] / pixSum;
}
void equalizeHist( InputArray src,
OutputArray dst );
OpenCV提供了函数原型,一步到位,但是直方图的基础概念在整个图像处理中非常重要,直方图的理解请参照:
《OpenCV开发笔记(四十七):红胖子8分钟带你深入了解直方图(图文并茂+浅显易懂+程序源码)》
(补充:源码中用到了分离通道cv::splite()和通道合成cv::merge(),该部分请参照博文:《OpenCV开发笔记(十):OpenCV图像颜色通道分离和图像颜色多通道混合》)
void OpenCVManager::testEqualizeHist()
{
QString fileName1 = "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/16.jpg";
cv::Mat srcMat = cv::imread(fileName1.toStdString());
cv::Mat dstMat;
int width = 300;
int height = 200;
cv::resize(srcMat, srcMat, cv::Size(width, height));
cv::String windowName = _windowTitle.toStdString();
cvui::init(windowName);
cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 2,
srcMat.rows * 5),
srcMat.type());
while(true)
{
// 刷新全图黑色
windowMat = cv::Scalar(0, 0, 0);
// 原图复制
cv::Mat mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, srcMat, 1.0f, 0.0f, mat);
// 三通道分离,显示,然后三通道分别进行直方图均衡化,最终合成3个通道都均衡化的
{
cv::Mat matR;
cv::Mat matG;
cv::Mat matB;
// 分离3个通道,bgr,分别做直方图
std::vector vecChannels;
cv::split(srcMat, vecChannels);
// 分离b,g,r通道
// 生成一个空矩阵
cv::Mat emptyChannel(srcMat.rows, srcMat.cols, CV_8UC1);
for(int row = 0; row < emptyChannel.rows; row++)
{
for(int col = 0; col < emptyChannel.cols; col++)
{
emptyChannel.at(row, col) = 0;
}
}
// r通道处理
{
// 合成
std::vector vec;
vec.push_back(emptyChannel);
vec.push_back(emptyChannel);
vec.push_back(vecChannels.at(2));
cv::merge(vec, dstMat);
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
// 均衡化
cv::Mat equalChannel;
cv::equalizeHist(vecChannels.at(2), equalChannel);
std::vector vec2;
vec2.push_back(emptyChannel);
vec2.push_back(emptyChannel);
vec2.push_back(equalChannel);
cv::merge(vec2, dstMat);
mat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
matR = equalChannel.clone();
}
// g通道处理
{
// 合成
std::vector vec;
vec.push_back(emptyChannel);
vec.push_back(vecChannels.at(1));
vec.push_back(emptyChannel);
cv::merge(vec, dstMat);
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
// 均衡化
cv::Mat equalChannel;
cv::equalizeHist(vecChannels.at(1), equalChannel);
std::vector vec2;
vec2.push_back(emptyChannel);
vec2.push_back(equalChannel);
vec2.push_back(emptyChannel);
cv::merge(vec2, dstMat);
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
matG = equalChannel.clone();
}
// b通道处理
{
// 合成
std::vector vec;
vec.push_back(vecChannels.at(0));
vec.push_back(emptyChannel);
vec.push_back(emptyChannel);
cv::merge(vec, dstMat);
mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
// 均衡化
cv::Mat equalChannel;
cv::equalizeHist(vecChannels.at(0), equalChannel);
std::vector vec2;
vec2.push_back(equalChannel);
vec2.push_back(emptyChannel);
vec2.push_back(emptyChannel);
cv::merge(vec2, dstMat);
mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
matB = equalChannel.clone();
}
// 合成
std::vector vec;
vec.push_back(matB);
vec.push_back(matG);
vec.push_back(matR);
cv::merge(vec, dstMat);
mat = windowMat(cv::Range(srcMat.rows * 0, srcMat.rows * 1),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
// 灰度图的均衡化
{
// 灰度图
cv::cvtColor(srcMat, dstMat, CV_BGR2GRAY);
cv::cvtColor(dstMat, dstMat, CV_GRAY2BGR);
mat = windowMat(cv::Range(srcMat.rows * 4, srcMat.rows * 5),
cv::Range(srcMat.cols * 0, srcMat.cols * 1));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
// 灰度图均衡化
cv::cvtColor(srcMat, dstMat, CV_BGR2GRAY);
cv::equalizeHist(dstMat, dstMat);
cv::cvtColor(dstMat, dstMat, CV_GRAY2BGR);
mat = windowMat(cv::Range(srcMat.rows * 4, srcMat.rows * 5),
cv::Range(srcMat.cols * 1, srcMat.cols * 2));
cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
}
}
// 更新
cvui::update();
// 显示
cv::imshow(windowName, windowMat);
// esc键退出
if(cv::waitKey(25) == 27)
{
break;
}
}
}
对应版本号v1.43.0
上一篇:《OpenCV开发笔记(四十七):红胖子8分钟带你深入了解直方图(图文并茂+浅显易懂+程序源码)》
下一篇:《OpenCV开发笔记(四十九):红胖子8分钟带你深入了解轮廓识别(图文并茂+浅显易懂+程序源码)》
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105843052