赞
踩
目的:生成直方图
直方图计算原理详解
https://blog.csdn.net/keith_bb/article/details/56680997?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-6
直方图概念
对于图像像素值、图像梯度、每个像素的角度等一切图像的属性值,都可以建立直方图,
不过基于图像像素灰度直方图是最常见的
API学习
calcHist直方图计算
计算直方图,获得每一个通道每一个像素值对应的频次,
存放在对应的数组中
calcHist (Hist-直方图)
(
const Mat images,//输入图像指针
int images,//图像数目
const int channels,//通道数
InputArray hist,//输出的直方图数据
int dims,//维数 (灰度直方图只有一个维度 所以我们默认为一维)
const int histsize,//直方图级数 (256)
const float* ranges,//值域范围 (0-256)
bool uniform,//true by default
bool accumulate//false by default
)***
#include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui_c.h> #include <iostream> #include <math.h> using namespace cv; using namespace std; Mat src, dst; void Split(int, void*); void CalcHist(int, void*); int main() { src = imread("D:/实验台/机器视觉/测试图片/盖茨.jpg"); if (src.empty())//如果src这个数据库属性为空 { cout << "无法打开" << endl; return -1; } imshow("原图", src); //Split(0,0); CalcHist(0, 0); waitKey(0); return 0; } void Split(int, void*)//分通道显示 { vector<Mat>bgrPlans;//定义一个动态向量数组存放Mat类型的图片 split(src,bgrPlans);//输出单通道图像数组 imshow("分通道效果 通道1B", bgrPlans[0]); imshow("分通道效果 通道2G", bgrPlans[1]); imshow("分通道效果 通道3R", bgrPlans[2]); } void CalcHist(int, void*)//分通道直方图计算 { vector<Mat>bgrPlans;//定义一个动态向量数组存放Mat类型的三维度数组(B G R) //分通道 将当前图片三通道的图像分为三个多通道的图像存放在单通道数组中 split(src, bgrPlans);//输出src的三个单通道 到 图像三维度数组bgrPlans //计算直方图 //目的是获取每一个通道的每一个像素对应的频次(频率) 存放到对应的数组中 //需对三个通道单独计算 再单独存放 这样就得到了三个通道的直方图信息 int histSize = 256;//每一维的bin(出现频率大小-直方图高度) 的变化范围 float range[] = { 0, 256 };//确定像素值的值域范围为 0-255 const float* histRanges= {range};//维度区分化值域范围(像素值区间) Mat Bhist, Ghist, Rhist;//建立存放图片信息(直方图数据)的三个Mat容器分别给独立进行计算的三个通道 calcHist(&bgrPlans[0], 1, 0, Mat(), Bhist, 1, &histSize, &histRanges, true, false); calcHist(&bgrPlans[1], 1, 0, Mat(), Ghist, 1, &histSize, &histRanges, true, false); calcHist(&bgrPlans[2], 1, 0, Mat(), Rhist, 1, &histSize, &histRanges, true, false); /* calcHist函数详解 https://www.cnblogs.com/phoenixdsg/p/8280362.html 直方图计算( [images输入源]&bgrPlans[0]-&输入的图像单通道指针,可以是多幅单通道图像(多幅单通道图像必须是同样的深度cv8uORcv32f)uchar或float [nimages数目]1-源输入数组的元素个数-"bgrPlans[0]"因为只计算了单个通道 所以这个参数都为1 [channels通道]0-通道数(对灰度图像来说只有一个通道值dims=1) [mask掩膜]mat()-掩码选择感兴趣区域,选定后只能对该区域进行操作 如果使用该掩膜矩阵 必须是8位的并和输入源大小相等 [hist矩阵]Bhist-输出的直方图数组数据 [dims维数]1-需要统计直方图的维度个数 此案例中我们仅计算灰度图的直方(灰度图只有一维度) 所以为1 [histsize]&histSize-在直方图维度上直方的个数 的地址 (直方图的区间) 255个直方 [renge范围]&histRanges-一维二元数组组成的直方图需要统计的(0-256)区间范围(只统计值为0-256的数据) [在例子1中采用的是均匀直方图,所以范围为0-256]如果需要自己定义范围需要自己设置 [uniform均匀]:true-直方图是否均匀的标志;[指定直方图每个bin统计的是否是相同数量的灰度级] 若设置为true,bin大小相同,则ranges[]是一个只包含两元素的数组,起始点和终止点 若设置为false,则bin大小是不同的,你需要在ranges[]中指定各个bin区间的大小 [accumulate累积]:false-如果设置为true,则直方图在使用之前不清除,用于保存多幅图像集中的单一直方图, 或及时更新直方图 ); */ /*上面这个函数就可以实现图像的直方图了,但是我们还要将其显示出来,我们要显示直方图的话' ,就需要另外创建一幅图片,横坐标表示灰度级,纵坐标表示该灰度级像素的个数,为方便显示, 我们通常会对直方图进行归一化。不然,万一某个灰度级的像素数目很多,那要是正常显示的话, 这创建的图片的行数还是比较吓人的。将直方图数据显示在图片中可以用矩形的形式也可用线条形式, 下面会演示这两种形式,先继续分析需要用到的函数。*/ //归一化处理 //归一化,因为像素值的个数有可能超过图像的高度,需要把像素值的频次归一化到0-400的范围内 //直方图归一化,就是将各个灰度级的像素数之间的比例关系,映射到归一化范围[histH,histW] //最大值对应到 histH 值,而不是简单的每级像素数除以范围值,这就是为什么描绘出来的直方图会置顶的原因 int histH = 400;//输出的直方图图像的高度 限值 int histW = 512;//输出的直方图图像的宽度 int binW = histW / histSize;//(输入的直方图的宽度除以直方图每一维频率的大小)代表划分的每一个bin的宽度(512/256=2) normalize(Bhist, Bhist, 0, histH, NORM_MINMAX, -1, Mat()); normalize(Ghist, Ghist, 0, histH, NORM_MINMAX, -1, Mat()); normalize(Rhist, Rhist, 0, histH, NORM_MINMAX, -1, Mat()); /*归一化操作 (输入的数组, 归一化后的输出图像数组, 归一化后的取值下限, 归一化后的取值上限histH(400), 归一化的数学公式类型, 当为负,输出在大小深度通道数都等于输入,当为正,输出只在深度与输如不同,不同的地方由dtype决定); 掩码。选择感兴趣区域,选定后只能对该区域进行操作 */ /* Mat histimg(histW, histH, CV_8UC3, Scalar(0, 0, 0));//创建一个三通道直方图图像 //绘制直方图 for(int i=1;i<histSize;i++)//每次循环连接相邻两个直方图的左顶点 { //第一个点为上一个直方图bin的左顶点,第二个点为本个直方图bin的左顶点 //cvRound() :返回跟参数最接近的整数值, 即四舍五入 //两点确认一条直线 line(histimg, Point((i - 1) * binW, histH - cvRound(Bhist.at<float>(i-1))), Point((i) * binW, histH - cvRound(Bhist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA);//绘制一次完成后是绘制了一个点(这个点就是第i个范围内的像素的直方图(出现频率)) line(histimg, Point((i - 1) * binW, histH - cvRound(Ghist.at<float>(i - 1))), Point((i)*binW, histH - cvRound(Ghist.at<float>(i))), Scalar(0, 255, 0), 2, LINE_AA); line(histimg, Point((i - 1) * binW, histH - cvRound(Rhist.at<float>(i - 1))), Point((i)*binW, histH - cvRound(Rhist.at<float>(i))), Scalar(0, 0, 255), 2, LINE_AA); }*/ /*画直线 目标图片 第一通道的直方图绘制原理(另外两组通道绘制原理也是如此) Point[端点一] ((i - 1) * binW[端点一的X纵坐标=第i组直方的*直方宽度(2)],histH - cvRound(Bhist.at<float>(i-1)))[端点一的Y横坐标 直方图的总高度(400) 减去 四舍五入后的Bhist(第i组直方图的具体高度也就是具体频率)] Point[端点一] ((i - 1) * binW[端点二的X纵坐标=第i组直方的*直方宽度(2)],histH - cvRound(Bhist.at<float>(i-1)))[端点二的Y横坐标 直方图的总高度(400) 减去 四舍五入后的Bhist(第i组直方图的具体高度也就是具体频率)] Bhist.at<float>(i-1) 提取第i个像素范围直方图的数据 比如像素值为5的像素有50个 Bhist[5]的值就是50 根据 hist-Bhist的某范围的值实现直方图从坐标系0点开始绘制(如果不用总高度减去Bhist的值 直方图将从图片像素坐标0.0开始绘制) */ Mat histimg(histW, histH*2, CV_8UC3, Scalar(0, 0, 0));//创建一个大于直线直方图面积的三通道图像(直方图绘制模板) for (int i = 0; i < histSize; ++i) { //“坐标原点”在左上角 rectangle(histimg, Point(i , histH - 1), Point((i + 1), histH - cvRound(Bhist.at<float>(i))), Scalar(255, 0, 0), CV_FILLED); rectangle(histimg, Point((i + histSize) , histH - 1), Point((i + histSize + 1), histH - cvRound(Ghist.at<float>(i))), Scalar(0, 255, 0), CV_FILLED); rectangle(histimg, Point((i + histSize * 2) , histH - 1), Point((i + histSize * 2 + 1), histH - cvRound(Rhist.at<float>(i))), Scalar(0, 255, 0), CV_FILLED); /// Scalar(0, 255, 0), CV_FILLED); 自动填充矩形函数 给建立好的矩形图框进行自动填充(酌情使用) 一般情况下效果较好 // Scalar(0, 0, 255), 1,LINE_AA); 手动设置矩形线条粗细和线条类型 (此案例中和CV_FILLED效果差不多) } imshow("直方图计算以及直方图绘制",histimg); return; }
图像的灰度直方图是一个离散函数,它表示图像每一灰度级与该灰度级出现频率的对应关系。
假设一幅图像的像素总数为 N,灰度级总数为 L,其中灰度级为 g 的像素总数为 Ng,则这幅
数字图像的灰度直方图横坐标即为灰度 g ( 0 ≤ g ≤ L-1 ),纵坐标则为灰度值出现的次数 Ng。
实际上,用 N 去除各个灰度值出现的次数Ng 即可得到各个灰度级出现的概率 Pg = Ng / N = Ng / ∑Ng
,从而得到归一化的灰度直方图,其纵坐标为概率 Pg 。
calcHist函数详解
https://www.cnblogs.com/phoenixdsg/p/8280362.html
images:输入的图像的指针,可以是多幅图像,所有的图像必须有同样的深度(CV_8U or CV_32F)。同时一副图像可以有多个channes。
nimages:输入图像的个数
channels:需要统计直方图的第几通道。用来计算直方图的channes的数组。比如输入是2副图像,第一副图像有
0,1,2共三个channel,第二幅图像只有0一个channel,那么输入就一共有4个channes,如果int channels[3] =
{3, 2, 0},那么就表示是使用第二副图像的第一个通道和第一副图像的第2和第0个通道来计算直方图。
mask:掩膜,如果mask不为空,那么它必须是一个8位(CV_8U)的数组,并且它的大小的和arrays[i]的大小
相同,值为1的点将用来计算掩膜内的直方图 …Mat()
hist:输出的直方图数组
dims:需要统计直方图通道的个数
histSize:指的是直方图分成多少个区间,就是 bin的个数。在每一维上直方图的个数。简单把直方图
看作一个一个的竖条的话,就是每一维上竖条的个数。
const float** ranges: 统计像素值得区间。比如:
float rang1[] = {0, 20};
float rang2[] = {30, 40};
const float *rangs[] = {rang1, rang2};那么就是对0,20和30,40范围的值进行统计。
uniform=true::是否对得到的直方图数组进行归一化处理
accumulate=false:在多个图像时,是否累计计算像素值得个数
关于直方图的对象识别应用
https://blog.csdn.net/qq_30241709/article/details/78539644?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。