diff --git a/modules/photo/include/opencv2/photo.hpp b/modules/photo/include/opencv2/photo.hpp index 2d1087e89..c25a35e6d 100644 --- a/modules/photo/include/opencv2/photo.hpp +++ b/modules/photo/include/opencv2/photo.hpp @@ -138,6 +138,31 @@ parameter. CV_EXPORTS_W void fastNlMeansDenoising( InputArray src, OutputArray dst, float h = 3, int templateWindowSize = 7, int searchWindowSize = 21); +/** @brief Perform image denoising using Non-local Means Denoising +algorithm +with several computational optimizations. Noise expected to be a +gaussian white noise. Uses squared sum of absolute value distances +instead of sum of squared distances for weight calculation + +@param src Input 8-bit or 16-bit 1-channel, 2-channel or 3-channel image. +@param dst Output image with the same size and type as src . +@param templateWindowSize Size in pixels of the template patch that is used to compute weights. +Should be odd. Recommended value 7 pixels +@param searchWindowSize Size in pixels of the window that is used to compute weighted average for +given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater +denoising time. Recommended value 21 pixels +@param h Parameter regulating filter strength. Big h value perfectly removes noise but also +removes image details, smaller h value preserves details but also preserves some noise + +This function expected to be applied to grayscale images. For colored images look at +fastNlMeansDenoisingColored. Advanced usage of this functions can be manual denoising of colored +image in different colorspaces. Such approach is used in fastNlMeansDenoisingColored by converting +image to CIELAB colorspace and then separately denoise L and AB components with different h +parameter. + */ +CV_EXPORTS_W void fastNlMeansDenoisingAbs( InputArray src, OutputArray dst, float h = 3, + int templateWindowSize = 7, int searchWindowSize = 21); + /** @brief Modification of fastNlMeansDenoising function for colored images @param src Input 8-bit 3-channel image. @@ -186,6 +211,37 @@ CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs, OutputA int imgToDenoiseIndex, int temporalWindowSize, float h = 3, int templateWindowSize = 7, int searchWindowSize = 21); +/** @brief Modification of fastNlMeansDenoising function for images +sequence where consequtive images have been captured in small period +of time. For example video. This version of the function is for +grayscale images or for manual manipulation with colorspaces. For more +details see +. Uses +squared sum of absolute value distances instead of sum of squared +distances for weight calculation + +@param srcImgs Input 8-bit or 16-bit 1-channel, 2-channel or 3-channel +images sequence. All images should +have the same type and size. +@param imgToDenoiseIndex Target image to denoise index in srcImgs sequence +@param temporalWindowSize Number of surrounding images to use for target image denoising. Should +be odd. Images from imgToDenoiseIndex - temporalWindowSize / 2 to +imgToDenoiseIndex - temporalWindowSize / 2 from srcImgs will be used to denoise +srcImgs[imgToDenoiseIndex] image. +@param dst Output image with the same size and type as srcImgs images. +@param templateWindowSize Size in pixels of the template patch that is used to compute weights. +Should be odd. Recommended value 7 pixels +@param searchWindowSize Size in pixels of the window that is used to compute weighted average for +given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater +denoising time. Recommended value 21 pixels +@param h Parameter regulating filter strength for luminance component. Bigger h value perfectly +removes noise but also removes image details, smaller h value preserves details but also preserves +some noise + */ +CV_EXPORTS_W void fastNlMeansDenoisingMultiAbs( InputArrayOfArrays srcImgs, OutputArray dst, + int imgToDenoiseIndex, int temporalWindowSize, + float h = 3, int templateWindowSize = 7, int searchWindowSize = 21); + /** @brief Modification of fastNlMeansDenoisingMulti function for colored images sequences @param srcImgs Input 8-bit 3-channel images sequence. All images should have the same type and diff --git a/modules/photo/src/denoising.cpp b/modules/photo/src/denoising.cpp index 8f9d1f84a..52065b5f6 100644 --- a/modules/photo/src/denoising.cpp +++ b/modules/photo/src/denoising.cpp @@ -65,32 +65,62 @@ void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst, float h, switch (src.type()) { case CV_8U: parallel_for_(cv::Range(0, src.rows), - FastNlMeansDenoisingInvoker( + FastNlMeansDenoisingInvoker( src, dst, templateWindowSize, searchWindowSize, h)); break; case CV_8UC2: parallel_for_(cv::Range(0, src.rows), - FastNlMeansDenoisingInvoker( + FastNlMeansDenoisingInvoker( src, dst, templateWindowSize, searchWindowSize, h)); break; case CV_8UC3: parallel_for_(cv::Range(0, src.rows), - FastNlMeansDenoisingInvoker( + FastNlMeansDenoisingInvoker( + src, dst, templateWindowSize, searchWindowSize, h)); + break; + default: + CV_Error(Error::StsBadArg, + "Unsupported image format! Only CV_8U, CV_8UC2, and CV_8UC3 are supported"); + } +} + +void cv::fastNlMeansDenoisingAbs( InputArray _src, OutputArray _dst, float h, + int templateWindowSize, int searchWindowSize) +{ + Size src_size = _src.size(); + Mat src = _src.getMat(); + _dst.create(src_size, src.type()); + Mat dst = _dst.getMat(); + + switch (src.type()) { + case CV_8U: + parallel_for_(cv::Range(0, src.rows), + FastNlMeansDenoisingInvoker( + src, dst, templateWindowSize, searchWindowSize, h)); + break; + case CV_8UC2: + parallel_for_(cv::Range(0, src.rows), + FastNlMeansDenoisingInvoker( + src, dst, templateWindowSize, searchWindowSize, h)); + break; + case CV_8UC3: + parallel_for_(cv::Range(0, src.rows), + FastNlMeansDenoisingInvoker( src, dst, templateWindowSize, searchWindowSize, h)); break; case CV_16U: parallel_for_(cv::Range(0, src.rows), - FastNlMeansDenoisingInvoker( + FastNlMeansDenoisingInvoker( src, dst, templateWindowSize, searchWindowSize, h)); break; case CV_16UC2: parallel_for_(cv::Range(0, src.rows), - FastNlMeansDenoisingInvoker, int64, uint64>( + FastNlMeansDenoisingInvoker, int64, uint64, DistAbs>( src, dst, templateWindowSize, searchWindowSize, h)); break; case CV_16UC3: parallel_for_(cv::Range(0, src.rows), - FastNlMeansDenoisingInvoker, int64, uint64>( + FastNlMeansDenoisingInvoker, int64, uint64, DistAbs>( src, dst, templateWindowSize, searchWindowSize, h)); break; default: @@ -105,9 +135,9 @@ void cv::fastNlMeansDenoisingColored( InputArray _src, OutputArray _dst, { int type = _src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); Size src_size = _src.size(); - if (type != CV_8UC3 && type != CV_16UC3 && type != CV_8UC4 && type != CV_16UC4) + if (type != CV_8UC3 && type != CV_8UC4) { - CV_Error(Error::StsBadArg, "Type of input image should be CV_8UC3, CV_16UC3, CV_8UC4, or CV_16UC4"); + CV_Error(Error::StsBadArg, "Type of input image should be CV_8UC3 or CV_8UC4!"); return; } @@ -190,37 +220,77 @@ void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _ds { case CV_8U: parallel_for_(cv::Range(0, srcImgs[0].rows), - FastNlMeansMultiDenoisingInvoker( + FastNlMeansMultiDenoisingInvoker( srcImgs, imgToDenoiseIndex, temporalWindowSize, dst, templateWindowSize, searchWindowSize, h)); break; case CV_8UC2: parallel_for_(cv::Range(0, srcImgs[0].rows), - FastNlMeansMultiDenoisingInvoker( + FastNlMeansMultiDenoisingInvoker( srcImgs, imgToDenoiseIndex, temporalWindowSize, dst, templateWindowSize, searchWindowSize, h)); break; case CV_8UC3: parallel_for_(cv::Range(0, srcImgs[0].rows), - FastNlMeansMultiDenoisingInvoker( + FastNlMeansMultiDenoisingInvoker( + srcImgs, imgToDenoiseIndex, temporalWindowSize, + dst, templateWindowSize, searchWindowSize, h)); + break; + default: + CV_Error(Error::StsBadArg, + "Unsupported image format! Only CV_8U, CV_8UC2, and CV_8UC3 are supported"); + } +} + +void cv::fastNlMeansDenoisingMultiAbs( InputArrayOfArrays _srcImgs, OutputArray _dst, + int imgToDenoiseIndex, int temporalWindowSize, + float h, int templateWindowSize, int searchWindowSize) +{ + std::vector srcImgs; + _srcImgs.getMatVector(srcImgs); + + fastNlMeansDenoisingMultiCheckPreconditions( + srcImgs, imgToDenoiseIndex, + temporalWindowSize, templateWindowSize, searchWindowSize); + + _dst.create(srcImgs[0].size(), srcImgs[0].type()); + Mat dst = _dst.getMat(); + + switch (srcImgs[0].type()) + { + case CV_8U: + parallel_for_(cv::Range(0, srcImgs[0].rows), + FastNlMeansMultiDenoisingInvoker( + srcImgs, imgToDenoiseIndex, temporalWindowSize, + dst, templateWindowSize, searchWindowSize, h)); + break; + case CV_8UC2: + parallel_for_(cv::Range(0, srcImgs[0].rows), + FastNlMeansMultiDenoisingInvoker( + srcImgs, imgToDenoiseIndex, temporalWindowSize, + dst, templateWindowSize, searchWindowSize, h)); + break; + case CV_8UC3: + parallel_for_(cv::Range(0, srcImgs[0].rows), + FastNlMeansMultiDenoisingInvoker( srcImgs, imgToDenoiseIndex, temporalWindowSize, dst, templateWindowSize, searchWindowSize, h)); break; case CV_16U: parallel_for_(cv::Range(0, srcImgs[0].rows), - FastNlMeansMultiDenoisingInvoker( + FastNlMeansMultiDenoisingInvoker( srcImgs, imgToDenoiseIndex, temporalWindowSize, dst, templateWindowSize, searchWindowSize, h)); break; case CV_16UC2: parallel_for_(cv::Range(0, srcImgs[0].rows), - FastNlMeansMultiDenoisingInvoker, int64, uint64>( + FastNlMeansMultiDenoisingInvoker, int64, uint64, DistAbs>( srcImgs, imgToDenoiseIndex, temporalWindowSize, dst, templateWindowSize, searchWindowSize, h)); break; case CV_16UC3: parallel_for_(cv::Range(0, srcImgs[0].rows), - FastNlMeansMultiDenoisingInvoker, int64, uint64>( + FastNlMeansMultiDenoisingInvoker, int64, uint64, DistAbs>( srcImgs, imgToDenoiseIndex, temporalWindowSize, dst, templateWindowSize, searchWindowSize, h)); break; @@ -248,9 +318,9 @@ void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, OutputAr int type = srcImgs[0].type(), depth = CV_MAT_DEPTH(type); int src_imgs_size = static_cast(srcImgs.size()); - if (type != CV_8UC3 && type != CV_16UC3) + if (type != CV_8UC3) { - CV_Error(Error::StsBadArg, "Type of input images should be CV_8UC3 or CV_16UC3!"); + CV_Error(Error::StsBadArg, "Type of input images should be CV_8UC3!"); return; } diff --git a/modules/photo/src/fast_nlmeans_denoising_invoker.hpp b/modules/photo/src/fast_nlmeans_denoising_invoker.hpp index a641c990e..468fa82f7 100644 --- a/modules/photo/src/fast_nlmeans_denoising_invoker.hpp +++ b/modules/photo/src/fast_nlmeans_denoising_invoker.hpp @@ -50,7 +50,7 @@ using namespace cv; -template +template struct FastNlMeansDenoisingInvoker : public ParallelLoopBody { @@ -99,8 +99,8 @@ inline int getNearestPowerOf2(int value) return p; } -template -FastNlMeansDenoisingInvoker::FastNlMeansDenoisingInvoker( +template +FastNlMeansDenoisingInvoker::FastNlMeansDenoisingInvoker( const Mat& src, Mat& dst, int template_window_size, int search_window_size, @@ -128,7 +128,7 @@ FastNlMeansDenoisingInvoker::FastNlMeansDenoisingInvoker( almost_template_window_size_sq_bin_shift_ = getNearestPowerOf2(template_window_size_sq); double almost_dist2actual_dist_multiplier = ((double)(1 << almost_template_window_size_sq_bin_shift_)) / template_window_size_sq; - IT max_dist = (IT)pixelInfo::sampleMax() * (IT)pixelInfo::channels; + IT max_dist = D::template maxDist(); size_t almost_max_dist = (size_t)(max_dist / almost_dist2actual_dist_multiplier + 1); almost_dist2weight_.resize(almost_max_dist); @@ -136,7 +136,7 @@ FastNlMeansDenoisingInvoker::FastNlMeansDenoisingInvoker( for (int almost_dist = 0; almost_dist < almost_max_dist; almost_dist++) { double dist = almost_dist * almost_dist2actual_dist_multiplier; - IT weight = (IT)round(fixed_point_mult_ * std::exp(-dist*dist / (h * h * pixelInfo::channels))); + IT weight = (IT)round(fixed_point_mult_ * D::template calcWeight(dist, h)); if (weight < WEIGHT_THRESHOLD * fixed_point_mult_) weight = 0; @@ -149,8 +149,8 @@ FastNlMeansDenoisingInvoker::FastNlMeansDenoisingInvoker( dst_ = Mat::zeros(src_.size(), src_.type()); } -template -void FastNlMeansDenoisingInvoker::operator() (const Range& range) const +template +void FastNlMeansDenoisingInvoker::operator() (const Range& range) const { int row_from = range.start; int row_to = range.end - 1; @@ -215,7 +215,7 @@ void FastNlMeansDenoisingInvoker::operator() (const Range& range) co dist_sums_row[x] -= col_dist_sums_row[x]; int bx = start_bx + x; - col_dist_sums_row[x] = up_col_dist_sums_row[x] + calcUpDownDist(a_up, a_down, b_up_ptr[bx], b_down_ptr[bx]); + col_dist_sums_row[x] = up_col_dist_sums_row[x] + D::template calcUpDownDist(a_up, a_down, b_up_ptr[bx], b_down_ptr[bx]); dist_sums_row[x] += col_dist_sums_row[x]; up_col_dist_sums_row[x] = col_dist_sums_row[x]; @@ -254,8 +254,8 @@ void FastNlMeansDenoisingInvoker::operator() (const Range& range) co } } -template -inline void FastNlMeansDenoisingInvoker::calcDistSumsForFirstElementInRow( +template +inline void FastNlMeansDenoisingInvoker::calcDistSumsForFirstElementInRow( int i, Array2d& dist_sums, Array3d& col_dist_sums, @@ -276,7 +276,7 @@ inline void FastNlMeansDenoisingInvoker::calcDistSumsForFirstElement for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++) for (int tx = -template_window_half_size_; tx <= template_window_half_size_; tx++) { - int dist = calcDist(extended_src_, + int dist = D::template calcDist(extended_src_, border_size_ + i + ty, border_size_ + j + tx, border_size_ + start_y + ty, border_size_ + start_x + tx); @@ -288,8 +288,8 @@ inline void FastNlMeansDenoisingInvoker::calcDistSumsForFirstElement } } -template -inline void FastNlMeansDenoisingInvoker::calcDistSumsForElementInFirstRow( +template +inline void FastNlMeansDenoisingInvoker::calcDistSumsForElementInFirstRow( int i, int j, int first_col_num, Array2d& dist_sums, Array3d& col_dist_sums, @@ -312,7 +312,7 @@ inline void FastNlMeansDenoisingInvoker::calcDistSumsForElementInFir int by = start_by + y; int bx = start_bx + x; for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++) - col_dist_sums[new_last_col_num][y][x] += calcDist(extended_src_, ay + ty, ax, by + ty, bx); + col_dist_sums[new_last_col_num][y][x] += D::template calcDist(extended_src_, ay + ty, ax, by + ty, bx); dist_sums[y][x] += col_dist_sums[new_last_col_num][y][x]; up_col_dist_sums[j][y][x] = col_dist_sums[new_last_col_num][y][x]; diff --git a/modules/photo/src/fast_nlmeans_denoising_invoker_commons.hpp b/modules/photo/src/fast_nlmeans_denoising_invoker_commons.hpp index 4ca63d652..d55d93ce7 100644 --- a/modules/photo/src/fast_nlmeans_denoising_invoker_commons.hpp +++ b/modules/photo/src/fast_nlmeans_denoising_invoker_commons.hpp @@ -81,47 +81,150 @@ template struct pixelInfo: public pixelInfo_ } }; -template struct calcDist_ +class DistAbs { - static inline IT f(const T a, const T b) + template struct calcDist_ { - return std::abs((IT)(a-b)); + static inline IT f(const T a, const T b) + { + return std::abs((IT)(a-b)); + } + }; + + template struct calcDist_, IT> + { + static inline IT f(const Vec a, const Vec b) + { + return std::abs((IT)(a[0]-b[0])) + std::abs((IT)(a[1]-b[1])); + } + }; + + template struct calcDist_, IT> + { + static inline IT f(const Vec a, const Vec b) + { + return + std::abs((IT)(a[0]-b[0])) + + std::abs((IT)(a[1]-b[1])) + + std::abs((IT)(a[2]-b[2])); + } + }; + +public: + template static inline IT calcDist(const T a, const T b) + { + return calcDist_::f(a, b); + } + + template + static inline IT calcDist(const Mat& m, int i1, int j1, int i2, int j2) + { + const T a = m.at(i1, j1); + const T b = m.at(i2, j2); + return calcDist(a,b); + } + + template + static inline IT calcUpDownDist(T a_up, T a_down, T b_up, T b_down) + { + return calcDist(a_down, b_down) - calcDist(a_up, b_up); + }; + + template + static double calcWeight(double dist, double h) + { + return std::exp(-dist*dist / (h * h * pixelInfo::channels)); + } + + template + static double maxDist() + { + return (IT)pixelInfo::sampleMax() * (IT)pixelInfo::channels; } }; -template struct calcDist_, IT> +class DistSquared { - static inline IT f(const Vec a, const Vec b) + template struct calcDist_ { - return std::abs((IT)(a[0]-b[0])) + std::abs((IT)(a[1]-b[1])); - } -}; + static inline IT f(const T a, const T b) + { + return (IT)(a-b) * (IT)(a-b); + } + }; -template struct calcDist_, IT> -{ - static inline IT f(const Vec a, const Vec b) + template struct calcDist_, IT> { - return std::abs((IT)(a[0]-b[0])) + std::abs((IT)(a[1]-b[1])) + std::abs((IT)(a[2]-b[2])); + static inline IT f(const Vec a, const Vec b) + { + return (IT)(a[0]-b[0])*(IT)(a[0]-b[0]) + (IT)(a[1]-b[1])*(IT)(a[1]-b[1]); + } + }; + + template struct calcDist_, IT> + { + static inline IT f(const Vec a, const Vec b) + { + return + (IT)(a[0]-b[0])*(IT)(a[0]-b[0]) + + (IT)(a[1]-b[1])*(IT)(a[1]-b[1]) + + (IT)(a[2]-b[2])*(IT)(a[2]-b[2]); + } + }; + + template struct calcUpDownDist_ + { + static inline IT f(T a_up, T a_down, T b_up, T b_down) + { + IT A = a_down - b_down; + IT B = a_up - b_up; + return (A-B)*(A+B); + } + }; + + template struct calcUpDownDist_, IT> + { + private: + typedef Vec T; + public: + static inline IT f(T a_up, T a_down, T b_up, T b_down) + { + return calcDist(a_down, b_down) - calcDist(a_up, b_up); + } + }; + +public: + template static inline IT calcDist(const T a, const T b) + { + return calcDist_::f(a, b); } -}; -template static inline IT calcDist(const T a, const T b) -{ - return calcDist_::f(a, b); -} + template + static inline IT calcDist(const Mat& m, int i1, int j1, int i2, int j2) + { + const T a = m.at(i1, j1); + const T b = m.at(i2, j2); + return calcDist(a,b); + } -template -static inline IT calcDist(const Mat& m, int i1, int j1, int i2, int j2) -{ - const T a = m.at(i1, j1); - const T b = m.at(i2, j2); - return calcDist(a,b); -} + template + static inline IT calcUpDownDist(T a_up, T a_down, T b_up, T b_down) + { + return calcUpDownDist_::f(a_up, a_down, b_up, b_down); + }; -template -static inline IT calcUpDownDist(T a_up, T a_down, T b_up, T b_down) -{ - return calcDist(a_down, b_down) - calcDist(a_up, b_up); + template + static double calcWeight(double dist, double h) + { + return std::exp(-dist / (h * h * pixelInfo::channels)); + } + + template + static double maxDist() + { + return (IT)pixelInfo::sampleMax() * (IT)pixelInfo::sampleMax() * + (IT)pixelInfo::channels; + } }; template struct incWithWeight_ diff --git a/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp index 808b01f50..0a2bdd739 100644 --- a/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp +++ b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp @@ -50,7 +50,7 @@ using namespace cv; -template +template struct FastNlMeansMultiDenoisingInvoker : ParallelLoopBody { @@ -94,8 +94,8 @@ private: Array4d& up_col_dist_sums) const; }; -template -FastNlMeansMultiDenoisingInvoker::FastNlMeansMultiDenoisingInvoker( +template +FastNlMeansMultiDenoisingInvoker::FastNlMeansMultiDenoisingInvoker( const std::vector& srcImgs, int imgToDenoiseIndex, int temporalWindowSize, @@ -139,7 +139,7 @@ FastNlMeansMultiDenoisingInvoker::FastNlMeansMultiDenoisingInvoker( int almost_template_window_size_sq = 1 << almost_template_window_size_sq_bin_shift; double almost_dist2actual_dist_multiplier = (double) almost_template_window_size_sq / template_window_size_sq; - IT max_dist = (IT)pixelInfo::sampleMax() * (IT)pixelInfo::channels; + IT max_dist = D::template maxDist(); int almost_max_dist = (int) (max_dist / almost_dist2actual_dist_multiplier + 1); almost_dist2weight.resize(almost_max_dist); @@ -147,7 +147,7 @@ FastNlMeansMultiDenoisingInvoker::FastNlMeansMultiDenoisingInvoker( for (int almost_dist = 0; almost_dist < almost_max_dist; almost_dist++) { double dist = almost_dist * almost_dist2actual_dist_multiplier; - IT weight = (IT)round(fixed_point_mult_ * std::exp(-dist*dist / (h * h * pixelInfo::channels))); + IT weight = (IT)round(fixed_point_mult_ * D::template calcWeight(dist, h)); if (weight < WEIGHT_THRESHOLD * fixed_point_mult_) weight = 0; @@ -160,8 +160,8 @@ FastNlMeansMultiDenoisingInvoker::FastNlMeansMultiDenoisingInvoker( dst_ = Mat::zeros(srcImgs[0].size(), srcImgs[0].type()); } -template -void FastNlMeansMultiDenoisingInvoker::operator() (const Range& range) const +template +void FastNlMeansMultiDenoisingInvoker::operator() (const Range& range) const { int row_from = range.start; int row_to = range.end - 1; @@ -234,7 +234,7 @@ void FastNlMeansMultiDenoisingInvoker::operator() (const Range& rang dist_sums_row[x] -= col_dist_sums_row[x]; col_dist_sums_row[x] = up_col_dist_sums_row[x] + - calcUpDownDist(a_up, a_down, b_up_ptr[start_bx + x], b_down_ptr[start_bx + x]); + D::template calcUpDownDist(a_up, a_down, b_up_ptr[start_bx + x], b_down_ptr[start_bx + x]); dist_sums_row[x] += col_dist_sums_row[x]; up_col_dist_sums_row[x] = col_dist_sums_row[x]; @@ -284,8 +284,8 @@ void FastNlMeansMultiDenoisingInvoker::operator() (const Range& rang } } -template -inline void FastNlMeansMultiDenoisingInvoker::calcDistSumsForFirstElementInRow( +template +inline void FastNlMeansMultiDenoisingInvoker::calcDistSumsForFirstElementInRow( int i, Array3d& dist_sums, Array4d& col_dist_sums, Array4d& up_col_dist_sums) const { int j = 0; @@ -310,7 +310,7 @@ inline void FastNlMeansMultiDenoisingInvoker::calcDistSumsForFirstEl { for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++) { - IT dist = calcDist( + IT dist = D::template calcDist( main_extended_src_.at(border_size_ + i + ty, border_size_ + j + tx), cur_extended_src.at(border_size_ + start_y + ty, border_size_ + start_x + tx)); @@ -325,8 +325,8 @@ inline void FastNlMeansMultiDenoisingInvoker::calcDistSumsForFirstEl } } -template -inline void FastNlMeansMultiDenoisingInvoker::calcDistSumsForElementInFirstRow( +template +inline void FastNlMeansMultiDenoisingInvoker::calcDistSumsForElementInFirstRow( int i, int j, int first_col_num, Array3d& dist_sums, Array4d& col_dist_sums, Array4d& up_col_dist_sums) const { @@ -353,7 +353,7 @@ inline void FastNlMeansMultiDenoisingInvoker::calcDistSumsForElement IT* col_dist_sums_ptr = &col_dist_sums[new_last_col_num][d][y][x]; for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++) { - *col_dist_sums_ptr += calcDist( + *col_dist_sums_ptr += D::template calcDist( main_extended_src_.at(ay + ty, ax), cur_extended_src.at(by + ty, bx)); }