本文共 7399 字,大约阅读时间需要 24 分钟。
直方图绘制时使用opencv的一项基本的能力,但当初接触直方图是我并没有整太明白,如今又好好研究了一遍代码,现在来总结一下。
直方图,顾名思义,就是类似于我们常规意义上的统计图,有三个术语:直方图的计算使用到了calcHist()函数,函数原型:
calcHist( const Mat* images, //输入数组 int nimages, //输入数组个数 const int* channels, //通道索引 InputArray mask; //Mat(), //不使用腌膜 OutputArray hist, //输出的目标直方图,一个二维数组 int dims, //需要计算的直方图的维度 例如:灰度,R,G,B,H,S,V等数据 congst int* histSize, //存放每个维度的直方图尺寸的数组 const float** ranges, //每一维数组的取值范围数组 bool uniform=true, bool accumulate = false );
这里会用到一个返回最大值最小值的函数,在我的上篇博文《opencv学习笔记二:角点检测》中有介绍:
void cv::MinMaxLoc( arr , //输入单通道数组 double* min_val, //返回最小值的指针 double* max_val,//返回最大值的指针 Point* min_loc,//指向返回最小值的位置指针 Point* max_loc,//指向返回最大值的位置指针)
注意:它的输入为单通道数组
我们先从绘制一维图像开始,一维直方图就像我们在word里用的柱状图一样,有这类似的思路:
#include "pch.h"#include#include using namespace std;using namespace cv;int main(){ //【1】读取原图并显示 Mat srcImage = imread("5.jpg", 0); imshow("原图:", srcImage); if (!srcImage.data) { cout << "fail to load image" << endl; return 0; }
首先,上面的是开头,将需要计算的图片载入并显示,原图如下图:
但我们载入的时候为: Mat srcImage = imread(“5.jpg”, 0); 是按灰度图载入的,因此显示出来为://【2】定义变量 MatND dstHist; int dims = 1; //特征数目(直方图维度) float hranges[] = { 0,255 }; //特征空间的取值范围 const float *ranges[] = { hranges }; int size = 256; //存放每个维度的直方图的尺寸的数组 int channels = 0; //通道数
然后就需要定义变量了,MatND为多维,多通道的密集数组类型
dims为特征数目,此程序只计算该图片的一个特征,且图片是一张灰度图,由后面的int channals = 0我们可以看出,计算的是该图片的通道0,也就是灰度的直方图。 hranges[]为特征空间的取值范围数组,为0-255;有几个特征就需要定义几个这样的数组,然后将这些数组存到const float *ranges[] = { hranges }中 size为存放每个维度的直方图的尺寸的数组//【3】计算直方图 calcHist(&srcImage, 1, &channels, Mat(), dstHist, dims, &size, ranges); int scale = 1; cout << dstHist << endl; Mat dstImage(size * scale, size, CV_8U, Scalar(0)); //【4】获取最大值和最小值 double minValue = 0; double maxValue = 0; minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);
接下来我们计算直方图并获取脂肪图的最大,最小值。我并不清楚计算出来的直方图是啥子?所以我在中间cout<<desHist<<endl;看了一下,如下图:
图片很长,是一个一列很多行的数组,这我们就明白了,每一行数都表示一个像素点的灰度值。//【5】绘制直方图 int hpt = saturate_cast (0.9*size); for (int i = 0; i < 256; i++) { float binValue = dstHist.at(i); int realValue = saturate_cast (binValue*hpt / maxValue); rectangle(dstImage, Point(i*scale, size - 1), Point((i + 1)*scale - 1, size - realValue), Scalar(255)); } imshow("一维直方图", dstImage); waitKey(0); return 0;}
程序到此就结束了,在绘制直方图的这一段程序里,我们运用了rectangle()函数,函数原型如下:是一个画矩形的函数
rectangle( img, //输入图像 pt1, //矩阵的一个定点 pt2, //矩阵对角线上另一个顶点 color, //线条颜色(RGB)或亮度(灰度图像)(grayscale image) thickness, //组成矩形的线条的粗细程度。取负值时函数绘制填充了色彩的矩形 line_type, //线条的类型 shift //坐标点的小数点位数 );
在其中有一句 int hpt = saturate_cast(0.9size); 感觉0.9出现的很突然,这一句其实是可以调整直方图绘制的大小的,看了下面截图应该就明白了: 当:int hpt = saturate_cast(0.9size);
当:int hpt = saturate_cast(0.5*size); 看到这里就应该明白了吧?接下来我们看看包含H-S两个特征的二维直方图的绘制///
#include "pch.h"#include#include using namespace std;using namespace cv;int main(){ //【1】输入原图像,并转换为HSV颜色模型 Mat srcImage, hsvImage; srcImage = imread("5.jpg"); cvtColor(srcImage, hsvImage, COLOR_BGR2HSV);
还是,先读取一张图片,然后转换为HSV模型。H为色调,S为饱和度
//【2】参数准备 //将色调量化为30个等级,将饱和度量化为32个等级 int hueBinNum = 30; //色调的直方图直条数量 int saturationBinNum = 32; //饱和度的直方图直条数量 int histSize[] = { hueBinNum,saturationBinNum }; //存放每个维度的直方图尺寸的数组 float hueRanges[] = { 0,180 }; //色调变化范围为0-179 float saturationRanges[] = { 0,256 }; //定义饱和度变化范围为0(黑,白,灰)到255(纯光谱颜色) const float* ranges[] = { hueRanges,saturationRanges }; //每一位数值的取值范围数组
还是我们示例一程序中的思路,先定义bins,就是区间个数,示例一中我们定义为了256,在这儿我们将色调的设为30,饱和度的设为32;
然后将这两个尺寸放进int histSize[] = { hueBinNum,saturationBinNum };中用来传给直方图计算函数; 接着定义特征值的取值范围,色调的取值范围为0-180,饱和度的为0-256,同样,装进const float* ranges[] = { hueRanges,saturationRanges }; 数组中传给直方图计算函数;//输出目标直方图 MatND dstHist; //参数准备,calcHist函数中将计算第0通道和第一通道的直方图 int channels[] = { 0,1 }; //【3】 正式调用calcHist,进行直方图计算 calcHist( &hsvImage, //输入数组 1, //输入数组个数 channels, //通道索引 Mat(), //不使用腌膜 dstHist, //输出的目标直方图,一个二维数组 2, //需要计算的直方图的维度为2 histSize, //存放每个维度的直方图尺寸的数组 ranges, //每一维数组的取值范围数组 true, false );
//打印直方图 cout << dstHist<
然后就是直方图计算了,这里就不多说了。同样,我们还打印一下看看直方图计算出来是个什么东西:是一个比较大的二维数组,就是H-S的大小啦
double maxValue = 0; //最大值 minMaxLoc(dstHist, 0, &maxValue, 0, 0); //查找数组和子数组的全局最小值和最大值存入maxValue中 int scale = 10; Mat histImg = Mat::zeros(saturationBinNum *scale, hueBinNum * 10, CV_8UC3); //每个bin分配10个像素宽度 //【5】双层循环,进行直方图绘制 for (int hue = 0; hue < hueBinNum; hue++) { for (int saturation = 0; saturation < saturationBinNum; saturation++) { float binValue = dstHist.at(hue, saturation); //直方图直条的值 //访问像素值 int intensity = cvRound(binValue * 255 / maxValue); //强度 cvRound返回和参数最接近的整数值 //正式进行绘制 rectangle(histImg, Point(hue* scale, saturation*scale), Point((hue + 1)*scale - 1, (saturation + 1)*scale - 1), Scalar::all(intensity), FILLED); } } //【6】显示效果图 imshow("素材图", srcImage); imshow("H-S直方图", histImg); waitKey();}
最后,就是绘制直方图了:运行结果如下:
对于二维的绘制过程,和画图表也很相似,原理就是下图:最后我们绘制RGB三色直方图,虽然是三色,但也是一维的,而不像示例二中是二维直方图。
#include "pch.h"#include#include using namespace std;using namespace cv;int main(){ //【1】载入原图并显示 Mat srcImage; srcImage = imread("5.jpg"); imshow("素材图", srcImage); //【2】参数准备 int bins = 256; //直条数 int hist_size[] = { bins}; //存放每个维度的直方图尺寸数组 // 均为256条宽度 float range[] = { 0,256 }; //每一维数组的取值范围 // 均为0-255高度 const float* ranges[] = { range }; MatND redHist, grayHist, blueHist; //定义三个图像数组
开头我们就不多说了,还是千篇一律的设置尺寸等,我们至此还没有定义通道;
//计算红色分量 int channels_r[] = { 0 }; //每个图像数组一个通道 calcHist(&srcImage, 1,channels_r, Mat(), redHist, 1, hist_size, ranges, true, false); //计算绿色分量 int channels_g[] = { 1 }; calcHist(&srcImage, 1, channels_g, Mat(), grayHist, 1, hist_size, ranges, true, false); //计算蓝色分量 int channels_b[] = { 2 }; calcHist(&srcImage, 1, channels_b, Mat(), blueHist, 1, hist_size, ranges, true, false);
然后我们分别定义了通道0,1,2并分别进行了计算;
最后就是绘制了://参数准备 double maxValue_red, maxValue_green, maxValue_blue; minMaxLoc(redHist, 0, &maxValue_red, 0, 0); minMaxLoc(grayHist, 0, &maxValue_green, 0, 0); minMaxLoc(blueHist, 0, &maxValue_blue, 0, 0); int scale = 1; int histHeight = 256; Mat histImage = Mat::zeros(histHeight, bins * 3, CV_8UC3); //绘制直方图 for (int i = 0; i < bins; i++) { float binValue_red = redHist.at(i); float binValue_green = grayHist.at (i); float binValue_blue = blueHist.at (i); int intensity_red = cvRound(binValue_red*histHeight / maxValue_red); int intensity_green = cvRound(binValue_green*histHeight / maxValue_green); int intensity_blue = cvRound(binValue_blue*histHeight / maxValue_blue); rectangle(histImage, Point(i*scale, histHeight - 1), Point((i + 1)*scale - 1, histHeight - intensity_red), Scalar(255, 0, 0)); rectangle(histImage, Point((i+bins)*scale, histHeight - 1), Point((i+bins+ 1)*scale - 1, histHeight - intensity_green), Scalar(0, 255, 0)); rectangle(histImage, Point((i+bins*2)*scale, histHeight - 1), Point((i + bins*2+1)*scale - 1, histHeight - intensity_blue), Scalar(0, 0, 255)); } imshow("图像的RGB直方图", histImage); waitKey(0); return 0;}
运行结果如下:
转载地址:http://ldewi.baihongyu.com/