Merge pull request #8776 from sovrasov:inpaint_adv_formats

pull/8788/head
Vadim Pisarevsky 8 years ago
commit 37b1bc9d58
  1. 2
      modules/photo/include/opencv2/photo.hpp
  2. 76
      modules/photo/src/inpaint.cpp
  3. 23
      modules/photo/test/test_inpaint.cpp

@ -89,7 +89,7 @@ enum
/** @brief Restores the selected region in an image using the region neighborhood. /** @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 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 @param inpaintMask Inpainting mask, 8-bit 1-channel image. Non-zero pixels indicate the area that
needs to be inpainted. needs to be inpainted.
@param dst Output image with the same size and type as src . @param dst Output image with the same size and type as src .

@ -277,7 +277,7 @@ icvCalcFMM(const CvMat *f, CvMat *t, CvPriorityQueueFloat *Heap, bool negate) {
} }
} }
template <typename data_type>
static void static void
icvTeleaInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueueFloat *Heap ) { 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; int i = 0, j = 0, ii = 0, jj = 0, k, l, q, color = 0;
@ -463,31 +463,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) {
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 { } 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 { } else {
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)-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 { } else {
gradI.x=0; gradI.x=0;
} }
} }
if (CV_MAT_ELEM(*f,uchar,k+1,l)!=INSIDE) { if (CV_MAT_ELEM(*f,uchar,k+1,l)!=INSIDE) {
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 { } 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 { } else {
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,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 { } else {
gradI.y=0; 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); Jx -= (float)w * (float)(gradI.x*r.x);
Jy -= (float)w * (float)(gradI.y*r.y); Jy -= (float)w * (float)(gradI.y*r.y);
s += w; s += w;
@ -497,7 +497,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)); 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<uchar>(sat); CV_MAT_ELEM(*out,data_type,i-1,j-1) = cv::saturate_cast<data_type>(sat);
} }
} }
@ -509,7 +509,7 @@ icvTeleaInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQu
} }
} }
template <typename data_type>
static void static void
icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueueFloat *Heap) { 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; int i = 0, j = 0, ii = 0, jj = 0, k, l, q, color = 0;
@ -640,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) {
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))+ gradI.x=(float)(std::abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm))+
abs(CV_MAT_ELEM(*out,uchar,kp,lm)-CV_MAT_ELEM(*out,uchar,km-1,lm))); std::abs(CV_MAT_ELEM(*out,data_type,kp,lm)-CV_MAT_ELEM(*out,data_type,km-1,lm)));
} else { } 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)(std::abs(CV_MAT_ELEM(*out,data_type,kp+1,lm)-CV_MAT_ELEM(*out,data_type,kp,lm)))*2.0f;
} }
} else { } else {
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,lm)-CV_MAT_ELEM(*out,uchar,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 { } else {
gradI.x=0; gradI.x=0;
} }
} }
if (CV_MAT_ELEM(*f,uchar,k,l+1)!=INSIDE) { if (CV_MAT_ELEM(*f,uchar,k,l+1)!=INSIDE) {
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))+ gradI.y=(float)(std::abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm))+
abs(CV_MAT_ELEM(*out,uchar,km,lm)-CV_MAT_ELEM(*out,uchar,km,lm-1))); std::abs(CV_MAT_ELEM(*out,data_type,km,lm)-CV_MAT_ELEM(*out,data_type,km,lm-1)));
} else { } 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)(std::abs(CV_MAT_ELEM(*out,data_type,km,lp+1)-CV_MAT_ELEM(*out,data_type,km,lm)))*2.0f;
} }
} else { } else {
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,lm)-CV_MAT_ELEM(*out,uchar,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 { } else {
gradI.y=0; gradI.y=0;
} }
@ -676,13 +676,13 @@ icvNSInpaintFMM(const CvMat *f, CvMat *t, CvMat *out, int range, CvPriorityQueue
dir = (float)fabs(VectorScalMult(r,gradI)/sqrt(VectorLength(r)*VectorLength(gradI))); dir = (float)fabs(VectorScalMult(r,gradI)/sqrt(VectorLength(r)*VectorLength(gradI)));
} }
w = dst*dir; 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; s += w;
} }
} }
} }
} }
CV_MAT_ELEM(*out,uchar,i-1,j-1) = cv::saturate_cast<uchar>((double)Ia/s); CV_MAT_ELEM(*out,data_type,i-1,j-1) = cv::saturate_cast<data_type>((double)Ia/s);
} }
CV_MAT_ELEM(*f,uchar,i,j) = BAND; CV_MAT_ELEM(*f,uchar,i,j) = BAND;
@ -744,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)) 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" ); 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_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_MAT_TYPE(input_img->type) != CV_8UC3) ||
!CV_ARE_TYPES_EQ(input_img,output_img) ) !CV_ARE_TYPES_EQ(input_img,output_img) )
CV_Error( CV_StsUnsupportedFormat, CV_Error( CV_StsUnsupportedFormat,
"Only 8-bit 1-channel and 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 ) if( CV_MAT_TYPE(inpaint_mask->type) != CV_8UC1 )
CV_Error( CV_StsUnsupportedFormat, "The mask must be 8-bit 1-channel image" ); CV_Error( CV_StsUnsupportedFormat, "The mask must be 8-bit 1-channel image" );
@ -798,10 +800,36 @@ cvInpaint( const CvArr* _input_img, const CvArr* _inpaint_mask, CvArr* _output_i
cvSub(out,band,out,NULL); cvSub(out,band,out,NULL);
SET_BORDER1_C1(out,uchar,0); SET_BORDER1_C1(out,uchar,0);
icvCalcFMM(out,t,Out,true); icvCalcFMM(out,t,Out,true);
icvTeleaInpaintFMM(mask,t,output_img,range,Heap); switch(CV_MAT_DEPTH(output_img->type))
{
case CV_8U:
icvTeleaInpaintFMM<uchar>(mask,t,output_img,range,Heap);
break;
case CV_16U:
icvTeleaInpaintFMM<ushort>(mask,t,output_img,range,Heap);
break;
case CV_32F:
icvTeleaInpaintFMM<float>(mask,t,output_img,range,Heap);
break;
default:
CV_Error( cv::Error::StsBadArg, "Unsupportedformat of the input image" );
}
} }
else if (flags == cv::INPAINT_NS) { else if (flags == cv::INPAINT_NS) {
icvNSInpaintFMM(mask,t,output_img,range,Heap); switch(CV_MAT_DEPTH(output_img->type))
{
case CV_8U:
icvNSInpaintFMM<uchar>(mask,t,output_img,range,Heap);
break;
case CV_16U:
icvNSInpaintFMM<ushort>(mask,t,output_img,range,Heap);
break;
case CV_32F:
icvNSInpaintFMM<float>(mask,t,output_img,range,Heap);
break;
default:
CV_Error( cv::Error::StsBadArg, "Unsupported format of the input image" );
}
} else { } else {
CV_Error( cv::Error::StsBadArg, "The flags argument must be one of CV_INPAINT_TELEA or CV_INPAINT_NS" ); CV_Error( cv::Error::StsBadArg, "The flags argument must be one of CV_INPAINT_TELEA or CV_INPAINT_NS" );
} }

@ -117,3 +117,26 @@ void CV_InpaintTest::run( int )
} }
TEST(Photo_Inpaint, regression) { CV_InpaintTest test; test.safe_run(); } TEST(Photo_Inpaint, regression) { CV_InpaintTest test; test.safe_run(); }
typedef testing::TestWithParam<std::tr1::tuple<int> > 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));

Loading…
Cancel
Save