0
点赞
收藏
分享

微信扫一扫

OpenCV官方:AKAZE和ORB目标检测对比


介绍

在本教程中,我们将使用AKAZE和ORB本地特性来查找视频帧之间的匹配和跟踪对象运动。
算法如下:

  1. 检测和描述第一帧的关键点,手动设置对象边界
  2. 对于每一帧:
  1. 检测并描述关键点
  2. 使用brute匹配器匹配它们
  3. 用RANSAC估计单应变换
  4. 过滤所有匹配的嵌套
  5. 对边界框应用单应变换来找到对象
  6. 绘制边界框和嵌线,计算嵌线比率作为评价指标

OpenCV官方:AKAZE和ORB目标检测对比_ide

 数据

 为了进行跟踪,我们需要视频和对象的位置在第一帧。
你可以从​​​这里​​​下载我们的示例视频和数据。
要运行代码,你必须指定输入和输出视频路径和对象边界框。

./planar_tracking blais.mp4 result.avi blais_bb.xml.gz

源程序

#include <opencv2/features2d.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/opencv.hpp>
#include <vector>
#include <iostream>
#include <iomanip>

#include "stats.h" // Stats structure definition
#include "utils.h" // Drawing and printing functions

using namespace std;
using namespace cv;

const double akaze_thresh = 3e-4; // AKAZE detection threshold set to locate about 1000 keypoints
const double ransac_thresh = 2.5f; // RANSAC inlier threshold
const double nn_match_ratio = 0.8f; // Nearest-neighbour matching ratio
const int bb_min_inliers = 100; // Minimal number of inliers to draw bounding box
const int stats_update_period = 10; // On-screen statistics are updated every 10 frames

class Tracker
{
public:
Tracker(Ptr<Feature2D> _detector, Ptr<DescriptorMatcher> _matcher) :
detector(_detector),
matcher(_matcher)
{}

void setFirstFrame(const Mat frame, vector<Point2f> bb, string title, Stats& stats);
Mat process(const Mat frame, Stats& stats);
Ptr<Feature2D> getDetector() {
return detector;
}
protected:
Ptr<Feature2D> detector;
Ptr<DescriptorMatcher> matcher;
Mat first_frame, first_desc;
vector<KeyPoint> first_kp;
vector<Point2f> object_bb;
};

void Tracker::setFirstFrame(const Mat frame, vector<Point2f> bb, string title, Stats& stats)
{
first_frame = frame.clone();
detector->detectAndCompute(first_frame, noArray(), first_kp, first_desc);
stats.keypoints = (int)first_kp.size();
drawBoundingBox(first_frame, bb);
putText(first_frame, title, Point(0, 60), FONT_HERSHEY_PLAIN, 5, Scalar::all(0), 4);
object_bb = bb;
}

Mat Tracker::process(const Mat frame, Stats& stats)
{
vector<KeyPoint> kp;
Mat desc;
detector->detectAndCompute(frame, noArray(), kp, desc);
stats.keypoints = (int)kp.size();

vector< vector<DMatch> > matches;
vector<KeyPoint> matched1, matched2;
matcher->knnMatch(first_desc, desc, matches, 2);
for(unsigned i = 0; i < matches.size(); i++) {
if(matches[i][0].distance < nn_match_ratio * matches[i][1].distance) {
matched1.push_back(first_kp[matches[i][0].queryIdx]);
matched2.push_back( kp[matches[i][0].trainIdx]);
}
}
stats.matches = (int)matched1.size();

Mat inlier_mask, homography;
vector<KeyPoint> inliers1, inliers2;
vector<DMatch> inlier_matches;
if(matched1.size() >= 4) {
homography = findHomography(Points(matched1), Points(matched2),
RANSAC, ransac_thresh, inlier_mask);
}

if(matched1.size() < 4 || homography.empty()) {
Mat res;
hconcat(first_frame, frame, res);
stats.inliers = 0;
stats.ratio = 0;
return res;
}
for(unsigned i = 0; i < matched1.size(); i++) {
if(inlier_mask.at<uchar>(i)) {
int new_i = static_cast<int>(inliers1.size());
inliers1.push_back(matched1[i]);
inliers2.push_back(matched2[i]);
inlier_matches.push_back(DMatch(new_i, new_i, 0));
}
}
stats.inliers = (int)inliers1.size();
stats.ratio = stats.inliers * 1.0 / stats.matches;

vector<Point2f> new_bb;
perspectiveTransform(object_bb, new_bb, homography);
Mat frame_with_bb = frame.clone();
if(stats.inliers >= bb_min_inliers) {
drawBoundingBox(frame_with_bb, new_bb);
}
Mat res;
drawMatches(first_frame, inliers1, frame_with_bb, inliers2,
inlier_matches, res,
Scalar(255, 0, 0), Scalar(255, 0, 0));
return res;
}

int main(int argc, char **argv)
{
if(argc < 4) {
cerr << "Usage: " << endl <<
"akaze_track input_path output_path bounding_box" << endl;
return 1;
}
VideoCapture video_in(argv[1]);
VideoWriter video_out(argv[2],
(int)video_in.get(CAP_PROP_FOURCC),
(int)video_in.get(CAP_PROP_FPS),
Size(2 * (int)video_in.get(CAP_PROP_FRAME_WIDTH),
2 * (int)video_in.get(CAP_PROP_FRAME_HEIGHT)));

if(!video_in.isOpened()) {
cerr << "Couldn't open " << argv[1] << endl;
return 1;
}
if(!video_out.isOpened()) {
cerr << "Couldn't open " << argv[2] << endl;
return 1;
}

vector<Point2f> bb;
FileStorage fs(argv[3], FileStorage::READ);
if(fs["bounding_box"].empty()) {
cerr << "Couldn't read bounding_box from " << argv[3] << endl;
return 1;
}
fs["bounding_box"] >> bb;

Stats stats, akaze_stats, orb_stats;
Ptr<AKAZE> akaze = AKAZE::create();
akaze->set("threshold", akaze_thresh);
Ptr<ORB> orb = ORB::create();
orb->setMaxFeatures(stats.keypoints);
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
Tracker akaze_tracker(akaze, matcher);
Tracker orb_tracker(orb, matcher);

Mat frame;
video_in >> frame;
akaze_tracker.setFirstFrame(frame, bb, "AKAZE", stats);
orb_tracker.setFirstFrame(frame, bb, "ORB", stats);

Stats akaze_draw_stats, orb_draw_stats;
int frame_count = (int)video_in.get(CAP_PROP_FRAME_COUNT);
Mat akaze_res, orb_res, res_frame;
for(int i = 1; i < frame_count; i++) {
bool update_stats = (i % stats_update_period == 0);
video_in >> frame;

akaze_res = akaze_tracker.process(frame, stats);
akaze_stats += stats;
if(update_stats) {
akaze_draw_stats = stats;
}

orb_tracker.getDetector()->set("nFeatures", stats.keypoints);
orb_res = orb_tracker.process(frame, stats);
orb_stats += stats;
if(update_stats) {
orb_draw_stats = stats;
}

drawStatistics(akaze_res, akaze_draw_stats);
drawStatistics(orb_res, orb_draw_stats);
vconcat(akaze_res, orb_res, res_frame);
video_out << res_frame;
cout << i << "/" << frame_count - 1 << endl;
}
akaze_stats /= frame_count - 1;
orb_stats /= frame_count - 1;
printStatistics("AKAZE", akaze_stats);
printStatistics("ORB", orb_stats);
return 0;
}

解释

类追踪器

这个类使用给定的特征检测器和描述符匹配器实现上述算法。

  • 设置第一个帧

void Tracker::setFirstFrame(const Mat frame, vector<Point2f> bb, string title, Stats& stats)
{
first_frame = frame.clone();
(*detector)(first_frame, noArray(), first_kp, first_desc);
stats.keypoints = (int)first_kp.size();
drawBoundingBox(first_frame, bb);
putText(first_frame, title, Point(0, 60), FONT_HERSHEY_PLAIN, 5, Scalar::all(0), 4);
object_bb = bb;
}

我们计算并存储第一帧的关键点和描述符,并为输出做好准备。
我们需要保存检测到的关键点的数量,以确保两个探测器大致定位相同数量的关键点。

  1. 处理框架
  • 定位关键点并计算描述符

(*detector)(frame, noArray(), kp, desc);

为了在帧之间找到匹配,我们必须先定位关键点。
在本教程中,检测器被设置为在每一帧中找到大约1000个关键点。

  • 使用2-nn匹配器查找对应联系

matcher->knnMatch(first_desc, desc, matches, 2);
for(unsigned i = 0; i < matches.size(); i++) {
if(matches[i][0].distance < nn_match_ratio * matches[i][1].distance) {
matched1.push_back(first_kp[matches[i][0].queryIdx]);
matched2.push_back( kp[matches[i][0].trainIdx]);
}
}

如果最接近的匹配是nn_match_ratio比第二接近的更接近,那么它就是一个匹配。

  • 用RANSAC估计单应变换

homography = findHomography(Points(matched1), Points(matched2),
RANSAC, ransac_thresh, inlier_mask);

如果有至少4个匹配,我们可以使用随机样本一致性估计图像变换。

  • 保存这个内点

for(unsigned i = 0; i < matched1.size(); i++) {
if(inlier_mask.at<uchar>(i)) {
int new_i = static_cast<int>(inliers1.size());
inliers1.push_back(matched1[i]);
inliers2.push_back(matched2[i]);
inlier_matches.push_back(DMatch(new_i, new_i, 0));
}
}

由于findHomography计算嵌套,我们只需要保存选择的点和匹配。

  • 目标对象边界框

perspectiveTransform(object_bb, new_bb, homography);

如果有合理数量的嵌套,我们可以使用估计转换来定位对象。

结果

可以​​在 youtube 上​​​观看生成的​​视频​​:

​​https://r3---sn-npoe7nez.googlevideo.com/videoplayback?expire=1634302897&ei=USdpYcy6IqmGlQSqgYjIDA&ip=124.219.119.15&id=o-AOfFG7lqVrRzBmnfzMJVpXqrPfmHRxUHWX5f3C1UIeL2&itag=18&source=youtube&requiressl=yes&vprv=1&mime=video%2Fmp4&ns=ZgdhzS5LheJCPssjI-Qh2iYG&gir=yes&clen=2737463&ratebypass=yes&dur=23.591&lmt=1435137598866527&fexp=24001373,24007246&c=WEB&n=fFMITftlLL_uFEfaJz&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cvprv%2Cmime%2Cns%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIhAJQ6QCRqvMnqckNi0wx55JZyTFUrpFDqCK5R2kQzlM7HAiBtIezYQ2g9J9k-d2nJFfTtvA9BYcIAvUNEOLJWGkTlsQ%3D%3D&cm2rm=sn-xcu-u2x67s,sn-un5e7s&req_id=82736264f230a3ee&ipbypass=yes&redirect_counter=2&cms_redirect=yes&mh=yP&mm=34&mn=sn-npoe7nez&ms=ltu&mt=1634281246&mv=m&mvi=3&pl=24&lsparams=ipbypass,mh,mm,mn,ms,mv,mvi,pl&lsig=AG3C_xAwRQIhAJtpKShrtJQqZxljHJ6kYVg5t4-rtAV6pxWX76hfR-AtAiAFX7pDYECBDTptKCPeoT-RtUBSHwovBMQzIEwuU2tChw%3D%3D​​

AKAZE统计:

Matches      626
Inliers 410
Inlier ratio 0.58
Keypoints 1117

ORB统计:

Matches      504
Inliers 319
Inlier ratio 0.56
Keypoints 1112

举报

相关推荐

0 条评论