Commit e92394d0 authored by Bellaktris's avatar Bellaktris

fixed bugs in WH transform, some code compression

parent 79020cf7
...@@ -62,6 +62,6 @@ createStructuredEdgeDetection ...@@ -62,6 +62,6 @@ createStructuredEdgeDetection
:param model: model file name :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, IEEE International Conference on Computer Vision (ICCV), 2013,
pp. 1841-1848. `DOI <http://dx.doi.org/10.1109/ICCV.2013.231>`_ pp. 1841-1848. `DOI <http://dx.doi.org/10.1109/ICCV.2013.231>`_
set(the_description "Addon to basic photo module") set(the_description "Addon to basic photo module")
ocv_define_module(xphoto opencv_core opencv_imgproc opencv_photo OPTIONAL opencv_photo opencv_highgui) ocv_define_module(xphoto opencv_core opencv_imgproc OPTIONAL opencv_photo opencv_highgui opencv_photo)
\ No newline at end of file \ No newline at end of file
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 <http://dx.doi.org/10.1007/978-3-642-33709-3_2>`_
\ No newline at end of file
...@@ -7,3 +7,4 @@ xphoto. Addon to basic photo modul ...@@ -7,3 +7,4 @@ xphoto. Addon to basic photo modul
Color balance <colorbalance/whitebalance> Color balance <colorbalance/whitebalance>
Denoising <denoising/denoising> Denoising <denoising/denoising>
Inpainting <inpainting/inpainting>
...@@ -46,6 +46,8 @@ int main( int argc, const char** argv ) ...@@ -46,6 +46,8 @@ int main( int argc, const char** argv )
return -1; return -1;
} }
cv::cvtColor(src, src, CV_RGB2Lab);
cv::Mat mask = cv::imread(maskFilename, 0); cv::Mat mask = cv::imread(maskFilename, 0);
if ( mask.empty() ) if ( mask.empty() )
{ {
...@@ -54,13 +56,13 @@ int main( int argc, const char** argv ) ...@@ -54,13 +56,13 @@ int main( int argc, const char** argv )
} }
cv::Mat res(src.size(), src.type()); cv::Mat res(src.size(), src.type());
cv::inpaint( src, mask, res, cv::INPAINT_SHIFTMAP ); cv::inpaint( src, mask, res, cv::INPAINT_SHIFTMAP );
cv::cvtColor(res, res, CV_Lab2RGB);
if ( outFilename == "" ) if ( outFilename == "" )
{ {
cv::namedWindow("denoising result", 1); cv::namedWindow("inpainting result", 1);
cv::imshow("denoising result", res); cv::imshow("inpainting result", res);
cv::waitKey(0); cv::waitKey(0);
} }
......
...@@ -76,15 +76,18 @@ private: ...@@ -76,15 +76,18 @@ private:
: main(_main), dimIdx(_dimIdx) {} : main(_main), dimIdx(_dimIdx) {}
}; };
const int height, width;
const int leafNumber; const int leafNumber;
std::vector <cv::Vec <Tp, cn> > data;
std::vector <int> idx;
std::vector <cv::Point2i> nodes;
int getMaxSpreadN(const int left, const int right) const; int getMaxSpreadN(const int left, const int right) const;
void operator =(const KDTree <Tp, cn> &) const {}; void operator =(const KDTree <Tp, cn> &) const {};
public: public:
std::vector <cv::Vec <Tp, cn> > data; void updateDist(const int leaf, const int &idx0, int &bestIdx, double &dist);
std::vector <int> idx;
std::vector <cv::Point2i> nodes;
KDTree(const cv::Mat &data, const int leafNumber = 8); KDTree(const cv::Mat &data, const int leafNumber = 8);
~KDTree(){}; ~KDTree(){};
...@@ -109,15 +112,16 @@ getMaxSpreadN(const int _left, const int _right) const ...@@ -109,15 +112,16 @@ getMaxSpreadN(const int _left, const int _right) const
template <typename Tp, int cn> KDTree <Tp, cn>:: template <typename Tp, int cn> KDTree <Tp, cn>::
KDTree(const cv::Mat &img, const int _leafNumber) 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) std::copy( (cv::Vec <Tp, cn> *) img.data,
for (int j = 0; j < img.cols; ++j) (cv::Vec <Tp, cn> *) img.data + img.total(),
data.push_back(img.template at<cv::Vec <Tp, cn> >(i, j)); std::back_inserter(data) );
generate_seq( std::back_inserter(idx), 0, int(data.size()) ); 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 <int> left, right; std::stack <int> left, right;
left.push( 0 ); left.push( 0 );
...@@ -125,75 +129,72 @@ KDTree(const cv::Mat &img, const int _leafNumber) ...@@ -125,75 +129,72 @@ KDTree(const cv::Mat &img, const int _leafNumber)
while ( !left.empty() ) while ( !left.empty() )
{ {
int _left = left.top(); left.pop(); int _left = left.top(); left.pop();
int _right = right.top(); right.pop(); int _right = right.top(); right.pop();
if ( _right - _left <= leafNumber) if ( _right - _left <= leafNumber)
{ {
for (int i = _left; i < _right; ++i) for (int i = _left; i < _right; ++i)
{ nodes[idx[i]] = cv::Point2i(_left, _right);
nodes[idx[i]].x = _left;
nodes[idx[i]].y = _right;
}
continue; continue;
} }
std::vector <int>::iterator begIt = idx.begin();
int nth = _left + (_right - _left)/2; int nth = _left + (_right - _left)/2;
std::nth_element(/**/ begIt + _left, int dimIdx = getMaxSpreadN(_left, _right);
begIt + nth, begIt + _right, KDTreeComparator comp( this, dimIdx );
KDTreeComparator( this,
getMaxSpreadN(_left, _right) ) /**/); std::nth_element(/**/
idx.begin() + _left,
idx.begin() + nth,
idx.begin() + _right, comp
/**/);
left.push(_left); right.push(nth + 1); left.push(_left); right.push(nth + 1);
left.push(nth + 1); right.push(_right); left.push(nth + 1); right.push(_right);
} }
} }
/************************** ANNF search **************************/ template <typename Tp, int cn> void KDTree <Tp, cn>::
updateDist(const int leaf, const int &idx0, int &bestIdx, double &dist)
template <typename Tp, int cn>
static void updateDist(const KDTree <Tp, cn> &kdTree, const cv::Point2i &I, const int height,
const int width, const int &currentIdx, 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; continue;
if (nx == width - 1 || ny == height - 1)
if (newIdx/width == height - 1)
continue; continue;
int dx = currentIdx%width - newIdx%width; double ndist = norm2(data[idx0], data[idx[k]]);
int dy = currentIdx/width - newIdx/width;
if (abs(dx) + abs(dy) < 32)
continue;
double ndist = norm2(kdTree.data[newIdx],
kdTree.data[currentIdx]);
if (ndist < dist) if (ndist < dist)
{ {
dist = ndist; dist = ndist;
bestIdx = newIdx; bestIdx = idx[k];
} }
} }
} }
static void getANNF(const cv::Mat &img, std::vector <cv::Matx33f> &transforms, /************************** ANNF search **************************/
const int nTransform, const int psize)
static void dominantTransforms(const cv::Mat &img, std::vector <cv::Matx33f> &transforms,
const int nTransform, const int psize)
{ {
/** Walsh-Hadamard Transformation **/ /** Walsh-Hadamard Transformation **/
std::vector <cv::Mat> channels; std::vector <cv::Mat> channels;
cv::split(img, 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) 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::Mat whs; // Walsh-Hadamard series
cv::merge(channels, whs); cv::merge(channels, whs);
...@@ -209,22 +210,16 @@ static void getANNF(const cv::Mat &img, std::vector <cv::Matx33f> &transforms, ...@@ -209,22 +210,16 @@ static void getANNF(const cv::Mat &img, std::vector <cv::Matx33f> &transforms,
double dist = std::numeric_limits <double>::max(); double dist = std::numeric_limits <double>::max();
int current = i*whs.cols + j; int current = i*whs.cols + j;
cv::Point2i I = kdTree.nodes[i*whs.cols + j]; int dy[] = {0, 1, 0}, dx[] = {0, 0, 1};
updateDist(kdTree, I, whs.rows, whs.cols, current, annf[i*whs.cols + j], dist); for (int k = 0; k < sizeof(dy)/sizeof(int); ++k)
if (i - dy[k] >= 0 && j - dx[k] >= 0)
if (i != 0) {
{ int neighbor = (i - dy[k])*whs.cols + (j - dx[k]);
int idx = annf[(i - 1)*whs.cols + j] + whs.cols; int leafIdx = k == 0 ? neighbor :
cv::Point2i I = kdTree.nodes[idx]; annf[neighbor] + dy[k]*whs.cols + dx[k];
updateDist(kdTree, I, whs.rows, whs.cols, current, annf[i*whs.cols + j], dist); kdTree.updateDist(leafIdx, 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);
}
} }
/** Local maxima extraction **/ /** Local maxima extraction **/
...@@ -233,12 +228,12 @@ static void getANNF(const cv::Mat &img, std::vector <cv::Matx33f> &transforms, ...@@ -233,12 +228,12 @@ static void getANNF(const cv::Mat &img, std::vector <cv::Matx33f> &transforms,
_annfHist(2*whs.rows, 2*whs.cols, 0.0); _annfHist(2*whs.rows, 2*whs.cols, 0.0);
for (size_t i = 0; i < annf.size(); ++i) for (size_t i = 0; i < annf.size(); ++i)
++annfHist( (annf[i] - int(i))/whs.cols + whs.rows, ++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::GaussianBlur( annfHist, annfHist,
cv::Size(9, 9), 1.41, 0.0, cv::BORDER_CONSTANT); cv::Size(9, 9), 1.41, 0.0, cv::BORDER_CONSTANT);
cv::dilate(annfHist, _annfHist, cv::dilate( annfHist, _annfHist,
cv::Matx<uchar, 9, 9>::ones()); cv::Matx<uchar, 9, 9>::ones() );
std::vector < std::pair<double, int> > amount; std::vector < std::pair<double, int> > amount;
std::vector <cv::Point2i> shiftM; std::vector <cv::Point2i> shiftM;
......
...@@ -57,6 +57,9 @@ ...@@ -57,6 +57,9 @@
#include "opencv2/core/types.hpp" #include "opencv2/core/types.hpp"
#include "opencv2/core/types_c.h" #include "opencv2/core/types_c.h"
#include "opencv2/highgui.hpp"
namespace xphotoInternal namespace xphotoInternal
{ {
# include "photomontage.hpp" # include "photomontage.hpp"
...@@ -66,22 +69,18 @@ namespace xphotoInternal ...@@ -66,22 +69,18 @@ namespace xphotoInternal
namespace cv namespace cv
{ {
template <typename Tp, unsigned int cn> template <typename Tp, unsigned int cn>
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 /** Preparing input **/
const int psize = 8; // single ANNF patch size
cv::Mat img; cv::Mat img;
src.convertTo( img, CV_32F );
cvtColor(src, img, CV_RGB2Lab);
img.setTo(0, 255 - mask); img.setTo(0, 255 - mask);
img.convertTo( img, CV_32F );
/** ANNF computation **/ /** ANNF computation **/
std::vector <Matx33f> transforms( nTransform ); std::vector <Matx33f> transforms( nTransform );
xphotoInternal::getANNF(img, transforms, xphotoInternal::dominantTransforms(img,
nTransform, psize); transforms, nTransform, psize);
/** Warping **/ /** Warping **/
std::vector <Mat> images( nTransform + 1 ); // source image transformed with transforms std::vector <Mat> images( nTransform + 1 ); // source image transformed with transforms
...@@ -109,8 +108,7 @@ namespace cv ...@@ -109,8 +108,7 @@ namespace cv
.assignResImage(photomontageResult); .assignResImage(photomontageResult);
/** Writing result **/ /** Writing result **/
photomontageResult.convertTo( photomontageResult, dst.type() ); photomontageResult.convertTo( dst, dst.type() );
cvtColor(photomontageResult, dst, CV_Lab2RGB);
} }
template <typename Tp, unsigned int cn> template <typename Tp, unsigned int cn>
...@@ -143,66 +141,90 @@ namespace cv ...@@ -143,66 +141,90 @@ namespace cv
switch ( src.type() ) switch ( src.type() )
{ {
//case CV_8UC1: case CV_8SC1:
// inpaint <uchar, 1>( src, mask, dst, algorithmType ); inpaint <char, 1>( src, mask, dst, algorithmType );
// break; break;
//case CV_8UC2: case CV_8SC2:
// inpaint <uchar, 2>( src, mask, dst, algorithmType ); inpaint <char, 2>( src, mask, dst, algorithmType );
// break; break;
case CV_8SC3:
inpaint <char, 3>( src, mask, dst, algorithmType );
break;
case CV_8SC4:
inpaint <char, 4>( src, mask, dst, algorithmType );
break;
case CV_8UC1:
inpaint <uchar, 1>( src, mask, dst, algorithmType );
break;
case CV_8UC2:
inpaint <uchar, 2>( src, mask, dst, algorithmType );
break;
case CV_8UC3: case CV_8UC3:
inpaint <uchar, 3>( src, mask, dst, algorithmType ); inpaint <uchar, 3>( src, mask, dst, algorithmType );
break; break;
//case CV_8UC4: case CV_8UC4:
// inpaint <uchar, 4>( src, mask, dst, algorithmType ); inpaint <uchar, 4>( src, mask, dst, algorithmType );
// break; break;
//case CV_16SC1: case CV_16SC1:
// inpaint <short, 1>( src, mask, dst, algorithmType ); inpaint <short, 1>( src, mask, dst, algorithmType );
// break; break;
//case CV_16SC2: case CV_16SC2:
// inpaint <short, 2>( src, mask, dst, algorithmType ); inpaint <short, 2>( src, mask, dst, algorithmType );
// break; break;
case CV_16SC3: case CV_16SC3:
inpaint <short, 3>( src, mask, dst, algorithmType ); inpaint <short, 3>( src, mask, dst, algorithmType );
break; break;
//case CV_16SC4: case CV_16SC4:
// inpaint <short, 4>( src, mask, dst, algorithmType ); inpaint <short, 4>( src, mask, dst, algorithmType );
// break; break;
//case CV_32SC1: case CV_16UC1:
// inpaint <int, 1>( src, mask, dst, algorithmType ); inpaint <ushort, 1>( src, mask, dst, algorithmType );
// break; break;
//case CV_32SC2: case CV_16UC2:
// inpaint <int, 2>( src, mask, dst, algorithmType ); inpaint <ushort, 2>( src, mask, dst, algorithmType );
// break; break;
case CV_16UC3:
inpaint <ushort, 3>( src, mask, dst, algorithmType );
break;
case CV_16UC4:
inpaint <ushort, 4>( src, mask, dst, algorithmType );
break;
case CV_32SC1:
inpaint <int, 1>( src, mask, dst, algorithmType );
break;
case CV_32SC2:
inpaint <int, 2>( src, mask, dst, algorithmType );
break;
case CV_32SC3: case CV_32SC3:
inpaint <int, 3>( src, mask, dst, algorithmType ); inpaint <int, 3>( src, mask, dst, algorithmType );
break; break;
//case CV_32SC4: case CV_32SC4:
// inpaint <int, 4>( src, mask, dst, algorithmType ); inpaint <int, 4>( src, mask, dst, algorithmType );
// break; break;
//case CV_32FC1: case CV_32FC1:
// inpaint <float, 1>( src, mask, dst, algorithmType ); inpaint <float, 1>( src, mask, dst, algorithmType );
// break; break;
//case CV_32FC2: case CV_32FC2:
// inpaint <float, 2>( src, mask, dst, algorithmType ); inpaint <float, 2>( src, mask, dst, algorithmType );
// break; break;
case CV_32FC3: case CV_32FC3:
inpaint <float, 3>( src, mask, dst, algorithmType ); inpaint <float, 3>( src, mask, dst, algorithmType );
break; break;
//case CV_32FC4: case CV_32FC4:
// inpaint <float, 4>( src, mask, dst, algorithmType ); inpaint <float, 4>( src, mask, dst, algorithmType );
// break; break;
//case CV_64FC1: case CV_64FC1:
// inpaint <double, 1>( src, mask, dst, algorithmType ); inpaint <double, 1>( src, mask, dst, algorithmType );
// break; break;
//case CV_64FC2: case CV_64FC2:
// inpaint <double, 2>( src, mask, dst, algorithmType ); inpaint <double, 2>( src, mask, dst, algorithmType );
// break; break;
case CV_64FC3: case CV_64FC3:
inpaint <double, 3>( src, mask, dst, algorithmType ); inpaint <double, 3>( src, mask, dst, algorithmType );
break; break;
//case CV_64FC4: case CV_64FC4:
// inpaint <double, 4>( src, mask, dst, algorithmType ); inpaint <double, 4>( src, mask, dst, algorithmType );
// break; break;
default: default:
CV_Error_( CV_StsNotImplemented, CV_Error_( CV_StsNotImplemented,
("Unsupported source image format (=%d)", ("Unsupported source image format (=%d)",
......
...@@ -40,24 +40,52 @@ ...@@ -40,24 +40,52 @@
#ifndef __NORM2_HPP__ #ifndef __NORM2_HPP__
#define __NORM2_HPP__ #define __NORM2_HPP__
static inline int sqr(uchar x) { return x*x; } /************************ General template *************************/
template <typename Tp> static inline Tp sqr(Tp x) { return x*x; }
template <typename Tp, int cn> static inline Tp sqr( cv::Vec<Tp, cn> x) { return x.dot(x); }
template <typename Tp> static inline Tp norm2(const Tp &a, const Tp &b) { return sqr(a - b); }
template <typename Tp, int cn> static inline
Tp norm2(const cv::Vec <Tp, cn> &a, const cv::Vec<Tp, cn> &b) { return sqr(a - b); }
template <int cn> static inline int sqr( cv::Vec<uchar, cn> 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 <int cn> static inline template <int cn> static inline
int norm2(const cv::Vec <uchar, cn> &a, const cv::Vec<uchar, cn> &b) { return sqr(a - b); } int norm2(const cv::Vec <uchar, cn> &a, const cv::Vec<uchar, cn> &b)
{
return sqr( cv::Vec<int, cn>(a) - cv::Vec<int, cn>(b) );
}
static inline int norm2(const char &a, const char &b) { return sqr(int(a) - int(b)); }
template <int cn> static inline
int norm2(const cv::Vec <char, cn> &a, const cv::Vec<char, cn> &b)
{
return sqr( cv::Vec<int, cn>(a) - cv::Vec<int, cn>(b) );
}
template <typename Tp> static inline Tp sqr(Tp x) { return x*x; } static inline short norm2(const ushort &a, const ushort &b) { return sqr <short>(short(a) - short(b)); }
template <typename Tp, int cn> static inline Tp sqr( cv::Vec<Tp, cn> x) { return x.dot(x); } template <int cn> static inline
short norm2(const cv::Vec <ushort, cn> &a, const cv::Vec<ushort, cn> &b)
{
return sqr( cv::Vec<short, cn>(a) - cv::Vec<short, cn>(b) );
}
template <typename Tp> 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 <int cn> static inline
int norm2(const cv::Vec <uint, cn> &a, const cv::Vec<uint, cn> &b)
{
return sqr( cv::Vec<int, cn>(a) - cv::Vec<int, cn>(b) );
}
template <typename Tp, int cn> static inline
Tp norm2(const cv::Vec <Tp, cn> &a, const cv::Vec<Tp, cn> &b) { return sqr(a - b); }
#endif /* __NORM2_HPP__ */ #endif /* __NORM2_HPP__ */
\ No newline at end of file
...@@ -93,7 +93,8 @@ private: ...@@ -93,7 +93,8 @@ private:
protected: protected:
virtual double dist(const Tp &l1p1, const Tp &l1p2, const Tp &l2p1, const Tp &l2p2); virtual double dist(const Tp &l1p1, const Tp &l1p2, const Tp &l2p1, const Tp &l2p2);
virtual void setWeights(GCGraph <double> &graph, const cv::Point &pA, const cv::Point &pB, const int lA, const int lB, const int lX); virtual void setWeights(GCGraph <double> &graph, const cv::Point &pA,
const cv::Point &pB, const int lA, const int lB, const int lX);
public: public:
Photomontage(const std::vector <cv::Mat> &images, const std::vector <cv::Mat> &masks); Photomontage(const std::vector <cv::Mat> &images, const std::vector <cv::Mat> &masks);
......
...@@ -51,69 +51,95 @@ static inline int hl(int x) ...@@ -51,69 +51,95 @@ static inline int hl(int x)
return res; return res;
} }
static inline int rp2(int x)
{
int res = 1;
while (res < x)
res <<= 1;
return res;
}
template <typename ForwardIterator>
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 <cv::Mat> &projections, const cv::Point &A, static void nextProjection(std::vector <cv::Mat> &projections, const cv::Point &A,
const cv::Point &B, const int psize) const cv::Point &B, const int psize)
{ {
int xsign = (A.x != B.x)*(hl(A.x&B.x) + (B.x > A.x))&1; 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; 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 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 dy = (A.y != B.y) << ( hl(psize - 1) - hl(A.y ^ B.y) );
cv::Mat proj = projections[projections.size() - 1]; cv::Mat proj = projections[projections.size() - 1],
cv::Mat nproj( proj.size(), proj.type(), cv::Scalar::all(0) ); nproj = -proj.clone();
for (int i = dy; i < nproj.rows; ++i) for (int i = dy; i < nproj.rows; ++i)
{ {
float *vCurrent = proj.template ptr<float>(i); float *vxNext = nproj.template ptr<float>(i - dy);
float *vxCurrent = proj.template ptr<float>(i - dy); float *vNext = nproj.template ptr<float>(i);
float *vxNext = nproj.template ptr<float>(i - dy); float *vxCurrent = proj.template ptr<float>(i - dy);
float *vNext = nproj.template ptr<float>(i);
if (plusToMinusUpdate) if (plusToMinusUpdate)
for (int j = dx; j < nproj.cols; ++j) 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 else
for (int j = dx; j < nproj.cols; ++j) 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); 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(nProjections <= psize*psize && src.type() == CV_32FC1);
CV_Assert( hl(psize) == 1 );
const int npsize = rp2(psize);
std::vector <cv::Mat> projections; std::vector <cv::Mat> projections;
cv::Mat proj; cv::Mat img, proj;
cv::boxFilter(src, proj, CV_32F, cv::Size(psize, psize), cv::copyMakeBorder(src, img, npsize, npsize, npsize, npsize,
cv::Point(-1,-1), true, cv::BORDER_REFLECT); 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); projections.push_back(proj);
std::vector <cv::Point2i> snake_idx( 1, cv::Point2i(0, 0) ); std::vector <cv::Point2i> snake_idx;
for (int k = 1, num = 1; k < psize && num <= nProjections; ++k) generate_snake(std::back_inserter(snake_idx), nProjections);
{
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]);
}
for (int i = 1; i < nProjections; ++i) for (int i = 1; i < nProjections; ++i)
nextProjection(projections, snake_idx[i - 1], 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__ */ #endif /* __WHS_HPP__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment