1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

Merge pull request #29175 from asmorkalov:as/geometry2

Geometry module #29175

OpenCV Contrib: https://github.com/opencv/opencv_contrib/pull/4129
CI changes: https://github.com/opencv/ci-gha-workflow/pull/313

Continues
- https://github.com/opencv/opencv/pull/28804
- https://github.com/opencv/opencv/pull/29101
- https://github.com/opencv/opencv/pull/29108
- https://github.com/opencv/opencv/pull/28810

Todo for followup PRs:
- [x] Rename doxygen groups
- [x] Fix JS modules layout and whitelists
- [ ] Sort tutorials code/snippets

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Alexander Smorkalov
2026-05-31 14:23:15 +03:00
committed by GitHub
parent 14a475aa0b
commit 59218f9edd
291 changed files with 267 additions and 248 deletions
@@ -0,0 +1,20 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_GEOMETRY_HPP
#define OPENCV_GEOMETRY_HPP
/**
@defgroup geometry Computational geometry primitives module.
*/
//! @addtogroup geometry
//! @{
#include "opencv2/geometry/2d.hpp"
#include "opencv2/geometry/3d.hpp"
//! @} geometry
#endif
@@ -0,0 +1,811 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_2D_HPP
#define OPENCV_2D_HPP
#include "opencv2/core.hpp"
#include "opencv2/core/utils/logger.hpp"
namespace cv {
//! @addtogroup geometry_shape
//! @{
//! types of intersection between rectangles
enum RectanglesIntersectTypes {
INTERSECT_NONE = 0, //!< No intersection
INTERSECT_PARTIAL = 1, //!< There is a partial intersection
INTERSECT_FULL = 2 //!< One of the rectangle is fully enclosed in the other
};
//! Variants of Line Segment %Detector
enum LineSegmentDetectorModes {
LSD_REFINE_NONE = 0, //!< No refinement applied
LSD_REFINE_STD = 1, //!< Standard refinement is applied. E.g. breaking arches into smaller straighter line approximations.
LSD_REFINE_ADV = 2 //!< Advanced refinement. Number of false alarms is calculated, lines are
//!< refined through increase of precision, decrement in size, etc.
};
//! @addtogroup geometry_subdiv2d
//! @{
class CV_EXPORTS_W Subdiv2D
{
public:
/** Subdiv2D point location cases */
enum { PTLOC_ERROR = -2, //!< Point location error
PTLOC_OUTSIDE_RECT = -1, //!< Point outside the subdivision bounding rect
PTLOC_INSIDE = 0, //!< Point inside some facet
PTLOC_VERTEX = 1, //!< Point coincides with one of the subdivision vertices
PTLOC_ON_EDGE = 2 //!< Point on some edge
};
/** Subdiv2D edge type navigation (see: getEdge()) */
enum { NEXT_AROUND_ORG = 0x00,
NEXT_AROUND_DST = 0x22,
PREV_AROUND_ORG = 0x11,
PREV_AROUND_DST = 0x33,
NEXT_AROUND_LEFT = 0x13,
NEXT_AROUND_RIGHT = 0x31,
PREV_AROUND_LEFT = 0x20,
PREV_AROUND_RIGHT = 0x02
};
/** creates an empty Subdiv2D object.
* To create a new empty Delaunay subdivision you need to use the #initDelaunay function.
*/
CV_WRAP Subdiv2D();
/** @overload
*
* @param rect Rectangle that includes all of the 2D points that are to be added to the subdivision.
*
* The function creates an empty Delaunay subdivision where 2D points can be added using the function
* insert() . All of the points to be added must be within the specified rectangle, otherwise a runtime
* error is raised.
*/
CV_WRAP Subdiv2D(Rect rect);
/** @overload */
CV_WRAP Subdiv2D(Rect2f rect2f);
/** @overload
*
* @brief Creates a new empty Delaunay subdivision
*
* @param rect Rectangle that includes all of the 2D points that are to be added to the subdivision.
*
*/
CV_WRAP void initDelaunay(Rect rect);
/** @overload
*
* @brief Creates a new empty Delaunay subdivision
*
* @param rect Rectangle that includes all of the 2d points that are to be added to the subdivision.
*
*/
CV_WRAP_AS(initDelaunay2f) CV_WRAP void initDelaunay(Rect2f rect);
/** @brief Insert a single point into a Delaunay triangulation.
*
* @param pt Point to insert.
*
* The function inserts a single point into a subdivision and modifies the subdivision topology
* appropriately. If a point with the same coordinates exists already, no new point is added.
* @returns the ID of the point.
*
* @note If the point is outside of the triangulation specified rect a runtime error is raised.
*/
CV_WRAP int insert(Point2f pt);
/** @brief Insert multiple points into a Delaunay triangulation.
*
* @param ptvec Points to insert.
*
* The function inserts a vector of points into a subdivision and modifies the subdivision topology
* appropriately.
*/
CV_WRAP void insert(const std::vector<Point2f>& ptvec);
/** @brief Returns the location of a point within a Delaunay triangulation.
*
* @param pt Point to locate.
* @param edge Output edge that the point belongs to or is located to the right of it.
* @param vertex Optional output vertex the input point coincides with.
*
* The function locates the input point within the subdivision and gives one of the triangle edges
* or vertices.
*
* @returns an integer which specify one of the following five cases for point location:
* - The point falls into some facet. The function returns #PTLOC_INSIDE and edge will contain one of
* edges of the facet.
* - The point falls onto the edge. The function returns #PTLOC_ON_EDGE and edge will contain this edge.
* - The point coincides with one of the subdivision vertices. The function returns #PTLOC_VERTEX and
* vertex will contain a pointer to the vertex.
* - The point is outside the subdivision reference rectangle. The function returns #PTLOC_OUTSIDE_RECT
* and no pointers are filled.
* - One of input arguments is invalid. A runtime error is raised or, if silent or "parent" error
* processing mode is selected, #PTLOC_ERROR is returned.
*/
CV_WRAP int locate(Point2f pt, CV_OUT int& edge, CV_OUT int& vertex);
/** @brief Finds the subdivision vertex closest to the given point.
*
* @param pt Input point.
* @param nearestPt Output subdivision vertex point.
*
* The function is another function that locates the input point within the subdivision. It finds the
* subdivision vertex that is the closest to the input point. It is not necessarily one of vertices
* of the facet containing the input point, though the facet (located using locate() ) is used as a
* starting point.
*
* @returns vertex ID.
*/
CV_WRAP int findNearest(Point2f pt, CV_OUT Point2f* nearestPt = 0);
/** @brief Returns a list of all edges.
*
* @param edgeList Output vector.
*
* The function gives each edge as a 4 numbers vector, where each two are one of the edge
* vertices. i.e. org_x = v[0], org_y = v[1], dst_x = v[2], dst_y = v[3].
*/
CV_WRAP void getEdgeList(CV_OUT std::vector<Vec4f>& edgeList) const;
/** @brief Returns a list of the leading edge ID connected to each triangle.
*
* @param leadingEdgeList Output vector.
*
* The function gives one edge ID for each triangle.
*/
CV_WRAP void getLeadingEdgeList(CV_OUT std::vector<int>& leadingEdgeList) const;
/** @brief Returns a list of all triangles.
*
* @param triangleList Output vector.
*
* The function gives each triangle as a 6 numbers vector, where each two are one of the triangle
* vertices. i.e. p1_x = v[0], p1_y = v[1], p2_x = v[2], p2_y = v[3], p3_x = v[4], p3_y = v[5].
*/
CV_WRAP void getTriangleList(CV_OUT std::vector<Vec6f>& triangleList) const;
/** @brief Returns a list of all Voronoi facets.
*
* @param idx Vector of vertices IDs to consider. For all vertices you can pass empty vector.
* @param facetList Output vector of the Voronoi facets.
* @param facetCenters Output vector of the Voronoi facets center points.
*
*/
CV_WRAP void getVoronoiFacetList(const std::vector<int>& idx, CV_OUT std::vector<std::vector<Point2f> >& facetList,
CV_OUT std::vector<Point2f>& facetCenters);
/** @brief Returns vertex location from vertex ID.
*
* @param vertex vertex ID.
* @param firstEdge Optional. The first edge ID which is connected to the vertex.
* @returns vertex (x,y)
*
*/
CV_WRAP Point2f getVertex(int vertex, CV_OUT int* firstEdge = 0) const;
/** @brief Returns one of the edges related to the given edge.
*
* @param edge Subdivision edge ID.
* @param nextEdgeType Parameter specifying which of the related edges to return.
* The following values are possible:
* - NEXT_AROUND_ORG next around the edge origin ( eOnext on the picture below if e is the input edge)
* - NEXT_AROUND_DST next around the edge vertex ( eDnext )
* - PREV_AROUND_ORG previous around the edge origin (reversed eRnext )
* - PREV_AROUND_DST previous around the edge destination (reversed eLnext )
* - NEXT_AROUND_LEFT next around the left facet ( eLnext )
* - NEXT_AROUND_RIGHT next around the right facet ( eRnext )
* - PREV_AROUND_LEFT previous around the left facet (reversed eOnext )
* - PREV_AROUND_RIGHT previous around the right facet (reversed eDnext )
*
* ![sample output](pics/quadedge.png)
*
* @returns edge ID related to the input edge.
*/
CV_WRAP int getEdge( int edge, int nextEdgeType ) const;
/** @brief Returns next edge around the edge origin.
*
* @param edge Subdivision edge ID.
*
* @returns an integer which is next edge ID around the edge origin: eOnext on the
* picture above if e is the input edge).
*/
CV_WRAP int nextEdge(int edge) const;
/** @brief Returns another edge of the same quad-edge.
*
* @param edge Subdivision edge ID.
* @param rotate Parameter specifying which of the edges of the same quad-edge as the input
* one to return. The following values are possible:
* - 0 - the input edge ( e on the picture below if e is the input edge)
* - 1 - the rotated edge ( eRot )
* - 2 - the reversed edge (reversed e (in green))
* - 3 - the reversed rotated edge (reversed eRot (in green))
*
* @returns one of the edges ID of the same quad-edge as the input edge.
*/
CV_WRAP int rotateEdge(int edge, int rotate) const;
CV_WRAP int symEdge(int edge) const;
/** @brief Returns the edge origin.
*
* @param edge Subdivision edge ID.
* @param orgpt Output vertex location.
*
* @returns vertex ID.
*/
CV_WRAP int edgeOrg(int edge, CV_OUT Point2f* orgpt = 0) const;
/** @brief Returns the edge destination.
*
* @param edge Subdivision edge ID.
* @param dstpt Output vertex location.
*
* @returns vertex ID.
*/
CV_WRAP int edgeDst(int edge, CV_OUT Point2f* dstpt = 0) const;
protected:
int newEdge();
void deleteEdge(int edge);
int newPoint(Point2f pt, bool isvirtual, int firstEdge = 0);
void deletePoint(int vtx);
void setEdgePoints( int edge, int orgPt, int dstPt );
void splice( int edgeA, int edgeB );
int connectEdges( int edgeA, int edgeB );
void swapEdges( int edge );
int isRightOf(Point2f pt, int edge) const;
void calcVoronoi();
void clearVoronoi();
void checkSubdiv() const;
struct CV_EXPORTS Vertex
{
Vertex();
Vertex(Point2f pt, bool isvirtual, int firstEdge=0);
bool isvirtual() const;
bool isfree() const;
int firstEdge;
int type;
Point2f pt;
};
struct CV_EXPORTS QuadEdge
{
QuadEdge();
QuadEdge(int edgeidx);
bool isfree() const;
int next[4];
int pt[4];
};
//! All of the vertices
std::vector<Vertex> vtx;
//! All of the edges
std::vector<QuadEdge> qedges;
int freeQEdge;
int freePoint;
bool validGeometry;
int recentEdge;
//! Top left corner of the bounding rect
Point2f topLeft;
//! Bottom right corner of the bounding rect
Point2f bottomRight;
};
//! @} geometry_subdiv2d
//! @addtogroup geometry_feature
//! @{
/** @example samples/cpp/snippets/lsd_lines.cpp
An example using the LineSegmentDetector
\image html building_lsd.png "Sample output image" width=434 height=300
*/
/** @brief Line segment detector class
following the algorithm described at @cite Rafael12 .
@note Implementation has been removed from OpenCV version 3.4.6 to 3.4.15 and version 4.1.0 to 4.5.3 due original code license conflict.
restored again after [Computation of a NFA](https://github.com/rafael-grompone-von-gioi/binomial_nfa) code published under the MIT license.
*/
class CV_EXPORTS_W LineSegmentDetector : public Algorithm
{
public:
/** @brief Finds lines in the input image.
This is the output of the default parameters of the algorithm on the above shown image.
![image](pics/building_lsd.png)
@param image A grayscale (CV_8UC1) input image. If only a roi needs to be selected, use:
`lsd_ptr-\>detect(image(roi), lines, ...); lines += Scalar(roi.x, roi.y, roi.x, roi.y);`
@param lines A vector of Vec4f elements specifying the beginning and ending point of a line. Where
Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end. Returned lines are strictly
oriented depending on the gradient.
@param width Vector of widths of the regions, where the lines are found. E.g. Width of line.
@param prec Vector of precisions with which the lines are found.
@param nfa Vector containing number of false alarms in the line region, with precision of 10%. The
bigger the value, logarithmically better the detection.
- -1 corresponds to 10 mean false alarms
- 0 corresponds to 1 mean false alarm
- 1 corresponds to 0.1 mean false alarms
This vector will be calculated only when the objects type is #LSD_REFINE_ADV.
*/
CV_WRAP virtual void detect(InputArray image, OutputArray lines,
OutputArray width = noArray(), OutputArray prec = noArray(),
OutputArray nfa = noArray()) = 0;
/** @brief Draws the line segments on a given image.
@param image The image, where the lines will be drawn. Should be bigger or equal to the image,
where the lines were found.
@param lines A vector of the lines that needed to be drawn.
*/
CV_WRAP virtual void drawSegments(InputOutputArray image, InputArray lines) = 0;
/** @brief Draws two groups of lines in blue and red, counting the non overlapping (mismatching) pixels.
@param size The size of the image, where lines1 and lines2 were found.
@param lines1 The first group of lines that needs to be drawn. It is visualized in blue color.
@param lines2 The second group of lines. They visualized in red color.
@param image Optional image, where the lines will be drawn. The image should be color(3-channel)
in order for lines1 and lines2 to be drawn in the above mentioned colors.
*/
CV_WRAP virtual int compareSegments(const Size& size, InputArray lines1, InputArray lines2, InputOutputArray image = noArray()) = 0;
virtual ~LineSegmentDetector() { }
};
/** @brief Creates a smart pointer to a LineSegmentDetector object and initializes it.
The LineSegmentDetector algorithm is defined using the standard values. Only advanced users may want
to edit those, as to tailor it for their own application.
@param refine The way found lines will be refined, see #LineSegmentDetectorModes
@param scale The scale of the image that will be used to find the lines. Range (0..1].
@param sigma_scale Sigma for Gaussian filter. It is computed as sigma = sigma_scale/scale.
@param quant Bound to the quantization error on the gradient norm.
@param ang_th Gradient angle tolerance in degrees.
@param log_eps Detection threshold: -log10(NFA) \> log_eps. Used only when advance refinement is chosen.
@param density_th Minimal density of aligned region points in the enclosing rectangle.
@param n_bins Number of bins in pseudo-ordering of gradient modulus.
*/
CV_EXPORTS_W Ptr<LineSegmentDetector> createLineSegmentDetector(
LineSegmentDetectorModes refine = LSD_REFINE_STD, double scale = 0.8,
double sigma_scale = 0.6, double quant = 2.0, double ang_th = 22.5,
double log_eps = 0, double density_th = 0.7, int n_bins = 1024);
//! @} geometry_feature
/** @example samples/python/snippets/squares.py
A n example using approxPolyDP function in python. *
*/
/** @brief Approximates a polygonal curve(s) with the specified precision.
*
T he function cv::approxPolyDP approximates a curve or a p*olygon with another curve/polygon with less
vertices so that the distance between them is less or equal to the specified precision. It uses the
Douglas-Peucker algorithm <https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm>
@param curve Input vector of a 2D point stored in std::vector or Mat
@param approxCurve Result of the approximation. The type should match the type of the input curve.
@param epsilon Parameter specifying the approximation accuracy. This is the maximum distance
between the original curve and its approximation.
@param closed If true, the approximated curve is closed (its first and last vertices are
connected). Otherwise, it is not closed.
*/
CV_EXPORTS_W void approxPolyDP( InputArray curve,
OutputArray approxCurve,
double epsilon, bool closed );
/** @brief Approximates a polygon with a convex hull with a specified accuracy and number of sides.
*
T he cv::approxPolyN function approximates a polygon with *a convex hull
so that the difference between the contour area of the original contour and the new polygon is minimal.
It uses a greedy algorithm for contracting two vertices into one in such a way that the additional area is minimal.
Straight lines formed by each edge of the convex contour are drawn and the areas of the resulting triangles are considered.
Each vertex will lie either on the original contour or outside it.
The algorithm based on the paper @cite LowIlie2003 .
@param curve Input vector of a 2D points stored in std::vector or Mat, points must be float or integer.
@param approxCurve Result of the approximation. The type is vector of a 2D point (Point2f or Point) in std::vector or Mat.
@param nsides The parameter defines the number of sides of the result polygon.
@param epsilon_percentage defines the percentage of the maximum of additional area.
If it equals -1, it is not used. Otherwise algorithm stops if additional area is greater than contourArea(_curve) * percentage.
If additional area exceeds the limit, algorithm returns as many vertices as there were at the moment the limit was exceeded.
@param ensure_convex If it is true, algorithm creates a convex hull of input contour. Otherwise input vector should be convex.
*/
CV_EXPORTS_W void approxPolyN(InputArray curve, OutputArray approxCurve,
int nsides, float epsilon_percentage = -1.0,
bool ensure_convex = true);
/** @brief Finds a rotated rectangle of the minimum area enclosing the input 2D point set.
*
* The function calculates and returns the minimum-area bounding rectangle (possibly rotated) for a
* specified point set. The angle of rotation represents the angle between the line connecting the starting
* and ending points (based on the clockwise order with greatest index for the corner with greatest \f$y\f$)
* and the horizontal axis. This angle always falls between \f$[-90, 0)\f$ because, if the object
* rotates more than a rect angle, the next edge is used to measure the angle. The starting and ending points change
* as the object rotates.Developer should keep in mind that the returned RotatedRect can contain negative
* indices when data is close to the containing Mat element boundary.
*
* @param points Input vector of 2D points, stored in std::vector\<\> or Mat
*/
CV_EXPORTS_W RotatedRect minAreaRect( InputArray points );
/** @brief Finds the four vertices of a rotated rect. Useful to draw the rotated rectangle.
*
* The function finds the four vertices of a rotated rectangle. The four vertices are returned
* in clockwise order starting from the point with greatest \f$y\f$. If two points have the
* same \f$y\f$ coordinate the rightmost is the starting point. This function is useful to draw the
* rectangle. In C++, instead of using this function, you can directly use RotatedRect::points method. Please
* visit the @ref tutorial_bounding_rotated_ellipses "tutorial on Creating Bounding rotated boxes and ellipses
* for contours" for more information.
*
* @param box The input rotated rectangle. It may be the output of @ref minAreaRect.
* @param points The output array of four vertices of rectangles.
*/
CV_EXPORTS_W void boxPoints(RotatedRect box, OutputArray points);
/** @brief Finds a circle of the minimum area enclosing a 2D point set.
*
* The function finds the minimal enclosing circle of a 2D point set using an iterative algorithm.
*
* @param points Input vector of 2D points, stored in std::vector\<\> or Mat
* @param center Output center of the circle.
* @param radius Output radius of the circle.
*/
CV_EXPORTS_W void minEnclosingCircle( InputArray points,
CV_OUT Point2f& center, CV_OUT float& radius );
/** @brief Finds a triangle of minimum area enclosing a 2D point set and returns its area.
*
* The function finds a triangle of minimum area enclosing the given set of 2D points and returns its
* area. The output for a given 2D point set is shown in the image below. 2D points are depicted in
*red* and the enclosing triangle in *yellow*.
*
* ![Sample output of the minimum enclosing triangle function](pics/minenclosingtriangle.png)
*
* The implementation of the algorithm is based on O'Rourke's @cite ORourke86 and Klee and Laskowski's
* @cite KleeLaskowski85 papers. O'Rourke provides a \f$\theta(n)\f$ algorithm for finding the minimal
* enclosing triangle of a 2D convex polygon with n vertices. Since the #minEnclosingTriangle function
* takes a 2D point set as input an additional preprocessing step of computing the convex hull of the
* 2D point set is required. The complexity of the #convexHull function is \f$O(n log(n))\f$ which is higher
* than \f$\theta(n)\f$. Thus the overall complexity of the function is \f$O(n log(n))\f$.
*
* @param points Input vector of 2D points with depth CV_32S or CV_32F, stored in std::vector\<\> or Mat
* @param triangle Output vector of three 2D points defining the vertices of the triangle. The depth
* of the OutputArray must be CV_32F.
*/
CV_EXPORTS_W double minEnclosingTriangle( InputArray points, CV_OUT OutputArray triangle );
/**
* @brief Finds a convex polygon of minimum area enclosing a 2D point set and returns its area.
*
* This function takes a given set of 2D points and finds the enclosing polygon with k vertices and minimal
* area. It takes the set of points and the parameter k as input and returns the area of the minimal
* enclosing polygon.
*
* The Implementation is based on a paper by Aggarwal, Chang and Yap @cite Aggarwal1985. They
* provide a \f$\theta(n²log(n)log(k))\f$ algorithm for finding the minimal convex polygon with k
* vertices enclosing a 2D convex polygon with n vertices (k < n). Since the #minEnclosingConvexPolygon
* function takes a 2D point set as input, an additional preprocessing step of computing the convex hull
* of the 2D point set is required. The complexity of the #convexHull function is \f$O(n log(n))\f$ which
* is lower than \f$\theta(n²log(n)log(k))\f$. Thus the overall complexity of the function is
* \f$O(n²log(n)log(k))\f$.
*
* @param points Input vector of 2D points, stored in std::vector\<\> or Mat
* @param polygon Output vector of 2D points defining the vertices of the enclosing polygon
* @param k Number of vertices of the output polygon
*/
CV_EXPORTS_W double minEnclosingConvexPolygon ( InputArray points, OutputArray polygon, int k );
/** @brief Compares two shapes.
*
* The function compares two shapes. All three implemented methods use the Hu invariants (see #HuMoments)
*
* @param contour1 First contour or grayscale image.
* @param contour2 Second contour or grayscale image.
* @param method Comparison method, see #ShapeMatchModes
* @param parameter Method-specific parameter (not supported now).
*/
CV_EXPORTS_W double matchShapes( InputArray contour1, InputArray contour2,
int method, double parameter );
/** @example samples/cpp/geometry.cpp
* An example program illustrates the use of cv::convexHull, cv::fitEllipse, cv::minEnclosingTriangle, cv::minEnclosingCircle and cv::minAreaRect.
*/
/** @brief Finds the convex hull of a point set.
*
* The function cv::convexHull finds the convex hull of a 2D point set using the Sklansky's algorithm @cite Sklansky82
* that has *O(N logN)* complexity in the current implementation.
*
* @param points Input 2D point set, stored in std::vector or Mat.
* @param hull Output convex hull. It is either an integer vector of indices or vector of points. In
* the first case, the hull elements are 0-based indices of the convex hull points in the original
* array (since the set of convex hull points is a subset of the original point set). In the second
* case, hull elements are the convex hull points themselves.
* @param clockwise Orientation flag. If it is true, the output convex hull is oriented clockwise.
* Otherwise, it is oriented counter-clockwise. The assumed coordinate system has its X axis pointing
* to the right, and its Y axis pointing upwards.
* @param returnPoints Operation flag. In case of a matrix, when the flag is true, the function
* returns convex hull points. Otherwise, it returns indices of the convex hull points. When the
* output array is std::vector, the flag is ignored, and the output depends on the type of the
* vector: std::vector\<int\> implies returnPoints=false, std::vector\<Point\> implies
* returnPoints=true.
*
* @note `points` and `hull` should be different arrays, inplace processing isn't supported.
*
* Check @ref tutorial_hull "the corresponding tutorial" for more details.
*
* useful links:
*
* https://www.learnopencv.com/convex-hull-using-opencv-in-python-and-c/
*/
CV_EXPORTS_W void convexHull( InputArray points, OutputArray hull,
bool clockwise = false, bool returnPoints = true );
/** @brief Finds the convexity defects of a contour.
*
* The figure below displays convexity defects of a hand contour:
*
* ![image](pics/defects.png)
*
* @param contour Input contour.
* @param convexhull Convex hull obtained using convexHull that should contain indices of the contour
* points that make the hull.
* @param convexityDefects The output vector of convexity defects. In C++ and the new Python/Java
* interface each convexity defect is represented as 4-element integer vector (a.k.a. #Vec4i):
* (start_index, end_index, farthest_pt_index, fixpt_depth), where indices are 0-based indices
* in the original contour of the convexity defect beginning, end and the farthest point, and
* fixpt_depth is fixed-point approximation (with 8 fractional bits) of the distance between the
* farthest contour point and the hull. That is, to get the floating-point value of the depth will be
* fixpt_depth/256.0.
*/
CV_EXPORTS_W void convexityDefects( InputArray contour, InputArray convexhull, OutputArray convexityDefects );
/** @brief Tests a contour convexity.
*
* The function tests whether the input contour is convex or not. The contour must be simple, that is,
* without self-intersections. Otherwise, the function output is undefined.
*
* @param contour Input vector of 2D points, stored in std::vector\<\> or Mat
*/
CV_EXPORTS_W bool isContourConvex( InputArray contour );
/** @example samples/cpp/snippets/intersectExample.cpp
* Examples of how intersectConvexConvex works
*/
/** @brief Finds intersection of two convex polygons
*
* @param p1 First polygon
* @param p2 Second polygon
* @param p12 Output polygon describing the intersecting area
* @param handleNested When true, an intersection is found if one of the polygons is fully enclosed in the other.
* When false, no intersection is found. If the polygons share a side or the vertex of one polygon lies on an edge
* of the other, they are not considered nested and an intersection will be found regardless of the value of handleNested.
*
* @returns Area of intersecting polygon. May be negative, if algorithm has not converged, e.g. non-convex input.
*
* @note intersectConvexConvex doesn't confirm that both polygons are convex and will return invalid results if they aren't.
*/
CV_EXPORTS_W float intersectConvexConvex( InputArray p1, InputArray p2,
OutputArray p12, bool handleNested = true );
/** @brief Fits an ellipse around a set of 2D points.
*
* The function calculates the ellipse that fits (in a least-squares sense) a set of 2D points best of
* all. It returns the rotated rectangle in which the ellipse is inscribed. The first algorithm described by @cite Fitzgibbon95
* is used. Developer should keep in mind that it is possible that the returned
* ellipse/rotatedRect data contains negative indices, due to the data points being close to the
* border of the containing Mat element.
*
* @param points Input 2D point set, stored in std::vector\<\> or Mat
*
* @note Input point types are @ref Point2i or @ref Point2f and at least 5 points are required.
* @note @ref getClosestEllipsePoints function can be used to compute the ellipse fitting error.
*/
CV_EXPORTS_W RotatedRect fitEllipse( InputArray points );
/** @brief Fits an ellipse around a set of 2D points.
*
* The function calculates the ellipse that fits a set of 2D points.
* It returns the rotated rectangle in which the ellipse is inscribed.
* The Approximate Mean Square (AMS) proposed by @cite Taubin1991 is used.
*
* For an ellipse, this basis set is \f$ \chi= \left(x^2, x y, y^2, x, y, 1\right) \f$,
* which is a set of six free coefficients \f$ A^T=\left\{A_{\text{xx}},A_{\text{xy}},A_{\text{yy}},A_x,A_y,A_0\right\} \f$.
* However, to specify an ellipse, all that is needed is five numbers; the major and minor axes lengths \f$ (a,b) \f$,
* the position \f$ (x_0,y_0) \f$, and the orientation \f$ \theta \f$. This is because the basis set includes lines,
* quadratics, parabolic and hyperbolic functions as well as elliptical functions as possible fits.
* If the fit is found to be a parabolic or hyperbolic function then the standard #fitEllipse method is used.
* The AMS method restricts the fit to parabolic, hyperbolic and elliptical curves
* by imposing the condition that \f$ A^T ( D_x^T D_x + D_y^T D_y) A = 1 \f$ where
* the matrices \f$ Dx \f$ and \f$ Dy \f$ are the partial derivatives of the design matrix \f$ D \f$ with
* respect to x and y. The matrices are formed row by row applying the following to
* each of the points in the set:
* \f{align*}{
* D(i,:)&=\left\{x_i^2, x_i y_i, y_i^2, x_i, y_i, 1\right\} &
* D_x(i,:)&=\left\{2 x_i,y_i,0,1,0,0\right\} &
* D_y(i,:)&=\left\{0,x_i,2 y_i,0,1,0\right\}
* \f}
* The AMS method minimizes the cost function
* \f{equation*}{
* \epsilon ^2=\frac{ A^T D^T D A }{ A^T (D_x^T D_x + D_y^T D_y) A^T }
* \f}
*
* The minimum cost is found by solving the generalized eigenvalue problem.
*
* \f{equation*}{
* D^T D A = \lambda \left( D_x^T D_x + D_y^T D_y\right) A
* \f}
*
* @param points Input 2D point set, stored in std::vector\<\> or Mat
*
* @note Input point types are @ref Point2i or @ref Point2f and at least 5 points are required.
* @note @ref getClosestEllipsePoints function can be used to compute the ellipse fitting error.
*/
CV_EXPORTS_W RotatedRect fitEllipseAMS( InputArray points );
/** @brief Fits an ellipse around a set of 2D points.
*
* The function calculates the ellipse that fits a set of 2D points.
* It returns the rotated rectangle in which the ellipse is inscribed.
* The Direct least square (Direct) method by @cite oy1998NumericallySD is used.
*
* For an ellipse, this basis set is \f$ \chi= \left(x^2, x y, y^2, x, y, 1\right) \f$,
* which is a set of six free coefficients \f$ A^T=\left\{A_{\text{xx}},A_{\text{xy}},A_{\text{yy}},A_x,A_y,A_0\right\} \f$.
* However, to specify an ellipse, all that is needed is five numbers; the major and minor axes lengths \f$ (a,b) \f$,
* the position \f$ (x_0,y_0) \f$, and the orientation \f$ \theta \f$. This is because the basis set includes lines,
* quadratics, parabolic and hyperbolic functions as well as elliptical functions as possible fits.
* The Direct method confines the fit to ellipses by ensuring that \f$ 4 A_{xx} A_{yy}- A_{xy}^2 > 0 \f$.
* The condition imposed is that \f$ 4 A_{xx} A_{yy}- A_{xy}^2=1 \f$ which satisfies the inequality
* and as the coefficients can be arbitrarily scaled is not overly restrictive.
*
* \f{equation*}{
* \epsilon ^2= A^T D^T D A \quad \text{with} \quad A^T C A =1 \quad \text{and} \quad C=\left(\begin{matrix}
* 0 & 0 & 2 & 0 & 0 & 0 \\
* 0 & -1 & 0 & 0 & 0 & 0 \\
* 2 & 0 & 0 & 0 & 0 & 0 \\
* 0 & 0 & 0 & 0 & 0 & 0 \\
* 0 & 0 & 0 & 0 & 0 & 0 \\
* 0 & 0 & 0 & 0 & 0 & 0
* \end{matrix} \right)
* \f}
*
* The minimum cost is found by solving the generalized eigenvalue problem.
*
* \f{equation*}{
* D^T D A = \lambda \left( C\right) A
* \f}
*
* The system produces only one positive eigenvalue \f$ \lambda\f$ which is chosen as the solution
* with its eigenvector \f$\mathbf{u}\f$. These are used to find the coefficients
*
* \f{equation*}{
* A = \sqrt{\frac{1}{\mathbf{u}^T C \mathbf{u}}} \mathbf{u}
* \f}
* The scaling factor guarantees that \f$A^T C A =1\f$.
*
* @param points Input 2D point set, stored in std::vector\<\> or Mat
*
* @note Input point types are @ref Point2i or @ref Point2f and at least 5 points are required.
* @note @ref getClosestEllipsePoints function can be used to compute the ellipse fitting error.
*/
CV_EXPORTS_W RotatedRect fitEllipseDirect( InputArray points );
/** @example samples/python/snippets/fitline.py
* An example for fitting line in python
*/
/** @brief Compute for each 2d point the nearest 2d point located on a given ellipse.
*
* The function computes the nearest 2d location on a given ellipse for a vector of 2d points and is based on @cite Chatfield2017 code.
* This function can be used to compute for instance the ellipse fitting error.
*
* @param ellipse_params Ellipse parameters
* @param points Input 2d points
* @param closest_pts For each 2d point, their corresponding closest 2d point located on a given ellipse
*
* @note Input point types are @ref Point2i or @ref Point2f
* @see fitEllipse, fitEllipseAMS, fitEllipseDirect
*/
CV_EXPORTS_W void getClosestEllipsePoints( const RotatedRect& ellipse_params, InputArray points, OutputArray closest_pts );
/** @brief Fits a line to a 2D or 3D point set.
*
* The function fitLine fits a line to a 2D or 3D point set by minimizing \f$\sum_i \rho(r_i)\f$ where
* \f$r_i\f$ is a distance between the \f$i^{th}\f$ point, the line and \f$\rho(r)\f$ is a distance function, one
* of the following:
* - DIST_L2
* \f[\rho (r) = r^2/2 \quad \text{(the simplest and the fastest least-squares method)}\f]
* - DIST_L1
* \f[\rho (r) = r\f]
* - DIST_L12
* \f[\rho (r) = 2 \cdot ( \sqrt{1 + \frac{r^2}{2}} - 1)\f]
* - DIST_FAIR
* \f[\rho \left (r \right ) = C^2 \cdot \left ( \frac{r}{C} - \log{\left(1 + \frac{r}{C}\right)} \right ) \quad \text{where} \quad C=1.3998\f]
* - DIST_WELSCH
* \f[\rho \left (r \right ) = \frac{C^2}{2} \cdot \left ( 1 - \exp{\left(-\left(\frac{r}{C}\right)^2\right)} \right ) \quad \text{where} \quad C=2.9846\f]
* - DIST_HUBER
* \f[\rho (r) = \fork{r^2/2}{if \(r < C\)}{C \cdot (r-C/2)}{otherwise} \quad \text{where} \quad C=1.345\f]
*
* The algorithm is based on the M-estimator ( <https://en.wikipedia.org/wiki/M-estimator> ) technique
* that iteratively fits the line using the weighted least-squares algorithm. After each iteration the
* weights \f$w_i\f$ are adjusted to be inversely proportional to \f$\rho(r_i)\f$ .
*
* @param points Input vector of 2D or 3D points, stored in std::vector\<\> or Mat.
* @param line Output line parameters. In case of 2D fitting, it should be a vector of 4 elements
* (like Vec4f) - (vx, vy, x0, y0), where (vx, vy) is a normalized vector collinear to the line and
* (x0, y0) is a point on the line. In case of 3D fitting, it should be a vector of 6 elements (like
* Vec6f) - (vx, vy, vz, x0, y0, z0), where (vx, vy, vz) is a normalized vector collinear to the line
* and (x0, y0, z0) is a point on the line.
* @param distType Distance used by the M-estimator, see #DistanceTypes
* @param param Numerical parameter ( C ) for some types of distances. If it is 0, an optimal value
* is chosen.
* @param reps Sufficient accuracy for the radius (distance between the coordinate origin and the line).
* @param aeps Sufficient accuracy for the angle. 0.01 would be a good default value for reps and aeps.
*/
CV_EXPORTS_W void fitLine( InputArray points, OutputArray line, int distType,
double param, double reps, double aeps );
/** @brief Performs a point-in-contour test.
*
* The function determines whether the point is inside a contour, outside, or lies on an edge (or
* coincides with a vertex). It returns positive (inside), negative (outside), or zero (on an edge)
* value, correspondingly. When measureDist=false , the return value is +1, -1, and 0, respectively.
* Otherwise, the return value is a signed distance between the point and the nearest contour edge.
*
* See below a sample output of the function where each image pixel is tested against the contour:
*
* ![sample output](pics/pointpolygon.png)
*
* @param contour Input contour.
* @param pt Point tested against the contour.
* @param measureDist If true, the function estimates the signed distance from the point to the
* nearest contour edge. Otherwise, the function only checks if the point is inside a contour or not.
*/
CV_EXPORTS_W double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );
/** @brief Finds out if there is any intersection between two rotated rectangles.
*
* If there is then the vertices of the intersecting region are returned as well.
*
* Below are some examples of intersection configurations. The hatched pattern indicates the
* intersecting region and the red vertices are returned by the function.
*
* ![intersection examples](pics/intersection.png)
*
* @param rect1 First rectangle
* @param rect2 Second rectangle
* @param intersectingRegion The output array of the vertices of the intersecting region. It returns
* at most 8 vertices. Stored as std::vector\<cv::Point2f\> or cv::Mat as Mx1 of type CV_32FC2.
* @returns One of #RectanglesIntersectTypes
*/
CV_EXPORTS_W int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion );
} // namespace cv
#endif // OPENCV_2D_HPP
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,181 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_DEPTH_HPP
#define OPENCV_3D_DEPTH_HPP
#include <opencv2/core.hpp>
#include <limits>
namespace cv
{
//! @addtogroup rgbd
//! @{
/** Object that can compute the normals in an image.
* It is an object as it can cache data for speed efficiency
* The implemented methods are either:
* - FALS (the fastest) and SRI from
* ``Fast and Accurate Computation of Surface Normals from Range Images``
* by H. Badino, D. Huber, Y. Park and T. Kanade
* - the normals with bilateral filtering on a depth image from
* ``Gradient Response Maps for Real-Time Detection of Texture-Less Objects``
* by S. Hinterstoisser, C. Cagniart, S. Ilic, P. Sturm, N. Navab, P. Fua, and V. Lepetit
*/
class CV_EXPORTS_W RgbdNormals
{
public:
enum RgbdNormalsMethod
{
RGBD_NORMALS_METHOD_FALS = 0,
RGBD_NORMALS_METHOD_LINEMOD = 1,
RGBD_NORMALS_METHOD_SRI = 2,
RGBD_NORMALS_METHOD_CROSS_PRODUCT = 3
};
RgbdNormals() { }
virtual ~RgbdNormals() { }
/** Creates new RgbdNormals object
* @param rows the number of rows of the depth image normals will be computed on
* @param cols the number of cols of the depth image normals will be computed on
* @param depth the depth of the normals (only CV_32F or CV_64F)
* @param K the calibration matrix to use
* @param window_size the window size to compute the normals: can only be 1,3,5 or 7
* @param diff_threshold threshold in depth difference, used in LINEMOD algirithm
* @param method one of the methods to use: RGBD_NORMALS_METHOD_SRI, RGBD_NORMALS_METHOD_FALS
*/
CV_WRAP static Ptr<RgbdNormals> create(int rows = 0, int cols = 0, int depth = 0, InputArray K = noArray(), int window_size = 5,
float diff_threshold = 50.f,
RgbdNormals::RgbdNormalsMethod method = RgbdNormals::RgbdNormalsMethod::RGBD_NORMALS_METHOD_FALS);
/** Given a set of 3d points in a depth image, compute the normals at each point.
* @param points a rows x cols x 3 matrix of CV_32F/CV64F or a rows x cols x 1 CV_U16S
* @param normals a rows x cols x 3 matrix
*/
CV_WRAP virtual void apply(InputArray points, OutputArray normals) const = 0;
/** Prepares cached data required for calculation
* If not called by user, called automatically at first calculation
*/
CV_WRAP virtual void cache() const = 0;
CV_WRAP virtual int getRows() const = 0;
CV_WRAP virtual void setRows(int val) = 0;
CV_WRAP virtual int getCols() const = 0;
CV_WRAP virtual void setCols(int val) = 0;
CV_WRAP virtual int getWindowSize() const = 0;
CV_WRAP virtual void setWindowSize(int val) = 0;
CV_WRAP virtual int getDepth() const = 0;
CV_WRAP virtual void getK(OutputArray val) const = 0;
CV_WRAP virtual void setK(InputArray val) = 0;
CV_WRAP virtual RgbdNormals::RgbdNormalsMethod getMethod() const = 0;
};
/** Registers depth data to an external camera
* Registration is performed by creating a depth cloud, transforming the cloud by
* the rigid body transformation between the cameras, and then projecting the
* transformed points into the RGB camera.
*
* uv_rgb = K_rgb * [R | t] * z * inv(K_ir) * uv_ir
*
* Currently does not check for negative depth values.
*
* @param unregisteredCameraMatrix the camera matrix of the depth camera
* @param registeredCameraMatrix the camera matrix of the external camera
* @param registeredDistCoeffs the distortion coefficients of the external camera
* @param Rt the rigid body transform between the cameras. Transforms points from depth camera frame to external camera frame.
* @param unregisteredDepth the input depth data
* @param outputImagePlaneSize the image plane dimensions of the external camera (width, height)
* @param registeredDepth the result of transforming the depth into the external camera
* @param depthDilation whether or not the depth is dilated to avoid holes and occlusion errors (optional)
*/
CV_EXPORTS_W void registerDepth(InputArray unregisteredCameraMatrix, InputArray registeredCameraMatrix, InputArray registeredDistCoeffs,
InputArray Rt, InputArray unregisteredDepth, const Size& outputImagePlaneSize,
OutputArray registeredDepth, bool depthDilation=false);
/**
* @param depth the depth image
* @param in_K
* @param in_points the list of xy coordinates
* @param points3d the resulting 3d points (point is represented by 4 chanels value [x, y, z, 0])
*/
CV_EXPORTS_W void depthTo3dSparse(InputArray depth, InputArray in_K, InputArray in_points, OutputArray points3d);
/** Converts a depth image to 3d points. If the mask is empty then the resulting array has the same dimensions as `depth`,
* otherwise it is 1d vector containing mask-enabled values only.
* The coordinate system is x pointing left, y down and z away from the camera
* @param depth the depth image (if given as short int CV_U, it is assumed to be the depth in millimeters
* (as done with the Microsoft Kinect), otherwise, if given as CV_32F or CV_64F, it is assumed in meters)
* @param K The calibration matrix
* @param points3d the resulting 3d points (point is represented by 4 channels value [x, y, z, 0]). They are of the same depth as `depth` if it is CV_32F or CV_64F, and the
* depth of `K` if `depth` is of depth CV_16U or CV_16S
* @param mask the mask of the points to consider (can be empty)
*/
CV_EXPORTS_W void depthTo3d(InputArray depth, InputArray K, OutputArray points3d, InputArray mask = noArray());
/** If the input image is of type CV_16UC1 (like the Kinect one), the image is converted to floats, divided
* by depth_factor to get a depth in meters, and the values 0 are converted to std::numeric_limits<float>::quiet_NaN()
* Otherwise, the image is simply converted to floats
* @param in the depth image (if given as short int CV_U, it is assumed to be the depth in millimeters
* (as done with the Microsoft Kinect), it is assumed in meters)
* @param type the desired output depth (CV_32F or CV_64F)
* @param out The rescaled float depth image
* @param depth_factor (optional) factor by which depth is converted to distance (by default = 1000.0 for Kinect sensor)
*/
CV_EXPORTS_W void rescaleDepth(InputArray in, int type, OutputArray out, double depth_factor = 1000.0);
/** Warps depth or RGB-D image by reprojecting it in 3d, applying Rt transformation
* and then projecting it back onto the image plane.
* This function can be used to visualize the results of the Odometry algorithm.
* @param depth Depth data, should be 1-channel CV_16U, CV_16S, CV_32F or CV_64F
* @param image RGB image (optional), should be 1-, 3- or 4-channel CV_8U
* @param mask Mask of used pixels (optional), should be CV_8UC1, CV_8SC1 or CV_BoolC1
* @param Rt Rotation+translation matrix (3x4 or 4x4) to be applied to depth points
* @param cameraMatrix Camera intrinsics matrix (3x3)
* @param warpedDepth The warped depth data (optional)
* @param warpedImage The warped RGB image (optional)
* @param warpedMask The mask of valid pixels in warped image (optional)
*/
CV_EXPORTS_W void warpFrame(InputArray depth, InputArray image, InputArray mask, InputArray Rt, InputArray cameraMatrix,
OutputArray warpedDepth = noArray(), OutputArray warpedImage = noArray(), OutputArray warpedMask = noArray());
enum RgbdPlaneMethod
{
RGBD_PLANE_METHOD_DEFAULT
};
/** Find the planes in a depth image
* @param points3d the 3d points organized like the depth image: rows x cols with 3 channels
* @param normals the normals for every point in the depth image; optional, can be empty
* @param mask An image where each pixel is labeled with the plane it belongs to
* and 255 if it does not belong to any plane
* @param plane_coefficients the coefficients of the corresponding planes (a,b,c,d) such that ax+by+cz+d=0, norm(a,b,c)=1
* and c < 0 (so that the normal points towards the camera)
* @param block_size The size of the blocks to look at for a stable MSE
* @param min_size The minimum size of a cluster to be considered a plane
* @param threshold The maximum distance of a point from a plane to belong to it (in meters)
* @param sensor_error_a coefficient of the sensor error. 0 by default, use 0.0075 for a Kinect
* @param sensor_error_b coefficient of the sensor error. 0 by default
* @param sensor_error_c coefficient of the sensor error. 0 by default
* @param method The method to use to compute the planes.
*/
CV_EXPORTS_W void findPlanes(InputArray points3d, InputArray normals, OutputArray mask, OutputArray plane_coefficients,
int block_size = 40, int min_size = 40*40, double threshold = 0.01,
double sensor_error_a = 0, double sensor_error_b = 0,
double sensor_error_c = 0,
RgbdPlaneMethod method = RGBD_PLANE_METHOD_DEFAULT);
// TODO Depth interpolation
// Curvature
// Get rescaleDepth return dubles if asked for
//! @}
} /* namespace cv */
#endif // include guard
@@ -0,0 +1,19 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_DETAIL_KINFU_FRAME_HPP
#define OPENCV_3D_DETAIL_KINFU_FRAME_HPP
#include <opencv2/core/affine.hpp>
namespace cv {
namespace detail {
CV_EXPORTS void renderPointsNormals(InputArray _points, InputArray _normals, OutputArray image, cv::Vec3f lightLoc);
CV_EXPORTS void renderPointsNormalsColors(InputArray _points, InputArray _normals, InputArray _colors, OutputArray image);
} // namespace detail
} // namespace cv
#endif // include guard
@@ -0,0 +1,148 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_DETAIL_OPTIMIZER_HPP
#define OPENCV_3D_DETAIL_OPTIMIZER_HPP
#include "opencv2/core/affine.hpp"
#include "opencv2/core/quaternion.hpp"
#include "opencv2/geometry/3d.hpp"
namespace cv
{
namespace detail
{
/*
This class provides functions required for Levenberg-Marquadt algorithm implementation.
See LevMarqBase::optimize() source code for details.
*/
class CV_EXPORTS LevMarqBackend
{
public:
virtual ~LevMarqBackend() { }
// enables geodesic acceleration support in a backend, returns true on success
virtual bool enableGeo() = 0;
// calculates an energy and/or jacobian at probe param vector
virtual bool calcFunc(double& energy, bool calcEnergy = true, bool calcJacobian = false) = 0;
// adds x to current variables and writes the sum to probe var
// or to geodesic acceleration var if geo flag is set
virtual void currentOplusX(const Mat_<double>& x, bool geo = false) = 0;
// allocates jtj, jtb and other resources for objective function calculation, sets probeX to current X
virtual void prepareVars() = 0;
// returns a J^T*b vector (aka gradient)
virtual const Mat_<double> getJtb() = 0;
// returns a J^T*J diagonal vector
virtual const Mat_<double> getDiag() = 0;
// sets a J^T*J diagonal
virtual void setDiag(const Mat_<double>& d) = 0;
// performs jacobi scaling if the option is turned on
virtual void doJacobiScaling(const Mat_<double>& di) = 0;
// decomposes LevMarq matrix before solution
virtual bool decompose() = 0;
// solves LevMarq equation (J^T*J + lmdiag) * x = -right for current iteration using existing decomposition
// right can be equal to J^T*b for LevMarq equation or J^T*rvv for geodesic acceleration equation
virtual bool solveDecomposed(const Mat_<double>& right, Mat_<double>& x) = 0;
// calculates J^T*f(geo) where geo is geodesic acceleration variable
// this is used for J^T*rvv calculation for geodesic acceleration
// calculates J^T*rvv where rvv is second directional derivative of the function in direction v
// rvv = (f(x0 + v*h) - f(x0))/h - J*v)/h
// where v is a LevMarq equation solution
virtual bool calcJtbv(Mat_<double>& jtbv) = 0;
// sets current params vector to probe params
virtual void acceptProbe() = 0;
};
/** @brief Base class for Levenberg-Marquadt solvers.
This class can be used for general local optimization using sparse linear solvers, exponential param update or fixed variables
implemented in child classes.
This base class does not depend on a type, layout or a group structure of a param vector or an objective function jacobian.
A child class should provide a storage for that data and implement all virtual member functions that process it.
This class does not support fixed/masked variables, this should also be implemented in child classes.
*/
class CV_EXPORTS LevMarqBase
{
public:
virtual ~LevMarqBase() { }
// runs optimization using given termination conditions
virtual LevMarq::Report optimize();
LevMarqBase(const Ptr<LevMarqBackend>& backend_, const LevMarq::Settings& settings_):
backend(backend_), settings(settings_)
{ }
Ptr<LevMarqBackend> backend;
LevMarq::Settings settings;
};
// ATTENTION! This class is used internally in Large KinFu.
// It has been pushed to publicly available headers for tests only.
// Source compatibility of this API is not guaranteed in the future.
// This class provides tools to solve so-called pose graph problem often arisen in SLAM problems
// The pose graph format, cost function and optimization techniques
// repeat the ones used in Ceres 3D Pose Graph Optimization:
// http://ceres-solver.org/nnls_tutorial.html#other-examples, pose_graph_3d.cc bullet
class CV_EXPORTS_W PoseGraph
{
public:
static Ptr<PoseGraph> create();
virtual ~PoseGraph();
// Node may have any id >= 0
virtual void addNode(size_t _nodeId, const Affine3d& _pose, bool fixed) = 0;
virtual bool isNodeExist(size_t nodeId) const = 0;
virtual bool setNodeFixed(size_t nodeId, bool fixed) = 0;
virtual bool isNodeFixed(size_t nodeId) const = 0;
virtual Affine3d getNodePose(size_t nodeId) const = 0;
virtual std::vector<size_t> getNodesIds() const = 0;
virtual size_t getNumNodes() const = 0;
// Edges have consequent indices starting from 0
virtual void addEdge(size_t _sourceNodeId, size_t _targetNodeId, const Affine3f& _transformation,
const Matx66f& _information = Matx66f::eye()) = 0;
virtual size_t getEdgeStart(size_t i) const = 0;
virtual size_t getEdgeEnd(size_t i) const = 0;
virtual Affine3d getEdgePose(size_t i) const = 0;
virtual Matx66f getEdgeInfo(size_t i) const = 0;
virtual size_t getNumEdges() const = 0;
// checks if graph is connected and each edge connects exactly 2 nodes
virtual bool isValid() const = 0;
// Calculates an initial pose estimate using the Minimum Spanning Tree (Prim's MST) algorithm.
// The result serves as a starting point for further optimization.
// Edge weights are calculated as:
// weight = translationNorm + lambda * rotationAngle
// The default lambda value (0.485) was empirically chosen based on its impact on optimizer performance,
// but can/should be tuned for different datasets.
virtual void initializePosesWithMST(double lambda = 0.485) = 0;
// creates an optimizer with user-defined settings and returns a pointer on it
virtual Ptr<LevMarqBase> createOptimizer(const LevMarq::Settings& settings) = 0;
// creates an optimizer with default settings and returns a pointer on it
virtual Ptr<LevMarqBase> createOptimizer() = 0;
// Creates an optimizer (with default settings) if it wasn't created before and runs it
// Returns number of iterations elapsed or -1 if failed to optimize
virtual LevMarq::Report optimize() = 0;
// calculate cost function based on current nodes parameters
virtual double calcEnergy() const = 0;
};
} // namespace detail
} // namespace cv
#endif // include guard
@@ -0,0 +1,558 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_DETAIL_SUBMAP_HPP
#define OPENCV_3D_DETAIL_SUBMAP_HPP
#include <opencv2/core.hpp>
#include <opencv2/core/affine.hpp>
#include "opencv2/geometry/detail/optimizer.hpp"
//TODO: remove it when it is rewritten to robust pose graph
#include "opencv2/core/dualquaternion.hpp"
#include <type_traits>
#include <vector>
#include <map>
#include <unordered_map>
namespace cv
{
namespace detail
{
template<typename MatType>
class Submap
{
public:
struct PoseConstraint
{
Affine3f estimatedPose;
int weight;
PoseConstraint() : weight(0){};
void accumulatePose(const Affine3f& _pose, int _weight = 1)
{
DualQuatf accPose = DualQuatf::createFromAffine3(estimatedPose) * float(weight) + DualQuatf::createFromAffine3(_pose) * float(_weight);
weight += _weight;
accPose = accPose / float(weight);
estimatedPose = accPose.toAffine3();
}
};
typedef std::map<int, PoseConstraint> Constraints;
Submap(int _id, const VolumeSettings& settings, const cv::Affine3f& _pose = cv::Affine3f::Identity(),
int _startFrameId = 0)
: id(_id), pose(_pose), cameraPose(Affine3f::Identity()), startFrameId(_startFrameId),
volume(VolumeType::HashTSDF, settings)
{ }
virtual ~Submap() = default;
virtual void integrate(InputArray _depth, const int currframeId);
virtual void raycast(const cv::Affine3f& cameraPose, cv::Size frameSize, cv::Matx33f K,
OutputArray points = noArray(), OutputArray normals = noArray());
virtual int getTotalAllocatedBlocks() const { return int(volume.getTotalVolumeUnits()); };
virtual int getVisibleBlocks(int currFrameId) const
{
CV_Assert(currFrameId >= startFrameId);
//return volume.getVisibleBlocks(currFrameId, FRAME_VISIBILITY_THRESHOLD);
return volume.getVisibleBlocks();
}
float calcVisibilityRatio(int currFrameId) const
{
int allocate_blocks = getTotalAllocatedBlocks();
int visible_blocks = getVisibleBlocks(currFrameId);
return float(visible_blocks) / float(allocate_blocks);
}
// Adding new Edge for Loop Closure Detection. Return true or false to indicate whether adding success.
bool addEdgeToSubmap(const int tarSubmapID, const Affine3f& tarPose);
//! TODO: Possibly useless
virtual void setStartFrameId(int _startFrameId) { startFrameId = _startFrameId; };
virtual void setStopFrameId(int _stopFrameId) { stopFrameId = _stopFrameId; };
void composeCameraPose(const cv::Affine3f& _relativePose) { cameraPose = cameraPose * _relativePose; }
PoseConstraint& getConstraint(const int _id)
{
//! Creates constraints if doesn't exist yet
return constraints[_id];
}
public:
const int id;
cv::Affine3f pose;
cv::Affine3f cameraPose;
Constraints constraints;
int startFrameId;
int stopFrameId;
//! TODO: Should we support submaps for regular volumes?
static constexpr int FRAME_VISIBILITY_THRESHOLD = 5;
//! TODO: Add support for GPU arrays (UMat)
OdometryFrame frame;
OdometryFrame renderFrame;
Volume volume;
};
template<typename MatType>
void Submap<MatType>::integrate(InputArray _depth, const int currFrameId)
{
CV_Assert(currFrameId >= startFrameId);
volume.integrate(_depth, cameraPose.matrix);
}
template<typename MatType>
void Submap<MatType>::raycast(const cv::Affine3f& _cameraPose, cv::Size frameSize, cv::Matx33f K,
OutputArray points, OutputArray normals)
{
if (!points.needed() && !normals.needed())
{
MatType pts, nrm;
//TODO: get depth instead of pts from raycast
volume.raycast(_cameraPose.matrix, frameSize.height, frameSize.width, K, pts, nrm);
std::vector<MatType> pch(3);
split(pts, pch);
renderFrame = frame;
frame = OdometryFrame(pch[2]);
}
else
{
volume.raycast(_cameraPose.matrix, frameSize.height, frameSize.width, K, points, normals);
}
}
template<typename MatType>
bool Submap<MatType>::addEdgeToSubmap(const int tarSubmapID, const Affine3f& tarPose)
{
auto iter = constraints.find(tarSubmapID);
// if there is NO edge of currSubmap to tarSubmap.
if(iter == constraints.end())
{
// Frome pose to tarPose transformation
Affine3f estimatePose = tarPose * pose.inv();
// Create new Edge.
PoseConstraint& preConstrain = getConstraint(tarSubmapID);
preConstrain.accumulatePose(estimatePose, 1);
return true;
} else
{
return false;
}
}
/**
* @brief: Manages all the created submaps for a particular scene
*/
template<typename MatType>
class SubmapManager
{
public:
enum class Type
{
NEW = 0,
CURRENT = 1,
RELOCALISATION = 2,
LOOP_CLOSURE = 3,
LOST = 4
};
struct ActiveSubmapData
{
Type type;
std::vector<Affine3f> constraints;
int trackingAttempts;
};
typedef Submap<MatType> SubmapT;
typedef std::map<int, Ptr<SubmapT>> IdToSubmapPtr;
typedef std::unordered_map<int, ActiveSubmapData> IdToActiveSubmaps;
explicit SubmapManager(const VolumeSettings& _volumeSettings) : volumeSettings(_volumeSettings) {}
virtual ~SubmapManager() = default;
void reset() { submapList.clear(); };
bool shouldCreateSubmap(int frameId);
bool shouldChangeCurrSubmap(int _frameId, int toSubmapId);
//! Adds a new submap/volume into the current list of managed/Active submaps
int createNewSubmap(bool isCurrentActiveMap, const int currFrameId = 0, const Affine3f& pose = cv::Affine3f::Identity());
void removeSubmap(int _id);
size_t numOfSubmaps(void) const { return submapList.size(); };
size_t numOfActiveSubmaps(void) const { return activeSubmaps.size(); };
Ptr<SubmapT> getSubmap(int _id) const;
Ptr<SubmapT> getCurrentSubmap(void) const;
int estimateConstraint(int fromSubmapId, int toSubmapId, int& inliers, Affine3f& inlierPose);
bool updateMap(int frameId, const OdometryFrame& frame);
bool addEdgeToCurrentSubmap(const int currentSubmapID, const int tarSubmapID);
Ptr<detail::PoseGraph> MapToPoseGraph();
void PoseGraphToMap(const Ptr<detail::PoseGraph>& updatedPoseGraph);
VolumeSettings volumeSettings;
std::vector<Ptr<SubmapT>> submapList;
IdToActiveSubmaps activeSubmaps;
Ptr<detail::PoseGraph> poseGraph;
};
template<typename MatType>
int SubmapManager<MatType>::createNewSubmap(bool isCurrentMap, int currFrameId, const Affine3f& pose)
{
int newId = int(submapList.size());
Ptr<SubmapT> newSubmap = cv::makePtr<SubmapT>(newId, volumeSettings, pose, currFrameId);
submapList.push_back(newSubmap);
ActiveSubmapData newSubmapData;
newSubmapData.trackingAttempts = 0;
newSubmapData.type = isCurrentMap ? Type::CURRENT : Type::NEW;
activeSubmaps[newId] = newSubmapData;
return newId;
}
template<typename MatType>
Ptr<Submap<MatType>> SubmapManager<MatType>::getSubmap(int _id) const
{
CV_Assert(submapList.size() > 0);
CV_Assert(_id >= 0 && _id < int(submapList.size()));
return submapList.at(_id);
}
template<typename MatType>
Ptr<Submap<MatType>> SubmapManager<MatType>::getCurrentSubmap(void) const
{
for (const auto& it : activeSubmaps)
{
if (it.second.type == Type::CURRENT)
return getSubmap(it.first);
}
return nullptr;
}
template<typename MatType>
bool SubmapManager<MatType>::shouldCreateSubmap(int currFrameId)
{
int currSubmapId = -1;
for (const auto& it : activeSubmaps)
{
auto submapData = it.second;
// No more than 1 new submap at a time!
if (submapData.type == Type::NEW)
{
return false;
}
if (submapData.type == Type::CURRENT)
{
currSubmapId = it.first;
}
}
//! TODO: This shouldn't be happening? since there should always be one active current submap
if (currSubmapId < 0)
{
return false;
}
Ptr<SubmapT> currSubmap = getSubmap(currSubmapId);
float ratio = currSubmap->calcVisibilityRatio(currFrameId);
//TODO: fix this when a new pose graph is ready
// if (ratio < 0.2f)
if (ratio < 0.5f)
return true;
return false;
}
template<typename MatType>
int SubmapManager<MatType>::estimateConstraint(int fromSubmapId, int toSubmapId, int& inliers, Affine3f& inlierPose)
{
static constexpr int MAX_ITER = 10;
static constexpr float CONVERGE_WEIGHT_THRESHOLD = 0.01f;
static constexpr float INLIER_WEIGHT_THRESH = 0.8f;
static constexpr int MIN_INLIERS = 10;
static constexpr int MAX_TRACKING_ATTEMPTS = 25;
//! thresh = HUBER_THRESH
auto huberWeight = [](float residual, float thresh = 0.1f) -> float {
float rAbs = abs(residual);
if (rAbs < thresh)
return 1.0;
float numerator = sqrt(2 * thresh * rAbs - thresh * thresh);
return numerator / rAbs;
};
Ptr<SubmapT> fromSubmap = getSubmap(fromSubmapId);
Ptr<SubmapT> toSubmap = getSubmap(toSubmapId);
ActiveSubmapData& fromSubmapData = activeSubmaps.at(fromSubmapId);
Affine3f TcameraToFromSubmap = fromSubmap->cameraPose;
Affine3f TcameraToToSubmap = toSubmap->cameraPose;
// FromSubmap -> ToSubmap transform
Affine3f candidateConstraint = TcameraToToSubmap * TcameraToFromSubmap.inv();
fromSubmapData.trackingAttempts++;
fromSubmapData.constraints.push_back(candidateConstraint);
std::vector<float> weights(fromSubmapData.constraints.size() + 1, 1.0f);
Affine3f prevConstraint = fromSubmap->getConstraint(toSubmap->id).estimatedPose;
int prevWeight = fromSubmap->getConstraint(toSubmap->id).weight;
// Iterative reweighted least squares with huber threshold to find the inliers in the past observations
Vec6f meanConstraint;
float sumWeight = 0.0f;
for (int i = 0; i < MAX_ITER; i++)
{
Vec6f constraintVec;
for (int j = 0; j < int(weights.size() - 1); j++)
{
Affine3f currObservation = fromSubmapData.constraints[j];
cv::vconcat(currObservation.rvec(), currObservation.translation(), constraintVec);
meanConstraint += weights[j] * constraintVec;
sumWeight += weights[j];
}
// Heavier weight given to the estimatedPose
cv::vconcat(prevConstraint.rvec(), prevConstraint.translation(), constraintVec);
meanConstraint += weights.back() * prevWeight * constraintVec;
sumWeight += prevWeight;
meanConstraint /= float(sumWeight);
float residual = 0.0f;
float diff = 0.0f;
for (int j = 0; j < int(weights.size()); j++)
{
int w;
if (j == int(weights.size() - 1))
{
cv::vconcat(prevConstraint.rvec(), prevConstraint.translation(), constraintVec);
w = prevWeight;
}
else
{
Affine3f currObservation = fromSubmapData.constraints[j];
cv::vconcat(currObservation.rvec(), currObservation.translation(), constraintVec);
w = 1;
}
cv::Vec6f residualVec = (constraintVec - meanConstraint);
residual = float(norm(residualVec));
float newWeight = huberWeight(residual);
diff += w * abs(newWeight - weights[j]);
weights[j] = newWeight;
}
if (diff / (prevWeight + weights.size() - 1) < CONVERGE_WEIGHT_THRESHOLD)
break;
}
int localInliers = 0;
DualQuatf inlierConstraint;
for (int i = 0; i < int(weights.size()); i++)
{
if (weights[i] > INLIER_WEIGHT_THRESH)
{
localInliers++;
if (i == int(weights.size() - 1))
inlierConstraint += DualQuatf::createFromMat(prevConstraint.matrix);
else
inlierConstraint += DualQuatf::createFromMat(fromSubmapData.constraints[i].matrix);
}
}
inlierConstraint = inlierConstraint * 1.0f/float(max(localInliers, 1));
inlierPose = inlierConstraint.toAffine3();
inliers = localInliers;
if (inliers >= MIN_INLIERS)
{
return 1;
}
if(fromSubmapData.trackingAttempts - inliers > (MAX_TRACKING_ATTEMPTS - MIN_INLIERS))
{
return -1;
}
return 0;
}
template<typename MatType>
bool SubmapManager<MatType>::shouldChangeCurrSubmap(int _frameId, int toSubmapId)
{
auto toSubmap = getSubmap(toSubmapId);
auto toSubmapData = activeSubmaps.at(toSubmapId);
auto currActiveSubmap = getCurrentSubmap();
int blocksInNewMap = toSubmap->getTotalAllocatedBlocks();
float newRatio = toSubmap->calcVisibilityRatio(_frameId);
float currRatio = currActiveSubmap->calcVisibilityRatio(_frameId);
//! TODO: Check for a specific threshold?
if (blocksInNewMap <= 0)
return false;
if ((newRatio > currRatio) && (toSubmapData.type == Type::NEW))
return true;
return false;
}
template<typename MatType>
bool SubmapManager<MatType>::addEdgeToCurrentSubmap(const int currentSubmapID, const int tarSubmapID)
{
Ptr<SubmapT> currentSubmap = getSubmap(currentSubmapID);
Ptr<SubmapT> tarSubmap = getSubmap(tarSubmapID);
return currentSubmap->addEdgeToSubmap(tarSubmapID, tarSubmap->pose);
}
template<typename MatType>
bool SubmapManager<MatType>::updateMap(int _frameId, const OdometryFrame& _frame)
{
bool mapUpdated = false;
int changedCurrentMapId = -1;
const int currSubmapId = getCurrentSubmap()->id;
for (auto& it : activeSubmaps)
{
int submapId = it.first;
auto& submapData = it.second;
if (submapData.type == Type::NEW || submapData.type == Type::LOOP_CLOSURE)
{
// Check with previous estimate
int inliers;
Affine3f inlierPose;
int constraintUpdate = estimateConstraint(submapId, currSubmapId, inliers, inlierPose);
if (constraintUpdate == 1)
{
typename SubmapT::PoseConstraint& submapConstraint = getSubmap(submapId)->getConstraint(currSubmapId);
submapConstraint.accumulatePose(inlierPose, inliers);
submapData.constraints.clear();
submapData.trackingAttempts = 0;
if (shouldChangeCurrSubmap(_frameId, submapId))
{
changedCurrentMapId = submapId;
}
mapUpdated = true;
}
else if(constraintUpdate == -1)
{
submapData.type = Type::LOST;
}
}
}
std::vector<int> createNewConstraintsList;
for (auto& it : activeSubmaps)
{
int submapId = it.first;
auto& submapData = it.second;
if (submapId == changedCurrentMapId)
{
submapData.type = Type::CURRENT;
}
if ((submapData.type == Type::CURRENT) && (changedCurrentMapId >= 0) && (submapId != changedCurrentMapId))
{
submapData.type = Type::LOST;
createNewConstraintsList.push_back(submapId);
}
if ((submapData.type == Type::NEW || submapData.type == Type::LOOP_CLOSURE) && (changedCurrentMapId >= 0))
{
//! TODO: Add a new type called NEW_LOST?
submapData.type = Type::LOST;
createNewConstraintsList.push_back(submapId);
}
}
for (typename IdToActiveSubmaps::iterator it = activeSubmaps.begin(); it != activeSubmaps.end();)
{
auto& submapData = it->second;
if (submapData.type == Type::LOST)
it = activeSubmaps.erase(it);
else
it++;
}
for (std::vector<int>::const_iterator it = createNewConstraintsList.begin(); it != createNewConstraintsList.end(); ++it)
{
int dataId = *it;
ActiveSubmapData newSubmapData;
newSubmapData.trackingAttempts = 0;
newSubmapData.type = Type::LOOP_CLOSURE;
activeSubmaps[dataId] = newSubmapData;
}
if (shouldCreateSubmap(_frameId))
{
Ptr<SubmapT> currActiveSubmap = getCurrentSubmap();
Affine3f newSubmapPose = currActiveSubmap->pose * currActiveSubmap->cameraPose;
int submapId = createNewSubmap(false, _frameId, newSubmapPose);
auto newSubmap = getSubmap(submapId);
newSubmap->frame = _frame;
}
return mapUpdated;
}
template<typename MatType>
Ptr<detail::PoseGraph> SubmapManager<MatType>::MapToPoseGraph()
{
Ptr<detail::PoseGraph> localPoseGraph = detail::PoseGraph::create();
for(const auto& currSubmap : submapList)
{
const typename SubmapT::Constraints& constraintList = currSubmap->constraints;
for(const auto& currConstraintPair : constraintList)
{
// TODO: Handle case with duplicate constraints A -> B and B -> A
/* Matx66f informationMatrix = Matx66f::eye() * (currConstraintPair.second.weight/10); */
Matx66f informationMatrix = Matx66f::eye();
localPoseGraph->addEdge(currSubmap->id, currConstraintPair.first, currConstraintPair.second.estimatedPose, informationMatrix);
}
}
for(const auto& currSubmap : submapList)
{
localPoseGraph->addNode(currSubmap->id, currSubmap->pose, (currSubmap->id == 0));
}
return localPoseGraph;
}
template <typename MatType>
void SubmapManager<MatType>::PoseGraphToMap(const Ptr<detail::PoseGraph>& updatedPoseGraph)
{
for(const auto& currSubmap : submapList)
{
Affine3d pose = updatedPoseGraph->getNodePose(currSubmap->id);
if(!updatedPoseGraph->isNodeFixed(currSubmap->id))
currSubmap->pose = pose;
}
}
} // namespace detail
} // namespace cv
#endif // include guard
@@ -0,0 +1,65 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_MST_HPP
#define OPENCV_3D_MST_HPP
#include <vector>
namespace cv
{
/**
* @brief Represents an edge in a graph for Minimum Spanning Tree (MST) computation.
*
* Each edge connects two nodes (source and target) and has an associated weight.
*/
struct CV_EXPORTS_W_SIMPLE MSTEdge
{
CV_PROP_RW int source, target;
CV_PROP_RW double weight;
};
/**
* @brief Represents the algorithms available for building a Minimum Spanning Tree (MST).
*
* More algorithms may be added in the future.
*/
enum MSTAlgorithm
{
MST_PRIM = 0,
MST_KRUSKAL = 1
};
/**
* @brief Builds a Minimum Spanning Tree (MST) using the specified algorithm (see @ref MSTAlgorithm).
*
* Supports graphs with negative edge weights. Self-loop edges (edges where source and target are the
* same) are ignored. If multiple edges exist between the same pair of nodes, only the one with the
* lowest weight is considered. If the graph is disconnected or input is invalid, the function
* returns false.
*
* @note The @p root parameter is ignored for algorithms that do not require a starting node.
* @note Additional MST algorithms may be supported in the future via the @p algorithm parameter
* (see @ref MSTAlgorithm).
*
* @param numNodes Number of nodes in the graph (must be greater than 0).
* @param inputEdges Input vector of edges representing the graph.
* @param[out] resultingEdges Output vector to store the edges of the resulting MST.
* @param algorithm Specifies which algorithm to use to compute the MST (see @ref MSTAlgorithm).
* @param root Starting node for the MST algorithm (only used for certain algorithms).
* @return true if a valid MST was successfully built; false otherwise.
* @throws cv::Error (StsBadArg) if an invalid algorithm is specified.
*/
CV_EXPORTS_W bool buildMST(
int numNodes,
const std::vector<MSTEdge>& inputEdges,
CV_OUT std::vector<MSTEdge>& resultingEdges,
MSTAlgorithm algorithm,
int root = 0
);
} // namespace cv
#endif // include guard
@@ -0,0 +1,112 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_ODOMETRY_HPP
#define OPENCV_3D_ODOMETRY_HPP
#include <opencv2/core.hpp>
#include "odometry_frame.hpp"
#include "odometry_settings.hpp"
namespace cv
{
/** These constants are used to set a type of data which odometry will use
* @param DEPTH only depth data
* @param RGB only rgb image
* @param RGB_DEPTH depth and rgb data simultaneously
*/
enum class OdometryType
{
DEPTH = 0,
RGB = 1,
RGB_DEPTH = 2
};
/** These constants are used to set the speed and accuracy of odometry
* @param COMMON accurate but not so fast
* @param FAST less accurate but faster
*/
enum class OdometryAlgoType
{
COMMON = 0,
FAST = 1
};
class CV_EXPORTS_W Odometry
{
public:
CV_WRAP Odometry();
CV_WRAP explicit Odometry(OdometryType otype);
CV_WRAP Odometry(OdometryType otype, const OdometrySettings& settings, OdometryAlgoType algtype);
~Odometry();
/** Prepare frame for odometry calculation
* @param frame odometry prepare this frame as src frame and dst frame simultaneously
*/
CV_WRAP void prepareFrame(OdometryFrame& frame) const;
/** Prepare frame for odometry calculation
* @param srcFrame frame will be prepared as src frame ("original" image)
* @param dstFrame frame will be prepared as dsr frame ("rotated" image)
*/
CV_WRAP void prepareFrames(OdometryFrame& srcFrame, OdometryFrame& dstFrame) const;
/** Compute Rigid Transformation between two frames so that Rt * src = dst
* Both frames, source and destination, should have been prepared by calling prepareFrame() first
*
* @param srcFrame src frame ("original" image)
* @param dstFrame dst frame ("rotated" image)
* @param Rt Rigid transformation, which will be calculated, in form:
* { R_11 R_12 R_13 t_1
* R_21 R_22 R_23 t_2
* R_31 R_32 R_33 t_3
* 0 0 0 1 }
* @return true on success, false if failed to find the transformation
*/
CV_WRAP bool compute(const OdometryFrame& srcFrame, const OdometryFrame& dstFrame, OutputArray Rt) const;
/**
* @brief Compute Rigid Transformation between two frames so that Rt * src = dst
*
* @param srcDepth source depth ("original" image)
* @param dstDepth destination depth ("rotated" image)
* @param Rt Rigid transformation, which will be calculated, in form:
* { R_11 R_12 R_13 t_1
* R_21 R_22 R_23 t_2
* R_31 R_32 R_33 t_3
* 0 0 0 1 }
* @return true on success, false if failed to find the transformation
*/
CV_WRAP bool compute(InputArray srcDepth, InputArray dstDepth, OutputArray Rt) const;
/**
* @brief Compute Rigid Transformation between two frames so that Rt * src = dst
*
* @param srcDepth source depth ("original" image)
* @param srcRGB source RGB
* @param dstDepth destination depth ("rotated" image)
* @param dstRGB destination RGB
* @param Rt Rigid transformation, which will be calculated, in form:
* { R_11 R_12 R_13 t_1
* R_21 R_22 R_23 t_2
* R_31 R_32 R_33 t_3
* 0 0 0 1 }
* @return true on success, false if failed to find the transformation
*/
CV_WRAP bool compute(InputArray srcDepth, InputArray srcRGB, InputArray dstDepth, InputArray dstRGB, OutputArray Rt) const;
/**
* @brief Get the normals computer object used for normals calculation (if presented).
* The normals computer is generated at first need during prepareFrame when normals are required for the ICP algorithm
* but not presented by a user. Re-generated each time the related settings change or a new frame arrives with the different size.
*/
CV_WRAP Ptr<RgbdNormals> getNormalsComputer() const;
class Impl;
private:
Ptr<Impl> impl;
};
}
#endif //OPENCV_3D_ODOMETRY_HPP
@@ -0,0 +1,108 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_ODOMETRY_FRAME_HPP
#define OPENCV_3D_ODOMETRY_FRAME_HPP
#include <opencv2/core.hpp>
namespace cv
{
/** Indicates what pyramid is to access using getPyramidAt() method:
*/
enum OdometryFramePyramidType
{
PYR_IMAGE = 0, //!< The pyramid of grayscale images
PYR_DEPTH = 1, //!< The pyramid of depth images
PYR_MASK = 2, //!< The pyramid of masks
PYR_CLOUD = 3, //!< The pyramid of point clouds, produced from the pyramid of depths
PYR_DIX = 4, //!< The pyramid of dI/dx derivative images
PYR_DIY = 5, //!< The pyramid of dI/dy derivative images
PYR_TEXMASK = 6, //!< The pyramid of "textured" masks (i.e. additional masks for normals or grayscale images)
PYR_NORM = 7, //!< The pyramid of normals
PYR_NORMMASK = 8, //!< The pyramid of normals masks
N_PYRAMIDS
};
/**
* @brief An object that keeps per-frame data for Odometry algorithms from user-provided images to algorithm-specific precalculated data.
* When not empty, it contains a depth image, a mask of valid pixels and a set of pyramids generated from that data.
* A BGR/Gray image and normals are optional.
* OdometryFrame is made to be used together with Odometry class to reuse precalculated data between Rt data calculations.
* A correct way to do that is to call Odometry::prepareFrames() on prev and next frames and then pass them to Odometry::compute() method.
*/
class CV_EXPORTS_W_SIMPLE OdometryFrame
{
public:
/**
* @brief Construct a new OdometryFrame object. All non-empty images should have the same size.
*
* @param depth A depth image, should be CV_8UC1
* @param image An BGR or grayscale image (or noArray() if it's not required for used ICP algorithm).
* Should be CV_8UC3 or CV_8C4 if it's BGR image or CV_8UC1 if it's grayscale. If it's BGR then it's converted to grayscale
* image automatically.
* @param mask A user-provided mask of valid pixels, should be CV_8UC1
* @param normals A user-provided normals to the depth surface, should be CV_32FC4
*/
CV_WRAP explicit OdometryFrame(InputArray depth = noArray(), InputArray image = noArray(), InputArray mask = noArray(), InputArray normals = noArray());
~OdometryFrame() {};
/**
* @brief Get the original user-provided BGR/Gray image
*
* @param image Output image
*/
CV_WRAP void getImage(OutputArray image) const;
/**
* @brief Get the gray image generated from the user-provided BGR/Gray image
*
* @param image Output image
*/
CV_WRAP void getGrayImage(OutputArray image) const;
/**
* @brief Get the original user-provided depth image
*
* @param depth Output image
*/
CV_WRAP void getDepth(OutputArray depth) const;
/**
* @brief Get the depth image generated from the user-provided one after conversion, rescale or filtering for ICP algorithm needs
*
* @param depth Output image
*/
CV_WRAP void getProcessedDepth(OutputArray depth) const;
/**
* @brief Get the valid pixels mask generated for the ICP calculations intersected with the user-provided mask
*
* @param mask Output image
*/
CV_WRAP void getMask(OutputArray mask) const;
/**
* @brief Get the normals image either generated for the ICP calculations or user-provided
*
* @param normals Output image
*/
CV_WRAP void getNormals(OutputArray normals) const;
/**
* @brief Get the amount of levels in pyramids (all of them if not empty should have the same number of levels)
* or 0 if no pyramids were prepared yet
*/
CV_WRAP int getPyramidLevels() const;
/**
* @brief Get the image generated for the ICP calculations from one of the pyramids specified by pyrType. Returns empty image if
* the pyramid is empty or there's no such pyramid level
*
* @param img Output image
* @param pyrType Type of pyramid
* @param level Level in the pyramid
*/
CV_WRAP void getPyramidAt(OutputArray img, OdometryFramePyramidType pyrType, size_t level) const;
class Impl;
Ptr<Impl> impl;
};
}
#endif // !OPENCV_3D_ODOMETRY_FRAME_HPP
@@ -0,0 +1,65 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_ODOMETRY_SETTINGS_HPP
#define OPENCV_3D_ODOMETRY_SETTINGS_HPP
#include <opencv2/core.hpp>
namespace cv
{
class CV_EXPORTS_W_SIMPLE OdometrySettings
{
public:
CV_WRAP OdometrySettings();
OdometrySettings(const OdometrySettings&);
OdometrySettings& operator=(const OdometrySettings&);
~OdometrySettings() {};
CV_WRAP void setCameraMatrix(InputArray val);
CV_WRAP void getCameraMatrix(OutputArray val) const;
CV_WRAP void setIterCounts(InputArray val);
CV_WRAP void getIterCounts(OutputArray val) const;
CV_WRAP void setMinDepth(float val);
CV_WRAP float getMinDepth() const;
CV_WRAP void setMaxDepth(float val);
CV_WRAP float getMaxDepth() const;
CV_WRAP void setMaxDepthDiff(float val);
CV_WRAP float getMaxDepthDiff() const;
CV_WRAP void setMaxPointsPart(float val);
CV_WRAP float getMaxPointsPart() const;
CV_WRAP void setSobelSize(int val);
CV_WRAP int getSobelSize() const;
CV_WRAP void setSobelScale(double val);
CV_WRAP double getSobelScale() const;
CV_WRAP void setNormalWinSize(int val);
CV_WRAP int getNormalWinSize() const;
CV_WRAP void setNormalDiffThreshold(float val);
CV_WRAP float getNormalDiffThreshold() const;
CV_WRAP void setNormalMethod(RgbdNormals::RgbdNormalsMethod nm);
CV_WRAP RgbdNormals::RgbdNormalsMethod getNormalMethod() const;
CV_WRAP void setAngleThreshold(float val);
CV_WRAP float getAngleThreshold() const;
CV_WRAP void setMaxTranslation(float val);
CV_WRAP float getMaxTranslation() const;
CV_WRAP void setMaxRotation(float val);
CV_WRAP float getMaxRotation() const;
CV_WRAP void setMinGradientMagnitude(float val);
CV_WRAP float getMinGradientMagnitude() const;
CV_WRAP void setMinGradientMagnitudes(InputArray val);
CV_WRAP void getMinGradientMagnitudes(OutputArray val) const;
class Impl;
private:
Ptr<Impl> impl;
};
}
#endif //OPENCV_3D_ODOMETRY_SETTINGS_HPP
@@ -0,0 +1,411 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021, Yechun Ruan <ruanyc@mail.sustech.edu.cn>
#ifndef OPENCV_3D_PTCLOUD_HPP
#define OPENCV_3D_PTCLOUD_HPP
namespace cv {
//! @addtogroup _3d
//! @{
//! type of the robust estimation algorithm
enum SacMethod
{
/** The RANSAC algorithm described in @cite fischler1981random.
*/
SAC_METHOD_RANSAC,
// SAC_METHOD_MAGSAC,
// SAC_METHOD_LMEDS,
// SAC_METHOD_MSAC,
// SAC_METHOD_RRANSAC,
// SAC_METHOD_RMSAC,
// SAC_METHOD_MLESAC,
// SAC_METHOD_PROSAC
};
enum SacModelType
{
/** The 3D PLANE model coefficients in list **[a, b, c, d]**,
corresponding to the coefficients of equation
\f$ ax + by + cz + d = 0 \f$. */
SAC_MODEL_PLANE,
/** The 3D SPHERE model coefficients in list **[center_x, center_y, center_z, radius]**,
corresponding to the coefficients of equation
\f$ (x - center\_x)^2 + (y - center\_y)^2 + (z - center\_z)^2 = radius^2 \f$.*/
SAC_MODEL_SPHERE,
// SAC_MODEL_CYLINDER,
};
/** @brief Sample Consensus algorithm segmentation of 3D point cloud model.
Example of segmenting plane from a 3D point cloud using the RANSAC algorithm:
@snippet snippets/3d_sac_segmentation.cpp planeSegmentationUsingRANSAC
@see
1. Supported algorithms: enum SacMethod in ptcloud.hpp.
2. Supported models: enum SacModelType in ptcloud.hpp.
*/
class CV_EXPORTS SACSegmentation
{
public:
/** @brief Custom function that take the model coefficients and return whether the model is acceptable or not.
Example of constructing SACSegmentation::ModelConstraintFunction:
@snippet snippets/3d_sac_segmentation.cpp usageExampleSacModelConstraintFunction
@note The content of model_coefficients depends on the model.
Refer to the comments inside enumeration type SacModelType.
*/
using ModelConstraintFunction =
std::function<bool(const std::vector<double> &/*model_coefficients*/)>;
//-------------------------- CREATE -----------------------
static Ptr<SACSegmentation> create(SacModelType sac_model_type = SAC_MODEL_PLANE,
SacMethod sac_method = SAC_METHOD_RANSAC,
double threshold = 0.5, int max_iterations = 1000);
// -------------------------- CONSTRUCTOR, DESTRUCTOR --------------------------
SACSegmentation() = default;
virtual ~SACSegmentation() = default;
//-------------------------- SEGMENT -----------------------
/**
* @brief Execute segmentation using the sample consensus method.
*
* @param input_pts Original point cloud, vector of Point3 or Mat of size Nx3/3xN.
* @param[out] labels The label corresponds to the model number, 0 means it
* does not belong to any model, range [0, Number of final resultant models obtained].
* @param[out] models_coefficients The resultant models coefficients.
* Currently supports passing in cv::Mat. Models coefficients are placed in a matrix of NxK
* with depth CV_64F (will automatically adjust if the passing one does not look like this),
* where N is the number of models and K is the number of coefficients of one model.
* The coefficients for each model refer to the comments inside enumeration type SacModelType.
* @return Number of final resultant models obtained by segmentation.
*/
virtual int
segment(InputArray input_pts, OutputArray labels, OutputArray models_coefficients) = 0;
//-------------------------- Getter and Setter -----------------------
//! Set the type of sample consensus model to use.
virtual void setSacModelType(SacModelType sac_model_type) = 0;
//! Get the type of sample consensus model used.
virtual SacModelType getSacModelType() const = 0;
//! Set the type of sample consensus method to use.
virtual void setSacMethodType(SacMethod sac_method) = 0;
//! Get the type of sample consensus method used.
virtual SacMethod getSacMethodType() const = 0;
//! Set the distance to the model threshold.
//! Considered as inlier point if distance to the model less than threshold.
virtual void setDistanceThreshold(double threshold) = 0;
//! Get the distance to the model threshold.
virtual double getDistanceThreshold() const = 0;
//! Set the minimum and maximum radius limits for the model.
//! Only used for models whose model parameters include a radius.
virtual void setRadiusLimits(double radius_min, double radius_max) = 0;
//! Get the minimum and maximum radius limits for the model.
virtual void getRadiusLimits(double &radius_min, double &radius_max) const = 0;
//! Set the maximum number of iterations to attempt.
virtual void setMaxIterations(int max_iterations) = 0;
//! Get the maximum number of iterations to attempt.
virtual int getMaxIterations() const = 0;
//! Set the confidence that ensure at least one of selections is an error-free set of data points.
virtual void setConfidence(double confidence) = 0;
//! Get the confidence that ensure at least one of selections is an error-free set of data points.
virtual double getConfidence() const = 0;
//! Set the number of models expected.
virtual void setNumberOfModelsExpected(int number_of_models_expected) = 0;
//! Get the expected number of models.
virtual int getNumberOfModelsExpected() const = 0;
//! Set whether to use parallelism or not.
//! The number of threads is set by cv::setNumThreads(int nthreads).
virtual void setParallel(bool is_parallel) = 0;
//! Get whether to use parallelism or not.
virtual bool isParallel() const = 0;
//! Set state used to initialize the RNG(Random Number Generator).
virtual void setRandomGeneratorState(uint64 rng_state) = 0;
//! Get state used to initialize the RNG(Random Number Generator).
virtual uint64 getRandomGeneratorState() const = 0;
//! Set custom model coefficient constraint function.
//! A custom function that takes model coefficients and returns whether the model is acceptable or not.
virtual void
setCustomModelConstraints(const ModelConstraintFunction &custom_model_constraints) = 0;
//! Get custom model coefficient constraint function.
virtual const ModelConstraintFunction &getCustomModelConstraints() const = 0;
};
/**
* @brief Point cloud sampling by Voxel Grid filter downsampling.
*
* Creates a 3D voxel grid (a set of tiny 3D boxes in space) over the input
* point cloud data, in each voxel (i.e., 3D box), all the points present will be
* approximated (i.e., downsampled) with the point closest to their centroid.
*
* @param[out] sampled_point_flags Flags of the sampled point, (pass in std::vector<int> or std::vector<char> etc.)
* sampled_point_flags[i] is 1 means i-th point selected, 0 means it is not selected.
* @param input_pts Original point cloud, vector of Point3 or Mat of size Nx3/3xN.
* @param length Grid length.
* @param width Grid width.
* @param height Grid height.
* @return The number of points actually sampled.
*/
CV_EXPORTS int voxelGridSampling(OutputArray sampled_point_flags, InputArray input_pts,
float length, float width, float height);
/**
* @brief Point cloud sampling by randomly select points.
*
* Use cv::randShuffle to shuffle the point index list,
* then take the points corresponding to the front part of the list.
*
* @param sampled_pts Point cloud after sampling.
* Support cv::Mat(sampled_pts_size, 3, CV_32F), std::vector<cv::Point3f>.
* @param input_pts Original point cloud, vector of Point3 or Mat of size Nx3/3xN.
* @param sampled_pts_size The desired point cloud size after sampling.
* @param rng Optional random number generator used for cv::randShuffle;
* if it is nullptr, theRNG () is used instead.
*/
CV_EXPORTS void randomSampling(OutputArray sampled_pts, InputArray input_pts,
int sampled_pts_size, RNG *rng = nullptr);
/**
* @overload
*
* @param sampled_pts Point cloud after sampling.
* Support cv::Mat(size * sampled_scale, 3, CV_32F), std::vector<cv::Point3f>.
* @param input_pts Original point cloud, vector of Point3 or Mat of size Nx3/3xN.
* @param sampled_scale Range (0, 1), the percentage of the sampled point cloud to the original size,
* that is, sampled size = original size * sampled_scale.
* @param rng Optional random number generator used for cv::randShuffle;
* if it is nullptr, theRNG () is used instead.
*/
CV_EXPORTS void randomSampling(OutputArray sampled_pts, InputArray input_pts,
float sampled_scale, RNG *rng = nullptr);
/**
* @brief Point cloud sampling by Farthest Point Sampling(FPS).
*
* FPS Algorithm:
* + Input: Point cloud *C*, *sampled_pts_size*, *dist_lower_limit*
* + Initialize: Set sampled point cloud S to the empty set
* + Step:
* 1. Randomly take a seed point from C and take it from C to S;
* 2. Find a point in C that is the farthest away from S and take it from C to S;
* (The distance from point to set S is the smallest distance from point to all points in S)
* 3. Repeat *step 2* until the farthest distance of the point in C from S
* is less than *dist_lower_limit*, or the size of S is equal to *sampled_pts_size*.
* + Output: Sampled point cloud S
*
* @param[out] sampled_point_flags Flags of the sampled point, (pass in std::vector<int> or std::vector<char> etc.)
* sampled_point_flags[i] is 1 means i-th point selected, 0 means it is not selected.
* @param input_pts Original point cloud, vector of Point3 or Mat of size Nx3/3xN.
* @param sampled_pts_size The desired point cloud size after sampling.
* @param dist_lower_limit Sampling is terminated early if the distance from
* the farthest point to S is less than dist_lower_limit, default 0.
* @param rng Optional random number generator used for selecting seed point for FPS;
* if it is nullptr, theRNG () is used instead.
* @return The number of points actually sampled.
*/
CV_EXPORTS int farthestPointSampling(OutputArray sampled_point_flags, InputArray input_pts,
int sampled_pts_size, float dist_lower_limit = 0, RNG *rng = nullptr);
/**
* @overload
*
* @param[out] sampled_point_flags Flags of the sampled point, (pass in std::vector<int> or std::vector<char> etc.)
* sampled_point_flags[i] is 1 means i-th point selected, 0 means it is not selected.
* @param input_pts Original point cloud, vector of Point3 or Mat of size Nx3/3xN.
* @param sampled_scale Range (0, 1), the percentage of the sampled point cloud to the original size,
* that is, sampled size = original size * sampled_scale.
* @param dist_lower_limit Sampling is terminated early if the distance from
* the farthest point to S is less than dist_lower_limit, default 0.
* @param rng Optional random number generator used for selecting seed point for FPS;
* if it is nullptr, theRNG () is used instead.
* @return The number of points actually sampled.
*/
CV_EXPORTS int farthestPointSampling(OutputArray sampled_point_flags, InputArray input_pts,
float sampled_scale, float dist_lower_limit = 0, RNG *rng = nullptr);
/**
* @brief Estimate the normal and curvature of each point in point cloud from NN results.
*
* Normal estimation by PCA:
* + Input: Nearest neighbor points of a specific point: \f$ pt\_set \f$
* + Step:
* 1. Calculate the \f$ mean(\bar{x},\bar{y},\bar{z}) \f$ of \f$ pt\_set \f$;
* 2. A 3x3 covariance matrix \f$ cov \f$ is obtained by \f$ mean^T \cdot mean \f$;
* 3. Calculate the eigenvalues(\f$ λ_2 \ge λ_1 \ge λ_0 \f$) and corresponding
* eigenvectors(\f$ v_2, v_1, v_0 \f$) of \f$ cov \f$;
* 4. \f$ v0 \f$ is the normal of the specific point,
* \f$ \frac{λ_0}{λ_0 + λ_1 + λ_2} \f$ is the curvature of the specific point;
* + Output: Normal and curvature of the specific point.
*
* @param[out] normals Normal of each point, support vector<Point3f> and Mat of size Nx3.
* @param[out] curvatures Curvature of each point, support vector<float> and Mat.
* @param input_pts Original point cloud, support vector<Point3f> and Mat of size Nx3/3xN.
* @param nn_idx Index information of nearest neighbors of all points. The first nearest neighbor of
* each point is itself. Support vector<vector<int>>, vector<Mat> and Mat of size NxK.
* If the information in a row is [0, 2, 1, -5, -1, 4, 7 ... negative number], it will
* use only non-negative indexes until it meets a negative number or bound of this row
* i.e. [0, 2, 1].
* @param max_neighbor_num The maximum number of neighbors want to use including itself. Setting to
* a non-positive number or default will use the information from nn_idx.
*/
CV_EXPORTS void normalEstimate(OutputArray normals, OutputArray curvatures, InputArray input_pts,
InputArrayOfArrays nn_idx, int max_neighbor_num = 0);
/**
* @brief Region Growing algorithm in 3D point cloud.
*
* The key idea of region growing is to merge the nearest neighbor points that satisfy a certain
* angle threshold into the same region according to the normal between the two points, so as to
* achieve the purpose of segmentation. For more details, please refer to @cite Rabbani2006SegmentationOP.
*/
class CV_EXPORTS RegionGrowing3D
{
public:
//-------------------------- CREATE -----------------------
static Ptr<RegionGrowing3D> create();
// -------------------------- CONSTRUCTOR, DESTRUCTOR --------------------------
RegionGrowing3D() = default;
virtual ~RegionGrowing3D() = default;
//-------------------------- SEGMENT -----------------------
/**
* @brief Execute segmentation using the Region Growing algorithm.
*
* @param[out] regions_idx Index information of all points in each region, support
* vector<vector<int>>, vector<Mat>.
* @param[out] labels The label corresponds to the model number, 0 means it does not belong to
* any model, range [0, Number of final resultant models obtained]. Support
* vector<int> and Mat.
* @param input_pts Original point cloud, support vector<Point3f> and Mat of size Nx3/3xN.
* @param normals Normal of each point, support vector<Point3f> and Mat of size Nx3.
* @param nn_idx Index information of nearest neighbors of all points. The first nearest
* neighbor of each point is itself. Support vector<vector<int>>, vector<Mat> and
* Mat of size NxK. If the information in a row is
* [0, 2, 1, -5, -1, 4, 7 ... negative number]
* it will use only non-negative indexes until it meets a negative number or bound
* of this row i.e. [0, 2, 1].
* @return Number of final resultant regions obtained by segmentation.
*/
virtual int
segment(OutputArrayOfArrays regions_idx, OutputArray labels, InputArray input_pts,
InputArray normals, InputArrayOfArrays nn_idx) = 0;
//-------------------------- Getter and Setter -----------------------
//! Set the minimum size of region.
//Setting to a non-positive number or default will be unlimited.
virtual void setMinSize(int min_size) = 0;
//! Get the minimum size of region.
virtual int getMinSize() const = 0;
//! Set the maximum size of region.
//Setting to a non-positive number or default will be unlimited.
virtual void setMaxSize(int max_size) = 0;
//! Get the maximum size of region.
virtual int getMaxSize() const = 0;
//! Set whether to use the smoothness mode. Default will be true.
//! If true it will check the angle between the normal of the current point and the normal of its neighbor.
//! Otherwise, it will check the angle between the normal of the seed point and the normal of current neighbor.
virtual void setSmoothModeFlag(bool smooth_mode) = 0;
//! Get whether to use the smoothness mode.
virtual bool getSmoothModeFlag() const = 0;
//! Set threshold value of the angle between normals, the input value is in radian.
//Default will be 30(degree)*PI/180.
virtual void setSmoothnessThreshold(double smoothness_thr) = 0;
//! Get threshold value of the angle between normals.
virtual double getSmoothnessThreshold() const = 0;
//! Set threshold value of curvature. Default will be 0.05.
//! Only points with curvature less than the threshold will be considered to belong to the same region.
//! If the curvature of each point is not set, this option will not work.
virtual void setCurvatureThreshold(double curvature_thr) = 0;
//! Get threshold value of curvature.
virtual double getCurvatureThreshold() const = 0;
//! Set the maximum number of neighbors want to use including itself.
//! Setting to a non-positive number or default will use the information from nn_idx.
virtual void setMaxNumberOfNeighbors(int max_neighbor_num) = 0;
//! Get the maximum number of neighbors including itself.
virtual int getMaxNumberOfNeighbors() const = 0;
//! Set the maximum number of regions you want.
//Setting to a non-positive number or default will be unlimited.
virtual void setNumberOfRegions(int region_num) = 0;
//! Get the maximum number of regions you want.
virtual int getNumberOfRegions() const = 0;
//! Set whether the results need to be sorted in descending order by the number of points.
virtual void setNeedSort(bool need_sort) = 0;
//! Get whether the results need to be sorted you have set.
virtual bool getNeedSort() const = 0;
//! Set the seed points, it will grow according to the seeds.
//! If noArray() is set, the default method will be used:
//! 1. If the curvature of each point is set, the seeds will be sorted in ascending order of curvatures.
//! 2. Otherwise, the natural order of the point cloud will be used.
virtual void setSeeds(InputArray seeds) = 0;
//! Get the seed points.
virtual void getSeeds(OutputArray seeds) const = 0;
//! Set the curvature of each point, support vector<float> and Mat. If not, you can set it to noArray().
virtual void setCurvatures(InputArray curvatures) = 0;
//! Get the curvature of each point if you have set.
virtual void getCurvatures(OutputArray curvatures) const = 0;
};
//! @} _3d
} //end namespace cv
#endif //OPENCV_3D_PTCLOUD_HPP
@@ -0,0 +1,171 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_VOLUME_HPP
#define OPENCV_3D_VOLUME_HPP
#include "volume_settings.hpp"
#include "opencv2/core/affine.hpp"
namespace cv
{
class CV_EXPORTS_W Volume
{
public:
/** @brief Constructor of custom volume.
* @param vtype the volume type [TSDF, HashTSDF, ColorTSDF].
* @param settings the custom settings for volume.
*/
CV_WRAP explicit Volume(VolumeType vtype = VolumeType::TSDF,
const VolumeSettings& settings = VolumeSettings(VolumeType::TSDF));
~Volume();
/** @brief Integrates the input data to the volume.
Camera intrinsics are taken from volume settings structure.
* @param frame the object from which to take depth and image data.
For color TSDF a depth data should be registered with color data, i.e. have the same intrinsics & camera pose.
This can be done using function registerDepth() from 3d module.
* @param pose the pose of camera in global coordinates.
*/
CV_WRAP_AS(integrateFrame) void integrate(const OdometryFrame& frame, InputArray pose);
/** @brief Integrates the input data to the volume.
Camera intrinsics are taken from volume settings structure.
* @param depth the depth image.
* @param pose the pose of camera in global coordinates.
*/
CV_WRAP_AS(integrate) void integrate(InputArray depth, InputArray pose);
/** @brief Integrates the input data to the volume.
Camera intrinsics are taken from volume settings structure.
* @param depth the depth image.
* @param image the color image (only for ColorTSDF).
For color TSDF a depth data should be registered with color data, i.e. have the same intrinsics & camera pose.
This can be done using function registerDepth() from 3d module.
* @param pose the pose of camera in global coordinates.
*/
CV_WRAP_AS(integrateColor) void integrate(InputArray depth, InputArray image, InputArray pose);
// Python bindings do not process noArray() default argument correctly, so the raycast() method is repeated several times
/** @brief Renders the volume contents into an image. The resulting points and normals are in camera's coordinate system.
Rendered image size and camera intrinsics are taken from volume settings structure.
* @param cameraPose the pose of camera in global coordinates.
* @param points image to store rendered points.
* @param normals image to store rendered normals corresponding to points.
*/
CV_WRAP void raycast(InputArray cameraPose, OutputArray points, OutputArray normals) const;
/** @brief Renders the volume contents into an image. The resulting points and normals are in camera's coordinate system.
Rendered image size and camera intrinsics are taken from volume settings structure.
* @param cameraPose the pose of camera in global coordinates.
* @param points image to store rendered points.
* @param normals image to store rendered normals corresponding to points.
* @param colors image to store rendered colors corresponding to points (only for ColorTSDF).
*/
CV_WRAP_AS(raycastColor) void raycast(InputArray cameraPose, OutputArray points, OutputArray normals, OutputArray colors) const;
/** @brief Renders the volume contents into an image. The resulting points and normals are in camera's coordinate system.
Rendered image size and camera intrinsics are taken from volume settings structure.
* @param cameraPose the pose of camera in global coordinates.
* @param height the height of result image
* @param width the width of result image
* @param K camera raycast intrinsics
* @param points image to store rendered points.
* @param normals image to store rendered normals corresponding to points.
*/
CV_WRAP_AS(raycastEx) void raycast(InputArray cameraPose, int height, int width, InputArray K, OutputArray points, OutputArray normals) const;
/** @brief Renders the volume contents into an image. The resulting points and normals are in camera's coordinate system.
Rendered image size and camera intrinsics are taken from volume settings structure.
* @param cameraPose the pose of camera in global coordinates.
* @param height the height of result image
* @param width the width of result image
* @param K camera raycast intrinsics
* @param points image to store rendered points.
* @param normals image to store rendered normals corresponding to points.
* @param colors image to store rendered colors corresponding to points (only for ColorTSDF).
*/
CV_WRAP_AS(raycastExColor) void raycast(InputArray cameraPose, int height, int width, InputArray K, OutputArray points, OutputArray normals, OutputArray colors) const;
/** @brief Extract the all data from volume.
* @param points the input exist point.
* @param normals the storage of normals (corresponding to input points) in the image.
*/
CV_WRAP void fetchNormals(InputArray points, OutputArray normals) const;
/** @brief Extract the all data from volume.
* @param points the storage of all points.
* @param normals the storage of all normals, corresponding to points.
*/
CV_WRAP void fetchPointsNormals(OutputArray points, OutputArray normals) const;
/** @brief Extract the all data from volume.
* @param points the storage of all points.
* @param normals the storage of all normals, corresponding to points.
* @param colors the storage of all colors, corresponding to points (only for ColorTSDF).
*/
CV_WRAP void fetchPointsNormalsColors(OutputArray points, OutputArray normals, OutputArray colors) const;
/** @brief clear all data in volume.
*/
CV_WRAP void reset();
//TODO: remove this
/** @brief return visible blocks in volume.
*/
CV_WRAP int getVisibleBlocks() const;
/** @brief return number of volume units in volume.
*/
CV_WRAP size_t getTotalVolumeUnits() const;
enum BoundingBoxPrecision
{
VOLUME_UNIT = 0,
VOXEL = 1
};
/**
* @brief Gets bounding box in volume coordinates with given precision:
* VOLUME_UNIT - up to volume unit
* VOXEL - up to voxel (currently not supported)
* @param bb 6-float 1d array containing (min_x, min_y, min_z, max_x, max_y, max_z) in volume coordinates
* @param precision bounding box calculation precision
*/
CV_WRAP void getBoundingBox(OutputArray bb, int precision) const;
/**
* @brief Enables or disables new volume unit allocation during integration.
* Makes sense for HashTSDF only.
*/
CV_WRAP void setEnableGrowth(bool v);
/**
* @brief Returns if new volume units are allocated during integration or not.
* Makes sense for HashTSDF only.
*/
CV_WRAP bool getEnableGrowth() const;
class Impl;
private:
Ptr<Impl> impl;
};
} // namespace cv
#endif // include guard
@@ -0,0 +1,208 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_3D_VOLUME_SETTINGS_HPP
#define OPENCV_3D_VOLUME_SETTINGS_HPP
#include <opencv2/core.hpp>
#include <opencv2/geometry/volume.hpp>
namespace cv
{
enum class VolumeType
{
TSDF = 0,
HashTSDF = 1,
ColorTSDF = 2
};
class CV_EXPORTS_W_SIMPLE VolumeSettings
{
public:
/** @brief Constructor of settings for custom Volume type.
* @param volumeType volume type.
*/
CV_WRAP explicit VolumeSettings(VolumeType volumeType = VolumeType::TSDF);
VolumeSettings(const VolumeSettings& vs);
VolumeSettings& operator=(const VolumeSettings&);
~VolumeSettings();
/** @brief Sets the width of the image for integration.
* @param val input value.
*/
CV_WRAP void setIntegrateWidth(int val);
/** @brief Returns the width of the image for integration.
*/
CV_WRAP int getIntegrateWidth() const;
/** @brief Sets the height of the image for integration.
* @param val input value.
*/
CV_WRAP void setIntegrateHeight(int val);
/** @brief Returns the height of the image for integration.
*/
CV_WRAP int getIntegrateHeight() const;
/** @brief Sets the width of the raycasted image, used when user does not provide it at raycast() call.
* @param val input value.
*/
CV_WRAP void setRaycastWidth(int val);
/** @brief Returns the width of the raycasted image, used when user does not provide it at raycast() call.
*/
CV_WRAP int getRaycastWidth() const;
/** @brief Sets the height of the raycasted image, used when user does not provide it at raycast() call.
* @param val input value.
*/
CV_WRAP void setRaycastHeight(int val);
/** @brief Returns the height of the raycasted image, used when user does not provide it at raycast() call.
*/
CV_WRAP int getRaycastHeight() const;
/** @brief Sets depth factor, witch is the number for depth scaling.
* @param val input value.
*/
CV_WRAP void setDepthFactor(float val);
/** @brief Returns depth factor, witch is the number for depth scaling.
*/
CV_WRAP float getDepthFactor() const;
/** @brief Sets the size of voxel.
* @param val input value.
*/
CV_WRAP void setVoxelSize(float val);
/** @brief Returns the size of voxel.
*/
CV_WRAP float getVoxelSize() const;
/** @brief Sets TSDF truncation distance. Distances greater than value from surface will be truncated to 1.0.
* @param val input value.
*/
CV_WRAP void setTsdfTruncateDistance(float val);
/** @brief Returns TSDF truncation distance. Distances greater than value from surface will be truncated to 1.0.
*/
CV_WRAP float getTsdfTruncateDistance() const;
/** @brief Sets threshold for depth truncation in meters. Truncates the depth greater than threshold to 0.
* @param val input value.
*/
CV_WRAP void setMaxDepth(float val);
/** @brief Returns threshold for depth truncation in meters. Truncates the depth greater than threshold to 0.
*/
CV_WRAP float getMaxDepth() const;
/** @brief Sets max number of frames to integrate per voxel.
Represents the max number of frames over which a running average of the TSDF is calculated for a voxel.
* @param val input value.
*/
CV_WRAP void setMaxWeight(int val);
/** @brief Returns max number of frames to integrate per voxel.
Represents the max number of frames over which a running average of the TSDF is calculated for a voxel.
*/
CV_WRAP int getMaxWeight() const;
/** @brief Sets length of single raycast step.
Describes the percentage of voxel length that is skipped per march.
* @param val input value.
*/
CV_WRAP void setRaycastStepFactor(float val);
/** @brief Returns length of single raycast step.
Describes the percentage of voxel length that is skipped per march.
*/
CV_WRAP float getRaycastStepFactor() const;
/** @brief Sets volume pose.
* @param val input value.
*/
CV_WRAP void setVolumePose(InputArray val);
/** @brief Sets volume pose.
* @param val output value.
*/
CV_WRAP void getVolumePose(OutputArray val) const;
/** @brief Resolution of voxel space.
Number of voxels in each dimension.
Applicable only for TSDF Volume.
HashTSDF volume only supports equal resolution in all three dimensions.
* @param val input value.
*/
CV_WRAP void setVolumeResolution(InputArray val);
/** @brief Resolution of voxel space.
Number of voxels in each dimension.
Applicable only for TSDF Volume.
HashTSDF volume only supports equal resolution in all three dimensions.
* @param val output value.
*/
CV_WRAP void getVolumeResolution(OutputArray val) const;
/** @brief Returns 3 integers representing strides by x, y and z dimension.
Can be used to iterate over raw volume unit data.
* @param val output value.
*/
CV_WRAP void getVolumeStrides(OutputArray val) const;
/** @brief Sets intrinsics of camera for integrations.
* Format of input:
* [ fx 0 cx ]
* [ 0 fy cy ]
* [ 0 0 1 ]
* where fx and fy are focus points of Ox and Oy axises, and cx and cy are central points of Ox and Oy axises.
* @param val input value.
*/
CV_WRAP void setCameraIntegrateIntrinsics(InputArray val);
/** @brief Returns intrinsics of camera for integrations.
* Format of output:
* [ fx 0 cx ]
* [ 0 fy cy ]
* [ 0 0 1 ]
* where fx and fy are focus points of Ox and Oy axises, and cx and cy are central points of Ox and Oy axises.
* @param val output value.
*/
CV_WRAP void getCameraIntegrateIntrinsics(OutputArray val) const;
/** @brief Sets camera intrinsics for raycast image which, used when user does not provide them at raycast() call.
* Format of input:
* [ fx 0 cx ]
* [ 0 fy cy ]
* [ 0 0 1 ]
* where fx and fy are focus points of Ox and Oy axises, and cx and cy are central points of Ox and Oy axises.
* @param val input value.
*/
CV_WRAP void setCameraRaycastIntrinsics(InputArray val);
/** @brief Returns camera intrinsics for raycast image, used when user does not provide them at raycast() call.
* Format of output:
* [ fx 0 cx ]
* [ 0 fy cy ]
* [ 0 0 1 ]
* where fx and fy are focus points of Ox and Oy axises, and cx and cy are central points of Ox and Oy axises.
* @param val output value.
*/
CV_WRAP void getCameraRaycastIntrinsics(OutputArray val) const;
class Impl;
private:
Ptr<Impl> impl;
};
}
#endif // !OPENCV_3D_VOLUME_SETTINGS_HPP