• 对OpenCV项目中由微信团队提供的超分辨率算法初步研究


    一、基本情况

    1.1 超分辨率初体验

    简单来讲,图像超分辨率就是提高图像的空间分辨率,例如将一幅图片的分辨率由352x288扩大到704x576,方便用户在大尺寸的显示设备上观看。图像的超分辨率,是图像处理相关问题中的基础问题之一,并具有广泛的实际需求和应用场景,在数字成像技术,视频编码通信技术,深空卫星遥感技术,目标识别分析技术和医学影像分析技术等方面,视频图像超分辨率技术都能够应对显示设备分辨率大于图像源分辨率的问题。

    简单来说超分辨率技术可以分为以下两种:

    1)只参考当前低分辨率图像,不依赖其他相关图像的超分辨率技术,称之为单幅图像的超分辨率(single image super resolution),也可以称之为图像插值(image interpolation);

    2)参考多幅图像或多个视频帧的超分辨率技术,称之为多帧视频/多图的超分辨率(multi-frame super resolution)。

     
    这两类技术中,一般来讲后者相比于前者具有更多的可参考信息,并具有更好的高分辨率视频图像的重建质量,但是其更高的计算复杂度也限制了其应用。

    1.2 超分辨率理论描述

    这个很直观的超分辨率问题,它的理论描述又是什么样子的呢?如下图所示,超分辨率就是将左图中像素点之间的空间位置用像素点进行填充,使得整个图像具有更多的像素点,更丰富的细节,从信号的角度讲就是补充出更多的高频成分。

    通常在处理这个超分辨率问题的时候,我们常常探索这个退化信号是如何从我们希望的理想信号变化得到的(即分辨率的退化过程),如果对退化过程进行精确地描述,往往对其逆问题的求解有重要的意义。

    在本文的问题中,即超分辨率的退化模型,可以通过以下公式来描述:

    Y = HDX + n

    其中Y为低分辨率的视频帧/图像,X为我们理想高分辨率的视频帧/图像,而H和D分别为模糊算子和分辨率下采样算子,n为退化过程中产生的噪声。 

    二、算法调用


    processImageScale是对外接口,superResolutionScale是内部函数。

    当缩小时,使用resize+INTER_AREA,当放大倍率在1.5-2.0时,使用resize+INTER_CUBIC,当放大倍率>2的时候,调用superResolutionsScale。
    其中 ,net_loaded_表面超分算法有没有被正确调用。

    superResolutionScale则是较为典型的前向调用方法。

    三、实际测试
    1、找到调用文件
    参考《微信二维码引擎OpenCV开源!3行代码让你拥有微信扫码能力》

    超分代码:https://github.com/opencv/opencv_contrib/blob/master/modules/wechat_qrcode/src/wechat_qrcode.cpp#L68
    模型文件:
    https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/sr.prototxt
    参数文件:
    https://github.com/WeChatCV/opencv_3rdparty/blob/wechat_qrcode/sr.caffemodel


        Mat src = imread("e:/template/bj1.png"); Mat dst;
        std::shared_ptr<SuperScale> sr = std::make_shared<SuperScale>();//智能指针
        sr->init("E:/template/sr.prototxt""E:/template/sr.caffemodel");
        bool use_nn_sr = true;
        sr->processImageScale(src, dst, 3, use_nn_sr);

    细节更平滑

    2、确定比对方法
        SSIM(Structural Similarity),结构相似性,是一种衡量两幅图像相似度的指标。该指标首先由德州大学奥斯丁分校的图像和视频工程实验室(Laboratory for Image and Video Engineering)提出。SSIM使用的两张图像中,一张为未经压缩的无失真图像,另一张为失真后的图像。

    path-to-opencvsourcesdoc utorialsvideoiovideo-input-psnr-ssim

    Scalar getMSSIMconst Mati1const Mati2)
    {
        const double C1 = 6.5025, C2 = 58.5225;
        /***************************** INITS **********************************/
        int d = CV_32F;
        Mat I1I2;
        i1.convertTo(I1d);            // cannot calculate on one byte large values
        i2.convertTo(I2d);
        Mat I2_2   = I2.mul(I2);        // I2^2
        Mat I1_2   = I1.mul(I1);        // I1^2
        Mat I1_I2  = I1.mul(I2);        // I1 * I2
        /*************************** END INITS **********************************/
        Mat mu1mu2;                   // PRELIMINARY COMPUTING
        GaussianBlur(I1mu1Size(11, 11), 1.5);
        GaussianBlur(I2mu2Size(11, 11), 1.5);
        Mat mu1_2   =   mu1.mul(mu1);
        Mat mu2_2   =   mu2.mul(mu2);
        Mat mu1_mu2 =   mu1.mul(mu2);
        Mat sigma1_2sigma2_2sigma12;
        GaussianBlur(I1_2sigma1_2Size(11, 11), 1.5);
        sigma1_2 -= mu1_2;
        GaussianBlur(I2_2sigma2_2Size(11, 11), 1.5);
        sigma2_2 -= mu2_2;
        GaussianBlur(I1_I2sigma12Size(11, 11), 1.5);
        sigma12 -= mu1_mu2;
        ///////////////////////////////// FORMULA ////////////////////////////////
        Mat t1t2t3;
        t1 = 2 * mu1_mu2 + C1;
        t2 = 2 * sigma12 + C2;
        t3 = t1.mul(t2);                 // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
        t1 = mu1_2 + mu2_2 + C1;
        t2 = sigma1_2 + sigma2_2 + C2;
        t1 = t1.mul(t2);                 // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
        Mat ssim_map;
        divide(t3t1ssim_map);        // ssim_map =  t3./t1;
        Scalar mssim = mean(ssim_map);   // mssim = average of ssim map
        return mssim;
    }

    3、获得定量结果 
        思路就是对于一张分辨率比较大的图片,将其按照不同比例变小后,分别用resize和超分的方法进行放大,计算ssim和运行时间。
    代码内容如下,其中倍率部分是手工修改的:
    Scalar getMSSIM(const Mat& i1, const Mat& i2)
    {
        const double C1 = 6.5025, C2 = 58.5225;
        /***************************** INITS **********************************/
        int d = CV_32F;
        Mat I1, I2;
        i1.convertTo(I1, d);            // cannot calculate on one byte large values
        i2.convertTo(I2, d);
        Mat I2_2 = I2.mul(I2);        // I2^2
        Mat I1_2 = I1.mul(I1);        // I1^2
        Mat I1_I2 = I1.mul(I2);        // I1 * I2
        /*************************** END INITS **********************************/
        Mat mu1, mu2;                   // PRELIMINARY COMPUTING
        GaussianBlur(I1, mu1, Size(1111), 1.5);
        GaussianBlur(I2, mu2, Size(1111), 1.5);
        Mat mu1_2 = mu1.mul(mu1);
        Mat mu2_2 = mu2.mul(mu2);
        Mat mu1_mu2 = mu1.mul(mu2);
        Mat sigma1_2, sigma2_2, sigma12;
        GaussianBlur(I1_2, sigma1_2, Size(1111), 1.5);
        sigma1_2 -= mu1_2;
        GaussianBlur(I2_2, sigma2_2, Size(1111), 1.5);
        sigma2_2 -= mu2_2;
        GaussianBlur(I1_I2, sigma12, Size(1111), 1.5);
        sigma12 -= mu1_mu2;
        ///////////////////////////////// FORMULA ////////////////////////////////
        Mat t1, t2, t3;
        t1 = 2 * mu1_mu2 + C1;
        t2 = 2 * sigma12 + C2;
        t3 = t1.mul(t2);                 // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
        t1 = mu1_2 + mu2_2 + C1;
        t2 = sigma1_2 + sigma2_2 + C2;
        t1 = t1.mul(t2);                 // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
        Mat ssim_map;
        divide(t3, t1, ssim_map);        // ssim_map =  t3./t1;
        Scalar mssim = mean(ssim_map);   // mssim = average of ssim map
        return mssim;
    }
    int main(int argc, char* argv[])
    {
        //对于一张分辨率比较大的图片,
        Mat src = imread("E:/template/test_3000X2000.bmp"); Mat dstResize; Mat dstSF;
        Mat temp;
        
        //将其按照不同比例变小后
        resize(src, temp, cv::Size(750500));
        std::shared_ptr<SuperScale> sr = std::make_shared<SuperScale>();//智能指针
        sr->init("E:/template/sr.prototxt""E:/template/sr.caffemodel");
        bool use_nn_sr = true;
        //主运算,并打印运算时间
        double t1 = (double)getTickCount();
        resize(temp, dstResize, cv::Size(30002000));
        double t2 = (double)getTickCount();
        sr->processImageScale(temp, dstSF,6, use_nn_sr);
        double t3 = (double)getTickCount();
        double timeResize = (t2 - t1) / getTickFrequency();
        double timeSF = (t3 - t2) / getTickFrequency();
        cout << "Resize耗时" << timeResize << "s " << "SF耗时" << timeSF << "s" << endl;
        /////计算并打印ssim
        Scalar mssimV = getMSSIM(src, src);
        cout << "自身对比结果: "
            << " R " << setiosflags(ios::fixed) << setprecision(2<< mssimV.val[2* 100 << "%"
            << " G " << setiosflags(ios::fixed) << setprecision(2<< mssimV.val[1* 100 << "%"
            << " B " << setiosflags(ios::fixed) << setprecision(2<< mssimV.val[0* 100 << "%" << endl;
        mssimV = getMSSIM(src, dstResize);
        cout << " resize结果: "
            << " R " << setiosflags(ios::fixed) << setprecision(2<< mssimV.val[2* 100 << "%"
            << " G " << setiosflags(ios::fixed) << setprecision(2<< mssimV.val[1* 100 << "%"
            << " B " << setiosflags(ios::fixed) << setprecision(2<< mssimV.val[0* 100 << "%" << endl;
        mssimV = getMSSIM(src, dstSF);
        cout << " sf结果: "
            << " R " << setiosflags(ios::fixed) << setprecision(2<< mssimV.val[2* 100 << "%"
            << " G " << setiosflags(ios::fixed) << setprecision(2<< mssimV.val[1* 100 << "%"
            << " B " << setiosflags(ios::fixed) << setprecision(2<< mssimV.val[0* 100 << "%";
        waitKey();
    }

    在Release模式下,4倍单次运算结果。从结果来看,Resize的速度要更快,sf的结果要更好。

    Resize耗时0.0121942s SF耗时0.0245566s
    自身对比结果:  R 100.00% G 100.00% B 100.00%
    resize结果:  R 75.12% G 74.45% B 73.46%
    sf结果:  R 76.90% G 76.09% B 75.11%


    多次对比表格(注意需要解除constexpr static float MAX_SCALE = 4.0f限制)

    2

    Resize耗时0.0107573s SF耗时0.0202321s
    自身对比结果:  R 100.00% G 100.00% B 100.00%
     resize结果:  R 93.68% G 93.49% B 93.23%
     sf结果:  R 97.25% G 97.20% B 97.07%

    4

    Resize耗时0.0121942s SF耗时0.0245566s
    自身对比结果:  R 100.00% G 100.00% B 100.00%
    resize结果:  R 75.12% G 74.45% B 73.46%
    sf结果:  R 76.90% G 76.09% B 75.11%

    5
    Resize耗时0.0116356s SF耗时0.0207268s
    自身对比结果:  R 100.00% G 100.00% B 100.00%
    resize结果:  R 67.05% G 66.16% B 65.05%
    sf结果:  R 66.74% G 65.48% B 64.20%

    8

    Resize耗时0.0197857s SF耗时0.0182384s
    自身对比结果:  R 100.00% G 100.00% B 100.00%
     resize结果:  R 52.94% G 51.89% B 51.05%
     sf结果:  R 51.66% G 50.15% B 49.13%

    10
    Resize耗时0.0175215s SF耗时0.0138289s
    自身对比结果:  R 100.00% G 100.00% B 100.00%
    resize结果:  R 47.59% G 46.66% B 46.18%
    sf结果:  R 46.14% G 44.85% B 44.22%


    结果对比来看


    1、以4-5倍为分界线,小倍数用SF,大倍数用resize,其中2倍放大尤其明显;
    2、时间消耗上,差距在若干毫秒
    3、另SF对放大倍数有要求,比如6倍时,由于2000不能被6整除,导致结果报错:





    附件列表

      目前方向:图像处理,人工智能
    • 相关阅读:
      APP版本更新通知流程测试要点
      Android+appium +python 点击坐标tap方法的封装
      appium 元素定位find_element_by_android_uiautomator方法使用
      Android 应用加固(乐固)操作说明
      查询APP Store已发布过的版本记录
      appium 报错:AttributeError:"NoneType' object has no attribute 'XXX'
      appium 运行报错:...... Attempt to re-install io.appium.settings without first uninstalling解决方案
      Charles模拟网络请求页面的网络超时测试
      利用漏洞中验证码绕过的小技巧
      C中的volatile用法
    • 原文地址:https://www.cnblogs.com/jsxyhelu/p/15365679.html
    Copyright © 2020-2023  润新知