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圖像修複執行個體
圖1
由于是圖像全區域做門檻值處理獲得的掩碼,圖像上部分區域也被當做掩碼對待,導緻部分圖像受損。
滑鼠框選區域+門檻值處理+Mask膨脹處理
代碼參看附件【demo2】。
圖2
可以看到,標明區域之外的圖像不受修複影響,沒有額外的損傷。
滑鼠劃定整個區域作為修複對象
這個方法標明一個矩形區域,把整個矩形區域作為要修複的對象,該方法适用于圖像結構比較簡單,特别是純色圖像,并且標明區域面積占比不大的情況,效果較好。
代碼參看附件【demo3】。
圖3
圖像修複綜合執行個體
代碼參看附件【demo4】。
圖4