1、绘制图像直方图的步骤
- 调用split函数,将彩色图像的通道进行分离
- 调用calcHist函数,返回Mat类型的向量
- 如果要在特定长宽的图像中显示图像直方图,需要进行归一化操作
- 在for循环中绘制直线
2、具体函数讲解
(1) split()函数
split(InputArray src, OutputArray dst);
其中:
第一个参数src:是指输入的图像
第二个参数dst:是指输出的数组或者是向量(vector类型)
如果可以用一个动作形容调用该函数的话,我习惯称之为削土豆。
(2)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:输入应当是一个数组(图像的地址)
第二个参数nimages:是指输入数组(图像地址)的个数
第三个参数channels:指通道数
第四个参数mask:掩膜
第五个参数hist:输出的图像直方图,一个二维数组
第六个参数dims:直方图的维数
第七个参数histSize:应当是一个指针,指向一个保存直方图尺寸的变量(例如int *histSize = 256)
第八个参数ranges:应当是一个指向指针的指针,具体含义是指直方图的范围(比如[0, 256])
(3)normalize()函数
void normalize(InputArray src,OutputArraydst, double alpha = 1, double beta = 0, intnorm_type = NORM_L2, int dtype = -1, InputArray mask = noArray());
其中:
第一个参数src:输入图像
第二个参数dst:输出图像
第三参数alpha:输出的最小值
第四个参数beta:输出的最大值
第五个参数norm_type:归一化的类型,类型包括NORM_INF,NORM_L1,NORM_L2,NORM_MINMAX等
第六个参数dtype:默认值-1,当参数为负数时,输出矩阵和src有同样的类型,否则,它和src有同样的通道数。
第七个参数mask:掩膜
3、完整代码
(1) 源图
(2) 效果图
完整代码如下:
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <vector>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char ** argv) {
Mat srcImage = imread("1.jpg");
if (!srcImage.data) {
printf("could not load this picture!\n");
return -1;
}
imshow("源图像", srcImage);
//调用split函数,将图像的通道进行拆分
vector<Mat> channels;
split(srcImage, channels);
int histsize = 256;
float range[] = { 0, 256 };
const float * histRange = { range };
Mat b_hist, r_hist, g_hist;
calcHist(&channels[0], 1,0, Mat(), b_hist, 1,&histsize, &histRange);
calcHist(&channels[1], 1, 0, Mat(), g_hist, 1, &histsize, &histRange);
calcHist(&channels[2], 1, 0, Mat(), r_hist, 1, &histsize, &histRange);
//创建相关变量
int hist_w = 512;
int hist_h = 400;
int bins = cvRound((double)hist_w / histsize);
Mat histImage = Mat(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));
//归一化处理
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 (size_t i = 0;i < histsize -1; i++) {
// //画蓝色通道的直方图
// line(histImage, Point(bins * i, hist_h - cvRound( b_hist.at<float>(i))),
// Point(bins * i, hist_h - cvRound(b_hist.at<float>(i + 1))), Scalar(0, 0, 255), 2, LINE_AA);
// //画绿色通道的直方图
// line(histImage, Point(bins * i, hist_h - cvRound( g_hist.at<float>(i))),
// Point(bins * i, hist_h - cvRound( g_hist.at<float>(i + 1))), Scalar(0, 255, 0), 2, LINE_AA);
// //画红色通道的直方图
// line(histImage, Point(bins * i, hist_h - cvRound( r_hist.at<float>(i))),
// Point(bins * i, hist_h - cvRound(r_hist.at<float>(i + 1))), Scalar(255, 0, 0),2, LINE_AA);
//}
//方法二
//绘制的是连续的图形,和第一种的主要区别就是在绘图时取的第二个点的x不同
for (size_t i = 0; i < histsize -1; i++) {
line(histImage, Point(bins * i, hist_h - cvRound(b_hist.at<float>(i))),
Point(bins * (i+1), hist_h - cvRound(b_hist.at<float>(i + 1))), Scalar(0, 0, 255), 1, LINE_AA);
line(histImage, Point(bins * i, hist_h - cvRound(g_hist.at<float>(i))),
Point(bins * (i + 1), hist_h - cvRound(g_hist.at<float>(i + 1))), Scalar(0, 255, 0), 1, LINE_AA);
line(histImage, Point(bins * i, hist_h - cvRound(r_hist.at<float>(i))),
Point(bins *(i + 1), hist_h - cvRound(r_hist.at<float>(i + 1))), Scalar(255, 0, 0), 1, LINE_AA);
}
imshow("图像直方图", histImage);
waitKey(0);
return 0;
}