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
56 constexpr
auto PlaneSvdMaxError { 1e-4 };
57 constexpr
auto PlaneSvdMaxIter { 10 };
59 template <
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 = 0u;
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 };
200 std::optional<vpPlane>
201 vpPlaneEstimation::estimatePlane(
const vpImage<uint16_t> &I_depth_raw,
double depth_scale,
203 const unsigned int avg_nb_of_pts_to_estimate,
206 #ifdef VISP_HAVE_OPENMP
207 auto num_procs = omp_get_num_procs();
208 num_procs = num_procs > 2 ? num_procs - 2 : num_procs;
209 omp_set_num_threads(num_procs);
220 static_cast<double>(I_depth_raw.
getHeight()) };
221 for (
const auto &roi_corner : roi.
getCorners()) {
222 if (img_bound.isInside(roi_corner)) {
230 if (!roi.
isInside(img_bound.getTopLeft()) ||
231 !roi.
isInside(img_bound.getTopRight()) ||
232 !roi.
isInside(img_bound.getBottomLeft()) ||
233 !roi.
isInside(img_bound.getBottomRight()))
242 const int roi_top =
static_cast<int>(std::max<double>(0., roi_bb.getTop()));
243 const int roi_bottom =
static_cast<int>(std::min<double>(
static_cast<double>(I_depth_raw.
getHeight()), roi_bb.getBottom()));
244 const int roi_left =
static_cast<int>(std::max<double>(0., roi_bb.getLeft()));
245 const int roi_right =
static_cast<int>(std::min<double>(
static_cast<double>(I_depth_raw.
getWidth()), roi_bb.getRight()));
248 unsigned int subsample_factor =
249 static_cast<int>(sqrt(((roi_right - roi_left) * (roi_bottom - roi_top)) / avg_nb_of_pts_to_estimate));
250 subsample_factor =
vpMath::clamp(subsample_factor, 1u, MaxSubSampFactorToEstimatePlane);
253 std::vector<double> pt_cloud {};
255 #if defined(VISP_HAVE_OPENMP) && !(_WIN32)
258 #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 ) ) ))
259 #pragma omp parallel for num_threads(num_procs) collapse(2) reduction(merge : pt_cloud)
261 for (
int i = roi_top; i < roi_bottom; i = i + subsample_factor) {
262 for (
int j = roi_left; j < roi_right; j = j + subsample_factor) {
263 const auto pixel =
vpImagePoint {
static_cast<double>(i),
static_cast<double>(j) };
264 if (I_depth_raw[i][j] != 0 && isInside(pixel)) {
265 double x { 0. }, y { 0. };
267 const double Z = I_depth_raw[i][j] * depth_scale;
269 pt_cloud.push_back(x * Z);
270 pt_cloud.push_back(y * Z);
271 pt_cloud.push_back(Z);
276 if (pt_cloud.size() < MinPointNbToEstimatePlane) {
283 const auto plane = estimatePlaneEquationSVD(pt_cloud, weights);
287 for (
auto i = 0u; i < weights.size(); i++) {
288 const auto X { pt_cloud[3 * i + 0] }, Y { pt_cloud[3 * i + 1] }, Z { pt_cloud[3 * i + 2] };
293 const int b =
static_cast<int>(std::max<double>(0., 255 * (1 - 2 * weights[i])));
294 const int r =
static_cast<int>(std::max<double>(0., 255 * (2 * weights[i] - 1)));
295 const int g = 255 - b - r;
297 heat_map->get()[
static_cast<int>(ip.get_i())][
static_cast<int>(ip.get_j())] =
vpColor(r, g, b);
302 return estimatePlaneEquationSVD(pt_cloud);
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)
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)
This class defines the container for a plane geometrical structure.
Defines a generic 2D polygon.
const std::vector< vpImagePoint > & getCorners() const
vpRect getBoundingBox() const
bool isInside(const vpImagePoint &iP, const PointInPolygonMethod &method=PnPolyRayCasting) const
Defines a rectangle in the plane.
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)