探索OpenCV的光流算法视频超分与源码实现

这篇具有很好参考价值的文章主要介绍了探索OpenCV的光流算法视频超分与源码实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在OpenCV4.0以后,视频图像超分模块已经迁移到opencv_contrib独立仓库。在视频超分有两种形式:结合光流算法实现超分、使用CNN卷积神经网络实现超分。在这里主要探索光流算法实现视频超分,然后进一步分析源码实现。

一、视频超分示例

1、光流算法选择

OpenCV提供多种光流算法:farneback、tvl1、brox、pyrlk。同时支持GPU加速,示例代码如下:

static Ptr<cv::superres::DenseOpticalFlowExt> createOptFlow(const string& name, bool useGpu)
{
    if (name == "farneback")
    {
        if (useGpu) // 开启GPU加速,使用CUDA实现
            return cv::superres::createOptFlow_Farneback_CUDA();
        else
            return cv::superres::createOptFlow_Farneback();
    }
    else if (name == "tvl1")
    {
        if (useGpu)
            return cv::superres::createOptFlow_DualTVL1_CUDA();
        else
            return cv::superres::createOptFlow_DualTVL1();
    }
    else if (name == "brox")
        return cv::superres::createOptFlow_Brox_CUDA();
    else if (name == "pyrlk")
        return cv::superres::createOptFlow_PyrLK_CUDA();
    else
        cerr << "Incorrect Optical Flow algorithm - " << name << endl;

    return Ptr<cv::superres::DenseOpticalFlowExt>();
}

2、创建超分实例

根据是否使用CUDA进行GPU加速,来创建视频超分实例:

Ptr<SuperResolution> createSuperResolution(bool useCuda)
{
    if (useCuda)
        return createSuperResolution_BTVL1_CUDA();
    else
        return createSuperResolution_BTVL1();
}

3、执行视频超分

在创建光流算法、视频超分实例后,设置相关参数,执行视频超分操作。示例代码如下:

#include <iostream>
#include <string>
#include <ctype.h>

#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/superres.hpp"
#include "opencv2/superres/optical_flow.hpp"
#include "opencv2/opencv_modules.hpp"

using namespace std;
using namespace cv;
using namespace cv::superres;


int main(int argc, const char* argv[])
{
    CommandLineParser cmd(argc, argv,
        "{ v video      |           | Input video (mandatory)}"
        "{ o output     |           | Output video }"
        "{ s scale      | 4         | Scale factor }"
        "{ i iterations | 180       | Iteration count }"
        "{ t temporal   | 4         | Radius of the temporal search area }"
        "{ f flow       | farneback | Optical flow algorithm (farneback, tvl1, brox, pyrlk) }"
        "{ g gpu        | false     | CPU as default device, cuda for CUDA }"
    );
    // 解析参数
    const string inputVideoName  = cmd.get<string>("video");
    const string outputVideoName = cmd.get<string>("output");
    const int scale              = cmd.get<int>("scale");
    const int iterations         = cmd.get<int>("iterations");
    const int temporalAreaRadius = cmd.get<int>("temporal");
    const string optFlow         = cmd.get<string>("flow");
    string gpuOption             = cmd.get<string>("gpu");
    bool useCuda                 = gpuOption.compare("cuda") == 0;
    // 创建视频超分实例
    Ptr<SuperResolution> superRes = createSuperResolution(useCuda);
    // 创建光流算法
    Ptr<cv::superres::DenseOpticalFlowExt> of = createOptFlow(optFlow, useCuda);
    // 设置光流算法、超分倍数、迭代次数、半径系数
    superRes->setOpticalFlow(of);
    superRes->setScale(scale);
    superRes->setIterations(iterations);
    superRes->setTemporalAreaRadius(temporalAreaRadius);
    // 读取视频帧
    Ptr<FrameSource> frameSource;
    if (useCuda)
    {
        try
        {
            frameSource = createFrameSource_Video_CUDA(inputVideoName);
            Mat frame;
            frameSource->nextFrame(frame);
        }
        catch (const cv::Exception&)
        {
            frameSource.release();
        }
    }
    if (!frameSource)
        frameSource = createFrameSource_Video(inputVideoName);

    // 跳过第一帧
    Mat frame;
    frameSource->nextFrame(frame);
    // 设置输入源
    superRes->setInput(frameSource);

    VideoWriter writer;

    for (int i = 0;; ++i)
    {
        Mat result;
        // 执行视频图像超分
        superRes->nextFrame(result);

        if (result.empty())
            break;

        imshow("Super Resolution", result);

        if (waitKey(1000) > 0)
            break;

        // 视频帧超分结果写到输出文件
        if (!outputVideoName.empty())
        {
            if (!writer.isOpened())
                writer.open(outputVideoName, VideoWriter::fourcc('X', 'V', 'I', 'D'), 25.0, result.size());
            writer << result;
        }
    }

    return 0;
}

二、视频超分源码

1、构造函数

视频图像超分的源码在opencv_contrib/modules/superres中,创建超分实例的函数位于btv_l1.cpp,其实是创建BTVL1()构造函数的智能指针:

Ptr<cv::superres::SuperResolution> cv::superres::createSuperResolution_BTVL1()
{
    return makePtr<BTVL1>();
}

2、超分入口代码

而nextFrame()是执行超分的函数,位于super_resolution.cpp:

void cv::superres::SuperResolution::nextFrame(OutputArray frame)
{
    isUmat_ = frame.isUMat() && cv::ocl::useOpenCL();

    if (firstCall_)
    {
        initImpl(frameSource_);
        firstCall_ = false;
    }

    processImpl(frameSource_, frame);
}

可以看到内部调用processImpl()函数来执行超分:

    void BTVL1::processImpl(Ptr<FrameSource>& frameSource, OutputArray _output)
    {
        if (outPos_ >= storePos_)
        {
            _output.release();
            return;
        }
        // 读取下一个视频帧
        readNextFrame(frameSource);
        // 处理视频帧
        if (procPos_ < storePos_)
        {
            ++procPos_;
            processFrame(procPos_);
        }
        ++outPos_;
        // 调用ocl_processImpl函数执行超分
        CV_OCL_RUN(isUmat_,
                   ocl_processImpl(frameSource, _output))

        const Mat& curOutput = at(outPos_, outputs_);

        if (_output.kind() < _InputArray::OPENGL_BUFFER || _output.isUMat())
            curOutput.convertTo(_output, CV_8U);
        else
        {
            curOutput.convertTo(finalOutput_, CV_8U);
            arrCopy(finalOutput_, _output);
        }
    }

3、光流检测运动矢量

接着看readNextFrame()的实现,调用光流检测算法来计算运动矢量:

void BTVL1::readNextFrame(Ptr<FrameSource>& frameSource)
    {
        frameSource->nextFrame(curFrame_);
        if (curFrame_.empty())
            return;

        ++storePos_;

        CV_OCL_RUN(isUmat_,
                   ocl_readNextFrame(frameSource))

        curFrame_.convertTo(at(storePos_, frames_), CV_32F);
        // 结合上一帧和当前帧计算运动矢量
        if (storePos_ > 0)
        {
            opticalFlow_->calc(prevFrame_, curFrame_, at(storePos_ - 1, forwardMotions_));
            opticalFlow_->calc(curFrame_, prevFrame_, at(storePos_, backwardMotions_));
        }

        curFrame_.copyTo(prevFrame_);
    }

4、处理超分

processFrame()函数负责处理视频帧:

void BTVL1::processFrame(int idx)
    {
        CV_OCL_RUN(isUmat_,
                   ocl_processFrame(idx))

        const int startIdx = std::max(idx - temporalAreaRadius_, 0);
        const int procIdx = idx;
        const int endIdx = std::min(startIdx + 2 * temporalAreaRadius_, storePos_);

        const int count = endIdx - startIdx + 1;

        srcFrames_.resize(count);
        srcForwardMotions_.resize(count);
        srcBackwardMotions_.resize(count);

        int baseIdx = -1;

        for (int i = startIdx, k = 0; i <= endIdx; ++i, ++k)
        {
            if (i == procIdx)
                baseIdx = k;

            srcFrames_[k] = at(i, frames_);

            if (i < endIdx)
                srcForwardMotions_[k] = at(i, forwardMotions_);
            if (i > startIdx)
                srcBackwardMotions_[k] = at(i, backwardMotions_);
        }
        // 根据前后方向的运动矢量来处理视频帧
        process(srcFrames_, at(idx, outputs_), srcForwardMotions_, srcBackwardMotions_, baseIdx);
    }
}

在计算得到前后方向的运动矢量后,调用BTVL1_Base基类的process()函数来处理:

    void BTVL1_Base::process(InputArrayOfArrays _src, OutputArray _dst, InputArrayOfArrays _forwardMotions,
                             InputArrayOfArrays _backwardMotions, int baseIdx)
    {
        CV_OCL_RUN(_src.isUMatVector() && _dst.isUMat() && _forwardMotions.isUMatVector() &&
                   _backwardMotions.isUMatVector(),
                   ocl_process(_src, _dst, _forwardMotions, _backwardMotions, baseIdx))

        std::vector<Mat> & src = *(std::vector<Mat> *)_src.getObj(),
                & forwardMotions = *(std::vector<Mat> *)_forwardMotions.getObj(),
                & backwardMotions = *(std::vector<Mat> *)_backwardMotions.getObj();

        // 更新运动模糊(高斯滤波)、 btv权重
        if (blurKernelSize_ != curBlurKernelSize_ || blurSigma_ != curBlurSigma_ || src[0].type() != curSrcType_)
        {
            //filter_ = createGaussianFilter(src[0].type(), Size(blurKernelSize_, blurKernelSize_), blurSigma_);
            curBlurKernelSize_ = blurKernelSize_;
            curBlurSigma_ = blurSigma_;
            curSrcType_ = src[0].type();
        }

        if (btvWeights_.empty() || btvKernelSize_ != curBtvKernelSize_ || alpha_ != curAlpha_)
        {
            calcBtvWeights(btvKernelSize_, alpha_, btvWeights_);
            curBtvKernelSize_ = btvKernelSize_;
            curAlpha_ = alpha_;
        }

        // 计算相对运动
        calcRelativeMotions(forwardMotions, backwardMotions, lowResForwardMotions_, lowResBackwardMotions_, baseIdx, src[0].size());
        // 对运动矢量进行放大
        upscaleMotions(lowResForwardMotions_, highResForwardMotions_, scale_);
        upscaleMotions(lowResBackwardMotions_, highResBackwardMotions_, scale_);

        forwardMaps_.resize(highResForwardMotions_.size());
        backwardMaps_.resize(highResForwardMotions_.size());
        for (size_t i = 0; i < highResForwardMotions_.size(); ++i)
            buildMotionMaps(highResForwardMotions_[i], highResBackwardMotions_[i], forwardMaps_[i], backwardMaps_[i]);


        const Size lowResSize = src[0].size();
        const Size highResSize(lowResSize.width * scale_, lowResSize.height * scale_);

        resize(src[baseIdx], highRes_, highResSize, 0, 0, INTER_CUBIC);

        diffTerm_.create(highResSize, highRes_.type());
        a_.create(highResSize, highRes_.type());
        b_.create(highResSize, highRes_.type());
        c_.create(lowResSize, highRes_.type());

        for (int i = 0; i < iterations_; ++i)
        {
            diffTerm_.setTo(Scalar::all(0));

            for (size_t k = 0; k < src.size(); ++k)
            {
                // a = M * Ih
                remap(highRes_, a_, backwardMaps_[k], noArray(), INTER_NEAREST);
                // 高斯模糊 b = HM * Ih
                GaussianBlur(a_, b_, Size(blurKernelSize_, blurKernelSize_), blurSigma_);
                // c = DHM * Ih
                resize(b_, c_, lowResSize, 0, 0, INTER_NEAREST);

                diffSign(src[k], c_, c_);

                // 超分运算 a = Dt * diff
                upscale(c_, a_, scale_);
                // b = HtDt * diff
                GaussianBlur(a_, b_, Size(blurKernelSize_, blurKernelSize_), blurSigma_);
                // a = MtHtDt * diff
                remap(b_, a_, forwardMaps_[k], noArray(), INTER_NEAREST);

                add(diffTerm_, a_, diffTerm_);
            }

            if (lambda_ > 0)
            {
                calcBtvRegularization(highRes_, regTerm_, btvKernelSize_, btvWeights_, ubtvWeights_);
                addWeighted(diffTerm_, 1.0, regTerm_, -lambda_, 0.0, diffTerm_);
            }

            addWeighted(highRes_, 1.0, diffTerm_, tau_, 0.0, highRes_);
        }

        Rect inner(btvKernelSize_, btvKernelSize_, highRes_.cols - 2 * btvKernelSize_, highRes_.rows - 2 * btvKernelSize_);
        highRes_(inner).copyTo(_dst);
    }

我们再看下upscaleMotions()函数的实现:

void upscaleMotions(InputArrayOfArrays _lowResMotions, OutputArrayOfArrays _highResMotions, int scale)
    {
        CV_OCL_RUN(_lowResMotions.isUMatVector() && _highResMotions.isUMatVector(),
                   ocl_upscaleMotions(_lowResMotions, _highResMotions, scale))

        std::vector<Mat> & lowResMotions = *(std::vector<Mat> *)_lowResMotions.getObj(),
                & highResMotions = *(std::vector<Mat> *)_highResMotions.getObj();

        highResMotions.resize(lowResMotions.size());

        for (size_t i = 0; i < lowResMotions.size(); ++i)
        {
            // 三次样条差值
            resize(lowResMotions[i], highResMotions[i], Size(), scale, scale, INTER_CUBIC);
            // 运动矢量与缩放因子相乘
            multiply(highResMotions[i], Scalar::all(scale), highResMotions[i]);
        }
    }

接着是upscale()超分函数的实现:

    void upscale(InputArray _src, OutputArray _dst, int scale)
    {
        int cn = _src.channels();
        CV_Assert( cn == 1 || cn == 3 || cn == 4 );

        CV_OCL_RUN(_dst.isUMat(),
                   ocl_upscale(_src, _dst, scale))

        typedef void (*func_t)(InputArray src, OutputArray dst, int scale);
        static const func_t funcs[] =
        {
            0, upscaleImpl<float>, 0, upscaleImpl<Point3f>, upscaleImpl<Point4f>
        };

        const func_t func = funcs[cn];
        CV_Assert(func != 0);
        func(_src, _dst, scale);
    }

最终是调用upscaleImpl()进行超分运算,属于模板函数:文章来源地址https://www.toymoban.com/news/detail-495246.html

    template <typename T>
    void upscaleImpl(InputArray _src, OutputArray _dst, int scale)
    {
        Mat src = _src.getMat();
        _dst.create(src.rows * scale, src.cols * scale, src.type());
        _dst.setTo(Scalar::all(0));
        Mat dst = _dst.getMat();

        for (int y = 0, Y = 0; y < src.rows; ++y, Y += scale)
        {
            const T * const srcRow = src.ptr<T>(y);
            T * const dstRow = dst.ptr<T>(Y);

            for (int x = 0, X = 0; x < src.cols; ++x, X += scale)
                dstRow[X] = srcRow[x];
        }
    }

到了这里,关于探索OpenCV的光流算法视频超分与源码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 常用视频目标跟踪算法仿真对比:帧间差分法,背景差分法,光流法,Meanshift

    目录 1.软件版本 2.本算法理论知识 帧差法  背景差分法 光流法

    2023年04月08日
    浏览(85)
  • SRGAN图像超分重建算法Python实现(含数据集代码)

    摘要:本文介绍深度学习的SRGAN图像超分重建算法,使用 P y t h o n 以及 P y t o r c h 框架实现,包含完整训练、测试代码,以及训练数据集文件。博文介绍图像超分算法的原理,包括生成对抗网络和SRGAN模型原理和实现的代码,同时结合具体内容进行解释说明,完整代码资源文

    2024年02月12日
    浏览(39)
  • 使用Python基于opencv实现多视频画面拼接融合算法demo

    单个相机视频画面尺寸有限,在需要全景展示的场景下,就需要将多个相机视频进行拼接融合,得到一张全景图。本文基于opencv实现一个视频拼接的demo,熟悉视频拼接流程和opencv接口。 直接上代码吧,注释还是比较清楚的: 该demo使用ORB算法检测关键点,使用BFMatcher进行特征

    2024年02月04日
    浏览(48)
  • OpenCV | 光流估计

    光流估计 光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”,根据各个像素点的速度的速度矢量特征,可以对图像进行动态分析,例如目标跟踪 高度恒定:同一点随着时间的变化,其亮度不会发生改变。 小运动:随着时间的变化不会引起位置的剧烈变化,只

    2024年02月02日
    浏览(31)
  • OpenCV实战(28)——光流估计

    当相机进行拍摄时,拍摄到的亮度图案会投射到图像传感器上,从而形成图像。在视频序列中,我们通常需要捕捉运动模式,即不同场景元素的 3D 运动在图像平面上的投影,这种投影 3D 运动矢量的图像称为运动场 ( motion field )。但是,我们无法从相机传感器直接测量场景点的

    2024年02月13日
    浏览(33)
  • python使用opencv提取光流

    光流flow特征中包含了一个视频当中运动相关的信息,在视频动作定位当中光流特征使用的比较多,所以记录一下提取光流特征的方法。 使用的方法是TVL1方法,最终提取的光流图片还可以配合I3D模型进行特征的提取。光流的计算先需要将视频一帧一帧提取出来,然后再通过连

    2024年02月05日
    浏览(39)
  • OpenCV 中的光流 (C++/Python)

    光流是一项视频中两个连续帧之间每像素运动估计的任务。基本上,光流任务意味着计算像素的位移矢量作为两个相邻图像之间的对象位移差。光流的主要思想是估计物体由其运动或相机运动引起的位移矢量。 理论基础 假设我们有一个灰度图像——具有像素强度的矩阵。我

    2024年02月13日
    浏览(34)
  • 常用的超分算法

    超分算法(Super-resolution algorithm)是一种图像处理算法,旨在从低分辨率(Low-resolution,LR)图像重建出高分辨率(High-resolution,HR)图像。它通过利用图像中的信息和先验知识,推测和还原图像中丢失的细节,从而增加图像的清晰度和细节级别。 以下是几种常见的超分算法:

    2024年02月04日
    浏览(36)
  • 18.Lucas-Kanade光流及OpenCV中的calcOpticalFlowPyrLK

    欢迎访问个人网络日志🌹🌹知行空间🌹🌹 光流描述了像素在图像中的运动,就像彗星☄划过天空中流动图像。同一个像素,随着时间的流逝,会在图像中运动,光流法就是追踪它的运动过程。 光流法根据追踪的像素数又可以分成 稀疏光流法 和 稠密光流法 。 稀疏光流法

    2024年02月13日
    浏览(44)
  • FPGA多路视频叠加融合 HLS算法实现 提供2套工程源码和技术支持

    视频叠加和融合在FPGA图像处理领域有着广泛应用,但其复杂的内存访问机制和视频叠加透明度的融合,使得实现难度很大,让很多FPGA工程师望而却步,在目前的技术条件下,使用HLS实现视频叠加融合是最简单方便的实现方式,本设计也是基于此实现。 本设计提供2套vivado工程

    2024年02月12日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包