對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-opencv\sources\doc\tutorials\videoio\video-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整除,導致結果報錯:





附件列表

     


    免責聲明!

    本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



     
    粵ICP備18138465號   © 2018-2025 CODEPRJ.COM