【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.從模型反向計算生成圖像
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章