OpenCV视频分析与对象跟踪C++(二)光流对象跟踪-稀疏光流、稠密光流

移动对象跟踪三要素:图像表示(跟踪的对象要在图像中出现)外光模型,移动模型。

稀疏光流跟踪,KTL

void calcOpticalFlowPyrLK( // 稀疏光流跟踪,KLT
 InputArray prevImg, // 要跟踪的图像,8bit
 InputArray nextImg, // 在目标图像跟跟踪 prevImg 上的 prevPts 特征点
 InputArray prevPts, // prevImg 上的特征点(光流)的坐标位置;点坐标必须是单精度浮点数
 InputOutputArray nextPts, // 如果在 nextImg 上跟踪到了 prevImg 上的 prevPts[i],则在 nextPts[i] 上保存该特征点现在的坐标,nextPts与prevPts尺寸相同
OutputArray status, // 输出状态向量(无符号char);如果相应位置的流特征被发现,向量的每个元素被设置为1,否则,被置为0.
 OutputArray err, // 跟踪时候区域误差和
Size winSize = Size(21,21), // 在每个金字塔水平搜寻窗口的尺寸。
 int maxLevel = 3, // 金字塔的高度,初始为3层
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01), // 在每个金字塔层,为某点寻找光流的迭代过程的终止条件
// flags    CV_LKFLOW_PYR_A_READY , 在调用之前,第一帧的金字塔已经准备好
CV_LKFLOW_PYR_B_READY , 在调用之前,第二帧的金字塔已经准备好
CV_LKFLOW_INITIAL_GUESSES , 在调用之前,数组 B 包含特征的初始坐标 (Hunnish: 在本节中没有出现数组 B,不知是指的哪一个)
int flags = 0,
 double minEigThreshold = 1e-4 // 大量实验得出的默认值,别乱改
 );

代码:

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include<opencv2/face.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 

using namespace cv::face;
using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;

Mat frame, gray;//当前帧
Mat prev_gray;//前一帧
vector<Point2f> features;//shi-tomasi角点检测-特征数据
vector<Point2f>fpts[2];//保证当前帧和前一帧的特征点位置
vector<Point2f> iniPoints;
vector<uchar>status;//特征点跟踪成功标志位
vector<float>errors;//跟踪时候区域误差和

void detectFeatures(Mat &inFrame, Mat &ingray)  // Shi-Tomas 角点检测
{
    double maxCorners = 5000;
    double qualitylevel = 0.01;
    double minDistance = 10;
    double blockSize = 3;
    double k = 0.04;
    goodFeaturesToTrack(ingray, features, maxCorners, qualitylevel, minDistance, Mat(), blockSize, false, k); // 算法很快,满足实时性要求
    cout << "detect features : " << features.size() << endl;
}

void drawFeature(Mat&inFrame) {//绘制特征点
    for (size_t t = 0;t< fpts[0].size(); t++) {
        circle(inFrame, fpts[0][t], 2, Scalar(0, 0, 255), 2);
    }
}
void drawTrackLines() // 在跟踪到的且移动了的特征点(光流)的开始跟踪的位置 到 当前跟踪到的位置之间绘制线段
{
    for (size_t t = 0; t<fpts[1].size(); t++)
    {
        line(frame, iniPoints[t], fpts[1][t], Scalar(0, 255, 0), 1, 8, 0); // 绘制线段
        circle(frame, fpts[1][t], 2, Scalar(0, 0, 255), 2, 8, 0);
    }
}
void KLTrackFeature() {//稀疏光流跟踪,KTL

    calcOpticalFlowPyrLK(prev_gray, gray, fpts[0], fpts[1], status, errors);
    int k = 0;//保存跟踪到的特征点数,最后将特征点的尺寸重新设置为k
    for (int 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以上的
        {
            iniPoints[k] = iniPoints[i];//将跟踪到的移动了的特征点在vector中连续起来,剔掉损失的和禁止不动的特征点(这些跟踪点在前面帧中)
            fpts[1][k++] = fpts[1][i];//同上(只是这些跟踪点在当前帧中)
        }
    }
    //保存特征点并绘制跟踪轨迹
    iniPoints.resize(k);
    fpts[1].resize(k);
    drawTrackLines();
    std::swap(fpts[1], fpts[0]);//交换,将此帧跟踪到特征点作为下一帧的待跟踪点
}

int main() {
    VideoCapture capture;
    capture.open("C:/Users/Administrator/Desktop/pic/3.avi");

    while (capture.read(frame)) {
        cvtColor(frame, gray, COLOR_BGR2GRAY);
        if (fpts[0].size() < 40) {//跟踪40个特征点,如果跟踪的时候损失了一些特征点,重新检测,追加
            detectFeatures(frame, gray);
            fpts[0].insert(fpts[0].end(), features.begin(), features.end());//追加带跟踪的特征点
            iniPoints.insert(iniPoints.end(), features.begin(), features.end());
        }
        else
        {
            cout << "tracjing........" << endl;
        }
        if (prev_gray.empty())
            gray.copyTo(prev_gray);//保存当前已帧,第一帧过完就不保存了
        KLTrackFeature();//稀疏光流跟踪,KLT
        drawFeature(frame);//绘制特征点
        //更新前一帧数据
        gray.copyTo(prev_gray);//只需要灰度图
        imshow("src", frame);
    }
    waitKey(0);
}

结果:
这里写图片描述

稠密光流跟踪

void calcOpticalFlowFarneback( // 稠密光流跟踪
InputArray prev, // 输入前一帧图像
InputArray next, // 输入后一帧图像
InputOutputArray flow, // 输出的光流
double pyr_scale, // 金字塔上下两层之间的尺度关系
int levels, // 金字塔层数
int winsize, // 均值窗口大小,越大越能denoise并且能够检测快速移动目标,但会引起模糊运动区域
int iterations, // 迭代次数
int poly_n, // 像素领域大小,一般为5,7等
double poly_sigma, // 高斯标注差,一般为1-1.5
int flags // 计算方法。主要包括OPTFLOW_USE_INITIAL_FLOW和OPTFLOW_FARNEBACK_GAUSSIAN
);

代码:

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
#include<opencv2/face.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 

using namespace cv::face;
using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;

void drawOpticalFlowHF(const Mat &flowdata, Mat& image, int step)
{
    for (int row = 0; row < image.rows; row++)
    {
        for (int col = 0; col < image.cols; col++)
        {
            const Point2f fxy = flowdata.at<Point2f>(row, col);
            if (fxy.x > 1 || fxy.y > 1) // x 或 y 方向移动了1个像素以上就表示该像素移动了
            {
                line(image, Point(col, row), Point(cvRound(col + fxy.x), cvRound(row + fxy.y)), Scalar(0, 255, 0), 2, 8, 0); // 移动轨迹
                circle(image, Point(col, row), 2, Scalar(0, 0, 255), -1); // 较上一帧移动的像素点
            }
        }
    }
}
int main()
{
    VideoCapture capture;
    capture.open("C:/Users/Administrator/Desktop/pic/3.avi");
    if (!capture.isOpened()) cout << "video not open.." << endl;

    Mat frame, gray;
    Mat prev_frame, prev_gray;
    Mat flowResult, flowdata;
    capture.read(frame); // 先取出第一帧图像
    cvtColor(frame, prev_gray, COLOR_BGR2GRAY);

    // 从第二帧数据开始,与第一帧进行比较
    while (capture.read(frame))
    {
        cvtColor(frame, gray, COLOR_BGR2GRAY);
        if (!prev_gray.empty())
        {
            calcOpticalFlowFarneback(prev_gray, gray, flowdata, 0.5, 3, 15, 3, 5, 1.2, 0); // 稠密光流是对整个图像的计算,所以实时性不好
            cvtColor(prev_gray, flowResult, COLOR_GRAY2BGR);
            drawOpticalFlowHF(flowdata, flowResult, 10); // 绘制跟踪
            imshow("flow", flowResult);
            imshow("src6-11", frame);
        }
    }

    waitKey(0);
}

结果:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_26907755/article/details/81777246