From e92394d0aaf00cdfa789001b10c518788756e491 Mon Sep 17 00:00:00 2001 From: Bellaktris Date: Wed, 13 Aug 2014 23:54:40 +0700 Subject: [PATCH] fixed bugs in WH transform, some code compression --- .../ximpgroc/structured_edge_detection.rst | 2 +- modules/xphoto/CMakeLists.txt | 2 +- modules/xphoto/doc/inpainting/inpainting.rst | 20 +++ modules/xphoto/doc/xphoto.rst | 1 + modules/xphoto/samples/inpainting.cpp | 8 +- modules/xphoto/src/annf.hpp | 121 ++++++++-------- modules/xphoto/src/inpainting.cpp | 136 ++++++++++-------- modules/xphoto/src/norm2.hpp | 46 ++++-- modules/xphoto/src/photomontage.hpp | 3 +- modules/xphoto/src/whs.hpp | 90 +++++++----- 10 files changed, 262 insertions(+), 167 deletions(-) create mode 100644 modules/xphoto/doc/inpainting/inpainting.rst diff --git a/modules/ximpgroc/doc/ximpgroc/structured_edge_detection.rst b/modules/ximpgroc/doc/ximpgroc/structured_edge_detection.rst index 2600ae276..55848b8b0 100644 --- a/modules/ximpgroc/doc/ximpgroc/structured_edge_detection.rst +++ b/modules/ximpgroc/doc/ximpgroc/structured_edge_detection.rst @@ -62,6 +62,6 @@ createStructuredEdgeDetection :param model: model file name -.. [Dollar2013] Dollár P., Zitnick C. L., "Structured forests for fast edge detection", +.. [Dollar2013] P. Dollár, C. L. Zitnick, "Structured forests for fast edge detection", IEEE International Conference on Computer Vision (ICCV), 2013, pp. 1841-1848. `DOI `_ diff --git a/modules/xphoto/CMakeLists.txt b/modules/xphoto/CMakeLists.txt index 570d7e63c..31d04beb2 100644 --- a/modules/xphoto/CMakeLists.txt +++ b/modules/xphoto/CMakeLists.txt @@ -1,2 +1,2 @@ set(the_description "Addon to basic photo module") -ocv_define_module(xphoto opencv_core opencv_imgproc opencv_photo OPTIONAL opencv_photo opencv_highgui) \ No newline at end of file +ocv_define_module(xphoto opencv_core opencv_imgproc OPTIONAL opencv_photo opencv_highgui opencv_photo) \ No newline at end of file diff --git a/modules/xphoto/doc/inpainting/inpainting.rst b/modules/xphoto/doc/inpainting/inpainting.rst new file mode 100644 index 000000000..1bc654a90 --- /dev/null +++ b/modules/xphoto/doc/inpainting/inpainting.rst @@ -0,0 +1,20 @@ +Single image inpainting +*********************** + +.. highlight:: cpp + +Inpainting +---------- +.. ocv:function:: void inpaint(const Mat &src, const Mat &mask, Mat &dst, const int algorithmType) + +The function implements different single-image inpainting algorithms. + + :param src : source image, it could be of any type and any number of channels from 1 to 4. In case of 3- and 4-channels images the function expect them in CIELab colorspace or similar one, where first color component shows intensity, while second and third shows colors. Nonetheless you can try any colorspaces. + :param mask : mask (CV_8UC1), where non-zero pixels indicate valid image area, while zero pixels indicate area to be inpainted + :param dst : destination image + :param algorithmType : expected noise standard deviation + * INPAINT_SHIFTMAP: This algorithm searches for dominant correspondences (transformations) of image patches and tries to seamlessly fill-in the area to be inpainted using this transformations. Look in the original paper [He2012]_ for details. + + .. [He2012] K. He, J. Sun., "Statistics of Patch Offsets for Image Completion", + IEEE European Conference on Computer Vision (ICCV), 2012, + pp. 16-29. `DOI `_ \ No newline at end of file diff --git a/modules/xphoto/doc/xphoto.rst b/modules/xphoto/doc/xphoto.rst index a228c79b8..f2dc6a4c3 100644 --- a/modules/xphoto/doc/xphoto.rst +++ b/modules/xphoto/doc/xphoto.rst @@ -7,3 +7,4 @@ xphoto. Addon to basic photo modul Color balance Denoising + Inpainting diff --git a/modules/xphoto/samples/inpainting.cpp b/modules/xphoto/samples/inpainting.cpp index 59677e95c..ff37f9cca 100644 --- a/modules/xphoto/samples/inpainting.cpp +++ b/modules/xphoto/samples/inpainting.cpp @@ -46,6 +46,8 @@ int main( int argc, const char** argv ) return -1; } + cv::cvtColor(src, src, CV_RGB2Lab); + cv::Mat mask = cv::imread(maskFilename, 0); if ( mask.empty() ) { @@ -54,13 +56,13 @@ int main( int argc, const char** argv ) } cv::Mat res(src.size(), src.type()); - cv::inpaint( src, mask, res, cv::INPAINT_SHIFTMAP ); + cv::cvtColor(res, res, CV_Lab2RGB); if ( outFilename == "" ) { - cv::namedWindow("denoising result", 1); - cv::imshow("denoising result", res); + cv::namedWindow("inpainting result", 1); + cv::imshow("inpainting result", res); cv::waitKey(0); } diff --git a/modules/xphoto/src/annf.hpp b/modules/xphoto/src/annf.hpp index 86f603b28..44ce72784 100644 --- a/modules/xphoto/src/annf.hpp +++ b/modules/xphoto/src/annf.hpp @@ -76,15 +76,18 @@ private: : main(_main), dimIdx(_dimIdx) {} }; + const int height, width; const int leafNumber; + std::vector > data; + std::vector idx; + std::vector nodes; + int getMaxSpreadN(const int left, const int right) const; void operator =(const KDTree &) const {}; public: - std::vector > data; - std::vector idx; - std::vector nodes; + void updateDist(const int leaf, const int &idx0, int &bestIdx, double &dist); KDTree(const cv::Mat &data, const int leafNumber = 8); ~KDTree(){}; @@ -109,15 +112,16 @@ getMaxSpreadN(const int _left, const int _right) const template KDTree :: KDTree(const cv::Mat &img, const int _leafNumber) - : leafNumber(_leafNumber) + : height(img.rows), width(img.cols), + leafNumber(_leafNumber) /////////////////////////////////////////////////// { - for (int i = 0; i < img.rows; ++i) - for (int j = 0; j < img.cols; ++j) - data.push_back(img.template at >(i, j)); - + std::copy( (cv::Vec *) img.data, + (cv::Vec *) img.data + img.total(), + std::back_inserter(data) ); generate_seq( std::back_inserter(idx), 0, int(data.size()) ); - fill_n( std::back_inserter(nodes), int(data.size()), cv::Point2i(0, 0) ); + std::fill_n( std::back_inserter(nodes), + int(data.size()), cv::Point2i(0, 0) ); std::stack left, right; left.push( 0 ); @@ -125,75 +129,72 @@ KDTree(const cv::Mat &img, const int _leafNumber) while ( !left.empty() ) { - int _left = left.top(); left.pop(); + int _left = left.top(); left.pop(); int _right = right.top(); right.pop(); if ( _right - _left <= leafNumber) { for (int i = _left; i < _right; ++i) - { - nodes[idx[i]].x = _left; - nodes[idx[i]].y = _right; - } - + nodes[idx[i]] = cv::Point2i(_left, _right); continue; } - std::vector ::iterator begIt = idx.begin(); int nth = _left + (_right - _left)/2; - std::nth_element(/**/ begIt + _left, - begIt + nth, begIt + _right, - KDTreeComparator( this, - getMaxSpreadN(_left, _right) ) /**/); + int dimIdx = getMaxSpreadN(_left, _right); + KDTreeComparator comp( this, dimIdx ); + + std::nth_element(/**/ + idx.begin() + _left, + idx.begin() + nth, + idx.begin() + _right, comp + /**/); - left.push(_left); right.push(nth + 1); - left.push(nth + 1); right.push(_right); + left.push(_left); right.push(nth + 1); + left.push(nth + 1); right.push(_right); } } -/************************** ANNF search **************************/ - -template -static void updateDist(const KDTree &kdTree, const cv::Point2i &I, const int height, - const int width, const int ¤tIdx, int &bestIdx, double &dist) +template void KDTree :: +updateDist(const int leaf, const int &idx0, int &bestIdx, double &dist) { - for (int k = I.x; k < I.y; ++k) + for (int k = nodes[leaf].x; k < nodes[leaf].y; ++k) { - int newIdx = kdTree.idx[k]; + int y = idx0/width, ny = idx[k]/width; + int x = idx0%width, nx = idx[k]%width; - if (newIdx%width == width - 1) + if (abs(ny - y) + abs(nx - x) < 32) continue; - - if (newIdx/width == height - 1) + if (nx == width - 1 || ny == height - 1) continue; - int dx = currentIdx%width - newIdx%width; - int dy = currentIdx/width - newIdx/width; - - if (abs(dx) + abs(dy) < 32) - continue; + double ndist = norm2(data[idx0], data[idx[k]]); - double ndist = norm2(kdTree.data[newIdx], - kdTree.data[currentIdx]); if (ndist < dist) { dist = ndist; - bestIdx = newIdx; + bestIdx = idx[k]; } } } -static void getANNF(const cv::Mat &img, std::vector &transforms, - const int nTransform, const int psize) +/************************** ANNF search **************************/ + +static void dominantTransforms(const cv::Mat &img, std::vector &transforms, + const int nTransform, const int psize) { /** Walsh-Hadamard Transformation **/ std::vector channels; cv::split(img, channels); - const int np[] = {16, 4, 4}; + int cncase = std::max(img.channels() - 2, 0); + const int np[] = {cncase == 0 ? 12 : (cncase == 1 ? 16 : 10), + cncase == 0 ? 12 : (cncase == 1 ? 04 : 02), + cncase == 0 ? 00 : (cncase == 1 ? 04 : 02), + cncase == 0 ? 00 : (cncase == 1 ? 00 : 10)}; + for (int i = 0; i < img.channels(); ++i) - getWHSeries(channels[i], channels[i], np[i], psize); + rgb2whs(channels[i], channels[i], np[i], psize); cv::Mat whs; // Walsh-Hadamard series cv::merge(channels, whs); @@ -209,22 +210,16 @@ static void getANNF(const cv::Mat &img, std::vector &transforms, double dist = std::numeric_limits ::max(); int current = i*whs.cols + j; - cv::Point2i I = kdTree.nodes[i*whs.cols + j]; - updateDist(kdTree, I, whs.rows, whs.cols, current, annf[i*whs.cols + j], dist); - - if (i != 0) - { - int idx = annf[(i - 1)*whs.cols + j] + whs.cols; - cv::Point2i I = kdTree.nodes[idx]; - updateDist(kdTree, I, whs.rows, whs.cols, current, annf[i*whs.cols + j], dist); - } - - if (j != 0) - { - int idx = annf[i*whs.cols + (j - 1)] + 1; - cv::Point2i I = kdTree.nodes[idx]; - updateDist(kdTree, I, whs.rows, whs.cols, current, annf[i*whs.cols + j], dist); - } + int dy[] = {0, 1, 0}, dx[] = {0, 0, 1}; + for (int k = 0; k < sizeof(dy)/sizeof(int); ++k) + if (i - dy[k] >= 0 && j - dx[k] >= 0) + { + int neighbor = (i - dy[k])*whs.cols + (j - dx[k]); + int leafIdx = k == 0 ? neighbor : + annf[neighbor] + dy[k]*whs.cols + dx[k]; + kdTree.updateDist(leafIdx, current, + annf[i*whs.cols + j], dist); + } } /** Local maxima extraction **/ @@ -233,12 +228,12 @@ static void getANNF(const cv::Mat &img, std::vector &transforms, _annfHist(2*whs.rows, 2*whs.cols, 0.0); for (size_t i = 0; i < annf.size(); ++i) ++annfHist( (annf[i] - int(i))/whs.cols + whs.rows, - (annf[i] - int(i))%whs.cols + whs.cols); + (annf[i] - int(i))%whs.cols + whs.cols ); cv::GaussianBlur( annfHist, annfHist, cv::Size(9, 9), 1.41, 0.0, cv::BORDER_CONSTANT); - cv::dilate(annfHist, _annfHist, - cv::Matx::ones()); + cv::dilate( annfHist, _annfHist, + cv::Matx::ones() ); std::vector < std::pair > amount; std::vector shiftM; diff --git a/modules/xphoto/src/inpainting.cpp b/modules/xphoto/src/inpainting.cpp index 1b3defc20..5bb882155 100644 --- a/modules/xphoto/src/inpainting.cpp +++ b/modules/xphoto/src/inpainting.cpp @@ -57,6 +57,9 @@ #include "opencv2/core/types.hpp" #include "opencv2/core/types_c.h" +#include "opencv2/highgui.hpp" + + namespace xphotoInternal { # include "photomontage.hpp" @@ -66,22 +69,18 @@ namespace xphotoInternal namespace cv { template - static void shiftMapInpaint(const Mat &src, const Mat &mask, Mat &dst) + static void shiftMapInpaint(const Mat &src, const Mat &mask, Mat &dst, + const int nTransform = 60, const int psize = 8) { - const int nTransform = 60; // number of dominant transforms for stitching - const int psize = 8; // single ANNF patch size - + /** Preparing input **/ cv::Mat img; - - cvtColor(src, img, CV_RGB2Lab); + src.convertTo( img, CV_32F ); img.setTo(0, 255 - mask); - img.convertTo( img, CV_32F ); - /** ANNF computation **/ std::vector transforms( nTransform ); - xphotoInternal::getANNF(img, transforms, - nTransform, psize); + xphotoInternal::dominantTransforms(img, + transforms, nTransform, psize); /** Warping **/ std::vector images( nTransform + 1 ); // source image transformed with transforms @@ -109,8 +108,7 @@ namespace cv .assignResImage(photomontageResult); /** Writing result **/ - photomontageResult.convertTo( photomontageResult, dst.type() ); - cvtColor(photomontageResult, dst, CV_Lab2RGB); + photomontageResult.convertTo( dst, dst.type() ); } template @@ -143,66 +141,90 @@ namespace cv switch ( src.type() ) { - //case CV_8UC1: - // inpaint ( src, mask, dst, algorithmType ); - // break; - //case CV_8UC2: - // inpaint ( src, mask, dst, algorithmType ); - // break; + case CV_8SC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8SC2: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8SC3: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8SC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8UC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_8UC2: + inpaint ( src, mask, dst, algorithmType ); + break; case CV_8UC3: inpaint ( src, mask, dst, algorithmType ); break; - //case CV_8UC4: - // inpaint ( src, mask, dst, algorithmType ); - // break; - //case CV_16SC1: - // inpaint ( src, mask, dst, algorithmType ); - // break; - //case CV_16SC2: - // inpaint ( src, mask, dst, algorithmType ); - // break; + case CV_8UC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16SC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16SC2: + inpaint ( src, mask, dst, algorithmType ); + break; case CV_16SC3: inpaint ( src, mask, dst, algorithmType ); break; - //case CV_16SC4: - // inpaint ( src, mask, dst, algorithmType ); - // break; - //case CV_32SC1: - // inpaint ( src, mask, dst, algorithmType ); - // break; - //case CV_32SC2: - // inpaint ( src, mask, dst, algorithmType ); - // break; + case CV_16SC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16UC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16UC2: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16UC3: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_16UC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32SC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32SC2: + inpaint ( src, mask, dst, algorithmType ); + break; case CV_32SC3: inpaint ( src, mask, dst, algorithmType ); break; - //case CV_32SC4: - // inpaint ( src, mask, dst, algorithmType ); - // break; - //case CV_32FC1: - // inpaint ( src, mask, dst, algorithmType ); - // break; - //case CV_32FC2: - // inpaint ( src, mask, dst, algorithmType ); - // break; + case CV_32SC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32FC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_32FC2: + inpaint ( src, mask, dst, algorithmType ); + break; case CV_32FC3: inpaint ( src, mask, dst, algorithmType ); break; - //case CV_32FC4: - // inpaint ( src, mask, dst, algorithmType ); - // break; - //case CV_64FC1: - // inpaint ( src, mask, dst, algorithmType ); - // break; - //case CV_64FC2: - // inpaint ( src, mask, dst, algorithmType ); - // break; + case CV_32FC4: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_64FC1: + inpaint ( src, mask, dst, algorithmType ); + break; + case CV_64FC2: + inpaint ( src, mask, dst, algorithmType ); + break; case CV_64FC3: inpaint ( src, mask, dst, algorithmType ); break; - //case CV_64FC4: - // inpaint ( src, mask, dst, algorithmType ); - // break; + case CV_64FC4: + inpaint ( src, mask, dst, algorithmType ); + break; default: CV_Error_( CV_StsNotImplemented, ("Unsupported source image format (=%d)", diff --git a/modules/xphoto/src/norm2.hpp b/modules/xphoto/src/norm2.hpp index bf98b5297..209295f5d 100644 --- a/modules/xphoto/src/norm2.hpp +++ b/modules/xphoto/src/norm2.hpp @@ -40,24 +40,52 @@ #ifndef __NORM2_HPP__ #define __NORM2_HPP__ -static inline int sqr(uchar x) { return x*x; } +/************************ General template *************************/ + +template static inline Tp sqr(Tp x) { return x*x; } + +template static inline Tp sqr( cv::Vec x) { return x.dot(x); } + +template static inline Tp norm2(const Tp &a, const Tp &b) { return sqr(a - b); } + +template static inline +Tp norm2(const cv::Vec &a, const cv::Vec &b) { return sqr(a - b); } + -template static inline int sqr( cv::Vec x) { return x.dot(x); } -static inline int norm2(const uchar &a, const uchar &b) { return sqr(a - b); } +/******************* uchar, char, ushort, uint *********************/ + +static inline int norm2(const uchar &a, const uchar &b) { return sqr(int(a) - int(b)); } template static inline -int norm2(const cv::Vec &a, const cv::Vec &b) { return sqr(a - b); } + int norm2(const cv::Vec &a, const cv::Vec &b) +{ + return sqr( cv::Vec(a) - cv::Vec(b) ); +} +static inline int norm2(const char &a, const char &b) { return sqr(int(a) - int(b)); } +template static inline + int norm2(const cv::Vec &a, const cv::Vec &b) +{ + return sqr( cv::Vec(a) - cv::Vec(b) ); +} -template static inline Tp sqr(Tp x) { return x*x; } +static inline short norm2(const ushort &a, const ushort &b) { return sqr (short(a) - short(b)); } -template static inline Tp sqr( cv::Vec x) { return x.dot(x); } +template static inline + short norm2(const cv::Vec &a, const cv::Vec &b) +{ + return sqr( cv::Vec(a) - cv::Vec(b) ); +} -template static inline Tp norm2(const Tp &a, const Tp &b) { return sqr(a - b); } +static inline int norm2(const uint &a, const uint &b) { return sqr(int(a) - int(b)); } + +template static inline + int norm2(const cv::Vec &a, const cv::Vec &b) +{ + return sqr( cv::Vec(a) - cv::Vec(b) ); +} -template static inline -Tp norm2(const cv::Vec &a, const cv::Vec &b) { return sqr(a - b); } #endif /* __NORM2_HPP__ */ \ No newline at end of file diff --git a/modules/xphoto/src/photomontage.hpp b/modules/xphoto/src/photomontage.hpp index 9ab0fac48..efb345db3 100644 --- a/modules/xphoto/src/photomontage.hpp +++ b/modules/xphoto/src/photomontage.hpp @@ -93,7 +93,8 @@ private: protected: virtual double dist(const Tp &l1p1, const Tp &l1p2, const Tp &l2p1, const Tp &l2p2); - virtual void setWeights(GCGraph &graph, const cv::Point &pA, const cv::Point &pB, const int lA, const int lB, const int lX); + virtual void setWeights(GCGraph &graph, const cv::Point &pA, + const cv::Point &pB, const int lA, const int lB, const int lX); public: Photomontage(const std::vector &images, const std::vector &masks); diff --git a/modules/xphoto/src/whs.hpp b/modules/xphoto/src/whs.hpp index e2e29df05..d0dcb3084 100644 --- a/modules/xphoto/src/whs.hpp +++ b/modules/xphoto/src/whs.hpp @@ -51,69 +51,95 @@ static inline int hl(int x) return res; } +static inline int rp2(int x) +{ + int res = 1; + while (res < x) + res <<= 1; + return res; +} + +template +static void generate_snake(ForwardIterator snake, const int n) +{ + cv::Point previous; + if (n > 0) + { + previous = cv::Point(0, 0); + *snake = previous; + } + + for (int k = 1, num = 1; num <= n; ++k) + { + const cv::Point2i dv[] = { cv::Point2i( !(k&1), (k&1) ), + cv::Point2i( -(k&1), -!(k&1) ) }; + + *snake = previous = previous - dv[1]; + ++num; + + for (int i = 0; i < 2; ++i) + for (int j = 0; j < k && num < n; ++j) + { + *snake = previous = previous + dv[i]; + ++num; + } + } +} + static void nextProjection(std::vector &projections, const cv::Point &A, const cv::Point &B, const int psize) { int xsign = (A.x != B.x)*(hl(A.x&B.x) + (B.x > A.x))&1; int ysign = (A.y != B.y)*(hl(A.y&B.y) + (B.y > A.y))&1; - bool plusToMinusUpdate = std::max(xsign, ysign); + bool plusToMinusUpdate = xsign || ysign; - int dx = (A.x != B.x) << hl(psize - 1) - hl(A.x ^ B.x); - int dy = (A.y != B.y) << hl(psize - 1) - hl(A.y ^ B.y); + int dx = (A.x != B.x) << ( hl(psize - 1) - hl(A.x ^ B.x) ); + int dy = (A.y != B.y) << ( hl(psize - 1) - hl(A.y ^ B.y) ); - cv::Mat proj = projections[projections.size() - 1]; - cv::Mat nproj( proj.size(), proj.type(), cv::Scalar::all(0) ); + cv::Mat proj = projections[projections.size() - 1], + nproj = -proj.clone(); for (int i = dy; i < nproj.rows; ++i) { - float *vCurrent = proj.template ptr(i); - float *vxCurrent = proj.template ptr(i - dy); + float *vxNext = nproj.template ptr(i - dy); + float *vNext = nproj.template ptr(i); - float *vxNext = nproj.template ptr(i - dy); - float *vNext = nproj.template ptr(i); + float *vxCurrent = proj.template ptr(i - dy); if (plusToMinusUpdate) for (int j = dx; j < nproj.cols; ++j) - vNext[j] = -vxNext[j - dx] + vCurrent[j] - vxCurrent[j - dx]; + vNext[j] += vxCurrent[j - dx] - vxNext[j - dx]; else for (int j = dx; j < nproj.cols; ++j) - vNext[j] = +vxNext[j - dx] + vCurrent[j] + vxCurrent[j - dx]; + vNext[j] -= vxCurrent[j - dx] - vxNext[j - dx]; } projections.push_back(nproj); } -static void getWHSeries(const cv::Mat &src, cv::Mat &dst, const int nProjections, const int psize) +static void rgb2whs(const cv::Mat &src, cv::Mat &dst, const int nProjections, const int psize) { CV_Assert(nProjections <= psize*psize && src.type() == CV_32FC1); - CV_Assert( hl(psize) == 1 ); + const int npsize = rp2(psize); std::vector projections; - cv::Mat proj; - cv::boxFilter(src, proj, CV_32F, cv::Size(psize, psize), - cv::Point(-1,-1), true, cv::BORDER_REFLECT); + cv::Mat img, proj; + cv::copyMakeBorder(src, img, npsize, npsize, npsize, npsize, + cv::BORDER_CONSTANT, 0); + cv::boxFilter(img, proj, CV_32F, cv::Size(npsize, npsize), + cv::Point(-1, -1), true, cv::BORDER_REFLECT); projections.push_back(proj); - std::vector snake_idx( 1, cv::Point2i(0, 0) ); - for (int k = 1, num = 1; k < psize && num <= nProjections; ++k) - { - const cv::Point2i dv[] = { cv::Point2i( !(k&1), (k&1) ), - cv::Point2i( -(k&1), -!(k&1) ) }; - - snake_idx.push_back(snake_idx[num++ - 1] - dv[1]); - - for (int i = 0; i < k && num < nProjections; ++i) - snake_idx.push_back(snake_idx[num++ - 1] + dv[0]); - - for (int i = 0; i < k && num < nProjections; ++i) - snake_idx.push_back(snake_idx[num++ - 1] + dv[1]); - } + std::vector snake_idx; + generate_snake(std::back_inserter(snake_idx), nProjections); for (int i = 1; i < nProjections; ++i) nextProjection(projections, snake_idx[i - 1], - snake_idx[i], psize); + snake_idx[i], npsize); - cv::merge(projections, dst); + cv::merge(projections, img); + img(cv::Rect(npsize, npsize, src.cols, src.rows)).copyTo(dst); } + #endif /* __WHS_HPP__ */