最近在做超分辨相关的东西,从网上了解到OpenCV有自带的超分辨算法,于是就有了下面这些尝试。

1、利用OpenCV驱动USB摄像头拍摄视频以及读取视频

        读取视频文件或者摄像头视频需要使用OpenCV中的VideoCapture类,保存视频或者摄像头视频到本地磁盘,需要使用OpenCV中的VideoWriter类,使用非常简单。

首先来看一下如何VideoWriter类:

VideoWriter(const string& filename, int fourcc, double fps, Size frameSize, bool isColor=true);

需要强调的参数是fourcc,它代表了所使用的编码方式,支持的编码器如下:

CV_FOURCC('P','I','M','1') = MPEG-1 codec
CV_FOURCC('M','J','P','G') = motion-jpeg codec
CV_FOURCC('M', 'P', '4', '2') = MPEG-4.2 codec
CV_FOURCC('D', 'I', 'V', '3') = MPEG-4.3 codec
CV_FOURCC('D', 'I', 'V', 'X') = MPEG-4 codec
CV_FOURCC('U', '2', '6', '3') = H263 codec
CV_FOURCC('I', '2', '6', '3') = H263I codec
CV_FOURCC('F', 'L', 'V', '1') = FLV1 codec

下面是一个驱动摄像头采集视频的程序(另外,貌似OpenCV只支持存储为.avi格式的视频):

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main( int argc, char **argv )
{
    //读取视频文件或者摄像头视频需要使用VideoCapture
    VideoCapture video_cap;
    video_cap.open( 0 );

    if( !video_cap.isOpened() )
    {
        cout << "can not open camera!" << endl;
        return -1;
    }

    Size size = Size( video_cap.get(CV_CAP_PROP_FRAME_WIDTH), video_cap.get(CV_CAP_PROP_FRAME_HEIGHT) );

    //保存视频或者摄像头视频到本地需要使用VideoWriter
    VideoWriter video_writer;
    video_writer.open( "video_path.avi", CV_FOURCC('M', 'P', '4', '2'), 10, size, true );

    Mat frame;

    namedWindow( "output", CV_WINDOW_AUTOSIZE );

    while( video_cap.read(frame) )
    {
        imshow( "output", frame );
        //将当前这一帧写入本地文件
        video_writer.write( frame );    
        waitKey( 10 );
    }

    video_cap.release();
    return 0;
}

2、超分辨算法的输入

OpenCV内部的超分辨模块有cpu版本和gpu版本两种,如果要使用gpu版本的要从源码编译支持cuda的OpenCV,现在还没有重新编译,使用cpu处理的速度非常的感人,第一帧要6秒左右,接下来一帧大概2秒左右。

还有一个要注意的就是设置超分辨cv::superres::SuperResolution的input的时候,它的input是FrameSource,也就是一个帧序列,这个帧序列是读取视频的时候生成的。我查了一下OpenCV的官方手册,好像没有处理单张的接口。所以我就采用上面保存视频到本地的方式,使用VideoWriter将要进行超分辨的图像写入到一个.avi的视频文件中(只写入一张图像),这样就相当对单张图像进行超分辨。(貌似这种方法有点傻乎乎的,不过暂时也还没有想到好点的方法,先实现在说吧)。

3、然后就是使用OpneCV的超分辨算法了

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

#include "opencv2/core.hpp"
#include "opencv2/core/utility.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;

//宏定义伪函数
#define MEASURE_TIME(op) \
    { \
        TickMeter tm; \
        tm.start(); \
        op; \
        tm.stop(); \
        cout << tm.getTimeSec() << " sec" << endl; \
    }

static Ptr<cv::superres::DenseOpticalFlowExt> createOptFlow(const string& name, bool useGpu)
{
    if (name == "farneback")
    {
        if (useGpu)
            return cv::superres::createOptFlow_Farneback_CUDA();
        else
            return cv::superres::createOptFlow_Farneback();
    }
    /*else if (name == "simple")
        return createOptFlow_Simple();*/
    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>();
}

int main( int argc, const char* argv[] )
{
    /*定义参数*/
    const string inputVideoName = "sr_input_by_read.avi";
    const int scale = 2;
    const int iterations = 5;
    const int temporalAreaRadius = 4;
    const string optFlow = "farneback";  //使用的算法:farneback, tvl1, brox, pyrlk
    const bool gpu = false;

    Ptr<SuperResolution> superRes;

    if ( gpu )
        superRes = createSuperResolution_BTVL1_CUDA();
    else
        superRes = createSuperResolution_BTVL1();

    //optFlow指定使用的超分辨算法
    Ptr<cv::superres::DenseOpticalFlowExt> of = createOptFlow(optFlow, gpu);

    if (of.empty())
        return EXIT_FAILURE;

    //设置使用的超分辨算法
    superRes->setOpticalFlow( of );
    superRes->setScale(scale);
    superRes->setIterations(iterations);
    superRes->setTemporalAreaRadius(temporalAreaRadius);
    
    Ptr<FrameSource> frameSource;
    //设置要使用的超分辨算法
    if( gpu )
    {
        //如果要使用gpu的话,要将视频进行gpu编码
        try
        {
            frameSource = createFrameSource_Video_CUDA(inputVideoName);
            Mat frame;
            frameSource->nextFrame(frame);
        }
        catch (const cv::Exception&)
        {
            frameSource.release();
        }
    }
    
    if (!frameSource)
    {
        frameSource = createFrameSource_Video(inputVideoName);
    }

    // skip first frame, it is usually corrupted
    Mat frame;
    frameSource->nextFrame(frame);

    superRes->setInput(frameSource);

    VideoWriter writer;

    for (int i = 0;; ++i)
    {
        //cout << flush; 无条件的将缓冲区中的输出信息送至显示器
        cout << '[' << setw(3) << i << "] : " << flush;
        Mat result, src_frame;

        frameSource->nextFrame( src_frame );
        resize( src_frame, src_frame, Size(src_frame.cols*2, src_frame.rows*2) );

        //nextFrame(result)的作用是处理下一帧,同时利用result返回当前真的额处理结果
        MEASURE_TIME(superRes->nextFrame(result));

        if (result.empty())
            break;

        imshow("src_frame", src_frame);
        imshow("Super Resolution", result);
        waitKey( 0 );
    }

    return 0;
}