探索OpenCV的AI实现视频超分

OpenCV除了使用光流算法与普通插值实现图像视频超分,还提供AI深度学习实现视频超分。算法模型包括:edsr、espcn、fsrcnn、lapsrn,实现超分的倍数有2、3、4、8。通过AI实现的视频超分比传统算法的效果更好,图像更清晰。

1、超分算法对比

在opencv_contrib外置库的dnn_superres模块,就是用AI实现的图像/视频超分。接下来,我们对比AI算法、双三次插值、最近邻插值、Lanczos插值的超分效果。通过计算图像的PSNR、SSIM来评估超分质量。

#include <iostream>
#include <opencv2/opencv_modules.hpp>
#include <opencv2/dnn_superres.hpp>
#include <opencv2/quality.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;
using namespace dnn_superres;

static Vec2d getQualityValues(Mat orig, Mat upsampled)
{
    double psnr = PSNR(upsampled, orig);
    Scalar q = quality::QualitySSIM::compute(upsampled, orig, noArray());
    double ssim = mean(Vec3d((q[0]), q[1], q[2]))[0];
    return Vec2d(psnr, ssim);
}

int main(int argc, char *argv[])
{
    if (argc < 4) {
        cout << "usage:";
        cout << "Arg 1: image path  | Path to image\n";
        cout << "Arg 2: algorithm | edsr, espcn, fsrcnn or lapsrn\n";
        cout << "Arg 3: path to model file \n";
        cout << "Arg 4: scale  | 2, 3, 4 or 8 \n";
        cout << "-----------------------------------------------" << endl;
        return -1;
    }
    string path = string(argv[1]);
    string algorithm = string(argv[2]);
    string model = string(argv[3]);
    int scale = atoi(argv[4]);

    Mat img = imread(path);
    if (img.empty()) {
        cerr << "Couldn't load image: " << img << "\n";
        return -2;
    }

    // 裁剪图像
    int width = img.cols - (img.cols % scale);
    int height = img.rows - (img.rows % scale);
    Mat cropped = img(Rect(0, 0, width, height));

    Mat img_downscaled;
    resize(cropped, img_downscaled, Size(), 1.0 / scale, 1.0 / scale);

    Mat img_new;
    DnnSuperResImpl sr;
    vector <Mat> allImages;

    // 读取模型:以ESPCN为例,"models/ESPCN_x2.pb"
    sr.readModel(model);
    // 设置超分算法、超分倍数
    sr.setModel(algorithm, scale);
    sr.upsample(img_downscaled, img_new);

    vector<double> psnrValues = vector<double>();
    vector<double> ssimValues = vector<double>();

    // 1、深度学习模型
    Vec2f quality = getQualityValues(cropped, img_new);

    psnrValues.push_back(quality[0]);
    ssimValues.push_back(quality[1]);

    cout << sr.getAlgorithm() << ":" << endl;
    cout << "PSNR: " << quality[0] << " SSIM: " << quality[1] << endl;

    // 2、双三次插值
    Mat bicubic;
    resize(img_downscaled, bicubic, Size(), scale, scale, INTER_CUBIC);
    quality = getQualityValues(cropped, bicubic);

    psnrValues.push_back(quality[0]);
    ssimValues.push_back(quality[1]);

    cout << "Bicubic " << endl;
    cout << "PSNR: " << quality[0] << " SSIM: " << quality[1] << endl;

    // 3、最近邻插值
    Mat nearest;
    resize(img_downscaled, nearest, Size(), scale, scale, INTER_NEAREST);
    quality = getQualityValues(cropped, nearest);

    psnrValues.push_back(quality[0]);
    ssimValues.push_back(quality[1]);

    cout << "Nearest neighbor" << endl;
    cout << "PSNR: " << quality[0] << " SSIM: " << quality[1] << endl;

    // 4、LANCZOS插值
    Mat lanczos;
    resize(img_downscaled, lanczos, Size(), scale, scale, INTER_LANCZOS4);
    quality = getQualityValues(cropped, lanczos);

    psnrValues.push_back(quality[0]);
    ssimValues.push_back(quality[1]);

    cout << "Lanczos" << endl;
    cout << "PSNR: " << quality[0] << " SSIM: " << quality[1] << endl;

    return 0;
}

2、超分效果对比

以4倍超分为例,各个算法实现超分的效果如下图所示。可以看到双三次插值、最近邻插值、Lanczos插值的图像有马赛克方块,而通过AI实现超分的图像比较清晰。

 具体的耗时、PSNR、SSIM数据,如下表所示。可以看到,传统算法的耗时很少,PSNR值在26左右。而AI算法的耗时比较高,其中EDSR模型的耗时有1.6s,PSNR值最高(超过28)。

 3、AI视频超分

通过VideoCapture来读取视频帧,然后使用DnnSuperResImpl实现逐帧超分,接着用VideoWriter写视频文件。完整的示例代码如下:

int main(int argc, char *argv[])
{
    if (argc < 4) {
        cout << "usage:   Arg 1: input video path" << endl;
        cout << "\t Arg 2: output video path" << endl;
        cout << "\t Arg 3: algorithm | edsr, espcn, fsrcnn or lapsrn" << endl;
        cout << "\t Arg 4: scale     | 2, 3, 4 or 8 \n";
        cout << "\t Arg 5: path to model file \n";
        return -1;
    }

    string input_path = string(argv[1]);
    string output_path = string(argv[2]);
    string algorithm = string(argv[3]);
    int scale = atoi(argv[4]);
    string path = string(argv[5]);

    VideoCapture input_video(input_path);
    int ex = static_cast<int>(input_video.get(CAP_PROP_FOURCC));
    Size S = Size((int) input_video.get(CAP_PROP_FRAME_WIDTH) * scale,
                  (int) input_video.get(CAP_PROP_FRAME_HEIGHT) * scale);

    VideoWriter output_video;
    output_video.open(output_path, ex, input_video.get(CAP_PROP_FPS), S, true);

    if (!input_video.isOpened())
    {
        std::cerr << "Could not open the video." << std::endl;
        return -1;
    }
    // 读取超分模型、设置超分倍数
    DnnSuperResImpl sr;
    sr.readModel(path);
    sr.setModel(algorithm, scale);

    for(;;)
    {
        Mat frame, output_frame;
        // 读取视频帧
        input_video >> frame;

        if ( frame.empty() )
            break;
        // 执行超分
        sr.upsample(frame, output_frame);
        // 写入超分后的视频帧
        output_video << output_frame;

        char c=(char)waitKey(25);
        if(c==27)
            break;
    }

    input_video.release();
    output_video.release();

    return 0;
}

4、AI超分源码

首先把图像转换为浮点格式,然后拆分YCbCr通道,传入深度学习网络进行超分,最后重建图像。源码如下:

void DnnSuperResImpl::upsample(InputArray img, OutputArray result)
{
    if (net.empty())
        CV_Error(Error::StsError, "Model not specified. Please set model via setModel().");

    if (this->alg == "espcn" || this->alg == "lapsrn" || this->alg == "fsrcnn")
    {
        // 预处理: 转成浮点格式
        Mat preproc_img;
        preprocess_YCrCb(img, preproc_img);

        // 拆分通道,仅用Y通道进行推理
        Mat ycbcr_channels[3];
        split(preproc_img, ycbcr_channels);

        Mat Y = ycbcr_channels[0];

        // 创建blob
        cv::Mat blob;
        dnn::blobFromImage(Y, blob, 1.0);

        // 使用神经网络进行超分
        this->net.setInput(blob);
        Mat blob_output = this->net.forward();

        // 从blob转换回image
        std::vector <Mat> model_outs;
        dnn::imagesFromBlob(blob_output, model_outs);
        Mat out_img = model_outs[0];

        // 重建图像: 对Cr、Cb进行超分,融合第三层网络
        reconstruct_YCrCb(out_img, preproc_img, result, this->sc);
    }
    else if (this->alg == "edsr")
    {
        // Div2K数据集的平均值
        Scalar mean = Scalar(103.1545782, 111.561547, 114.35629928);

        // 转成浮点格式
        Mat float_img;
        img.getMat().convertTo(float_img, CV_32F, 1.0);

        // 创建blob、抽取数据集的平均值
        cv::Mat blob;
        dnn::blobFromImage(float_img, blob, 1.0, Size(), mean);

        // 使用神经网络进行超分
        this->net.setInput(blob);
        Mat blob_output = this->net.forward();

        // 从blob转换回image
        std::vector <Mat> model_outs;
        dnn::imagesFromBlob(blob_output, model_outs);

        // 后处理: 添加平均值
        Mat(model_outs[0] + mean).convertTo(result, CV_8U);
    }
    else
    {
        CV_Error(cv::Error::StsNotImplemented, String("Unknown/unsupported superres algorithm: ") + this->alg);
    }
}

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要使用Qt和OpenCV实现录制视频的功能,可以参考以下步骤: 1. 首先,确保你已经安装了Qt和OpenCV,并且配置好了开发环境。 2. 创建一个Qt项目,并在项目中包含OpenCV的头文件和库文件。 3. 在项目中创建一个Widget类,继承自QWidget,并在头文件中添加必要的成员变量和函数。 4. 在Widget类的构造函数中初始化摄像头,并设置视频的帧率、宽度和高度。 5. 创建一个定时器对象,并连接到一个槽函数readFrame(),用于读取摄像头的每一帧图像。 6. 在readFrame()函数中,使用OpenCV的VideoCapture类读取摄像头的图像,并进行帧差法处理,判断是否存在物体运动。 7. 如果存在物体运动,可以选择保存当前帧的图像或者将当前帧写入视频文件。 8. 在Widget类的析构函数中释放摄像头和定时器对象。 9. 在Qt的界面中添加一个按钮,用于开始和停止录制视频。 10. 在按钮的槽函数中,根据按钮的状态来控制录制视频的开始和停止。 以上是一个简单的实现录制视频的步骤,具体的代码实现可以参考引用\[1\]中的示例代码。希望对你有帮助! #### 引用[.reference_title] - *1* *2* *3* [[视觉实战案例]Qt+OpenCV实现USB摄像头监测移动物体并录制视频功能(帧差法)](https://blog.csdn.net/fengyaowuhui/article/details/124656844)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐福记456

您的鼓励和肯定是我创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值