removed optim module; moved its functionality to core and photo modules; moved drawing functions from core to imgproc. Removed FilterEngine etc. from public API

pull/3103/head
Vadim Pisarevsky 10 years ago
parent a602185fb6
commit 257463719b
  1. 1
      modules/core/doc/core.rst
  2. 341
      modules/core/doc/optim.rst
  3. 92
      modules/core/include/opencv2/core.hpp
  4. 186
      modules/core/include/opencv2/core/core_c.h
  5. 31
      modules/core/include/opencv2/core/optim.hpp
  6. 24
      modules/core/src/conjugate_gradient.cpp
  7. 483
      modules/core/src/datastructs.cpp
  8. 50
      modules/core/src/downhill_simplex.cpp
  9. 531
      modules/core/src/kdtree.cpp
  10. 457
      modules/core/src/kmeans.cpp
  11. 9
      modules/core/src/lpsolver.cpp
  12. 335
      modules/core/src/matmul.cpp
  13. 414
      modules/core/src/matrix.cpp
  14. 384
      modules/core/src/pca.cpp
  15. 12
      modules/core/test/test_conjugate_gradient.cpp
  16. 12
      modules/core/test/test_downhill_simplex.cpp
  17. 14
      modules/core/test/test_lpsolver.cpp
  18. 22
      modules/core/test/test_misc.cpp
  19. 0
      modules/imgproc/doc/drawing_functions.rst
  20. 666
      modules/imgproc/doc/filtering.rst
  21. 1
      modules/imgproc/doc/imgproc.rst
  22. 0
      modules/imgproc/doc/pics/ellipse.png
  23. 401
      modules/imgproc/include/opencv2/imgproc.hpp
  24. 185
      modules/imgproc/include/opencv2/imgproc/imgproc_c.h
  25. 1
      modules/imgproc/src/drawing.cpp
  26. 375
      modules/imgproc/src/filterengine.hpp
  27. 3359
      modules/imgproc/src/hershey_fonts.cpp
  28. 33
      modules/imgproc/src/precomp.hpp
  29. 2
      modules/optim/CMakeLists.txt
  30. 162
      modules/optim/doc/downhill_simplex_method.rst
  31. 48
      modules/optim/doc/linear_programming.rst
  32. 136
      modules/optim/doc/nonlinear_conjugate_gradient.rst
  33. 13
      modules/optim/doc/optim.rst
  34. 48
      modules/optim/doc/primal_dual_algorithm.rst
  35. 46
      modules/optim/include/opencv2/optim/optim.hpp
  36. 58
      modules/optim/src/debug.hpp
  37. 47
      modules/optim/src/precomp.hpp
  38. 3
      modules/optim/test/test_main.cpp
  39. 16
      modules/optim/test/test_precomp.hpp
  40. 46
      modules/photo/doc/denoising.rst
  41. 2
      modules/photo/include/opencv2/photo.hpp
  42. 6
      modules/photo/src/denoise_tvl1.cpp
  43. 4
      modules/photo/test/test_denoise_tvl1.cpp
  44. 16
      modules/superres/src/btv_l1.cpp

@ -17,3 +17,4 @@ core. The Core Functionality
utility_and_system_functions_and_macros utility_and_system_functions_and_macros
opengl_interop opengl_interop
ipp_async_converters ipp_async_converters
optim

@ -0,0 +1,341 @@
Optimization Algorithms
=======================
.. highlight:: cpp
The algorithms in this section minimize or maximize function value within specified constraints or without any constraints.
solveLP
--------------------
Solve given (non-integer) linear programming problem using the Simplex Algorithm (Simplex Method).
What we mean here by "linear programming problem" (or LP problem, for short) can be
formulated as:
.. math::
\mbox{Maximize } c\cdot x\\
\mbox{Subject to:}\\
Ax\leq b\\
x\geq 0
Where :math:`c` is fixed *1*-by-*n* row-vector, :math:`A` is fixed *m*-by-*n* matrix, :math:`b` is fixed *m*-by-*1* column vector and
:math:`x` is an arbitrary *n*-by-*1* column vector, which satisfies the constraints.
Simplex algorithm is one of many algorithms that are designed to handle this sort of problems efficiently. Although it is not optimal in theoretical
sense (there exist algorithms that can solve any problem written as above in polynomial type, while simplex method degenerates to exponential time
for some special cases), it is well-studied, easy to implement and is shown to work well for real-life purposes.
The particular implementation is taken almost verbatim from **Introduction to Algorithms, third edition**
by T. H. Cormen, C. E. Leiserson, R. L. Rivest and Clifford Stein. In particular, the Bland's rule
(`http://en.wikipedia.org/wiki/Bland%27s\_rule <http://en.wikipedia.org/wiki/Bland%27s_rule>`_) is used to prevent cycling.
.. ocv:function:: int solveLP(const Mat& Func, const Mat& Constr, Mat& z)
:param Func: This row-vector corresponds to :math:`c` in the LP problem formulation (see above). It should contain 32- or 64-bit floating point numbers. As a convenience, column-vector may be also submitted, in the latter case it is understood to correspond to :math:`c^T`.
:param Constr: *m*-by-*n\+1* matrix, whose rightmost column corresponds to :math:`b` in formulation above and the remaining to :math:`A`. It should containt 32- or 64-bit floating point numbers.
:param z: The solution will be returned here as a column-vector - it corresponds to :math:`c` in the formulation above. It will contain 64-bit floating point numbers.
:return: One of the return codes:
::
//!the return codes for solveLP() function
enum
{
SOLVELP_UNBOUNDED = -2, //problem is unbounded (target function can achieve arbitrary high values)
SOLVELP_UNFEASIBLE = -1, //problem is unfeasible (there are no points that satisfy all the constraints imposed)
SOLVELP_SINGLE = 0, //there is only one maximum for target function
SOLVELP_MULTI = 1 //there are multiple maxima for target function - the arbitrary one is returned
};
DownhillSolver
---------------------------------
.. ocv:class:: DownhillSolver
This class is used to perform the non-linear non-constrained *minimization* of a function, defined on an *n*-dimensional Euclidean space,
using the **Nelder-Mead method**, also known as **downhill simplex method**. The basic idea about the method can be obtained from
(`http://en.wikipedia.org/wiki/Nelder-Mead\_method <http://en.wikipedia.org/wiki/Nelder-Mead_method>`_). It should be noted, that
this method, although deterministic, is rather a heuristic and therefore may converge to a local minima, not necessary a global one.
It is iterative optimization technique, which at each step uses an information about the values of a function evaluated only at
*n+1* points, arranged as a *simplex* in *n*-dimensional space (hence the second name of the method). At each step new point is
chosen to evaluate function at, obtained value is compared with previous ones and based on this information simplex changes it's shape
, slowly moving to the local minimum. Thus this method is using *only* function values to make decision, on contrary to, say, Nonlinear
Conjugate Gradient method (which is also implemented in ``optim``).
Algorithm stops when the number of function evaluations done exceeds ``termcrit.maxCount``, when the function values at the
vertices of simplex are within ``termcrit.epsilon`` range or simplex becomes so small that it
can enclosed in a box with ``termcrit.epsilon`` sides, whatever comes first, for some defined by user
positive integer ``termcrit.maxCount`` and positive non-integer ``termcrit.epsilon``.
::
class CV_EXPORTS Solver : public Algorithm
{
public:
class CV_EXPORTS Function
{
public:
virtual ~Function() {}
virtual double calc(const double* x) const = 0;
virtual void getGradient(const double* /*x*/,double* /*grad*/) {}
};
virtual Ptr<Function> getFunction() const = 0;
virtual void setFunction(const Ptr<Function>& f) = 0;
virtual TermCriteria getTermCriteria() const = 0;
virtual void setTermCriteria(const TermCriteria& termcrit) = 0;
// x contain the initial point before the call and the minima position (if algorithm converged) after. x is assumed to be (something that
// after getMat() will return) row-vector or column-vector. *It's size and should
// be consisted with previous dimensionality data given, if any (otherwise, it determines dimensionality)*
virtual double minimize(InputOutputArray x) = 0;
};
class CV_EXPORTS DownhillSolver : public Solver
{
public:
//! returns row-vector, even if the column-vector was given
virtual void getInitStep(OutputArray step) const=0;
//!This should be called at least once before the first call to minimize() and step is assumed to be (something that
//! after getMat() will return) row-vector or column-vector. *It's dimensionality determines the dimensionality of a problem.*
virtual void setInitStep(InputArray step)=0;
};
It should be noted, that ``DownhillSolver`` is a derivative of the abstract interface ``Solver``, which in
turn is derived from the ``Algorithm`` interface and is used to encapsulate the functionality, common to all non-linear optimization
algorithms in the ``optim`` module.
DownhillSolver::getFunction
--------------------------------------------
Getter for the optimized function. The optimized function is represented by ``Solver::Function`` interface, which requires
derivatives to implement the sole method ``calc(double*)`` to evaluate the function.
.. ocv:function:: Ptr<Solver::Function> DownhillSolver::getFunction()
:return: Smart-pointer to an object that implements ``Solver::Function`` interface - it represents the function that is being optimized. It can be empty, if no function was given so far.
DownhillSolver::setFunction
-----------------------------------------------
Setter for the optimized function. *It should be called at least once before the call to* ``DownhillSolver::minimize()``, as
default value is not usable.
.. ocv:function:: void DownhillSolver::setFunction(const Ptr<Solver::Function>& f)
:param f: The new function to optimize.
DownhillSolver::getTermCriteria
----------------------------------------------------
Getter for the previously set terminal criteria for this algorithm.
.. ocv:function:: TermCriteria DownhillSolver::getTermCriteria()
:return: Deep copy of the terminal criteria used at the moment.
DownhillSolver::setTermCriteria
------------------------------------------
Set terminal criteria for downhill simplex method. Two things should be noted. First, this method *is not necessary* to be called
before the first call to ``DownhillSolver::minimize()``, as the default value is sensible. Second, the method will raise an error
if ``termcrit.type!=(TermCriteria::MAX_ITER+TermCriteria::EPS)``, ``termcrit.epsilon<=0`` or ``termcrit.maxCount<=0``. That is,
both ``epsilon`` and ``maxCount`` should be set to positive values (non-integer and integer respectively) and they represent
tolerance and maximal number of function evaluations that is allowed.
Algorithm stops when the number of function evaluations done exceeds ``termcrit.maxCount``, when the function values at the
vertices of simplex are within ``termcrit.epsilon`` range or simplex becomes so small that it
can enclosed in a box with ``termcrit.epsilon`` sides, whatever comes first.
.. ocv:function:: void DownhillSolver::setTermCriteria(const TermCriteria& termcrit)
:param termcrit: Terminal criteria to be used, represented as ``TermCriteria`` structure (defined elsewhere in openCV). Mind you, that it should meet ``(termcrit.type==(TermCriteria::MAX_ITER+TermCriteria::EPS) && termcrit.epsilon>0 && termcrit.maxCount>0)``, otherwise the error will be raised.
DownhillSolver::getInitStep
-----------------------------------
Returns the initial step that will be used in downhill simplex algorithm. See the description
of corresponding setter (follows next) for the meaning of this parameter.
.. ocv:function:: void getInitStep(OutputArray step)
:param step: Initial step that will be used in algorithm. Note, that although corresponding setter accepts column-vectors as well as row-vectors, this method will return a row-vector.
DownhillSolver::setInitStep
----------------------------------
Sets the initial step that will be used in downhill simplex algorithm. Step, together with initial point (givin in ``DownhillSolver::minimize``)
are two *n*-dimensional vectors that are used to determine the shape of initial simplex. Roughly said, initial point determines the position
of a simplex (it will become simplex's centroid), while step determines the spread (size in each dimension) of a simplex. To be more precise,
if :math:`s,x_0\in\mathbb{R}^n` are the initial step and initial point respectively, the vertices of a simplex will be: :math:`v_0:=x_0-\frac{1}{2}
s` and :math:`v_i:=x_0+s_i` for :math:`i=1,2,\dots,n` where :math:`s_i` denotes projections of the initial step of *n*-th coordinate (the result
of projection is treated to be vector given by :math:`s_i:=e_i\cdot\left<e_i\cdot s\right>`, where :math:`e_i` form canonical basis)
.. ocv:function:: void setInitStep(InputArray step)
:param step: Initial step that will be used in algorithm. Roughly said, it determines the spread (size in each dimension) of an initial simplex.
DownhillSolver::minimize
-----------------------------------
The main method of the ``DownhillSolver``. It actually runs the algorithm and performs the minimization. The sole input parameter determines the
centroid of the starting simplex (roughly, it tells where to start), all the others (terminal criteria, initial step, function to be minimized)
are supposed to be set via the setters before the call to this method or the default values (not always sensible) will be used.
.. ocv:function:: double DownhillSolver::minimize(InputOutputArray x)
:param x: The initial point, that will become a centroid of an initial simplex. After the algorithm will terminate, it will be setted to the point where the algorithm stops, the point of possible minimum.
:return: The value of a function at the point found.
createDownhillSolver
------------------------------------
This function returns the reference to the ready-to-use ``DownhillSolver`` object. All the parameters are optional, so this procedure can be called
even without parameters at all. In this case, the default values will be used. As default value for terminal criteria are the only sensible ones,
``DownhillSolver::setFunction()`` and ``DownhillSolver::setInitStep()`` should be called upon the obtained object, if the respective parameters
were not given to ``createDownhillSolver()``. Otherwise, the two ways (give parameters to ``createDownhillSolver()`` or miss them out and call the
``DownhillSolver::setFunction()`` and ``DownhillSolver::setInitStep()``) are absolutely equivalent (and will drop the same errors in the same way,
should invalid input be detected).
.. ocv:function:: Ptr<DownhillSolver> createDownhillSolver(const Ptr<Solver::Function>& f,InputArray initStep, TermCriteria termcrit)
:param f: Pointer to the function that will be minimized, similarly to the one you submit via ``DownhillSolver::setFunction``.
:param step: Initial step, that will be used to construct the initial simplex, similarly to the one you submit via ``DownhillSolver::setInitStep``.
:param termcrit: Terminal criteria to the algorithm, similarly to the one you submit via ``DownhillSolver::setTermCriteria``.
ConjGradSolver
---------------------------------
.. ocv:class:: ConjGradSolver
This class is used to perform the non-linear non-constrained *minimization* of a function with *known gradient*
, defined on an *n*-dimensional Euclidean space,
using the **Nonlinear Conjugate Gradient method**. The implementation was done based on the beautifully clear explanatory article `An Introduction to the Conjugate Gradient Method Without the Agonizing Pain <http://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf>`_
by Jonathan Richard Shewchuk. The method can be seen as an adaptation of a standard Conjugate Gradient method (see, for example
`http://en.wikipedia.org/wiki/Conjugate_gradient_method <http://en.wikipedia.org/wiki/Conjugate_gradient_method>`_) for numerically solving the
systems of linear equations.
It should be noted, that
this method, although deterministic, is rather a heuristic method and therefore may converge to a local minima, not necessary a global one. What
is even more disastrous, most of its behaviour is ruled by gradient, therefore it essentially cannot distinguish between local minima and maxima.
Therefore, if it starts sufficiently near to the local maximum, it may converge to it. Another obvious restriction is that it should be possible
to compute the gradient of a function at any point, thus it is preferable to have analytic expression for gradient and computational burden
should be born by the user.
The latter responsibility is accompilished via the ``getGradient(const double* x,double* grad)`` method of a
``Solver::Function`` interface (which represents function that is being optimized). This method takes point a point in *n*-dimensional space
(first argument represents the array of coordinates of that point) and comput its gradient (it should be stored in the second argument as an array).
::
class CV_EXPORTS Solver : public Algorithm
{
public:
class CV_EXPORTS Function
{
public:
virtual ~Function() {}
virtual double calc(const double* x) const = 0;
virtual void getGradient(const double* /*x*/,double* /*grad*/) {}
};
virtual Ptr<Function> getFunction() const = 0;
virtual void setFunction(const Ptr<Function>& f) = 0;
virtual TermCriteria getTermCriteria() const = 0;
virtual void setTermCriteria(const TermCriteria& termcrit) = 0;
// x contain the initial point before the call and the minima position (if algorithm converged) after. x is assumed to be (something that
// after getMat() will return) row-vector or column-vector. *It's size and should
// be consisted with previous dimensionality data given, if any (otherwise, it determines dimensionality)*
virtual double minimize(InputOutputArray x) = 0;
};
class CV_EXPORTS ConjGradSolver : public Solver{
};
Note, that class ``ConjGradSolver`` thus does not add any new methods to the basic ``Solver`` interface.
ConjGradSolver::getFunction
--------------------------------------------
Getter for the optimized function. The optimized function is represented by ``Solver::Function`` interface, which requires
derivatives to implement the method ``calc(double*)`` to evaluate the function. It should be emphasized once more, that since Nonlinear
Conjugate Gradient method requires gradient to be computable in addition to the function values,
``getGradient(const double* x,double* grad)`` method of a ``Solver::Function`` interface should be also implemented meaningfully.
.. ocv:function:: Ptr<Solver::Function> ConjGradSolver::getFunction()
:return: Smart-pointer to an object that implements ``Solver::Function`` interface - it represents the function that is being optimized. It can be empty, if no function was given so far.
ConjGradSolver::setFunction
-----------------------------------------------
Setter for the optimized function. *It should be called at least once before the call to* ``ConjGradSolver::minimize()``, as
default value is not usable.
.. ocv:function:: void ConjGradSolver::setFunction(const Ptr<Solver::Function>& f)
:param f: The new function to optimize.
ConjGradSolver::getTermCriteria
----------------------------------------------------
Getter for the previously set terminal criteria for this algorithm.
.. ocv:function:: TermCriteria ConjGradSolver::getTermCriteria()
:return: Deep copy of the terminal criteria used at the moment.
ConjGradSolver::setTermCriteria
------------------------------------------
Set terminal criteria for downhill simplex method. Two things should be noted. First, this method *is not necessary* to be called
before the first call to ``ConjGradSolver::minimize()``, as the default value is sensible. Second, the method will raise an error
if ``termcrit.type!=(TermCriteria::MAX_ITER+TermCriteria::EPS)`` and ``termcrit.type!=TermCriteria::MAX_ITER``. This means that termination criteria
has to restrict maximum number of iterations to be done and may optionally allow algorithm to stop earlier if certain tolerance
is achieved (what we mean by "tolerance is achieved" will be clarified below). If ``termcrit`` restricts both tolerance and maximum iteration
number, both ``termcrit.epsilon`` and ``termcrit.maxCount`` should be positive. In case, if ``termcrit.type==TermCriteria::MAX_ITER``,
only member ``termcrit.maxCount`` is required to be positive and in this case algorithm will just work for required number of iterations.
In current implementation, "tolerance is achieved" means that we have arrived at the point where the :math:`L_2`-norm of the gradient is less
than the tolerance value.
.. ocv:function:: void ConjGradSolver::setTermCriteria(const TermCriteria& termcrit)
:param termcrit: Terminal criteria to be used, represented as ``TermCriteria`` structure (defined elsewhere in openCV). Mind you, that it should meet ``termcrit.type==(TermCriteria::MAX_ITER+TermCriteria::EPS) && termcrit.epsilon>0 && termcrit.maxCount>0`` or ``termcrit.type==TermCriteria::MAX_ITER) && termcrit.maxCount>0``, otherwise the error will be raised.
ConjGradSolver::minimize
-----------------------------------
The main method of the ``ConjGradSolver``. It actually runs the algorithm and performs the minimization. The sole input parameter determines the
centroid of the starting simplex (roughly, it tells where to start), all the others (terminal criteria and function to be minimized)
are supposed to be set via the setters before the call to this method or the default values (not always sensible) will be used. Sometimes it may
throw an error, if these default values cannot be used (say, you forgot to set the function to minimize and default value, that is, empty function,
cannot be used).
.. ocv:function:: double ConjGradSolver::minimize(InputOutputArray x)
:param x: The initial point. It is hard to overemphasize how important the choise of initial point is when you are using the heuristic algorithm like this one. Badly chosen initial point can make algorithm converge to (local) maximum instead of minimum, do not converge at all, converge to local minimum instead of global one.
:return: The value of a function at the point found.
createConjGradSolver
------------------------------------
This function returns the reference to the ready-to-use ``ConjGradSolver`` object. All the parameters are optional, so this procedure can be called
even without parameters at all. In this case, the default values will be used. As default value for terminal criteria are the only sensible ones,
``ConjGradSolver::setFunction()`` should be called upon the obtained object, if the function
was not given to ``createConjGradSolver()``. Otherwise, the two ways (submit it to ``createConjGradSolver()`` or miss it out and call the
``ConjGradSolver::setFunction()``) are absolutely equivalent (and will drop the same errors in the same way,
should invalid input be detected).
.. ocv:function:: Ptr<ConjGradSolver> createConjGradSolver(const Ptr<Solver::Function>& f, TermCriteria termcrit)
:param f: Pointer to the function that will be minimized, similarly to the one you submit via ``ConjGradSolver::setFunction``.
:param termcrit: Terminal criteria to the algorithm, similarly to the one you submit via ``ConjGradSolver::setTermCriteria``.

@ -506,96 +506,6 @@ CV_EXPORTS_W void randn(InputOutputArray dst, InputArray mean, InputArray stddev
//! shuffles the input array elements //! shuffles the input array elements
CV_EXPORTS_W void randShuffle(InputOutputArray dst, double iterFactor = 1., RNG* rng = 0); CV_EXPORTS_W void randShuffle(InputOutputArray dst, double iterFactor = 1., RNG* rng = 0);
//! draws the line segment (pt1, pt2) in the image
CV_EXPORTS_W void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
int thickness = 1, int lineType = LINE_8, int shift = 0);
//! draws an arrow from pt1 to pt2 in the image
CV_EXPORTS_W void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
int thickness=1, int line_type=8, int shift=0, double tipLength=0.1);
//! draws the rectangle outline or a solid rectangle with the opposite corners pt1 and pt2 in the image
CV_EXPORTS_W void rectangle(InputOutputArray img, Point pt1, Point pt2,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
//! draws the rectangle outline or a solid rectangle covering rec in the image
CV_EXPORTS void rectangle(CV_IN_OUT Mat& img, Rect rec,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
//! draws the circle outline or a solid circle in the image
CV_EXPORTS_W void circle(InputOutputArray img, Point center, int radius,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
//! draws an elliptic arc, ellipse sector or a rotated ellipse in the image
CV_EXPORTS_W void ellipse(InputOutputArray img, Point center, Size axes,
double angle, double startAngle, double endAngle,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
//! draws a rotated ellipse in the image
CV_EXPORTS_W void ellipse(InputOutputArray img, const RotatedRect& box, const Scalar& color,
int thickness = 1, int lineType = LINE_8);
//! draws a filled convex polygon in the image
CV_EXPORTS void fillConvexPoly(Mat& img, const Point* pts, int npts,
const Scalar& color, int lineType = LINE_8,
int shift = 0);
CV_EXPORTS_W void fillConvexPoly(InputOutputArray img, InputArray points,
const Scalar& color, int lineType = LINE_8,
int shift = 0);
//! fills an area bounded by one or more polygons
CV_EXPORTS void fillPoly(Mat& img, const Point** pts,
const int* npts, int ncontours,
const Scalar& color, int lineType = LINE_8, int shift = 0,
Point offset = Point() );
CV_EXPORTS_W void fillPoly(InputOutputArray img, InputArrayOfArrays pts,
const Scalar& color, int lineType = LINE_8, int shift = 0,
Point offset = Point() );
//! draws one or more polygonal curves
CV_EXPORTS void polylines(Mat& img, const Point* const* pts, const int* npts,
int ncontours, bool isClosed, const Scalar& color,
int thickness = 1, int lineType = LINE_8, int shift = 0 );
CV_EXPORTS_W void polylines(InputOutputArray img, InputArrayOfArrays pts,
bool isClosed, const Scalar& color,
int thickness = 1, int lineType = LINE_8, int shift = 0 );
//! draws contours in the image
CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color,
int thickness = 1, int lineType = LINE_8,
InputArray hierarchy = noArray(),
int maxLevel = INT_MAX, Point offset = Point() );
//! clips the line segment by the rectangle Rect(0, 0, imgSize.width, imgSize.height)
CV_EXPORTS bool clipLine(Size imgSize, CV_IN_OUT Point& pt1, CV_IN_OUT Point& pt2);
//! clips the line segment by the rectangle imgRect
CV_EXPORTS_W bool clipLine(Rect imgRect, CV_OUT CV_IN_OUT Point& pt1, CV_OUT CV_IN_OUT Point& pt2);
//! converts elliptic arc to a polygonal curve
CV_EXPORTS_W void ellipse2Poly( Point center, Size axes, int angle,
int arcStart, int arcEnd, int delta,
CV_OUT std::vector<Point>& pts );
//! renders text string in the image
CV_EXPORTS_W void putText( InputOutputArray img, const String& text, Point org,
int fontFace, double fontScale, Scalar color,
int thickness = 1, int lineType = LINE_8,
bool bottomLeftOrigin = false );
//! returns bounding box of the text string
CV_EXPORTS_W Size getTextSize(const String& text, int fontFace,
double fontScale, int thickness,
CV_OUT int* baseLine);
/*! /*!
Principal Component Analysis Principal Component Analysis
@ -1319,5 +1229,7 @@ template<> struct ParamType<uchar>
#include "opencv2/core/operations.hpp" #include "opencv2/core/operations.hpp"
#include "opencv2/core/cvstd.inl.hpp" #include "opencv2/core/cvstd.inl.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/core/optim.hpp"
#endif /*__OPENCV_CORE_HPP__*/ #endif /*__OPENCV_CORE_HPP__*/

@ -1262,192 +1262,6 @@ CVAPI(int) cvNextGraphItem( CvGraphScanner* scanner );
/* Creates a copy of graph */ /* Creates a copy of graph */
CVAPI(CvGraph*) cvCloneGraph( const CvGraph* graph, CvMemStorage* storage ); CVAPI(CvGraph*) cvCloneGraph( const CvGraph* graph, CvMemStorage* storage );
/****************************************************************************************\
* Drawing *
\****************************************************************************************/
/****************************************************************************************\
* Drawing functions work with images/matrices of arbitrary type. *
* For color images the channel order is BGR[A] *
* Antialiasing is supported only for 8-bit image now. *
* All the functions include parameter color that means rgb value (that may be *
* constructed with CV_RGB macro) for color images and brightness *
* for grayscale images. *
* If a drawn figure is partially or completely outside of the image, it is clipped.*
\****************************************************************************************/
#define CV_RGB( r, g, b ) cvScalar( (b), (g), (r), 0 )
#define CV_FILLED -1
#define CV_AA 16
/* Draws 4-connected, 8-connected or antialiased line segment connecting two points */
CVAPI(void) cvLine( CvArr* img, CvPoint pt1, CvPoint pt2,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) );
/* Draws a rectangle given two opposite corners of the rectangle (pt1 & pt2),
if thickness<0 (e.g. thickness == CV_FILLED), the filled box is drawn */
CVAPI(void) cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8),
int shift CV_DEFAULT(0));
/* Draws a rectangle specified by a CvRect structure */
CVAPI(void) cvRectangleR( CvArr* img, CvRect r,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8),
int shift CV_DEFAULT(0));
/* Draws a circle with specified center and radius.
Thickness works in the same way as with cvRectangle */
CVAPI(void) cvCircle( CvArr* img, CvPoint center, int radius,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));
/* Draws ellipse outline, filled ellipse, elliptic arc or filled elliptic sector,
depending on <thickness>, <start_angle> and <end_angle> parameters. The resultant figure
is rotated by <angle>. All the angles are in degrees */
CVAPI(void) cvEllipse( CvArr* img, CvPoint center, CvSize axes,
double angle, double start_angle, double end_angle,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));
CV_INLINE void cvEllipseBox( CvArr* img, CvBox2D box, CvScalar color,
int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) )
{
CvSize axes;
axes.width = cvRound(box.size.width*0.5);
axes.height = cvRound(box.size.height*0.5);
cvEllipse( img, cvPointFrom32f( box.center ), axes, box.angle,
0, 360, color, thickness, line_type, shift );
}
/* Fills convex or monotonous polygon. */
CVAPI(void) cvFillConvexPoly( CvArr* img, const CvPoint* pts, int npts, CvScalar color,
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));
/* Fills an area bounded by one or more arbitrary polygons */
CVAPI(void) cvFillPoly( CvArr* img, CvPoint** pts, const int* npts,
int contours, CvScalar color,
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) );
/* Draws one or more polygonal curves */
CVAPI(void) cvPolyLine( CvArr* img, CvPoint** pts, const int* npts, int contours,
int is_closed, CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) );
#define cvDrawRect cvRectangle
#define cvDrawLine cvLine
#define cvDrawCircle cvCircle
#define cvDrawEllipse cvEllipse
#define cvDrawPolyLine cvPolyLine
/* Clips the line segment connecting *pt1 and *pt2
by the rectangular window
(0<=x<img_size.width, 0<=y<img_size.height). */
CVAPI(int) cvClipLine( CvSize img_size, CvPoint* pt1, CvPoint* pt2 );
/* Initializes line iterator. Initially, line_iterator->ptr will point
to pt1 (or pt2, see left_to_right description) location in the image.
Returns the number of pixels on the line between the ending points. */
CVAPI(int) cvInitLineIterator( const CvArr* image, CvPoint pt1, CvPoint pt2,
CvLineIterator* line_iterator,
int connectivity CV_DEFAULT(8),
int left_to_right CV_DEFAULT(0));
/* Moves iterator to the next line point */
#define CV_NEXT_LINE_POINT( line_iterator ) \
{ \
int _line_iterator_mask = (line_iterator).err < 0 ? -1 : 0; \
(line_iterator).err += (line_iterator).minus_delta + \
((line_iterator).plus_delta & _line_iterator_mask); \
(line_iterator).ptr += (line_iterator).minus_step + \
((line_iterator).plus_step & _line_iterator_mask); \
}
/* basic font types */
#define CV_FONT_HERSHEY_SIMPLEX 0
#define CV_FONT_HERSHEY_PLAIN 1
#define CV_FONT_HERSHEY_DUPLEX 2
#define CV_FONT_HERSHEY_COMPLEX 3
#define CV_FONT_HERSHEY_TRIPLEX 4
#define CV_FONT_HERSHEY_COMPLEX_SMALL 5
#define CV_FONT_HERSHEY_SCRIPT_SIMPLEX 6
#define CV_FONT_HERSHEY_SCRIPT_COMPLEX 7
/* font flags */
#define CV_FONT_ITALIC 16
#define CV_FONT_VECTOR0 CV_FONT_HERSHEY_SIMPLEX
/* Font structure */
typedef struct CvFont
{
const char* nameFont; //Qt:nameFont
CvScalar color; //Qt:ColorFont -> cvScalar(blue_component, green_component, red\_component[, alpha_component])
int font_face; //Qt: bool italic /* =CV_FONT_* */
const int* ascii; /* font data and metrics */
const int* greek;
const int* cyrillic;
float hscale, vscale;
float shear; /* slope coefficient: 0 - normal, >0 - italic */
int thickness; //Qt: weight /* letters thickness */
float dx; /* horizontal interval between letters */
int line_type; //Qt: PointSize
}
CvFont;
/* Initializes font structure used further in cvPutText */
CVAPI(void) cvInitFont( CvFont* font, int font_face,
double hscale, double vscale,
double shear CV_DEFAULT(0),
int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8));
CV_INLINE CvFont cvFont( double scale, int thickness CV_DEFAULT(1) )
{
CvFont font;
cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, scale, scale, 0, thickness, CV_AA );
return font;
}
/* Renders text stroke with specified font and color at specified location.
CvFont should be initialized with cvInitFont */
CVAPI(void) cvPutText( CvArr* img, const char* text, CvPoint org,
const CvFont* font, CvScalar color );
/* Calculates bounding box of text stroke (useful for alignment) */
CVAPI(void) cvGetTextSize( const char* text_string, const CvFont* font,
CvSize* text_size, int* baseline );
/* Unpacks color value, if arrtype is CV_8UC?, <color> is treated as
packed color value, otherwise the first channels (depending on arrtype)
of destination scalar are set to the same value = <color> */
CVAPI(CvScalar) cvColorToScalar( double packed_color, int arrtype );
/* Returns the polygon points which make up the given ellipse. The ellipse is define by
the box of size 'axes' rotated 'angle' around the 'center'. A partial sweep
of the ellipse arc can be done by spcifying arc_start and arc_end to be something
other than 0 and 360, respectively. The input array 'pts' must be large enough to
hold the result. The total number of points stored into 'pts' is returned by this
function. */
CVAPI(int) cvEllipse2Poly( CvPoint center, CvSize axes,
int angle, int arc_start, int arc_end, CvPoint * pts, int delta );
/* Draws contour outlines or filled interiors on the image */
CVAPI(void) cvDrawContours( CvArr *img, CvSeq* contour,
CvScalar external_color, CvScalar hole_color,
int max_level, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8),
CvPoint offset CV_DEFAULT(cvPoint(0,0)));
/* Does look-up transformation. Elements of the source array /* Does look-up transformation. Elements of the source array
(that should be 8uC1 or 8sC1) are used as indexes in lutarr 256-element table */ (that should be 8uC1 or 8sC1) are used as indexes in lutarr 256-element table */

@ -44,9 +44,10 @@
#include "opencv2/core.hpp" #include "opencv2/core.hpp"
namespace cv{namespace optim namespace cv
{ {
class CV_EXPORTS Solver : public Algorithm
class CV_EXPORTS MinProblemSolver : public Algorithm
{ {
public: public:
class CV_EXPORTS Function class CV_EXPORTS Function
@ -70,7 +71,7 @@ public:
}; };
//! downhill simplex class //! downhill simplex class
class CV_EXPORTS DownhillSolver : public Solver class CV_EXPORTS DownhillSolver : public MinProblemSolver
{ {
public: public:
//! returns row-vector, even if the column-vector was given //! returns row-vector, even if the column-vector was given
@ -78,20 +79,22 @@ public:
//!This should be called at least once before the first call to minimize() and step is assumed to be (something that //!This should be called at least once before the first call to minimize() and step is assumed to be (something that
//! after getMat() will return) row-vector or column-vector. *It's dimensionality determines the dimensionality of a problem.* //! after getMat() will return) row-vector or column-vector. *It's dimensionality determines the dimensionality of a problem.*
virtual void setInitStep(InputArray step)=0; virtual void setInitStep(InputArray step)=0;
};
// both minRange & minError are specified by termcrit.epsilon; In addition, user may specify the number of iterations that the algorithm does. // both minRange & minError are specified by termcrit.epsilon;
CV_EXPORTS_W Ptr<DownhillSolver> createDownhillSolver(const Ptr<Solver::Function>& f=Ptr<Solver::Function>(), // In addition, user may specify the number of iterations that the algorithm does.
InputArray initStep=Mat_<double>(1,1,0.0), static Ptr<DownhillSolver> create(const Ptr<MinProblemSolver::Function>& f=Ptr<MinProblemSolver::Function>(),
TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5000,0.000001)); InputArray initStep=Mat_<double>(1,1,0.0),
TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5000,0.000001));
};
//! conjugate gradient method //! conjugate gradient method
class CV_EXPORTS ConjGradSolver : public Solver{ class CV_EXPORTS ConjGradSolver : public MinProblemSolver
{
public:
static Ptr<ConjGradSolver> create(const Ptr<MinProblemSolver::Function>& f=Ptr<ConjGradSolver::Function>(),
TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5000,0.000001));
}; };
CV_EXPORTS_W Ptr<ConjGradSolver> createConjGradSolver(const Ptr<Solver::Function>& f=Ptr<ConjGradSolver::Function>(),
TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5000,0.000001));
//!the return codes for solveLP() function //!the return codes for solveLP() function
enum enum
{ {
@ -102,7 +105,7 @@ enum
}; };
CV_EXPORTS_W int solveLP(const Mat& Func, const Mat& Constr, Mat& z); CV_EXPORTS_W int solveLP(const Mat& Func, const Mat& Constr, Mat& z);
CV_EXPORTS_W void denoise_TVL1(const std::vector<Mat>& observations,Mat& result, double lambda=1.0, int niters=30);
}}// cv }// cv
#endif #endif

@ -40,10 +40,12 @@
//M*/ //M*/
#include "precomp.hpp" #include "precomp.hpp"
#undef ALEX_DEBUG
#include "debug.hpp"
namespace cv{namespace optim{ #define dprintf(x)
#define print_matrix(x)
namespace cv
{
#define SEC_METHOD_ITERATIONS 4 #define SEC_METHOD_ITERATIONS 4
#define INITIAL_SEC_METHOD_SIGMA 0.1 #define INITIAL_SEC_METHOD_SIGMA 0.1
@ -57,15 +59,15 @@ namespace cv{namespace optim{
void setTermCriteria(const TermCriteria& termcrit); void setTermCriteria(const TermCriteria& termcrit);
double minimize(InputOutputArray x); double minimize(InputOutputArray x);
protected: protected:
Ptr<Solver::Function> _Function; Ptr<MinProblemSolver::Function> _Function;
TermCriteria _termcrit; TermCriteria _termcrit;
Mat_<double> d,r,buf_x,r_old; Mat_<double> d,r,buf_x,r_old;
Mat_<double> minimizeOnTheLine_buf1,minimizeOnTheLine_buf2; Mat_<double> minimizeOnTheLine_buf1,minimizeOnTheLine_buf2;
private: private:
static void minimizeOnTheLine(Ptr<Solver::Function> _f,Mat_<double>& x,const Mat_<double>& d,Mat_<double>& buf1,Mat_<double>& buf2); static void minimizeOnTheLine(Ptr<MinProblemSolver::Function> _f,Mat_<double>& x,const Mat_<double>& d,Mat_<double>& buf1,Mat_<double>& buf2);
}; };
void ConjGradSolverImpl::minimizeOnTheLine(Ptr<Solver::Function> _f,Mat_<double>& x,const Mat_<double>& d,Mat_<double>& buf1, void ConjGradSolverImpl::minimizeOnTheLine(Ptr<MinProblemSolver::Function> _f,Mat_<double>& x,const Mat_<double>& d,Mat_<double>& buf1,
Mat_<double>& buf2){ Mat_<double>& buf2){
double sigma=INITIAL_SEC_METHOD_SIGMA; double sigma=INITIAL_SEC_METHOD_SIGMA;
buf1=0.0; buf1=0.0;
@ -160,7 +162,7 @@ namespace cv{namespace optim{
ConjGradSolverImpl::ConjGradSolverImpl(){ ConjGradSolverImpl::ConjGradSolverImpl(){
_Function=Ptr<Function>(); _Function=Ptr<Function>();
} }
Ptr<Solver::Function> ConjGradSolverImpl::getFunction()const{ Ptr<MinProblemSolver::Function> ConjGradSolverImpl::getFunction()const{
return _Function; return _Function;
} }
void ConjGradSolverImpl::setFunction(const Ptr<Function>& f){ void ConjGradSolverImpl::setFunction(const Ptr<Function>& f){
@ -175,10 +177,10 @@ namespace cv{namespace optim{
_termcrit=termcrit; _termcrit=termcrit;
} }
// both minRange & minError are specified by termcrit.epsilon; In addition, user may specify the number of iterations that the algorithm does. // both minRange & minError are specified by termcrit.epsilon; In addition, user may specify the number of iterations that the algorithm does.
Ptr<ConjGradSolver> createConjGradSolver(const Ptr<Solver::Function>& f, TermCriteria termcrit){ Ptr<ConjGradSolver> ConjGradSolver::create(const Ptr<MinProblemSolver::Function>& f, TermCriteria termcrit){
ConjGradSolver *CG=new ConjGradSolverImpl(); Ptr<ConjGradSolver> CG = makePtr<ConjGradSolverImpl>();
CG->setFunction(f); CG->setFunction(f);
CG->setTermCriteria(termcrit); CG->setTermCriteria(termcrit);
return Ptr<ConjGradSolver>(CG); return CG;
} }
}} }

@ -3528,492 +3528,9 @@ cvPrevTreeNode( CvTreeNodeIterator* treeIterator )
return prevNode; return prevNode;
} }
namespace cv namespace cv
{ {
// This is reimplementation of kd-trees from cvkdtree*.* by Xavier Delacour, cleaned-up and
// adopted to work with the new OpenCV data structures. It's in cxcore to be shared by
// both cv (CvFeatureTree) and ml (kNN).
// The algorithm is taken from:
// J.S. Beis and D.G. Lowe. Shape indexing using approximate nearest-neighbor search
// in highdimensional spaces. In Proc. IEEE Conf. Comp. Vision Patt. Recog.,
// pages 1000--1006, 1997. http://citeseer.ist.psu.edu/beis97shape.html
const int MAX_TREE_DEPTH = 32;
KDTree::KDTree()
{
maxDepth = -1;
normType = NORM_L2;
}
KDTree::KDTree(InputArray _points, bool _copyData)
{
maxDepth = -1;
normType = NORM_L2;
build(_points, _copyData);
}
KDTree::KDTree(InputArray _points, InputArray _labels, bool _copyData)
{
maxDepth = -1;
normType = NORM_L2;
build(_points, _labels, _copyData);
}
struct SubTree
{
SubTree() : first(0), last(0), nodeIdx(0), depth(0) {}
SubTree(int _first, int _last, int _nodeIdx, int _depth)
: first(_first), last(_last), nodeIdx(_nodeIdx), depth(_depth) {}
int first;
int last;
int nodeIdx;
int depth;
};
static float
medianPartition( size_t* ofs, int a, int b, const float* vals )
{
int k, a0 = a, b0 = b;
int middle = (a + b)/2;
while( b > a )
{
int i0 = a, i1 = (a+b)/2, i2 = b;
float v0 = vals[ofs[i0]], v1 = vals[ofs[i1]], v2 = vals[ofs[i2]];
int ip = v0 < v1 ? (v1 < v2 ? i1 : v0 < v2 ? i2 : i0) :
v0 < v2 ? i0 : (v1 < v2 ? i2 : i1);
float pivot = vals[ofs[ip]];
std::swap(ofs[ip], ofs[i2]);
for( i1 = i0, i0--; i1 <= i2; i1++ )
if( vals[ofs[i1]] <= pivot )
{
i0++;
std::swap(ofs[i0], ofs[i1]);
}
if( i0 == middle )
break;
if( i0 > middle )
b = i0 - (b == i0);
else
a = i0;
}
float pivot = vals[ofs[middle]];
int less = 0, more = 0;
for( k = a0; k < middle; k++ )
{
CV_Assert(vals[ofs[k]] <= pivot);
less += vals[ofs[k]] < pivot;
}
for( k = b0; k > middle; k-- )
{
CV_Assert(vals[ofs[k]] >= pivot);
more += vals[ofs[k]] > pivot;
}
CV_Assert(std::abs(more - less) <= 1);
return vals[ofs[middle]];
}
static void
computeSums( const Mat& points, const size_t* ofs, int a, int b, double* sums )
{
int i, j, dims = points.cols;
const float* data = points.ptr<float>(0);
for( j = 0; j < dims; j++ )
sums[j*2] = sums[j*2+1] = 0;
for( i = a; i <= b; i++ )
{
const float* row = data + ofs[i];
for( j = 0; j < dims; j++ )
{
double t = row[j], s = sums[j*2] + t, s2 = sums[j*2+1] + t*t;
sums[j*2] = s; sums[j*2+1] = s2;
}
}
}
void KDTree::build(InputArray _points, bool _copyData)
{
build(_points, noArray(), _copyData);
}
void KDTree::build(InputArray __points, InputArray __labels, bool _copyData)
{
Mat _points = __points.getMat(), _labels = __labels.getMat();
CV_Assert(_points.type() == CV_32F && !_points.empty());
std::vector<KDTree::Node>().swap(nodes);
if( !_copyData )
points = _points;
else
{
points.release();
points.create(_points.size(), _points.type());
}
int i, j, n = _points.rows, ptdims = _points.cols, top = 0;
const float* data = _points.ptr<float>(0);
float* dstdata = points.ptr<float>(0);
size_t step = _points.step1();
size_t dstep = points.step1();
int ptpos = 0;
labels.resize(n);
const int* _labels_data = 0;
if( !_labels.empty() )
{
int nlabels = _labels.checkVector(1, CV_32S, true);
CV_Assert(nlabels == n);
_labels_data = _labels.ptr<int>();
}
Mat sumstack(MAX_TREE_DEPTH*2, ptdims*2, CV_64F);
SubTree stack[MAX_TREE_DEPTH*2];
std::vector<size_t> _ptofs(n);
size_t* ptofs = &_ptofs[0];
for( i = 0; i < n; i++ )
ptofs[i] = i*step;
nodes.push_back(Node());
computeSums(points, ptofs, 0, n-1, sumstack.ptr<double>(top));
stack[top++] = SubTree(0, n-1, 0, 0);
int _maxDepth = 0;
while( --top >= 0 )
{
int first = stack[top].first, last = stack[top].last;
int depth = stack[top].depth, nidx = stack[top].nodeIdx;
int count = last - first + 1, dim = -1;
const double* sums = sumstack.ptr<double>(top);
double invCount = 1./count, maxVar = -1.;
if( count == 1 )
{
int idx0 = (int)(ptofs[first]/step);
int idx = _copyData ? ptpos++ : idx0;
nodes[nidx].idx = ~idx;
if( _copyData )
{
const float* src = data + ptofs[first];
float* dst = dstdata + idx*dstep;
for( j = 0; j < ptdims; j++ )
dst[j] = src[j];
}
labels[idx] = _labels_data ? _labels_data[idx0] : idx0;
_maxDepth = std::max(_maxDepth, depth);
continue;
}
// find the dimensionality with the biggest variance
for( j = 0; j < ptdims; j++ )
{
double m = sums[j*2]*invCount;
double varj = sums[j*2+1]*invCount - m*m;
if( maxVar < varj )
{
maxVar = varj;
dim = j;
}
}
int left = (int)nodes.size(), right = left + 1;
nodes.push_back(Node());
nodes.push_back(Node());
nodes[nidx].idx = dim;
nodes[nidx].left = left;
nodes[nidx].right = right;
nodes[nidx].boundary = medianPartition(ptofs, first, last, data + dim);
int middle = (first + last)/2;
double *lsums = (double*)sums, *rsums = lsums + ptdims*2;
computeSums(points, ptofs, middle+1, last, rsums);
for( j = 0; j < ptdims*2; j++ )
lsums[j] = sums[j] - rsums[j];
stack[top++] = SubTree(first, middle, left, depth+1);
stack[top++] = SubTree(middle+1, last, right, depth+1);
}
maxDepth = _maxDepth;
}
struct PQueueElem
{
PQueueElem() : dist(0), idx(0) {}
PQueueElem(float _dist, int _idx) : dist(_dist), idx(_idx) {}
float dist;
int idx;
};
int KDTree::findNearest(InputArray _vec, int K, int emax,
OutputArray _neighborsIdx, OutputArray _neighbors,
OutputArray _dist, OutputArray _labels) const
{
Mat vecmat = _vec.getMat();
CV_Assert( vecmat.isContinuous() && vecmat.type() == CV_32F && vecmat.total() == (size_t)points.cols );
const float* vec = vecmat.ptr<float>();
K = std::min(K, points.rows);
int ptdims = points.cols;
CV_Assert(K > 0 && (normType == NORM_L2 || normType == NORM_L1));
AutoBuffer<uchar> _buf((K+1)*(sizeof(float) + sizeof(int)));
int* idx = (int*)(uchar*)_buf;
float* dist = (float*)(idx + K + 1);
int i, j, ncount = 0, e = 0;
int qsize = 0, maxqsize = 1 << 10;
AutoBuffer<uchar> _pqueue(maxqsize*sizeof(PQueueElem));
PQueueElem* pqueue = (PQueueElem*)(uchar*)_pqueue;
emax = std::max(emax, 1);
for( e = 0; e < emax; )
{
float d, alt_d = 0.f;
int nidx;
if( e == 0 )
nidx = 0;
else
{
// take the next node from the priority queue
if( qsize == 0 )
break;
nidx = pqueue[0].idx;
alt_d = pqueue[0].dist;
if( --qsize > 0 )
{
std::swap(pqueue[0], pqueue[qsize]);
d = pqueue[0].dist;
for( i = 0;;)
{
int left = i*2 + 1, right = i*2 + 2;
if( left >= qsize )
break;
if( right < qsize && pqueue[right].dist < pqueue[left].dist )
left = right;
if( pqueue[left].dist >= d )
break;
std::swap(pqueue[i], pqueue[left]);
i = left;
}
}
if( ncount == K && alt_d > dist[ncount-1] )
continue;
}
for(;;)
{
if( nidx < 0 )
break;
const Node& n = nodes[nidx];
if( n.idx < 0 )
{
i = ~n.idx;
const float* row = points.ptr<float>(i);
if( normType == NORM_L2 )
for( j = 0, d = 0.f; j < ptdims; j++ )
{
float t = vec[j] - row[j];
d += t*t;
}
else
for( j = 0, d = 0.f; j < ptdims; j++ )
d += std::abs(vec[j] - row[j]);
dist[ncount] = d;
idx[ncount] = i;
for( i = ncount-1; i >= 0; i-- )
{
if( dist[i] <= d )
break;
std::swap(dist[i], dist[i+1]);
std::swap(idx[i], idx[i+1]);
}
ncount += ncount < K;
e++;
break;
}
int alt;
if( vec[n.idx] <= n.boundary )
{
nidx = n.left;
alt = n.right;
}
else
{
nidx = n.right;
alt = n.left;
}
d = vec[n.idx] - n.boundary;
if( normType == NORM_L2 )
d = d*d + alt_d;
else
d = std::abs(d) + alt_d;
// subtree prunning
if( ncount == K && d > dist[ncount-1] )
continue;
// add alternative subtree to the priority queue
pqueue[qsize] = PQueueElem(d, alt);
for( i = qsize; i > 0; )
{
int parent = (i-1)/2;
if( parent < 0 || pqueue[parent].dist <= d )
break;
std::swap(pqueue[i], pqueue[parent]);
i = parent;
}
qsize += qsize+1 < maxqsize;
}
}
K = std::min(K, ncount);
if( _neighborsIdx.needed() )
{
_neighborsIdx.create(K, 1, CV_32S, -1, true);
Mat nidx = _neighborsIdx.getMat();
Mat(nidx.size(), CV_32S, &idx[0]).copyTo(nidx);
}
if( _dist.needed() )
sqrt(Mat(K, 1, CV_32F, dist), _dist);
if( _neighbors.needed() || _labels.needed() )
getPoints(Mat(K, 1, CV_32S, idx), _neighbors, _labels);
return K;
}
void KDTree::findOrthoRange(InputArray _lowerBound,
InputArray _upperBound,
OutputArray _neighborsIdx,
OutputArray _neighbors,
OutputArray _labels ) const
{
int ptdims = points.cols;
Mat lowerBound = _lowerBound.getMat(), upperBound = _upperBound.getMat();
CV_Assert( lowerBound.size == upperBound.size &&
lowerBound.isContinuous() &&
upperBound.isContinuous() &&
lowerBound.type() == upperBound.type() &&
lowerBound.type() == CV_32F &&
lowerBound.total() == (size_t)ptdims );
const float* L = lowerBound.ptr<float>();
const float* R = upperBound.ptr<float>();
std::vector<int> idx;
AutoBuffer<int> _stack(MAX_TREE_DEPTH*2 + 1);
int* stack = _stack;
int top = 0;
stack[top++] = 0;
while( --top >= 0 )
{
int nidx = stack[top];
if( nidx < 0 )
break;
const Node& n = nodes[nidx];
if( n.idx < 0 )
{
int j, i = ~n.idx;
const float* row = points.ptr<float>(i);
for( j = 0; j < ptdims; j++ )
if( row[j] < L[j] || row[j] >= R[j] )
break;
if( j == ptdims )
idx.push_back(i);
continue;
}
if( L[n.idx] <= n.boundary )
stack[top++] = n.left;
if( R[n.idx] > n.boundary )
stack[top++] = n.right;
}
if( _neighborsIdx.needed() )
{
_neighborsIdx.create((int)idx.size(), 1, CV_32S, -1, true);
Mat nidx = _neighborsIdx.getMat();
Mat(nidx.size(), CV_32S, &idx[0]).copyTo(nidx);
}
getPoints( idx, _neighbors, _labels );
}
void KDTree::getPoints(InputArray _idx, OutputArray _pts, OutputArray _labels) const
{
Mat idxmat = _idx.getMat(), pts, labelsmat;
CV_Assert( idxmat.isContinuous() && idxmat.type() == CV_32S &&
(idxmat.cols == 1 || idxmat.rows == 1) );
const int* idx = idxmat.ptr<int>();
int* dstlabels = 0;
int ptdims = points.cols;
int i, nidx = (int)idxmat.total();
if( nidx == 0 )
{
_pts.release();
_labels.release();
return;
}
if( _pts.needed() )
{
_pts.create( nidx, ptdims, points.type());
pts = _pts.getMat();
}
if(_labels.needed())
{
_labels.create(nidx, 1, CV_32S, -1, true);
labelsmat = _labels.getMat();
CV_Assert( labelsmat.isContinuous() );
dstlabels = labelsmat.ptr<int>();
}
const int* srclabels = !labels.empty() ? &labels[0] : 0;
for( i = 0; i < nidx; i++ )
{
int k = idx[i];
CV_Assert( (unsigned)k < (unsigned)points.rows );
const float* src = points.ptr<float>(k);
if( !pts.empty() )
std::copy(src, src + ptdims, pts.ptr<float>(i));
if( dstlabels )
dstlabels[i] = srclabels ? srclabels[k] : k;
}
}
const float* KDTree::getPoint(int ptidx, int* label) const
{
CV_Assert( (unsigned)ptidx < (unsigned)points.rows);
if(label)
*label = labels[ptidx];
return points.ptr<float>(ptidx);
}
int KDTree::dims() const
{
return !points.empty() ? points.cols : 0;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
schar* seqPush( CvSeq* seq, const void* element ) schar* seqPush( CvSeq* seq, const void* element )

@ -39,8 +39,9 @@
// //
//M*/ //M*/
#include "precomp.hpp" #include "precomp.hpp"
#include "debug.hpp"
#include "opencv2/core/core_c.h" #define dprintf(x)
#define print_matrix(x)
/* /*
@ -83,13 +84,13 @@ Created by @SareeAlnaghy
using namespace std; using namespace std;
using namespace cv; using namespace cv;
void test(Ptr<optim::DownhillSolver> solver, Ptr<optim::Solver::Function> ptr_F, Mat &P, Mat &step) void test(Ptr<optim::DownhillSolver> MinProblemSolver, Ptr<optim::MinProblemSolver::Function> ptr_F, Mat &P, Mat &step)
{ {
try{ try{
solver->setFunction(ptr_F); MinProblemSolver->setFunction(ptr_F);
solver->setInitStep(step); MinProblemSolver->setInitStep(step);
double res = solver->minimize(P); double res = MinProblemSolver->minimize(P);
cout << "res " << res << endl; cout << "res " << res << endl;
} }
@ -102,7 +103,7 @@ cerr << "Error:: " << e.what() << endl;
int main() int main()
{ {
class DistanceToLines :public optim::Solver::Function { class DistanceToLines :public optim::MinProblemSolver::Function {
public: public:
double calc(const double* x)const{ double calc(const double* x)const{
@ -114,10 +115,10 @@ return x[0] * x[0] + x[1] * x[1];
Mat P = (Mat_<double>(1, 2) << 1.0, 1.0); Mat P = (Mat_<double>(1, 2) << 1.0, 1.0);
Mat step = (Mat_<double>(2, 1) << -0.5, 0.5); Mat step = (Mat_<double>(2, 1) << -0.5, 0.5);
Ptr<optim::Solver::Function> ptr_F(new DistanceToLines()); Ptr<optim::MinProblemSolver::Function> ptr_F(new DistanceToLines());
Ptr<optim::DownhillSolver> solver = optim::createDownhillSolver(); Ptr<optim::DownhillSolver> MinProblemSolver = optim::createDownhillSolver();
test(solver, ptr_F, P, step); test(MinProblemSolver, ptr_F, P, step);
system("pause"); system("pause");
return 0; return 0;
@ -131,11 +132,8 @@ multiple lines in three dimensions as not all lines intersect in three dimension
*/ */
namespace cv
{
namespace cv{namespace optim{
class DownhillSolverImpl : public DownhillSolver class DownhillSolverImpl : public DownhillSolver
{ {
@ -149,7 +147,7 @@ namespace cv{namespace optim{
void setTermCriteria(const TermCriteria& termcrit); void setTermCriteria(const TermCriteria& termcrit);
double minimize(InputOutputArray x); double minimize(InputOutputArray x);
protected: protected:
Ptr<Solver::Function> _Function; Ptr<MinProblemSolver::Function> _Function;
TermCriteria _termcrit; TermCriteria _termcrit;
Mat _step; Mat _step;
Mat_<double> buf_x; Mat_<double> buf_x;
@ -157,8 +155,8 @@ namespace cv{namespace optim{
private: private:
inline void createInitialSimplex(Mat_<double>& simplex,Mat& step); inline void createInitialSimplex(Mat_<double>& simplex,Mat& step);
inline double innerDownhillSimplex(cv::Mat_<double>& p,double MinRange,double MinError,int& nfunk, inline double innerDownhillSimplex(cv::Mat_<double>& p,double MinRange,double MinError,int& nfunk,
const Ptr<Solver::Function>& f,int nmax); const Ptr<MinProblemSolver::Function>& f,int nmax);
inline double tryNewPoint(Mat_<double>& p,Mat_<double>& y,Mat_<double>& coord_sum,const Ptr<Solver::Function>& f,int ihi, inline double tryNewPoint(Mat_<double>& p,Mat_<double>& y,Mat_<double>& coord_sum,const Ptr<MinProblemSolver::Function>& f,int ihi,
double fac,Mat_<double>& ptry); double fac,Mat_<double>& ptry);
}; };
@ -166,7 +164,7 @@ namespace cv{namespace optim{
Mat_<double>& p, Mat_<double>& p,
Mat_<double>& y, Mat_<double>& y,
Mat_<double>& coord_sum, Mat_<double>& coord_sum,
const Ptr<Solver::Function>& f, const Ptr<MinProblemSolver::Function>& f,
int ihi, int ihi,
double fac, double fac,
Mat_<double>& ptry Mat_<double>& ptry
@ -197,7 +195,7 @@ namespace cv{namespace optim{
} }
/* /*
Performs the actual minimization of Solver::Function f (after the initialization was done) Performs the actual minimization of MinProblemSolver::Function f (after the initialization was done)
The matrix p[ndim+1][1..ndim] represents ndim+1 vertices that The matrix p[ndim+1][1..ndim] represents ndim+1 vertices that
form a simplex - each row is an ndim vector. form a simplex - each row is an ndim vector.
@ -208,7 +206,7 @@ namespace cv{namespace optim{
double MinRange, double MinRange,
double MinError, double MinError,
int& nfunk, int& nfunk,
const Ptr<Solver::Function>& f, const Ptr<MinProblemSolver::Function>& f,
int nmax int nmax
) )
{ {
@ -373,7 +371,7 @@ namespace cv{namespace optim{
_Function=Ptr<Function>(); _Function=Ptr<Function>();
_step=Mat_<double>(); _step=Mat_<double>();
} }
Ptr<Solver::Function> DownhillSolverImpl::getFunction()const{ Ptr<MinProblemSolver::Function> DownhillSolverImpl::getFunction()const{
return _Function; return _Function;
} }
void DownhillSolverImpl::setFunction(const Ptr<Function>& f){ void DownhillSolverImpl::setFunction(const Ptr<Function>& f){
@ -387,12 +385,12 @@ namespace cv{namespace optim{
_termcrit=termcrit; _termcrit=termcrit;
} }
// both minRange & minError are specified by termcrit.epsilon; In addition, user may specify the number of iterations that the algorithm does. // both minRange & minError are specified by termcrit.epsilon; In addition, user may specify the number of iterations that the algorithm does.
Ptr<DownhillSolver> createDownhillSolver(const Ptr<Solver::Function>& f, InputArray initStep, TermCriteria termcrit){ Ptr<DownhillSolver> DownhillSolver::create(const Ptr<MinProblemSolver::Function>& f, InputArray initStep, TermCriteria termcrit){
DownhillSolver *DS=new DownhillSolverImpl(); Ptr<DownhillSolver> DS = makePtr<DownhillSolverImpl>();
DS->setFunction(f); DS->setFunction(f);
DS->setInitStep(initStep); DS->setInitStep(initStep);
DS->setTermCriteria(termcrit); DS->setTermCriteria(termcrit);
return Ptr<DownhillSolver>(DS); return DS;
} }
void DownhillSolverImpl::getInitStep(OutputArray step)const{ void DownhillSolverImpl::getInitStep(OutputArray step)const{
_step.copyTo(step); _step.copyTo(step);
@ -408,4 +406,4 @@ namespace cv{namespace optim{
transpose(m,_step); transpose(m,_step);
} }
} }
}} }

@ -0,0 +1,531 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
namespace cv
{
// This is reimplementation of kd-trees from cvkdtree*.* by Xavier Delacour, cleaned-up and
// adopted to work with the new OpenCV data structures. It's in cxcore to be shared by
// both cv (CvFeatureTree) and ml (kNN).
// The algorithm is taken from:
// J.S. Beis and D.G. Lowe. Shape indexing using approximate nearest-neighbor search
// in highdimensional spaces. In Proc. IEEE Conf. Comp. Vision Patt. Recog.,
// pages 1000--1006, 1997. http://citeseer.ist.psu.edu/beis97shape.html
const int MAX_TREE_DEPTH = 32;
KDTree::KDTree()
{
maxDepth = -1;
normType = NORM_L2;
}
KDTree::KDTree(InputArray _points, bool _copyData)
{
maxDepth = -1;
normType = NORM_L2;
build(_points, _copyData);
}
KDTree::KDTree(InputArray _points, InputArray _labels, bool _copyData)
{
maxDepth = -1;
normType = NORM_L2;
build(_points, _labels, _copyData);
}
struct SubTree
{
SubTree() : first(0), last(0), nodeIdx(0), depth(0) {}
SubTree(int _first, int _last, int _nodeIdx, int _depth)
: first(_first), last(_last), nodeIdx(_nodeIdx), depth(_depth) {}
int first;
int last;
int nodeIdx;
int depth;
};
static float
medianPartition( size_t* ofs, int a, int b, const float* vals )
{
int k, a0 = a, b0 = b;
int middle = (a + b)/2;
while( b > a )
{
int i0 = a, i1 = (a+b)/2, i2 = b;
float v0 = vals[ofs[i0]], v1 = vals[ofs[i1]], v2 = vals[ofs[i2]];
int ip = v0 < v1 ? (v1 < v2 ? i1 : v0 < v2 ? i2 : i0) :
v0 < v2 ? i0 : (v1 < v2 ? i2 : i1);
float pivot = vals[ofs[ip]];
std::swap(ofs[ip], ofs[i2]);
for( i1 = i0, i0--; i1 <= i2; i1++ )
if( vals[ofs[i1]] <= pivot )
{
i0++;
std::swap(ofs[i0], ofs[i1]);
}
if( i0 == middle )
break;
if( i0 > middle )
b = i0 - (b == i0);
else
a = i0;
}
float pivot = vals[ofs[middle]];
int less = 0, more = 0;
for( k = a0; k < middle; k++ )
{
CV_Assert(vals[ofs[k]] <= pivot);
less += vals[ofs[k]] < pivot;
}
for( k = b0; k > middle; k-- )
{
CV_Assert(vals[ofs[k]] >= pivot);
more += vals[ofs[k]] > pivot;
}
CV_Assert(std::abs(more - less) <= 1);
return vals[ofs[middle]];
}
static void
computeSums( const Mat& points, const size_t* ofs, int a, int b, double* sums )
{
int i, j, dims = points.cols;
const float* data = points.ptr<float>(0);
for( j = 0; j < dims; j++ )
sums[j*2] = sums[j*2+1] = 0;
for( i = a; i <= b; i++ )
{
const float* row = data + ofs[i];
for( j = 0; j < dims; j++ )
{
double t = row[j], s = sums[j*2] + t, s2 = sums[j*2+1] + t*t;
sums[j*2] = s; sums[j*2+1] = s2;
}
}
}
void KDTree::build(InputArray _points, bool _copyData)
{
build(_points, noArray(), _copyData);
}
void KDTree::build(InputArray __points, InputArray __labels, bool _copyData)
{
Mat _points = __points.getMat(), _labels = __labels.getMat();
CV_Assert(_points.type() == CV_32F && !_points.empty());
std::vector<KDTree::Node>().swap(nodes);
if( !_copyData )
points = _points;
else
{
points.release();
points.create(_points.size(), _points.type());
}
int i, j, n = _points.rows, ptdims = _points.cols, top = 0;
const float* data = _points.ptr<float>(0);
float* dstdata = points.ptr<float>(0);
size_t step = _points.step1();
size_t dstep = points.step1();
int ptpos = 0;
labels.resize(n);
const int* _labels_data = 0;
if( !_labels.empty() )
{
int nlabels = _labels.checkVector(1, CV_32S, true);
CV_Assert(nlabels == n);
_labels_data = _labels.ptr<int>();
}
Mat sumstack(MAX_TREE_DEPTH*2, ptdims*2, CV_64F);
SubTree stack[MAX_TREE_DEPTH*2];
std::vector<size_t> _ptofs(n);
size_t* ptofs = &_ptofs[0];
for( i = 0; i < n; i++ )
ptofs[i] = i*step;
nodes.push_back(Node());
computeSums(points, ptofs, 0, n-1, sumstack.ptr<double>(top));
stack[top++] = SubTree(0, n-1, 0, 0);
int _maxDepth = 0;
while( --top >= 0 )
{
int first = stack[top].first, last = stack[top].last;
int depth = stack[top].depth, nidx = stack[top].nodeIdx;
int count = last - first + 1, dim = -1;
const double* sums = sumstack.ptr<double>(top);
double invCount = 1./count, maxVar = -1.;
if( count == 1 )
{
int idx0 = (int)(ptofs[first]/step);
int idx = _copyData ? ptpos++ : idx0;
nodes[nidx].idx = ~idx;
if( _copyData )
{
const float* src = data + ptofs[first];
float* dst = dstdata + idx*dstep;
for( j = 0; j < ptdims; j++ )
dst[j] = src[j];
}
labels[idx] = _labels_data ? _labels_data[idx0] : idx0;
_maxDepth = std::max(_maxDepth, depth);
continue;
}
// find the dimensionality with the biggest variance
for( j = 0; j < ptdims; j++ )
{
double m = sums[j*2]*invCount;
double varj = sums[j*2+1]*invCount - m*m;
if( maxVar < varj )
{
maxVar = varj;
dim = j;
}
}
int left = (int)nodes.size(), right = left + 1;
nodes.push_back(Node());
nodes.push_back(Node());
nodes[nidx].idx = dim;
nodes[nidx].left = left;
nodes[nidx].right = right;
nodes[nidx].boundary = medianPartition(ptofs, first, last, data + dim);
int middle = (first + last)/2;
double *lsums = (double*)sums, *rsums = lsums + ptdims*2;
computeSums(points, ptofs, middle+1, last, rsums);
for( j = 0; j < ptdims*2; j++ )
lsums[j] = sums[j] - rsums[j];
stack[top++] = SubTree(first, middle, left, depth+1);
stack[top++] = SubTree(middle+1, last, right, depth+1);
}
maxDepth = _maxDepth;
}
struct PQueueElem
{
PQueueElem() : dist(0), idx(0) {}
PQueueElem(float _dist, int _idx) : dist(_dist), idx(_idx) {}
float dist;
int idx;
};
int KDTree::findNearest(InputArray _vec, int K, int emax,
OutputArray _neighborsIdx, OutputArray _neighbors,
OutputArray _dist, OutputArray _labels) const
{
Mat vecmat = _vec.getMat();
CV_Assert( vecmat.isContinuous() && vecmat.type() == CV_32F && vecmat.total() == (size_t)points.cols );
const float* vec = vecmat.ptr<float>();
K = std::min(K, points.rows);
int ptdims = points.cols;
CV_Assert(K > 0 && (normType == NORM_L2 || normType == NORM_L1));
AutoBuffer<uchar> _buf((K+1)*(sizeof(float) + sizeof(int)));
int* idx = (int*)(uchar*)_buf;
float* dist = (float*)(idx + K + 1);
int i, j, ncount = 0, e = 0;
int qsize = 0, maxqsize = 1 << 10;
AutoBuffer<uchar> _pqueue(maxqsize*sizeof(PQueueElem));
PQueueElem* pqueue = (PQueueElem*)(uchar*)_pqueue;
emax = std::max(emax, 1);
for( e = 0; e < emax; )
{
float d, alt_d = 0.f;
int nidx;
if( e == 0 )
nidx = 0;
else
{
// take the next node from the priority queue
if( qsize == 0 )
break;
nidx = pqueue[0].idx;
alt_d = pqueue[0].dist;
if( --qsize > 0 )
{
std::swap(pqueue[0], pqueue[qsize]);
d = pqueue[0].dist;
for( i = 0;;)
{
int left = i*2 + 1, right = i*2 + 2;
if( left >= qsize )
break;
if( right < qsize && pqueue[right].dist < pqueue[left].dist )
left = right;
if( pqueue[left].dist >= d )
break;
std::swap(pqueue[i], pqueue[left]);
i = left;
}
}
if( ncount == K && alt_d > dist[ncount-1] )
continue;
}
for(;;)
{
if( nidx < 0 )
break;
const Node& n = nodes[nidx];
if( n.idx < 0 )
{
i = ~n.idx;
const float* row = points.ptr<float>(i);
if( normType == NORM_L2 )
for( j = 0, d = 0.f; j < ptdims; j++ )
{
float t = vec[j] - row[j];
d += t*t;
}
else
for( j = 0, d = 0.f; j < ptdims; j++ )
d += std::abs(vec[j] - row[j]);
dist[ncount] = d;
idx[ncount] = i;
for( i = ncount-1; i >= 0; i-- )
{
if( dist[i] <= d )
break;
std::swap(dist[i], dist[i+1]);
std::swap(idx[i], idx[i+1]);
}
ncount += ncount < K;
e++;
break;
}
int alt;
if( vec[n.idx] <= n.boundary )
{
nidx = n.left;
alt = n.right;
}
else
{
nidx = n.right;
alt = n.left;
}
d = vec[n.idx] - n.boundary;
if( normType == NORM_L2 )
d = d*d + alt_d;
else
d = std::abs(d) + alt_d;
// subtree prunning
if( ncount == K && d > dist[ncount-1] )
continue;
// add alternative subtree to the priority queue
pqueue[qsize] = PQueueElem(d, alt);
for( i = qsize; i > 0; )
{
int parent = (i-1)/2;
if( parent < 0 || pqueue[parent].dist <= d )
break;
std::swap(pqueue[i], pqueue[parent]);
i = parent;
}
qsize += qsize+1 < maxqsize;
}
}
K = std::min(K, ncount);
if( _neighborsIdx.needed() )
{
_neighborsIdx.create(K, 1, CV_32S, -1, true);
Mat nidx = _neighborsIdx.getMat();
Mat(nidx.size(), CV_32S, &idx[0]).copyTo(nidx);
}
if( _dist.needed() )
sqrt(Mat(K, 1, CV_32F, dist), _dist);
if( _neighbors.needed() || _labels.needed() )
getPoints(Mat(K, 1, CV_32S, idx), _neighbors, _labels);
return K;
}
void KDTree::findOrthoRange(InputArray _lowerBound,
InputArray _upperBound,
OutputArray _neighborsIdx,
OutputArray _neighbors,
OutputArray _labels ) const
{
int ptdims = points.cols;
Mat lowerBound = _lowerBound.getMat(), upperBound = _upperBound.getMat();
CV_Assert( lowerBound.size == upperBound.size &&
lowerBound.isContinuous() &&
upperBound.isContinuous() &&
lowerBound.type() == upperBound.type() &&
lowerBound.type() == CV_32F &&
lowerBound.total() == (size_t)ptdims );
const float* L = lowerBound.ptr<float>();
const float* R = upperBound.ptr<float>();
std::vector<int> idx;
AutoBuffer<int> _stack(MAX_TREE_DEPTH*2 + 1);
int* stack = _stack;
int top = 0;
stack[top++] = 0;
while( --top >= 0 )
{
int nidx = stack[top];
if( nidx < 0 )
break;
const Node& n = nodes[nidx];
if( n.idx < 0 )
{
int j, i = ~n.idx;
const float* row = points.ptr<float>(i);
for( j = 0; j < ptdims; j++ )
if( row[j] < L[j] || row[j] >= R[j] )
break;
if( j == ptdims )
idx.push_back(i);
continue;
}
if( L[n.idx] <= n.boundary )
stack[top++] = n.left;
if( R[n.idx] > n.boundary )
stack[top++] = n.right;
}
if( _neighborsIdx.needed() )
{
_neighborsIdx.create((int)idx.size(), 1, CV_32S, -1, true);
Mat nidx = _neighborsIdx.getMat();
Mat(nidx.size(), CV_32S, &idx[0]).copyTo(nidx);
}
getPoints( idx, _neighbors, _labels );
}
void KDTree::getPoints(InputArray _idx, OutputArray _pts, OutputArray _labels) const
{
Mat idxmat = _idx.getMat(), pts, labelsmat;
CV_Assert( idxmat.isContinuous() && idxmat.type() == CV_32S &&
(idxmat.cols == 1 || idxmat.rows == 1) );
const int* idx = idxmat.ptr<int>();
int* dstlabels = 0;
int ptdims = points.cols;
int i, nidx = (int)idxmat.total();
if( nidx == 0 )
{
_pts.release();
_labels.release();
return;
}
if( _pts.needed() )
{
_pts.create( nidx, ptdims, points.type());
pts = _pts.getMat();
}
if(_labels.needed())
{
_labels.create(nidx, 1, CV_32S, -1, true);
labelsmat = _labels.getMat();
CV_Assert( labelsmat.isContinuous() );
dstlabels = labelsmat.ptr<int>();
}
const int* srclabels = !labels.empty() ? &labels[0] : 0;
for( i = 0; i < nidx; i++ )
{
int k = idx[i];
CV_Assert( (unsigned)k < (unsigned)points.rows );
const float* src = points.ptr<float>(k);
if( !pts.empty() )
std::copy(src, src + ptdims, pts.ptr<float>(i));
if( dstlabels )
dstlabels[i] = srclabels ? srclabels[k] : k;
}
}
const float* KDTree::getPoint(int ptidx, int* label) const
{
CV_Assert( (unsigned)ptidx < (unsigned)points.rows);
if(label)
*label = labels[ptidx];
return points.ptr<float>(ptidx);
}
int KDTree::dims() const
{
return !points.empty() ? points.cols : 0;
}
}

@ -0,0 +1,457 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
////////////////////////////////////////// kmeans ////////////////////////////////////////////
namespace cv
{
static void generateRandomCenter(const std::vector<Vec2f>& box, float* center, RNG& rng)
{
size_t j, dims = box.size();
float margin = 1.f/dims;
for( j = 0; j < dims; j++ )
center[j] = ((float)rng*(1.f+margin*2.f)-margin)*(box[j][1] - box[j][0]) + box[j][0];
}
class KMeansPPDistanceComputer : public ParallelLoopBody
{
public:
KMeansPPDistanceComputer( float *_tdist2,
const float *_data,
const float *_dist,
int _dims,
size_t _step,
size_t _stepci )
: tdist2(_tdist2),
data(_data),
dist(_dist),
dims(_dims),
step(_step),
stepci(_stepci) { }
void operator()( const cv::Range& range ) const
{
const int begin = range.start;
const int end = range.end;
for ( int i = begin; i<end; i++ )
{
tdist2[i] = std::min(normL2Sqr_(data + step*i, data + stepci, dims), dist[i]);
}
}
private:
KMeansPPDistanceComputer& operator=(const KMeansPPDistanceComputer&); // to quiet MSVC
float *tdist2;
const float *data;
const float *dist;
const int dims;
const size_t step;
const size_t stepci;
};
/*
k-means center initialization using the following algorithm:
Arthur & Vassilvitskii (2007) k-means++: The Advantages of Careful Seeding
*/
static void generateCentersPP(const Mat& _data, Mat& _out_centers,
int K, RNG& rng, int trials)
{
int i, j, k, dims = _data.cols, N = _data.rows;
const float* data = _data.ptr<float>(0);
size_t step = _data.step/sizeof(data[0]);
std::vector<int> _centers(K);
int* centers = &_centers[0];
std::vector<float> _dist(N*3);
float* dist = &_dist[0], *tdist = dist + N, *tdist2 = tdist + N;
double sum0 = 0;
centers[0] = (unsigned)rng % N;
for( i = 0; i < N; i++ )
{
dist[i] = normL2Sqr_(data + step*i, data + step*centers[0], dims);
sum0 += dist[i];
}
for( k = 1; k < K; k++ )
{
double bestSum = DBL_MAX;
int bestCenter = -1;
for( j = 0; j < trials; j++ )
{
double p = (double)rng*sum0, s = 0;
for( i = 0; i < N-1; i++ )
if( (p -= dist[i]) <= 0 )
break;
int ci = i;
parallel_for_(Range(0, N),
KMeansPPDistanceComputer(tdist2, data, dist, dims, step, step*ci));
for( i = 0; i < N; i++ )
{
s += tdist2[i];
}
if( s < bestSum )
{
bestSum = s;
bestCenter = ci;
std::swap(tdist, tdist2);
}
}
centers[k] = bestCenter;
sum0 = bestSum;
std::swap(dist, tdist);
}
for( k = 0; k < K; k++ )
{
const float* src = data + step*centers[k];
float* dst = _out_centers.ptr<float>(k);
for( j = 0; j < dims; j++ )
dst[j] = src[j];
}
}
class KMeansDistanceComputer : public ParallelLoopBody
{
public:
KMeansDistanceComputer( double *_distances,
int *_labels,
const Mat& _data,
const Mat& _centers )
: distances(_distances),
labels(_labels),
data(_data),
centers(_centers)
{
}
void operator()( const Range& range ) const
{
const int begin = range.start;
const int end = range.end;
const int K = centers.rows;
const int dims = centers.cols;
const float *sample;
for( int i = begin; i<end; ++i)
{
sample = data.ptr<float>(i);
int k_best = 0;
double min_dist = DBL_MAX;
for( int k = 0; k < K; k++ )
{
const float* center = centers.ptr<float>(k);
const double dist = normL2Sqr_(sample, center, dims);
if( min_dist > dist )
{
min_dist = dist;
k_best = k;
}
}
distances[i] = min_dist;
labels[i] = k_best;
}
}
private:
KMeansDistanceComputer& operator=(const KMeansDistanceComputer&); // to quiet MSVC
double *distances;
int *labels;
const Mat& data;
const Mat& centers;
};
}
double cv::kmeans( InputArray _data, int K,
InputOutputArray _bestLabels,
TermCriteria criteria, int attempts,
int flags, OutputArray _centers )
{
const int SPP_TRIALS = 3;
Mat data0 = _data.getMat();
bool isrow = data0.rows == 1 && data0.channels() > 1;
int N = !isrow ? data0.rows : data0.cols;
int dims = (!isrow ? data0.cols : 1)*data0.channels();
int type = data0.depth();
attempts = std::max(attempts, 1);
CV_Assert( data0.dims <= 2 && type == CV_32F && K > 0 );
CV_Assert( N >= K );
Mat data(N, dims, CV_32F, data0.ptr(), isrow ? dims * sizeof(float) : static_cast<size_t>(data0.step));
_bestLabels.create(N, 1, CV_32S, -1, true);
Mat _labels, best_labels = _bestLabels.getMat();
if( flags & CV_KMEANS_USE_INITIAL_LABELS )
{
CV_Assert( (best_labels.cols == 1 || best_labels.rows == 1) &&
best_labels.cols*best_labels.rows == N &&
best_labels.type() == CV_32S &&
best_labels.isContinuous());
best_labels.copyTo(_labels);
}
else
{
if( !((best_labels.cols == 1 || best_labels.rows == 1) &&
best_labels.cols*best_labels.rows == N &&
best_labels.type() == CV_32S &&
best_labels.isContinuous()))
best_labels.create(N, 1, CV_32S);
_labels.create(best_labels.size(), best_labels.type());
}
int* labels = _labels.ptr<int>();
Mat centers(K, dims, type), old_centers(K, dims, type), temp(1, dims, type);
std::vector<int> counters(K);
std::vector<Vec2f> _box(dims);
Vec2f* box = &_box[0];
double best_compactness = DBL_MAX, compactness = 0;
RNG& rng = theRNG();
int a, iter, i, j, k;
if( criteria.type & TermCriteria::EPS )
criteria.epsilon = std::max(criteria.epsilon, 0.);
else
criteria.epsilon = FLT_EPSILON;
criteria.epsilon *= criteria.epsilon;
if( criteria.type & TermCriteria::COUNT )
criteria.maxCount = std::min(std::max(criteria.maxCount, 2), 100);
else
criteria.maxCount = 100;
if( K == 1 )
{
attempts = 1;
criteria.maxCount = 2;
}
const float* sample = data.ptr<float>(0);
for( j = 0; j < dims; j++ )
box[j] = Vec2f(sample[j], sample[j]);
for( i = 1; i < N; i++ )
{
sample = data.ptr<float>(i);
for( j = 0; j < dims; j++ )
{
float v = sample[j];
box[j][0] = std::min(box[j][0], v);
box[j][1] = std::max(box[j][1], v);
}
}
for( a = 0; a < attempts; a++ )
{
double max_center_shift = DBL_MAX;
for( iter = 0;; )
{
swap(centers, old_centers);
if( iter == 0 && (a > 0 || !(flags & KMEANS_USE_INITIAL_LABELS)) )
{
if( flags & KMEANS_PP_CENTERS )
generateCentersPP(data, centers, K, rng, SPP_TRIALS);
else
{
for( k = 0; k < K; k++ )
generateRandomCenter(_box, centers.ptr<float>(k), rng);
}
}
else
{
if( iter == 0 && a == 0 && (flags & KMEANS_USE_INITIAL_LABELS) )
{
for( i = 0; i < N; i++ )
CV_Assert( (unsigned)labels[i] < (unsigned)K );
}
// compute centers
centers = Scalar(0);
for( k = 0; k < K; k++ )
counters[k] = 0;
for( i = 0; i < N; i++ )
{
sample = data.ptr<float>(i);
k = labels[i];
float* center = centers.ptr<float>(k);
j=0;
#if CV_ENABLE_UNROLLED
for(; j <= dims - 4; j += 4 )
{
float t0 = center[j] + sample[j];
float t1 = center[j+1] + sample[j+1];
center[j] = t0;
center[j+1] = t1;
t0 = center[j+2] + sample[j+2];
t1 = center[j+3] + sample[j+3];
center[j+2] = t0;
center[j+3] = t1;
}
#endif
for( ; j < dims; j++ )
center[j] += sample[j];
counters[k]++;
}
if( iter > 0 )
max_center_shift = 0;
for( k = 0; k < K; k++ )
{
if( counters[k] != 0 )
continue;
// if some cluster appeared to be empty then:
// 1. find the biggest cluster
// 2. find the farthest from the center point in the biggest cluster
// 3. exclude the farthest point from the biggest cluster and form a new 1-point cluster.
int max_k = 0;
for( int k1 = 1; k1 < K; k1++ )
{
if( counters[max_k] < counters[k1] )
max_k = k1;
}
double max_dist = 0;
int farthest_i = -1;
float* new_center = centers.ptr<float>(k);
float* old_center = centers.ptr<float>(max_k);
float* _old_center = temp.ptr<float>(); // normalized
float scale = 1.f/counters[max_k];
for( j = 0; j < dims; j++ )
_old_center[j] = old_center[j]*scale;
for( i = 0; i < N; i++ )
{
if( labels[i] != max_k )
continue;
sample = data.ptr<float>(i);
double dist = normL2Sqr_(sample, _old_center, dims);
if( max_dist <= dist )
{
max_dist = dist;
farthest_i = i;
}
}
counters[max_k]--;
counters[k]++;
labels[farthest_i] = k;
sample = data.ptr<float>(farthest_i);
for( j = 0; j < dims; j++ )
{
old_center[j] -= sample[j];
new_center[j] += sample[j];
}
}
for( k = 0; k < K; k++ )
{
float* center = centers.ptr<float>(k);
CV_Assert( counters[k] != 0 );
float scale = 1.f/counters[k];
for( j = 0; j < dims; j++ )
center[j] *= scale;
if( iter > 0 )
{
double dist = 0;
const float* old_center = old_centers.ptr<float>(k);
for( j = 0; j < dims; j++ )
{
double t = center[j] - old_center[j];
dist += t*t;
}
max_center_shift = std::max(max_center_shift, dist);
}
}
}
if( ++iter == MAX(criteria.maxCount, 2) || max_center_shift <= criteria.epsilon )
break;
// assign labels
Mat dists(1, N, CV_64F);
double* dist = dists.ptr<double>(0);
parallel_for_(Range(0, N),
KMeansDistanceComputer(dist, labels, data, centers));
compactness = 0;
for( i = 0; i < N; i++ )
{
compactness += dist[i];
}
}
if( compactness < best_compactness )
{
best_compactness = compactness;
if( _centers.needed() )
centers.copyTo(_centers);
_labels.copyTo(best_labels);
}
}
return best_compactness;
}

@ -42,9 +42,12 @@
#include <climits> #include <climits>
#include <algorithm> #include <algorithm>
#include <cstdarg> #include <cstdarg>
#include <debug.hpp>
namespace cv{namespace optim{ #define dprintf(x)
#define print_matrix(x)
namespace cv{
using std::vector; using std::vector;
#ifdef ALEX_DEBUG #ifdef ALEX_DEBUG
@ -355,4 +358,4 @@ static inline void swap_columns(Mat_<double>& A,int col1,int col2){
A(i,col2)=tmp; A(i,col2)=tmp;
} }
} }
}} }

@ -2959,341 +2959,6 @@ double Mat::dot(InputArray _mat) const
return r; return r;
} }
/****************************************************************************************\
* PCA *
\****************************************************************************************/
PCA::PCA() {}
PCA::PCA(InputArray data, InputArray _mean, int flags, int maxComponents)
{
operator()(data, _mean, flags, maxComponents);
}
PCA::PCA(InputArray data, InputArray _mean, int flags, double retainedVariance)
{
operator()(data, _mean, flags, retainedVariance);
}
PCA& PCA::operator()(InputArray _data, InputArray __mean, int flags, int maxComponents)
{
Mat data = _data.getMat(), _mean = __mean.getMat();
int covar_flags = CV_COVAR_SCALE;
int i, len, in_count;
Size mean_sz;
CV_Assert( data.channels() == 1 );
if( flags & CV_PCA_DATA_AS_COL )
{
len = data.rows;
in_count = data.cols;
covar_flags |= CV_COVAR_COLS;
mean_sz = Size(1, len);
}
else
{
len = data.cols;
in_count = data.rows;
covar_flags |= CV_COVAR_ROWS;
mean_sz = Size(len, 1);
}
int count = std::min(len, in_count), out_count = count;
if( maxComponents > 0 )
out_count = std::min(count, maxComponents);
// "scrambled" way to compute PCA (when cols(A)>rows(A)):
// B = A'A; B*x=b*x; C = AA'; C*y=c*y -> AA'*y=c*y -> A'A*(A'*y)=c*(A'*y) -> c = b, x=A'*y
if( len <= in_count )
covar_flags |= CV_COVAR_NORMAL;
int ctype = std::max(CV_32F, data.depth());
mean.create( mean_sz, ctype );
Mat covar( count, count, ctype );
if( !_mean.empty() )
{
CV_Assert( _mean.size() == mean_sz );
_mean.convertTo(mean, ctype);
covar_flags |= CV_COVAR_USE_AVG;
}
calcCovarMatrix( data, covar, mean, covar_flags, ctype );
eigen( covar, eigenvalues, eigenvectors );
if( !(covar_flags & CV_COVAR_NORMAL) )
{
// CV_PCA_DATA_AS_ROW: cols(A)>rows(A). x=A'*y -> x'=y'*A
// CV_PCA_DATA_AS_COL: rows(A)>cols(A). x=A''*y -> x'=y'*A'
Mat tmp_data, tmp_mean = repeat(mean, data.rows/mean.rows, data.cols/mean.cols);
if( data.type() != ctype || tmp_mean.data == mean.data )
{
data.convertTo( tmp_data, ctype );
subtract( tmp_data, tmp_mean, tmp_data );
}
else
{
subtract( data, tmp_mean, tmp_mean );
tmp_data = tmp_mean;
}
Mat evects1(count, len, ctype);
gemm( eigenvectors, tmp_data, 1, Mat(), 0, evects1,
(flags & CV_PCA_DATA_AS_COL) ? CV_GEMM_B_T : 0);
eigenvectors = evects1;
// normalize eigenvectors
for( i = 0; i < out_count; i++ )
{
Mat vec = eigenvectors.row(i);
normalize(vec, vec);
}
}
if( count > out_count )
{
// use clone() to physically copy the data and thus deallocate the original matrices
eigenvalues = eigenvalues.rowRange(0,out_count).clone();
eigenvectors = eigenvectors.rowRange(0,out_count).clone();
}
return *this;
}
void PCA::write(FileStorage& fs ) const
{
CV_Assert( fs.isOpened() );
fs << "name" << "PCA";
fs << "vectors" << eigenvectors;
fs << "values" << eigenvalues;
fs << "mean" << mean;
}
void PCA::read(const FileNode& fs)
{
CV_Assert( !fs.empty() );
String name = (String)fs["name"];
CV_Assert( name == "PCA" );
cv::read(fs["vectors"], eigenvectors);
cv::read(fs["values"], eigenvalues);
cv::read(fs["mean"], mean);
}
template <typename T>
int computeCumulativeEnergy(const Mat& eigenvalues, double retainedVariance)
{
CV_DbgAssert( eigenvalues.type() == DataType<T>::type );
Mat g(eigenvalues.size(), DataType<T>::type);
for(int ig = 0; ig < g.rows; ig++)
{
g.at<T>(ig, 0) = 0;
for(int im = 0; im <= ig; im++)
{
g.at<T>(ig,0) += eigenvalues.at<T>(im,0);
}
}
int L;
for(L = 0; L < eigenvalues.rows; L++)
{
double energy = g.at<T>(L, 0) / g.at<T>(g.rows - 1, 0);
if(energy > retainedVariance)
break;
}
L = std::max(2, L);
return L;
}
PCA& PCA::operator()(InputArray _data, InputArray __mean, int flags, double retainedVariance)
{
Mat data = _data.getMat(), _mean = __mean.getMat();
int covar_flags = CV_COVAR_SCALE;
int i, len, in_count;
Size mean_sz;
CV_Assert( data.channels() == 1 );
if( flags & CV_PCA_DATA_AS_COL )
{
len = data.rows;
in_count = data.cols;
covar_flags |= CV_COVAR_COLS;
mean_sz = Size(1, len);
}
else
{
len = data.cols;
in_count = data.rows;
covar_flags |= CV_COVAR_ROWS;
mean_sz = Size(len, 1);
}
CV_Assert( retainedVariance > 0 && retainedVariance <= 1 );
int count = std::min(len, in_count);
// "scrambled" way to compute PCA (when cols(A)>rows(A)):
// B = A'A; B*x=b*x; C = AA'; C*y=c*y -> AA'*y=c*y -> A'A*(A'*y)=c*(A'*y) -> c = b, x=A'*y
if( len <= in_count )
covar_flags |= CV_COVAR_NORMAL;
int ctype = std::max(CV_32F, data.depth());
mean.create( mean_sz, ctype );
Mat covar( count, count, ctype );
if( !_mean.empty() )
{
CV_Assert( _mean.size() == mean_sz );
_mean.convertTo(mean, ctype);
}
calcCovarMatrix( data, covar, mean, covar_flags, ctype );
eigen( covar, eigenvalues, eigenvectors );
if( !(covar_flags & CV_COVAR_NORMAL) )
{
// CV_PCA_DATA_AS_ROW: cols(A)>rows(A). x=A'*y -> x'=y'*A
// CV_PCA_DATA_AS_COL: rows(A)>cols(A). x=A''*y -> x'=y'*A'
Mat tmp_data, tmp_mean = repeat(mean, data.rows/mean.rows, data.cols/mean.cols);
if( data.type() != ctype || tmp_mean.data == mean.data )
{
data.convertTo( tmp_data, ctype );
subtract( tmp_data, tmp_mean, tmp_data );
}
else
{
subtract( data, tmp_mean, tmp_mean );
tmp_data = tmp_mean;
}
Mat evects1(count, len, ctype);
gemm( eigenvectors, tmp_data, 1, Mat(), 0, evects1,
(flags & CV_PCA_DATA_AS_COL) ? CV_GEMM_B_T : 0);
eigenvectors = evects1;
// normalize all eigenvectors
for( i = 0; i < eigenvectors.rows; i++ )
{
Mat vec = eigenvectors.row(i);
normalize(vec, vec);
}
}
// compute the cumulative energy content for each eigenvector
int L;
if (ctype == CV_32F)
L = computeCumulativeEnergy<float>(eigenvalues, retainedVariance);
else
L = computeCumulativeEnergy<double>(eigenvalues, retainedVariance);
// use clone() to physically copy the data and thus deallocate the original matrices
eigenvalues = eigenvalues.rowRange(0,L).clone();
eigenvectors = eigenvectors.rowRange(0,L).clone();
return *this;
}
void PCA::project(InputArray _data, OutputArray result) const
{
Mat data = _data.getMat();
CV_Assert( !mean.empty() && !eigenvectors.empty() &&
((mean.rows == 1 && mean.cols == data.cols) || (mean.cols == 1 && mean.rows == data.rows)));
Mat tmp_data, tmp_mean = repeat(mean, data.rows/mean.rows, data.cols/mean.cols);
int ctype = mean.type();
if( data.type() != ctype || tmp_mean.data == mean.data )
{
data.convertTo( tmp_data, ctype );
subtract( tmp_data, tmp_mean, tmp_data );
}
else
{
subtract( data, tmp_mean, tmp_mean );
tmp_data = tmp_mean;
}
if( mean.rows == 1 )
gemm( tmp_data, eigenvectors, 1, Mat(), 0, result, GEMM_2_T );
else
gemm( eigenvectors, tmp_data, 1, Mat(), 0, result, 0 );
}
Mat PCA::project(InputArray data) const
{
Mat result;
project(data, result);
return result;
}
void PCA::backProject(InputArray _data, OutputArray result) const
{
Mat data = _data.getMat();
CV_Assert( !mean.empty() && !eigenvectors.empty() &&
((mean.rows == 1 && eigenvectors.rows == data.cols) ||
(mean.cols == 1 && eigenvectors.rows == data.rows)));
Mat tmp_data, tmp_mean;
data.convertTo(tmp_data, mean.type());
if( mean.rows == 1 )
{
tmp_mean = repeat(mean, data.rows, 1);
gemm( tmp_data, eigenvectors, 1, tmp_mean, 1, result, 0 );
}
else
{
tmp_mean = repeat(mean, 1, data.cols);
gemm( eigenvectors, tmp_data, 1, tmp_mean, 1, result, GEMM_1_T );
}
}
Mat PCA::backProject(InputArray data) const
{
Mat result;
backProject(data, result);
return result;
}
}
void cv::PCACompute(InputArray data, InputOutputArray mean,
OutputArray eigenvectors, int maxComponents)
{
PCA pca;
pca(data, mean, 0, maxComponents);
pca.mean.copyTo(mean);
pca.eigenvectors.copyTo(eigenvectors);
}
void cv::PCACompute(InputArray data, InputOutputArray mean,
OutputArray eigenvectors, double retainedVariance)
{
PCA pca;
pca(data, mean, 0, retainedVariance);
pca.mean.copyTo(mean);
pca.eigenvectors.copyTo(eigenvectors);
}
void cv::PCAProject(InputArray data, InputArray mean,
InputArray eigenvectors, OutputArray result)
{
PCA pca;
pca.mean = mean.getMat();
pca.eigenvectors = eigenvectors.getMat();
pca.project(data, result);
}
void cv::PCABackProject(InputArray data, InputArray mean,
InputArray eigenvectors, OutputArray result)
{
PCA pca;
pca.mean = mean.getMat();
pca.eigenvectors = eigenvectors.getMat();
pca.backProject(data, result);
} }
/****************************************************************************************\ /****************************************************************************************\

@ -3971,420 +3971,6 @@ void cv::sortIdx( InputArray _src, OutputArray _dst, int flags )
} }
////////////////////////////////////////// kmeans ////////////////////////////////////////////
namespace cv
{
static void generateRandomCenter(const std::vector<Vec2f>& box, float* center, RNG& rng)
{
size_t j, dims = box.size();
float margin = 1.f/dims;
for( j = 0; j < dims; j++ )
center[j] = ((float)rng*(1.f+margin*2.f)-margin)*(box[j][1] - box[j][0]) + box[j][0];
}
class KMeansPPDistanceComputer : public ParallelLoopBody
{
public:
KMeansPPDistanceComputer( float *_tdist2,
const float *_data,
const float *_dist,
int _dims,
size_t _step,
size_t _stepci )
: tdist2(_tdist2),
data(_data),
dist(_dist),
dims(_dims),
step(_step),
stepci(_stepci) { }
void operator()( const cv::Range& range ) const
{
const int begin = range.start;
const int end = range.end;
for ( int i = begin; i<end; i++ )
{
tdist2[i] = std::min(normL2Sqr_(data + step*i, data + stepci, dims), dist[i]);
}
}
private:
KMeansPPDistanceComputer& operator=(const KMeansPPDistanceComputer&); // to quiet MSVC
float *tdist2;
const float *data;
const float *dist;
const int dims;
const size_t step;
const size_t stepci;
};
/*
k-means center initialization using the following algorithm:
Arthur & Vassilvitskii (2007) k-means++: The Advantages of Careful Seeding
*/
static void generateCentersPP(const Mat& _data, Mat& _out_centers,
int K, RNG& rng, int trials)
{
int i, j, k, dims = _data.cols, N = _data.rows;
const float* data = _data.ptr<float>(0);
size_t step = _data.step/sizeof(data[0]);
std::vector<int> _centers(K);
int* centers = &_centers[0];
std::vector<float> _dist(N*3);
float* dist = &_dist[0], *tdist = dist + N, *tdist2 = tdist + N;
double sum0 = 0;
centers[0] = (unsigned)rng % N;
for( i = 0; i < N; i++ )
{
dist[i] = normL2Sqr_(data + step*i, data + step*centers[0], dims);
sum0 += dist[i];
}
for( k = 1; k < K; k++ )
{
double bestSum = DBL_MAX;
int bestCenter = -1;
for( j = 0; j < trials; j++ )
{
double p = (double)rng*sum0, s = 0;
for( i = 0; i < N-1; i++ )
if( (p -= dist[i]) <= 0 )
break;
int ci = i;
parallel_for_(Range(0, N),
KMeansPPDistanceComputer(tdist2, data, dist, dims, step, step*ci));
for( i = 0; i < N; i++ )
{
s += tdist2[i];
}
if( s < bestSum )
{
bestSum = s;
bestCenter = ci;
std::swap(tdist, tdist2);
}
}
centers[k] = bestCenter;
sum0 = bestSum;
std::swap(dist, tdist);
}
for( k = 0; k < K; k++ )
{
const float* src = data + step*centers[k];
float* dst = _out_centers.ptr<float>(k);
for( j = 0; j < dims; j++ )
dst[j] = src[j];
}
}
class KMeansDistanceComputer : public ParallelLoopBody
{
public:
KMeansDistanceComputer( double *_distances,
int *_labels,
const Mat& _data,
const Mat& _centers )
: distances(_distances),
labels(_labels),
data(_data),
centers(_centers)
{
}
void operator()( const Range& range ) const
{
const int begin = range.start;
const int end = range.end;
const int K = centers.rows;
const int dims = centers.cols;
const float *sample;
for( int i = begin; i<end; ++i)
{
sample = data.ptr<float>(i);
int k_best = 0;
double min_dist = DBL_MAX;
for( int k = 0; k < K; k++ )
{
const float* center = centers.ptr<float>(k);
const double dist = normL2Sqr_(sample, center, dims);
if( min_dist > dist )
{
min_dist = dist;
k_best = k;
}
}
distances[i] = min_dist;
labels[i] = k_best;
}
}
private:
KMeansDistanceComputer& operator=(const KMeansDistanceComputer&); // to quiet MSVC
double *distances;
int *labels;
const Mat& data;
const Mat& centers;
};
}
double cv::kmeans( InputArray _data, int K,
InputOutputArray _bestLabels,
TermCriteria criteria, int attempts,
int flags, OutputArray _centers )
{
const int SPP_TRIALS = 3;
Mat data0 = _data.getMat();
bool isrow = data0.rows == 1 && data0.channels() > 1;
int N = !isrow ? data0.rows : data0.cols;
int dims = (!isrow ? data0.cols : 1)*data0.channels();
int type = data0.depth();
attempts = std::max(attempts, 1);
CV_Assert( data0.dims <= 2 && type == CV_32F && K > 0 );
CV_Assert( N >= K );
Mat data(N, dims, CV_32F, data0.ptr(), isrow ? dims * sizeof(float) : static_cast<size_t>(data0.step));
_bestLabels.create(N, 1, CV_32S, -1, true);
Mat _labels, best_labels = _bestLabels.getMat();
if( flags & CV_KMEANS_USE_INITIAL_LABELS )
{
CV_Assert( (best_labels.cols == 1 || best_labels.rows == 1) &&
best_labels.cols*best_labels.rows == N &&
best_labels.type() == CV_32S &&
best_labels.isContinuous());
best_labels.copyTo(_labels);
}
else
{
if( !((best_labels.cols == 1 || best_labels.rows == 1) &&
best_labels.cols*best_labels.rows == N &&
best_labels.type() == CV_32S &&
best_labels.isContinuous()))
best_labels.create(N, 1, CV_32S);
_labels.create(best_labels.size(), best_labels.type());
}
int* labels = _labels.ptr<int>();
Mat centers(K, dims, type), old_centers(K, dims, type), temp(1, dims, type);
std::vector<int> counters(K);
std::vector<Vec2f> _box(dims);
Vec2f* box = &_box[0];
double best_compactness = DBL_MAX, compactness = 0;
RNG& rng = theRNG();
int a, iter, i, j, k;
if( criteria.type & TermCriteria::EPS )
criteria.epsilon = std::max(criteria.epsilon, 0.);
else
criteria.epsilon = FLT_EPSILON;
criteria.epsilon *= criteria.epsilon;
if( criteria.type & TermCriteria::COUNT )
criteria.maxCount = std::min(std::max(criteria.maxCount, 2), 100);
else
criteria.maxCount = 100;
if( K == 1 )
{
attempts = 1;
criteria.maxCount = 2;
}
const float* sample = data.ptr<float>(0);
for( j = 0; j < dims; j++ )
box[j] = Vec2f(sample[j], sample[j]);
for( i = 1; i < N; i++ )
{
sample = data.ptr<float>(i);
for( j = 0; j < dims; j++ )
{
float v = sample[j];
box[j][0] = std::min(box[j][0], v);
box[j][1] = std::max(box[j][1], v);
}
}
for( a = 0; a < attempts; a++ )
{
double max_center_shift = DBL_MAX;
for( iter = 0;; )
{
swap(centers, old_centers);
if( iter == 0 && (a > 0 || !(flags & KMEANS_USE_INITIAL_LABELS)) )
{
if( flags & KMEANS_PP_CENTERS )
generateCentersPP(data, centers, K, rng, SPP_TRIALS);
else
{
for( k = 0; k < K; k++ )
generateRandomCenter(_box, centers.ptr<float>(k), rng);
}
}
else
{
if( iter == 0 && a == 0 && (flags & KMEANS_USE_INITIAL_LABELS) )
{
for( i = 0; i < N; i++ )
CV_Assert( (unsigned)labels[i] < (unsigned)K );
}
// compute centers
centers = Scalar(0);
for( k = 0; k < K; k++ )
counters[k] = 0;
for( i = 0; i < N; i++ )
{
sample = data.ptr<float>(i);
k = labels[i];
float* center = centers.ptr<float>(k);
j=0;
#if CV_ENABLE_UNROLLED
for(; j <= dims - 4; j += 4 )
{
float t0 = center[j] + sample[j];
float t1 = center[j+1] + sample[j+1];
center[j] = t0;
center[j+1] = t1;
t0 = center[j+2] + sample[j+2];
t1 = center[j+3] + sample[j+3];
center[j+2] = t0;
center[j+3] = t1;
}
#endif
for( ; j < dims; j++ )
center[j] += sample[j];
counters[k]++;
}
if( iter > 0 )
max_center_shift = 0;
for( k = 0; k < K; k++ )
{
if( counters[k] != 0 )
continue;
// if some cluster appeared to be empty then:
// 1. find the biggest cluster
// 2. find the farthest from the center point in the biggest cluster
// 3. exclude the farthest point from the biggest cluster and form a new 1-point cluster.
int max_k = 0;
for( int k1 = 1; k1 < K; k1++ )
{
if( counters[max_k] < counters[k1] )
max_k = k1;
}
double max_dist = 0;
int farthest_i = -1;
float* new_center = centers.ptr<float>(k);
float* old_center = centers.ptr<float>(max_k);
float* _old_center = temp.ptr<float>(); // normalized
float scale = 1.f/counters[max_k];
for( j = 0; j < dims; j++ )
_old_center[j] = old_center[j]*scale;
for( i = 0; i < N; i++ )
{
if( labels[i] != max_k )
continue;
sample = data.ptr<float>(i);
double dist = normL2Sqr_(sample, _old_center, dims);
if( max_dist <= dist )
{
max_dist = dist;
farthest_i = i;
}
}
counters[max_k]--;
counters[k]++;
labels[farthest_i] = k;
sample = data.ptr<float>(farthest_i);
for( j = 0; j < dims; j++ )
{
old_center[j] -= sample[j];
new_center[j] += sample[j];
}
}
for( k = 0; k < K; k++ )
{
float* center = centers.ptr<float>(k);
CV_Assert( counters[k] != 0 );
float scale = 1.f/counters[k];
for( j = 0; j < dims; j++ )
center[j] *= scale;
if( iter > 0 )
{
double dist = 0;
const float* old_center = old_centers.ptr<float>(k);
for( j = 0; j < dims; j++ )
{
double t = center[j] - old_center[j];
dist += t*t;
}
max_center_shift = std::max(max_center_shift, dist);
}
}
}
if( ++iter == MAX(criteria.maxCount, 2) || max_center_shift <= criteria.epsilon )
break;
// assign labels
Mat dists(1, N, CV_64F);
double* dist = dists.ptr<double>(0);
parallel_for_(Range(0, N),
KMeansDistanceComputer(dist, labels, data, centers));
compactness = 0;
for( i = 0; i < N; i++ )
{
compactness += dist[i];
}
}
if( compactness < best_compactness )
{
best_compactness = compactness;
if( _centers.needed() )
centers.copyTo(_centers);
_labels.copyTo(best_labels);
}
}
return best_compactness;
}
CV_IMPL void cvSetIdentity( CvArr* arr, CvScalar value ) CV_IMPL void cvSetIdentity( CvArr* arr, CvScalar value )
{ {
cv::Mat m = cv::cvarrToMat(arr); cv::Mat m = cv::cvarrToMat(arr);

@ -0,0 +1,384 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
/****************************************************************************************\
* PCA *
\****************************************************************************************/
namespace cv
{
PCA::PCA() {}
PCA::PCA(InputArray data, InputArray _mean, int flags, int maxComponents)
{
operator()(data, _mean, flags, maxComponents);
}
PCA::PCA(InputArray data, InputArray _mean, int flags, double retainedVariance)
{
operator()(data, _mean, flags, retainedVariance);
}
PCA& PCA::operator()(InputArray _data, InputArray __mean, int flags, int maxComponents)
{
Mat data = _data.getMat(), _mean = __mean.getMat();
int covar_flags = CV_COVAR_SCALE;
int i, len, in_count;
Size mean_sz;
CV_Assert( data.channels() == 1 );
if( flags & CV_PCA_DATA_AS_COL )
{
len = data.rows;
in_count = data.cols;
covar_flags |= CV_COVAR_COLS;
mean_sz = Size(1, len);
}
else
{
len = data.cols;
in_count = data.rows;
covar_flags |= CV_COVAR_ROWS;
mean_sz = Size(len, 1);
}
int count = std::min(len, in_count), out_count = count;
if( maxComponents > 0 )
out_count = std::min(count, maxComponents);
// "scrambled" way to compute PCA (when cols(A)>rows(A)):
// B = A'A; B*x=b*x; C = AA'; C*y=c*y -> AA'*y=c*y -> A'A*(A'*y)=c*(A'*y) -> c = b, x=A'*y
if( len <= in_count )
covar_flags |= CV_COVAR_NORMAL;
int ctype = std::max(CV_32F, data.depth());
mean.create( mean_sz, ctype );
Mat covar( count, count, ctype );
if( !_mean.empty() )
{
CV_Assert( _mean.size() == mean_sz );
_mean.convertTo(mean, ctype);
covar_flags |= CV_COVAR_USE_AVG;
}
calcCovarMatrix( data, covar, mean, covar_flags, ctype );
eigen( covar, eigenvalues, eigenvectors );
if( !(covar_flags & CV_COVAR_NORMAL) )
{
// CV_PCA_DATA_AS_ROW: cols(A)>rows(A). x=A'*y -> x'=y'*A
// CV_PCA_DATA_AS_COL: rows(A)>cols(A). x=A''*y -> x'=y'*A'
Mat tmp_data, tmp_mean = repeat(mean, data.rows/mean.rows, data.cols/mean.cols);
if( data.type() != ctype || tmp_mean.data == mean.data )
{
data.convertTo( tmp_data, ctype );
subtract( tmp_data, tmp_mean, tmp_data );
}
else
{
subtract( data, tmp_mean, tmp_mean );
tmp_data = tmp_mean;
}
Mat evects1(count, len, ctype);
gemm( eigenvectors, tmp_data, 1, Mat(), 0, evects1,
(flags & CV_PCA_DATA_AS_COL) ? CV_GEMM_B_T : 0);
eigenvectors = evects1;
// normalize eigenvectors
for( i = 0; i < out_count; i++ )
{
Mat vec = eigenvectors.row(i);
normalize(vec, vec);
}
}
if( count > out_count )
{
// use clone() to physically copy the data and thus deallocate the original matrices
eigenvalues = eigenvalues.rowRange(0,out_count).clone();
eigenvectors = eigenvectors.rowRange(0,out_count).clone();
}
return *this;
}
void PCA::write(FileStorage& fs ) const
{
CV_Assert( fs.isOpened() );
fs << "name" << "PCA";
fs << "vectors" << eigenvectors;
fs << "values" << eigenvalues;
fs << "mean" << mean;
}
void PCA::read(const FileNode& fs)
{
CV_Assert( !fs.empty() );
String name = (String)fs["name"];
CV_Assert( name == "PCA" );
cv::read(fs["vectors"], eigenvectors);
cv::read(fs["values"], eigenvalues);
cv::read(fs["mean"], mean);
}
template <typename T>
int computeCumulativeEnergy(const Mat& eigenvalues, double retainedVariance)
{
CV_DbgAssert( eigenvalues.type() == DataType<T>::type );
Mat g(eigenvalues.size(), DataType<T>::type);
for(int ig = 0; ig < g.rows; ig++)
{
g.at<T>(ig, 0) = 0;
for(int im = 0; im <= ig; im++)
{
g.at<T>(ig,0) += eigenvalues.at<T>(im,0);
}
}
int L;
for(L = 0; L < eigenvalues.rows; L++)
{
double energy = g.at<T>(L, 0) / g.at<T>(g.rows - 1, 0);
if(energy > retainedVariance)
break;
}
L = std::max(2, L);
return L;
}
PCA& PCA::operator()(InputArray _data, InputArray __mean, int flags, double retainedVariance)
{
Mat data = _data.getMat(), _mean = __mean.getMat();
int covar_flags = CV_COVAR_SCALE;
int i, len, in_count;
Size mean_sz;
CV_Assert( data.channels() == 1 );
if( flags & CV_PCA_DATA_AS_COL )
{
len = data.rows;
in_count = data.cols;
covar_flags |= CV_COVAR_COLS;
mean_sz = Size(1, len);
}
else
{
len = data.cols;
in_count = data.rows;
covar_flags |= CV_COVAR_ROWS;
mean_sz = Size(len, 1);
}
CV_Assert( retainedVariance > 0 && retainedVariance <= 1 );
int count = std::min(len, in_count);
// "scrambled" way to compute PCA (when cols(A)>rows(A)):
// B = A'A; B*x=b*x; C = AA'; C*y=c*y -> AA'*y=c*y -> A'A*(A'*y)=c*(A'*y) -> c = b, x=A'*y
if( len <= in_count )
covar_flags |= CV_COVAR_NORMAL;
int ctype = std::max(CV_32F, data.depth());
mean.create( mean_sz, ctype );
Mat covar( count, count, ctype );
if( !_mean.empty() )
{
CV_Assert( _mean.size() == mean_sz );
_mean.convertTo(mean, ctype);
}
calcCovarMatrix( data, covar, mean, covar_flags, ctype );
eigen( covar, eigenvalues, eigenvectors );
if( !(covar_flags & CV_COVAR_NORMAL) )
{
// CV_PCA_DATA_AS_ROW: cols(A)>rows(A). x=A'*y -> x'=y'*A
// CV_PCA_DATA_AS_COL: rows(A)>cols(A). x=A''*y -> x'=y'*A'
Mat tmp_data, tmp_mean = repeat(mean, data.rows/mean.rows, data.cols/mean.cols);
if( data.type() != ctype || tmp_mean.data == mean.data )
{
data.convertTo( tmp_data, ctype );
subtract( tmp_data, tmp_mean, tmp_data );
}
else
{
subtract( data, tmp_mean, tmp_mean );
tmp_data = tmp_mean;
}
Mat evects1(count, len, ctype);
gemm( eigenvectors, tmp_data, 1, Mat(), 0, evects1,
(flags & CV_PCA_DATA_AS_COL) ? CV_GEMM_B_T : 0);
eigenvectors = evects1;
// normalize all eigenvectors
for( i = 0; i < eigenvectors.rows; i++ )
{
Mat vec = eigenvectors.row(i);
normalize(vec, vec);
}
}
// compute the cumulative energy content for each eigenvector
int L;
if (ctype == CV_32F)
L = computeCumulativeEnergy<float>(eigenvalues, retainedVariance);
else
L = computeCumulativeEnergy<double>(eigenvalues, retainedVariance);
// use clone() to physically copy the data and thus deallocate the original matrices
eigenvalues = eigenvalues.rowRange(0,L).clone();
eigenvectors = eigenvectors.rowRange(0,L).clone();
return *this;
}
void PCA::project(InputArray _data, OutputArray result) const
{
Mat data = _data.getMat();
CV_Assert( !mean.empty() && !eigenvectors.empty() &&
((mean.rows == 1 && mean.cols == data.cols) || (mean.cols == 1 && mean.rows == data.rows)));
Mat tmp_data, tmp_mean = repeat(mean, data.rows/mean.rows, data.cols/mean.cols);
int ctype = mean.type();
if( data.type() != ctype || tmp_mean.data == mean.data )
{
data.convertTo( tmp_data, ctype );
subtract( tmp_data, tmp_mean, tmp_data );
}
else
{
subtract( data, tmp_mean, tmp_mean );
tmp_data = tmp_mean;
}
if( mean.rows == 1 )
gemm( tmp_data, eigenvectors, 1, Mat(), 0, result, GEMM_2_T );
else
gemm( eigenvectors, tmp_data, 1, Mat(), 0, result, 0 );
}
Mat PCA::project(InputArray data) const
{
Mat result;
project(data, result);
return result;
}
void PCA::backProject(InputArray _data, OutputArray result) const
{
Mat data = _data.getMat();
CV_Assert( !mean.empty() && !eigenvectors.empty() &&
((mean.rows == 1 && eigenvectors.rows == data.cols) ||
(mean.cols == 1 && eigenvectors.rows == data.rows)));
Mat tmp_data, tmp_mean;
data.convertTo(tmp_data, mean.type());
if( mean.rows == 1 )
{
tmp_mean = repeat(mean, data.rows, 1);
gemm( tmp_data, eigenvectors, 1, tmp_mean, 1, result, 0 );
}
else
{
tmp_mean = repeat(mean, 1, data.cols);
gemm( eigenvectors, tmp_data, 1, tmp_mean, 1, result, GEMM_1_T );
}
}
Mat PCA::backProject(InputArray data) const
{
Mat result;
backProject(data, result);
return result;
}
}
void cv::PCACompute(InputArray data, InputOutputArray mean,
OutputArray eigenvectors, int maxComponents)
{
PCA pca;
pca(data, mean, 0, maxComponents);
pca.mean.copyTo(mean);
pca.eigenvectors.copyTo(eigenvectors);
}
void cv::PCACompute(InputArray data, InputOutputArray mean,
OutputArray eigenvectors, double retainedVariance)
{
PCA pca;
pca(data, mean, 0, retainedVariance);
pca.mean.copyTo(mean);
pca.eigenvectors.copyTo(eigenvectors);
}
void cv::PCAProject(InputArray data, InputArray mean,
InputArray eigenvectors, OutputArray result)
{
PCA pca;
pca.mean = mean.getMat();
pca.eigenvectors = eigenvectors.getMat();
pca.project(data, result);
}
void cv::PCABackProject(InputArray data, InputArray mean,
InputArray eigenvectors, OutputArray result)
{
PCA pca;
pca.mean = mean.getMat();
pca.eigenvectors = eigenvectors.getMat();
pca.backProject(data, result);
}

@ -41,7 +41,7 @@
#include "test_precomp.hpp" #include "test_precomp.hpp"
#include <cstdlib> #include <cstdlib>
static void mytest(cv::Ptr<cv::optim::ConjGradSolver> solver,cv::Ptr<cv::optim::Solver::Function> ptr_F,cv::Mat& x, static void mytest(cv::Ptr<cv::ConjGradSolver> solver,cv::Ptr<cv::MinProblemSolver::Function> ptr_F,cv::Mat& x,
cv::Mat& etalon_x,double etalon_res){ cv::Mat& etalon_x,double etalon_res){
solver->setFunction(ptr_F); solver->setFunction(ptr_F);
//int ndim=MAX(step.cols,step.rows); //int ndim=MAX(step.cols,step.rows);
@ -58,7 +58,7 @@ static void mytest(cv::Ptr<cv::optim::ConjGradSolver> solver,cv::Ptr<cv::optim::
std::cout<<"--------------------------\n"; std::cout<<"--------------------------\n";
} }
class SphereF:public cv::optim::Solver::Function{ class SphereF:public cv::MinProblemSolver::Function{
public: public:
double calc(const double* x)const{ double calc(const double* x)const{
return x[0]*x[0]+x[1]*x[1]+x[2]*x[2]+x[3]*x[3]; return x[0]*x[0]+x[1]*x[1]+x[2]*x[2]+x[3]*x[3];
@ -69,7 +69,7 @@ public:
} }
} }
}; };
class RosenbrockF:public cv::optim::Solver::Function{ class RosenbrockF:public cv::MinProblemSolver::Function{
double calc(const double* x)const{ double calc(const double* x)const{
return 100*(x[1]-x[0]*x[0])*(x[1]-x[0]*x[0])+(1-x[0])*(1-x[0]); return 100*(x[1]-x[0]*x[0])*(x[1]-x[0]*x[0])+(1-x[0])*(1-x[0]);
} }
@ -80,10 +80,10 @@ class RosenbrockF:public cv::optim::Solver::Function{
}; };
TEST(Optim_ConjGrad, regression_basic){ TEST(Optim_ConjGrad, regression_basic){
cv::Ptr<cv::optim::ConjGradSolver> solver=cv::optim::createConjGradSolver(); cv::Ptr<cv::ConjGradSolver> solver=cv::ConjGradSolver::create();
#if 1 #if 1
{ {
cv::Ptr<cv::optim::Solver::Function> ptr_F(new SphereF()); cv::Ptr<cv::MinProblemSolver::Function> ptr_F(new SphereF());
cv::Mat x=(cv::Mat_<double>(4,1)<<50.0,10.0,1.0,-10.0), cv::Mat x=(cv::Mat_<double>(4,1)<<50.0,10.0,1.0,-10.0),
etalon_x=(cv::Mat_<double>(1,4)<<0.0,0.0,0.0,0.0); etalon_x=(cv::Mat_<double>(1,4)<<0.0,0.0,0.0,0.0);
double etalon_res=0.0; double etalon_res=0.0;
@ -92,7 +92,7 @@ TEST(Optim_ConjGrad, regression_basic){
#endif #endif
#if 1 #if 1
{ {
cv::Ptr<cv::optim::Solver::Function> ptr_F(new RosenbrockF()); cv::Ptr<cv::MinProblemSolver::Function> ptr_F(new RosenbrockF());
cv::Mat x=(cv::Mat_<double>(2,1)<<0.0,0.0), cv::Mat x=(cv::Mat_<double>(2,1)<<0.0,0.0),
etalon_x=(cv::Mat_<double>(2,1)<<1.0,1.0); etalon_x=(cv::Mat_<double>(2,1)<<1.0,1.0);
double etalon_res=0.0; double etalon_res=0.0;

@ -43,7 +43,7 @@
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
static void mytest(cv::Ptr<cv::optim::DownhillSolver> solver,cv::Ptr<cv::optim::Solver::Function> ptr_F,cv::Mat& x,cv::Mat& step, static void mytest(cv::Ptr<cv::DownhillSolver> solver,cv::Ptr<cv::MinProblemSolver::Function> ptr_F,cv::Mat& x,cv::Mat& step,
cv::Mat& etalon_x,double etalon_res){ cv::Mat& etalon_x,double etalon_res){
solver->setFunction(ptr_F); solver->setFunction(ptr_F);
int ndim=MAX(step.cols,step.rows); int ndim=MAX(step.cols,step.rows);
@ -66,23 +66,23 @@ static void mytest(cv::Ptr<cv::optim::DownhillSolver> solver,cv::Ptr<cv::optim::
std::cout<<"--------------------------\n"; std::cout<<"--------------------------\n";
} }
class SphereF:public cv::optim::Solver::Function{ class SphereF:public cv::MinProblemSolver::Function{
public: public:
double calc(const double* x)const{ double calc(const double* x)const{
return x[0]*x[0]+x[1]*x[1]; return x[0]*x[0]+x[1]*x[1];
} }
}; };
class RosenbrockF:public cv::optim::Solver::Function{ class RosenbrockF:public cv::MinProblemSolver::Function{
double calc(const double* x)const{ double calc(const double* x)const{
return 100*(x[1]-x[0]*x[0])*(x[1]-x[0]*x[0])+(1-x[0])*(1-x[0]); return 100*(x[1]-x[0]*x[0])*(x[1]-x[0]*x[0])+(1-x[0])*(1-x[0]);
} }
}; };
TEST(Optim_Downhill, regression_basic){ TEST(Optim_Downhill, regression_basic){
cv::Ptr<cv::optim::DownhillSolver> solver=cv::optim::createDownhillSolver(); cv::Ptr<cv::DownhillSolver> solver=cv::DownhillSolver::create();
#if 1 #if 1
{ {
cv::Ptr<cv::optim::Solver::Function> ptr_F(new SphereF()); cv::Ptr<cv::MinProblemSolver::Function> ptr_F = cv::makePtr<SphereF>();
cv::Mat x=(cv::Mat_<double>(1,2)<<1.0,1.0), cv::Mat x=(cv::Mat_<double>(1,2)<<1.0,1.0),
step=(cv::Mat_<double>(2,1)<<-0.5,-0.5), step=(cv::Mat_<double>(2,1)<<-0.5,-0.5),
etalon_x=(cv::Mat_<double>(1,2)<<-0.0,0.0); etalon_x=(cv::Mat_<double>(1,2)<<-0.0,0.0);
@ -92,7 +92,7 @@ TEST(Optim_Downhill, regression_basic){
#endif #endif
#if 1 #if 1
{ {
cv::Ptr<cv::optim::Solver::Function> ptr_F(new RosenbrockF()); cv::Ptr<cv::MinProblemSolver::Function> ptr_F = cv::makePtr<RosenbrockF>();
cv::Mat x=(cv::Mat_<double>(2,1)<<0.0,0.0), cv::Mat x=(cv::Mat_<double>(2,1)<<0.0,0.0),
step=(cv::Mat_<double>(2,1)<<0.5,+0.5), step=(cv::Mat_<double>(2,1)<<0.5,+0.5),
etalon_x=(cv::Mat_<double>(2,1)<<1.0,1.0); etalon_x=(cv::Mat_<double>(2,1)<<1.0,1.0);

@ -49,7 +49,7 @@ TEST(Optim_LpSolver, regression_basic){
A=(cv::Mat_<double>(3,1)<<3,1,2); A=(cv::Mat_<double>(3,1)<<3,1,2);
B=(cv::Mat_<double>(3,4)<<1,1,3,30,2,2,5,24,4,1,2,36); B=(cv::Mat_<double>(3,4)<<1,1,3,30,2,2,5,24,4,1,2,36);
std::cout<<"here A goes\n"<<A<<"\n"; std::cout<<"here A goes\n"<<A<<"\n";
cv::optim::solveLP(A,B,z); cv::solveLP(A,B,z);
std::cout<<"here z goes\n"<<z<<"\n"; std::cout<<"here z goes\n"<<z<<"\n";
etalon_z=(cv::Mat_<double>(3,1)<<8,4,0); etalon_z=(cv::Mat_<double>(3,1)<<8,4,0);
ASSERT_EQ(cv::countNonZero(z!=etalon_z),0); ASSERT_EQ(cv::countNonZero(z!=etalon_z),0);
@ -60,7 +60,7 @@ TEST(Optim_LpSolver, regression_basic){
A=(cv::Mat_<double>(1,2)<<18,12.5); A=(cv::Mat_<double>(1,2)<<18,12.5);
B=(cv::Mat_<double>(3,3)<<1,1,20,1,0,20,0,1,16); B=(cv::Mat_<double>(3,3)<<1,1,20,1,0,20,0,1,16);
std::cout<<"here A goes\n"<<A<<"\n"; std::cout<<"here A goes\n"<<A<<"\n";
cv::optim::solveLP(A,B,z); cv::solveLP(A,B,z);
std::cout<<"here z goes\n"<<z<<"\n"; std::cout<<"here z goes\n"<<z<<"\n";
etalon_z=(cv::Mat_<double>(2,1)<<20,0); etalon_z=(cv::Mat_<double>(2,1)<<20,0);
ASSERT_EQ(cv::countNonZero(z!=etalon_z),0); ASSERT_EQ(cv::countNonZero(z!=etalon_z),0);
@ -71,7 +71,7 @@ TEST(Optim_LpSolver, regression_basic){
A=(cv::Mat_<double>(1,2)<<5,-3); A=(cv::Mat_<double>(1,2)<<5,-3);
B=(cv::Mat_<double>(2,3)<<1,-1,1,2,1,2); B=(cv::Mat_<double>(2,3)<<1,-1,1,2,1,2);
std::cout<<"here A goes\n"<<A<<"\n"; std::cout<<"here A goes\n"<<A<<"\n";
cv::optim::solveLP(A,B,z); cv::solveLP(A,B,z);
std::cout<<"here z goes\n"<<z<<"\n"; std::cout<<"here z goes\n"<<z<<"\n";
etalon_z=(cv::Mat_<double>(2,1)<<1,0); etalon_z=(cv::Mat_<double>(2,1)<<1,0);
ASSERT_EQ(cv::countNonZero(z!=etalon_z),0); ASSERT_EQ(cv::countNonZero(z!=etalon_z),0);
@ -86,7 +86,7 @@ TEST(Optim_LpSolver, regression_init_unfeasible){
A=(cv::Mat_<double>(1,3)<<-1,-1,-1); A=(cv::Mat_<double>(1,3)<<-1,-1,-1);
B=(cv::Mat_<double>(2,4)<<-2,-7.5,-3,-10000,-20,-5,-10,-30000); B=(cv::Mat_<double>(2,4)<<-2,-7.5,-3,-10000,-20,-5,-10,-30000);
std::cout<<"here A goes\n"<<A<<"\n"; std::cout<<"here A goes\n"<<A<<"\n";
cv::optim::solveLP(A,B,z); cv::solveLP(A,B,z);
std::cout<<"here z goes\n"<<z<<"\n"; std::cout<<"here z goes\n"<<z<<"\n";
etalon_z=(cv::Mat_<double>(3,1)<<1250,1000,0); etalon_z=(cv::Mat_<double>(3,1)<<1250,1000,0);
ASSERT_EQ(cv::countNonZero(z!=etalon_z),0); ASSERT_EQ(cv::countNonZero(z!=etalon_z),0);
@ -101,7 +101,7 @@ TEST(Optim_LpSolver, regression_absolutely_unfeasible){
A=(cv::Mat_<double>(1,1)<<1); A=(cv::Mat_<double>(1,1)<<1);
B=(cv::Mat_<double>(2,2)<<1,-1); B=(cv::Mat_<double>(2,2)<<1,-1);
std::cout<<"here A goes\n"<<A<<"\n"; std::cout<<"here A goes\n"<<A<<"\n";
int res=cv::optim::solveLP(A,B,z); int res=cv::solveLP(A,B,z);
ASSERT_EQ(res,-1); ASSERT_EQ(res,-1);
#endif #endif
} }
@ -114,7 +114,7 @@ TEST(Optim_LpSolver, regression_multiple_solutions){
A=(cv::Mat_<double>(2,1)<<1,1); A=(cv::Mat_<double>(2,1)<<1,1);
B=(cv::Mat_<double>(1,3)<<1,1,1); B=(cv::Mat_<double>(1,3)<<1,1,1);
std::cout<<"here A goes\n"<<A<<"\n"; std::cout<<"here A goes\n"<<A<<"\n";
int res=cv::optim::solveLP(A,B,z); int res=cv::solveLP(A,B,z);
printf("res=%d\n",res); printf("res=%d\n",res);
printf("scalar %g\n",z.dot(A)); printf("scalar %g\n",z.dot(A));
std::cout<<"here z goes\n"<<z<<"\n"; std::cout<<"here z goes\n"<<z<<"\n";
@ -131,7 +131,7 @@ TEST(Optim_LpSolver, regression_cycling){
A=(cv::Mat_<double>(4,1)<<10,-57,-9,-24); A=(cv::Mat_<double>(4,1)<<10,-57,-9,-24);
B=(cv::Mat_<double>(3,5)<<0.5,-5.5,-2.5,9,0,0.5,-1.5,-0.5,1,0,1,0,0,0,1); B=(cv::Mat_<double>(3,5)<<0.5,-5.5,-2.5,9,0,0.5,-1.5,-0.5,1,0,1,0,0,0,1);
std::cout<<"here A goes\n"<<A<<"\n"; std::cout<<"here A goes\n"<<A<<"\n";
int res=cv::optim::solveLP(A,B,z); int res=cv::solveLP(A,B,z);
printf("res=%d\n",res); printf("res=%d\n",res);
printf("scalar %g\n",z.dot(A)); printf("scalar %g\n",z.dot(A));
std::cout<<"here z goes\n"<<z<<"\n"; std::cout<<"here z goes\n"<<z<<"\n";

@ -3,28 +3,6 @@
using namespace cv; using namespace cv;
using namespace std; using namespace std;
TEST(Core_Drawing, _914)
{
const int rows = 256;
const int cols = 256;
Mat img(rows, cols, CV_8UC1, Scalar(255));
line(img, Point(0, 10), Point(255, 10), Scalar(0), 2, 4);
line(img, Point(-5, 20), Point(260, 20), Scalar(0), 2, 4);
line(img, Point(10, 0), Point(10, 255), Scalar(0), 2, 4);
double x0 = 0.0/pow(2.0, -2.0);
double x1 = 255.0/pow(2.0, -2.0);
double y = 30.5/pow(2.0, -2.0);
line(img, Point(int(x0), int(y)), Point(int(x1), int(y)), Scalar(0), 2, 4, 2);
int pixelsDrawn = rows*cols - countNonZero(img);
ASSERT_EQ( (3*rows + cols)*3 - 3*9, pixelsDrawn);
}
TEST(Core_OutputArrayCreate, _1997) TEST(Core_OutputArrayCreate, _1997)
{ {
struct local { struct local {

@ -10,7 +10,7 @@ Functions and classes described in this section are used to perform various line
Another common feature of the functions and classes described in this section is that, unlike simple arithmetic functions, they need to extrapolate values of some non-existing pixels. For example, if you want to smooth an image using a Gaussian Another common feature of the functions and classes described in this section is that, unlike simple arithmetic functions, they need to extrapolate values of some non-existing pixels. For example, if you want to smooth an image using a Gaussian
:math:`3 \times 3` filter, then, when processing the left-most pixels in each row, you need pixels to the left of them, that is, outside of the image. You can let these pixels be the same as the left-most image pixels ("replicated border" extrapolation method), or assume that all the non-existing pixels are zeros ("constant border" extrapolation method), and so on. :math:`3 \times 3` filter, then, when processing the left-most pixels in each row, you need pixels to the left of them, that is, outside of the image. You can let these pixels be the same as the left-most image pixels ("replicated border" extrapolation method), or assume that all the non-existing pixels are zeros ("constant border" extrapolation method), and so on.
OpenCV enables you to specify the extrapolation method. For details, see the function :ocv:func:`borderInterpolate` and discussion of the ``borderType`` parameter in the section and various functions below. :: OpenCV enables you to specify the extrapolation method. For details, see the function ``borderInterpolate`` and discussion of the ``borderType`` parameter in the section and various functions below. ::
/* /*
Various border types, image boundaries are denoted with '|' Various border types, image boundaries are denoted with '|'
@ -26,363 +26,6 @@ OpenCV enables you to specify the extrapolation method. For details, see the fun
* (Python) A complete example illustrating different morphological operations like erode/dilate, open/close, blackhat/tophat ... can be found at opencv_source_code/samples/python2/morphology.py * (Python) A complete example illustrating different morphological operations like erode/dilate, open/close, blackhat/tophat ... can be found at opencv_source_code/samples/python2/morphology.py
BaseColumnFilter
----------------
.. ocv:class:: BaseColumnFilter
Base class for filters with single-column kernels. ::
class BaseColumnFilter
{
public:
virtual ~BaseColumnFilter();
// To be overriden by the user.
//
// runs a filtering operation on the set of rows,
// "dstcount + ksize - 1" rows on input,
// "dstcount" rows on output,
// each input and output row has "width" elements
// the filtered rows are written into "dst" buffer.
virtual void operator()(const uchar** src, uchar* dst, int dststep,
int dstcount, int width) = 0;
// resets the filter state (may be needed for IIR filters)
virtual void reset();
int ksize; // the aperture size
int anchor; // position of the anchor point,
// normally not used during the processing
};
The class ``BaseColumnFilter`` is a base class for filtering data using single-column kernels. Filtering does not have to be a linear operation. In general, it could be written as follows:
.. math::
\texttt{dst} (x,y) = F( \texttt{src} [y](x), \; \texttt{src} [y+1](x), \; ..., \; \texttt{src} [y+ \texttt{ksize} -1](x)
where
:math:`F` is a filtering function but, as it is represented as a class, it can produce any side effects, memorize previously processed data, and so on. The class only defines an interface and is not used directly. Instead, there are several functions in OpenCV (and you can add more) that return pointers to the derived classes that implement specific filtering operations. Those pointers are then passed to the
:ocv:class:`FilterEngine` constructor. While the filtering operation interface uses the ``uchar`` type, a particular implementation is not limited to 8-bit data.
.. seealso::
:ocv:class:`BaseRowFilter`,
:ocv:class:`BaseFilter`,
:ocv:class:`FilterEngine`,
:ocv:func:`getColumnSumFilter`,
:ocv:func:`getLinearColumnFilter`,
:ocv:func:`getMorphologyColumnFilter`
BaseFilter
----------
.. ocv:class:: BaseFilter
Base class for 2D image filters. ::
class BaseFilter
{
public:
virtual ~BaseFilter();
// To be overriden by the user.
//
// runs a filtering operation on the set of rows,
// "dstcount + ksize.height - 1" rows on input,
// "dstcount" rows on output,
// each input row has "(width + ksize.width-1)*cn" elements
// each output row has "width*cn" elements.
// the filtered rows are written into "dst" buffer.
virtual void operator()(const uchar** src, uchar* dst, int dststep,
int dstcount, int width, int cn) = 0;
// resets the filter state (may be needed for IIR filters)
virtual void reset();
Size ksize;
Point anchor;
};
The class ``BaseFilter`` is a base class for filtering data using 2D kernels. Filtering does not have to be a linear operation. In general, it could be written as follows:
.. math::
\begin{array}{l} \texttt{dst} (x,y) = F( \texttt{src} [y](x), \; \texttt{src} [y](x+1), \; ..., \; \texttt{src} [y](x+ \texttt{ksize.width} -1), \\ \texttt{src} [y+1](x), \; \texttt{src} [y+1](x+1), \; ..., \; \texttt{src} [y+1](x+ \texttt{ksize.width} -1), \\ ......................................................................................... \\ \texttt{src} [y+ \texttt{ksize.height-1} ](x), \\ \texttt{src} [y+ \texttt{ksize.height-1} ](x+1), \\ ...
\texttt{src} [y+ \texttt{ksize.height-1} ](x+ \texttt{ksize.width} -1))
\end{array}
where
:math:`F` is a filtering function. The class only defines an interface and is not used directly. Instead, there are several functions in OpenCV (and you can add more) that return pointers to the derived classes that implement specific filtering operations. Those pointers are then passed to the
:ocv:class:`FilterEngine` constructor. While the filtering operation interface uses the ``uchar`` type, a particular implementation is not limited to 8-bit data.
.. seealso::
:ocv:class:`BaseColumnFilter`,
:ocv:class:`BaseRowFilter`,
:ocv:class:`FilterEngine`,
:ocv:func:`getLinearFilter`,
:ocv:func:`getMorphologyFilter`
BaseRowFilter
-------------
.. ocv:class:: BaseRowFilter
Base class for filters with single-row kernels. ::
class BaseRowFilter
{
public:
virtual ~BaseRowFilter();
// To be overriden by the user.
//
// runs filtering operation on the single input row
// of "width" element, each element is has "cn" channels.
// the filtered row is written into "dst" buffer.
virtual void operator()(const uchar* src, uchar* dst,
int width, int cn) = 0;
int ksize, anchor;
};
The class ``BaseRowFilter`` is a base class for filtering data using single-row kernels. Filtering does not have to be a linear operation. In general, it could be written as follows:
.. math::
\texttt{dst} (x,y) = F( \texttt{src} [y](x), \; \texttt{src} [y](x+1), \; ..., \; \texttt{src} [y](x+ \texttt{ksize.width} -1))
where
:math:`F` is a filtering function. The class only defines an interface and is not used directly. Instead, there are several functions in OpenCV (and you can add more) that return pointers to the derived classes that implement specific filtering operations. Those pointers are then passed to the
:ocv:class:`FilterEngine` constructor. While the filtering operation interface uses the ``uchar`` type, a particular implementation is not limited to 8-bit data.
.. seealso::
:ocv:class:`BaseColumnFilter`,
:ocv:class:`BaseFilter`,
:ocv:class:`FilterEngine`,
:ocv:func:`getLinearRowFilter`,
:ocv:func:`getMorphologyRowFilter`,
:ocv:func:`getRowSumFilter`
FilterEngine
------------
.. ocv:class:: FilterEngine
Generic image filtering class. ::
class FilterEngine
{
public:
// empty constructor
FilterEngine();
// builds a 2D non-separable filter (!_filter2D.empty()) or
// a separable filter (!_rowFilter.empty() && !_columnFilter.empty())
// the input data type will be "srcType", the output data type will be "dstType",
// the intermediate data type is "bufType".
// _rowBorderType and _columnBorderType determine how the image
// will be extrapolated beyond the image boundaries.
// _borderValue is only used when _rowBorderType and/or _columnBorderType
// == BORDER_CONSTANT
FilterEngine(const Ptr<BaseFilter>& _filter2D,
const Ptr<BaseRowFilter>& _rowFilter,
const Ptr<BaseColumnFilter>& _columnFilter,
int srcType, int dstType, int bufType,
int _rowBorderType=BORDER_REPLICATE,
int _columnBorderType=-1, // use _rowBorderType by default
const Scalar& _borderValue=Scalar());
virtual ~FilterEngine();
// separate function for the engine initialization
void init(const Ptr<BaseFilter>& _filter2D,
const Ptr<BaseRowFilter>& _rowFilter,
const Ptr<BaseColumnFilter>& _columnFilter,
int srcType, int dstType, int bufType,
int _rowBorderType=BORDER_REPLICATE, int _columnBorderType=-1,
const Scalar& _borderValue=Scalar());
// starts filtering of the ROI in an image of size "wholeSize".
// returns the starting y-position in the source image.
virtual int start(Size wholeSize, Rect roi, int maxBufRows=-1);
// alternative form of start that takes the image
// itself instead of "wholeSize". Set isolated to true to pretend that
// there are no real pixels outside of the ROI
// (so that the pixels are extrapolated using the specified border modes)
virtual int start(const Mat& src, const Rect& srcRoi=Rect(0,0,-1,-1),
bool isolated=false, int maxBufRows=-1);
// processes the next portion of the source image,
// "srcCount" rows starting from "src" and
// stores the results in "dst".
// returns the number of produced rows
virtual int proceed(const uchar* src, int srcStep, int srcCount,
uchar* dst, int dstStep);
// higher-level function that processes the whole
// ROI or the whole image with a single call
virtual void apply( const Mat& src, Mat& dst,
const Rect& srcRoi=Rect(0,0,-1,-1),
Point dstOfs=Point(0,0),
bool isolated=false);
bool isSeparable() const { return filter2D.empty(); }
// how many rows from the input image are not yet processed
int remainingInputRows() const;
// how many output rows are not yet produced
int remainingOutputRows() const;
...
// the starting and the ending rows in the source image
int startY, endY;
// pointers to the filters
Ptr<BaseFilter> filter2D;
Ptr<BaseRowFilter> rowFilter;
Ptr<BaseColumnFilter> columnFilter;
};
The class ``FilterEngine`` can be used to apply an arbitrary filtering operation to an image.
It contains all the necessary intermediate buffers, computes extrapolated values
of the "virtual" pixels outside of the image, and so on. Pointers to the initialized ``FilterEngine`` instances
are returned by various ``create*Filter`` functions (see below) and they are used inside high-level functions such as
:ocv:func:`filter2D`,
:ocv:func:`erode`,
:ocv:func:`dilate`, and others. Thus, the class plays a key role in many of OpenCV filtering functions.
This class makes it easier to combine filtering operations with other operations, such as color space conversions, thresholding, arithmetic operations, and others. By combining several operations together you can get much better performance because your data will stay in cache. For example, see below the implementation of the Laplace operator for floating-point images, which is a simplified implementation of
:ocv:func:`Laplacian` : ::
void laplace_f(const Mat& src, Mat& dst)
{
CV_Assert( src.type() == CV_32F );
dst.create(src.size(), src.type());
// get the derivative and smooth kernels for d2I/dx2.
// for d2I/dy2 consider using the same kernels, just swapped
Mat kd, ks;
getSobelKernels( kd, ks, 2, 0, ksize, false, ktype );
// process 10 source rows at once
int DELTA = std::min(10, src.rows);
Ptr<FilterEngine> Fxx = createSeparableLinearFilter(src.type(),
dst.type(), kd, ks, Point(-1,-1), 0, borderType, borderType, Scalar() );
Ptr<FilterEngine> Fyy = createSeparableLinearFilter(src.type(),
dst.type(), ks, kd, Point(-1,-1), 0, borderType, borderType, Scalar() );
int y = Fxx->start(src), dsty = 0, dy = 0;
Fyy->start(src);
const uchar* sptr = src.data + y*src.step;
// allocate the buffers for the spatial image derivatives;
// the buffers need to have more than DELTA rows, because at the
// last iteration the output may take max(kd.rows-1,ks.rows-1)
// rows more than the input.
Mat Ixx( DELTA + kd.rows - 1, src.cols, dst.type() );
Mat Iyy( DELTA + kd.rows - 1, src.cols, dst.type() );
// inside the loop always pass DELTA rows to the filter
// (note that the "proceed" method takes care of possibe overflow, since
// it was given the actual image height in the "start" method)
// on output you can get:
// * < DELTA rows (initial buffer accumulation stage)
// * = DELTA rows (settled state in the middle)
// * > DELTA rows (when the input image is over, generate
// "virtual" rows using the border mode and filter them)
// this variable number of output rows is dy.
// dsty is the current output row.
// sptr is the pointer to the first input row in the portion to process
for( ; dsty < dst.rows; sptr += DELTA*src.step, dsty += dy )
{
Fxx->proceed( sptr, (int)src.step, DELTA, Ixx.data, (int)Ixx.step );
dy = Fyy->proceed( sptr, (int)src.step, DELTA, d2y.data, (int)Iyy.step );
if( dy > 0 )
{
Mat dstripe = dst.rowRange(dsty, dsty + dy);
add(Ixx.rowRange(0, dy), Iyy.rowRange(0, dy), dstripe);
}
}
}
If you do not need that much control of the filtering process, you can simply use the ``FilterEngine::apply`` method. The method is implemented as follows: ::
void FilterEngine::apply(const Mat& src, Mat& dst,
const Rect& srcRoi, Point dstOfs, bool isolated)
{
// check matrix types
CV_Assert( src.type() == srcType && dst.type() == dstType );
// handle the "whole image" case
Rect _srcRoi = srcRoi;
if( _srcRoi == Rect(0,0,-1,-1) )
_srcRoi = Rect(0,0,src.cols,src.rows);
// check if the destination ROI is inside dst.
// and FilterEngine::start will check if the source ROI is inside src.
CV_Assert( dstOfs.x >= 0 && dstOfs.y >= 0 &&
dstOfs.x + _srcRoi.width <= dst.cols &&
dstOfs.y + _srcRoi.height <= dst.rows );
// start filtering
int y = start(src, _srcRoi, isolated);
// process the whole ROI. Note that "endY - startY" is the total number
// of the source rows to process
// (including the possible rows outside of srcRoi but inside the source image)
proceed( src.data + y*src.step,
(int)src.step, endY - startY,
dst.data + dstOfs.y*dst.step +
dstOfs.x*dst.elemSize(), (int)dst.step );
}
Unlike the earlier versions of OpenCV, now the filtering operations fully support the notion of image ROI, that is, pixels outside of the ROI but inside the image can be used in the filtering operations. For example, you can take a ROI of a single pixel and filter it. This will be a filter response at that particular pixel. However, it is possible to emulate the old behavior by passing ``isolated=false`` to ``FilterEngine::start`` or ``FilterEngine::apply`` . You can pass the ROI explicitly to ``FilterEngine::apply`` or construct new matrix headers: ::
// compute dI/dx derivative at src(x,y)
// method 1:
// form a matrix header for a single value
float val1 = 0;
Mat dst1(1,1,CV_32F,&val1);
Ptr<FilterEngine> Fx = createDerivFilter(CV_32F, CV_32F,
1, 0, 3, BORDER_REFLECT_101);
Fx->apply(src, Rect(x,y,1,1), Point(), dst1);
// method 2:
// form a matrix header for a single value
float val2 = 0;
Mat dst2(1,1,CV_32F,&val2);
Mat pix_roi(src, Rect(x,y,1,1));
Sobel(pix_roi, dst2, dst2.type(), 1, 0, 3, 1, 0, BORDER_REFLECT_101);
printf("method1 =
Explore the data types. As it was mentioned in the
:ocv:class:`BaseFilter` description, the specific filters can process data of any type, despite that ``Base*Filter::operator()`` only takes ``uchar`` pointers and no information about the actual types. To make it all work, the following rules are used:
*
In case of separable filtering, ``FilterEngine::rowFilter`` is applied first. It transforms the input image data (of type ``srcType`` ) to the intermediate results stored in the internal buffers (of type ``bufType`` ). Then, these intermediate results are processed as
*single-channel data*
with ``FilterEngine::columnFilter`` and stored in the output image (of type ``dstType`` ). Thus, the input type for ``rowFilter`` is ``srcType`` and the output type is ``bufType`` . The input type for ``columnFilter`` is ``CV_MAT_DEPTH(bufType)`` and the output type is ``CV_MAT_DEPTH(dstType)`` .
*
In case of non-separable filtering, ``bufType`` must be the same as ``srcType`` . The source data is copied to the temporary buffer, if needed, and then just passed to ``FilterEngine::filter2D`` . That is, the input type for ``filter2D`` is ``srcType`` (= ``bufType`` ) and the output type is ``dstType`` .
.. seealso::
:ocv:class:`BaseColumnFilter`,
:ocv:class:`BaseFilter`,
:ocv:class:`BaseRowFilter`,
:ocv:func:`createBoxFilter`,
:ocv:func:`createDerivFilter`,
:ocv:func:`createGaussianFilter`,
:ocv:func:`createLinearFilter`,
:ocv:func:`createMorphologyFilter`,
:ocv:func:`createSeparableLinearFilter`
bilateralFilter bilateralFilter
------------------- -------------------
Applies the bilateral filter to an image. Applies the bilateral filter to an image.
@ -504,247 +147,12 @@ Constructs the Gaussian pyramid for an image.
:param maxlevel: 0-based index of the last (the smallest) pyramid layer. It must be non-negative. :param maxlevel: 0-based index of the last (the smallest) pyramid layer. It must be non-negative.
:param borderType: Pixel extrapolation method (BORDER_CONSTANT don't supported). See :ocv:func:`borderInterpolate` for details. :param borderType: Pixel extrapolation method (BORDER_CONSTANT don't supported). See ``borderInterpolate`` for details.
The function constructs a vector of images and builds the Gaussian pyramid by recursively applying The function constructs a vector of images and builds the Gaussian pyramid by recursively applying
:ocv:func:`pyrDown` to the previously built pyramid layers, starting from ``dst[0]==src`` . :ocv:func:`pyrDown` to the previously built pyramid layers, starting from ``dst[0]==src`` .
createBoxFilter
-------------------
Returns a box filter engine.
.. ocv:function:: Ptr<FilterEngine> createBoxFilter( int srcType, int dstType, Size ksize, Point anchor=Point(-1,-1), bool normalize=true, int borderType=BORDER_DEFAULT)
.. ocv:function:: Ptr<BaseRowFilter> getRowSumFilter(int srcType, int sumType, int ksize, int anchor=-1)
.. ocv:function:: Ptr<BaseColumnFilter> getColumnSumFilter(int sumType, int dstType, int ksize, int anchor=-1, double scale=1)
:param srcType: Source image type.
:param sumType: Intermediate horizontal sum type that must have as many channels as ``srcType`` .
:param dstType: Destination image type that must have as many channels as ``srcType`` .
:param ksize: Aperture size.
:param anchor: Anchor position with the kernel. Negative values mean that the anchor is at the kernel center.
:param normalize: Flag specifying whether the sums are normalized or not. See :ocv:func:`boxFilter` for details.
:param scale: Another way to specify normalization in lower-level ``getColumnSumFilter`` .
:param borderType: Border type to use. See :ocv:func:`borderInterpolate` .
The function is a convenience function that retrieves the horizontal sum primitive filter with
:ocv:func:`getRowSumFilter` , vertical sum filter with
:ocv:func:`getColumnSumFilter` , constructs new
:ocv:class:`FilterEngine` , and passes both of the primitive filters there. The constructed filter engine can be used for image filtering with normalized or unnormalized box filter.
The function itself is used by
:ocv:func:`blur` and
:ocv:func:`boxFilter` .
.. seealso::
:ocv:class:`FilterEngine`,
:ocv:func:`blur`,
:ocv:func:`boxFilter`
createDerivFilter
---------------------
Returns an engine for computing image derivatives.
.. ocv:function:: Ptr<FilterEngine> createDerivFilter( int srcType, int dstType, int dx, int dy, int ksize, int borderType=BORDER_DEFAULT )
:param srcType: Source image type.
:param dstType: Destination image type that must have as many channels as ``srcType`` .
:param dx: Derivative order in respect of x.
:param dy: Derivative order in respect of y.
:param ksize: Aperture size See :ocv:func:`getDerivKernels` .
:param borderType: Border type to use. See :ocv:func:`borderInterpolate` .
The function :ocv:func:`createDerivFilter` is a small convenience function that retrieves linear filter coefficients for computing image derivatives using
:ocv:func:`getDerivKernels` and then creates a separable linear filter with
:ocv:func:`createSeparableLinearFilter` . The function is used by
:ocv:func:`Sobel` and
:ocv:func:`Scharr` .
.. seealso::
:ocv:func:`createSeparableLinearFilter`,
:ocv:func:`getDerivKernels`,
:ocv:func:`Scharr`,
:ocv:func:`Sobel`
createGaussianFilter
------------------------
Returns an engine for smoothing images with the Gaussian filter.
.. ocv:function:: Ptr<FilterEngine> createGaussianFilter( int type, Size ksize, double sigma1, double sigma2=0, int borderType=BORDER_DEFAULT )
:param type: Source and destination image type.
:param ksize: Aperture size. See :ocv:func:`getGaussianKernel` .
:param sigma1: Gaussian sigma in the horizontal direction. See :ocv:func:`getGaussianKernel` .
:param sigma2: Gaussian sigma in the vertical direction. If 0, then :math:`\texttt{sigma2}\leftarrow\texttt{sigma1}` .
:param borderType: Border type to use. See :ocv:func:`borderInterpolate` .
The function :ocv:func:`createGaussianFilter` computes Gaussian kernel coefficients and then returns a separable linear filter for that kernel. The function is used by
:ocv:func:`GaussianBlur` . Note that while the function takes just one data type, both for input and output, you can pass this limitation by calling
:ocv:func:`getGaussianKernel` and then
:ocv:func:`createSeparableLinearFilter` directly.
.. seealso::
:ocv:func:`createSeparableLinearFilter`,
:ocv:func:`getGaussianKernel`,
:ocv:func:`GaussianBlur`
createLinearFilter
----------------------
Creates a non-separable linear filter engine.
.. ocv:function:: Ptr<FilterEngine> createLinearFilter( int srcType, int dstType, InputArray kernel, Point _anchor=Point(-1,-1), double delta=0, int rowBorderType=BORDER_DEFAULT, int columnBorderType=-1, const Scalar& borderValue=Scalar() )
.. ocv:function:: Ptr<BaseFilter> getLinearFilter(int srcType, int dstType, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int bits=0)
:param srcType: Source image type.
:param dstType: Destination image type that must have as many channels as ``srcType`` .
:param kernel: 2D array of filter coefficients.
:param anchor: Anchor point within the kernel. Special value ``Point(-1,-1)`` means that the anchor is at the kernel center.
:param delta: Value added to the filtered results before storing them.
:param bits: Number of the fractional bits. The parameter is used when the kernel is an integer matrix representing fixed-point filter coefficients.
:param rowBorderType: Pixel extrapolation method in the vertical direction. For details, see :ocv:func:`borderInterpolate`.
:param columnBorderType: Pixel extrapolation method in the horizontal direction.
:param borderValue: Border value used in case of a constant border.
The function returns a pointer to a 2D linear filter for the specified kernel, the source array type, and the destination array type. The function is a higher-level function that calls ``getLinearFilter`` and passes the retrieved 2D filter to the
:ocv:class:`FilterEngine` constructor.
.. seealso::
:ocv:func:`createSeparableLinearFilter`,
:ocv:class:`FilterEngine`,
:ocv:func:`filter2D`
createMorphologyFilter
--------------------------
Creates an engine for non-separable morphological operations.
.. ocv:function:: Ptr<FilterEngine> createMorphologyFilter( int op, int type, InputArray kernel, Point anchor=Point(-1,-1), int rowBorderType=BORDER_CONSTANT, int columnBorderType=-1, const Scalar& borderValue=morphologyDefaultBorderValue() )
.. ocv:function:: Ptr<BaseFilter> getMorphologyFilter( int op, int type, InputArray kernel, Point anchor=Point(-1,-1) )
.. ocv:function:: Ptr<BaseRowFilter> getMorphologyRowFilter( int op, int type, int ksize, int anchor=-1 )
.. ocv:function:: Ptr<BaseColumnFilter> getMorphologyColumnFilter( int op, int type, int ksize, int anchor=-1 )
.. ocv:function:: Scalar morphologyDefaultBorderValue()
:param op: Morphology operation ID, ``MORPH_ERODE`` or ``MORPH_DILATE`` .
:param type: Input/output image type. The number of channels can be arbitrary. The depth should be one of ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32F` or ``CV_64F``.
:param kernel: 2D 8-bit structuring element for a morphological operation. Non-zero elements indicate the pixels that belong to the element.
:param ksize: Horizontal or vertical structuring element size for separable morphological operations.
:param anchor: Anchor position within the structuring element. Negative values mean that the anchor is at the kernel center.
:param rowBorderType: Pixel extrapolation method in the vertical direction. For details, see :ocv:func:`borderInterpolate`.
:param columnBorderType: Pixel extrapolation method in the horizontal direction.
:param borderValue: Border value in case of a constant border. The default value, \ ``morphologyDefaultBorderValue`` , has a special meaning. It is transformed :math:`+\inf` for the erosion and to :math:`-\inf` for the dilation, which means that the minimum (maximum) is effectively computed only over the pixels that are inside the image.
The functions construct primitive morphological filtering operations or a filter engine based on them. Normally it is enough to use
:ocv:func:`createMorphologyFilter` or even higher-level
:ocv:func:`erode`,
:ocv:func:`dilate` , or
:ocv:func:`morphologyEx` .
Note that
:ocv:func:`createMorphologyFilter` analyzes the structuring element shape and builds a separable morphological filter engine when the structuring element is square.
.. seealso::
:ocv:func:`erode`,
:ocv:func:`dilate`,
:ocv:func:`morphologyEx`,
:ocv:class:`FilterEngine`
createSeparableLinearFilter
-------------------------------
Creates an engine for a separable linear filter.
.. ocv:function:: Ptr<FilterEngine> createSeparableLinearFilter( int srcType, int dstType, InputArray rowKernel, InputArray columnKernel, Point anchor=Point(-1,-1), double delta=0, int rowBorderType=BORDER_DEFAULT, int columnBorderType=-1, const Scalar& borderValue=Scalar() )
.. ocv:function:: Ptr<BaseColumnFilter> getLinearColumnFilter( int bufType, int dstType, InputArray kernel, int anchor, int symmetryType, double delta=0, int bits=0 )
.. ocv:function:: Ptr<BaseRowFilter> getLinearRowFilter( int srcType, int bufType, InputArray kernel, int anchor, int symmetryType )
:param srcType: Source array type.
:param dstType: Destination image type that must have as many channels as ``srcType`` .
:param bufType: Intermediate buffer type that must have as many channels as ``srcType`` .
:param rowKernel: Coefficients for filtering each row.
:param columnKernel: Coefficients for filtering each column.
:param anchor: Anchor position within the kernel. Negative values mean that anchor is positioned at the aperture center.
:param delta: Value added to the filtered results before storing them.
:param bits: Number of the fractional bits. The parameter is used when the kernel is an integer matrix representing fixed-point filter coefficients.
:param rowBorderType: Pixel extrapolation method in the vertical direction. For details, see :ocv:func:`borderInterpolate`.
:param columnBorderType: Pixel extrapolation method in the horizontal direction.
:param borderValue: Border value used in case of a constant border.
:param symmetryType: Type of each row and column kernel. See :ocv:func:`getKernelType` .
The functions construct primitive separable linear filtering operations or a filter engine based on them. Normally it is enough to use
:ocv:func:`createSeparableLinearFilter` or even higher-level
:ocv:func:`sepFilter2D` . The function
:ocv:func:`createMorphologyFilter` is smart enough to figure out the ``symmetryType`` for each of the two kernels, the intermediate ``bufType`` and, if filtering can be done in integer arithmetics, the number of ``bits`` to encode the filter coefficients. If it does not work for you, it is possible to call ``getLinearColumnFilter``,``getLinearRowFilter`` directly and then pass them to the
:ocv:class:`FilterEngine` constructor.
.. seealso::
:ocv:func:`sepFilter2D`,
:ocv:func:`createLinearFilter`,
:ocv:class:`FilterEngine`,
:ocv:func:`getKernelType`
dilate dilate
------ ------
Dilates an image by using a specific structuring element. Dilates an image by using a specific structuring element.
@ -759,15 +167,15 @@ Dilates an image by using a specific structuring element.
:param dst: output image of the same size and type as ``src``. :param dst: output image of the same size and type as ``src``.
:param kernel: structuring element used for dilation; if ``element=Mat()`` , a ``3 x 3`` rectangular structuring element is used. Kernel can be created using :ocv:func:`getStructuringElement` :param kernel: structuring element used for dilation; if ``elemenat=Mat()`` , a ``3 x 3`` rectangular structuring element is used. Kernel can be created using :ocv:func:`getStructuringElement`
:param anchor: position of the anchor within the element; default value ``(-1, -1)`` means that the anchor is at the element center. :param anchor: position of the anchor within the element; default value ``(-1, -1)`` means that the anchor is at the element center.
:param iterations: number of times dilation is applied. :param iterations: number of times dilation is applied.
:param borderType: pixel extrapolation method (see :ocv:func:`borderInterpolate` for details). :param borderType: pixel extrapolation method (see ``borderInterpolate`` for details).
:param borderValue: border value in case of a constant border (see :ocv:func:`createMorphologyFilter` for details). :param borderValue: border value in case of a constant border
The function dilates the source image using the specified structuring element that determines the shape of a pixel neighborhood over which the maximum is taken: The function dilates the source image using the specified structuring element that determines the shape of a pixel neighborhood over which the maximum is taken:
@ -781,7 +189,6 @@ The function supports the in-place mode. Dilation can be applied several ( ``ite
:ocv:func:`erode`, :ocv:func:`erode`,
:ocv:func:`morphologyEx`, :ocv:func:`morphologyEx`,
:ocv:func:`createMorphologyFilter`
:ocv:func:`getStructuringElement` :ocv:func:`getStructuringElement`
@ -790,8 +197,6 @@ The function supports the in-place mode. Dilation can be applied several ( ``ite
* An example using the morphological dilate operation can be found at opencv_source_code/samples/cpp/morphology2.cpp * An example using the morphological dilate operation can be found at opencv_source_code/samples/cpp/morphology2.cpp
erode erode
----- -----
Erodes an image by using a specific structuring element. Erodes an image by using a specific structuring element.
@ -812,9 +217,9 @@ Erodes an image by using a specific structuring element.
:param iterations: number of times erosion is applied. :param iterations: number of times erosion is applied.
:param borderType: pixel extrapolation method (see :ocv:func:`borderInterpolate` for details). :param borderType: pixel extrapolation method (see ``borderInterpolate`` for details).
:param borderValue: border value in case of a constant border (see :ocv:func:`createMorphologyFilter` for details). :param borderValue: border value in case of a constant border
The function erodes the source image using the specified structuring element that determines the shape of a pixel neighborhood over which the minimum is taken: The function erodes the source image using the specified structuring element that determines the shape of a pixel neighborhood over which the minimum is taken:
@ -828,7 +233,6 @@ The function supports the in-place mode. Erosion can be applied several ( ``iter
:ocv:func:`dilate`, :ocv:func:`dilate`,
:ocv:func:`morphologyEx`, :ocv:func:`morphologyEx`,
:ocv:func:`createMorphologyFilter`,
:ocv:func:`getStructuringElement` :ocv:func:`getStructuringElement`
.. note:: .. note::
@ -864,7 +268,7 @@ Convolves an image with the kernel.
:param delta: optional value added to the filtered pixels before storing them in ``dst``. :param delta: optional value added to the filtered pixels before storing them in ``dst``.
:param borderType: pixel extrapolation method (see :ocv:func:`borderInterpolate` for details). :param borderType: pixel extrapolation method (see ``borderInterpolate`` for details).
The function applies an arbitrary linear filter to an image. In-place operation is supported. When the aperture is partially outside the image, the function interpolates outlier pixel values according to the specified border mode. The function applies an arbitrary linear filter to an image. In-place operation is supported. When the aperture is partially outside the image, the function interpolates outlier pixel values according to the specified border mode.
@ -877,12 +281,11 @@ The function does actually compute correlation, not the convolution:
That is, the kernel is not mirrored around the anchor point. If you need a real convolution, flip the kernel using That is, the kernel is not mirrored around the anchor point. If you need a real convolution, flip the kernel using
:ocv:func:`flip` and set the new anchor to ``(kernel.cols - anchor.x - 1, kernel.rows - anchor.y - 1)`` . :ocv:func:`flip` and set the new anchor to ``(kernel.cols - anchor.x - 1, kernel.rows - anchor.y - 1)`` .
The function uses the DFT-based algorithm in case of sufficiently large kernels (~``11 x 11`` or larger) and the direct algorithm (that uses the engine retrieved by :ocv:func:`createLinearFilter` ) for small kernels. The function uses the DFT-based algorithm in case of sufficiently large kernels (~``11 x 11`` or larger) and the direct algorithm for small kernels.
.. seealso:: .. seealso::
:ocv:func:`sepFilter2D`, :ocv:func:`sepFilter2D`,
:ocv:func:`createLinearFilter`,
:ocv:func:`dft`, :ocv:func:`dft`,
:ocv:func:`matchTemplate` :ocv:func:`matchTemplate`
@ -906,7 +309,7 @@ Blurs an image using a Gaussian filter.
:param sigmaY: Gaussian kernel standard deviation in Y direction; if ``sigmaY`` is zero, it is set to be equal to ``sigmaX``, if both sigmas are zeros, they are computed from ``ksize.width`` and ``ksize.height`` , respectively (see :ocv:func:`getGaussianKernel` for details); to fully control the result regardless of possible future modifications of all this semantics, it is recommended to specify all of ``ksize``, ``sigmaX``, and ``sigmaY``. :param sigmaY: Gaussian kernel standard deviation in Y direction; if ``sigmaY`` is zero, it is set to be equal to ``sigmaX``, if both sigmas are zeros, they are computed from ``ksize.width`` and ``ksize.height`` , respectively (see :ocv:func:`getGaussianKernel` for details); to fully control the result regardless of possible future modifications of all this semantics, it is recommended to specify all of ``ksize``, ``sigmaX``, and ``sigmaY``.
:param borderType: pixel extrapolation method (see :ocv:func:`borderInterpolate` for details). :param borderType: pixel extrapolation method (see ``borderInterpolate`` for details).
The function convolves the source image with the specified Gaussian kernel. In-place filtering is supported. The function convolves the source image with the specified Gaussian kernel. In-place filtering is supported.
@ -947,8 +350,6 @@ The function computes and returns the filter coefficients for spatial image deri
:ocv:func:`Scharr` ). Otherwise, Sobel kernels are generated (see :ocv:func:`Scharr` ). Otherwise, Sobel kernels are generated (see
:ocv:func:`Sobel` ). The filters are normally passed to :ocv:func:`Sobel` ). The filters are normally passed to
:ocv:func:`sepFilter2D` or to :ocv:func:`sepFilter2D` or to
:ocv:func:`createSeparableLinearFilter` .
getGaussianKernel getGaussianKernel
@ -977,14 +378,12 @@ where
:math:`\sum_i G_i=1`. :math:`\sum_i G_i=1`.
Two of such generated kernels can be passed to Two of such generated kernels can be passed to
:ocv:func:`sepFilter2D` or to :ocv:func:`sepFilter2D`. Those functions automatically recognize smoothing kernels (a symmetrical kernel with sum of weights equal to 1) and handle them accordingly. You may also use the higher-level
:ocv:func:`createSeparableLinearFilter`. Those functions automatically recognize smoothing kernels (a symmetrical kernel with sum of weights equal to 1) and handle them accordingly. You may also use the higher-level
:ocv:func:`GaussianBlur`. :ocv:func:`GaussianBlur`.
.. seealso:: .. seealso::
:ocv:func:`sepFilter2D`, :ocv:func:`sepFilter2D`,
:ocv:func:`createSeparableLinearFilter`,
:ocv:func:`getDerivKernels`, :ocv:func:`getDerivKernels`,
:ocv:func:`getStructuringElement`, :ocv:func:`getStructuringElement`,
:ocv:func:`GaussianBlur` :ocv:func:`GaussianBlur`
@ -1016,30 +415,6 @@ Returns Gabor filter coefficients.
For more details about gabor filter equations and parameters, see: `Gabor Filter <http://en.wikipedia.org/wiki/Gabor_filter>`_. For more details about gabor filter equations and parameters, see: `Gabor Filter <http://en.wikipedia.org/wiki/Gabor_filter>`_.
getKernelType
-------------
Returns the kernel type.
.. ocv:function:: int getKernelType(InputArray kernel, Point anchor)
:param kernel: 1D array of the kernel coefficients to analyze.
:param anchor: Anchor position within the kernel.
The function analyzes the kernel coefficients and returns the corresponding kernel type:
* **KERNEL_GENERAL** The kernel is generic. It is used when there is no any type of symmetry or other properties.
* **KERNEL_SYMMETRICAL** The kernel is symmetrical: :math:`\texttt{kernel}_i == \texttt{kernel}_{ksize-i-1}` , and the anchor is at the center.
* **KERNEL_ASYMMETRICAL** The kernel is asymmetrical: :math:`\texttt{kernel}_i == -\texttt{kernel}_{ksize-i-1}` , and the anchor is at the center.
* **KERNEL_SMOOTH** All the kernel elements are non-negative and summed to 1. For example, the Gaussian kernel is both smooth kernel and symmetrical, so the function returns ``KERNEL_SMOOTH | KERNEL_SYMMETRICAL`` .
* **KERNEL_INTEGER** All the kernel coefficients are integer numbers. This flag can be combined with ``KERNEL_SYMMETRICAL`` or ``KERNEL_ASYMMETRICAL`` .
getStructuringElement getStructuringElement
--------------------- ---------------------
Returns a structuring element of the specified size and shape for morphological operations. Returns a structuring element of the specified size and shape for morphological operations.
@ -1083,7 +458,6 @@ Returns a structuring element of the specified size and shape for morphological
:param values: integer array of ``cols``*``rows`` elements that specifies the custom shape of the structuring element, when ``shape=CV_SHAPE_CUSTOM``. :param values: integer array of ``cols``*``rows`` elements that specifies the custom shape of the structuring element, when ``shape=CV_SHAPE_CUSTOM``.
The function constructs and returns the structuring element that can be further passed to The function constructs and returns the structuring element that can be further passed to
:ocv:func:`createMorphologyFilter`,
:ocv:func:`erode`, :ocv:func:`erode`,
:ocv:func:`dilate` or :ocv:func:`dilate` or
:ocv:func:`morphologyEx` . But you can also construct an arbitrary binary mask yourself and use it as the structuring element. :ocv:func:`morphologyEx` . But you can also construct an arbitrary binary mask yourself and use it as the structuring element.
@ -1149,9 +523,9 @@ Performs advanced morphological transformations.
:param iterations: Number of times erosion and dilation are applied. :param iterations: Number of times erosion and dilation are applied.
:param borderType: Pixel extrapolation method. See :ocv:func:`borderInterpolate` for details. :param borderType: Pixel extrapolation method. See ``borderInterpolate`` for details.
:param borderValue: Border value in case of a constant border. The default value has a special meaning. See :ocv:func:`createMorphologyFilter` for details. :param borderValue: Border value in case of a constant border. The default value has a special meaning.
The function can perform advanced morphological transformations using an erosion and dilation as basic operations. The function can perform advanced morphological transformations using an erosion and dilation as basic operations.
@ -1191,7 +565,6 @@ Any of the operations can be done in-place. In case of multi-channel images, eac
:ocv:func:`dilate`, :ocv:func:`dilate`,
:ocv:func:`erode`, :ocv:func:`erode`,
:ocv:func:`createMorphologyFilter`,
:ocv:func:`getStructuringElement` :ocv:func:`getStructuringElement`
.. note:: .. note::
@ -1220,7 +593,7 @@ Calculates the Laplacian of an image.
:param delta: Optional delta value that is added to the results prior to storing them in ``dst`` . :param delta: Optional delta value that is added to the results prior to storing them in ``dst`` .
:param borderType: Pixel extrapolation method. See :ocv:func:`borderInterpolate` for details. :param borderType: Pixel extrapolation method. See ``borderInterpolate`` for details.
The function calculates the Laplacian of the source image by adding up the second x and y derivatives calculated using the Sobel operator: The function calculates the Laplacian of the source image by adding up the second x and y derivatives calculated using the Sobel operator:
@ -1260,7 +633,7 @@ Blurs an image and downsamples it.
:param dstsize: size of the output image. :param dstsize: size of the output image.
:param borderType: Pixel extrapolation method (BORDER_CONSTANT don't supported). See :ocv:func:`borderInterpolate` for details. :param borderType: Pixel extrapolation method (BORDER_CONSTANT don't supported). See ``borderInterpolate`` for details.
By default, size of the output image is computed as ``Size((src.cols+1)/2, (src.rows+1)/2)``, but in any case, the following conditions should be satisfied: By default, size of the output image is computed as ``Size((src.cols+1)/2, (src.rows+1)/2)``, but in any case, the following conditions should be satisfied:
@ -1293,7 +666,7 @@ Upsamples an image and then blurs it.
:param dstsize: size of the output image. :param dstsize: size of the output image.
:param borderType: Pixel extrapolation method (only BORDER_DEFAULT supported). See :ocv:func:`borderInterpolate` for details. :param borderType: Pixel extrapolation method (only BORDER_DEFAULT supported). See ``borderInterpolate`` for details.
By default, size of the output image is computed as ``Size(src.cols*2, (src.rows*2)``, but in any case, the following conditions should be satisfied: By default, size of the output image is computed as ``Size(src.cols*2, (src.rows*2)``, but in any case, the following conditions should be satisfied:
@ -1388,13 +761,12 @@ Applies a separable linear filter to an image.
:param delta: Value added to the filtered results before storing them. :param delta: Value added to the filtered results before storing them.
:param borderType: Pixel extrapolation method. See :ocv:func:`borderInterpolate` for details. :param borderType: Pixel extrapolation method. See ``borderInterpolate`` for details.
The function applies a separable linear filter to the image. That is, first, every row of ``src`` is filtered with the 1D kernel ``kernelX`` . Then, every column of the result is filtered with the 1D kernel ``kernelY`` . The final result shifted by ``delta`` is stored in ``dst`` . The function applies a separable linear filter to the image. That is, first, every row of ``src`` is filtered with the 1D kernel ``kernelX`` . Then, every column of the result is filtered with the 1D kernel ``kernelY`` . The final result shifted by ``delta`` is stored in ``dst`` .
.. seealso:: .. seealso::
:ocv:func:`createSeparableLinearFilter`,
:ocv:func:`filter2D`, :ocv:func:`filter2D`,
:ocv:func:`Sobel`, :ocv:func:`Sobel`,
:ocv:func:`GaussianBlur`, :ocv:func:`GaussianBlur`,
@ -1484,7 +856,7 @@ Calculates the first, second, third, or mixed image derivatives using an extende
:param delta: optional delta value that is added to the results prior to storing them in ``dst``. :param delta: optional delta value that is added to the results prior to storing them in ``dst``.
:param borderType: pixel extrapolation method (see :ocv:func:`borderInterpolate` for details). :param borderType: pixel extrapolation method (see ``borderInterpolate`` for details).
In all cases except one, the In all cases except one, the
:math:`\texttt{ksize} \times :math:`\texttt{ksize} \times
@ -1559,7 +931,7 @@ Calculates the first x- or y- image derivative using Scharr operator.
:param delta: optional delta value that is added to the results prior to storing them in ``dst``. :param delta: optional delta value that is added to the results prior to storing them in ``dst``.
:param borderType: pixel extrapolation method (see :ocv:func:`borderInterpolate` for details). :param borderType: pixel extrapolation method (see ``borderInterpolate`` for details).
The function computes the first x- or y- spatial image derivative using the Scharr operator. The call The function computes the first x- or y- spatial image derivative using the Scharr operator. The call

@ -10,6 +10,7 @@ imgproc. Image Processing
filtering filtering
geometric_transformations geometric_transformations
miscellaneous_transformations miscellaneous_transformations
drawing_functions
colormaps colormaps
histograms histograms
structural_analysis_and_shape_descriptors structural_analysis_and_shape_descriptors

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -51,14 +51,6 @@
namespace cv namespace cv
{ {
//! type of the kernel
enum { KERNEL_GENERAL = 0, // the kernel is generic. No any type of symmetry or other properties.
KERNEL_SYMMETRICAL = 1, // kernel[i] == kernel[ksize-i-1] , and the anchor is at the center
KERNEL_ASYMMETRICAL = 2, // kernel[i] == -kernel[ksize-i-1] , and the anchor is at the center
KERNEL_SMOOTH = 4, // all the kernel elements are non-negative and summed to 1
KERNEL_INTEGER = 8 // all the kernel coefficients are integer numbers
};
//! type of morphological operation //! type of morphological operation
enum { MORPH_ERODE = 0, enum { MORPH_ERODE = 0,
MORPH_DILATE = 1, MORPH_DILATE = 1,
@ -471,235 +463,6 @@ enum { INTERSECT_NONE = 0,
INTERSECT_FULL = 2 INTERSECT_FULL = 2
}; };
/*!
The Base Class for 1D or Row-wise Filters
This is the base class for linear or non-linear filters that process 1D data.
In particular, such filters are used for the "horizontal" filtering parts in separable filters.
Several functions in OpenCV return Ptr<BaseRowFilter> for the specific types of filters,
and those pointers can be used directly or within cv::FilterEngine.
*/
class CV_EXPORTS BaseRowFilter
{
public:
//! the default constructor
BaseRowFilter();
//! the destructor
virtual ~BaseRowFilter();
//! the filtering operator. Must be overrided in the derived classes. The horizontal border interpolation is done outside of the class.
virtual void operator()(const uchar* src, uchar* dst, int width, int cn) = 0;
int ksize;
int anchor;
};
/*!
The Base Class for Column-wise Filters
This is the base class for linear or non-linear filters that process columns of 2D arrays.
Such filters are used for the "vertical" filtering parts in separable filters.
Several functions in OpenCV return Ptr<BaseColumnFilter> for the specific types of filters,
and those pointers can be used directly or within cv::FilterEngine.
Unlike cv::BaseRowFilter, cv::BaseColumnFilter may have some context information,
i.e. box filter keeps the sliding sum of elements. To reset the state BaseColumnFilter::reset()
must be called (e.g. the method is called by cv::FilterEngine)
*/
class CV_EXPORTS BaseColumnFilter
{
public:
//! the default constructor
BaseColumnFilter();
//! the destructor
virtual ~BaseColumnFilter();
//! the filtering operator. Must be overrided in the derived classes. The vertical border interpolation is done outside of the class.
virtual void operator()(const uchar** src, uchar* dst, int dststep, int dstcount, int width) = 0;
//! resets the internal buffers, if any
virtual void reset();
int ksize;
int anchor;
};
/*!
The Base Class for Non-Separable 2D Filters.
This is the base class for linear or non-linear 2D filters.
Several functions in OpenCV return Ptr<BaseFilter> for the specific types of filters,
and those pointers can be used directly or within cv::FilterEngine.
Similar to cv::BaseColumnFilter, the class may have some context information,
that should be reset using BaseFilter::reset() method before processing the new array.
*/
class CV_EXPORTS BaseFilter
{
public:
//! the default constructor
BaseFilter();
//! the destructor
virtual ~BaseFilter();
//! the filtering operator. The horizontal and the vertical border interpolation is done outside of the class.
virtual void operator()(const uchar** src, uchar* dst, int dststep, int dstcount, int width, int cn) = 0;
//! resets the internal buffers, if any
virtual void reset();
Size ksize;
Point anchor;
};
/*!
The Main Class for Image Filtering.
The class can be used to apply an arbitrary filtering operation to an image.
It contains all the necessary intermediate buffers, it computes extrapolated values
of the "virtual" pixels outside of the image etc.
Pointers to the initialized cv::FilterEngine instances
are returned by various OpenCV functions, such as cv::createSeparableLinearFilter(),
cv::createLinearFilter(), cv::createGaussianFilter(), cv::createDerivFilter(),
cv::createBoxFilter() and cv::createMorphologyFilter().
Using the class you can process large images by parts and build complex pipelines
that include filtering as some of the stages. If all you need is to apply some pre-defined
filtering operation, you may use cv::filter2D(), cv::erode(), cv::dilate() etc.
functions that create FilterEngine internally.
Here is the example on how to use the class to implement Laplacian operator, which is the sum of
second-order derivatives. More complex variant for different types is implemented in cv::Laplacian().
\code
void laplace_f(const Mat& src, Mat& dst)
{
CV_Assert( src.type() == CV_32F );
// make sure the destination array has the proper size and type
dst.create(src.size(), src.type());
// get the derivative and smooth kernels for d2I/dx2.
// for d2I/dy2 we could use the same kernels, just swapped
Mat kd, ks;
getSobelKernels( kd, ks, 2, 0, ksize, false, ktype );
// let's process 10 source rows at once
int DELTA = std::min(10, src.rows);
Ptr<FilterEngine> Fxx = createSeparableLinearFilter(src.type(),
dst.type(), kd, ks, Point(-1,-1), 0, borderType, borderType, Scalar() );
Ptr<FilterEngine> Fyy = createSeparableLinearFilter(src.type(),
dst.type(), ks, kd, Point(-1,-1), 0, borderType, borderType, Scalar() );
int y = Fxx->start(src), dsty = 0, dy = 0;
Fyy->start(src);
const uchar* sptr = src.data + y*src.step;
// allocate the buffers for the spatial image derivatives;
// the buffers need to have more than DELTA rows, because at the
// last iteration the output may take max(kd.rows-1,ks.rows-1)
// rows more than the input.
Mat Ixx( DELTA + kd.rows - 1, src.cols, dst.type() );
Mat Iyy( DELTA + kd.rows - 1, src.cols, dst.type() );
// inside the loop we always pass DELTA rows to the filter
// (note that the "proceed" method takes care of possibe overflow, since
// it was given the actual image height in the "start" method)
// on output we can get:
// * < DELTA rows (the initial buffer accumulation stage)
// * = DELTA rows (settled state in the middle)
// * > DELTA rows (then the input image is over, but we generate
// "virtual" rows using the border mode and filter them)
// this variable number of output rows is dy.
// dsty is the current output row.
// sptr is the pointer to the first input row in the portion to process
for( ; dsty < dst.rows; sptr += DELTA*src.step, dsty += dy )
{
Fxx->proceed( sptr, (int)src.step, DELTA, Ixx.data, (int)Ixx.step );
dy = Fyy->proceed( sptr, (int)src.step, DELTA, d2y.data, (int)Iyy.step );
if( dy > 0 )
{
Mat dstripe = dst.rowRange(dsty, dsty + dy);
add(Ixx.rowRange(0, dy), Iyy.rowRange(0, dy), dstripe);
}
}
}
\endcode
*/
class CV_EXPORTS FilterEngine
{
public:
//! the default constructor
FilterEngine();
//! the full constructor. Either _filter2D or both _rowFilter and _columnFilter must be non-empty.
FilterEngine(const Ptr<BaseFilter>& _filter2D,
const Ptr<BaseRowFilter>& _rowFilter,
const Ptr<BaseColumnFilter>& _columnFilter,
int srcType, int dstType, int bufType,
int _rowBorderType = BORDER_REPLICATE,
int _columnBorderType = -1,
const Scalar& _borderValue = Scalar());
//! the destructor
virtual ~FilterEngine();
//! reinitializes the engine. The previously assigned filters are released.
void init(const Ptr<BaseFilter>& _filter2D,
const Ptr<BaseRowFilter>& _rowFilter,
const Ptr<BaseColumnFilter>& _columnFilter,
int srcType, int dstType, int bufType,
int _rowBorderType = BORDER_REPLICATE,
int _columnBorderType = -1,
const Scalar& _borderValue = Scalar());
//! starts filtering of the specified ROI of an image of size wholeSize.
virtual int start(Size wholeSize, Rect roi, int maxBufRows = -1);
//! starts filtering of the specified ROI of the specified image.
virtual int start(const Mat& src, const Rect& srcRoi = Rect(0,0,-1,-1),
bool isolated = false, int maxBufRows = -1);
//! processes the next srcCount rows of the image.
virtual int proceed(const uchar* src, int srcStep, int srcCount,
uchar* dst, int dstStep);
//! applies filter to the specified ROI of the image. if srcRoi=(0,0,-1,-1), the whole image is filtered.
virtual void apply( const Mat& src, Mat& dst,
const Rect& srcRoi = Rect(0,0,-1,-1),
Point dstOfs = Point(0,0),
bool isolated = false);
//! returns true if the filter is separable
bool isSeparable() const { return !filter2D; }
//! returns the number
int remainingInputRows() const;
int remainingOutputRows() const;
int srcType;
int dstType;
int bufType;
Size ksize;
Point anchor;
int maxWidth;
Size wholeSize;
Rect roi;
int dx1;
int dx2;
int rowBorderType;
int columnBorderType;
std::vector<int> borderTab;
int borderElemSize;
std::vector<uchar> ringBuf;
std::vector<uchar> srcRow;
std::vector<uchar> constBorderValue;
std::vector<uchar> constBorderRow;
int bufStep;
int startY;
int startY0;
int endY;
int rowCount;
int dstY;
std::vector<uchar*> rows;
Ptr<BaseFilter> filter2D;
Ptr<BaseRowFilter> rowFilter;
Ptr<BaseColumnFilter> columnFilter;
};
//! finds arbitrary template in the grayscale image using Generalized Hough Transform //! finds arbitrary template in the grayscale image using Generalized Hough Transform
class CV_EXPORTS GeneralizedHough : public Algorithm class CV_EXPORTS GeneralizedHough : public Algorithm
{ {
@ -963,94 +726,21 @@ CV_EXPORTS_W Ptr<LineSegmentDetector> createLineSegmentDetector(
double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5, double _sigma_scale = 0.6, double _quant = 2.0, double _ang_th = 22.5,
double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024); double _log_eps = 0, double _density_th = 0.7, int _n_bins = 1024);
//! returns type (one of KERNEL_*) of 1D or 2D kernel specified by its coefficients.
CV_EXPORTS int getKernelType(InputArray kernel, Point anchor);
//! returns the primitive row filter with the specified kernel
CV_EXPORTS Ptr<BaseRowFilter> getLinearRowFilter(int srcType, int bufType,
InputArray kernel, int anchor,
int symmetryType);
//! returns the primitive column filter with the specified kernel
CV_EXPORTS Ptr<BaseColumnFilter> getLinearColumnFilter(int bufType, int dstType,
InputArray kernel, int anchor,
int symmetryType, double delta = 0,
int bits = 0);
//! returns 2D filter with the specified kernel
CV_EXPORTS Ptr<BaseFilter> getLinearFilter(int srcType, int dstType,
InputArray kernel,
Point anchor = Point(-1,-1),
double delta = 0, int bits = 0);
//! returns the separable linear filter engine
CV_EXPORTS Ptr<FilterEngine> createSeparableLinearFilter(int srcType, int dstType,
InputArray rowKernel, InputArray columnKernel,
Point anchor = Point(-1,-1), double delta = 0,
int rowBorderType = BORDER_DEFAULT,
int columnBorderType = -1,
const Scalar& borderValue = Scalar());
//! returns the non-separable linear filter engine
CV_EXPORTS Ptr<FilterEngine> createLinearFilter(int srcType, int dstType,
InputArray kernel, Point _anchor = Point(-1,-1),
double delta = 0, int rowBorderType = BORDER_DEFAULT,
int columnBorderType = -1, const Scalar& borderValue = Scalar());
//! returns the Gaussian kernel with the specified parameters //! returns the Gaussian kernel with the specified parameters
CV_EXPORTS_W Mat getGaussianKernel( int ksize, double sigma, int ktype = CV_64F ); CV_EXPORTS_W Mat getGaussianKernel( int ksize, double sigma, int ktype = CV_64F );
//! returns the Gaussian filter engine
CV_EXPORTS Ptr<FilterEngine> createGaussianFilter( int type, Size ksize,
double sigma1, double sigma2 = 0,
int borderType = BORDER_DEFAULT);
//! initializes kernels of the generalized Sobel operator //! initializes kernels of the generalized Sobel operator
CV_EXPORTS_W void getDerivKernels( OutputArray kx, OutputArray ky, CV_EXPORTS_W void getDerivKernels( OutputArray kx, OutputArray ky,
int dx, int dy, int ksize, int dx, int dy, int ksize,
bool normalize = false, int ktype = CV_32F ); bool normalize = false, int ktype = CV_32F );
//! returns filter engine for the generalized Sobel operator
CV_EXPORTS Ptr<FilterEngine> createDerivFilter( int srcType, int dstType,
int dx, int dy, int ksize,
int borderType = BORDER_DEFAULT );
//! returns horizontal 1D box filter
CV_EXPORTS Ptr<BaseRowFilter> getRowSumFilter(int srcType, int sumType,
int ksize, int anchor = -1);
//! returns vertical 1D box filter
CV_EXPORTS Ptr<BaseColumnFilter> getColumnSumFilter( int sumType, int dstType,
int ksize, int anchor = -1,
double scale = 1);
//! returns box filter engine
CV_EXPORTS Ptr<FilterEngine> createBoxFilter( int srcType, int dstType, Size ksize,
Point anchor = Point(-1,-1),
bool normalize = true,
int borderType = BORDER_DEFAULT);
//! returns the Gabor kernel with the specified parameters //! returns the Gabor kernel with the specified parameters
CV_EXPORTS_W Mat getGaborKernel( Size ksize, double sigma, double theta, double lambd, CV_EXPORTS_W Mat getGaborKernel( Size ksize, double sigma, double theta, double lambd,
double gamma, double psi = CV_PI*0.5, int ktype = CV_64F ); double gamma, double psi = CV_PI*0.5, int ktype = CV_64F );
//! returns horizontal 1D morphological filter
CV_EXPORTS Ptr<BaseRowFilter> getMorphologyRowFilter(int op, int type, int ksize, int anchor = -1);
//! returns vertical 1D morphological filter
CV_EXPORTS Ptr<BaseColumnFilter> getMorphologyColumnFilter(int op, int type, int ksize, int anchor = -1);
//! returns 2D morphological filter
CV_EXPORTS Ptr<BaseFilter> getMorphologyFilter(int op, int type, InputArray kernel,
Point anchor = Point(-1,-1));
//! returns "magic" border value for erosion and dilation. It is automatically transformed to Scalar::all(-DBL_MAX) for dilation. //! returns "magic" border value for erosion and dilation. It is automatically transformed to Scalar::all(-DBL_MAX) for dilation.
static inline Scalar morphologyDefaultBorderValue() { return Scalar::all(DBL_MAX); } static inline Scalar morphologyDefaultBorderValue() { return Scalar::all(DBL_MAX); }
//! returns morphological filter engine. Only MORPH_ERODE and MORPH_DILATE are supported.
CV_EXPORTS Ptr<FilterEngine> createMorphologyFilter(int op, int type, InputArray kernel,
Point anchor = Point(-1,-1), int rowBorderType = BORDER_CONSTANT,
int columnBorderType = -1, const Scalar& borderValue = morphologyDefaultBorderValue());
//! returns structuring element of the specified shape and size //! returns structuring element of the specified shape and size
CV_EXPORTS_W Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1)); CV_EXPORTS_W Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
@ -1536,6 +1226,97 @@ enum
CV_EXPORTS_W void applyColorMap(InputArray src, OutputArray dst, int colormap); CV_EXPORTS_W void applyColorMap(InputArray src, OutputArray dst, int colormap);
//! draws the line segment (pt1, pt2) in the image
CV_EXPORTS_W void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
int thickness = 1, int lineType = LINE_8, int shift = 0);
//! draws an arrow from pt1 to pt2 in the image
CV_EXPORTS_W void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
int thickness=1, int line_type=8, int shift=0, double tipLength=0.1);
//! draws the rectangle outline or a solid rectangle with the opposite corners pt1 and pt2 in the image
CV_EXPORTS_W void rectangle(InputOutputArray img, Point pt1, Point pt2,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
//! draws the rectangle outline or a solid rectangle covering rec in the image
CV_EXPORTS void rectangle(CV_IN_OUT Mat& img, Rect rec,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
//! draws the circle outline or a solid circle in the image
CV_EXPORTS_W void circle(InputOutputArray img, Point center, int radius,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
//! draws an elliptic arc, ellipse sector or a rotated ellipse in the image
CV_EXPORTS_W void ellipse(InputOutputArray img, Point center, Size axes,
double angle, double startAngle, double endAngle,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
//! draws a rotated ellipse in the image
CV_EXPORTS_W void ellipse(InputOutputArray img, const RotatedRect& box, const Scalar& color,
int thickness = 1, int lineType = LINE_8);
//! draws a filled convex polygon in the image
CV_EXPORTS void fillConvexPoly(Mat& img, const Point* pts, int npts,
const Scalar& color, int lineType = LINE_8,
int shift = 0);
CV_EXPORTS_W void fillConvexPoly(InputOutputArray img, InputArray points,
const Scalar& color, int lineType = LINE_8,
int shift = 0);
//! fills an area bounded by one or more polygons
CV_EXPORTS void fillPoly(Mat& img, const Point** pts,
const int* npts, int ncontours,
const Scalar& color, int lineType = LINE_8, int shift = 0,
Point offset = Point() );
CV_EXPORTS_W void fillPoly(InputOutputArray img, InputArrayOfArrays pts,
const Scalar& color, int lineType = LINE_8, int shift = 0,
Point offset = Point() );
//! draws one or more polygonal curves
CV_EXPORTS void polylines(Mat& img, const Point* const* pts, const int* npts,
int ncontours, bool isClosed, const Scalar& color,
int thickness = 1, int lineType = LINE_8, int shift = 0 );
CV_EXPORTS_W void polylines(InputOutputArray img, InputArrayOfArrays pts,
bool isClosed, const Scalar& color,
int thickness = 1, int lineType = LINE_8, int shift = 0 );
//! draws contours in the image
CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color,
int thickness = 1, int lineType = LINE_8,
InputArray hierarchy = noArray(),
int maxLevel = INT_MAX, Point offset = Point() );
//! clips the line segment by the rectangle Rect(0, 0, imgSize.width, imgSize.height)
CV_EXPORTS bool clipLine(Size imgSize, CV_IN_OUT Point& pt1, CV_IN_OUT Point& pt2);
//! clips the line segment by the rectangle imgRect
CV_EXPORTS_W bool clipLine(Rect imgRect, CV_OUT CV_IN_OUT Point& pt1, CV_OUT CV_IN_OUT Point& pt2);
//! converts elliptic arc to a polygonal curve
CV_EXPORTS_W void ellipse2Poly( Point center, Size axes, int angle,
int arcStart, int arcEnd, int delta,
CV_OUT std::vector<Point>& pts );
//! renders text string in the image
CV_EXPORTS_W void putText( InputOutputArray img, const String& text, Point org,
int fontFace, double fontScale, Scalar color,
int thickness = 1, int lineType = LINE_8,
bool bottomLeftOrigin = false );
//! returns bounding box of the text string
CV_EXPORTS_W Size getTextSize(const String& text, int fontFace,
double fontScale, int thickness,
CV_OUT int* baseLine);
} // cv } // cv
#endif #endif

@ -616,6 +616,191 @@ CVAPI(CvSeq*) cvHoughCircles( CvArr* image, void* circle_storage,
CVAPI(void) cvFitLine( const CvArr* points, int dist_type, double param, CVAPI(void) cvFitLine( const CvArr* points, int dist_type, double param,
double reps, double aeps, float* line ); double reps, double aeps, float* line );
/****************************************************************************************\
* Drawing *
\****************************************************************************************/
/****************************************************************************************\
* Drawing functions work with images/matrices of arbitrary type. *
* For color images the channel order is BGR[A] *
* Antialiasing is supported only for 8-bit image now. *
* All the functions include parameter color that means rgb value (that may be *
* constructed with CV_RGB macro) for color images and brightness *
* for grayscale images. *
* If a drawn figure is partially or completely outside of the image, it is clipped.*
\****************************************************************************************/
#define CV_RGB( r, g, b ) cvScalar( (b), (g), (r), 0 )
#define CV_FILLED -1
#define CV_AA 16
/* Draws 4-connected, 8-connected or antialiased line segment connecting two points */
CVAPI(void) cvLine( CvArr* img, CvPoint pt1, CvPoint pt2,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) );
/* Draws a rectangle given two opposite corners of the rectangle (pt1 & pt2),
if thickness<0 (e.g. thickness == CV_FILLED), the filled box is drawn */
CVAPI(void) cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8),
int shift CV_DEFAULT(0));
/* Draws a rectangle specified by a CvRect structure */
CVAPI(void) cvRectangleR( CvArr* img, CvRect r,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8),
int shift CV_DEFAULT(0));
/* Draws a circle with specified center and radius.
Thickness works in the same way as with cvRectangle */
CVAPI(void) cvCircle( CvArr* img, CvPoint center, int radius,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));
/* Draws ellipse outline, filled ellipse, elliptic arc or filled elliptic sector,
depending on <thickness>, <start_angle> and <end_angle> parameters. The resultant figure
is rotated by <angle>. All the angles are in degrees */
CVAPI(void) cvEllipse( CvArr* img, CvPoint center, CvSize axes,
double angle, double start_angle, double end_angle,
CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));
CV_INLINE void cvEllipseBox( CvArr* img, CvBox2D box, CvScalar color,
int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) )
{
CvSize axes;
axes.width = cvRound(box.size.width*0.5);
axes.height = cvRound(box.size.height*0.5);
cvEllipse( img, cvPointFrom32f( box.center ), axes, box.angle,
0, 360, color, thickness, line_type, shift );
}
/* Fills convex or monotonous polygon. */
CVAPI(void) cvFillConvexPoly( CvArr* img, const CvPoint* pts, int npts, CvScalar color,
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));
/* Fills an area bounded by one or more arbitrary polygons */
CVAPI(void) cvFillPoly( CvArr* img, CvPoint** pts, const int* npts,
int contours, CvScalar color,
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) );
/* Draws one or more polygonal curves */
CVAPI(void) cvPolyLine( CvArr* img, CvPoint** pts, const int* npts, int contours,
int is_closed, CvScalar color, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) );
#define cvDrawRect cvRectangle
#define cvDrawLine cvLine
#define cvDrawCircle cvCircle
#define cvDrawEllipse cvEllipse
#define cvDrawPolyLine cvPolyLine
/* Clips the line segment connecting *pt1 and *pt2
by the rectangular window
(0<=x<img_size.width, 0<=y<img_size.height). */
CVAPI(int) cvClipLine( CvSize img_size, CvPoint* pt1, CvPoint* pt2 );
/* Initializes line iterator. Initially, line_iterator->ptr will point
to pt1 (or pt2, see left_to_right description) location in the image.
Returns the number of pixels on the line between the ending points. */
CVAPI(int) cvInitLineIterator( const CvArr* image, CvPoint pt1, CvPoint pt2,
CvLineIterator* line_iterator,
int connectivity CV_DEFAULT(8),
int left_to_right CV_DEFAULT(0));
/* Moves iterator to the next line point */
#define CV_NEXT_LINE_POINT( line_iterator ) \
{ \
int _line_iterator_mask = (line_iterator).err < 0 ? -1 : 0; \
(line_iterator).err += (line_iterator).minus_delta + \
((line_iterator).plus_delta & _line_iterator_mask); \
(line_iterator).ptr += (line_iterator).minus_step + \
((line_iterator).plus_step & _line_iterator_mask); \
}
/* basic font types */
#define CV_FONT_HERSHEY_SIMPLEX 0
#define CV_FONT_HERSHEY_PLAIN 1
#define CV_FONT_HERSHEY_DUPLEX 2
#define CV_FONT_HERSHEY_COMPLEX 3
#define CV_FONT_HERSHEY_TRIPLEX 4
#define CV_FONT_HERSHEY_COMPLEX_SMALL 5
#define CV_FONT_HERSHEY_SCRIPT_SIMPLEX 6
#define CV_FONT_HERSHEY_SCRIPT_COMPLEX 7
/* font flags */
#define CV_FONT_ITALIC 16
#define CV_FONT_VECTOR0 CV_FONT_HERSHEY_SIMPLEX
/* Font structure */
typedef struct CvFont
{
const char* nameFont; //Qt:nameFont
CvScalar color; //Qt:ColorFont -> cvScalar(blue_component, green_component, red\_component[, alpha_component])
int font_face; //Qt: bool italic /* =CV_FONT_* */
const int* ascii; /* font data and metrics */
const int* greek;
const int* cyrillic;
float hscale, vscale;
float shear; /* slope coefficient: 0 - normal, >0 - italic */
int thickness; //Qt: weight /* letters thickness */
float dx; /* horizontal interval between letters */
int line_type; //Qt: PointSize
}
CvFont;
/* Initializes font structure used further in cvPutText */
CVAPI(void) cvInitFont( CvFont* font, int font_face,
double hscale, double vscale,
double shear CV_DEFAULT(0),
int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8));
CV_INLINE CvFont cvFont( double scale, int thickness CV_DEFAULT(1) )
{
CvFont font;
cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, scale, scale, 0, thickness, CV_AA );
return font;
}
/* Renders text stroke with specified font and color at specified location.
CvFont should be initialized with cvInitFont */
CVAPI(void) cvPutText( CvArr* img, const char* text, CvPoint org,
const CvFont* font, CvScalar color );
/* Calculates bounding box of text stroke (useful for alignment) */
CVAPI(void) cvGetTextSize( const char* text_string, const CvFont* font,
CvSize* text_size, int* baseline );
/* Unpacks color value, if arrtype is CV_8UC?, <color> is treated as
packed color value, otherwise the first channels (depending on arrtype)
of destination scalar are set to the same value = <color> */
CVAPI(CvScalar) cvColorToScalar( double packed_color, int arrtype );
/* Returns the polygon points which make up the given ellipse. The ellipse is define by
the box of size 'axes' rotated 'angle' around the 'center'. A partial sweep
of the ellipse arc can be done by spcifying arc_start and arc_end to be something
other than 0 and 360, respectively. The input array 'pts' must be large enough to
hold the result. The total number of points stored into 'pts' is returned by this
function. */
CVAPI(int) cvEllipse2Poly( CvPoint center, CvSize axes,
int angle, int arc_start, int arc_end, CvPoint * pts, int delta );
/* Draws contour outlines or filled interiors on the image */
CVAPI(void) cvDrawContours( CvArr *img, CvSeq* contour,
CvScalar external_color, CvScalar hole_color,
int max_level, int thickness CV_DEFAULT(1),
int line_type CV_DEFAULT(8),
CvPoint offset CV_DEFAULT(cvPoint(0,0)));
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

@ -1945,6 +1945,7 @@ static const int* getFontData(int fontFace)
return ascii; return ascii;
} }
extern const char* g_HersheyGlyphs[];
void putText( InputOutputArray _img, const String& text, Point org, void putText( InputOutputArray _img, const String& text, Point org,
int fontFace, double fontScale, Scalar color, int fontFace, double fontScale, Scalar color,

@ -0,0 +1,375 @@
/*
By downloading, copying, installing or using the software you agree to this license.
If you do not agree to this license, do not download, install,
copy or use the software.
License Agreement
For Open Source Computer Vision Library
(3-clause BSD License)
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the names of the copyright holders nor the names of the contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are disclaimed.
In no event shall copyright holders or contributors be liable for any direct,
indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused
and on any theory of liability, whether in contract, strict liability,
or tort (including negligence or otherwise) arising in any way out of
the use of this software, even if advised of the possibility of such damage.
*/
#ifndef __OPENCV_IMGPROC_FILTERENGINE_HPP__
#define __OPENCV_IMGPROC_FILTERENGINE_HPP__
namespace cv
{
//! type of the kernel
enum
{
KERNEL_GENERAL = 0, // the kernel is generic. No any type of symmetry or other properties.
KERNEL_SYMMETRICAL = 1, // kernel[i] == kernel[ksize-i-1] , and the anchor is at the center
KERNEL_ASYMMETRICAL = 2, // kernel[i] == -kernel[ksize-i-1] , and the anchor is at the center
KERNEL_SMOOTH = 4, // all the kernel elements are non-negative and summed to 1
KERNEL_INTEGER = 8 // all the kernel coefficients are integer numbers
};
/*!
The Base Class for 1D or Row-wise Filters
This is the base class for linear or non-linear filters that process 1D data.
In particular, such filters are used for the "horizontal" filtering parts in separable filters.
Several functions in OpenCV return Ptr<BaseRowFilter> for the specific types of filters,
and those pointers can be used directly or within cv::FilterEngine.
*/
class BaseRowFilter
{
public:
//! the default constructor
BaseRowFilter();
//! the destructor
virtual ~BaseRowFilter();
//! the filtering operator. Must be overrided in the derived classes. The horizontal border interpolation is done outside of the class.
virtual void operator()(const uchar* src, uchar* dst, int width, int cn) = 0;
int ksize;
int anchor;
};
/*!
The Base Class for Column-wise Filters
This is the base class for linear or non-linear filters that process columns of 2D arrays.
Such filters are used for the "vertical" filtering parts in separable filters.
Several functions in OpenCV return Ptr<BaseColumnFilter> for the specific types of filters,
and those pointers can be used directly or within cv::FilterEngine.
Unlike cv::BaseRowFilter, cv::BaseColumnFilter may have some context information,
i.e. box filter keeps the sliding sum of elements. To reset the state BaseColumnFilter::reset()
must be called (e.g. the method is called by cv::FilterEngine)
*/
class BaseColumnFilter
{
public:
//! the default constructor
BaseColumnFilter();
//! the destructor
virtual ~BaseColumnFilter();
//! the filtering operator. Must be overrided in the derived classes. The vertical border interpolation is done outside of the class.
virtual void operator()(const uchar** src, uchar* dst, int dststep, int dstcount, int width) = 0;
//! resets the internal buffers, if any
virtual void reset();
int ksize;
int anchor;
};
/*!
The Base Class for Non-Separable 2D Filters.
This is the base class for linear or non-linear 2D filters.
Several functions in OpenCV return Ptr<BaseFilter> for the specific types of filters,
and those pointers can be used directly or within cv::FilterEngine.
Similar to cv::BaseColumnFilter, the class may have some context information,
that should be reset using BaseFilter::reset() method before processing the new array.
*/
class BaseFilter
{
public:
//! the default constructor
BaseFilter();
//! the destructor
virtual ~BaseFilter();
//! the filtering operator. The horizontal and the vertical border interpolation is done outside of the class.
virtual void operator()(const uchar** src, uchar* dst, int dststep, int dstcount, int width, int cn) = 0;
//! resets the internal buffers, if any
virtual void reset();
Size ksize;
Point anchor;
};
/*!
The Main Class for Image Filtering.
The class can be used to apply an arbitrary filtering operation to an image.
It contains all the necessary intermediate buffers, it computes extrapolated values
of the "virtual" pixels outside of the image etc.
Pointers to the initialized cv::FilterEngine instances
are returned by various OpenCV functions, such as cv::createSeparableLinearFilter(),
cv::createLinearFilter(), cv::createGaussianFilter(), cv::createDerivFilter(),
cv::createBoxFilter() and cv::createMorphologyFilter().
Using the class you can process large images by parts and build complex pipelines
that include filtering as some of the stages. If all you need is to apply some pre-defined
filtering operation, you may use cv::filter2D(), cv::erode(), cv::dilate() etc.
functions that create FilterEngine internally.
Here is the example on how to use the class to implement Laplacian operator, which is the sum of
second-order derivatives. More complex variant for different types is implemented in cv::Laplacian().
\code
void laplace_f(const Mat& src, Mat& dst)
{
CV_Assert( src.type() == CV_32F );
// make sure the destination array has the proper size and type
dst.create(src.size(), src.type());
// get the derivative and smooth kernels for d2I/dx2.
// for d2I/dy2 we could use the same kernels, just swapped
Mat kd, ks;
getSobelKernels( kd, ks, 2, 0, ksize, false, ktype );
// let's process 10 source rows at once
int DELTA = std::min(10, src.rows);
Ptr<FilterEngine> Fxx = createSeparableLinearFilter(src.type(),
dst.type(), kd, ks, Point(-1,-1), 0, borderType, borderType, Scalar() );
Ptr<FilterEngine> Fyy = createSeparableLinearFilter(src.type(),
dst.type(), ks, kd, Point(-1,-1), 0, borderType, borderType, Scalar() );
int y = Fxx->start(src), dsty = 0, dy = 0;
Fyy->start(src);
const uchar* sptr = src.data + y*src.step;
// allocate the buffers for the spatial image derivatives;
// the buffers need to have more than DELTA rows, because at the
// last iteration the output may take max(kd.rows-1,ks.rows-1)
// rows more than the input.
Mat Ixx( DELTA + kd.rows - 1, src.cols, dst.type() );
Mat Iyy( DELTA + kd.rows - 1, src.cols, dst.type() );
// inside the loop we always pass DELTA rows to the filter
// (note that the "proceed" method takes care of possibe overflow, since
// it was given the actual image height in the "start" method)
// on output we can get:
// * < DELTA rows (the initial buffer accumulation stage)
// * = DELTA rows (settled state in the middle)
// * > DELTA rows (then the input image is over, but we generate
// "virtual" rows using the border mode and filter them)
// this variable number of output rows is dy.
// dsty is the current output row.
// sptr is the pointer to the first input row in the portion to process
for( ; dsty < dst.rows; sptr += DELTA*src.step, dsty += dy )
{
Fxx->proceed( sptr, (int)src.step, DELTA, Ixx.data, (int)Ixx.step );
dy = Fyy->proceed( sptr, (int)src.step, DELTA, d2y.data, (int)Iyy.step );
if( dy > 0 )
{
Mat dstripe = dst.rowRange(dsty, dsty + dy);
add(Ixx.rowRange(0, dy), Iyy.rowRange(0, dy), dstripe);
}
}
}
\endcode
*/
class FilterEngine
{
public:
//! the default constructor
FilterEngine();
//! the full constructor. Either _filter2D or both _rowFilter and _columnFilter must be non-empty.
FilterEngine(const Ptr<BaseFilter>& _filter2D,
const Ptr<BaseRowFilter>& _rowFilter,
const Ptr<BaseColumnFilter>& _columnFilter,
int srcType, int dstType, int bufType,
int _rowBorderType = BORDER_REPLICATE,
int _columnBorderType = -1,
const Scalar& _borderValue = Scalar());
//! the destructor
virtual ~FilterEngine();
//! reinitializes the engine. The previously assigned filters are released.
void init(const Ptr<BaseFilter>& _filter2D,
const Ptr<BaseRowFilter>& _rowFilter,
const Ptr<BaseColumnFilter>& _columnFilter,
int srcType, int dstType, int bufType,
int _rowBorderType = BORDER_REPLICATE,
int _columnBorderType = -1,
const Scalar& _borderValue = Scalar());
//! starts filtering of the specified ROI of an image of size wholeSize.
virtual int start(Size wholeSize, Rect roi, int maxBufRows = -1);
//! starts filtering of the specified ROI of the specified image.
virtual int start(const Mat& src, const Rect& srcRoi = Rect(0,0,-1,-1),
bool isolated = false, int maxBufRows = -1);
//! processes the next srcCount rows of the image.
virtual int proceed(const uchar* src, int srcStep, int srcCount,
uchar* dst, int dstStep);
//! applies filter to the specified ROI of the image. if srcRoi=(0,0,-1,-1), the whole image is filtered.
virtual void apply( const Mat& src, Mat& dst,
const Rect& srcRoi = Rect(0,0,-1,-1),
Point dstOfs = Point(0,0),
bool isolated = false);
//! returns true if the filter is separable
bool isSeparable() const { return !filter2D; }
//! returns the number
int remainingInputRows() const;
int remainingOutputRows() const;
int srcType;
int dstType;
int bufType;
Size ksize;
Point anchor;
int maxWidth;
Size wholeSize;
Rect roi;
int dx1;
int dx2;
int rowBorderType;
int columnBorderType;
std::vector<int> borderTab;
int borderElemSize;
std::vector<uchar> ringBuf;
std::vector<uchar> srcRow;
std::vector<uchar> constBorderValue;
std::vector<uchar> constBorderRow;
int bufStep;
int startY;
int startY0;
int endY;
int rowCount;
int dstY;
std::vector<uchar*> rows;
Ptr<BaseFilter> filter2D;
Ptr<BaseRowFilter> rowFilter;
Ptr<BaseColumnFilter> columnFilter;
};
//! returns type (one of KERNEL_*) of 1D or 2D kernel specified by its coefficients.
int getKernelType(InputArray kernel, Point anchor);
//! returns the primitive row filter with the specified kernel
Ptr<BaseRowFilter> getLinearRowFilter(int srcType, int bufType,
InputArray kernel, int anchor,
int symmetryType);
//! returns the primitive column filter with the specified kernel
Ptr<BaseColumnFilter> getLinearColumnFilter(int bufType, int dstType,
InputArray kernel, int anchor,
int symmetryType, double delta = 0,
int bits = 0);
//! returns 2D filter with the specified kernel
Ptr<BaseFilter> getLinearFilter(int srcType, int dstType,
InputArray kernel,
Point anchor = Point(-1,-1),
double delta = 0, int bits = 0);
//! returns the separable linear filter engine
Ptr<FilterEngine> createSeparableLinearFilter(int srcType, int dstType,
InputArray rowKernel, InputArray columnKernel,
Point anchor = Point(-1,-1), double delta = 0,
int rowBorderType = BORDER_DEFAULT,
int columnBorderType = -1,
const Scalar& borderValue = Scalar());
//! returns the non-separable linear filter engine
Ptr<FilterEngine> createLinearFilter(int srcType, int dstType,
InputArray kernel, Point _anchor = Point(-1,-1),
double delta = 0, int rowBorderType = BORDER_DEFAULT,
int columnBorderType = -1, const Scalar& borderValue = Scalar());
//! returns the Gaussian filter engine
Ptr<FilterEngine> createGaussianFilter( int type, Size ksize,
double sigma1, double sigma2 = 0,
int borderType = BORDER_DEFAULT);
//! returns filter engine for the generalized Sobel operator
Ptr<FilterEngine> createDerivFilter( int srcType, int dstType,
int dx, int dy, int ksize,
int borderType = BORDER_DEFAULT );
//! returns horizontal 1D box filter
Ptr<BaseRowFilter> getRowSumFilter(int srcType, int sumType,
int ksize, int anchor = -1);
//! returns vertical 1D box filter
Ptr<BaseColumnFilter> getColumnSumFilter( int sumType, int dstType,
int ksize, int anchor = -1,
double scale = 1);
//! returns box filter engine
Ptr<FilterEngine> createBoxFilter( int srcType, int dstType, Size ksize,
Point anchor = Point(-1,-1),
bool normalize = true,
int borderType = BORDER_DEFAULT);
//! returns horizontal 1D morphological filter
Ptr<BaseRowFilter> getMorphologyRowFilter(int op, int type, int ksize, int anchor = -1);
//! returns vertical 1D morphological filter
Ptr<BaseColumnFilter> getMorphologyColumnFilter(int op, int type, int ksize, int anchor = -1);
//! returns 2D morphological filter
Ptr<BaseFilter> getMorphologyFilter(int op, int type, InputArray kernel,
Point anchor = Point(-1,-1));
//! returns morphological filter engine. Only MORPH_ERODE and MORPH_DILATE are supported.
CV_EXPORTS Ptr<FilterEngine> createMorphologyFilter(int op, int type, InputArray kernel,
Point anchor = Point(-1,-1), int rowBorderType = BORDER_CONSTANT,
int columnBorderType = -1,
const Scalar& borderValue = morphologyDefaultBorderValue());
static inline Point normalizeAnchor( Point anchor, Size ksize )
{
if( anchor.x == -1 )
anchor.x = ksize.width/2;
if( anchor.y == -1 )
anchor.y = ksize.height/2;
CV_Assert( anchor.inside(Rect(0, 0, ksize.width, ksize.height)) );
return anchor;
}
void preprocess2DKernel( const Mat& kernel, std::vector<Point>& coords, std::vector<uchar>& coeffs );
void crossCorr( const Mat& src, const Mat& templ, Mat& dst,
Size corrsize, int ctype,
Point anchor=Point(0,0), double delta=0,
int borderType=BORDER_REFLECT_101 );
}
#endif

File diff suppressed because it is too large Load Diff

@ -78,38 +78,6 @@ extern const float icv8x32fTab_cv[];
extern const float icv8x32fSqrTab[]; extern const float icv8x32fSqrTab[];
#define CV_8TO32F_SQR(x) icv8x32fSqrTab[(x)+128] #define CV_8TO32F_SQR(x) icv8x32fSqrTab[(x)+128]
namespace cv
{
static inline Point normalizeAnchor( Point anchor, Size ksize )
{
if( anchor.x == -1 )
anchor.x = ksize.width/2;
if( anchor.y == -1 )
anchor.y = ksize.height/2;
CV_Assert( anchor.inside(Rect(0, 0, ksize.width, ksize.height)) );
return anchor;
}
void preprocess2DKernel( const Mat& kernel, std::vector<Point>& coords, std::vector<uchar>& coeffs );
void crossCorr( const Mat& src, const Mat& templ, Mat& dst,
Size corrsize, int ctype,
Point anchor=Point(0,0), double delta=0,
int borderType=BORDER_REFLECT_101 );
}
typedef struct CvPyramid
{
uchar **ptr;
CvSize *sz;
double *rate;
int *step;
uchar *state;
int level;
}
CvPyramid;
#define CV_COPY( dst, src, len, idx ) \ #define CV_COPY( dst, src, len, idx ) \
for( (idx) = 0; (idx) < (len); (idx)++) (dst)[idx] = (src)[idx] for( (idx) = 0; (idx) < (len); (idx)++) (dst)[idx] = (src)[idx]
@ -123,5 +91,6 @@ CvPyramid;
#define CV_CALC_MAX(a, b) if((a) < (b)) (a) = (b) #define CV_CALC_MAX(a, b) if((a) < (b)) (a) = (b)
#include "_geom.h" #include "_geom.h"
#include "filterengine.hpp"
#endif /*__OPENCV_CV_INTERNAL_H_*/ #endif /*__OPENCV_CV_INTERNAL_H_*/

@ -1,2 +0,0 @@
set(the_description "Generic optimization")
ocv_define_module(optim opencv_core)

@ -1,162 +0,0 @@
Downhill Simplex Method
=======================
.. highlight:: cpp
optim::DownhillSolver
---------------------------------
.. ocv:class:: optim::DownhillSolver
This class is used to perform the non-linear non-constrained *minimization* of a function, defined on an *n*-dimensional Euclidean space,
using the **Nelder-Mead method**, also known as **downhill simplex method**. The basic idea about the method can be obtained from
(`http://en.wikipedia.org/wiki/Nelder-Mead\_method <http://en.wikipedia.org/wiki/Nelder-Mead_method>`_). It should be noted, that
this method, although deterministic, is rather a heuristic and therefore may converge to a local minima, not necessary a global one.
It is iterative optimization technique, which at each step uses an information about the values of a function evaluated only at
*n+1* points, arranged as a *simplex* in *n*-dimensional space (hence the second name of the method). At each step new point is
chosen to evaluate function at, obtained value is compared with previous ones and based on this information simplex changes it's shape
, slowly moving to the local minimum. Thus this method is using *only* function values to make decision, on contrary to, say, Nonlinear
Conjugate Gradient method (which is also implemented in ``optim``).
Algorithm stops when the number of function evaluations done exceeds ``termcrit.maxCount``, when the function values at the
vertices of simplex are within ``termcrit.epsilon`` range or simplex becomes so small that it
can enclosed in a box with ``termcrit.epsilon`` sides, whatever comes first, for some defined by user
positive integer ``termcrit.maxCount`` and positive non-integer ``termcrit.epsilon``.
::
class CV_EXPORTS Solver : public Algorithm
{
public:
class CV_EXPORTS Function
{
public:
virtual ~Function() {}
virtual double calc(const double* x) const = 0;
virtual void getGradient(const double* /*x*/,double* /*grad*/) {}
};
virtual Ptr<Function> getFunction() const = 0;
virtual void setFunction(const Ptr<Function>& f) = 0;
virtual TermCriteria getTermCriteria() const = 0;
virtual void setTermCriteria(const TermCriteria& termcrit) = 0;
// x contain the initial point before the call and the minima position (if algorithm converged) after. x is assumed to be (something that
// after getMat() will return) row-vector or column-vector. *It's size and should
// be consisted with previous dimensionality data given, if any (otherwise, it determines dimensionality)*
virtual double minimize(InputOutputArray x) = 0;
};
class CV_EXPORTS DownhillSolver : public Solver
{
public:
//! returns row-vector, even if the column-vector was given
virtual void getInitStep(OutputArray step) const=0;
//!This should be called at least once before the first call to minimize() and step is assumed to be (something that
//! after getMat() will return) row-vector or column-vector. *It's dimensionality determines the dimensionality of a problem.*
virtual void setInitStep(InputArray step)=0;
};
It should be noted, that ``optim::DownhillSolver`` is a derivative of the abstract interface ``optim::Solver``, which in
turn is derived from the ``Algorithm`` interface and is used to encapsulate the functionality, common to all non-linear optimization
algorithms in the ``optim`` module.
optim::DownhillSolver::getFunction
--------------------------------------------
Getter for the optimized function. The optimized function is represented by ``Solver::Function`` interface, which requires
derivatives to implement the sole method ``calc(double*)`` to evaluate the function.
.. ocv:function:: Ptr<Solver::Function> optim::DownhillSolver::getFunction()
:return: Smart-pointer to an object that implements ``Solver::Function`` interface - it represents the function that is being optimized. It can be empty, if no function was given so far.
optim::DownhillSolver::setFunction
-----------------------------------------------
Setter for the optimized function. *It should be called at least once before the call to* ``DownhillSolver::minimize()``, as
default value is not usable.
.. ocv:function:: void optim::DownhillSolver::setFunction(const Ptr<Solver::Function>& f)
:param f: The new function to optimize.
optim::DownhillSolver::getTermCriteria
----------------------------------------------------
Getter for the previously set terminal criteria for this algorithm.
.. ocv:function:: TermCriteria optim::DownhillSolver::getTermCriteria()
:return: Deep copy of the terminal criteria used at the moment.
optim::DownhillSolver::setTermCriteria
------------------------------------------
Set terminal criteria for downhill simplex method. Two things should be noted. First, this method *is not necessary* to be called
before the first call to ``DownhillSolver::minimize()``, as the default value is sensible. Second, the method will raise an error
if ``termcrit.type!=(TermCriteria::MAX_ITER+TermCriteria::EPS)``, ``termcrit.epsilon<=0`` or ``termcrit.maxCount<=0``. That is,
both ``epsilon`` and ``maxCount`` should be set to positive values (non-integer and integer respectively) and they represent
tolerance and maximal number of function evaluations that is allowed.
Algorithm stops when the number of function evaluations done exceeds ``termcrit.maxCount``, when the function values at the
vertices of simplex are within ``termcrit.epsilon`` range or simplex becomes so small that it
can enclosed in a box with ``termcrit.epsilon`` sides, whatever comes first.
.. ocv:function:: void optim::DownhillSolver::setTermCriteria(const TermCriteria& termcrit)
:param termcrit: Terminal criteria to be used, represented as ``TermCriteria`` structure (defined elsewhere in openCV). Mind you, that it should meet ``(termcrit.type==(TermCriteria::MAX_ITER+TermCriteria::EPS) && termcrit.epsilon>0 && termcrit.maxCount>0)``, otherwise the error will be raised.
optim::DownhillSolver::getInitStep
-----------------------------------
Returns the initial step that will be used in downhill simplex algorithm. See the description
of corresponding setter (follows next) for the meaning of this parameter.
.. ocv:function:: void optim::getInitStep(OutputArray step)
:param step: Initial step that will be used in algorithm. Note, that although corresponding setter accepts column-vectors as well as row-vectors, this method will return a row-vector.
optim::DownhillSolver::setInitStep
----------------------------------
Sets the initial step that will be used in downhill simplex algorithm. Step, together with initial point (givin in ``DownhillSolver::minimize``)
are two *n*-dimensional vectors that are used to determine the shape of initial simplex. Roughly said, initial point determines the position
of a simplex (it will become simplex's centroid), while step determines the spread (size in each dimension) of a simplex. To be more precise,
if :math:`s,x_0\in\mathbb{R}^n` are the initial step and initial point respectively, the vertices of a simplex will be: :math:`v_0:=x_0-\frac{1}{2}
s` and :math:`v_i:=x_0+s_i` for :math:`i=1,2,\dots,n` where :math:`s_i` denotes projections of the initial step of *n*-th coordinate (the result
of projection is treated to be vector given by :math:`s_i:=e_i\cdot\left<e_i\cdot s\right>`, where :math:`e_i` form canonical basis)
.. ocv:function:: void optim::setInitStep(InputArray step)
:param step: Initial step that will be used in algorithm. Roughly said, it determines the spread (size in each dimension) of an initial simplex.
optim::DownhillSolver::minimize
-----------------------------------
The main method of the ``DownhillSolver``. It actually runs the algorithm and performs the minimization. The sole input parameter determines the
centroid of the starting simplex (roughly, it tells where to start), all the others (terminal criteria, initial step, function to be minimized)
are supposed to be set via the setters before the call to this method or the default values (not always sensible) will be used.
.. ocv:function:: double optim::DownhillSolver::minimize(InputOutputArray x)
:param x: The initial point, that will become a centroid of an initial simplex. After the algorithm will terminate, it will be setted to the point where the algorithm stops, the point of possible minimum.
:return: The value of a function at the point found.
optim::createDownhillSolver
------------------------------------
This function returns the reference to the ready-to-use ``DownhillSolver`` object. All the parameters are optional, so this procedure can be called
even without parameters at all. In this case, the default values will be used. As default value for terminal criteria are the only sensible ones,
``DownhillSolver::setFunction()`` and ``DownhillSolver::setInitStep()`` should be called upon the obtained object, if the respective parameters
were not given to ``createDownhillSolver()``. Otherwise, the two ways (give parameters to ``createDownhillSolver()`` or miss them out and call the
``DownhillSolver::setFunction()`` and ``DownhillSolver::setInitStep()``) are absolutely equivalent (and will drop the same errors in the same way,
should invalid input be detected).
.. ocv:function:: Ptr<optim::DownhillSolver> optim::createDownhillSolver(const Ptr<Solver::Function>& f,InputArray initStep, TermCriteria termcrit)
:param f: Pointer to the function that will be minimized, similarly to the one you submit via ``DownhillSolver::setFunction``.
:param step: Initial step, that will be used to construct the initial simplex, similarly to the one you submit via ``DownhillSolver::setInitStep``.
:param termcrit: Terminal criteria to the algorithm, similarly to the one you submit via ``DownhillSolver::setTermCriteria``.

@ -1,48 +0,0 @@
Linear Programming
==================
.. highlight:: cpp
optim::solveLP
--------------------
Solve given (non-integer) linear programming problem using the Simplex Algorithm (Simplex Method).
What we mean here by "linear programming problem" (or LP problem, for short) can be
formulated as:
.. math::
\mbox{Maximize } c\cdot x\\
\mbox{Subject to:}\\
Ax\leq b\\
x\geq 0
Where :math:`c` is fixed *1*-by-*n* row-vector, :math:`A` is fixed *m*-by-*n* matrix, :math:`b` is fixed *m*-by-*1* column vector and
:math:`x` is an arbitrary *n*-by-*1* column vector, which satisfies the constraints.
Simplex algorithm is one of many algorithms that are designed to handle this sort of problems efficiently. Although it is not optimal in theoretical
sense (there exist algorithms that can solve any problem written as above in polynomial type, while simplex method degenerates to exponential time
for some special cases), it is well-studied, easy to implement and is shown to work well for real-life purposes.
The particular implementation is taken almost verbatim from **Introduction to Algorithms, third edition**
by T. H. Cormen, C. E. Leiserson, R. L. Rivest and Clifford Stein. In particular, the Bland's rule
(`http://en.wikipedia.org/wiki/Bland%27s\_rule <http://en.wikipedia.org/wiki/Bland%27s_rule>`_) is used to prevent cycling.
.. ocv:function:: int optim::solveLP(const Mat& Func, const Mat& Constr, Mat& z)
:param Func: This row-vector corresponds to :math:`c` in the LP problem formulation (see above). It should contain 32- or 64-bit floating point numbers. As a convenience, column-vector may be also submitted, in the latter case it is understood to correspond to :math:`c^T`.
:param Constr: *m*-by-*n\+1* matrix, whose rightmost column corresponds to :math:`b` in formulation above and the remaining to :math:`A`. It should containt 32- or 64-bit floating point numbers.
:param z: The solution will be returned here as a column-vector - it corresponds to :math:`c` in the formulation above. It will contain 64-bit floating point numbers.
:return: One of the return codes:
::
//!the return codes for solveLP() function
enum
{
SOLVELP_UNBOUNDED = -2, //problem is unbounded (target function can achieve arbitrary high values)
SOLVELP_UNFEASIBLE = -1, //problem is unfeasible (there are no points that satisfy all the constraints imposed)
SOLVELP_SINGLE = 0, //there is only one maximum for target function
SOLVELP_MULTI = 1 //there are multiple maxima for target function - the arbitrary one is returned
};

@ -1,136 +0,0 @@
Nonlinear Conjugate Gradient
===============================
.. highlight:: cpp
optim::ConjGradSolver
---------------------------------
.. ocv:class:: optim::ConjGradSolver
This class is used to perform the non-linear non-constrained *minimization* of a function with *known gradient*
, defined on an *n*-dimensional Euclidean space,
using the **Nonlinear Conjugate Gradient method**. The implementation was done based on the beautifully clear explanatory article `An Introduction to the Conjugate Gradient Method Without the Agonizing Pain <http://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf>`_
by Jonathan Richard Shewchuk. The method can be seen as an adaptation of a standard Conjugate Gradient method (see, for example
`http://en.wikipedia.org/wiki/Conjugate_gradient_method <http://en.wikipedia.org/wiki/Conjugate_gradient_method>`_) for numerically solving the
systems of linear equations.
It should be noted, that
this method, although deterministic, is rather a heuristic method and therefore may converge to a local minima, not necessary a global one. What
is even more disastrous, most of its behaviour is ruled by gradient, therefore it essentially cannot distinguish between local minima and maxima.
Therefore, if it starts sufficiently near to the local maximum, it may converge to it. Another obvious restriction is that it should be possible
to compute the gradient of a function at any point, thus it is preferable to have analytic expression for gradient and computational burden
should be born by the user.
The latter responsibility is accompilished via the ``getGradient(const double* x,double* grad)`` method of a
``Solver::Function`` interface (which represents function that is being optimized). This method takes point a point in *n*-dimensional space
(first argument represents the array of coordinates of that point) and comput its gradient (it should be stored in the second argument as an array).
::
class CV_EXPORTS Solver : public Algorithm
{
public:
class CV_EXPORTS Function
{
public:
virtual ~Function() {}
virtual double calc(const double* x) const = 0;
virtual void getGradient(const double* /*x*/,double* /*grad*/) {}
};
virtual Ptr<Function> getFunction() const = 0;
virtual void setFunction(const Ptr<Function>& f) = 0;
virtual TermCriteria getTermCriteria() const = 0;
virtual void setTermCriteria(const TermCriteria& termcrit) = 0;
// x contain the initial point before the call and the minima position (if algorithm converged) after. x is assumed to be (something that
// after getMat() will return) row-vector or column-vector. *It's size and should
// be consisted with previous dimensionality data given, if any (otherwise, it determines dimensionality)*
virtual double minimize(InputOutputArray x) = 0;
};
class CV_EXPORTS ConjGradSolver : public Solver{
};
Note, that class ``ConjGradSolver`` thus does not add any new methods to the basic ``Solver`` interface.
optim::ConjGradSolver::getFunction
--------------------------------------------
Getter for the optimized function. The optimized function is represented by ``Solver::Function`` interface, which requires
derivatives to implement the method ``calc(double*)`` to evaluate the function. It should be emphasized once more, that since Nonlinear
Conjugate Gradient method requires gradient to be computable in addition to the function values,
``getGradient(const double* x,double* grad)`` method of a ``Solver::Function`` interface should be also implemented meaningfully.
.. ocv:function:: Ptr<Solver::Function> optim::ConjGradSolver::getFunction()
:return: Smart-pointer to an object that implements ``Solver::Function`` interface - it represents the function that is being optimized. It can be empty, if no function was given so far.
optim::ConjGradSolver::setFunction
-----------------------------------------------
Setter for the optimized function. *It should be called at least once before the call to* ``ConjGradSolver::minimize()``, as
default value is not usable.
.. ocv:function:: void optim::ConjGradSolver::setFunction(const Ptr<Solver::Function>& f)
:param f: The new function to optimize.
optim::ConjGradSolver::getTermCriteria
----------------------------------------------------
Getter for the previously set terminal criteria for this algorithm.
.. ocv:function:: TermCriteria optim::ConjGradSolver::getTermCriteria()
:return: Deep copy of the terminal criteria used at the moment.
optim::ConjGradSolver::setTermCriteria
------------------------------------------
Set terminal criteria for downhill simplex method. Two things should be noted. First, this method *is not necessary* to be called
before the first call to ``ConjGradSolver::minimize()``, as the default value is sensible. Second, the method will raise an error
if ``termcrit.type!=(TermCriteria::MAX_ITER+TermCriteria::EPS)`` and ``termcrit.type!=TermCriteria::MAX_ITER``. This means that termination criteria
has to restrict maximum number of iterations to be done and may optionally allow algorithm to stop earlier if certain tolerance
is achieved (what we mean by "tolerance is achieved" will be clarified below). If ``termcrit`` restricts both tolerance and maximum iteration
number, both ``termcrit.epsilon`` and ``termcrit.maxCount`` should be positive. In case, if ``termcrit.type==TermCriteria::MAX_ITER``,
only member ``termcrit.maxCount`` is required to be positive and in this case algorithm will just work for required number of iterations.
In current implementation, "tolerance is achieved" means that we have arrived at the point where the :math:`L_2`-norm of the gradient is less
than the tolerance value.
.. ocv:function:: void optim::ConjGradSolver::setTermCriteria(const TermCriteria& termcrit)
:param termcrit: Terminal criteria to be used, represented as ``TermCriteria`` structure (defined elsewhere in openCV). Mind you, that it should meet ``termcrit.type==(TermCriteria::MAX_ITER+TermCriteria::EPS) && termcrit.epsilon>0 && termcrit.maxCount>0`` or ``termcrit.type==TermCriteria::MAX_ITER) && termcrit.maxCount>0``, otherwise the error will be raised.
optim::ConjGradSolver::minimize
-----------------------------------
The main method of the ``ConjGradSolver``. It actually runs the algorithm and performs the minimization. The sole input parameter determines the
centroid of the starting simplex (roughly, it tells where to start), all the others (terminal criteria and function to be minimized)
are supposed to be set via the setters before the call to this method or the default values (not always sensible) will be used. Sometimes it may
throw an error, if these default values cannot be used (say, you forgot to set the function to minimize and default value, that is, empty function,
cannot be used).
.. ocv:function:: double optim::ConjGradSolver::minimize(InputOutputArray x)
:param x: The initial point. It is hard to overemphasize how important the choise of initial point is when you are using the heuristic algorithm like this one. Badly chosen initial point can make algorithm converge to (local) maximum instead of minimum, do not converge at all, converge to local minimum instead of global one.
:return: The value of a function at the point found.
optim::createConjGradSolver
------------------------------------
This function returns the reference to the ready-to-use ``ConjGradSolver`` object. All the parameters are optional, so this procedure can be called
even without parameters at all. In this case, the default values will be used. As default value for terminal criteria are the only sensible ones,
``ConjGradSolver::setFunction()`` should be called upon the obtained object, if the function
was not given to ``createConjGradSolver()``. Otherwise, the two ways (submit it to ``createConjGradSolver()`` or miss it out and call the
``ConjGradSolver::setFunction()``) are absolutely equivalent (and will drop the same errors in the same way,
should invalid input be detected).
.. ocv:function:: Ptr<optim::ConjGradSolver> optim::createConjGradSolver(const Ptr<Solver::Function>& f, TermCriteria termcrit)
:param f: Pointer to the function that will be minimized, similarly to the one you submit via ``ConjGradSolver::setFunction``.
:param termcrit: Terminal criteria to the algorithm, similarly to the one you submit via ``ConjGradSolver::setTermCriteria``.

@ -1,13 +0,0 @@
**************************************
optim. Generic numerical optimization
**************************************
.. highlight:: cpp
.. toctree::
:maxdepth: 2
linear_programming
downhill_simplex_method
primal_dual_algorithm
nonlinear_conjugate_gradient

@ -1,48 +0,0 @@
Primal-Dual Algorithm
=======================
.. highlight:: cpp
optim::denoise_TVL1
---------------------------------
Primal-dual algorithm is an algorithm for solving special types of variational
problems (that is, finding a function to minimize some functional)
. As the image denoising, in particular, may be seen as the variational
problem, primal-dual algorithm then can be used to perform denoising and this
is exactly what is implemented.
It should be noted, that this implementation was taken from the July 2013 blog entry [Mordvintsev]_, which also contained
(slightly more general) ready-to-use
source code on Python. Subsequently, that code was rewritten on C++ with the usage of openCV by Vadim Pisarevsky
at the end of July 2013 and finally it was slightly adapted by later authors.
Although the thorough discussion and justification
of the algorithm involved may be found in [ChambolleEtAl]_, it might make sense to skim over it here, following [Mordvintsev]_. To
begin with, we consider the 1-byte gray-level images as the functions from the rectangular domain of pixels
(it may be seen as set :math:`\left\{(x,y)\in\mathbb{N}\times\mathbb{N}\mid 1\leq x\leq n,\;1\leq y\leq m\right\}`
for some :math:`m,\;n\in\mathbb{N}`) into :math:`\{0,1,\dots,255\}`. We shall denote the noised images as :math:`f_i` and with this
view, given some image :math:`x` of the same size, we may measure how bad it is by the formula
.. math::
\left\|\left\|\nabla x\right\|\right\| + \lambda\sum_i\left\|\left\|x-f_i\right\|\right\|
:math:`\|\|\cdot\|\|` here denotes :math:`L_2`-norm and as you see, the first addend states that we want our image to be smooth
(ideally, having zero gradient, thus being constant) and the second states that we want our result to be close to the observations we've got.
If we treat :math:`x` as a function, this is exactly the functional what we seek to minimize and here the Primal-Dual algorithm comes
into play.
.. ocv:function:: void optim::denoise_TVL1(const std::vector<Mat>& observations,Mat& result, double lambda, int niters)
:param observations: This array should contain one or more noised versions of the image that is to be restored.
:param result: Here the denoised image will be stored. There is no need to do pre-allocation of storage space, as it will be automatically allocated, if necessary.
:param lambda: Corresponds to :math:`\lambda` in the formulas above. As it is enlarged, the smooth (blurred) images are treated more favorably than detailed (but maybe more noised) ones. Roughly speaking, as it becomes smaller, the result will be more blur but more sever outliers will be removed.
:param niters: Number of iterations that the algorithm will run. Of course, as more iterations as better, but it is hard to quantitatively refine this statement, so just use the default and increase it if the results are poor.
.. [ChambolleEtAl] A. Chambolle, V. Caselles, M. Novaga, D. Cremers and T. Pock, An Introduction to Total Variation for Image Analysis, http://hal.archives-ouvertes.fr/docs/00/43/75/81/PDF/preprint.pdf (pdf)
.. [Mordvintsev] Alexander Mordvintsev, ROF and TV-L1 denoising with Primal-Dual algorithm, http://znah.net/rof-and-tv-l1-denoising-with-primal-dual-algorithm.html (blog entry)

@ -1,46 +0,0 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the OpenCV Foundation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifdef __OPENCV_BUILD
#error this is a compatibility header which should not be used inside the OpenCV library
#endif
#include "opencv2/optim.hpp"

@ -1,58 +0,0 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the OpenCV Foundation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
namespace cv{namespace optim{
#ifdef ALEX_DEBUG
#define dprintf(x) printf x
static void print_matrix(const Mat& x){
printf("\ttype:%d vs %d,\tsize: %d-on-%d\n",x.type(),CV_64FC1,x.rows,x.cols);
for(int i=0;i<x.rows;i++){
printf("\t[");
for(int j=0;j<x.cols;j++){
printf("%g, ",x.at<double>(i,j));
}
printf("]\n");
}
}
#else
#define dprintf(x)
#define print_matrix(x)
#endif
}}

@ -1,47 +0,0 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the OpenCV Foundation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef __OPENCV_PRECOMP_H__
#define __OPENCV_PRECOMP_H__
#include "opencv2/optim.hpp"
#endif

@ -1,3 +0,0 @@
#include "test_precomp.hpp"
CV_TEST_MAIN("cv")

@ -1,16 +0,0 @@
#ifdef __GNUC__
# pragma GCC diagnostic ignored "-Wmissing-declarations"
# if defined __clang__ || defined __APPLE__
# pragma GCC diagnostic ignored "-Wmissing-prototypes"
# pragma GCC diagnostic ignored "-Wextra"
# endif
#endif
#ifndef __OPENCV_TEST_PRECOMP_HPP__
#define __OPENCV_TEST_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/optim.hpp"
#include "opencv2/imgcodecs.hpp"
#endif

@ -195,3 +195,49 @@ The function converts image to CIELAB colorspace and then separately denoise L a
.. seealso:: .. seealso::
:ocv:func:`fastNlMeansDenoisingColored` :ocv:func:`fastNlMeansDenoisingColored`
denoise_TVL1
---------------------------------
Primal-dual algorithm is an algorithm for solving special types of variational
problems (that is, finding a function to minimize some functional).
As the image denoising, in particular, may be seen as the variational
problem, primal-dual algorithm then can be used to perform denoising and this
is exactly what is implemented.
It should be noted, that this implementation was taken from the July 2013 blog entry [Mordvintsev]_, which also contained
(slightly more general) ready-to-use
source code on Python. Subsequently, that code was rewritten on C++ with the usage of openCV by Vadim Pisarevsky
at the end of July 2013 and finally it was slightly adapted by later authors.
Although the thorough discussion and justification
of the algorithm involved may be found in [ChambolleEtAl]_, it might make sense to skim over it here, following [Mordvintsev]_. To
begin with, we consider the 1-byte gray-level images as the functions from the rectangular domain of pixels
(it may be seen as set :math:`\left\{(x,y)\in\mathbb{N}\times\mathbb{N}\mid 1\leq x\leq n,\;1\leq y\leq m\right\}`
for some :math:`m,\;n\in\mathbb{N}`) into :math:`\{0,1,\dots,255\}`. We shall denote the noised images as :math:`f_i` and with this
view, given some image :math:`x` of the same size, we may measure how bad it is by the formula
.. math::
\left\|\left\|\nabla x\right\|\right\| + \lambda\sum_i\left\|\left\|x-f_i\right\|\right\|
:math:`\|\|\cdot\|\|` here denotes :math:`L_2`-norm and as you see, the first addend states that we want our image to be smooth
(ideally, having zero gradient, thus being constant) and the second states that we want our result to be close to the observations we've got.
If we treat :math:`x` as a function, this is exactly the functional what we seek to minimize and here the Primal-Dual algorithm comes
into play.
.. ocv:function:: void denoise_TVL1(const std::vector<Mat>& observations,Mat& result, double lambda, int niters)
:param observations: This array should contain one or more noised versions of the image that is to be restored.
:param result: Here the denoised image will be stored. There is no need to do pre-allocation of storage space, as it will be automatically allocated, if necessary.
:param lambda: Corresponds to :math:`\lambda` in the formulas above. As it is enlarged, the smooth (blurred) images are treated more favorably than detailed (but maybe more noised) ones. Roughly speaking, as it becomes smaller, the result will be more blur but more sever outliers will be removed.
:param niters: Number of iterations that the algorithm will run. Of course, as more iterations as better, but it is hard to quantitatively refine this statement, so just use the default and increase it if the results are poor.
.. [ChambolleEtAl] A. Chambolle, V. Caselles, M. Novaga, D. Cremers and T. Pock, An Introduction to Total Variation for Image Analysis, http://hal.archives-ouvertes.fr/docs/00/43/75/81/PDF/preprint.pdf (pdf)
.. [Mordvintsev] Alexander Mordvintsev, ROF and TV-L1 denoising with Primal-Dual algorithm, http://znah.net/rof-and-tv-l1-denoising-with-primal-dual-algorithm.html (blog entry)

@ -93,6 +93,8 @@ CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs,
float h = 3, float hColor = 3, float h = 3, float hColor = 3,
int templateWindowSize = 7, int searchWindowSize = 21); int templateWindowSize = 7, int searchWindowSize = 21);
CV_EXPORTS_W void denoise_TVL1(const std::vector<Mat>& observations,Mat& result, double lambda=1.0, int niters=30);
enum { LDR_SIZE = 256 }; enum { LDR_SIZE = 256 };
class CV_EXPORTS_W Tonemap : public Algorithm class CV_EXPORTS_W Tonemap : public Algorithm

@ -39,14 +39,12 @@
// //
//M*/ //M*/
#include "precomp.hpp" #include "precomp.hpp"
#undef ALEX_DEBUG
#include "debug.hpp"
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#define ABSCLIP(val,threshold) MIN(MAX((val),-(threshold)),(threshold)) #define ABSCLIP(val,threshold) MIN(MAX((val),-(threshold)),(threshold))
namespace cv{namespace optim{ namespace cv{
class AddFloatToCharScaled{ class AddFloatToCharScaled{
public: public:
@ -165,4 +163,4 @@ namespace cv{namespace optim{
result.create(X.rows,X.cols,CV_8U); result.create(X.rows,X.cols,CV_8U);
X.convertTo(result, CV_8U, 255); X.convertTo(result, CV_8U, 255);
} }
}} }

@ -70,7 +70,7 @@ void make_spotty(cv::Mat& img,cv::RNG& rng, int r=3,int n=1000)
bool validate_pixel(const cv::Mat& image,int x,int y,uchar val) bool validate_pixel(const cv::Mat& image,int x,int y,uchar val)
{ {
printf("test: image(%d,%d)=%d vs %d - %s\n",x,y,(int)image.at<uchar>(x,y),val,(val==image.at<uchar>(x,y))?"true":"false"); printf("test: image(%d,%d)=%d vs %d - %s\n",x,y,(int)image.at<uchar>(x,y),val,(val==image.at<uchar>(x,y))?"true":"false");
return (image.at<uchar>(x,y)==val); return std::abs(image.at<uchar>(x,y) - val) < 10;
} }
TEST(Optim_denoise_tvl1, regression_basic) TEST(Optim_denoise_tvl1, regression_basic)
@ -89,7 +89,7 @@ TEST(Optim_denoise_tvl1, regression_basic)
} }
//cv::imshow("test", images[0]); //cv::imshow("test", images[0]);
cv::optim::denoise_TVL1(images, res); cv::denoise_TVL1(images, res);
//cv::imshow("denoised", res); //cv::imshow("denoised", res);
//cv::waitKey(); //cv::waitKey();

@ -485,7 +485,7 @@ namespace
bool ocl_process(InputArrayOfArrays src, OutputArray dst, InputArrayOfArrays forwardMotions, bool ocl_process(InputArrayOfArrays src, OutputArray dst, InputArrayOfArrays forwardMotions,
InputArrayOfArrays backwardMotions, int baseIdx); InputArrayOfArrays backwardMotions, int baseIdx);
Ptr<FilterEngine> filter_; //Ptr<FilterEngine> filter_;
int curBlurKernelSize_; int curBlurKernelSize_;
double curBlurSigma_; double curBlurSigma_;
int curSrcType_; int curSrcType_;
@ -559,9 +559,9 @@ namespace
& backwardMotions = *(std::vector<UMat> *)_backwardMotions.getObj(); & backwardMotions = *(std::vector<UMat> *)_backwardMotions.getObj();
// update blur filter and btv weights // update blur filter and btv weights
if (!filter_ || blurKernelSize_ != curBlurKernelSize_ || blurSigma_ != curBlurSigma_ || src[0].type() != curSrcType_) if (blurKernelSize_ != curBlurKernelSize_ || blurSigma_ != curBlurSigma_ || src[0].type() != curSrcType_)
{ {
filter_ = createGaussianFilter(src[0].type(), Size(blurKernelSize_, blurKernelSize_), blurSigma_); //filter_ = createGaussianFilter(src[0].type(), Size(blurKernelSize_, blurKernelSize_), blurSigma_);
curBlurKernelSize_ = blurKernelSize_; curBlurKernelSize_ = blurKernelSize_;
curBlurSigma_ = blurSigma_; curBlurSigma_ = blurSigma_;
curSrcType_ = src[0].type(); curSrcType_ = src[0].type();
@ -662,9 +662,9 @@ namespace
& backwardMotions = *(std::vector<Mat> *)_backwardMotions.getObj(); & backwardMotions = *(std::vector<Mat> *)_backwardMotions.getObj();
// update blur filter and btv weights // update blur filter and btv weights
if (!filter_ || blurKernelSize_ != curBlurKernelSize_ || blurSigma_ != curBlurSigma_ || src[0].type() != curSrcType_) if (blurKernelSize_ != curBlurKernelSize_ || blurSigma_ != curBlurSigma_ || src[0].type() != curSrcType_)
{ {
filter_ = createGaussianFilter(src[0].type(), Size(blurKernelSize_, blurKernelSize_), blurSigma_); //filter_ = createGaussianFilter(src[0].type(), Size(blurKernelSize_, blurKernelSize_), blurSigma_);
curBlurKernelSize_ = blurKernelSize_; curBlurKernelSize_ = blurKernelSize_;
curBlurSigma_ = blurSigma_; curBlurSigma_ = blurSigma_;
curSrcType_ = src[0].type(); curSrcType_ = src[0].type();
@ -709,7 +709,7 @@ namespace
// a = M * Ih // a = M * Ih
remap(highRes_, a_, backwardMaps_[k], noArray(), INTER_NEAREST); remap(highRes_, a_, backwardMaps_[k], noArray(), INTER_NEAREST);
// b = HM * Ih // b = HM * Ih
filter_->apply(a_, b_); GaussianBlur(a_, b_, Size(blurKernelSize_, blurKernelSize_), blurSigma_);
// c = DHM * Ih // c = DHM * Ih
resize(b_, c_, lowResSize, 0, 0, INTER_NEAREST); resize(b_, c_, lowResSize, 0, 0, INTER_NEAREST);
@ -718,7 +718,7 @@ namespace
// a = Dt * diff // a = Dt * diff
upscale(c_, a_, scale_); upscale(c_, a_, scale_);
// b = HtDt * diff // b = HtDt * diff
filter_->apply(a_, b_); GaussianBlur(a_, b_, Size(blurKernelSize_, blurKernelSize_), blurSigma_);
// a = MtHtDt * diff // a = MtHtDt * diff
remap(b_, a_, forwardMaps_[k], noArray(), INTER_NEAREST); remap(b_, a_, forwardMaps_[k], noArray(), INTER_NEAREST);
@ -740,8 +740,6 @@ namespace
void BTVL1_Base::collectGarbage() void BTVL1_Base::collectGarbage()
{ {
filter_.release();
// Mat // Mat
lowResForwardMotions_.clear(); lowResForwardMotions_.clear();
lowResBackwardMotions_.clear(); lowResBackwardMotions_.clear();

Loading…
Cancel
Save