Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new algorithm Rapid Frequency Selective Reconstruction (FSR) added #2296

Merged
merged 9 commits into from
Nov 2, 2019

Conversation

si40wiga
Copy link
Contributor

@si40wiga si40wiga commented Oct 15, 2019

Submission

This pullrequest adds the implementation of the high-quality extrapolation algorithm Rapid Frequency Selective Reconstruction (FSR) with two quality profiles. As one of its fields of applications is inpainting, it was added to the existing inpainting interface in xphoto.
Please refer to
https://github.com/opencv/opencv_contrib/files/3730212/inpainting_comparison.pdf
for a detailed description of the algorithm and a comparison to existing inpainting methods in OpenCV.

#disable_ipp=ON

Copy link
Member

@alalek alalek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for great contribution!

May I ask you to add some simple test:

  • code example
  • lena.png image is available in opencv_extra repository: cvtest::findDataFile("cv/shared/lena.png")
  • modify input image in test code (add mask)
  • compare result with original unmodified image through PSNR metric.



static void
icvBGR2YCbCr(const cv::Mat& bgr, cv::Mat& Y, cv::Mat& Cb, cv::Mat& Cr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use cvtColor() + cv::split() instead of manual function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for your fast review!
I added a simple test with existing test data following your advice and tried to take all your comments into account.

I changed that and use cvtColor() now.
The manual function was used to achieve the same value range for the YCbCr color space as in Matlab for a better comparison (the algorithm was ported from Matlab to C++ and the matlab color conversion covers a slighlty different value range as the result of cvtColor()).
So, the implementation might give slightly different results than those stated in the paper. I made a quick check for the Kodak image set, the resulting mean PSNR value differs by 0.04 dB.

{
for (int x = 0; x < src.cols; ++x)
{
double curr_val = srcData[y*src.step1() + x];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid manual calculation through steps:

CV_CheckTypeEQ(CV_64F, src.type(), "");
for (y...)
{
    double curr_row = src.ptr<double>(y);
    for (x...)
    {
        double curr_val = curr_row[x];
        ...
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used src.at<double>(y,x) instead of the manual calculation. I hope that is fine as well.

}
cv::Scalar tmp_stddev, tmp_mean;
cv::Mat mask8u = error_mask_2d*2;
mask8u.convertTo(mask8u, CV_8U);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*2

Why 2? What is values range?

mask8u.convertTo(mask8u, CV_8U);

There is third parameter which can help with multiplication.
BTW, Perhaps inplace mode doesn't work well here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error_mask_2d can have the values 0 (pixels that need to be reconstructed), 0.5 (already reconstructed) or 1 (valid, original pixels).
By multiplication with 2, the pixels with the value 0.5 won't be 0 after the conversion to CV_8U. With that, the already reconstructed pixels are used for the computation of the standard deviation.

Thanks for the remarks.

// inital scan of distorted blocks
std::vector< std::tuple< int, int > > set_todo;
int blocks_column = cvCeil(static_cast<double>(img_height) / block_size);
int blocks_line = cvCeil(static_cast<double>(img_width) / block_size);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can avoid floating-point computations here.
Use int divUp(int a, unsigned int b) from core/utility.hpp

CV_Error(cv::Error::StsUnsupportedFormat, "Unsupported source image format!");
break;
}
src.convertTo(src, CV_8UC3, 1 / 257.0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, .convertTo() does not care about number of channels. CV_8UC3 can be safely replaced by CV_8UC1, or CV_8U. Also this case code block can be merged with case CV_16UC1: (similar for others).

{
for (int x = 0; x < width; ++x)
{
recData[y*dst.step1() + x] = cv::saturate_cast<uchar>(yData[y*y_reconstructed.step1() + x]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not convertTo(dst, CV_8U)?

Copy link
Member

@alalek alalek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for update!

I updated testing code:

  • full test is very long - ~1min in release mode, ~8 min in debug
  • separate tests for modes (full-sized "best" is disabled by default, can be tested through "--test_tag_enable=verylong,debug_verylong" parameter)
  • added grayscale test with "reduced" input

original.copyTo(im_distorted, mask_valid);

Mat reconstructed;
cv::xphoto::inpaint(im_distorted, mask_valid * (1/255.0), reconstructed, mode);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mask_valid * (1/255.0)

Looks like masks {0, 1} are accepted only. {0, 255} doesn't work good.

Please add mask pre-processing code into algorithm implementation, like documentation says:

@param mask mask (CV_8UC1), where non-zero pixels indicate valid image area, while zero pixels
indicate area to be inpainted indicate area to be inpainted

Try this pre-processing code:

threshold(mask_as_inputarray, mask_01, 0.0, 1.0, THRESH_BINARY)l

(after that please remove this fix from test code)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your work on the test code!
I fixed the mask pre-processing accordingly and removed your fix from the test code.

@alalek
Copy link
Member

alalek commented Nov 1, 2019

@alalek
Copy link
Member

alalek commented Nov 2, 2019

Vagrind catches errors:

[ RUN      ] xphoto_inpaint.smoke_FSR_FAST
==10110== Conditional jump or move depends on uninitialised value(s)
==10110==    at 0x77444D4: void cv::minMaxIdx_<double, double>(double const*, unsigned char const*, double*, double*, unsigned long*, unsigned long*, int, unsigned long) (minmax.cpp:38)
==10110==    by 0x7741F27: cv::minMaxIdx_64f(double const*, unsigned char const*, double*, double*, unsigned long*, unsigned long*, int, unsigned long) (minmax.cpp:100)
==10110==    by 0x77434EF: cv::minMaxIdx(cv::_InputArray const&, double*, double*, int*, int*, cv::_InputArray const&) (minmax.cpp:802)
==10110==    by 0x7743887: cv::minMaxLoc(cv::_InputArray const&, double*, double*, cv::Point_<int>*, cv::Point_<int>*, cv::_InputArray const&) (minmax.cpp:837)
==10110==    by 0x487C77B: cv::xphoto::icvExtrapolateBlock(cv::Mat&, cv::Mat&, cv::xphoto::fsr_parameters&, double, double, cv::Mat&) (inpainting_fsr.impl.hpp:170)
==10110==    by 0x4880206: cv::xphoto::icvDetermineProcessingOrder(cv::Mat const&, cv::Mat const&, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cv::Mat&) (inpainting_fsr.impl.hpp:674)
==10110==    by 0x4881814: cv::xphoto::inpaint_fsr(cv::Mat const&, cv::Mat const&, cv::Mat&, int) (inpainting_fsr.impl.hpp:814)
==10110==    by 0x48826AF: cv::xphoto::inpaint(cv::Mat const&, cv::Mat const&, cv::Mat&, int) (inpainting.cpp:423)
==10110==    by 0x4261AB: opencv_test::(anonymous namespace)::test_inpainting(cv::Size_<int>, cv::xphoto::InpaintTypes, double, cv::ImreadModes) (test_inpainting.cpp:30)
==10110==    by 0x4267F0: opencv_test::(anonymous namespace)::xphoto_inpaint_smoke_FSR_FAST_Test::Body() (test_inpainting.cpp:46)
==10110==    by 0x426726: opencv_test::(anonymous namespace)::xphoto_inpaint_smoke_FSR_FAST_Test::TestBody() (test_inpainting.cpp:44)
==10110==    by 0x46734D: void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (ts_gtest.cpp:3917)
==10110== 

@si40wiga
Copy link
Contributor Author

si40wiga commented Nov 2, 2019

Thanks for checking.
I got the same valgrind error, but I don't really know how to spot the uninitialised value(s).
With the option --track-origins=yes I got the following additional output:

==4838==  Uninitialised value was created by a heap allocation
==4838==    at 0x4C31E76: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4838==    by 0x4C31F91: posix_memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4838==    by 0x7620493: cv::fastMalloc(unsigned long) (in /home/anna/opencv/build2/lib/libopencv_core.so.4.1.2)
==4838==    by 0x7753F61: cv::Mat::create(int, int const*, int) (in /home/anna/opencv/build2/lib/libopencv_core.so.4.1.2)
==4838==    by 0x4E48198: cv::Mat::create(int, int, int) (in /home/anna/opencv/build2/lib/libopencv_xphoto.so.4.1.2)
==4838==    by 0x4E48917: cv::xphoto::icvExtrapolateBlock(cv::Mat&, cv::Mat&, cv::xphoto::fsr_parameters&, double, double, cv::Mat&) [clone .constprop.634] (in /home/anna/opencv/build2/lib/libopencv_xphoto.so.4.1.2)
==4838==    by 0x4E4C793: cv::xphoto::icvDetermineProcessingOrder(cv::Mat const&, cv::Mat const&, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cv::Mat&) (in /home/anna/opencv/build2/lib/libopencv_xphoto.so.4.1.2)
==4838==    by 0x4E4F2CA: cv::xphoto::inpaint_fsr(cv::Mat const&, cv::Mat const&, cv::Mat&, int) (in /home/anna/opencv/build2/lib/libopencv_xphoto.so.4.1.2)
==4838==    by 0x4E6489C: cv::xphoto::inpaint(cv::Mat const&, cv::Mat const&, cv::Mat&, int) (in /home/anna/opencv/build2/lib/libopencv_xphoto.so.4.1.2)
==4838==    by 0x1296E8: opencv_test::(anonymous namespace)::test_inpainting(cv::Size_<int>, cv::xphoto::InpaintTypes, double, cv::ImreadModes) (in /home/anna/opencv/build2/bin/opencv_test_xphoto)
==4838==    by 0x12A3A0: opencv_test::(anonymous namespace)::xphoto_inpaint_smoke_FSR_FAST_Test::TestBody() (in /home/anna/opencv/build2/bin/opencv_test_xphoto)
==4838==    by 0x160419: void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) (in /home/anna/opencv/build2/bin/opencv_test_xphoto) 

Maybe you have an idea on how to interpret this?

@alalek
Copy link
Member

alalek commented Nov 2, 2019

You need "Debug" build (or add "-g1" C++ compiler option, "-O1" is also preferable instead of "-O2" or "-O3"). It blames on this line: inpainting_fsr.impl.hpp:133

==12955==  Uninitialised value was created by a heap allocation
==12955==    at 0x483780B: malloc (vg_replace_malloc.c:309)
==12955==    by 0x75120C0: cv::fastMalloc(unsigned long) (alloc.cpp:150)
==12955==    by 0x76C611B: cv::StdMatAllocator::allocate(int, int const*, int, void*, unsigned long*, cv::AccessFlag, cv::UMatUsageFlags) const (matrix.cpp:147)
==12955==    by 0x76C70E9: cv::Mat::create(int, int const*, int) (matrix.cpp:360)
==12955==    by 0x486ED71: cv::Mat::create(int, int, int) (mat.inl.hpp:831)
==12955==    by 0x486E9E1: cv::Mat::Mat(cv::Size_<int>, int) (mat.inl.hpp:477)
==12955==    by 0x487C142: cv::xphoto::icvExtrapolateBlock(cv::Mat&, cv::Mat&, cv::xphoto::fsr_parameters&, double, double, cv::Mat&) (inpainting_fsr.impl.hpp:133)
==12955==    by 0x4880206: cv::xphoto::icvDetermineProcessingOrder(cv::Mat const&, cv::Mat const&, int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cv::Mat&) (inpainting_fsr.impl.hpp:674)
==12955==    by 0x4881814: cv::xphoto::inpaint_fsr(cv::Mat const&, cv::Mat const&, cv::Mat&, int) (inpainting_fsr.impl.hpp:814)
==12955==    by 0x48826AF: cv::xphoto::inpaint(cv::Mat const&, cv::Mat const&, cv::Mat&, int) (inpainting.cpp:423)
==12955==    by 0x4261AB: opencv_test::(anonymous namespace)::test_inpainting(cv::Size_<int>, cv::xphoto::InpaintTypes, double, cv::ImreadModes) (test_inpainting.cpp:30)
==12955==    by 0x4267F0: opencv_test::(anonymous namespace)::xphoto_inpaint_smoke_FSR_FAST_Test::Body() (test_inpainting.cpp:46)

I pushed fix here.
Lets see if valgrind run becomes clean (this is not a fast tool)


Update: valgrind run is clean with fix

@si40wiga
Copy link
Contributor Author

si40wiga commented Nov 2, 2019

You need "Debug" build (or add "-g1" C++ compiler option, "-O1" is also preferable instead of "-O2" or "-O3"). It blames on this line: inpainting_fsr.impl.hpp:133

Thanks for the info, I wasn't aware of these options and looked at the wrong lines.

Copy link
Member

@alalek alalek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done! Thank you for contribution 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants