【opencv學習筆記】027之直方圖反向投影 - calcBackProject函式詳解

shuiyixin發表於2018-05-16

一、前言

        在學習直方圖反向投影之前,如果你對直方圖的概念,直方圖均衡化,直方圖計算等直方圖相關的概念比較模糊,建議先了解一下

        1.直方圖概念及直方圖均衡化 https://blog.csdn.net/shuiyixin/article/details/80001756

        2.直方圖計算 https://blog.csdn.net/shuiyixin/article/details/80032167

        3.直方圖比較 https://blog.csdn.net/shuiyixin/article/details/80257822

二、反向投影概念

        反向投影是一種記錄給定影象中的畫素點如何適應直方圖模型畫素分佈的方式,簡單來講,反向投影就是首先計算某一特徵的直方圖模型,然後使用模型去尋找影象中存在的特徵。反向投影在某一位置的值就是原圖對應位置畫素值在原影象中的總數目。

三、反向投影理解

        舉個栗子,給大家介紹反向投影:

        想必大家都玩過一個遊戲,(沒玩過的可以體驗一下),一個人矇住眼睛,其餘的人在指定區域內活動,矇眼睛的人依靠聲音等方式去尋找其餘的人。設矇眼睛的人為A,其餘人中有個人是B。

        假如這個人只能通過聲音去尋找,那這個人其實是通過“雙耳效應”,簡化一下,就是說當B發出聲音的時候,A聽到B的聲音,根據自己感知到聲音傳到兩個耳朵的不同,順著聲音的方向,做兩條反向延長線,兩條線交於一個點,這個點的位置,就是B所在的位置。

        同樣原理的還有測量地震的裝置,很多時候,測震源有兩個裝置,裝置對地震源做反向投影,就會找到地震的中心。(如下圖)

四、反向投影原理

 

        (1)例如灰度影象如下 :
                Image=

                      0    1    2    3
                      4    5    6    7
                      8    9   10   11
                      8    9   14   15
        (2)該灰度圖的直方圖為(bin指定的區間為[0,3),[4,7),[8,11),[12,16))
                Histogram=        4    4    6    2

 

        (3)反向投影圖
                Back_Projection=
                      4    4    4    4
                      4    4    4    4
                      6    6    6    6
                      6    6    2    2

 

        例如位置(0,0)上的畫素值為0,對應的bin為[0,3),所以反向直方圖在該位置上的值這個bin的值4。

 

        我們看到,實際上是原影象的256個灰度值被置為很少的幾個值了,具體有幾個值,要看把0~255劃分為多少個區間!反向投影矩陣中某點的值就是它對應的原影象中的點所在區間的灰度直方圖值。所以我們可以看出,一個區間點越多,在反向投影矩陣中就越亮。

        那麼怎麼理解反向投影矩陣中的“反向”二字呢?從這個過程可以看出,我們是先求出原影象的直方圖,再由直方圖得到反向投影矩陣,由直方圖到反向投影矩陣實際上就是一個反向的過程,所以叫反向。

五、反向投影作用——目標檢測

        一幅影象的反向投影利用了其原始影象(或目標區域)的直方圖,將該直方圖作為一張查詢表來找對應畫素點的畫素值,即將目標影象畫素點的值設定為原始影象(或目標區域)直方圖上對應的bin值。該bin值代表了(目標區域)上該畫素值出現的概率。從而得到一幅影象的概率值。從而我們可以通過這幅概率圖可以得知在這幅影象中,目標出現可能出現的位置。

六、API

1.calcBackProject函式

//1.函式原型
void cv::calcBackProject(
	const Mat *        images,
	int                nimages,
	const int *        channels,
	InputArray         hist,
	OutputArray        backProject,
	const float **     ranges,
	double             scale = 1,
	bool               uniform = true
	)

//2.引數解釋
//const Mat* images:輸入影象,影象深度必須位CV_8U, CV_16U或CV_32F中的一種,尺寸相同,每一幅影象都可以有任意的通道數
//int nimages : 輸入影象的數量
//const int* channels : 用於計算反向投影的通道列表,通道數必須與直方圖維度相匹配,第一個陣列的通道是從0到image[0].channels() - 1, 第二個陣列通道從影象image[0].channels()到image[0].channels() + image[1].channels() - 1計數
//InputArray hist : 輸入的直方圖,直方圖的bin可以是密集(dense)或稀疏(sparse)
//OutputArray backProject : 目標反向投影輸出影象,是一個單通道影象,與原影象有相同的尺寸和深度
//const float ranges** : 直方圖中每個維度bin的取值範圍
//double scale = 1 : 可選輸出反向投影的比例因子
//bool uniform = true : 直方圖是否均勻分佈(uniform)的識別符號,有預設值true

//另外兩種定義

void cv::calcBackProject(
	const Mat *          images,
	int	             nimages,
	const int *	     channels,
	const SparseMat &    hist,
	OutputArray          backProject,
	const float **       ranges,
	double               scale = 1,
	bool                 uniform = true
	)

void cv::calcBackProject(
	InputArrayOfArrays             images,
	const std::vector< int > &     channels,
	InputArray                     hist,
	OutputArray                    dst,
	const std::vector< float > &   ranges,
	double                         scale
	)

2.mixChannels函式

//1.函式功能:從輸入影象中拷貝某通道到輸出影象中特定的通道。
//2.函式原型及引數解釋:
void mixChannels(
	const Mat*src,      //一系列輸入影象的陣列, 被拷貝的通道的來源一系列輸入影象的陣列, 被拷貝的通道的來源
	size_t nsrcs,       //輸入影象的個數
	Mat* dst,           //一系列目的影象的陣列, 儲存拷貝的通道,所有的陣列必須事先分配空間(如用create),大小和深度須與輸入陣列等同。
	size_t ndsts,       //目的陣列中影象的數目
	const int* fromTo,  //通道索引對的陣列,指示如何將輸入影象的某一通道拷貝到目的影象的某一通道。偶數下標的用來標識輸入矩陣,奇數下標的用來標識輸出矩陣。如果偶數下標為負數,那麼相應的輸出矩陣為零矩陣。
	size_t npairs       //fromTo中的序號對數(兩個算1對)。
);

 

七、原始碼

#define INPUT_TITLE "Input Image"
#define OUTPUT_TITLE "Back Projection"
#define HIST_TITLE "Histogram"

#include<iostream>
#include<opencv2\opencv.hpp>

using namespace std;
using namespace cv;



/*———————————本程式碼所需變數定義及初始化——————————— */
Mat src, hsv_src, hue, backProjectionImg;
int bins = 12;
int nchannels[] = { 0,0 };

/*—————————————本程式碼所需函式宣告————————————— */
void Hist_And_BackProjection(int, void*);


int main() {
	/*—————————————全域性變數的賦值————————————— */
	//1.影象載入
	src = imread("D:/hand.jpg");
	if (!src.data)
	{
		cout << "ERROR : could not load image.\n";
		return -1;
	}
	//2.將影象轉化為HSV影象
	cvtColor(src, hsv_src, CV_BGR2HSV);

	//3.建立一個影象
	hue.create(hsv_src.size(), hsv_src.depth());

	//視窗命名
	namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);
	namedWindow(OUTPUT_TITLE, CV_WINDOW_AUTOSIZE);
	namedWindow(HIST_TITLE, CV_WINDOW_AUTOSIZE);

	//從輸入影象中拷貝某通道到輸出影象中特定的通道
	mixChannels(&hsv_src, 1, &hue, 1, nchannels, 1);

	//動態調整直方圖的 bins ,並做反向投影
	createTrackbar("Histogram Bins", INPUT_TITLE, &bins, 180, Hist_And_BackProjection);
	Hist_And_BackProjection(0, 0);

	imshow(INPUT_TITLE, src);

	waitKey(0);
	return 0;
}


/*—————————————本程式碼所需函式實現————————————— */
void Hist_And_BackProjection(int, void*) {

	//區域性變數
	float range[] = { 0,180 };
	const float *histRanges = { range };
	int hist_h = 400;
	int hist_w = 400;
	int bin_w = hist_w / bins;
	Mat h_hist;
	Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));

	//直方圖計算及歸一化處理
	calcHist(&hue, 1, 0, Mat(), h_hist, 1, &bins, &histRanges, true, false);
	normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());

	//直方圖反向投影
	calcBackProject(&hue, 1, 0, h_hist, backProjectionImg, &histRanges, 1, true);

	//畫直方圖分部圖
	for (int i = 0; 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)))),
			Scalar(0, 0, 255),
			-1);*/

		rectangle(histImage,
			Point((i - 1)*bin_w, (hist_h - cvRound(h_hist.at<float>(i - 1)*(400 / 255)))),
			Point(i*bin_w, hist_h),
			Scalar(0, 0, 255),
			-1);
	}

	imshow(OUTPUT_TITLE, backProjectionImg);
	imshow(HIST_TITLE, histImage);

}

八、結果展示

相關文章