【碎片知识(7)· 计算机视觉基础】基于光流估计的目标跟踪

xiaoxiao2021-02-28  53

1.1         问题需求及实现流程:

   要求:使用光流法跟踪给定视频或摄像头中的运动特点。

   检测流程:1)视频采集(取到视频中当前帧图像);2)图像预处理;3)提取特征点;4)使用光流法估计特征运动;5)相邻帧及特征点交换。

1.2         OpenCV实现:

   OpenCV相关函数:

Ø  void 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,第一帧图像。

  nextImg,第二帧图像。

  prevPts,第一帧图像中的所有特征向量。

  nextPts,第二帧图像中的所有特征向量。

  status,输出状态向量;如果相应点光流被发现,向量的每个单元被设置为1,否则,被置为0。

Ø  void goodFeatureToTrack(InputArray _image, OutputArray _corners,int maxCorners, double qualityLevel,double minDistance, InputArray _mask,int blockSize, bool useHarrisDeterctor,double harrisK)

  _image,8位或32位浮点型输入图像,单通道。

  _corners,保存检测出的角点。

  maxCorners,角点数目最大值,如果实际检测的角点超过此值,则只返回前maxCorners个强角点。

  qualityLevel,角点的品质因子,决定角点可信度。

  minDistance,此领域范围内如果存在更强角点,则删除此角点。

#include <opencv2/opencv.hpp> using namespace cv; using namespace std; const int MAX_POINT_COUNT = 500; // 全局变量限制单词循环中检测的特征点数量 bool needTolnit(); void main() { char *fn = "D:\\CommonSoftware\\OpenCV\\Workplace\\Example3_OpticalFlow\\vtest.avi"; VideoCapture cap; Mat source, result, gray, lastGray; // 原始图像、、上一帧灰度图像、本帧灰度图像 vector<Point2f> points[2]; // 定义一维向量数组points[2],分别存放上一帧和当前帧的特征点 // vector<Point2f> temp; vector<uchar> status; vector<float> err; cap.open(fn); // 打开视频 if (!cap.isOpened()) // 检测是否成功打开视频 { cout << "Cannot open the source, check camera or file." << endl; return; } for ( ; ; ) // 无条件循环 { cap >> source; // 将cap抓取的当前帧图像传递给原始图像 if (source.empty()) // 通过判断提取到的当前帧是否为空来判断是否到了视频结尾 { break; } cvtColor(source, gray, COLOR_BGR2GRAY); // 将当前帧原始图像转换为灰度图像 if (points[0].size() < 10) // 如果上一帧中的特征点数量小于10个认为不可信,则重新检测 { goodFeaturesToTrack(gray, points[0], MAX_POINT_COUNT, 0.01, 20); } // 从灰度图像gray中提取特征点放入points[0]中,角点品质因子为0.01,20的范围内仅保留最强角点 if (lastGray.empty()) { // 如果上一帧灰度图像为空,即视频开头,将当前帧灰度图像复制到上一帧灰度图像中 gray.copyTo(lastGray); } calcOpticalFlowPyrLK(lastGray, gray, points[0], points[1], status, err); // 从上一帧和当前帧灰度图像中的特征点计算光流,第i个特征点如光流发现则status[i]=1,否则为0 int counter = 0; for (int i = 0; i < points[1].size(); i++) // 当i小于当前帧特征点总数时,进行循环 { double dist = norm(points[1][i] - points[0][i]); // 计算当前帧第i个特征点与上一帧第i个特征点的距离 if (status[i] && dist >= 2.0 && dist <= 20.0) // 对所有得到的特征点光流进行一个筛选 { // 筛选依据是帧间光流被发现且帧间光流长度介于2到20之间 points[0][counter] = points[0][i]; points[1][counter++] = points[1][i]; } // 将points[0]和points[1]分别的前counter个元素用过滤合格的元素代替 } points[0].resize(counter); points[1].resize(counter); // 将前counter以后的元素去除 source.copyTo(result); // 将原始图像复制到result当中 for (int i = 0; i < points[1].size(); i++) // 当i小于滤除后当前帧中特征点总数 { line(result, points[0][i], points[1][i], Scalar(0, 0, 0xff)); // 在result图像中用红色线将当前帧和上一帧中分别的第i个特征点连起来 circle(result, points[1][i], 3, Scalar (0, 0xff, 0)); // 在result图像中将所有筛选出的特征点用半径为3的绿色圈标识出来 } swap(points[0], points[1]); swap(lastGray, gray); // 将当前帧和上一帧的特征点和图像数据交换,从而将当前帧放入上一帧中以进行后续循环 imshow("Source Image", source); imshow("Result Image", result); char key = waitKey(100); // 等待100好眠 if (key == 27) // Esc键退出 { break; } } } bool needTolnit() { return true; }
转载请注明原文地址: https://www.6miu.com/read-2623368.html

最新回复(0)