mirror of
https://github.com/opencv/opencv.git
synced 2026-10-03 04:03:30 +03:00
109 lines
4.4 KiB
C++
109 lines
4.4 KiB
C++
// 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) 2026, BigVision LLC, all rights reserved.
|
|
// Third party copyrights are property of their respective owners.
|
|
|
|
// Demonstrates the point-cloud processing pipeline added in the ptcloud module:
|
|
// outlier removal, normal estimation/orientation, ball-pivoting meshing and
|
|
// bounding-box estimation, visualized with cv::viz3d.
|
|
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <cmath>
|
|
#include <opencv2/core.hpp>
|
|
#include <opencv2/highgui.hpp>
|
|
#include <opencv2/geometry.hpp> // cv::normalEstimate
|
|
#include <opencv2/ptcloud.hpp>
|
|
|
|
using namespace cv;
|
|
|
|
// Evenly sampled unit sphere (Fibonacci lattice) with a few injected far outliers.
|
|
// Returned as N x 1, CV_32FC3 - the layout the ptcloud functions expect.
|
|
static Mat makeNoisySphere(int surfacePoints, int outliers)
|
|
{
|
|
std::vector<Point3f> pts;
|
|
pts.reserve(surfacePoints + outliers);
|
|
|
|
const float ga = (float)(CV_PI * (3.0 - std::sqrt(5.0)));
|
|
for (int i = 0; i < surfacePoints; ++i)
|
|
{
|
|
float z = 1.f - 2.f * (i + 0.5f) / surfacePoints;
|
|
float r = std::sqrt(std::max(0.f, 1.f - z * z));
|
|
float t = ga * i;
|
|
pts.emplace_back(r * std::cos(t), r * std::sin(t), z);
|
|
}
|
|
|
|
RNG rng(12345);
|
|
for (int i = 0; i < outliers; ++i)
|
|
pts.emplace_back((float)rng.uniform(-3.0, 3.0),
|
|
(float)rng.uniform(-3.0, 3.0),
|
|
(float)rng.uniform(-3.0, 3.0));
|
|
|
|
return Mat(pts).clone(); // N x 1, CV_32FC3
|
|
}
|
|
|
|
// Attach a uniform color so the cloud can be shown with viz3d::showPoints (expects [x y z r g b]).
|
|
static Mat withColor(const Mat& cloud, const Vec3f& color)
|
|
{
|
|
Mat xyz = cloud.reshape(1, (int)cloud.total()); // N x 3, CV_32F
|
|
Mat rgb(xyz.rows, 1, CV_32FC3, Scalar(color[0], color[1], color[2]));
|
|
Mat out;
|
|
hconcat(xyz, rgb.reshape(1, xyz.rows), out);
|
|
return out; // N x 6, CV_32F
|
|
}
|
|
|
|
int main()
|
|
{
|
|
const int surfacePoints = 3000, outliers = 150;
|
|
Mat cloud = makeNoisySphere(surfacePoints, outliers);
|
|
std::cout << "input cloud: " << cloud.total() << " points (" << outliers << " outliers)" << std::endl;
|
|
|
|
// 1) Statistical outlier removal.
|
|
Mat cleaned;
|
|
removeStatisticalOutliers(cloud, cleaned, 20, 2.0);
|
|
std::cout << "after statistical outlier removal: " << cleaned.total()
|
|
<< " points (removed " << cloud.total() - cleaned.total() << ")" << std::endl;
|
|
|
|
// 2) Mean spacing and normals (estimate, then orient consistently).
|
|
std::cout << "mean spacing: " << estimateMeanSpacing(cleaned) << std::endl;
|
|
|
|
Mat normals, curvatures;
|
|
normalEstimate(normals, curvatures, cleaned, noArray(), 12);
|
|
normals = normals.reshape(3, (int)cleaned.total());
|
|
orientNormalsConsistent(cleaned, normals, 12);
|
|
|
|
// 3) Surface reconstruction via ball pivoting.
|
|
Mat vertices, triangles;
|
|
createMeshBPA(cleaned, normals, vertices, triangles);
|
|
std::cout << "ball-pivoting mesh: " << vertices.total() << " vertices, "
|
|
<< triangles.total() << " triangles" << std::endl;
|
|
|
|
// 4) Bounding volumes.
|
|
Mat center, axes, halfExtents;
|
|
getOrientedBoundingBox(cleaned, center, axes, halfExtents);
|
|
Mat sphereCenter;
|
|
double sphereRadius = getBoundingSphere(cleaned, sphereCenter);
|
|
std::cout << "oriented bounding box half-extents: " << halfExtents.reshape(1, 1) << std::endl;
|
|
std::cout << "bounding sphere radius: " << sphereRadius << std::endl;
|
|
|
|
// 5) Visualize: noisy input (red), cleaned cloud (green), reconstructed mesh.
|
|
viz3d::showPoints("processing", "input", withColor(cloud, {1.0f, 0.2f, 0.2f}));
|
|
viz3d::setObjectPosition("processing", "input", {-3.0f, 0.0f, 0.0f});
|
|
|
|
viz3d::showPoints("processing", "cleaned", withColor(cleaned, {0.2f, 1.0f, 0.2f}));
|
|
|
|
Mat meshVerts = vertices.reshape(1, (int)vertices.total()); // N x 3, CV_32F
|
|
Mat meshTris = triangles.reshape(1, (int)triangles.total()); // M x 3, CV_32S
|
|
viz3d::showMesh("processing", "mesh", meshVerts, meshTris);
|
|
viz3d::setObjectPosition("processing", "mesh", {3.0f, 0.0f, 0.0f});
|
|
|
|
viz3d::setGridVisible("processing", true);
|
|
|
|
std::cout << "Press ESC in the window to exit." << std::endl;
|
|
while (waitKey(16) != 27)
|
|
;
|
|
|
|
return 0;
|
|
}
|