【OpenCV:从零到一】17:直方图均衡化、计算、比较、反向投影

前言
这是我《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.从模型反向计算生成图像
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章