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

Adds Edge-Preserving Filter #1690

Merged
merged 26 commits into from
Aug 2, 2018
Merged

Adds Edge-Preserving Filter #1690

merged 26 commits into from
Aug 2, 2018

Conversation

simonreich
Copy link
Contributor

This pullrequest changes

This request adds the Edge-Preserving Filter to the OpenCV libreary as described in
Reich, S. and Wörgötter, F. and Dellen, B. (2018). A Real-Time Edge-Preserving Denoising Filter. Proceedings of the 13th International Joint Conference on Computer Vision, Imaging and Computer Graphics Theory and Applications (VISIGRAPP): Visapp, 85-94, 4. DOI: 10.5220/0006509000850094.

http://fortknox.physik3.gwdg.de/cns/modules/BibtexModule/uploads/PDF/reichwoergoetterdellen2018.pdf

@simonreich
Copy link
Contributor Author

Please advise on further steps. Thank you!

@alalek
Copy link
Member

alalek commented Jul 12, 2018

Thank you for contribution!

  1. Perhaps this functionality should go to ximgproc module.
  2. Please use this license header (for headers and module sources):
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
  1. Remove license header from the sample files (most part of OpenCV samples are inserted into documentation).
  2. Remove extra indentation from "namespace" blocks
  3. #ifdef __cplusplus is no necessary too in .hpp files.
  4. Please add some simple enough test. You can reuse images from opencv_extra repository.


#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
Copy link
Member

Choose a reason for hiding this comment

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

opencv2/imgcodecs.hpp
opencv2/highgui.hpp
opencv2/ximgproc.hpp

Copy link
Contributor Author

Choose a reason for hiding this comment

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

opencv2/imgcodecs.hpp
opencv2/opencv.hpp
'''

do not seem to be necessary and are not included any more.


int showWindow(cv::Mat image, std::string title);

int showWindow(cv::Mat image, std::string title)
Copy link
Member

Choose a reason for hiding this comment

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

Use "static" to eliminate forward declaration:
static int showWindow(...) { }

Use const reference for input parameters:

  • const Mat&
  • const std::string&

Copy link
Contributor Author

Choose a reason for hiding this comment

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

showWindow function removed.

int main(int argc, char **argv)
{
// Help text
if (argc != 2)
Copy link
Member

Choose a reason for hiding this comment

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

It is better to use OpenCV's CommandLineParser: similar example

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


// Initialize filter. Kernel size 5x5, threshold 20
Ptr<epf::edgepreservingFilter> filter =
epf::edgepreservingFilter::create(&image, &res, 5, 20);
Copy link
Member

Choose a reason for hiding this comment

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

Looks like using "filter" object is over-complicated for filtering case. Ptr is not necessary.
It is better to use just a function, like OpenCV's cv::blur.

Please use OpenCV's InputArray / OutputArray types for parameters (they are used for Python/Java bindings generation at least)

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 did not know how to implement this properly. It is now a part of ximgproc.

InputArray is used.

No pointers are used.


int showWindow(cv::Mat image, std::string title)
{
namedWindow(title);
Copy link
Member

Choose a reason for hiding this comment

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

namedWindow() is not necessary here.

imshow() implies namedWindow() call

Copy link
Contributor Author

Choose a reason for hiding this comment

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

showWindow function removed.

@simonreich
Copy link
Contributor Author

Thanks for the quick reply.

I will close this request and resubmit after I added the above changes.

@simonreich simonreich closed this Jul 12, 2018
@alalek
Copy link
Member

alalek commented Jul 12, 2018

No need to open a new pull request. Just push changes / new commits into your branch "epf" (probably with "--force" option to override patch).

Lets keep discussion in a single place.

* @param d Diameter of each pixel neighborhood that is used during filtering. Must be greater or equal 3..
* @param threshold Threshold, which distinguishes between noise, outliers, and data.
*/
CV_WRAP static Ptr<edgepreservingFilter> create(const cv::Mat *src, cv::Mat *dst, int d, int threshold);
Copy link
Contributor

Choose a reason for hiding this comment

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

if i may ask: why is this a class ? (maybe having a simple function, at least in the public interface might be better)

it's not doing anything outside of the constructor, and we'd need to create a new object for different images here, right ?

also, please rather use InputArray and OutputArray instead of raw Mat * (which cannot be properly wrapped into python / java)

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 did not know how to implement this properly. It is now a part of ximgproc.

InputArray is used.

No pointers are used.

* @param d Diameter of each pixel neighborhood that is used during filtering. Must be greater or equal 3..
* @param threshold Threshold, which distinguishes between noise, outliers, and data.
*/
CV_EXPORTS_W void edgepreservingFilter( const InputArray *src, OutputArray *dst, int d, int threshold );
Copy link
Contributor

@berak berak Jul 12, 2018

Choose a reason for hiding this comment

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

can you try without the pointers ?

CV_EXPORTS_W void edgepreservingFilter( const InputArray src, OutputArray dst, int d, int threshold );

should be enough. (passing cv::Mat by address is usually a bad idea, since it defeats the internal refcounting)

(the buildbot does not like it, either )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

#include <opencv2/highgui.hpp>
#include <opencv2/ximgproc.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/epf.hpp>
Copy link
Contributor

@berak berak Jul 12, 2018

Choose a reason for hiding this comment

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

this does not exist, right ? ( #include <opencv2/ximgproc.hpp> should be enough, 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.

Done.

subPosY < subwindow.rows;
subPosY++)
{
if (meanPixelwiseDist <=
Copy link
Contributor

Choose a reason for hiding this comment

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

i can't see any difference between the if and the else block.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Historic reasons of this implementation. Is removed in latest commit.

{
using namespace std;

edgepreservingFilter(const InputArray *src, OutputArray *dst, int d,
Copy link
Contributor

Choose a reason for hiding this comment

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

void edgepreservingFilter(...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

cv::GaussianBlur(subwindow1, subwindow,
cv::Size(5, 5), 0.3, 0.3);

// compute arithmetic mean of subwindow
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented cv::mean instead of manual computation.

@simonreich
Copy link
Contributor Author

Thanks for all the helpful comments and tips! I think I included all of them in the last commits.

I am waiting for the build bot to finish and will check for further issues.

Please let me know what I should be doing next.

PERF_TEST_P(EdgepreservingFilterTest, perf,
Combine(Values(-20, 0, 10), Values(-100, 0, 20)))
{
/* 3. Get actual test parameters */
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 4 spaces

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 https://raw.githubusercontent.com/opencv/opencv_contrib/master/modules/cvv/.clang-format for beautification.

Is there a working .clang-format file (or any other code beautifier)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The above code should read

PERF_TEST_P(EdgepreservingFilterTest, perf,
    Combine(Values(-20, 0, 10), Values(-100, 0, 20)))
{
	/* 3. Get actual test parameters */

Instead of

PERF_TEST_P(EdgepreservingFilterTest, perf,
            Combine(Values(-20, 0, 10), Values(-100, 0, 20)))
{
	/* 3. Get actual test parameters */

Is this correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, I did not notice that the above .clang-format file uses tabs instead of 4 spaces.

Please ignore my last comment.

double threshold = get<1>(params);

/* 4. Allocate and initialize arguments for tested function */
std::string filename = getDataPath("samples/data/corridor.jpg");
Copy link
Member

Choose a reason for hiding this comment

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

Currently test framework doesn't search test images in samples/data directory.

Tests using testdata from opencv_extra repository:
https://github.com/opencv/opencv_extra/tree/master/testdata

Please create patch with image here too (the the same name of branch: "epf")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it considered good practice to use https://github.com/opencv/opencv_extra/blob/master/testdata/perf/320x260.png instead of creating my own image?

Thus, no patch would be needed.

Copy link
Member

Choose a reason for hiding this comment

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

Reusing existed test images is a good practice.


/* 2. Declare the testsuite */
PERF_TEST_P(EdgepreservingFilterTest, perf,
Combine(Values(-20, 0, 10), Values(-100, 0, 20)))
Copy link
Member

Choose a reason for hiding this comment

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

May be we should start from accuracy test and do some checks?
Performance tests have not any checks usually, they just measure performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are there any resources on OpenCV on how to write an accuracy test available? I just found lengthy example code at https://github.com/opencv/opencv/wiki/QA_in_OpenCV.

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 added an accuracy test.

However, some more resources would be helpful.

Mat dst(src.size(), src.type());

/* 5. Manifest your expectations about this test */
declare.in(src, WARMUP_RNG).out(dst);
Copy link
Member

Choose a reason for hiding this comment

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

in(src, WARMUP_RNG)

This code overrides your input image. Do not do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed , WARMUP_RNG.

/* 6. Collect the samples! */
TEST_CYCLE_N(1)
{
ximgproc::edgepreservingFilter(src, dst, kernelSize, threshold);
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 this pattern:

    PERF_SAMPLE_BEGIN();
        Canny(img, edges, thresh_low, thresh_high, aperture, useL2);
    PERF_SAMPLE_END();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


int main(int argc, char **argv)
{
cv::CommandLineParser parser(
Copy link
Member

Choose a reason for hiding this comment

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

4 spaces

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed '\t ' to " ".

void edgepreservingFilter(const InputArray _src, OutputArray _dst, int d,
double threshold)
{
Mat src = _src.getMat();
Copy link
Member

Choose a reason for hiding this comment

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

4 spaces

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed '\t ' to " ".

cv::Rect(posX, posY, subwindowX, subwindowY);
subwindow = src(roi);
cv::Mat subwindow1 = src(roi);
cv::GaussianBlur(subwindow1, subwindow, cv::Size(5, 5),
Copy link
Member

Choose a reason for hiding this comment

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

subwindow = src(roi);

This line seems useless. Due cv::GaussianBlur() call behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed subwindow = src(roi);.

Mat dst = _dst.getMat();
src.copyTo(dst);

CV_Assert(src.type() == CV_8UC3);
Copy link
Member

Choose a reason for hiding this comment

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

Move this check in the beginning of function. Perhaps via _src.type()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


namespace cv { namespace ximgproc {

//! @addtogroup ximgproc
Copy link
Member

Choose a reason for hiding this comment

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

No indentation in namespaces please.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@simonreich
Copy link
Contributor Author

I worked on all of the above issues. One question remains: Currently, there is one performance test and one accuracy test. Which one should be deleted?

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.

Which one should be deleted?

No one. It is OK now.
Accuracy and performance tests have different purposes.

Patch looks good to me. Please check the last one comment below.

* @param d Diameter of each pixel neighborhood that is used during filtering. Must be greater or equal 3.
* @param threshold Threshold, which distinguishes between noise, outliers, and data.
*/
CV_EXPORTS_W void edgepreservingFilter( InputArray src, OutputArray dst, int d, double threshold );
Copy link
Member

Choose a reason for hiding this comment

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

edgepreservingFilter

Rename to "edgePreservingFilter". See naming guide for details.

BTW, OpenCV already has some edgePreservingFilter() function (from photo module). But there is no huge problem due different namespaces.

@simonreich
Copy link
Contributor Author

Thanks for all the help! If I can be of further assistance, please do not hesitate to contact me.

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! Looks good to me 👍

@alalek alalek merged commit 9695384 into opencv:master Aug 2, 2018
@simonreich simonreich deleted the epf branch August 6, 2018 06:14
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.

3 participants