opencv學習之基於背景提取等目標跟蹤算法#20190704


/* ***********************************************************************************************************************
任務目標:
基於背景提取的目標跟蹤算法實踐及代碼分析。
*********************************************************************************************************************** */

include "opencv2/opencv.hpp"

using namespace cv;
using namespace std;

int labelTargets(Mat &src, Mat &mask, int thresh = 100);
// 在自定義函數模塊聲明中對thresh的值進行了賦值

int main()
{
char fn = "D:\CommonSoftware\OpenCV\Workplace\Example2_track\vtest.avi";
// 指針
fn指向視頻地址,此處使用的是OpenCV庫中自帶的視頻
VideoCapture cap; // 定義VideoCapture類用以打開指定視頻
Mat source, image, foreGround, backGround, fgMask;
// Mat類:原始視頻、縮放后視頻、前景、背景、掩膜
Ptr pBgModel =
createBackgroundSubtractorMOG2().dynamicCast ();
/* ********************************************************************************************************************
1. 此處采用了模板類的概念,變量"pBgModel"是一個指向"BackgroundSubtractor"對象的指針。
這里的Mat.Ptr(i)[j]並未指定相應"i"和"j"的值,所以默認值都為0,即指向Mat第一行的第一個元素。
2. OpenCV中不止有一種背景提取方法,但是所有的背景提取方法都歸結為同一個基類"BackgroundSubtractor",
具體所采用的方法為"creatBackgroundSubtractorMOG2"。
3. "creatBackgroundSubtractorMOG2()"對"pBgModel"指針進行初始化,將其初始化成混合高斯模型。(注
意:在OpenCV 3.0以后的版本中不再含有"BackgroundSubtractorMOG"模型。)
4. 利用C++中的"dynamicCast"函數將pBgModel指針所指的內容動態的轉換成"BackgroundSubtractor"型,
並檢測是否成功。這里"BackgroundSubtractor"是一個類的引用,所以"()"中也需要放入一個類的引用,否則
拋出一個異常。
******************************************************************************************************************** */

cap.open(fn);

/* ********************************************************************************************************************
1. 使用cap類的open函數打開指針*fn所指的視頻
2. cap.open()中()內也可以放入數字,而放入數字代表的是接入幾號攝像機的視頻,該攝像機必須支持windows
的VFW。而且攝像機的編號是從0開始的。
******************************************************************************************************************** */
if (!cap.isOpened()) // cap類的isOpened函數查看是否成功打開
cout << "Cannot open the file: " << fn << endl;

for (; ; )  // 強制循環
{
	cap >> source;  // 將cap提取到的當前幀傳給source(原始圖像)
	if (source.empty())  // 如果原始圖像為空,即視頻已結束
	{
		break;  // 直接跳出循環
	}
	
	resize(source, image, Size(source.cols / 2, source.rows / 2), INTER_LINEAR);
	// 如果原始圖像不為空,調用resize函數改變原始圖像尺寸,方式為"線性差值法"

	if (foreGround.empty())
	{   // 通過前景是否為空,判斷當前幀是否為視頻開始
		foreGround.create(image.size(), image.type());
	}   // 如果是視頻開始,則為"前景圖像"創建矩陣空間,此處是按照原始圖像的縮放圖像的尺寸和類型進行初始化

	pBgModel->apply(image, fgMask);

/* *********************************************************************************************************************
在這里pBgModel這個指針在定義時已經設定好了處理模式,而"apply"函數會實時的將"pBgModel"中的當前幀
縮放圖像"image"采用pBgModel的方式進行處理,並提取fgMask,即前景的掩膜。
********************************************************************************************************************* */

	GaussianBlur(fgMask, fgMask, Size(5, 5), 0);  // 利用高斯濾波器對前景的掩膜做平滑降噪
	threshold(fgMask, fgMask, 30, 255, THRESH_BINARY);  // 通過閾值化去除掩膜中灰度小於30的像素

	foreGround = Scalar::all(0);  // 利用"Scalar"函數將所創建的前景矩陣空間中所有元素賦初值0
	image.copyTo(foreGround, fgMask);  // 標記運動目標

/* **********************************************************************************************************************
調用Mat類的copyTo()函數,僅將原始圖像的縮放圖像(image)中的掩膜部分(fgMask)復制到前景圖像中。
********************************************************************************************************************** */

	int nTargets = labelTargets(image, fgMask);
	cout << "There are " << nTargets << " targets." << endl;

	pBgModel->getBackgroundImage(backGround);

/* ***********************************************************************************************************************
這里使用了getBackgroundImage()函數,其會從pBgModel設定的模型中提取出背景。
*********************************************************************************************************************** */

	imshow("Sized Image", image);  // 顯示原始圖像的縮放圖像。
	imshow("Background", backGround);  // 顯示背景圖像。
	imshow("Foreground", foreGround);  // 顯示前景圖像。
	imshow("Foreground Mask", fgMask);  // 顯示前景圖像的掩膜。

	char key = waitKey(100);  // 每一幀等待100毫秒
	if (key == 27)  // 如果想中途終止程序,按"Esc"退出
	{
		break;
	}
}

waitKey(0);  // 程序執行結束后防止閃退

}

int labelTargets(Mat &src, Mat &mask, int thresh) // 自定義的目標標記函數
{
Mat seg = mask.clone(); // 將掩膜復制到seg當中
vector<vector > cnts; // 定義了二維點類型變量cuts
findContours(seg, cnts, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// 調用findContours()函數對掩膜復制圖像seg中的Blob進行檢測,檢測方式是忽略內部邊緣僅識別外部邊緣

// 以下進行篩選
float area;  // 定義浮點型area變量用以存放單一Blob外圍邊緣所圍面積
Rect rect;
int count = 0;  // 定義整型變量用以計數
string strCount;  // 定義字符串型變量
for (int i = cnts.size() - 1; i >= 0; i--)
{  // 調用vector的size()函數以檢測cnts第一維的個數,即Blob個體數,並對其進行循環處理
	vector<Point> c = cnts[i];  // 定義一維點類型變量c用以存放Blob個體,即一維cnts集合
	area = contourArea(c);  // 調用contourArea()函數計算該單一Blob外圍邊緣所圍面積
	if (area < thresh) // 濾除面積小於10的分割結果:可能是噪聲
	{
		continue;
	}

	count++; // 統計米粒數量
	cout << "blob " << i << " : " << area << endl;
	rect = boundingRect(c);  // 創建包圍矩形數據
	// 在原始圖像上畫出包圍矩形,並給每個矩形標號
	rectangle(src, rect, Scalar(0, 0, 0xff), 1);
	// 調用rectangle()函數在src圖像中,把rect包圍矩形用紅色,1個像素粗細的線框包圍起來

	stringstream ss;  // 定義字符流變量"ss",作用是將指定字符串生成輸入或輸出流
	ss << count;  // 將計數數據傳遞給字符流
	ss >> strCount;  // 將字符流中的數據傳送給字符串變量
	putText(src, strCount, Point(rect.x, rect.y), CV_FONT_HERSHEY_PLAIN, 0.5, Scalar(0, 0xff, 0));
	// 調用putText函數,在src圖像上將strCount字符串輸出在矩形框左上角,並定義了字體和字號和顏色
}

return count;

}

OpenCV中Mat類的指針ptr的使用:

  1. 定義Mat型的類”image”,1)image.ptr (0),指向image第1行第1個元素的指針;2)image.ptr (1),指向image的第2行第1個元素的指針;3)image.ptr (0)[1],指向image第1行第2個元素的指針。
  • 注意:在使用image.ptr()[]指針的時候,要注意防止索引值溢出(cv::Exception)。
  1. dynamic.cast的用法:
  • 將一個基類對象指針(或引用)cast到繼承類指針,dynamic_cast會根據基類指針是否真正指向繼承類指針來做相應處理。

  • 例如:dynamic_cast (expression);

該運算符把expression轉換成type-id類型的對象。

Type-id 必須是類的指針、類的引用或者void*。

如果 type-id 是類指針類型,那么expression也必須是一個指針,(對指針進行dynamic_cast,失敗返回null,成功返回正常cast后的對象指針)。如果 type-id 是一個引用,那么 expression 也必須是一個引用(對引用進行dynamic_cast,失敗拋出一個異常,成功返回正常cast后的對象引用)。

dynamic_cast主要用於類層次間的上行轉換和下行轉換,還可以用於類之間的交叉轉換。
轉自 neilkuang


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM