用SURF演算法和其描述子實現視訊的特徵影象跟蹤和標定
阿新 • • 發佈:2019-01-25
其實最初我是想採用SIFT
的運算元和特徵點識別來進行特徵影象的跟蹤標定的,但是經過測試後發現SIFT
的檢測時間實在是太長了,根本不適合作為視訊的特徵識別的演算法,所以雖然其識別準確率是最高的,但是還是隻能進行捨棄
而且SURF
雖然準確率沒有SIFT
高,但其也保留了基本的尺度不變性和旋轉不變型,用作特徵影象的標定其實也算不錯,不過這樣的方法也有很大的問題,就是每一幀都必須對全域性影象進行一個特徵點的描述,這樣會造成每一幀的計算量太大而導致掉幀的情況。
我當前所做成的Demo也有這個問題,就是幀數過低,看起來並不流暢,但是隻是對SURF
的視訊識別效率做一個測試,問題也不算太大
當然,後面我會對這個特徵影象的跟蹤Demo進行進一步的優化,
初步方案是採用光流金字塔calcOpticalFlowPyrLK
SURF
混用的方式達到比較好的同步率,然後採用更加動態化的方式去進行對應特徵點的篩選以達到更好的準確性。
執行環境
- Visual Studio 2017 ( X86 Release )
- opencv 2.3.1
Demo實現的大致步驟
- 獲取特徵影象的描述子存入固定容器
- 取一幀影象
- 將幀影象的描述子算出並和特徵影象的描述子進行對比
- 對比得到
DMatch
物件容器,並進行篩選得到較好的匹配點和其對應的位置points
(如何篩選DMatch
請看SIFT演算法的特徵點篩選和DMatch、Keypoint描述) - 繪製出匹配點
- 通過獲取多個點的透視變化
findHomography
points
相對於原影象對應位置的透視矩陣 - 通過前面的透視矩陣將幀影象中的特徵影象圈出
程式碼詳情
由於註釋比較詳細,就不多說了,直接貼程式碼吧
int main()
{
float MaxDistance = 0; //最大尤拉角
float MinDistance = 999; //最小尤拉角
//識別影象
Mat inputObj = imread("image/img2.jpg", 1);
//場景影象
Mat inputScene;
//兩者的灰度影象
Mat imgGrayObj, imgGrayScene;
//轉換灰度圖
cvtColor(inputObj, imgGrayObj, CV_BGR2GRAY);
//SURF特徵檢測
SurfFeatureDetector detector;
//特徵點容器
vector<KeyPoint> keypointsObj, keypointsScene;
//找出特徵影象的特徵點
detector.detect(imgGrayObj, keypointsObj);
//surf描述子
SurfDescriptorExtractor extractor;
//描述子矩陣
Mat descriptorObj, descriptorScene;
//匹配器
BruteForceMatcher<L2<float>> matcher;
//匹配得到的DMatch容器
vector<DMatch> matches;
extractor.compute(imgGrayObj, keypointsObj, descriptorObj);
VideoCapture camera(0);//初始化攝像頭,0代表預設攝像頭
vector<DMatch> matchesVoted;//選出的優質匹配
vector<Point2f> points1, points2;//對應點的位置
vector<KeyPoint> imagePoints1, imagePoints2;//選出的對應優質特徵點
while (true)
{
if (!camera.isOpened())
{
cerr << "open camera error" << endl;
break;
}
camera >> inputScene;//獲取當前幀
//灰度化影象
cvtColor(inputScene, imgGrayScene, CV_BGR2GRAY);
//找出特徵點
detector.detect(imgGrayScene, keypointsScene);
extractor.compute(imgGrayScene, keypointsScene, descriptorScene);
matcher.match(descriptorObj, descriptorScene, matches);
//得到匹配的最大最小尤拉距離
for (int i = 0; i < matches.size(); i++) {
if (matches[i].distance > MaxDistance) {
MaxDistance = matches[i].distance;
}
if (matches[i].distance < MinDistance) {
MinDistance = matches[i].distance;
}
}
int count = 0;
//篩選特徵點
for (int i = 0; i < matches.size(); i++)
{
if (matches[i].distance <= 2.8 * MinDistance) {
DMatch dmatch;
dmatch.queryIdx = count;
dmatch.trainIdx = count;
dmatch.distance = matches[i].distance;
matchesVoted.push_back(dmatch);
imagePoints1.push_back(keypointsObj[matches[i].queryIdx]);
imagePoints2.push_back(keypointsScene[matches[i].trainIdx]);
points1.push_back(keypointsObj[matches[i].queryIdx].pt);
points2.push_back(keypointsScene[matches[i].trainIdx].pt);
count++;
}
}
Mat img_matches;
drawMatches(inputObj, imagePoints1, inputScene, imagePoints2, matchesVoted, img_matches, DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
if (points1.size() >= 4) {//是否具備進行透視矩陣變換的基礎
//透視變換
Mat h = findHomography(points1, points2, CV_RANSAC);
std::vector<Point2f> obj_corners(4);
//特徵影象中的四個頂點位置
obj_corners[0] = cvPoint(0, 0);
obj_corners[1] = cvPoint(imgGrayObj.cols, 0);
obj_corners[2] = cvPoint(imgGrayObj.cols, imgGrayObj.rows);
obj_corners[3] = cvPoint(0, imgGrayObj.rows);
std::vector<Point2f> scene_corners(4);
//透視變換將頂點位置轉換成幀影象中的位置
perspectiveTransform(obj_corners, scene_corners, h);
//畫矩陣
line(img_matches, scene_corners[0] + Point2f(imgGrayObj.cols, 0), scene_corners[1] + Point2f(imgGrayObj.cols, 0), Scalar(0, 255, 0), 4);
line(img_matches, scene_corners[1] + Point2f(imgGrayObj.cols, 0), scene_corners[2] + Point2f(imgGrayObj.cols, 0), Scalar(0, 255, 0), 4);
line(img_matches, scene_corners[2] + Point2f(imgGrayObj.cols, 0), scene_corners[3] + Point2f(imgGrayObj.cols, 0), Scalar(0, 255, 0), 4);
line(img_matches, scene_corners[3] + Point2f(imgGrayObj.cols, 0), scene_corners[0] + Point2f(imgGrayObj.cols, 0), Scalar(0, 255, 0), 4);
}
imshow("SURF_Match_Image", img_matches);
//清理場景特徵點容器
matches.clear();
keypointsScene.clear();
matchesVoted.clear();
points1.clear();
points2.clear();
imagePoints1.clear();
imagePoints2.clear();
waitKey(33);
}
return 0;
}
效果展示
缺點
- 效率太低,幀率太低,應該只有10幀左右
- 準確度不高,壞點的去除不完善