34#include <visp3/vision/vpPlaneEstimation.h>
37#if ((__cplusplus >= 201703L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)))
40#ifdef VISP_HAVE_OPENMP
45#include <visp3/core/vpMeterPixelConversion.h>
46#include <visp3/core/vpPixelMeterConversion.h>
47#include <visp3/core/vpRobust.h>
51#ifndef DOXYGEN_SHOULD_SKIP_THIS
56constexpr auto PlaneSvdMaxError { 1
e-4 };
57constexpr auto PlaneSvdMaxIter { 10 };
59template <
class T> T &make_ref(T &&x) {
return x; }
71#ifdef VISP_HAVE_OPENMP
72 auto num_procs = omp_get_num_procs();
73 num_procs = num_procs > 2 ? num_procs - 2 : num_procs;
74 omp_set_num_threads(num_procs);
77 auto compute_centroid = [=](
const std::vector<double> &point_cloud,
const vpColVector &weights) {
78 double cent_x { 0. }, cent_y { 0. }, cent_z { 0. }, total_w { 0. };
81#ifdef VISP_HAVE_OPENMP
82#pragma omp parallel for num_threads(num_procs) reduction(+ : total_w, cent_x, cent_y, cent_z)
84 for (i = 0; i < static_cast<int>(weights.size());
i++) {
85 const auto pt_cloud_start_idx = 3 *
i;
87 cent_x += weights[
i] * point_cloud[pt_cloud_start_idx + 0];
88 cent_y += weights[
i] * point_cloud[pt_cloud_start_idx + 1];
89 cent_z += weights[
i] * point_cloud[pt_cloud_start_idx + 2];
91 total_w += weights[
i];
94 return std::make_tuple(
vpColVector { cent_x, cent_y, cent_z }, total_w);
98 auto prev_error = 1e3;
99 auto error = prev_error - 1;
100 const unsigned int nPoints =
static_cast<unsigned int>(point_cloud.size() / 3);
109 for (
auto iter = 0u;
iter < PlaneSvdMaxIter && std::fabs(error - prev_error) > PlaneSvdMaxError;
iter++) {
115#if ((__cplusplus > 201703L) || (defined(_MSVC_LANG) && (_MSVC_LANG > 201703L)))
116 auto [centroid, total_w] = compute_centroid(point_cloud, weights);
123 std::tie(centroid, total_w) = compute_centroid(point_cloud, weights);
130#ifdef VISP_HAVE_OPENMP
131#pragma omp parallel for num_threads(num_procs)
133 for (i = 0; i < static_cast<int>(nPoints);
i++) {
134 const auto pt_cloud_start_idx = 3 *
i;
136 M[
i][0] = weights[
i] * (point_cloud[pt_cloud_start_idx + 0] - centroid[0]);
137 M[
i][1] = weights[
i] * (point_cloud[pt_cloud_start_idx + 1] - centroid[1]);
138 M[
i][2] = weights[
i] * (point_cloud[pt_cloud_start_idx + 2] - centroid[2]);
146 auto smallestSv = W[0];
147 auto indexSmallestSv = 0
u;
148 for (
auto i = 1u;
i < W.
size();
i++) {
149 if (W[i] < smallestSv) {
155 normal = V.
getCol(indexSmallestSv);
158 const auto A = normal[0], B = normal[1], C = normal[2];
159 const auto D = -(A * centroid[0] + B * centroid[1] + C * centroid[2]);
164 const auto smth = std::hypot(A, B, C);
166#ifdef VISP_HAVE_OPENMP
167#pragma omp parallel for num_threads(num_procs) reduction(+ : error)
169 for (i = 0; i < static_cast<int>(nPoints);
i++) {
170 const auto pt_cloud_start_idx = 3 *
i;
172 residues[
i] = std::fabs(A * point_cloud[pt_cloud_start_idx + 0] + B * point_cloud[pt_cloud_start_idx + 1] +
173 C * point_cloud[pt_cloud_start_idx + 2] + D) /
176 error += weights[
i] * residues[
i];
186 auto [centroid, total_w] = compute_centroid(point_cloud, weights);
190 const auto A = normal[0], B = normal[1], C = normal[2];
191 const auto D = -(A * centroid[0] + B * centroid[1] + C * centroid[2]);
194 return { A, B, C, D };
208void getHelpers(
const vpPolygon &roi,
const unsigned int &height,
const unsigned int &width,
209 const unsigned int &MaxSubSampFactorToEstimatePlane,
const unsigned int &avg_nb_of_pts_to_estimate,
211 unsigned int &subsample_factor,
212 int &roi_top,
int &roi_bottom,
int &roi_left,
int &roi_right
219 static_cast<double>(height) };
220 for (
const auto &roi_corner : roi.
getCorners()) {
221 if (img_bound.
isInside(roi_corner)) {
241 roi_top =
static_cast<int>(std::max<double>(0., roi_bb.getTop()));
242 roi_bottom =
static_cast<int>(std::min<double>(
static_cast<double>(height), roi_bb.getBottom()));
243 roi_left =
static_cast<int>(std::max<double>(0., roi_bb.getLeft()));
244 roi_right =
static_cast<int>(std::min<double>(
static_cast<double>(width), roi_bb.getRight()));
248 static_cast<int>(sqrt(((roi_right - roi_left) * (roi_bottom - roi_top)) / avg_nb_of_pts_to_estimate));
249 subsample_factor =
vpMath::clamp(subsample_factor, 1u, MaxSubSampFactorToEstimatePlane);
252std::optional<vpPlane> estimatePlaneEquation(
const std::vector<double> &pt_cloud,
const unsigned int &MinPointNbToEstimatePlane,
253 const unsigned int &height,
const unsigned int &width,
const vpCameraParameters &depth_intrinsics,
256 if (pt_cloud.size() < MinPointNbToEstimatePlane) {
263 const auto plane = estimatePlaneEquationSVD(pt_cloud, weights);
267 for (
auto i = 0u;
i < weights.
size();
i++) {
268 const auto X { pt_cloud[3 *
i + 0] }, Y { pt_cloud[3 *
i + 1] }, Z { pt_cloud[3 *
i + 2] };
273 const int b =
static_cast<int>(std::max<double>(0., 255 * (1 - 2 * weights[i])));
274 const int r =
static_cast<int>(std::max<double>(0., 255 * (2 * weights[i] - 1)));
275 const int g = 255 - b -
r;
277 heat_map->get()[
static_cast<int>(ip.
get_i())][
static_cast<int>(ip.
get_j())] =
vpColor(r, g, b);
282 return estimatePlaneEquationSVD(pt_cloud);
288std::optional<vpPlane>
291 const unsigned int avg_nb_of_pts_to_estimate,
294 return estimatePlane(I_depth_raw, depth_scale, depth_intrinsics, roi, std::nullopt, avg_nb_of_pts_to_estimate, heat_map);
297std::optional<vpPlane>
301 const unsigned int avg_nb_of_pts_to_estimate,
313 isValid = [&mask](
const vpImagePoint &ip) {
return (*mask)[
static_cast<unsigned int>(ip.
get_i())][
static_cast<unsigned int>(ip.
get_j())]; };
316 unsigned int subsample_factor = 0;
317 int roi_top = 0, roi_bottom = 0, roi_left = 0, roi_right = 0;
319 MaxSubSampFactorToEstimatePlane, avg_nb_of_pts_to_estimate,
320 isInside, subsample_factor, roi_top, roi_bottom, roi_left, roi_right);
323 std::vector<double> pt_cloud {};
325#if defined(VISP_HAVE_OPENMP) && !(_WIN32)
326 auto num_procs = omp_get_num_procs();
327 num_procs = num_procs > 2 ? num_procs - 2 : num_procs;
328 omp_set_num_threads(num_procs);
331#pragma omp declare reduction (merge : std::vector<double> : omp_out.insert( end( omp_out ), std::make_move_iterator( begin( omp_in ) ), std::make_move_iterator( end( omp_in ) ) ))
332#pragma omp parallel for num_threads(num_procs) collapse(2) reduction(merge : pt_cloud)
334 for (
int i = roi_top; i < roi_bottom; i = i + subsample_factor) {
335 for (
int j = roi_left; j < roi_right; j = j + subsample_factor) {
336 const auto pixel =
vpImagePoint {
static_cast<double>(i),
static_cast<double>(j) };
337 if ((I_depth_raw[i][j] != 0) && isInside(pixel, roi) && isValid(pixel)) {
338 double x { 0. }, y { 0. };
340 const double Z = I_depth_raw[i][j] * depth_scale;
342 pt_cloud.push_back(x * Z);
343 pt_cloud.push_back(y * Z);
344 pt_cloud.push_back(Z);
349 return estimatePlaneEquation(pt_cloud, MinPointNbToEstimatePlane,
351 depth_intrinsics, heat_map);
356 const unsigned int &avg_nb_of_pts_to_estimate,
368 isValid = [&mask](
const vpImagePoint &ip) {
return (*mask)[
static_cast<unsigned int>(ip.
get_i())][
static_cast<unsigned int>(ip.
get_j())]; };
371 unsigned int subsample_factor = 0;
372 int roi_top = 0, roi_bottom = 0, roi_left = 0, roi_right = 0;
373 getHelpers(roi, I_depth.getHeight(), I_depth.getWidth(),
374 MaxSubSampFactorToEstimatePlane, avg_nb_of_pts_to_estimate,
375 isInside, subsample_factor, roi_top, roi_bottom, roi_left, roi_right);
378 std::vector<double> pt_cloud {};
380#if defined(VISP_HAVE_OPENMP) && !(_WIN32)
381 auto num_procs = omp_get_num_procs();
382 num_procs = num_procs > 2 ? num_procs - 2 : num_procs;
383 omp_set_num_threads(num_procs);
386#pragma omp declare reduction (merge : std::vector<double> : omp_out.insert( end( omp_out ), std::make_move_iterator( begin( omp_in ) ), std::make_move_iterator( end( omp_in ) ) ))
387#pragma omp parallel for num_threads(num_procs) collapse(2) reduction(merge : pt_cloud)
389 for (
int i = roi_top; i < roi_bottom; i = i + subsample_factor) {
390 for (
int j = roi_left; j < roi_right; j = j + subsample_factor) {
391 const auto pixel =
vpImagePoint {
static_cast<double>(i),
static_cast<double>(j) };
392 if ((I_depth[i][j] != 0) && isInside(pixel, roi) && isValid(pixel)) {
393 double x { 0. }, y { 0. };
395 const double Z = I_depth[i][j];
397 pt_cloud.push_back(x * Z);
398 pt_cloud.push_back(y * Z);
399 pt_cloud.push_back(Z);
404 return estimatePlaneEquation(pt_cloud, MinPointNbToEstimatePlane,
405 I_depth.getHeight(), I_depth.getWidth(),
406 depth_intrinsics, heat_map);
unsigned int size() const
Return the number of elements of the 2D array.
Generic class defining intrinsic camera parameters.
Implementation of column vector and the associated operations.
Class to define RGB colors available for display functionalities.
static const vpColor black
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition of the vpImage class member functions.
unsigned int getWidth() const
unsigned int getHeight() const
static T clamp(const T &v, const T &lower, const T &upper)
Implementation of a matrix and operations on matrices.
void svd(vpColVector &w, vpMatrix &V)
vpColVector getCol(unsigned int j) const
static void convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v)
static void convertPoint(const vpCameraParameters &cam, const double &u, const double &v, double &x, double &y)
static std::optional< vpPlane > estimatePlane(const vpImage< uint16_t > &I_depth_raw, double depth_scale, const vpCameraParameters &depth_intrinsics, const vpPolygon &roi, const unsigned int avg_nb_of_pts_to_estimate=500, std::optional< std::reference_wrapper< vpImage< vpRGBa > > > heat_map={})
This class defines the container for a plane geometrical structure.
Defines a generic 2D polygon.
vpRect getBoundingBox() const
const std::vector< vpImagePoint > & getCorners() const
bool isInside(const vpImagePoint &iP, const PointInPolygonMethod &method=PnPolyRayCasting) const
Defines a rectangle in the plane.
vpImagePoint getBottomLeft() const
bool isInside(const vpImagePoint &ip) const
vpImagePoint getTopLeft() const
vpImagePoint getTopRight() const
vpImagePoint getBottomRight() const
Contains an M-estimator and various influence function.
@ TUKEY
Tukey influence function.
void MEstimator(const vpRobustEstimatorType method, const vpColVector &residues, vpColVector &weights)
void setMinMedianAbsoluteDeviation(double mad_min)