目录
1、概述
案例:使用稀疏光流实现对象跟踪
稀疏光流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_8UC1nextImg:视频后一帧图像/金字塔,单通道CV_8UC1preVPts:前一帧图像的特征向量(输入)需要找到流的2D点的矢量(vector of 2D points for which the flow needs to be found;);点坐标必需是单精度浮点数nextPts:后一帧图像的特征向量(输出),输出二维点的矢量(具有单精度浮点坐标),包含第二图像中输入特征的计算新位置;当传送OPTFLOW_USE_INITIAL_FLOW标志时,向量必需与输入中的大小相同。status:输出状态向量(无符号字符);假设找到相应特征的流,则向量的每个元素设置为1,否则设置为0err:输出错误的矢量; 向量的每个元素都设置为相应特征的错误,错误度量的类型可以在flags参数中设置; 假设未找到流,则未定义错误(使用status参数查找此类情况)winSize:每个金字塔等级的搜索窗口的winSize大小maxLevel:基于0的最大金字塔等级数;假设设置为0,则不使用金字塔(单级),假设设置为1,则使用两个级别,依此类推;假设将金字塔传送给输入,那么算法将使用与金字塔一样多的级别,但不超越maxLevelcriteria:停止条件,指定迭代搜索算法的终止条件(在指定的最大迭代次数criteria.maxCount之后或当搜索窗口挪动小于criteria.epsilon时)。flags:操作标志,OPTFLOW_USE_INITIAL_FLOW使用初始估计,存储在nextPts中;假设未设置标志,则将prevPts复制到nextPts并将其视为初始估计。OPTFLOW_LK_GET_MIN_EIGENVALS使用最小特征值作为误差丈量(参见minEigThreshold描绘);假设没有设置标志,则将原稿四周的色块和挪动点之间的L1间隔除以窗口中的像素数,用作误差丈量minEigThreshold:算法计算光流方程的2x2正常矩阵的最小特征值,除以窗口中的像素数;假设此值小于minEigThreshold,则过滤掉相应的功能并且不处置其流程,因而它允许删除坏点并获得性能提升
算法实现步骤:
1.实例化VideoCapture
2.循环读取视频数据
3.视频帧灰度转换
4.执行角点检测
5.保管角点检测的特征数据
6.初始化时假设检测到前一帧为空,把当前帧的灰度图像给前一帧
7.执行光流跟踪,并输出跟踪后的特征向量
8.遍历光流跟踪的输出特征向量,并得到间隔和状态都符合预期的特征向量。让后将其重新填充到fpts[1]中备用
9.重置集合大小
10.绘制光流线
11.交换特征向量的输入和输出
12.将用于跟踪的角点绘制出来
13.展示最终的跟踪效果
14.循环3~13步骤
15.完毕
2、代码示例
- KLT_Object_Tracking::KLT_Object_Tracking(QWidget *parent)
- : MyGraphicsView{parent}
- {
- isShowLine = false;
- this->setWindowTitle("KLT稀疏光流实现对象跟踪");
- 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;//光流输出状态
- vector<float> err;//光流输出错误
- //【2】循环读取视频
- while(capture.read(frame)){//循环读取视频中每一帧的图像
- //【3】将视频帧图像转为灰度图
- cvtColor(frame,gray,COLOR_BGR2GRAY);//ps:角点检测输入要求单通道
- //【4】假设特征向量(角点)小于40个我们就重新执行角点检测
- if(fpts[0].size()<40){//假设小于40个角点就重新开端执行角点检测
- //执行角点检测
- 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()<<"角点检测执行完成,角点个数为:"<<features.size();
- }else{
- qDebug()<<"正在跟踪...";
- }
- //【6】初始化的时候假设检测到前一帧为空,这个把当前帧的灰度图像给前一帧
- if(pre_gray.empty()){//假设前一帧为空就给前一帧赋值一次
- gray.copyTo(pre_gray);
- }
- //执行光流跟踪
- qDebug()<<"开端执行光流跟踪";
- //【7】执行光流跟踪,并将输出的特征向量放入fpts[1]中
- calcOpticalFlowPyrLK(pre_gray,gray,fpts[0],fpts[1],status,err);
- qDebug()<<"光流跟踪执行完毕";
- //【8】遍历光流跟踪的输出特征向量,并得到间隔和状态都符合预期的特征向量。让后将其重新填充到fpts[1]中备用
- int k =0;
- for(size_t i=0;i<fpts[1].size();i++){//循环遍历二维输出向量
- 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】重置集合大小(由于有错误/不符合条件的输出特征向量),只拿状态正确的
- //重新设置集合大小
- 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()<<"特征向量的输入输出交换数据";
- //【11】交换特征向量的输入和输出,(循环往复/进入下一个循环),此时特征向量的值会递减
- std::swap(fpts[1],fpts[0]);//交换特征向量的输入和输出,此处焦点的总数量会递减
- //【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】重置前一帧图像(每一个循环都要刷新)
- gray.copyTo(pre_gray);
- frame.copyTo(pre_frame);
- //【14】展示最终的效果
- imshow("frame",frame);
- int keyValue = waitKey(100);
- if(keyValue==27){//假设用户按ese键退出播放
- break;
- }
- }
- }
复制代码 3、图像演示
到此这篇关于OpenCV使用稀疏光流实现视频对象跟踪的方法详解的文章就介绍到这了,更多相关OpenCV视频对象跟踪内容请搜索网站以前的文章或继续阅读下面的相关文章希望大家以后多多支持网站! |