OpenCV使用稀疏光流實現(xiàn)視頻對象跟蹤的方法詳解
1、概述
案例:使用稀疏光流實現(xiàn)對象跟蹤
稀疏光流API介紹:
calcOpticalFlowPyrLK( InputArray prevImg, InputArray nextImg,
InputArray prevPts, InputOutputArray nextPts,
OutputArray status, OutputArray err,
Size winSize = Size(21,21), int maxLevel = 3,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01),
int flags = 0, double minEigThreshold = 1e-4 );
- prevImg:視頻前一幀圖像/金字塔,單通道CV_8UC1
- nextImg:視頻后一幀圖像/金字塔,單通道CV_8UC1
- preVPts:前一幀圖像的特征向量(輸入)需要找到流的2D點的矢量(vector of 2D points for which the flow needs to be found;);點坐標(biāo)必須是單精度浮點數(shù)
- nextPts:后一幀圖像的特征向量(輸出),輸出二維點的矢量(具有單精度浮點坐標(biāo)),包含第二圖像中輸入特征的計算新位置;當(dāng)傳遞OPTFLOW_USE_INITIAL_FLOW標(biāo)志時,向量必須與輸入中的大小相同。
- status:輸出狀態(tài)向量(無符號字符);如果找到相應(yīng)特征的流,則向量的每個元素設(shè)置為1,否則設(shè)置為0
- err:輸出錯誤的矢量; 向量的每個元素都設(shè)置為相應(yīng)特征的錯誤,錯誤度量的類型可以在flags參數(shù)中設(shè)置; 如果未找到流,則未定義錯誤(使用status參數(shù)查找此類情況)
- winSize:每個金字塔等級的搜索窗口的winSize大小
- maxLevel:基于0的最大金字塔等級數(shù);如果設(shè)置為0,則不使用金字塔(單級),如果設(shè)置為1,則使用兩個級別,依此類推;如果將金字塔傳遞給輸入,那么算法將使用與金字塔一樣多的級別,但不超過maxLevel
- criteria:停止條件,指定迭代搜索算法的終止條件(在指定的最大迭代次數(shù)criteria.maxCount之后或當(dāng)搜索窗口移動小于criteria.epsilon時)。
- flags:操作標(biāo)志,OPTFLOW_USE_INITIAL_FLOW使用初始估計,存儲在nextPts中;如果未設(shè)置標(biāo)志,則將prevPts復(fù)制到nextPts并將其視為初始估計。
- OPTFLOW_LK_GET_MIN_EIGENVALS使用最小特征值作為誤差測量(參見minEigThreshold描述);如果沒有設(shè)置標(biāo)志,則將原稿周圍的色塊和移動點之間的L1距離除以窗口中的像素數(shù),用作誤差測量
- minEigThreshold:算法計算光流方程的2x2正常矩陣的最小特征值,除以窗口中的像素數(shù);如果此值小于minEigThreshold,則過濾掉相應(yīng)的功能并且不處理其流程,因此它允許刪除壞點并獲得性能提升
算法實現(xiàn)步驟:
1.實例化VideoCapture
2.循環(huán)讀取視頻數(shù)據(jù)
3.視頻幀灰度轉(zhuǎn)換
4.執(zhí)行角點檢測
5.保存角點檢測的特征數(shù)據(jù)
6.初始化時如果檢測到前一幀為空,把當(dāng)前幀的灰度圖像給前一幀
7.執(zhí)行光流跟蹤,并輸出跟蹤后的特征向量
8.遍歷光流跟蹤的輸出特征向量,并得到距離和狀態(tài)都符合預(yù)期的特征向量。讓后將其重新填充到fpts[1]中備用
9.重置集合大小
10.繪制光流線
11.交換特征向量的輸入和輸出
12.將用于跟蹤的角點繪制出來
13.展示最終的跟蹤效果
14.循環(huán)3~13步驟
15.結(jié)束
2、代碼示例
KLT_Object_Tracking::KLT_Object_Tracking(QWidget *parent)
: MyGraphicsView{parent}
{
isShowLine = false;
this->setWindowTitle("KLT稀疏光流實現(xiàn)對象跟蹤");
QPushButton *btn = new QPushButton(this);
btn->setText("選擇視頻");
connect(btn,&QPushButton::clicked,[=](){
//選擇視頻
path = QFileDialog::getOpenFileName(this,"請選擇視頻","/Users/yangwei/Downloads/",tr("Image Files(*.mp4 *.avi)"));
qDebug()<<"視頻路徑:"<<path;
startKltTracking(path.toStdString().c_str());
});
//
QButtonGroup * group = new QButtonGroup(this);
QRadioButton * radioNo = new QRadioButton(this);
radioNo->setText("否");
radioNo->setChecked(true);
QRadioButton *radioYes = new QRadioButton(this);
radioYes->setText("是");
group->addButton(radioNo,0);
group->addButton(radioYes,1);
radioNo->move(0,btn->y()+btn->height()+20);
radioYes->move(radioNo->x()+radioNo->width()+20,btn->y()+btn->height()+20);
connect(radioNo,&QRadioButton::clicked,[=](){
isShowLine = false;//顯示光流線
});
connect(radioYes,&QRadioButton::clicked,[=](){
isShowLine = true;//不顯示光流線
});
}
void KLT_Object_Tracking::startKltTracking(const char* filePath){
//【1】實例化VideoCapture并打開視頻
VideoCapture capture;//實例化視頻捕獲器
capture.open(filePath);//打開視頻文件(或攝像頭)
if(!capture.isOpened()){//檢測文件是否打開,如果沒打開直接退出
qDebug()<<"無法打開視頻";
return;
}
Mat frame,gray;
vector<Point2f> features;//檢測出來的角點集合
vector<Point2f> inPoints;//這個主要是為了畫線用的
vector<Point2f> fpts[2];//[0],存入的是是二維特征向量,[1]輸出的二維特征向量
Mat pre_frame,pre_gray;
vector<uchar> status;//光流輸出狀態(tài)
vector<float> err;//光流輸出錯誤
//【2】循環(huán)讀取視頻
while(capture.read(frame)){//循環(huán)讀取視頻中每一幀的圖像
//【3】將視頻幀圖像轉(zhuǎn)為灰度圖
cvtColor(frame,gray,COLOR_BGR2GRAY);//ps:角點檢測輸入要求單通道
//【4】如果特征向量(角點)小于40個我們就重新執(zhí)行角點檢測
if(fpts[0].size()<40){//如果小于40個角點就重新開始執(zhí)行角點檢測
//執(zhí)行角點檢測
goodFeaturesToTrack(gray,features,5000,0.01,10,Mat(),3,false,0.04);
//【5】將檢測到的角點放入fpts[0]中作為,光流跟蹤的輸入特征向量
//將檢測到的角點插入vector
fpts[0].insert(fpts[0].begin(),features.begin(),features.end());
inPoints.insert(inPoints.end(),features.begin(),features.end());
qDebug()<<"角點檢測執(zhí)行完成,角點個數(shù)為:"<<features.size();
}else{
qDebug()<<"正在跟蹤...";
}
//【6】初始化的時候如果檢測到前一幀為空,這個把當(dāng)前幀的灰度圖像給前一幀
if(pre_gray.empty()){//如果前一幀為空就給前一幀賦值一次
gray.copyTo(pre_gray);
}
//執(zhí)行光流跟蹤
qDebug()<<"開始執(zhí)行光流跟蹤";
//【7】執(zhí)行光流跟蹤,并將輸出的特征向量放入fpts[1]中
calcOpticalFlowPyrLK(pre_gray,gray,fpts[0],fpts[1],status,err);
qDebug()<<"光流跟蹤執(zhí)行結(jié)束";
//【8】遍歷光流跟蹤的輸出特征向量,并得到距離和狀態(tài)都符合預(yù)期的特征向量。讓后將其重新填充到fpts[1]中備用
int k =0;
for(size_t i=0;i<fpts[1].size();i++){//循環(huán)遍歷二維輸出向量
double dist = abs(fpts[0][i].x - fpts[1][i].x) + abs(fpts[0][i].y - fpts[1][i].y);//特征向量移動距離
if(dist>2&&status[i]){//如果距離大于2,status=true(正常)
inPoints[k] = inPoints[i];
fpts[1][k++] = fpts[1][i];
}
}
//【9】重置集合大小(由于有錯誤/不符合條件的輸出特征向量),只拿狀態(tài)正確的
//重新設(shè)置集合大小
inPoints.resize(k);
fpts[1].resize(k);
//【10】繪制光流線,這一步要不要都行
//繪制光流線
if(isShowLine){
for(size_t i = 0;i<fpts[1].size();i++){
line(frame,inPoints[i],fpts[1][i],Scalar(0,255,0),1,8,0);
circle(frame, fpts[1][i], 2, Scalar(0, 0, 255), 2, 8, 0);
}
}
qDebug()<<"特征向量的輸入輸出交換數(shù)據(jù)";
//【11】交換特征向量的輸入和輸出,(循環(huán)往復(fù)/進入下一個循環(huán)),此時特征向量的值會遞減
std::swap(fpts[1],fpts[0]);//交換特征向量的輸入和輸出,此處焦點的總數(shù)量會遞減
//【12】將用于跟蹤的角點繪制出來
//將角點繪制出來
for(size_t i = 0;i<fpts[0].size();i++){
circle(frame,fpts[0][i],2,Scalar(0,0,255),2,8,0);
}
//【13】重置前一幀圖像(每一個循環(huán)都要刷新)
gray.copyTo(pre_gray);
frame.copyTo(pre_frame);
//【14】展示最終的效果
imshow("frame",frame);
int keyValue = waitKey(100);
if(keyValue==27){//如果用戶按ese鍵退出播放
break;
}
}
}3、圖像演示


到此這篇關(guān)于OpenCV使用稀疏光流實現(xiàn)視頻對象跟蹤的方法詳解的文章就介紹到這了,更多相關(guān)OpenCV視頻對象跟蹤內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實戰(zhàn)之二進制數(shù)據(jù)處理與封裝
在電腦上一切數(shù)據(jù)都是通過二進制(0或1)進行存儲的,通過多位二進制數(shù)據(jù)可以進而表示整形、浮點型、字符、字符串等各種基礎(chǔ)類型數(shù)據(jù)或者一些更復(fù)雜的數(shù)據(jù)格式。本文將為大家詳細講講二進制數(shù)據(jù)處理與封裝,需要的可以參考一下2022-08-08
C++實現(xiàn)神經(jīng)網(wǎng)絡(luò)框架SimpleNN的詳細過程
本來自己想到用C++實現(xiàn)神經(jīng)網(wǎng)絡(luò)主要是想強化一下編碼能力并入門深度學(xué)習(xí),對C++實現(xiàn)神經(jīng)網(wǎng)絡(luò)框架SimpleNN的詳細過程感興趣的朋友一起看看吧2021-08-08
對比C語言中memccpy()函數(shù)和memcpy()函數(shù)的用法
這篇文章主要介紹了對比C語言中memccpy()函數(shù)和memcpy()函數(shù)的用法,二者都是用于復(fù)制內(nèi)存內(nèi)容,注意區(qū)別,需要的朋友可以參考下2015-08-08
c++如何控制對象的創(chuàng)建方式(禁止創(chuàng)建棧對象or堆對象)和創(chuàng)建的數(shù)量
這篇文章主要介紹了c++如何控制對象的創(chuàng)建方式和創(chuàng)建的數(shù)量,幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-08-08
Visual C++中Tab View的多種實現(xiàn)方法
這篇文章主要介紹了Visual C++中Tab View的多種實現(xiàn)方法,包括了CTabCtrl控件、CSheetCtrl標(biāo)簽選擇窗口以及靜態(tài)分割窗口等實現(xiàn)Tab View的方法,需要的朋友可以參考下2014-10-10
基于C語言編寫簡易的英文統(tǒng)計和加密系統(tǒng)
這篇文章主要介紹如何基于C語言編寫一個簡易的英文統(tǒng)計和加密系統(tǒng),實際上就是對字符數(shù)組的基本操作的各種使用,感興趣的可以了解一下2023-05-05

