diff --git a/modules/README.md b/modules/README.md index f0733abfd..52a0266b9 100644 --- a/modules/README.md +++ b/modules/README.md @@ -62,7 +62,7 @@ $ cmake -D OPENCV_EXTRA_MODULES_PATH=/modules -D BUILD_opencv_ + +namespace cv { namespace ximgproc { + +//! @addtogroup ximgproc_filters +//! @{ + +/** @brief Applies Ridge Detection Filter to an input image. +Implements Ridge detection similar to the one in [Mathematica](http://reference.wolfram.com/language/ref/RidgeFilter.html) +using the eigen values from the Hessian Matrix of the input image using Sobel Derivatives. +Additional refinement can be done using Skeletonization and Binarization. +*/ +class CV_EXPORTS_W RidgeDetectionFilter : public Algorithm +{ +public: + /** + @brief Create pointer to the Ridge detection filter. + @param ddepth Specifies output image depth. Defualt is CV_32FC1 + @param dx Order of derivative x, default is 1 + @param dy Order of derivative y, default is 1 + @param ksize Sobel kernel size , default is 3 + @param out_dtype Converted format for output, default is CV_8UC1 + @param scale Optional scale value for derivative values, default is 1 + @param delta Optional bias added to output, default is 0 + @param borderType Pixel extrapolation method, default is BORDER_DEFAULT + @see Sobel, threshold, getStructuringElement, morphologyEx.( for additional refinement) + */ + CV_WRAP static Ptr create(int ddepth = CV_32FC1, int dx=1, int dy=1, int ksize = 3, int out_dtype=CV_8UC1, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT); + /** + @brief Apply Ridge detection filter on input image. + @param _img InputArray as supported by Sobel. img can be 1-Channel or 3-Channels. + @param out OutputAray of structure as RidgeDetectionFilter::ddepth. Output image with ridges. + */ + CV_WRAP virtual void getRidgeFilteredImage(InputArray _img, OutputArray out) = 0; +}; + +//! @} +}} // namespace +#endif diff --git a/modules/ximgproc/perf/perf_ridge_detection_filter.cpp b/modules/ximgproc/perf/perf_ridge_detection_filter.cpp new file mode 100644 index 000000000..ef1ded6e6 --- /dev/null +++ b/modules/ximgproc/perf/perf_ridge_detection_filter.cpp @@ -0,0 +1,39 @@ +// 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. +#include "perf_precomp.hpp" + +namespace cvtest { + +using namespace perf; +using namespace testing; +using namespace cv; +using namespace cv::ximgproc; + +typedef tuple RDFParams; +typedef TestBaseWithParam RidgeDetectionFilterPerfTest; + +PERF_TEST_P(RidgeDetectionFilterPerfTest, perf, Combine( + Values((MatDepth)CV_32F), + Values(3), + SZ_TYPICAL +)) +{ + RDFParams params = GetParam(); + int ddepth = get<0>(params); + int ksize = get<1>(params); + Size sz = get<2>(params); + + Mat src(sz, ddepth); + Mat out(sz, src.type()); + + declare.in(src).out(out); + + Ptr rdf = RidgeDetectionFilter::create(ddepth,1, 1, ksize); + + TEST_CYCLE() rdf->getRidgeFilteredImage(src, out); + + SANITY_CHECK_NOTHING(); +} + +} // namespace diff --git a/modules/ximgproc/src/ridgedetectionfilter.cpp b/modules/ximgproc/src/ridgedetectionfilter.cpp new file mode 100644 index 000000000..dd4a383df --- /dev/null +++ b/modules/ximgproc/src/ridgedetectionfilter.cpp @@ -0,0 +1,73 @@ +// 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. + +#include "precomp.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/ximgproc/ridgefilter.hpp" + +namespace cv { namespace ximgproc { + +class RidgeDetectionFilterImpl : public RidgeDetectionFilter +{ +public: + int _ddepth, _dx, _dy, _ksize; + double _scale, _delta; + int _borderType; + int _out_dtype; + RidgeDetectionFilterImpl(int ddepth=CV_32FC1, int dx=1, int dy=1, int ksize = 3, int out_dtype=CV_8UC1, double scale = 1, double delta = 0, int borderType = BORDER_DEFAULT) + { + CV_Assert((ksize == 1 || ksize == 3 || ksize == 5 || ksize == 7)); + CV_Assert((ddepth == CV_32FC1 || ddepth == CV_64FC1)); + _ddepth = ddepth; + _dx = dx; + _dy = dy; + _ksize = ksize; + _scale = scale; + _delta = delta; + _borderType = borderType; + _out_dtype = out_dtype; + } + virtual void getRidgeFilteredImage(InputArray _img, OutputArray out); +}; + +void RidgeDetectionFilterImpl::getRidgeFilteredImage(InputArray _img, OutputArray out) +{ + Mat img = _img.getMat(); + CV_Assert(img.channels() == 1 || img.channels() == 3); + + if(img.channels() == 3) + cvtColor(img, img, COLOR_BGR2GRAY); + + Mat sbx, sby; + Sobel(img, sbx, _ddepth, _dx, 0, _ksize, _scale, _delta, _borderType); + Sobel(img, sby, _ddepth, 0, _dy, _ksize, _scale, _delta, _borderType); + + Mat sbxx, sbyy, sbxy; + Sobel(sbx, sbxx, _ddepth, _dx, 0, _ksize, _scale, _delta, _borderType); + Sobel(sby, sbyy, _ddepth, 0, _dy, _ksize, _scale, _delta, _borderType); + Sobel(sbx, sbxy, _ddepth, 0, _dy, _ksize, _scale, _delta, _borderType); + + Mat sb2xx, sb2yy, sb2xy; + multiply(sbxx, sbxx, sb2xx); + multiply(sbyy, sbyy, sb2yy); + multiply(sbxy, sbxy, sb2xy); + + Mat sbxxyy; + multiply(sbxx, sbyy, sbxxyy); + + Mat rootex; + rootex = (sb2xx + (sb2xy + sb2xy + sb2xy + sb2xy) - (sbxxyy + sbxxyy) + sb2xy ); + Mat root; + sqrt(rootex, root); + Mat ridgexp; + ridgexp = ( (sbxx + sbyy) + root ); + ridgexp.convertTo(out, _out_dtype, 0.5); +} + +Ptr RidgeDetectionFilter::create(int ddepth, int dx, int dy, int ksize, int out_dtype, double scale, double delta, int borderType) +{ + return makePtr(ddepth, dx, dy, ksize, out_dtype, scale, delta, borderType); +} + +}} // namespace diff --git a/modules/ximgproc/test/test_ridge_detection_filter.cpp b/modules/ximgproc/test/test_ridge_detection_filter.cpp new file mode 100644 index 000000000..ba56f1ecf --- /dev/null +++ b/modules/ximgproc/test/test_ridge_detection_filter.cpp @@ -0,0 +1,32 @@ +// 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. + +#include "test_precomp.hpp" + +namespace cvtest { + +using namespace cv; +using namespace cv::ximgproc; + +TEST(ximgproc_ridgedetectionfilter, ReferenceAccuracy) +{ + String openCVExtraDir = cvtest::TS::ptr()->get_data_path(); + String srcImgPath = "cv/ximgproc/sources/04.png"; + String refPath = "cv/ximgproc/results/ridge_filter_test_ref/04.png"; + Mat src = imread(openCVExtraDir + srcImgPath); + Mat ref = imread(openCVExtraDir + refPath, 0); + Mat n_ref; + ref.convertTo(n_ref, CV_8UC1); + Ptr rdf = RidgeDetectionFilter::create(); + Mat out; + rdf->getRidgeFilteredImage(src, out); + Mat out_cmp; + out.convertTo(out_cmp, CV_8UC1); + Mat sb; + subtract(out_cmp, n_ref, sb); + int zeros = countNonZero(sb); + EXPECT_EQ(zeros, 0); +} + +} // namespace