opencv——图像直方图与反向投影
引言
在图像处理中,对于直方图这个概念,肯定不会陌生。但是其原理真的可以信手拈来吗?
本文篇幅有点长,在此列个目录,大家可以跳着看:
- 分析图像直方图的概念,以及opencv函数calcHist()对于RGB图像的直方图的绘制
- 在其基础上自已定义函数实现对灰度图像直方图的简单绘制
- 直方图均衡化
- 直方图的反向投影
图像直方图分析以及opencv函数实现
(一)直方图的介绍
直方图到底可以干什么呢?我觉得最明显的作用就是有利于很直观的对图像进行分析了,直方图就像我们常用的统计图,直方图可以用来描述各种不同的事情,如物体的色彩分布、物体边缘梯度模板,以及表示目标位置的概率分布。
例如:我们统计了一个有11个学生的班级的身高和体重情况,身高为160cm的有5人,170cm的有4人,180cm的有2人。然后看体重,体重160斤的有3人,170斤的有5人,180斤的有3人。
用直方图统计就是这样:
在opencv中,对于图像的直方图来说。对应上图数据,也有三个参数:
- dims:需要统计的特征的数目。如上面例子里有身高和体重两个特征。
- bins:每个特征空间子区段数目。如身高有160,170,180所以子区段数目为3。
- range:每个特征空间的取值范围。例如:身高的取值范围就是[160,180]
直方图的意义:
1. 直方图是图像中像素强度分布的图形表达方式。
2. 直方图统计了每一个强度值所具有的像素个数。
任一幅图像,都能唯一地算出一幅与它对应的直方图。但不同的图像,可能有相同的直方图。即图像与直方图之间是多对一的映射关系。
(二)直方图API
直方图是对数据的统计,并把统计值显示到事先设定好的bin(如上表,设置160,170,180)中,bin中的数值是从数据中计算出的特征的统计量。总之,直方图获取的是数据分布的统计图,通常直方图的维数要低于原始数据。
在OpenCV中封装了直方图的计算函数calcHist
,为了更为通用,该函数的参数有些复杂,其声明如下:
calcHist( const Mat* images, //输入图像的数组(CV_8U,CV_16U,CV_32F) int nimages, //输入数组个数 const int* channels, //通道索引,可以传一个数组 {0, 1} 表示计算第0通道与第1通道的直方图,此数组长度要与histsize,ranges 数组长度一致 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实现:
Mat src = imread("D:/opencv练习图片/src1.jpg"); imshow("原图片", src); // //步骤一:分通道显示 vector<Mat> bgr_plane; split(src, bgr_plane); //split//把多通道图像分为多个单通道图像 (const Mat &src, //输入图像 Mat* mvbegin //输出的通道图像数组) //步骤二:计算直方图 // 定义参数变量 const int channels[1] = { 0 }; const int bins[1] = { 256 }; float hranges[2] = { 0,255 }; const float* ranges[1] = { hranges }; Mat b_hist; Mat g_hist; Mat r_hist; // 计算Blue, Green, Red通道的直方图 calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges); calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges); calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges); // 显示直方图 int hist_w = 512; int hist_h = 400; int bin_w = cvRound((double)hist_w / bins[0]);//直方图的等级 Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3); // 归一化直方图数据(范围在0-400) normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat()); normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat()); normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat()); //步骤三:绘制直方图并显示 for (int i = 1; i < bins[0]; 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); } // 显示直方图 namedWindow("Histogram Demo", WINDOW_AUTOSIZE); imshow("Histogram Demo", histImage);