前言
这是我《OpenCV:从零到一》专栏的第十七篇博客,想看跟多请戳这。
本文概要
equalizeHist
split
calcHist
calcBackProject
compareHist
mixChannels
案例代码
大概内容:直方图均衡化和计算 。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat src = imread("D:/86186/Documents/opencv/lena.jpg");
//Mat src = imread("D:/86186/Documents/opencv/geometricFigure.png");
if (!src.data) {
printf("could not load image...\n");
return -1;
}
char INPUT_T[] = "input image";
char OUTPUT_T[] = "histogram demo";
namedWindow(INPUT_T, WINDOW_AUTOSIZE);
namedWindow(OUTPUT_T, WINDOW_AUTOSIZE);
imshow(INPUT_T, src);
// 分通道显示
vector<Mat> bgr_planes;
split(src, bgr_planes);//把多通道图像分为多个单通道图像
/*
const Mat &src, //输入图像
Mat* mvbegin// 输出的通道图像数组
*/
//imshow("single channel demo", bgr_planes[0]);
// 计算直方图
int histSize = 256;
float range[] = { 0, 256 };
const float *histRanges = { range };
Mat b_hist, g_hist, r_hist;
calcHist(&bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, &histRanges, true, false);
calcHist(&bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, &histRanges, true, false);
calcHist(&bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, &histRanges, true, false);
/*
const Mat* images,//输入图像指针
int images,// 图像数目
const int* channels,// 通道列表指针
InputArray mask,// 输入mask,可选,不用
OutputArray hist,//输出的直方图数据
int dims,// 维数
const int* histsize,// 直方图级数
const float* ranges,// 值域范围
bool uniform=true,
bool accumulate=false
*/
// 归一化
int hist_h = 400;
int hist_w = 512;
int bin_w = hist_w / histSize;
Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
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());
// render histogram chart
for (int i = 1; i < histSize; i++) {
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(b_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, LINE_AA);
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, LINE_AA);
line(histImage, Point((i - 1)*bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, LINE_AA);
}
imshow(OUTPUT_T, histImage);
//均衡化
cvtColor(src, src, COLOR_BGR2GRAY);
imshow("gray", src);
equalizeHist(src, src);
//equalizeHist(InputArray src,OutputArray dst)//输入图像,必须是8-bit的单通道图像
imshow("equalizeHist", src);
waitKey(0);
return 0;
}
运行效果:
大概内容:直方图比较。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
string convertToString(double d);
int main(int argc, char** argv) {
Mat base, test1, test2;
Mat hsvbase, hsvtest1, hsvtest2;
base = imread("D:/86186/Documents/opencv/lena.jpg");
if (!base.data) {
printf("could not load image...\n");
return -1;
}
test1 = imread("D:/86186/Documents/opencv/lena2.jpg");
test2 = imread("D:/86186/Documents/opencv/lena3.jpg");
cvtColor(base, hsvbase, COLOR_BGR2HSV);
cvtColor(test1, hsvtest1, COLOR_BGR2HSV);
cvtColor(test2, hsvtest2, COLOR_BGR2HSV);
int h_bins = 50; int s_bins = 60;
int histSize[] = { h_bins, s_bins };
// hue varies from 0 to 179, saturation from 0 to 255
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
// Use the o-th and 1-st channels
int channels[] = { 0, 1 };
Mat hist_base;
Mat hist_test1;
Mat hist_test2;
/*
MatND hist_base;
MatND hist_test1;
MatND hist_test2;
OpenCV2.2以前的版本里它们稍微有点区别:Mat特指2维矩阵,MatND是多维矩阵(>=3维),但2.2以后它们被统一成Mat,
Mat可以表示任意维矩阵,所以没必要在意MatND和Mat的区别,如果你用的是2.2以后的版本,统一使用Mat就行了。
*/
calcHist(&hsvbase, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false);
normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());
/*
const Mat * images, //Source arrays.
int nimages,//Number of source images.
const int * channels,//List of the dims channels used to compute the histogram.
InputArray mask,//Optional mask.
OutputArray hist,//Output histogram, which is a dense or sparse dims -dimensional array.
int dims,//Histogram dimensionality that must be positive and not greater than CV_MAX_DIMS
const int * histSize,// Array of histogram sizes in each dimension.
const float ** ranges,//Array of the dims arrays of the histogram bin boundaries in each dimension.
bool uniform = true,//Flag indicating whether the histogram is uniform or not
bool accumulate = false //Accumulation flag.
*/
calcHist(&hsvtest1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false);
normalize(hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvtest2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false);
normalize(hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat());
double basebase = compareHist(hist_base, hist_base, HISTCMP_INTERSECT);
/*
double cv::compareHist(InputArray H1, InputArray H2, int method)
double cv::compareHist(const SparseMat &H1, const SparseMat &H2, int method)
method:
HISTCMP_CORREL 相关性计算
HISTCMP_CHISQR 卡方计算
HISTCMP_INTERSECT 十字计算
HISTCMP_BHATTACHARYYA 巴氏距离计算
HISTCMP_HELLINGER
HISTCMP_CHISQR_ALT
HISTCMP_KL_DIV
*/
double basetest1 = compareHist(hist_base, hist_test1, HISTCMP_INTERSECT);
double basetest2 = compareHist(hist_base, hist_test2, HISTCMP_INTERSECT);
double tes1test2 = compareHist(hist_test1, hist_test2, HISTCMP_INTERSECT);
printf("test1 compare with test2 correlation value :%f", tes1test2);
Mat test12;
test2.copyTo(test12);
putText(base, convertToString(basebase), Point(50, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(test1, convertToString(basetest1), Point(50, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(test2, convertToString(basetest2), Point(50, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
putText(test12, convertToString(tes1test2), Point(50, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, LINE_AA);
namedWindow("base", WINDOW_AUTOSIZE);
namedWindow("test1", WINDOW_AUTOSIZE);
namedWindow("test2", WINDOW_AUTOSIZE);
imshow("base", base);
imshow("test1", test1);
imshow("test2", test2);
imshow("test12", test12);
waitKey(0);
return 0;
}
string convertToString(double d) {
ostringstream os;
if (os << d)
return os.str();
return "invalid conversion";
}
运行结果:
大概内容:直方图方向投影。
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace std;
using namespace cv;
Mat src; Mat hsv; Mat hue;
int bins = 12;
void Hist_And_Backprojection(int, void*);
int main(int argc, char** argv) {
src = imread("D:/vcprojects/images/t1.jpg");
if (src.empty()) {
printf("could not load image...\n");
return -1;
}
const char* window_image = "input image";
namedWindow(window_image, CV_WINDOW_NORMAL);
namedWindow("BackProj", CV_WINDOW_NORMAL);
namedWindow("Histogram", CV_WINDOW_NORMAL);
cvtColor(src, hsv, CV_BGR2HSV);
hue.create(hsv.size(), hsv.depth());
int nchannels[] = { 0, 0 };
mixChannels(&hsv, 1, &hue, 1, nchannels, 1);
//mixChannels (const Mat *src, size_t nsrcs, Mat *dst, size_t ndsts, const int *fromTo, size_t npairs)
//Copies specified channels from input arrays to the specified channels of output arrays.
createTrackbar("Histogram Bins:", window_image, &bins, 180, Hist_And_Backprojection);
Hist_And_Backprojection(0, 0);
imshow(window_image, src);
waitKey(0);
return 0;
}
void Hist_And_Backprojection(int, void*) {
float range[] = { 0, 180 };
const float *histRanges = { range };
Mat h_hist;
calcHist(&hue, 1, 0, Mat(), h_hist, 1, &bins, &histRanges, true, false);
normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());
Mat backPrjImage;
calcBackProject(&hue, 1, 0, h_hist, backPrjImage, &histRanges, 1, true);
/*
const Mat * images,
int nimages,
const int * channels,
InputArray hist,// Input histogram that can be dense or sparse.
OutputArray backProject,//Destination back projection array that is a single-channel array of the same size and depth as images[0] .
const float ** ranges,
double scale = 1,
bool uniform = true
*/
imshow("BackProj", backPrjImage);
int hist_h = 400;
int hist_w = 400;
Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
int bin_w = (hist_w / bins);
for (int i = 1; i < bins; i++) {
rectangle(histImage,
Point((i - 1)*bin_w, (hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255)))),
//Point(i*bin_w, (hist_h - cvRound(h_hist.at<float>(i) * (400 / 255)))),
Point(i*bin_w, hist_h),
Scalar(0, 0, 255), -1);
}
imshow("Histogram", histImage);
return;
}
运行结果:
解析及注意事项
- 直方图最常见的几个属性:dims 表示维度,对灰度图像来说只有一个通道值dims=1 。bins 表示在维度中子区域大小划分,bins=256,划分为256个级别。range 表示值得范围,灰度值范围为[0~255]之间
- 直方图比较步骤:首先把图像从RGB色彩空间转换到HSV色彩空间cvtColor。然后,计算图像的直方图,然后归一化到[0~1]之间calcHist和normalize;最后使用compareHist进行比较。
- Opencv提供的比较方法有四种:Correlation 相关性比较,Chi-Square 卡方比较,Intersection 十字交叉性,Bhattacharyya distance 巴氏距离。
- 反向投影是反映直方图模型在目标图像中的分布情况,简单点说就是用直方图模型去目标图像中寻找是否有相似的对象。通常用HSV色彩空间的HS两个通道直方图模型。
- 反向投影步骤:1.建立直方图模型。2.计算待测图像直方图并映射到模型中。3.从模型反向计算生成图像