【QtOpenCV 直方图计算 split/calcHist/normalize】
文章目录
- 前言
- 一、函数介绍
- 1 split
- 2 calcHist
- 3 normalize
- 二、演示
- 1、GUI
- 2、实现代码
- 总结
前言
越来越多的开发人员选择基于开源的Qt框架与OpenCV来实现界面和算法,其原因不单单是无版权问题,更多是两个社区的发展蓬勃,可用来学习的资料与例程特别丰富。以下是关于利用Qt构建GUI并使用OpenCV中的split/calcHist/normalize函数进行直方图计算。
软件版本:Qt-5.12.0/OpenCV-4.5.3
平台:Windows10/11–64
一、函数介绍
1 split
cv::split(const Mat& src, Mat *mvBegin)
cv::split(InputArray m, OutputArrayOfArrays mv);
参数解释:
src/m:要进行分离的图像矩阵;
mvBegin:Mat数组的首地址;
mv:vector对象;
2 calcHist
cv::calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate = false )
cv::calcHist(const Mat* images, int nimages, const int* channels, InputArray mask, SparseMat& hist, int dims, const int* histSize, const float** ranges, bool uniform = true, bool accumulate= false )
参数解释:
images:源图像数组,它们有同样的位深CV_8U或 CV_32F ,同样的尺寸;图像阵列中的每一个图像都可以有任意多个通道;
nimages:源图像的数目;
channels:维度通道序列,第一幅图像的通道标号从0~image[0].channels( )-1。Image[0]表示图像数组中的第一幅图像,channels()表示该图像的通道数量。同理,图像阵列中的第二幅图像,通道标号从image[0].channerls( )开始,到image[1].channels( )-1为止;第三、四幅图像的通道标号顺序依此类推;也就是说图像阵列中的所有图像的通道根据图像排列顺序,排成一个通道队列;
mask: 可选择的mask。如果该矩阵不空的话,它必须是一个8-bit的矩阵,与images[i]同尺寸。在图像中,只有被mask覆盖的区域的像素才参与直方图统计。如果这个参数想用默认值,输入Mat()就可以了;
hist:输出直方图, 它是一个稠密或稀疏矩阵,具有dims个维度;
dims :直方图的维度,一定是正值, CV_MAX_DIMS(当前OpenCV版本是32个);
histSize:数组,即histSize[i]表示第i个维度上bin的个数;这里的维度可以理解为通道;
ranges: 当uniform=true时,ranges是多个二元数组组成的数组;当uniform=false时,ranges是多元数组组成的数组。当在每个维度(或通道)上每个直方条等宽时,即uniform=true时,灰度值的有效统计范围的下界用L0表示,上界用UhistSize[i]-1表示,角标中的i表示第i个维度(或通道),上下界值可以表示为hrange[i]={ L0, UhistSize[i]-1}, 在统计时, L0和UhistSize[i]-1不在统计范围内。而ranges={ hrange[0], hrange[1], …… , hrange[dims]}。ranges的元素个数由参数dims决定。其中,L0表示在该通道上第0个直方条(bin)的下边界,UhistSize[i]-1表示最后一个直方条histSize[i]-1的上边界。在该维度上直方条的个数为histSize[i],如hrange[0]={ L0, UhistSize[0]},hrange[1]={ L1, UhistSize[1]}, hrange[2]={ L2, UhistSize[2]}, …… , hrange[dims]={ L0, UhistSize[0]}。当uniform=false时,ranges中的每个元素ranges[i]都是一个多元数组,元素个数为histSize[i]+1,它们分别是:L0 , U0=L1, U1= L2, …… ,UhistSize[i]-2 , LhistSize[i]-1 , UhistSize[i]-1 。所以,ranges[i]={ L0 , L1, L2, …… , LhistSize[i]-1 ,UhistSize[i]-1};
uniform:标识,用于说明直方条bin是否是均匀等宽的;
accumulate: 累积标识。如果该项设置,当直方图重新分配时,直方图在开始清零。这个特征可以使你通过几幅图像,累积计算一个简单的直方图,或者及时更新直方图;
函数calcHist可以计算一幅或多幅图像的直方图。在元组中增量一个直方图的时候,就是从输入图像组中的原位置提取一幅图像,并计算出它的直方图,并添加到元组中。当参数dims>1时,输出矩阵Hist是二维矩阵。
3 normalize
cv::normalize(InputArry src, InputOutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mark=noArry())
参数解释:
src:输入数组;
**dst **:输出数组,数组的大小和原数组一致;
alpha:1-用来规范值,2-规范范围,并且是下限;
beta:只用来规范范围并且是上限;
**norm_type **: 归一化选择的数学公式类型;
**dtype **:当为负,输出的宽高通道数都等于输入,当为正,输出只在深度与输入不同,不同的地方由dtype决定;
mark:掩码,选择感兴趣区域,选定后只能对该区域进行操作;
函数作用归一化数据,该函数分为范围归一化与数据值归一化,经常应用在将数值限定在一个范围,以便使用同一套阈值参数的情况。
二、演示
1、GUI
如上图创建Histgram的功能按钮QPushButton, 直方图展示在Histgram的窗口中。
2、实现代码
histBtn的clicked()槽函数实现如下:
void MainWindow::on_histBtn_clicked()
{std::size_t numView = ui->tabWidget->currentIndex() % 4;if (dispMat[numView]->empty()){outputInfo(2, tr("Please make sure the Mat exist!"));}std::vector<cv::Mat> rgb_planes;if (dispMat[numView]->channels() == 3) {cv::split(*dispMat[numView], rgb_planes); // split函数,分离通道;}else{rgb_planes.push_back(*dispMat[numView]);rgb_planes.push_back(*dispMat[numView]);rgb_planes.push_back(*dispMat[numView]);}outputInfo(1, "Channels: " + QString::number(dispMat[numView]->channels()));int histSize = 255;float range[] = {0, 255};const float* histRange = {range};bool uniform = true;bool accumulate = false;cv::Mat r_hist, g_hist, b_hist;cv::calcHist(&rgb_planes[0], 1, nullptr, cv::Mat(), r_hist, 1, \ // 直方图计算&histSize, &histRange, uniform, accumulate);cv::calcHist(&rgb_planes[1], 1, nullptr, cv::Mat(), g_hist, 1,\&histSize, &histRange, uniform, accumulate);cv::calcHist(&rgb_planes[0], 1, nullptr, cv::Mat(), b_hist, 1,\&histSize, &histRange, uniform, accumulate);int hist_w = 512;int hist_h = 800;cv::Mat tmpMat(hist_w, hist_h, CV_8UC3, cv::Scalar(0, 0, 0)); // 准备画布/*cv::normalize(r_hist, r_hist, 0, tmpMat.rows, cv::NORM_MINMAX, \ // 归一化-1, cv::Mat());cv::normalize(g_hist, g_hist, 0, tmpMat.rows, cv::NORM_MINMAX, \-1, cv::Mat());cv::normalize(b_hist, b_hist, 0, tmpMat.rows, cv::NORM_MINMAX, \-1, cv::Mat());int bin_w = cvRound(static_cast<double> (hist_w/histSize));for (int i = 1; i < histSize; i++){cv::line(tmpMat, cv::Point(bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1))),cv::Point(bin_w*(i), hist_h - cvRound(r_hist.at<float>(i))),cv::Scalar(0, 0, 255), 2, 8, 0);cv::line(tmpMat, cv::Point(bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1))),cv::Point(bin_w*(i), hist_h - cvRound(g_hist.at<float>(i))),cv::Scalar(0, 0, 255), 2, 8, 0);cv::line(tmpMat, cv::Point(bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1))),cv::Point(bin_w*(i), hist_h - cvRound(b_hist.at<float>(i))),cv::Scalar(0, 0, 255), 2, 8, 0);}*/double rMax, gMax, bMax;cv::minMaxLoc(r_hist, nullptr, &rMax, nullptr, nullptr);cv::minMaxLoc(g_hist, nullptr, &gMax, nullptr, nullptr);cv::minMaxLoc(b_hist, nullptr, &bMax, nullptr, nullptr);double rBin_w = static_cast<double>(tmpMat.cols / histSize);double rBin_h = static_cast<double>(tmpMat.rows / rMax);double gBin_w = static_cast<double>(tmpMat.cols / histSize);double gBin_h = static_cast<double>(tmpMat.rows / gMax);double bBin_w = static_cast<double>(tmpMat.cols / histSize);double bBin_h = static_cast<double>(tmpMat.rows / bMax);for (int i = 1; i < histSize; i++){cv::Point rP_0 = cv::Point(static_cast<int>(i*rBin_w), tmpMat.rows);int rVal = static_cast<int>(r_hist.at<float>(i));cv::Point rP_1 = cv::Point(static_cast<int>((i + 1)*rBin_w), \static_cast<int>(tmpMat.rows - rVal*rBin_h));cv::rectangle(tmpMat, rP_0, rP_1, cv::Scalar(0, 0, 255), 2, 8, 0);cv::Point gP_0 = cv::Point(static_cast<int>(i*gBin_w), tmpMat.rows);int gVal = static_cast<int>(g_hist.at<float>(i));cv::Point gP_1 = cv::Point(static_cast<int>((i + 1)*gBin_w), \static_cast<int>(tmpMat.rows - gVal*gBin_h));cv::rectangle(tmpMat, gP_0, gP_1, cv::Scalar(0, 255, 0), 2, 8, 0);cv::Point bP_0 = cv::Point(static_cast<int>(i*bBin_w), tmpMat.rows);int bVal = static_cast<int>(b_hist.at<float>(i));cv::Point bP_1 = cv::Point(static_cast<int>((i + 1)*bBin_w), \static_cast<int>(tmpMat.rows - bVal*bBin_h));cv::rectangle(tmpMat, bP_0, bP_1, cv::Scalar(255, 0, 0), 2, 8, 0);}char string[12];int mark = 0;for (int i = 1; mark < static_cast<int>(rMax); i++){mark = static_cast<int>(i * rMax / 20);itoa(mark, string, 10);cv::putText(tmpMat, string, cv::Point(0, static_cast<int>(tmpMat.rows - \mark * rBin_h)), 1, 1, \cv::Scalar(0, 255, 255));}mark = 0;for (int i = 1; mark < 256; i++){mark = i * 20;itoa(mark, string, 10);cv::putText(tmpMat, string, cv::Point(mark * (tmpMat.cols / 256),tmpMat.rows), 1, 1, cv::Scalar(0, 255, 255));}*dispMat[3] = tmpMat.clone();cvtMatPixmap(dispMat, dispPixmap, 3);outputInfo(1, tr("Histogram Action done."));double minVal, maxVal;cv::Point minLoc, maxLoc;if (dispMat[numView]->channels() == 3){cv::cvtColor(*dispMat[numView], tmpMat, cv::COLOR_RGB2GRAY);}else{tmpMat = *dispMat[numView];}cv::minMaxLoc(tmpMat, &minVal, &maxVal, &minLoc, &maxLoc);QString minMaxLocVal = "minVal: " + QString::number(minVal) + \" maxVal: " + QString::number(maxVal);std::stringstream tmpStream;std::streambuf* coutBuf = std::cout.rdbuf();std::cout.rdbuf(tmpStream.rdbuf());std::cout << " minLoc: " << minLoc << " maxLoc: " << maxLoc << std::endl;std::string minMaxLocString(tmpStream.str());std::cout.rdbuf(coutBuf);QString infoMinMaxLoc = minMaxLocVal + QString::fromStdString(minMaxLocString);outputInfo(1, infoMinMaxLoc);
}
总结
以上是关于利用Qt进行GUI构建并使用OpenCV中的split/calcHist/normalized函数进行图像缩放的介绍。
参考:
链接: 往期/前期https://blog.csdn.net/richard_yuu/article/details/127859793
链接: 往期/前期https://blog.csdn.net/richard_yuu/article/details/124140746
其中疑问或错误,欢迎联系交流,微信:electrical_program
【QtOpenCV 直方图计算 split/calcHist/normalize】相关推荐
- 详解为什么OpenCV的直方图计算函数calcHist()计算出的灰度值为255的像素个数为0
在使用OpenCV的直方图计算函数calcHist()时,发现灰度值为255的像素个数总是为0. 哪怕图像中灰度值为255的像素个数不为0,使用OpenCV的直方图计算函数calcHist()计算出的 ...
- OpenCV直方图计算函数calcHist详解
原文转自https://docs.opencv.org/2.4/modules/imgproc/doc/histograms.html 文档是英文的,应该不难看懂,就不给翻译了. ---------- ...
- 【opencv学习笔记】025之直方图计算 - calcHist函数详解
前言 如果你想了解更多有关于计算机视觉.OpenCV.机器学习.深度学习等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们吧! 1.calcHist函数是干什么滴? 这个问题嘛,看看标 ...
- OpenCV 图像直方图计算calcHist()
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/keith_bb/article/details/56680997 图像直方图是对数据集合的一种统计方 ...
- OpenCV-Python直方图计算calcHist函数详解
☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython ░ 一.引言 在<<数字图像处理>第三章学习总结感悟2:直方图处理: h ...
- OpenCV-Python图像直方图计算calcHist函数详解、示例及图形呈现
☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython ░ 一.引言 在前面几篇直方图相关的文章中介绍了直方图均衡.直方图匹配.局部直方图处理.基 ...
- 【OpenCV 】直方图均衡化,直方图计算,直方图对比
目录 1.直方图均衡化¶ 1.1 原理 1.2 直方图均衡化 1.3 直方图均衡化原理 1.4 代码实例 1.5 运行效果 2. 直方图计算¶ 2.1 目标 2.2 直方图 2.3 代码实例 2.4 ...
- OpenCV之imgproc 模块. 图像处理(4)直方图均衡化 直方图计算 直方图对比 反向投影 模板匹配
直方图均衡化 目标 在这个教程中你将学到: 什么是图像的直方图和为什么图像的直方图很有用 用OpenCV函数 equalizeHist 对图像进行直方图均衡化 原理 图像的直方图是什么? 直方图是图像 ...
- 【opencv450 Image Processing】Histogram Calculation直方图计算
Goal 在本教程中,您将学习如何: 使用 OpenCV 函数 cv::split 将图像划分为其对应的平面. 使用 OpenCV 函数 cv::calcHist 计算图像数组的直方图 使用函数 cv ...
最新文章
- usaco Overfencing 穿越栅栏(BFS)
- 皮一皮:35岁后你做什么?
- 学生籍贯信息管理系统c语言,学生籍贯信息管理系统(c).doc
- 分区供水条件口诀_口诀+总结!耐火等级要求及调整原则及记忆方式
- BZOJ3230 相似子串 字符串 SA ST表
- php识别号码格式豹子,[转载]php新手入门之PHP常用特殊运算符号
- matlab图像的腐蚀和膨胀_OpenCV图像处理系列八 --- 腐蚀与膨胀
- html php打开,html格式如何打开
- centos 7 php7 yum源
- c语言编译器app官网下载,c语言编译器
- C语言代码输出星期几,C语言程序设计: 输入年月日 然后输出是星期几
- java基础练习 2
- 菜鸟python_菜鸟爱Python第1期:Python发展史?对Python最深刻的解读
- 图书馆占座系统(四)
- 快递鸟预约快递员上门揽件API开发指南
- uniapp唤醒手机地图app
- Tomcat遇到500 The server encountered an internal error that prevented it from fulfilling this request
- 电机远计算机控制,电机控制器
- thinkpad重装系统不引导_联想Y400电脑装WIN7无法重装系统的解决方法
- 超详细的k8s对接ceph RBD存储
热门文章
- (转)塔吉特读心术——用户数据分析的魔力
- 笔试题-2023-思特威-FPGA【纯净题目版】
- 台式计算机接地,台式电脑主机漏电怎么办?
- Java HashMap1.7头插法扩容时出现循环链表 1.8换成尾插法
- 西农新版锐捷linux系统客户端联网方法
- windows 下实现socket编程_传送文件
- c语言堆栈基本代码入栈出栈_顺序栈基本操作(入栈和出栈)C语言详解
- “信创产业”是什么?2020“信创”脱颖而出
- 苹果13和苹果14的差异
- 登陆qq时显示服务器错误,刚才想上QQ的时候,显示无法登陆QQ,显示连接服务器超时,错误代码0x00000001是怎么回事啊!!急...