当前位置: 首页 > news >正文

C/C++开发,opencv光流法跟踪特征点

目录

一、Lucas-Kanade光流法

1.1cv::ORB特征点提取方法

1.2 cv::calcOpticalFlowPyrLK函数

二、完整案例实现

2.1 程序代码

2.2 程序编译及输出


一、Lucas-Kanade光流法

          在 OpenCV 中,使用 特征检测器(例如ORB ,Oriented FAST and Rotated BRIEF,更多的特征点提取方法见本栏博文:C/C++开发,opencv-features2d模块,SIFT等特征检测器应用-CSDN博客)与 cv::calcOpticalFlowPyrLK 光流跟踪器结合是一种有效的方法,用于跟踪视频序列中的关键点变化。

1.1cv::ORB特征点提取方法

        在OpenCV中,ORB(Oriented FAST and Rotated BRIEF)是一种快速的特征点检测和描述符提取算法,它结合了FAST(Features from Accelerated Segment Test)特征点检测器和BRIEF(Binary Robust Independent Elementary Features)描述符的优点,并增加旋转不变性和尺度不变性。

        在实际使用中,主要用到cv::ORB::create创建对象,然后使用detect*函数提取特征点。

//函数原型,OpenCV 4.x为例
cv::Ptr<cv::ORB> cv::ORB::create(  int nfeatures = 500,         // 最大特征点数量  float scaleFactor = 1.2f,    // 金字塔图像间的尺度参数(每层图像之间的尺度比例)  int nlevels = 8,             // 金字塔层数(包括原始图像层)  int edgeThreshold = 31,      // 用于FAST检测的边缘阈值(边缘像素不会被认为是关键点)  int firstLevel = 0,          // 使用的金字塔的第一层(通常设置为0)  int WTA_K = 2,               // 生成描述子时用于局部比较的点数  int scoreType = cv::ORB::HARRIS_SCORE, // 角点检测评分类型(HARRIS_SCORE或FAST_SCORE)  int patchSize = 31,          // 用于描述子计算的邻域大小(以像素为单位)  int fastThreshold = 20        // FAST角点检测的阈值(低于此值的点不会被认为是角点)  
);

        cv::ORB::create 是一个静态成员函数,用于创建并配置一个ORB(Oriented FAST and Rotated BRIEF)特征检测器对象。cv::ORB::create 函数允许你通过传递不同的参数来定制ORB检测器的行为。这个函数是可选的,因为cv::Ptr<cv::ORB>可以直接通过new关键字和构造函数来创建ORB对象,但使用create函数是一种更简洁、更现代的方式,它会自动处理内存管理(通过智能指针)。

         cv::ORB::detectAndCompute 函数是ORB特征检测器的一个关键方法,它同时执行特征点检测和描述符计算两个步骤。该函数的参数允许用户指定输入图像、掩码(可选)、输出关键点向量和输出描述符矩阵。

//函数原型
void cv::ORB::detectAndCompute(InputArray image,  InputArray mask,  std::vector<KeyPoint>& keypoints,  OutputArray descriptors,  bool useProvidedKeyPoints = false);
/*
参数:1)image (InputArray): 输入图像,可以是灰度图像或彩色图像。ORB算法通常在灰度图像上运行得更好,因此如果输入是彩色图像,ORB可能会在内部将其转换为灰度图。2)mask (InputArray, 可选): 一个与输入图像大小相同的掩码图像,用于指定哪些区域应该被考虑用于特征检测。掩码图像必须是单通道、8位深度的图像,其中非零像素表示要检测特征的区域,零像素表示要忽略的区域。如果掩码为空(即传递Mat()),则没有区域会被忽略。3)keypoints (std::vector<KeyPoint>&): 输出参数,用于存储检测到的关键点。每个关键点包含位置(x, y)、尺度(size)、方向(angle,对于ORB是可选的,因为ORB可能不计算方向)等信息。4)descriptors (OutputArray): 输出参数,用于存储关键点的描述符。ORB使用BRIEF或rBRIEF作为描述符,因此每个描述符是一个二进制字符串,通常表示为一个uchar类型的向量或矩阵。5)useProvidedKeyPoints (bool, 默认为false): 一个可选参数,指定是否使用用户提供的关键点来计算描述符。如果设置为true,则detectAndCompute函数将仅计算由keypoints参数提供的关键点的描述符,而不会执行特征点检测。这通常用于当你已经有了关键点位置,但需要计算这些关键点的描述符时。
*/

        调整cv::ORB::create函数中的参数,如nfeatures(特征点数量)、scaleFactor(尺度因子)、nlevels(金字塔层数)等,可以控制ORB检测器的detectAndCompute 行为,以适应不同的应用场景和需求。 

1.2 cv::calcOpticalFlowPyrLK函数

        在C++中使用OpenCV库进行光流法(Lucas-Kanade)跟踪特征点是一种常见的技术,特别适用于视频测振处理、运动估计和物体跟踪等应用。OpenCV中的光流法实现是通过cv::calcOpticalFlowPyrLK函数来跟踪视频中的特征点。

//函数原型
void cv::calcOpticalFlowPyrLK(InputArray prevImg, InputArray nextImg,  InputArray prevPts, OutputArray 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 或 CV_32FC1。nextImg:当前帧图像,与 prevImg 类型相同。prevPts:前一帧中特征点的坐标,类型为 Point2f 的 vector 或 Mat。nextPts:输出参数,存储当前帧中对应特征点的坐标,类型与 prevPts 相同。status:输出状态数组,用于表示每个特征点是否成功找到对应点,类型为 uchar 的 vector 或 Mat。err:输出数组,存储每个特征点的光流误差估计,类型为 float 的 vector 或 Mat。winSize:搜索窗口的大小,默认为 Size(21, 21)。maxLevel:金字塔的最大层数,默认为 3。criteria:迭代算法的终止条件,默认为迭代 30 次或达到某个最小误差(0.01)。flags:操作标志,默认为 0。minEigThreshold:最小特征值阈值,用于检测跟踪质量,默认为 1e-4。
*/

        通常是先读取视频或捕获摄像头,选用第一帧中检测 ORB 特征点,并将它们作为初始点集传递给 cv::calcOpticalFlowPyrLK。然后,在视频序列的每一帧中,计算这些点的光流,并更新它们的位置。通常,由于使用了光流跟踪,因此不需要在每帧中都重新检测特征点,除非跟踪失败或需要重新初始化。

        在实际应用中,由于遮挡、快速运动或光照变化等原因,一些特征点可能会丢失或变得不可靠。因此,可能需要实现一些额外的逻辑来处理这些情况,比如重新检测丢失的特征点或删除不可靠的跟踪点。

        由于 cv::calcOpticalFlowPyrLKprevPtsnextPts 需要在每次迭代后更新,因此确保在每次迭代开始时正确地设置了这些变量是很重要的。

二、完整案例实现

2.1 程序代码

        main.cpp代码,使用OpenCV的VideoCapture类来从文件或摄像头捕获视频帧,使用ORB特征检测(cv::ORB::create,cv::ORB::detectAndCompute),使用光流法(cv::calcOpticalFlowPyrLK)跟踪特征点。获得了特征点的位置随时间的变化,进行了这些特征点绘制(在原图像上绘制圆点、变化线等),也可以进一步分析这些变化来估计振动参数,如频率、振幅等。

#include <opencv2/opencv.hpp>  
#include <vector>
#include <iostream>int main(int argc, char* argv[]) 
{  cv::VideoCapture cap; // 视频捕获对象 if(argc< 2){ cap.open(0);        //0是摄像头的索引}else{        // 加载视频 cap.open(argv[1]);  //如果是视频文件,则传入文件路径  }// cv::VideoCapture cap(0); // 0是摄像头的索引,如果是视频文件,则传入文件路径  if (!cap.isOpened()) {  std::cerr << "Error opening video stream or file" << std::endl;  return -1;  }  cv::Mat frame, gray, prevGray;  cv::Ptr<cv::ORB> detector = cv::ORB::create(500); // 创建ORB检测器,这里设置最大特征点数为500  std::vector<cv::Point2f> prevPts, nextPts;  std::vector<uchar> status;  std::vector<float> err;  // 读取第一帧并检测ORB特征点  if (cap.read(frame)) {  cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);  std::vector<cv::KeyPoint> keypoints;  cv::Mat descriptors;  detector->detectAndCompute(gray, cv::Mat(), keypoints, descriptors);  // 将关键点坐标转换为Point2f  for (const auto& kp : keypoints) {  prevPts.push_back(kp.pt);  }   }  prevGray = gray.clone();  // 循环读取视频帧并计算光流  while (cap.read(frame)) {  cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);  // 使用金字塔Lucas-Kanade方法计算光流  cv::calcOpticalFlowPyrLK(prevGray, gray, prevPts, nextPts, status, err);  // 绘制跟踪点  for (size_t i = 0; i < nextPts.size(); i++) {  if (status[i]) {  // cv::circle(frame, prevPts[i], 2, cv::Scalar(0, 255, 0), -1); cv::circle(frame, nextPts[i], 2, cv::Scalar(255, 0, 0), -1); cv::line(frame, prevPts[i], nextPts[i], cv::Scalar(0, 0, 255), 1); //可以存储nextPts点,形成时间序列信息//进一步分析prevPts, nextPts点变化来估计振动参数,如频率、振幅等。这通常涉及到信号处理技术,如傅里叶变换等。}  } // 更新前一帧的跟踪点  prevPts = nextPts;  std::vector<cv::Point2f>().swap(nextPts); // 清空nextPts以准备下一帧使用  // 更新前一帧的灰度图  prevGray = gray.clone();  // 显示结果  cv::imshow("Frame", frame);  char c = (char)cv::waitKey(25);  if (c == 27) break; // 按ESC退出  }  cap.release();  cv::destroyAllWindows();  return 0;  
}
2.2 程序编译及输出

        本文是采用win系统下,opencv采用MinGW编译的静态库(C/C++开发,win下OpenCV+MinGW编译环境搭建_opencv mingw-CSDN博客),建立makefile:

#/bin/sh
#win32
CX= g++ -DWIN32 
#linux
#CX= g++ -Dlinux BIN 		:= ./
TARGET      := Video_vibrometer.exe
FLAGS		:= -std=c++11 -static
SRCDIR 		:= ./
#INCLUDES
INCLUDEDIR 	:= -I"../../opencv_MinGW/include" -I"./"
#-I"$(SRCDIR)"
staticDir   := ../../opencv_MinGW/x64/mingw/staticlib/
#LIBDIR		:= $(staticDir)/libopencv_world460.a\
#			   $(staticDir)/libade.a \
#			   $(staticDir)/libIlmImf.a \
#			   $(staticDir)/libquirc.a \
#			   $(staticDir)/libzlib.a \
#			   $(wildcard $(staticDir)/liblib*.a) \
#			   -lgdi32 -lComDlg32 -lOleAut32 -lOle32 -luuid 
#opencv_world放弃前,然后是opencv依赖的第三方库,后面的库是MinGW编译工具的库LIBDIR 	    := -L $(staticDir) -lopencv_world460 -lade -lIlmImf -lquirc -lzlib \-llibjpeg-turbo -llibopenjp2 -llibpng -llibprotobuf -llibtiff -llibwebp \-lgdi32 -lComDlg32 -lOleAut32 -lOle32 -luuid 
source		:= $(wildcard $(SRCDIR)/*.cpp) $(TARGET) :$(CX) $(FLAGS) $(INCLUDEDIR) $(source)  -o $(BIN)/$(TARGET) $(LIBDIR)clean:rm  $(BIN)/$(TARGET)

编译如下:

程序运行输出如下,本测试没有指定视频图像,采用笔记本的摄像头抓取视频:

        PS:读者可以调整参数、图像光影效果等验证测试。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 17085 工作分配问题(优先做)
  • C# 设计模式之抽象工厂模式
  • 定时器知识点
  • Go语言加Vue3零基础入门全栈班15 gin+gorm+vue3用户管理系统实战录播课 2024年08月04日 课程笔记
  • Python爬虫与MongoDB的完美结合
  • 《零散知识点 · 自定义 HandleMapping》
  • 鸿蒙媒体开发【相机数据采集保存】拍照和图片
  • 大模型术语表
  • 24年第五届“华数杯”数学建模竞赛浅析
  • 利用ffmpeg转码视频为gif图片,调整gif图片的大小
  • 全球氢燃料电池汽车市场规划预测:未来六年CAGR为44.4%
  • 前端-防抖代码
  • App推广新利器:Xinstall携带参数安装功能详解
  • FIR低通滤波器
  • 可验证随机函数 vrf 概述
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • Iterator 和 for...of 循环
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JSONP原理
  • PAT A1050
  • Python 反序列化安全问题(二)
  • Sublime Text 2/3 绑定Eclipse快捷键
  • Vue.js-Day01
  • windows-nginx-https-本地配置
  • 从输入URL到页面加载发生了什么
  • 翻译--Thinking in React
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 给github项目添加CI badge
  • 前端临床手札——文件上传
  • 前端知识点整理(待续)
  • 如何编写一个可升级的智能合约
  • 如何学习JavaEE,项目又该如何做?
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 我的zsh配置, 2019最新方案
  • 学习ES6 变量的解构赋值
  • kubernetes资源对象--ingress
  • MyCAT水平分库
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • ​水经微图Web1.5.0版即将上线
  • #07【面试问题整理】嵌入式软件工程师
  • #stm32整理(一)flash读写
  • #单片机(TB6600驱动42步进电机)
  • (2)nginx 安装、启停
  • (8)STL算法之替换
  • (AngularJS)Angular 控制器之间通信初探
  • (java)关于Thread的挂起和恢复
  • (差分)胡桃爱原石
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转)linux 命令大全
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .NET CLR基本术语