mirror of https://github.com/opencv/opencv.git
removed optim module; moved its functionality to core and photo modules; moved drawing functions from core to imgproc. Removed FilterEngine etc. from public API
parent
a602185fb6
commit
257463719b
44 changed files with 5886 additions and 3186 deletions
@ -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``. |
@ -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; |
||||||
|
} |
@ -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); |
||||||
|
} |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
@ -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
@ -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 |
|
Loading…
Reference in new issue