天天看點

【第二部分 圖像處理】第4章 Opencv圖像處理高階【3圖像修複】

3.1圖像修複概述

在實際應用中,我們的圖像常常會被噪聲腐蝕,這些噪聲或是鏡頭上的灰塵或水滴,或是舊照片的劃痕,或者是圖像遭到人為的塗畫(比如馬賽克)或者圖像的部分本身已經損壞。如果我們想讓這些受到破壞的額圖檔盡可能恢複到原樣,Opencv能幫我們做到嗎?

OpenCV真的有這個妙手回春的功能!别以為圖像修補的工作隻能用PS或者美圖秀秀那些軟體去做,其實由程式員自己寫代碼去做更加高效!

圖像修複技術的原理是什麼呢?簡而言之,就是利用那些已經被破壞的區域的邊緣, 即邊緣的顔色和結構,根據這些圖像留下的資訊去推斷被破壞的資訊區的資訊内容,然後對破壞區進行填補 ,以達到圖像修補的目的。

3.2圖像修複API:inpaint()函數

 inpaint()函數講解

c++: void inpaint(InputArray src, 
                  InputArray inpaintMask, 
                  OutputArray dst, 
                  double inpaintRadius, 
                  int flags)           

【參數】

第一個參數,src,輸入的單通道或三通道圖像;

第二個參數,inpaintMask,圖像的掩碼,單通道圖像,大小跟原圖像一緻,inpaintMask圖像上除了需要修複的部分之外其他部分的像素值全部為0;

第三個參數,dst,輸出的經過修複的圖像;

第四個參數,inpaintRadius,修複算法取的鄰域半徑,用于計算目前像素點的內插補點;

第五個參數,flags ,修複算法,有兩種:INPAINT_NS 和I NPAINT_TELEA;

 INPAINT_NS Navier-Stokes based method.

 INPAINT_TELEA Method by Alexandru Telea [Telea04].

 inpaint()函數源代碼

/*【inpaint()源代碼】****************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\photo\src\inpaint.cpp
 * @起始行數:810行   
********************************************************************************/
void cv::inpaint( InputArray _src, InputArray _mask, OutputArray _dst,
                  double inpaintRange, int flags )
{
    Mat src = _src.getMat(), mask = _mask.getMat();
    _dst.create( src.size(), src.type() );
    CvMat c_src = src, c_mask = mask, c_dst = _dst.getMat();
    cvInpaint( &c_src, &c_mask, &c_dst, inpaintRange, flags );
}           
可以看出inpaint源碼中調用的是cvInpaint()函數,其源代碼如下。
           
/*【inpaint()源代碼】****************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的對應版本源碼完全一樣,均在對應的安裝目錄下)  
 * @源碼路徑:…\opencv\sources\modules\photo\src\inpaint.cpp
 * @起始行數:727行   
********************************************************************************/
Void cvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_img,
           double inpaintRange, int flags )
{
    cv::Ptr<CvMat> mask, band, f, t, out;
    cv::Ptr<CvPriorityQueueFloat> Heap, Out;
    cv::Ptr<IplConvKernel> el_cross, el_range;

    CvMat input_hdr, mask_hdr, output_hdr;
    CvMat* input_img, *inpaint_mask, *output_img;
    int range=cvRound(inpaintRange);
    int erows, ecols;

    input_img = cvGetMat( _input_img, &input_hdr );
    inpaint_mask = cvGetMat( _inpaint_mask, &mask_hdr );
    output_img = cvGetMat( _output_img, &output_hdr );

    if( !CV_ARE_SIZES_EQ(input_img,output_img) || !CV_ARE_SIZES_EQ(input_img,inpaint_mask))
        CV_Error( CV_StsUnmatchedSizes, "All the input and output images must have the same size" );

    if( (CV_MAT_TYPE(input_img->type) != CV_8UC1 &&
        CV_MAT_TYPE(input_img->type) != CV_8UC3) ||
        !CV_ARE_TYPES_EQ(input_img,output_img) )
        CV_Error( CV_StsUnsupportedFormat,
        "Only 8-bit 1-channel and 3-channel input/output images are supported" );

    if( CV_MAT_TYPE(inpaint_mask->type) != CV_8UC1 )
        CV_Error( CV_StsUnsupportedFormat, "The mask must be 8-bit 1-channel image" );

    range = MAX(range,1);
    range = MIN(range,100);

    ecols = input_img->cols + 2;
    erows = input_img->rows + 2;

    f.reset(cvCreateMat(erows, ecols, CV_8UC1));
    t.reset(cvCreateMat(erows, ecols, CV_32FC1));
    band.reset(cvCreateMat(erows, ecols, CV_8UC1));
    mask.reset(cvCreateMat(erows, ecols, CV_8UC1));
    el_cross.reset(cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_CROSS,NULL));

    cvCopy( input_img, output_img );
    cvSet(mask,cvScalar(KNOWN,0,0,0));
    COPY_MASK_BORDER1_C1(inpaint_mask,mask,uchar);
    SET_BORDER1_C1(mask,uchar,0);
    cvSet(f,cvScalar(KNOWN,0,0,0));
    cvSet(t,cvScalar(1.0e6f,0,0,0));
    cvDilate(mask,band,el_cross,1);   // image with narrow band
    Heap=cv::makePtr<CvPriorityQueueFloat>();
    if (!Heap->Init(band))
        return;
    cvSub(band,mask,band,NULL);
    SET_BORDER1_C1(band,uchar,0);
    if (!Heap->Add(band))
        return;
    cvSet(f,cvScalar(BAND,0,0,0),band);
    cvSet(f,cvScalar(INSIDE,0,0,0),mask);
    cvSet(t,cvScalar(0,0,0,0),band);

    if( flags == cv::INPAINT_TELEA )
    {
        out.reset(cvCreateMat(erows, ecols, CV_8UC1));
        el_range.reset(cvCreateStructuringElementEx(2*range+1,2*range+1,
            range,range,CV_SHAPE_RECT,NULL));
        cvDilate(mask,out,el_range,1);
        cvSub(out,mask,out,NULL);
        Out=cv::makePtr<CvPriorityQueueFloat>();
        if (!Out->Init(out))
            return;
        if (!Out->Add(band))
            return;
        cvSub(out,band,out,NULL);
        SET_BORDER1_C1(out,uchar,0);
        icvCalcFMM(out,t,Out,true);
        icvTeleaInpaintFMM(mask,t,output_img,range,Heap);
    }
    else if (flags == cv::INPAINT_NS) {
        icvNSInpaintFMM(mask,t,output_img,range,Heap);
    } else {
        CV_Error( cv::Error::StsBadArg, "The flags argument must be one of CV_INPAINT_TELEA or CV_INPAINT_NS" );
    }
}           

3.3圖像修複執行個體

【第二部分 圖像處理】第4章 Opencv圖像處理高階【3圖像修複】

圖1

由于是圖像全區域做門檻值處理獲得的掩碼,圖像上部分區域也被當做掩碼對待,導緻部分圖像受損。

 滑鼠框選區域+門檻值處理+Mask膨脹處理

代碼參看附件【demo2】。

【第二部分 圖像處理】第4章 Opencv圖像處理高階【3圖像修複】

圖2

可以看到,標明區域之外的圖像不受修複影響,沒有額外的損傷。

 滑鼠劃定整個區域作為修複對象

這個方法標明一個矩形區域,把整個矩形區域作為要修複的對象,該方法适用于圖像結構比較簡單,特别是純色圖像,并且標明區域面積占比不大的情況,效果較好。

代碼參看附件【demo3】。

【第二部分 圖像處理】第4章 Opencv圖像處理高階【3圖像修複】

圖3

 圖像修複綜合執行個體

代碼參看附件【demo4】。

【第二部分 圖像處理】第4章 Opencv圖像處理高階【3圖像修複】

圖4

本章參考附件

繼續閱讀