Advertisement

OpenCV光流Optical Flow

阅读量:

OpenCV光流Optical Flow

  • LightFlow Optic Flow
    • 目标转换为目的
    • LightFlow 替换为 LightFlow(注意:此处可能需要进一步处理以确保正确性)
    • L-K algorithm 替换为 L-K 方法(注意:此处可能需要进一步处理以确保正确性)
    • OpenCV 中的 Lucas-Kanade 光流 替换为 基于 OpenCV 的 L-K 光流 算法(注意:此处可能需要进一步处理以确保正确性)
    • OpenCV 中的 稠密 光流向量 替换为 基于 OpenCV 的 稠密 光流 计算(注意:此处可能需要进一步处理以确保正确性)

光流Optical Flow

目标

在本章中,
我们将学习光流的概念,并利用Lucas-Kanade方法进行估算。
我们计划利用cv.calcOpticalFlowPyrLK函数等工具来追踪视频中的特征点。
我们还将采用cv.calcOpticalFlowFarneback方法来生成密集的光流场。

光流

光流是一种基于物体或照相机运动的概念,在两个连续帧之间的图像中体现为物体视在运动的形式。作为二维矢量场,在其中每个矢量代表点从第一个图像到第二个图像之间的移动位置。请参考下图

在这里插入图片描述

该视频呈现一个球在五个连续帧中的运动;箭头指示出球的位移向量;光流可用于多个领域

运动的结构
视频压缩
视频稳定…
光流的工作基于以下几个假设:

实体中的每个像元亮度在整个连续的画面序列中保持恒定。
附近的 pixels 展现出类似的 motion 特性。
分析初始 frame 中的 pixel I(x,y,t),并新增 dimensions 及所需的时间信息。该 pixel 随后将在 dt 时间段内移动到下一 frame 的位置 (dx, dy) 处。
基于此观察可知,

在这里插入图片描述

然后采用泰勒级数的右侧逼近,去掉常用项并除以 dŤ 得到以下等式:

在这里插入图片描述

我们将其命名为光流方程,在其中我们能够确定fx和fy。 它们代表图像的空间变化率,并且同样地表示随着时间的变化而出现的梯度场。 然而(u,v)未知,在这种情况下由于有两个未知变量无法直接求解该方程。 为此提出了多种解决方案,并且其中一种方法就是Lucas-Kanade法

卢卡斯-卡纳德方法

在之前的讨论中我们已认识到一个假设即所有相邻像素都将呈现相似的运动状态为此Lucas-Kanade方法在其邻域内采用3×3像素块从而保证了计算的有效性因此我们可以找到这9个点对应的(fx fy ft)参数现在的问题转化为求解由9个方程组成的两未知量系统通过最小二乘法优化可以显著提升解决方案的质量最后我们将展示这一优化后的具体实现过程

在这里插入图片描述

用Harris拐角检测器检查逆矩阵的相似性。这表示拐角是更好的跟踪点。)

从用户的视角看这个想法其实不算复杂列举了一些需要追踪的关键点随后收集这些关键点对应的光流向量然而到目前为止我们的系统主要关注的是微小的操作这导致无法有效捕捉较大的动作为了改进这一缺陷我们使用了一种层次化的方法即所谓的金字塔结构在这种结构下在进行操作时会自动去除过于微小的动作并将其分解为多个较小的操作层通过在该层次应用Lucas-Kanade算法我们可以实现精确计算每个层次上的光流信息

OpenCV中的Lucas-Kanade光流

在本例中开发了一个基本的应用程序,在该应用中实现了 OpenCV 的多个功能集成。通过调用 cv.goodFeaturesToTrack()函数来检测 Shi-Tomasi 角点,并随后利用 Lucas-Kanade 算法进行迭代追踪以确定目标点的变化情况。具体而言,在第一帧图像中识别出若干目标特征点后,在后续每一帧图像中持续追踪这些特征的变化轨迹。为了实现这一目标,在每一步骤中都需要调用 cv.calcOpticalFlowPyrLK()函数并传递前一帧图像、初始特征点位置以及当前帧图像数据作为输入参数集合。该函数将返回当前时刻的所有特征点位置变化信息及其状态标志值:若状态标志值为 1,则表示成功捕获到了当前时刻的目标特征;反之则表明未能找到对应的目标特征点信息。通过这种方式不断更新并传递追踪结果即可实现对目标特征进行持续而精确的追踪定位过程;参考以下代码片段获取具体实现细节

复制代码
    #include <iostream>
    #include <opencv2/core.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/videoio.hpp>
    #include <opencv2/video.hpp>
    using namespace cv;
    using namespace std;
    int main(int argc, char **argv)
    {
    const string about =
        "This sample demonstrates Lucas-Kanade Optical Flow calculation.\n"
        "The example file can be downloaded from:\n"
        "  https://www.bogotobogo.com/python/OpenCV_Python/images/mean_shift_tracking/slow_traffic_small.mp4";
    const string keys =
        "{ h help |      | print this help message }"
        "{ @image | vtest.avi | path to image file }";
    CommandLineParser parser(argc, argv, keys);
    parser.about(about);
    if (parser.has("help"))
    {
        parser.printMessage();
        return 0;
    }
    string filename = samples::findFile(parser.get<string>("@image"));
    if (!parser.check())
    {
        parser.printErrors();
        return 0;
    }
    VideoCapture capture(filename);
    if (!capture.isOpened()){
        //error in opening the video input
        cerr << "Unable to open file!" << endl;
        return 0;
    }
    // Create some random colors
    vector<Scalar> colors;
    RNG rng;
    for(int i = 0; i < 100; i++)
    {
        int r = rng.uniform(0, 256);
        int g = rng.uniform(0, 256);
        int b = rng.uniform(0, 256);
        colors.push_back(Scalar(r,g,b));
    }
    Mat old_frame, old_gray;
    vector<Point2f> p0, p1;
    // Take first frame and find corners in it
    capture >> old_frame;
    cvtColor(old_frame, old_gray, COLOR_BGR2GRAY);
    goodFeaturesToTrack(old_gray, p0, 100, 0.3, 7, Mat(), 7, false, 0.04);
    // Create a mask image for drawing purposes
    Mat mask = Mat::zeros(old_frame.size(), old_frame.type());
    while(true){
        Mat frame, frame_gray;
        capture >> frame;
        if (frame.empty())
            break;
        cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
        // calculate optical flow
        vector<uchar> status;
        vector<float> err;
        TermCriteria criteria = TermCriteria((TermCriteria::COUNT) + (TermCriteria::EPS), 10, 0.03);
        calcOpticalFlowPyrLK(old_gray, frame_gray, p0, p1, status, err, Size(15,15), 2, criteria);
        vector<Point2f> good_new;
        for(uint i = 0; i < p0.size(); i++)
        {
            // Select good points
            if(status[i] == 1) {
                good_new.push_back(p1[i]);
                // draw the tracks
                line(mask,p1[i], p0[i], colors[i], 2);
                circle(frame, p1[i], 5, colors[i], -1);
            }
        }
        Mat img;
        add(frame, mask, img);
        imshow("Frame", img);
        int keyboard = waitKey(30);
        if (keyboard == 'q' || keyboard == 27)
            break;
        // Now update the previous frame and previous points
        old_gray = frame_gray.clone();
        p0 = good_new;
    }
    }

该代码无法验证后续关键点的准确性。
因此,在图像中消失任何一个特征点时,
光流可能会尝试找到一个看起来与之接近的新位置。
然而,在稳健追踪中,
拐角处应定期检测关键点,
每隔一定时间进行一次。
如OpenCV官方示例所示,
lk_track.py)

查看我们得到的结果:

在这里插入图片描述

OpenCV中的密集光流

Lucas-Kanade方法用于计算稀疏特征集中的光流(例如我们在示例中使用Shi-Tomasi算法检测到的角度)。OpenCV引入了另一种用于查找密集光流的方法。该算法则通过分析每一帧中的每个点来评估其亮度变化。这一技术源自Gunnar Farneback于2003年提出的基于多项式展开的方法。

通过上述算法的应用,在该示例中实现了对密集光流的有效捕捉。生成了一个包含两个通道的空间分布向量场,并用(u, v)表示这些向量参数的具体值。通过计算获得了这些向量场元素的具体大小及其指向方向。通过对计算出的结果进行色彩编码处理,在视觉上增强了数据信息的表现效果。请参见下面的代码:

复制代码
    #include <iostream>
    #include <opencv2/core.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/videoio.hpp>
    #include <opencv2/video.hpp>
    using namespace cv;
    using namespace std;
    int main()
    {
    VideoCapture capture(samples::findFile("vtest.avi"));
    if (!capture.isOpened()){
        //error in opening the video input
        cerr << "Unable to open file!" << endl;
        return 0;
    }
    Mat frame1, prvs;
    capture >> frame1;
    cvtColor(frame1, prvs, COLOR_BGR2GRAY);
    while(true){
        Mat frame2, next;
        capture >> frame2;
        if (frame2.empty())
            break;
        cvtColor(frame2, next, COLOR_BGR2GRAY);
        Mat flow(prvs.size(), CV_32FC2);
        calcOpticalFlowFarneback(prvs, next, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
        // visualization
        Mat flow_parts[2];
        split(flow, flow_parts);
        Mat magnitude, angle, magn_norm;
        cartToPolar(flow_parts[0], flow_parts[1], magnitude, angle, true);
        normalize(magnitude, magn_norm, 0.0f, 1.0f, NORM_MINMAX);
        angle *= ((1.f / 360.f) * (180.f / 255.f));
        //build hsv image
        Mat _hsv[3], hsv, hsv8, bgr;
        _hsv[0] = angle;
        _hsv[1] = Mat::ones(angle.size(), CV_32F);
        _hsv[2] = magn_norm;
        merge(_hsv, 3, hsv);
        hsv.convertTo(hsv8, CV_8U, 255.0);
        cvtColor(hsv8, bgr, COLOR_HSV2BGR);
        imshow("frame2", bgr);
        int keyboard = waitKey(30);
        if (keyboard == 'q' || keyboard == 27)
            break;
        prvs = next;
    }
    }

看到下面的结果:

在这里插入图片描述

全部评论 (0)

还没有任何评论哟~