本文結合上文《OpenCV2 計算機視覺編碼手冊》視訊處理一的基礎上,添加視訊跟蹤類,來對視訊中運動對象進行跟蹤。
1. 添加特征跟蹤類
#ifndef FTRACKER
#define FTRACKER
#include "head.h"
#include "videoprocessor.h"
#include <opencv2/video/tracking.hpp>
#include <opencv2/features2d/features2d.hpp>
class FeatureTracker : public FrameProcessor
{
private:
cv::Mat gray; // 目前灰階圖像
cv::Mat gray_prev; // 前一個灰階圖像
std::vector<cv::Point2f> points[2]; // 兩幅圖像跟蹤特征 0->1
std::vector<cv::Point2f> initial; // 跟蹤點的初始化
std::vector<cv::Point2f> features; // 檢測到的特征
int max_count; // 需要跟蹤的最大特征數目
double qlevel; // 特征檢測中的品質等級
double minDist; // 兩特征點之間的最小距離
std::vector<uchar> status; // 跟蹤的特征狀态
std::vector<float> err; // 跟蹤錯誤
public:
// 構造函數
FeatureTracker() : max_count(500), qlevel(0.01), minDist(10.) {}
// 處理方法
void process(cv:: Mat &frame, cv:: Mat &output)
{
cv::cvtColor(frame, gray, CV_BGR2GRAY); // 轉換為灰階圖像
frame.copyTo(output);
// 1. 如果需要添加新的特征點
if(addNewPoints())
{
detectFeaturePoints(); // 檢測特征點
points[0].insert(points[0].end(),features.begin(),features.end());// 添加檢測的特征到目前跟蹤的特征
initial.insert(initial.end(),features.begin(),features.end());
}
// 對應視訊序列中的第一幅圖像
if(gray_prev.empty())
gray.copyTo(gray_prev);
// 2.跟蹤特征
cv::calcOpticalFlowPyrLK(gray_prev, gray, // 兩幅連續圖像
points[0], // 圖1中的輸入點坐标
points[1], // 圖2中的輸出點坐标
status, // 跟蹤成功
err); // 跟蹤失敗
// 2. 周遊所有跟蹤點進行篩選
int k=0;
for( int i= 0; i < points[1].size(); i++ )
{
// 是否需要保留該跟蹤點?
if (acceptTrackedPoint(i))
{
// 保留該跟蹤點到vector
initial[k]= initial[i];
points[1][k++] = points[1][i];
}
}
// 去除不成功點
points[1].resize(k);
initial.resize(k);
// 3. 處理接受的跟蹤點
handleTrackedPoints(frame, output);
// 4. 目前的點和圖像變為它之前的點和圖像
std::swap(points[1], points[0]);
cv::swap(gray_prev, gray);
}
// 特征點檢測
void detectFeaturePoints()
{ // 檢測特征
cv::goodFeaturesToTrack(gray, // 圖像
features, // 檢測到的特征
max_count, // 特征的最大數目
qlevel, // 品質等級
minDist); // 兩個特征之間的最小距離
}
// 決定是否添加新點
bool addNewPoints()
{
// 如果點的數量太少
return points[0].size()<=10;
}
// 決定哪些點應該跟蹤
bool acceptTrackedPoint(int i)
{
return status[i] &&
// 如果它移動了
(abs(points[0][i].x-points[1][i].x)+
(abs(points[0][i].y-points[1][i].y))>2);
}
// 處理目前跟蹤點
void handleTrackedPoints(cv:: Mat &frame, cv:: Mat &output)
{
// 周遊所有跟蹤點
for(int i= 0; i < points[1].size(); i++ )
{
// 繪制直線和圓
cv::line(output,
initial[i], // 初始位置
points[1][i], // 新位置
cv::Scalar(255,255,255)// 白色
);
cv::circle(output, // 輸出圖像
points[1][i], // 圓心
3, // 半徑
cv::Scalar(255,255,255),// 白色
-1 // 負數表示填充圓圈, 整數表示線條厚度
);
}
}
};
#endif
2. main函數
#include "featuretracker.h"
int main()
{
VideoProcessor processor; // 建立一個視訊處理執行個體
FeatureTracker tracker; // 建立一個特征跟蹤執行個體
processor.setInput("../bike.avi"); // 打開視訊檔案
processor.setFrameProcessor(&tracker); // 設定幀處理器為一個特征跟蹤執行個體tracker
processor.displayOutput("Tracked Features"); // 聲明跟蹤特征顯示視窗
processor.setDelay(1000./processor.getFrameRate()); // 設定視訊播放幀率為原始幀率
processor.run(); // 開始處理
cv::waitKey(); // 等待按鍵響應
return 0;
}