【Open3D】如何在CMake/C++中调用Open3D
0. 引言
\qquad
Open3D是点云的开源处理库,支持Python或C++。其Python已有较全的教程,也可以直接使用pip install open3d
直接进行安装,而若想在C++中调用Open3D则麻烦一些,需要满足以下条件:
- Open3D git源代码(本教程针对0.16.1的版本)
- CMake >= 3.20
- clang >= 7
分为以下几步进行:
- 下载Open3D源代码
- 升级CMake和匹配clang(如果有必要)
- build & install Open3D
- 在工程中引用Open3D
我们会将步骤1和3放在一起讲,有需要的朋友可以再回到步骤2。
参考链接1:GitHub中的教程
参考链接2:官网教程
1. 下载和安装Open3D
1.1.步骤详解
git clone --recursive https://github.com/intel-isl/Open3D.git
cd Open3D
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=${HOME}/open3d_install ..
make install -j 12
cd ../..
可能的错误有以下几种:
- make install出现Perimission Denied错误
- clang或CMake版本过低错误
- 找不到libc++或libc++abi
-
make install
如果报错,则改为sudo make install
,Open3D会被安装在~/open3d_install/目录下,同时make install指令将软链接链接到/usr/的各个目录下。 -
如果提示CMake版本过低(如下图)或Clang版本过低,请执行步骤2。
CMake Error at CMakeLists.txt:1 (cmake_minimum_required):
CMake 3.20 or higher is required. You are running version 3.16.3
- 如果提示找不到libc++或libc++abi(如下图)
CMake Error at 3rdparty/find_dependencies.cmake:1317 (message):
Cannot find matching libc++ and libc++abi libraries with version >=7.
Call Stack (most recent call first):
CMakeLists.txt:486 (include)
请按照如下指令安装:
sudo apt-get install libc++-13-dev libc++abi-13-dev
上面版本13也可以替换成更高的版本,通过apt-cache search libc++-
查找最高支持版本即可。
1.2.下载中遇到问题
- 如若git下载过慢,请使用我上传到CSDN的Open3D代码,资源是永久免费的。另外不加
--recursive
也是可以的,编译的时候会再自动下载。 - 在make install阶段Open3D需要下载第三方库约500MB的资源,如若出现下载过慢或失败的情况,请使用我上传到CSDN的资源(永久免费),解压到与build文件夹同目录的位置即可在make阶段跳过下载步骤。
2. 升级CMake和clang
2.1.CMake
\qquad 新版的Open3D要求cmake >= 3.20.1,有很多原生Ubuntu的CMake都是3.16的,这里需要卸载CMake重装。提供一种比较简单的方法:
pip install -U cmake
cmake --version
\qquad
这样就会通过pip下载最新版的cmake了,如果需要特定的版本,也可以加cmake==x.x.x类似的限定。通常pip下载完会提示某某路径未加入bin目录,例如我的就是/home/xxx/.local/cmake
未加入二进制目录,此时运行cmake --version
仍然是旧版本,这种情况需要手动删除旧版本。
*如果未提示bin目录,且运行cmake --version
时出现:
CMake Error: Could not find CMAKE_ROOT !!!
CMake has most likely not been installed correctly.
*则需要手动搜索pip下载的cmake:which cmake
,以此得到它的下载路径。
删除旧版本:
sudo rm -rf /usr/bin/cmake
后面的/usr/bin/cmake
可以通过whereis cmake
命令查询(第一个目录一般就是)
链接新版本:
sudo ln -s ${HOME}/.local/cmake /usr/bin/cmake
此时再运行cmake --version
,应该显示pip下载的CMake最新版本。
2.2.clang
通过clang --version
查询clang的版本,如若版本低于7,请参照该博客下载clang和设置默认版本,一般情况只需要sudo apt-get install clang-10
一条命令即可解决问题。
3. 在新的工程中调用Open3D
新工程里面的CMakeLists.txt怎么写,看个例子就可以了,以和之前的安装路径匹配:
cmake_minimum_required(VERSION 3.20.1)
project(open3d_example)
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_CXX_FLAGS "-std=c++17")
set(Open3D_DIR ${HOME}/open3d_install/lib/cmake/Open3D)
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# Open3D
find_package(Open3D HINTS REQUIRED)
list(APPEND Open3D_LIBRARIES dl)
if (Open3D_FOUND)
message(STATUS "Found Open3D ${Open3D_VERSION}")
link_directories(${Open3D_LIBRARY_DIRS})
endif()
# Add cpp files
add_executable(open3d_test src/open3d_test.cpp)
target_link_libraries(open3d_test ${Open3D_LIBRARIES})
cpp示例文件
// ----------------------------------------------------------------------------
// - Open3D: www.open3d.org -
// ----------------------------------------------------------------------------
// The MIT License (MIT)
//
// Copyright (c) 2018-2021 www.open3d.org
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// ----------------------------------------------------------------------------
#include <Eigen/Dense>
#include <iostream>
#include <memory>
#include "open3d/Open3D.h"
void PrintPointCloud(const open3d::geometry::PointCloud &pointcloud) {
using namespace open3d;
bool pointcloud_has_normal = pointcloud.HasNormals();
utility::LogInfo("Pointcloud has %d points.",
(int)pointcloud.points_.size());
Eigen::Vector3d min_bound = pointcloud.GetMinBound();
Eigen::Vector3d max_bound = pointcloud.GetMaxBound();
utility::LogInfo(
"Bounding box is: ({:.4f}, {:.4f}, {:.4f}) - ({:.4f}, {:.4f}, "
"{:.4f})",
min_bound(0), min_bound(1), min_bound(2), max_bound(0),
max_bound(1), max_bound(2));
for (size_t i = 0; i < pointcloud.points_.size(); i++) {
if (pointcloud_has_normal) {
const Eigen::Vector3d &point = pointcloud.points_[i];
const Eigen::Vector3d &normal = pointcloud.normals_[i];
utility::LogInfo("{:.6f} {:.6f} {:.6f} {:.6f} {:.6f} {:.6f}",
point(0), point(1), point(2), normal(0), normal(1),
normal(2));
} else {
const Eigen::Vector3d &point = pointcloud.points_[i];
utility::LogInfo("{:.6f} {:.6f} {:.6f}", point(0), point(1),
point(2));
}
}
utility::LogInfo("End of the list.");
}
void PrintHelp() {
using namespace open3d;
PrintOpen3DVersion();
// clang-format off
utility::LogInfo("Usage:");
utility::LogInfo(" > PointCloud [pointcloud_filename]");
// clang-format on
utility::LogInfo("");
}
int main(int argc, char *argv[]) {
using namespace open3d;
utility::SetVerbosityLevel(utility::VerbosityLevel::Debug);
if (argc != 2 ||
utility::ProgramOptionExistsAny(argc, argv, {"-h", "--help"})) {
PrintHelp();
return 1;
}
auto pcd = io::CreatePointCloudFromFile(argv[1]);
{
utility::ScopeTimer timer("Normal estimation with KNN20");
for (int i = 0; i < 20; i++) {
pcd->EstimateNormals(open3d::geometry::KDTreeSearchParamKNN(20));
}
}
std::cout << pcd->normals_[0] << std::endl;
std::cout << pcd->normals_[10] << std::endl;
{
utility::ScopeTimer timer("Normal estimation with Radius 0.01666");
for (int i = 0; i < 20; i++) {
pcd->EstimateNormals(
open3d::geometry::KDTreeSearchParamRadius(0.01666));
}
}
std::cout << pcd->normals_[0] << std::endl;
std::cout << pcd->normals_[10] << std::endl;
{
utility::ScopeTimer timer("Normal estimation with Hybrid 0.01666, 60");
for (int i = 0; i < 20; i++) {
pcd->EstimateNormals(
open3d::geometry::KDTreeSearchParamHybrid(0.01666, 60));
}
}
{
utility::ScopeTimer timer("FPFH estimation with Radius 0.25");
// for (int i = 0; i < 20; i++) {
pipelines::registration::ComputeFPFHFeature(
*pcd, open3d::geometry::KDTreeSearchParamRadius(0.25));
//}
}
std::cout << pcd->normals_[0] << std::endl;
std::cout << pcd->normals_[10] << std::endl;
auto downpcd = pcd->VoxelDownSample(0.05);
// 1. test basic pointcloud functions.
geometry::PointCloud pointcloud;
PrintPointCloud(pointcloud);
pointcloud.points_.push_back(Eigen::Vector3d(0.0, 0.0, 0.0));
pointcloud.points_.push_back(Eigen::Vector3d(1.0, 0.0, 0.0));
pointcloud.points_.push_back(Eigen::Vector3d(0.0, 1.0, 0.0));
pointcloud.points_.push_back(Eigen::Vector3d(0.0, 0.0, 1.0));
PrintPointCloud(pointcloud);
// 2. test pointcloud IO.
const std::string filename_xyz("test.xyz");
const std::string filename_ply("test.ply");
if (io::ReadPointCloud(argv[1], pointcloud)) {
utility::LogInfo("Successfully read {}", argv[1]);
/*
geometry::PointCloud pointcloud_copy;
pointcloud_copy.CloneFrom(pointcloud);
if (io::WritePointCloud(filename_xyz, pointcloud)) {
utility::LogInfo("Successfully wrote {}",
filename_xyz.c_str()); } else { utility::LogError("Failed to write
{}", filename_xyz);
}
if (io::WritePointCloud(filename_ply, pointcloud_copy)) {
utility::LogInfo("Successfully wrote {}",
filename_ply); } else { utility::LogError("Failed to write
{}", filename_ply);
}
*/
} else {
utility::LogWarning("Failed to read {}", argv[1]);
}
// 3. test pointcloud visualization
visualization::Visualizer visualizer;
std::shared_ptr<geometry::PointCloud> pointcloud_ptr(
new geometry::PointCloud);
*pointcloud_ptr = pointcloud;
pointcloud_ptr->NormalizeNormals();
auto bounding_box = pointcloud_ptr->GetAxisAlignedBoundingBox();
std::shared_ptr<geometry::PointCloud> pointcloud_transformed_ptr(
new geometry::PointCloud);
*pointcloud_transformed_ptr = *pointcloud_ptr;
Eigen::Matrix4d trans_to_origin = Eigen::Matrix4d::Identity();
trans_to_origin.block<3, 1>(0, 3) = bounding_box.GetCenter() * -1.0;
Eigen::Matrix4d transformation = Eigen::Matrix4d::Identity();
transformation.block<3, 3>(0, 0) = static_cast<Eigen::Matrix3d>(
Eigen::AngleAxisd(M_PI / 4.0, Eigen::Vector3d::UnitX()));
pointcloud_transformed_ptr->Transform(trans_to_origin.inverse() *
transformation * trans_to_origin);
visualizer.CreateVisualizerWindow("Open3D", 1600, 900);
visualizer.AddGeometry(pointcloud_ptr);
visualizer.AddGeometry(pointcloud_transformed_ptr);
visualizer.Run();
visualizer.DestroyVisualizerWindow();
// 4. test operations
*pointcloud_transformed_ptr += *pointcloud_ptr;
visualization::DrawGeometries({pointcloud_transformed_ptr},
"Combined Pointcloud");
// 5. test downsample
auto downsampled = pointcloud_ptr->VoxelDownSample(0.05);
visualization::DrawGeometries({downsampled}, "Down Sampled Pointcloud");
// 6. test normal estimation
visualization::DrawGeometriesWithKeyCallbacks(
{pointcloud_ptr},
{{GLFW_KEY_SPACE,
[&](visualization::Visualizer *vis) {
// EstimateNormals(*pointcloud_ptr,
// open3d::KDTreeSearchParamKNN(20));
pointcloud_ptr->EstimateNormals(
open3d::geometry::KDTreeSearchParamRadius(0.05));
utility::LogInfo("Done.");
return true;
}}},
"Press Space to Estimate Normal", 1600, 900);
// n. test end
utility::LogInfo("End of the test.");
return 0;
}
Cmake编译通过之后,运行./open3d_test xxx.pcd(应该pcd或者ply文件)应该出现如下信息:
[Open3D DEBUG] Format auto File ../download/BunnyMesh/BunnyMesh.ply
[Open3D DEBUG] Read geometry::PointCloud: 35947 vertices.
[Open3D DEBUG] [RemoveNonFinitePoints] 0 nan points have been removed.
[Open3D INFO] Normal estimation with KNN20 took 326.03 ms.
-0.220564
-0.970041
0.10184
-0.369056
0.618998
0.693282
[Open3D INFO] Normal estimation with Radius 0.01666 took 1744.88 ms.
-0.337697
-0.926129
0.168065
-0.193585
0.557339
0.807402
[Open3D INFO] Normal estimation with Hybrid 0.01666, 60 took 312.49 ms.
[Open3D INFO] FPFH estimation with Radius 0.25 took 24324.31 ms.
-0.238001
-0.966113
0.0999029
-0.311548
0.593865
0.741796
[Open3D DEBUG] Pointcloud down sampled from 35947 points to 32 points.
[Open3D INFO] Pointcloud has %d points.
[Open3D INFO] Bounding box is: (0.0000, 0.0000, 0.0000) - (0.0000, 0.0000, 0.0000)
[Open3D INFO] End of the list.
[Open3D INFO] Pointcloud has %d points.
[Open3D INFO] Bounding box is: (0.0000, 0.0000, 0.0000) - (1.0000, 1.0000, 1.0000)
[Open3D INFO] 0.000000 0.000000 0.000000
[Open3D INFO] 1.000000 0.000000 0.000000
[Open3D INFO] 0.000000 1.000000 0.000000
[Open3D INFO] 0.000000 0.000000 1.000000
[Open3D INFO] End of the list.
[Open3D DEBUG] Format auto File ../download/BunnyMesh/BunnyMesh.ply
[Open3D DEBUG] Read geometry::PointCloud: 35947 vertices.
[Open3D INFO] Successfully read ../download/BunnyMesh/BunnyMesh.pl
到此为止就可以证明你的Open3D C++库安装成功且被成功调用了,需注意由于时间推移,本教程针对的版本是open3d 0.16.1,如有新问题或解决方案,欢迎读者在评论区补充。
更多推荐
所有评论(0)