0014-OpenCV环境下计算并绘制灰度直方图的源码!
图像的直方图是图像进行运算时的一个重要的数据特征,许多算法都需要用到图像的直方图数据,OpenCV提供了函数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);
参数意义如下:
images:源图像数组,数组内的图片应该有相同的深度和大小,图像深度只能是CV_8U或CV_32F,通道数可以不同,注意这个参数是指针。
nimages:要计算图像的个数。
channel:计算哪些通道的直方图。通道编号方法:第一张图的通道编号为0至images[0].channels()-1,第二张图的通道编号为images[0].channels()至images[0].channels() + images[1].channels()-1,以此类推,注意这个参数是指针。
mask:掩码阵列。掩码中的非0元素对应的图像元素将会被计算,0元素则被屏蔽不参与计算。掩码阵列可以为空。
hist:直方图计算结果存储阵列。
dims:直方图的维度,比如灰度图的直方图为1维,H-S直方图为2维,以此类推,最大不超过CV_MAX_DIMS,目前CV_MAX_DIMS=32。
histSize:各个维度的大小。
ranges:存储每个维度的统计范围。
uniform:直方图是否均匀化的标志,意义暂时不清楚,等以后搞清楚了再来补充说明。
accumulate:记忆标志。表示这个函数空间被再次调用前是否清零,如果不清零,则可以使得用户存储多张图的直方图数据,或者对之前的计算过的直方图进行更新。
OpenCV下计算并绘制直方图的代码如下:
图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!
源码中使用的图像下载链接:http://pan.baidu.com/s/1kUJMx1t 密码:r3d3
//opencv版本:OpenCV3.0
//VS版本:VS2013
//Author:qxsf321.net
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img = imread("02.jpg");
if (img.empty())
{
cout << "Error: Could not load image" << endl;
return 0;
}
Mat srcImage;
cvtColor(img, srcImage, CV_BGR2GRAY);
imshow("【原图的灰度图】", srcImage);
//为计算直方图配置变量
//首先是需要计算的通道编号,就是需要计算哪个通道的直方图(BGR空间需要确定计算,计算方法见帖子中对相关参数的说明)
int channels = 0;
//然后是定义直方图计算结果的存储空间
Mat dstHist;
//接下来是直方图的每一个维度的数目(这个数目用于将每一维度的数值分组)
int histSize[] = { 256 };
//最后是确定每个维度的取值范围,就是每一维度的横坐标的取值范围
//首先得定义一个数组用来存储单个维度的的取值范围
float midRanges[] = { 0, 256 };
//然后把这个数组再放到一个二维数组中
const float *ranges[] = { midRanges };
//准备工作做好后,就可以调用calcHis函数计算直方图数据了
calcHist(&srcImage, 1, &channels, Mat(), dstHist, 1, histSize, ranges, true, false);
//calcHist函数调用结束后,dstHist变量中将储存直方图数据
//接下来绘制直方图
//首先先创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像
Mat drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
//因为图像中的某个灰度值的总个数可能会超出所定义的图像的尺寸,所以要对个数进行归一化处理
//先用 minMaxLoc函数来得到计算直方图中的最大数据,这个函数的使用方法大家一看函数原型便知
double g_dHistMaxValue;
minMaxLoc(dstHist, 0, &g_dHistMaxValue, 0, 0);
//遍历直方图数据,对数据进行归一化和绘图处理
for (int i = 0; i < 256; i++)
{
int value = cvRound(dstHist.at<float>(i) * 256 * 0.9 / g_dHistMaxValue);
//line(drawImage, Point(i, drawImage.rows - 1), Point(i, drawImage.rows - 1 - value), Scalar(255, 0, 0));
line(drawImage, Point(i,0), Point(i, value), Scalar(255, 0, 0));
}
//对绘制出的图像作y方向上的翻转处理,以符合我们的看图习惯
// 输出矩阵定义
Mat resultImage(drawImage.size(), drawImage.type());
// X与Y方向矩阵
Mat xMapImage(drawImage.size(), CV_32FC1);
Mat yMapImage(drawImage.size(), CV_32FC1);
int rows = drawImage.rows;
int cols = drawImage.cols;
for (int j = 0; j < rows; j++)
{
for (int i = 0; i < cols; i++)
{
// y方向上翻转
xMapImage.at<float>(j, i) = i;
yMapImage.at<float>(j, i) = rows - j;
}
}
// 重映射操作
remap(drawImage, resultImage, xMapImage, yMapImage,
CV_INTER_LINEAR, cv::BORDER_CONSTANT,
cv::Scalar(0, 0, 0));
imshow("【未翻转前的直方图】", drawImage);
imshow("【翻转后的直方图】", resultImage);
waitKey(0);
return 0;
}
代码说明:
代码中用到了line函数来绘制直方图,这里给出它的原型,大家看下原型再结合原型自然就明白了怎么用了。
void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,int thickness = 1, int lineType = LINE_8, int shift = 0);
另外,大家要注意,在opencv中,坐标原点在左上角,也就是说向下为y轴正方向,所以代码中为了大家的阅读习惯还对图像进行了一次翻转处理。翻转部分代码的原理请大家参看博文https://blog.****.net/lehuoziyuan/article/details/84029770
代码运行结果截图如下: