要绘制直方图,最重要的三个函数是calcHist、line、和rectangle,下面分别进行介绍!
calcHist函数:
calcHist函数的原型如下:
void 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 );
const Mat* images:待计算直方图的图像源的指针,注意是指针,因为有可能包含多幅图
int nimage:表示待计算图像源中图像的个数,一般情况下都取值为1,就算你要计算多幅,也可以一个个地算啊!如果是多幅,那么说明 images是一个数组撒!
const int* channels:表示要计算哪些通道的指针,注意是指针,因为通道数可能是多个。灰度图为0,HSV的H-S直方图为0和1,BGR为0,1,2(不过BGR图可以使用split分离三个通道,再分别计算,通常也是这样处理)
InputArray mask:掩码阵列,关于这个参数可以参看我写的博文http://blog.csdn.net/wenhao_ir/article/details/51120508
OutputArray hist:存储计算出的直方图数据
int dims:直方图的维度,单通道为1,H-S直方图为2,一维的就是一条线,二维的就是一个平面(即包含x,y两个维度)
const int* histSize:表示直方图横坐标的区间数的指针,注意也是指针,因为对于二维的直方图而言,有两个维度的变量区间
const float** ranges:表示直方图每一维变量的上下界,注意是指针的指针,因为每一维度的区间都是两个边界值,又有可能是多维,如果不明白我说的话,看H-S直方图源码就清楚了
bool uniform=true:表示直方图是否均匀的标志,什么叫是否均匀?我这里就不翻译译官方的文档的注释了。我就说下自己的理解。要理解这个,首先要知道histSize的含义,histSize就明表示咱们这个直方图的横坐标被划分为多少个区间。如果是均匀的,那么从下界到上界之间就被均匀划分为这么多个区间,如果是非均匀,就需要在ranges中给出各个区间的起点和终点,显然histSize个区间需要histSize+1个点。这里我要说明一下在均匀的情况下如果ranges中写了多个区间程序是怎么操作的?比如自定义直方图程序中的histSize[1]={256};
float hranges[6]={0, 60, 120, 160, 220, 255};具体是这样操作的:把256个区间平分5个大区间中,即每个区间51.2个小区间,然后这些每个大区间再分别被划为51.2个小区间。就是这回事!
bool accumulate=false :这个参数表示如果你之前计算过这个直方图,那么这个直方图再次计算时一开始需不需要清零,显然,如果不清零,那么再次计算时是一种更新。
line函数:
这个函数就是利用两点确定一条直线的原理绘线,原型如下:
CV_EXPORTS_W void line(CV_IN_OUT Mat& img, Point pt1, Point pt2, const Scalar& color,
int thickness=1, int lineType=8, int shift=0);
一看原型就什么都知道了,所以没必要多讲!
rectangle函数:
rectangle的绘制过程可以参见博文:
另外有一点我需要特别强调一下 那就是用calcHist计算出来的255级灰度值总是0,关于这个问题,我专门写了篇博文,详见http://blog.csdn.net/wenhao_ir/article/details/51332416 如果以后你的程序中要用到255级灰度值,那么可以自己写一段程序计算255级的灰度值,这个程序实际上很容易完成,可以参考我写的博文http://blog.csdn.net/wenhao_ir/article/details/51332416
好,接下来上源码,明白了以上函数的使用之后,再来看这个源码就很容易了!所以我就不注释什么了~
源码中用到的图片的下载链接:http://pan.baidu.com/s/1kUEDw5x
首先是灰度直方图的源码
//OpenCV版本2.4.9 //交流QQ2487872782 #include <opencv2\opencv.hpp> int main() { // 图像源获取及判断 cv::Mat Image, ImageGray; //Image = cv::imread("coins.png"); Image = cv::imread("flower3.jpg"); if(Image.empty()) return -1; cv::imshow("Image",Image); // 转换为灰度图像 cv::cvtColor(Image,ImageGray,CV_BGR2GRAY); // 定义直方图参数 const int channels[1]={0}; const int histSize[1]={256}; float pranges[2]={0,255}; const float* ranges[1]={pranges}; cv::MatND hist; // 计算直方图 cv::calcHist(&ImageGray,1,channels,cv::Mat(),hist,1, histSize,ranges); // 初始化画布参数 int hist_w = 500; int hist_h = 500; int nHistSize = 255; // 区间 int bin_w = cvRound( (double) hist_w / nHistSize ); cv::Mat histImage( hist_w, hist_h, CV_8UC3, cv::Scalar( 0,0,0) ); // 将直方图归一化到范围 [ 0, histImage.rows ] normalize(hist, hist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat() ); // 在直方图画布上画出直方图 for( int i = 1; i < nHistSize; i++ )//注意这里没有绘制255级的灰度值 { cv::line( histImage, cv::Point(bin_w*(i-1), hist_h-cvRound(hist.at<float>(i-1)) ) , cv::Point( bin_w*(i), hist_h - cvRound(hist.at<float>(i)) ), cv::Scalar( 0, 0, 255), 2, 8, 0 ); } int hist_255; hist_255=int(hist.at<float>(255)); // 显示直方图 cv::imshow("histImage", histImage); cv::waitKey(); return 0; }
其次是H-S直方图的源码
//OpenCV版本2.4.9 //交流QQ2487872782 #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> using namespace std; using namespace cv; int main() { cv::Mat srcImage, hsvMat; srcImage = cv::imread("flower3.jpg"); if(srcImage.empty()) return -1; cv::cvtColor(srcImage, hsvMat, CV_BGR2HSV); // 初始化灰度阶参数 int hbins = 30, sbins = 32; int histSize[] = {hbins, sbins}; // 灰度变化范围设置 float hranges[] = { 0, 180 }; // 饱和度变化范围 float sranges[] = { 0, 256 }; const float* ranges[] = { hranges, sranges }; cv::MatND hist; // 选取计算直方图通道 int channels[] = {0, 1}; // 计算当前通道直方图 cv::calcHist( &hsvMat, 1, channels, cv::Mat(), hist, 2, histSize, ranges, true, false ); double maxVal=0; // 找到直方图的最大值 cv::minMaxLoc(hist, 0, &maxVal, 0, 0); int scale = 10; cv::Mat histImg = cv::Mat::zeros(sbins*scale, hbins*10,CV_8UC3); // 遍历hs通道 for( int h = 0; h < hbins; h++ ) { for( int s = 0; s < sbins; s++ ) { float binVal = hist.at<float>(h, s); // 根据最大值计算变换范围 int intensity = cvRound(binVal*255/maxVal); // 绘图显示 cv::rectangle( histImg, cv::Point(h*scale, s*scale), cv::Point( (h+1)*scale - 1, (s+1)*scale - 1), cv::Scalar::all(intensity), CV_FILLED ); } } cv::imshow( "Source", srcImage); cv::imshow( "H-S Histogram", histImg); cv::waitKey(); }
再次是BGR直方图
//OpenCV版本2.4.9 //交流QQ2487872782 #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> using namespace std; using namespace cv; int main() { // 图像获取及判断 cv::Mat srcImage = cv::imread("flower3.jpg"); if( !srcImage.data ) return 1; cv::imshow("srcImage",srcImage); // 分离图像的三个通道 B G R std::vector<cv::Mat> bgr_planes; split( srcImage, bgr_planes ); // 初始化直方图计算参数 int histSize = 256; float range[] = { 0, 256 }; const float* histRange = { range }; bool uniform = true; bool accumulate = false; cv::Mat b_hist, g_hist, r_hist; // 计算各个通道的直方图 calcHist( &bgr_planes[0], 1, 0, cv::Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate ); calcHist( &bgr_planes[1], 1, 0, cv::Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate ); calcHist( &bgr_planes[2], 1, 0, cv::Mat(),r_hist, 1, &histSize, &histRange, uniform, accumulate ); // 设置直方图绘图参数 int hist_w = 640; int hist_h = 512; int bin_w = cvRound( (double) hist_w/histSize ); cv::Mat histImage( hist_h, hist_w, CV_8UC3, cv::Scalar( 0,0,0) ); // 分别归一化直方图到[ 0, histImage.rows ] normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() ); normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() ); normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() ); // 分别对每个通道进行绘图 for( int i = 1; i < histSize; i++ ) { line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) , Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ), Scalar( 255, 0, 0), 2, 8, 0 ); line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) , Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ), Scalar( 0, 255, 0), 2, 8, 0 ); line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ) , Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ), Scalar( 0, 0, 255), 2, 8, 0 ); } imshow("calcHist", histImage ); waitKey(0); return 0; }
最后是自定义直方图
//OpenCV版本2.4.9 //交流QQ2487872782 #include <opencv2/opencv.hpp> int main() { // 图像获取及判断 cv::Mat srcImage = cv::imread("flower3.jpg"); if( !srcImage.data ) return 1; cv::imshow("srcImage",srcImage); // 灰度转换 cv::Mat srcGray; cv::cvtColor(srcImage,srcGray,CV_BGR2GRAY); // 初始化直方图计算参数 const int channels[1]={0}; const int histSize[1]={256}; // 设定区间[0 60],[61 120],[121 160],[161 220],[221 255] float hranges[6]={0, 60, 120, 160, 220, 255}; const float* ranges[1] = {hranges}; cv::MatND hist; // 计算直方图 cv::calcHist( &srcGray, 1, channels, cv::Mat(), hist, 1, histSize, ranges ); // 求直方图中最大值 double maxHist=0; cv::minMaxLoc(hist, 0, &maxHist, 0 ,0); // 设置直方图绘图参数 int hist_Size = hist.rows; cv::Mat histImg(hist_Size, hist_Size, CV_8U, cv::Scalar(255)); // 直方图绘制 for(int h = 0; h < hist_Size; h++) { float binVal = hist.at<float>(h); //归一化 根据最大值计算变换范围 int intensity = static_cast<int>(binVal * hist_Size / maxHist); // 绘图直方图信息 //cv::line(histImg, cv::Point(h, hist_Size), // cv::Point(h, hist_Size - intensity), // cv::Scalar::all(0)); cv::line(histImg, cv::Point(h, hist_Size), cv::Point(h, hist_Size - intensity), cv::Scalar::all(0)); } cv::imshow("histImg", histImg); cv::waitKey(0); return 0; }
运行结果依次如下图所示:
-------------------------------------------
欢迎大家加入图像识别技术交流群:271891601,另外,特别欢迎成都从事图像识别工作的朋友交流,我的QQ号2487872782