-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
e23e838
Module EPF - Edge-Preserving Filter added
3891dd5
Changed name from template to epf
538049f
Removed clang-format file
0d96f6d
Added header Files. Eliminated showWindow function. Used CommandLineP…
c272800
Moved filter from epf module to ximgproc
23d45a6
Removed header files from sample
82dfa23
Minor bug fix in demo. Pointers in demo removed.
88ce7a7
Pointers removed. InputArray/OutputArray added
1f115b6
License header added
19686de
License header from sample file removed
9cc391b
Unit test for performance added
c411b39
Replaced manual mean computation with cv::mean
55766d9
Beautified code via clang-format and https://raw.githubusercontent.co…
7e89c3d
Merged historic if... else if statement into one if statement
1b079bd
Trailing whitespace removed and .. changed into .
16ee637
Tabs replaced with 4 spaces.
3216c78
Removed subwindow = src(roi);
5fa0aba
Moved type test to beginning of code
845d807
Removed indentation from namespace and added //! @}
c6ea43f
Added name to header
a91e629
git cleanup introduced some errors fixed here
7e7d108
Changed path testdata/perf/320x260.png to perf/320x260.png
acb5b27
Fixed warning declaration of 'subwindow1' hides previous local declar…
c7e4c3a
Fixed warning 'const' qualifier on reference type 'cv::InputArray' (a…
fee6573
Accuracy test added/
5795c0a
Renamed void edgepreservingFilter to void edgePreservingFilter
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
modules/ximgproc/include/opencv2/ximgproc/edgepreserving_filter.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// 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. | ||
|
||
#ifndef __OPENCV_EDGEPRESERVINGFILTER_HPP__ | ||
#define __OPENCV_EDGEPRESERVINGFILTER_HPP__ | ||
|
||
#include <opencv2/core.hpp> | ||
|
||
namespace cv { namespace ximgproc { | ||
|
||
//! @addtogroup ximgproc | ||
//! @{ | ||
|
||
/** | ||
* @brief Smoothes an image using the Edge-Preserving filter. | ||
* | ||
* The function smoothes Gaussian noise as well as salt & pepper noise. | ||
* For more details about this implementation, please see | ||
* [ReiWoe18] 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. | ||
* | ||
* @param src Source 8-bit 3-channel image. | ||
* @param dst Destination image of the same size and type as src. | ||
* @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 ); | ||
|
||
}} // namespace | ||
|
||
//! @} | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// 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. | ||
// | ||
// Created by Simon Reich | ||
// | ||
#include "perf_precomp.hpp" | ||
|
||
namespace opencv_test | ||
{ | ||
namespace | ||
{ | ||
|
||
/* 1. Define parameter type and test fixture */ | ||
typedef tuple<int, double> RGFTestParam; | ||
typedef TestBaseWithParam<RGFTestParam> EdgepreservingFilterTest; | ||
|
||
/* 2. Declare the testsuite */ | ||
PERF_TEST_P(EdgepreservingFilterTest, perf, | ||
Combine(Values(-20, 0, 10), Values(-100, 0, 20))) | ||
{ | ||
/* 3. Get actual test parameters */ | ||
RGFTestParam params = GetParam(); | ||
int kernelSize = get<0>(params); | ||
double threshold = get<1>(params); | ||
|
||
/* 4. Allocate and initialize arguments for tested function */ | ||
std::string filename = getDataPath("perf/320x260.png"); | ||
Mat src = imread(filename, 1); | ||
Mat dst(src.size(), src.type()); | ||
|
||
/* 5. Manifest your expectations about this test */ | ||
declare.in(src).out(dst); | ||
|
||
/* 6. Collect the samples! */ | ||
PERF_SAMPLE_BEGIN(); | ||
ximgproc::edgePreservingFilter(src, dst, kernelSize, threshold); | ||
PERF_SAMPLE_END(); | ||
|
||
/* 7. Do not check anything */ | ||
SANITY_CHECK_NOTHING(); | ||
} | ||
|
||
} // namespace | ||
} // namespace opencv_test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
#include <iostream> | ||
#include <opencv2/highgui.hpp> | ||
#include <opencv2/ximgproc.hpp> | ||
#include <string> | ||
|
||
using namespace cv; | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
cv::CommandLineParser parser( | ||
argc, argv, | ||
"{help h ? | | help message}" | ||
"{@image | | Image filename to process }"); | ||
if (parser.has("help") || !parser.has("@image")) | ||
{ | ||
parser.printMessage(); | ||
return 0; | ||
} | ||
|
||
// Load image from first parameter | ||
std::string filename = parser.get<std::string>("@image"); | ||
Mat image = imread(filename, 1), res; | ||
|
||
if (!image.data) | ||
{ | ||
std::cerr << "No image data at " << filename << std::endl; | ||
throw; | ||
} | ||
|
||
// Before filtering | ||
imshow("Original image", image); | ||
waitKey(0); | ||
|
||
// Initialize filter. Kernel size 5x5, threshold 20 | ||
ximgproc::edgePreservingFilter(image, res, 9, 20); | ||
|
||
// After filtering | ||
imshow("Filtered image", res); | ||
waitKey(0); | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
// 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. | ||
// | ||
// Created by Simon Reich | ||
// | ||
|
||
#include "precomp.hpp" | ||
|
||
namespace cv | ||
{ | ||
namespace ximgproc | ||
{ | ||
using namespace std; | ||
|
||
void edgePreservingFilter(InputArray _src, OutputArray _dst, int d, | ||
double threshold) | ||
{ | ||
CV_Assert(_src.type() == CV_8UC3); | ||
|
||
Mat src = _src.getMat(); | ||
|
||
// [re]create the output array so that it has the proper size and type. | ||
_dst.create(src.size(), src.type()); | ||
Mat dst = _dst.getMat(); | ||
src.copyTo(dst); | ||
|
||
if (d < 3) | ||
d = 3; | ||
int subwindowX = d, subwindowY = d; | ||
|
||
if (threshold < 0) | ||
threshold = 0; | ||
|
||
// number of image channels | ||
int nChannel = src.channels(); | ||
|
||
vector<double> pixel(nChannel, 0); | ||
vector<vector<double>> line1(src.rows, pixel); | ||
vector<vector<vector<double>>> weight(src.cols, | ||
line1); // global weights | ||
vector<vector<vector<double>>> imageResult( | ||
src.cols, line1); // global normalized image | ||
|
||
// do algorithm | ||
cv::Mat subwindow, subwindow1; | ||
for (int posX = 0; posX < src.cols - subwindowX; posX++) | ||
{ | ||
for (int posY = 0; posY < src.rows - subwindowY; posY++) | ||
{ | ||
cv::Rect roi = | ||
cv::Rect(posX, posY, subwindowX, subwindowY); | ||
subwindow1 = src(roi); | ||
cv::GaussianBlur(subwindow1, subwindow, cv::Size(5, 5), | ||
0.3, 0.3); | ||
|
||
// compute arithmetic mean of subwindow | ||
cv::Scalar ArithmeticMean = cv::mean(subwindow); | ||
|
||
// compute pixelwise distance | ||
vector<vector<double>> pixelwiseDist; | ||
|
||
for (int subPosX = 0; subPosX < subwindow.cols; | ||
subPosX++) | ||
{ | ||
vector<double> line; | ||
for (int subPosY = 0; subPosY < subwindow.rows; | ||
subPosY++) | ||
{ | ||
cv::Vec3b intensity = | ||
subwindow.at<cv::Vec3b>(subPosY, | ||
subPosX); | ||
double distance = | ||
((double)intensity.val[0] - | ||
ArithmeticMean[0]) * | ||
((double)intensity.val[0] - | ||
ArithmeticMean[0]) + | ||
((double)intensity.val[1] - | ||
ArithmeticMean[1]) * | ||
((double)intensity.val[1] - | ||
ArithmeticMean[1]) + | ||
((double)intensity.val[2] - | ||
ArithmeticMean[2]) * | ||
((double)intensity.val[2] - | ||
ArithmeticMean[2]); | ||
distance = sqrt(distance); | ||
|
||
line.push_back(distance); | ||
}; | ||
|
||
pixelwiseDist.push_back(line); | ||
}; | ||
|
||
// compute mean pixelwise distance | ||
double meanPixelwiseDist = 0; | ||
|
||
for (int i = 0; i < (int)pixelwiseDist.size(); i++) | ||
for (int j = 0; | ||
j < (int)pixelwiseDist[i].size(); j++) | ||
meanPixelwiseDist += | ||
pixelwiseDist[i][j]; | ||
|
||
meanPixelwiseDist /= ((int)pixelwiseDist.size() * | ||
(int)pixelwiseDist[0].size()); | ||
|
||
// detect edge | ||
for (int subPosX = 0; subPosX < subwindow.cols; | ||
subPosX++) | ||
{ | ||
for (int subPosY = 0; subPosY < subwindow.rows; | ||
subPosY++) | ||
{ | ||
if ((meanPixelwiseDist <= threshold && | ||
pixelwiseDist[subPosX][subPosY] <= | ||
threshold) || | ||
(meanPixelwiseDist <= threshold && | ||
pixelwiseDist[subPosX][subPosY] > | ||
threshold)) | ||
{ | ||
// global Position | ||
int globalPosX = posX + subPosX; | ||
int globalPosY = posY + subPosY; | ||
|
||
// compute global weight | ||
cv::Vec3b intensity = | ||
subwindow.at<cv::Vec3b>( | ||
subPosY, subPosX); | ||
weight[globalPosX][globalPosY] | ||
[0] += | ||
intensity.val[0] * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]) * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]); | ||
weight[globalPosX][globalPosY] | ||
[1] += | ||
intensity.val[1] * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]) * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]); | ||
weight[globalPosX][globalPosY] | ||
[2] += | ||
intensity.val[2] * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]) * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]); | ||
|
||
// compute final image | ||
imageResult[globalPosX] | ||
[globalPosY][0] += | ||
intensity.val[0] * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]) * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]) * | ||
ArithmeticMean[0]; | ||
imageResult[globalPosX] | ||
[globalPosY][1] += | ||
intensity.val[1] * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]) * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]) * | ||
ArithmeticMean[1]; | ||
imageResult[globalPosX] | ||
[globalPosY][2] += | ||
intensity.val[2] * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]) * | ||
(threshold - | ||
pixelwiseDist[subPosX] | ||
[subPosY]) * | ||
ArithmeticMean[2]; | ||
}; | ||
}; | ||
}; | ||
}; | ||
}; | ||
|
||
// compute final image | ||
for (int globalPosX = 0; globalPosX < (int)imageResult.size(); | ||
globalPosX++) | ||
{ | ||
for (int globalPosY = 0; | ||
globalPosY < (int)imageResult[globalPosX].size(); | ||
globalPosY++) | ||
{ | ||
// cout << "globalPosX: " << globalPosX << "/" | ||
// << dst.cols << "," << imageResult.size () << | ||
// "\tglobalPosY: " << globalPosY << "/" << | ||
// dst.rows << "," <<imageResult.at | ||
// (globalPosX).size () << endl; | ||
|
||
// add image to result | ||
cv::Vec3b intensity = | ||
src.at<cv::Vec3b>(globalPosY, globalPosX); | ||
imageResult[globalPosX][globalPosY][0] += | ||
(double)intensity.val[0]; | ||
imageResult[globalPosX][globalPosY][1] += | ||
(double)intensity.val[1]; | ||
imageResult[globalPosX][globalPosY][2] += | ||
(double)intensity.val[2]; | ||
|
||
// normalize using weight | ||
imageResult[globalPosX][globalPosY][0] /= | ||
(weight[globalPosX][globalPosY][0] + 1); | ||
imageResult[globalPosX][globalPosY][1] /= | ||
(weight[globalPosX][globalPosY][1] + 1); | ||
imageResult[globalPosX][globalPosY][2] /= | ||
(weight[globalPosX][globalPosY][2] + 1); | ||
|
||
// copy to output image frame | ||
dst.at<cv::Vec3b>(globalPosY, globalPosX)[0] = | ||
(uchar)imageResult[globalPosX][globalPosY][0]; | ||
dst.at<cv::Vec3b>(globalPosY, globalPosX)[1] = | ||
(uchar)imageResult[globalPosX][globalPosY][1]; | ||
dst.at<cv::Vec3b>(globalPosY, globalPosX)[2] = | ||
(uchar)imageResult[globalPosX][globalPosY][2]; | ||
}; | ||
}; | ||
} | ||
} // namespace ximgproc | ||
} // namespace cv |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.