一、介绍
图像直方图是用一表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。可以借助观察该直方图了解需要如何调整亮度分布的直方图。这种直方图中,横坐标的左侧为纯黑、较暗的区域,而右侧为较亮、纯白的区域。因此,一张较暗图片的图像直方图中的数据多集中于左侧和中间部分,而整体明亮、只有少量阴影的图像则相反。计算机视觉邻域常借助图像直方图来实现图像的二值化。
二、API函数
C++ Void calcHist(
const Mat* images,//输入图像指针
int images,// 图像数目
const int* channels,// 通道数
InputArray mask,// 输入mask,可选,不用
OutputArray hist,//输出的直方图数据
int dims,// 维数
const int* histsize,// 直方图级数
const float* ranges,// 值域范围
bool uniform,// true by default
bool accumulate)// false by defaut
函数cvRound,cvFloor,cvCeil 都是用一种舍入的方法将输入浮点数转换成整数:
cvRound():返回跟参数最接近的整数值,即四舍五入;
cvFloor():返回不大于参数的最大整数值,即向下取整;
cvCeil():返回不小于参数的最小整数值,即向上取整;
三、绘制图像直方图
1、使用API函数 calcHist计算图像直方图;
2、进行直方图归一化;
3、绘制各级直方图;
#include<opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat src, dst;
src = imread("1.jpg");
cvtColor(src, src, COLOR_BGR2GRAY);
int histSize = 16;
float range[] = { 0, 256 };
const float* histRange = { range };
if (src.data && src.channels()==1)
{
cout << "您输入的是灰度图像" << endl;
Mat hist;
calcHist(&src, 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);
//绘制直方图
int hist_w = 512, hist_h = 400;
//直方图中各点x轴距离
int distance = cvRound((double)hist_w / histSize);
//创建一个图像以显示直方图
Mat histImage1(hist_h, hist_w, src.type(), Scalar(255, 255, 255));
//归一化直方图
normalize(hist, hist, 0, hist_h, NORM_MINMAX, -1,Mat());
for (int i = 1; i < histSize; i++)
{
// hist_h - cvRound(hist.at<float>(i)):hist.at<float>(i)存储的值是从图像
//左上角为0开始计算的,所以需要使用hist_h减以后才是坐标轴的纵轴值
line(histImage1, Point(distance * (i-1), hist_h - cvRound(hist.at<float>(i-1))),
Point(distance * (i), hist_h - cvRound(hist.at<float>(i))),
Scalar(0,0,255),2,8,0);
}
namedWindow("calcHist1", WINDOW_AUTOSIZE);
imshow("calcHist1", histImage1);
}
else if (src.data && src.channels() == 3)
{
cout << "您输入的是彩色图像" << endl;
vector<Mat> mv;
split(src, mv);
Mat b_hist, g_hist, r_hist;
calcHist(&mv[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, true, false);
calcHist(&mv[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, true, false);
calcHist(&mv[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, true, false);
// 绘制直方图
int hist_w = 512,hist_h = 400;
int distance = cvRound((double)hist_w / histSize);
Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(255, 255, 255));
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 < histSize; i++)
{
//
line(histImage, Point(distance * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
Point(distance * (i), hist_h - cvRound(b_hist.at<float>(i))),
Scalar(255, 0, 0), 2, 8, 0);
line(histImage, Point(distance * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),
Point(distance * (i), hist_h - cvRound(g_hist.at<float>(i))),
Scalar(0, 255, 0), 2, 8, 0);
line(histImage, Point(distance * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),
Point(distance * (i), hist_h - cvRound(r_hist.at<float>(i))),
Scalar(0, 0, 255), 2, 8, 0);
}
namedWindow("calcHist", WINDOW_AUTOSIZE);
imshow("calcHist", histImage);
}
else
{
cout << "您未输入图像" << endl;
return -1;
}
waitKey(0);
return 0;
}
四、直方图比较
- 为了比较两个直方图( H1 和 H2 ),首先我们必须选择度量( d(H1,H2)来表示两个直方图的匹配度。
- OpenCV实现函数cv :: compareHist进行比较。它还提供4种不同的指标来计算匹配:
- 相关性(CV_COMP_CORREL)(1为最相似)
其中:
N是直方图库的总数。
- Chi-Square(CV_COMP_CHISQR)(0为最相似)
- 交点(method= CV_COMP_INTERSECT)(越大越相似)
- Bhattacharyya distance ( CV_COMP_BHATTACHARYYA )(0为最相似)
*Method* | Base - Base |
---|---|
*Correlation* | 1.000000 |
*Chi-square* | 0.000000 |
*Intersection* | 24.391548 |
*Bhattacharyya* | 0.000000 |
#include<opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
Mat src, dst;
src = imread("12.bmp");
namedWindow("原图", WINDOW_AUTOSIZE);
imshow("原图", src);
int histSize = 16;
float range[] = { 0, 256 };
const float* histRange = { range };
if (src.data && src.channels()==1)
{
cout << "您输入的是灰度图像" << endl;
Mat hist;
calcHist(&src, 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false);
//绘制直方图
int hist_w = 512, hist_h = 400;
//直方图中各点x轴距离
int distance = cvRound((double)hist_w / histSize);
//创建一个图像以显示直方图
Mat histImage1(hist_h, hist_w, src.type(), Scalar(255, 255, 255));
//归一化直方图
normalize(hist, hist, 0, hist_h, NORM_MINMAX, -1,Mat());
for (int i = 1; i < histSize; i++)
{
// hist_h - cvRound(hist.at<float>(i)):hist.at<float>(i)存储的值是从图像
//左上角为0开始计算的,所以需要使用hist_h减以后才是坐标轴的纵轴值
line(histImage1, Point(distance * (i-1), hist_h - cvRound(hist.at<float>(i-1))),
Point(distance * (i), hist_h - cvRound(hist.at<float>(i))),
Scalar(0,0,255),2,8,0);
}
namedWindow("calcHist1", WINDOW_AUTOSIZE);
imshow("calcHist1", histImage1);
}
else if (src.data && src.channels() == 3)
{
cout << "您输入的是彩色图像" << endl;
vector<Mat> mv;
split(src, mv);
Mat b_hist, g_hist, r_hist;
calcHist(&mv[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRange, true, false);
calcHist(&mv[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRange, true, false);
calcHist(&mv[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRange, true, false);
// 绘制直方图
int hist_w = 512,hist_h = 400;
int distance = cvRound((double)hist_w / histSize);
Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(255, 255, 255));
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 < histSize; i++)
{
//
line(histImage, Point(distance * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
Point(distance * (i), hist_h - cvRound(b_hist.at<float>(i))),
Scalar(255, 0, 0), 2, 8, 0);
line(histImage, Point(distance * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),
Point(distance * (i), hist_h - cvRound(g_hist.at<float>(i))),
Scalar(0, 255, 0), 2, 8, 0);
line(histImage, Point(distance * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),
Point(distance * (i), hist_h - cvRound(r_hist.at<float>(i))),
Scalar(0, 0, 255), 2, 8, 0);
}
//比较直方图相似度
double similar = compareHist(b_hist, b_hist, CV_COMP_CORREL);
cout << similar << endl;
namedWindow("calcHist", WINDOW_AUTOSIZE);
imshow("calcHist", histImage);
}
else
{
cout << "您未输入图像" << endl;
return -1;
}
waitKey(0);
return 0;
}