一、什么是直方图
直方图广泛应用于很多计算机视觉处理当中。通过标记帧与帧之间显著的边缘和颜色的变化,可以检测视频中的场景变化。在每个兴趣点设置一个有相似特征的直方图所构成的“标签”,可以用来标记各种不同的事情,比如图像的色彩分布,物体边缘梯度模板等等。是计算机视觉中最经典的工具之一。
简单来说直方图就是对数据进行统计的一种方法,它将统计值组织到一系列事先定义好的bin
中。bin
中的数值是从数据中计算出的特征的统计量,这些数据可以是梯度、方向、色彩以及其他任何特征。直方图获取的是数据分布的统计图,通常情况下,直方图的维度要低于原始数据。由于原始数据可以表示任何事情,直方图就可以很好的表示图像的特征。
二、直方图的相关函数
1、计算直方图 calcHist()
函数cv:calcHist()可以计算一个或多个数组的直方图。
void cv::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
)
其中,第一个参数表示输入的数组或数组集,它们需要相同的深度和相同的尺寸。第二个参数表示输入数组的个数,也就是第一个参数中有几个原数组。第三个参数表示需要统计的通道索引。第四个参数表示可选的操作掩码,如果不为空,那么必须为8位,并且与images[i]有同样大小的尺寸,这里的非零掩码元素用于标记出统计直方图的数组元素数据。第五个参数表示输出的目标直方图,是一个二维数组。第六个参数表示需要计算的直方图维度,必须是正数。第七个参数表示存放每个维度的直方图尺寸的数组。第八个参数表示每一维数值的取值范围,第九个参数表示直方图是否均匀的标识符,默认为true。第十个参数表示累计标识符,有默认值false。若为true,直方图在配置阶段不会被清零。此功能主要是允许从多个阵列中计算单个直方图,或者用于在特定的时间更新直方图。
2、找寻最值 minMaxLoc()
函数minMaxLoc() 查找最小和最大元素值及其位置。在整个数组中搜索极值,如果掩码不是空数组,则在指定的数组区域中搜索极值。
void cv::minMaxLoc ( InputArray src,
double * minVal,
double * maxVal = 0,
Point * minLoc = 0,
Point * maxLoc = 0,
InputArray mask = noArray()
)
其中第一个参数表示输入的单通道列阵。第二个参数表示返回最小值的指针,若无须返回则为NULL。第三个参数表示返回的最大值的指针,若无须返回则为NULL。第四个参数表示返回最小位置的指针若无须返回则为NULL。第五个参数表示返回最大位置的指针若无须返回则为NULL。
三、程序演示
1、色调 —— 饱和度直方图
下面我们来说明一下如何计算彩色图像的色调 —— 饱和度直方图。
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
int main()
{
// 读取图像并创建一个Mat对象来存储图像数据
Mat src, hsv;
src = imread("C://Users//86173//Desktop//c.png");
// 检查图像是否成功加载
if (!src.data)
return -1;
// 将图像从BGR颜色空间转换为HSV颜色空间
cvtColor(src, hsv, COLOR_BGR2HSV);
// 定义直方图的参数
int hbins = 30, sbins = 32;
int histSize[] = { hbins, sbins };
float hranges[] = { 0, 180 };
float sranges[] = { 0, 256 };
const float* ranges[] = { hranges, sranges };
// 创建一个用于存储直方图的Mat对象
MatND hist;
// 指定通道
int channels[] = { 0, 1 };
// 计算直方图
calcHist(&hsv, 1, channels, Mat(), hist, 2, histSize, ranges, true, false);
// 寻找直方图的最大值
double maxVal = 0;
minMaxLoc(hist, 0, &maxVal, 0, 0);
// 定义绘制直方图的参数
int scale = 10;
Mat histImg = Mat::zeros(sbins * scale, hbins * 10, CV_8UC3);
// 绘制直方图
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);
rectangle(histImg, Point(h * scale, s * scale), Point((h + 1) * scale - 1, (s + 1) * scale - 1), Scalar::all(intensity), -1);
}
}
// 创建并显示源图像窗口
namedWindow("Source", 1);
imshow("Source", src);
// 创建并显示H-S直方图窗口
namedWindow("H-S Histogram", 1);
imshow("H-S Histogram", histImg);
waitKey();
}
2、一维直方图
下面我们来介绍一下如何计算绘制图像一维直方图。
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/utils/logger.hpp>
using namespace cv;
using namespace std;
int main()
{
// 读取图像
Mat srcImage = imread("C://Users//86173//Desktop//c.png");
if (!srcImage.data)
{
printf("fail to load image!\n");
return 0;
}
// 显示原始图像
imshow("【原始图】", srcImage);
// 计算直方图
MatND dstHist;
int dims = 1;
float hranges[] = { 0, 255 };
const float* ranges[] = { hranges };
int size = 256;
int channels = 0;
calcHist(&srcImage, 1, &channels, Mat(), dstHist, dims, &size, ranges, true, false);
// 创建用于显示直方图的图像
int scale = 1;
Mat dstImage(size * scale, size, CV_8U, Scalar(0));
// 获取直方图的最小值和最大值
double minValue = 0;
double maxValue = 0;
minMaxLoc(dstHist, &minValue, &maxValue, 0, 0);
// 绘制直方图
int hpt = saturate_cast<int>(0.9 * size);
for (int i = 0; i < 256; i++)
{
float binValue = dstHist.at<float>(i);
int realValue = saturate_cast<int>(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;
}
3、RGB 三色直方图
下面我们来介绍一下如何绘制图像的 RGB 三色直方图。
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/utils/logger.hpp>
using namespace cv;
using namespace std;
int main()
{
// 读取图像
Mat srcImage = imread("C://Users//86173//Desktop//c.png");
if (!srcImage.data)
{
printf("fail to load image!\n");
return 0;
}
imshow("【原始图】", srcImage);
int bins = 256;
int hist_size[] = { bins };
float range[] = { 0, 256 };
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);
// 绘制三色直方图
// 参数准备
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<float>(i);
float binValue_green = grayHist.at<float>(i);
float binValue_blue = blueHist.at<float>(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;
}
更多专栏订阅推荐: