From 2ddaaf69fa1d581eb13fd7fc9ac9df312c797b92 Mon Sep 17 00:00:00 2001 From: Vadzim Piatrou Date: Sat, 29 Apr 2017 11:21:58 +0300 Subject: [PATCH 1/2] Inpainting support for any 1-channel input images --- modules/photo/include/opencv2/photo.hpp | 2 +- modules/photo/src/inpaint.cpp | 57 ++++++++++++++++--------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/modules/photo/include/opencv2/photo.hpp b/modules/photo/include/opencv2/photo.hpp index 39778bec0d..83cd19c165 100644 --- a/modules/photo/include/opencv2/photo.hpp +++ b/modules/photo/include/opencv2/photo.hpp @@ -89,7 +89,7 @@ enum /** @brief Restores the selected region in an image using the region neighborhood. -@param src Input 8-bit 1-channel or 3-channel image. +@param src Input any 1-channel or 8-bit 3-channel image. @param inpaintMask Inpainting mask, 8-bit 1-channel image. Non-zero pixels indicate the area that needs to be inpainted. @param dst Output image with the same size and type as src . diff --git a/modules/photo/src/inpaint.cpp b/modules/photo/src/inpaint.cpp index 4ae3cf311c..9ddedecff2 100644 --- a/modules/photo/src/inpaint.cpp +++ b/modules/photo/src/inpaint.cpp @@ -67,6 +67,10 @@ min4( float a, float b, float c, float d ) #define INSIDE 2 //unknown #define CHANGE 3 //servise +// Processing data types +typedef double data_type; +int data_type_cv = CV_64F; + typedef struct CvHeapElem { float T; @@ -463,31 +467,31 @@ icvTeleaInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQu if (CV_MAT_ELEM(*f,uchar,k,l+1)!=INSIDE) { if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) { - gradI.x=(float)((CV_MAT_ELEM(*out,uchar,km,lp+1)-CV_MAT_ELEM(*out,uchar,km,lm-1)))*2.0f; + gradI.x=(float)((CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm-1)))*2.0f; } else { - gradI.x=(float)((CV_MAT_ELEM(*out,uchar,km,lp+1)-CV_MAT_ELEM(*out,uchar,km,lm))); + gradI.x=(float)((CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm))); } } else { if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) { - gradI.x=(float)((CV_MAT_ELEM(*out,uchar,km,lp)-CV_MAT_ELEM(*out,uchar,km,lm-1))); + gradI.x=(float)((CV_MAT_ELEM(*out,data_type,km,lp)-CV_MAT_ELEM(*out,data_type,km,lm-1))); } else { gradI.x=0; } } if (CV_MAT_ELEM(*f,uchar,k+1,l)!=INSIDE) { if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) { - gradI.y=(float)((CV_MAT_ELEM(*out,uchar,kp+1,lm)-CV_MAT_ELEM(*out,uchar,km-1,lm)))*2.0f; + gradI.y=(float)((CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm)))*2.0f; } else { - gradI.y=(float)((CV_MAT_ELEM(*out,uchar,kp+1,lm)-CV_MAT_ELEM(*out,uchar,km,lm))); + gradI.y=(float)((CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,km,lm))); } } else { if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) { - gradI.y=(float)((CV_MAT_ELEM(*out,uchar,kp,lm)-CV_MAT_ELEM(*out,uchar,km-1,lm))); + gradI.y=(float)((CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm))); } else { gradI.y=0; } } - Ia += (float)w * (float)(CV_MAT_ELEM(*out,uchar,km,lm)); + Ia += (float)w * (float)(CV_MAT_ELEM(*out,data_type,km,lm)); Jx -= (float)w * (float)(gradI.x*r.x); Jy -= (float)w * (float)(gradI.y*r.y); s += w; @@ -497,7 +501,7 @@ icvTeleaInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQu } sat = (float)((Ia/s+(Jx+Jy)/(sqrt(Jx*Jx+Jy*Jy)+1.0e-20f)+0.5f)); { - CV_MAT_ELEM(*out,uchar,i-1,j-1) = cv::saturate_cast(sat); + CV_MAT_ELEM(*out,data_type,i-1,j-1) = cv::saturate_cast(sat); } } @@ -640,28 +644,28 @@ icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueue if (CV_MAT_ELEM(*f,uchar,k+1,l)!=INSIDE) { if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) { - gradI.x=(float)(abs(CV_MAT_ELEM(*out,uchar,kp+1,lm)-CV_MAT_ELEM(*out,uchar,kp,lm))+ - abs(CV_MAT_ELEM(*out,uchar,kp,lm)-CV_MAT_ELEM(*out,uchar,km-1,lm))); + gradI.x=(float)(abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm))+ + abs(CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm))); } else { - gradI.x=(float)(abs(CV_MAT_ELEM(*out,uchar,kp+1,lm)-CV_MAT_ELEM(*out,uchar,kp,lm)))*2.0f; + gradI.x=(float)(abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm)))*2.0f; } } else { if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) { - gradI.x=(float)(abs(CV_MAT_ELEM(*out,uchar,kp,lm)-CV_MAT_ELEM(*out,uchar,km-1,lm)))*2.0f; + gradI.x=(float)(abs(CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm)))*2.0f; } else { gradI.x=0; } } if (CV_MAT_ELEM(*f,uchar,k,l+1)!=INSIDE) { if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) { - gradI.y=(float)(abs(CV_MAT_ELEM(*out,uchar,km,lp+1)-CV_MAT_ELEM(*out,uchar,km,lm))+ - abs(CV_MAT_ELEM(*out,uchar,km,lm)-CV_MAT_ELEM(*out,uchar,km,lm-1))); + gradI.y=(float)(abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm))+ + abs(CV_MAT_ELEM(*out,data_type,km,lm)-CV_MAT_ELEM(*out,data_type,km,lm-1))); } else { - gradI.y=(float)(abs(CV_MAT_ELEM(*out,uchar,km,lp+1)-CV_MAT_ELEM(*out,uchar,km,lm)))*2.0f; + gradI.y=(float)(abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm)))*2.0f; } } else { if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) { - gradI.y=(float)(abs(CV_MAT_ELEM(*out,uchar,km,lm)-CV_MAT_ELEM(*out,uchar,km,lm-1)))*2.0f; + gradI.y=(float)(abs(CV_MAT_ELEM(*out,data_type,km,lm)-CV_MAT_ELEM(*out,data_type,km,lm-1)))*2.0f; } else { gradI.y=0; } @@ -676,13 +680,13 @@ icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueue dir = (float)fabs(VectorScalMult(r,gradI)/sqrt(VectorLength(r)*VectorLength(gradI))); } w = dst*dir; - Ia += (float)w * (float)(CV_MAT_ELEM(*out,uchar,km,lm)); + Ia += (float)w * (float)(CV_MAT_ELEM(*out,data_type,km,lm)); s += w; } } } } - CV_MAT_ELEM(*out,uchar,i-1,j-1) = cv::saturate_cast((double)Ia/s); + CV_MAT_ELEM(*out,data_type,i-1,j-1) = cv::saturate_cast((double)Ia/s); } CV_MAT_ELEM(*f,uchar,i,j) = BAND; @@ -744,11 +748,11 @@ cvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_i 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 && + if( (CV_MAT_CN(input_img->type) != 1 && 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" ); + "Any 1-channel and 8-bit 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" ); @@ -815,6 +819,17 @@ void cv::inpaint( InputArray _src, InputArray _mask, OutputArray _dst, Mat src = _src.getMat(), mask = _mask.getMat(); _dst.create( src.size(), src.type() ); Mat dst = _dst.getMat(); - CvMat c_src = src, c_mask = mask, c_dst = dst; + + Mat src_temp; + if (src.channels() == 1) { + src.convertTo(src_temp, data_type_cv); + } else + src_temp = src.clone(); + + Mat dst_temp = Mat::zeros(src_temp.size(), src_temp.type()); + CvMat c_src = src_temp, c_mask = mask, c_dst = dst_temp; cvInpaint( &c_src, &c_mask, &c_dst, inpaintRange, flags ); + + if (src.channels() == 1) dst_temp.convertTo(dst_temp, src.type()); + dst_temp.copyTo(dst); } From d9ffc4c83ec747bab5d803f862d40c9579025ee4 Mon Sep 17 00:00:00 2001 From: Vladislav Sovrasov Date: Tue, 23 May 2017 16:31:16 +0300 Subject: [PATCH 2/2] photo: add support of all types of 1c images in inpaint --- modules/photo/include/opencv2/photo.hpp | 2 +- modules/photo/src/inpaint.cpp | 73 +++++++++++++++---------- modules/photo/test/test_inpaint.cpp | 23 ++++++++ 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/modules/photo/include/opencv2/photo.hpp b/modules/photo/include/opencv2/photo.hpp index 83cd19c165..431a6324d6 100644 --- a/modules/photo/include/opencv2/photo.hpp +++ b/modules/photo/include/opencv2/photo.hpp @@ -89,7 +89,7 @@ enum /** @brief Restores the selected region in an image using the region neighborhood. -@param src Input any 1-channel or 8-bit 3-channel image. +@param src Input 8-bit, 16-bit unsigned or 32-bit float 1-channel or 8-bit 3-channel image. @param inpaintMask Inpainting mask, 8-bit 1-channel image. Non-zero pixels indicate the area that needs to be inpainted. @param dst Output image with the same size and type as src . diff --git a/modules/photo/src/inpaint.cpp b/modules/photo/src/inpaint.cpp index 9ddedecff2..9669d02087 100644 --- a/modules/photo/src/inpaint.cpp +++ b/modules/photo/src/inpaint.cpp @@ -67,10 +67,6 @@ min4( float a, float b, float c, float d ) #define INSIDE 2 //unknown #define CHANGE 3 //servise -// Processing data types -typedef double data_type; -int data_type_cv = CV_64F; - typedef struct CvHeapElem { float T; @@ -281,7 +277,7 @@ icvCalcFMM(const CvMat *f, CvMat *t, CvPriorityQueueFloat *Heap, bool negate) { } } - +template static void icvTeleaInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueueFloat *Heap ) { int i = 0, j = 0, ii = 0, jj = 0, k, l, q, color = 0; @@ -513,7 +509,7 @@ icvTeleaInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQu } } - +template static void icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueueFloat *Heap) { int i = 0, j = 0, ii = 0, jj = 0, k, l, q, color = 0; @@ -644,28 +640,28 @@ icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueue if (CV_MAT_ELEM(*f,uchar,k+1,l)!=INSIDE) { if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) { - gradI.x=(float)(abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm))+ - abs(CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm))); + gradI.x=(float)(std::abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm))+ + std::abs(CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm))); } else { - gradI.x=(float)(abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm)))*2.0f; + gradI.x=(float)(std::abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm)))*2.0f; } } else { if (CV_MAT_ELEM(*f,uchar,k-1,l)!=INSIDE) { - gradI.x=(float)(abs(CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm)))*2.0f; + gradI.x=(float)(std::abs(CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm)))*2.0f; } else { gradI.x=0; } } if (CV_MAT_ELEM(*f,uchar,k,l+1)!=INSIDE) { if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) { - gradI.y=(float)(abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm))+ - abs(CV_MAT_ELEM(*out,data_type,km,lm)-CV_MAT_ELEM(*out,data_type,km,lm-1))); + gradI.y=(float)(std::abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm))+ + std::abs(CV_MAT_ELEM(*out,data_type,km,lm)-CV_MAT_ELEM(*out,data_type,km,lm-1))); } else { - gradI.y=(float)(abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm)))*2.0f; + gradI.y=(float)(std::abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm)))*2.0f; } } else { if (CV_MAT_ELEM(*f,uchar,k,l-1)!=INSIDE) { - gradI.y=(float)(abs(CV_MAT_ELEM(*out,data_type,km,lm)-CV_MAT_ELEM(*out,data_type,km,lm-1)))*2.0f; + gradI.y=(float)(std::abs(CV_MAT_ELEM(*out,data_type,km,lm)-CV_MAT_ELEM(*out,data_type,km,lm-1)))*2.0f; } else { gradI.y=0; } @@ -748,11 +744,13 @@ cvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_i 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_CN(input_img->type) != 1 && + if( (CV_MAT_TYPE(input_img->type) != CV_8U && + CV_MAT_TYPE(input_img->type) != CV_16U && + CV_MAT_TYPE(input_img->type) != CV_32F && CV_MAT_TYPE(input_img->type) != CV_8UC3) || !CV_ARE_TYPES_EQ(input_img,output_img) ) CV_Error( CV_StsUnsupportedFormat, - "Any 1-channel and 8-bit 3-channel input/output images are supported" ); + "8-bit, 16-bit unsigned or 32-bit float 1-channel and 8-bit 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" ); @@ -802,10 +800,36 @@ cvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_i cvSub(out,band,out,NULL); SET_BORDER1_C1(out,uchar,0); icvCalcFMM(out,t,Out,true); - icvTeleaInpaintFMM(mask,t,output_img,range,Heap); + switch(CV_MAT_DEPTH(output_img->type)) + { + case CV_8U: + icvTeleaInpaintFMM(mask,t,output_img,range,Heap); + break; + case CV_16U: + icvTeleaInpaintFMM(mask,t,output_img,range,Heap); + break; + case CV_32F: + icvTeleaInpaintFMM(mask,t,output_img,range,Heap); + break; + default: + CV_Error( cv::Error::StsBadArg, "Unsupportedformat of the input image" ); + } } else if (flags == cv::INPAINT_NS) { - icvNSInpaintFMM(mask,t,output_img,range,Heap); + switch(CV_MAT_DEPTH(output_img->type)) + { + case CV_8U: + icvNSInpaintFMM(mask,t,output_img,range,Heap); + break; + case CV_16U: + icvNSInpaintFMM(mask,t,output_img,range,Heap); + break; + case CV_32F: + icvNSInpaintFMM(mask,t,output_img,range,Heap); + break; + default: + CV_Error( cv::Error::StsBadArg, "Unsupported format of the input image" ); + } } else { CV_Error( cv::Error::StsBadArg, "The flags argument must be one of CV_INPAINT_TELEA or CV_INPAINT_NS" ); } @@ -819,17 +843,6 @@ void cv::inpaint( InputArray _src, InputArray _mask, OutputArray _dst, Mat src = _src.getMat(), mask = _mask.getMat(); _dst.create( src.size(), src.type() ); Mat dst = _dst.getMat(); - - Mat src_temp; - if (src.channels() == 1) { - src.convertTo(src_temp, data_type_cv); - } else - src_temp = src.clone(); - - Mat dst_temp = Mat::zeros(src_temp.size(), src_temp.type()); - CvMat c_src = src_temp, c_mask = mask, c_dst = dst_temp; + CvMat c_src = src, c_mask = mask, c_dst = dst; cvInpaint( &c_src, &c_mask, &c_dst, inpaintRange, flags ); - - if (src.channels() == 1) dst_temp.convertTo(dst_temp, src.type()); - dst_temp.copyTo(dst); } diff --git a/modules/photo/test/test_inpaint.cpp b/modules/photo/test/test_inpaint.cpp index 8f031e8d38..6c3426fb04 100644 --- a/modules/photo/test/test_inpaint.cpp +++ b/modules/photo/test/test_inpaint.cpp @@ -117,3 +117,26 @@ void CV_InpaintTest::run( int ) } TEST(Photo_Inpaint, regression) { CV_InpaintTest test; test.safe_run(); } + +typedef testing::TestWithParam > formats; + +TEST_P(formats, 1c) +{ + const int type = std::tr1::get<0>(GetParam()); + Mat src(100, 100, type); + src.setTo(Scalar::all(128)); + Mat ref = src.clone(); + Mat dst, mask = Mat::zeros(src.size(), CV_8U); + + circle(src, Point(50, 50), 5, Scalar(200), 6); + circle(mask, Point(50, 50), 5, Scalar(200), 6); + inpaint(src, mask, dst, 10, INPAINT_NS); + + Mat dst2; + inpaint(src, mask, dst2, 10, INPAINT_TELEA); + + ASSERT_LE(cv::norm(dst, ref, NORM_INF), 3.); + ASSERT_LE(cv::norm(dst2, ref, NORM_INF), 3.); +} + +INSTANTIATE_TEST_CASE_P(Photo_Inpaint, formats, testing::Values(CV_32F, CV_16U, CV_8U));