duanbotu123
commited on
Commit
·
f6dd1c2
0
Parent(s):
Initial commit: add index.html
Browse files- .gitattributes +39 -0
- .gitignore +28 -0
- 3rdparty/nanoflann.hpp +1305 -0
- CMakeLists.txt +156 -0
- README.md +14 -0
- app.py +638 -0
- apt.txt +16 -0
- build.sh +22 -0
- cmake/FindEigen3.cmake +19 -0
- cmake/FindNanoFlann.cmake +24 -0
- cmake/FindOpenMesh.cmake +27 -0
- cpp/core/nonrigid_spare_registration.cpp +1013 -0
- cpp/core/nonrigid_spare_registration.h +142 -0
- cpp/core/registration.cpp +279 -0
- cpp/core/registration.h +84 -0
- cpp/core/rigid_fricp_registration.cpp +448 -0
- cpp/core/rigid_fricp_registration.h +48 -0
- cpp/io/io.h +94 -0
- cpp/pch.h +21 -0
- cpp/tools/AndersonAcceleration.h +132 -0
- cpp/tools/OmpHelper.h +79 -0
- cpp/tools/Types.h +248 -0
- cpp/tools/geodesic/geodesic_algorithm_base.h +417 -0
- cpp/tools/geodesic/geodesic_algorithm_exact.h +918 -0
- cpp/tools/geodesic/geodesic_algorithm_exact_elements.h +152 -0
- cpp/tools/geodesic/geodesic_constants_and_simple_functions.h +153 -0
- cpp/tools/geodesic/geodesic_memory.h +192 -0
- cpp/tools/median.h +61 -0
- cpp/tools/nanoflann.h +108 -0
- cpp/tools/nodeSampler.cpp +638 -0
- cpp/tools/nodeSampler.h +94 -0
- cpp/tools/parameters.h +92 -0
- cpp/tools/robust_norm.h +37 -0
- cpp/tools/tools.cpp +85 -0
- cpp/tools/tools.h +12 -0
- docs/.gitkeep +1 -0
- examples/.gitkeep +1 -0
- examples/c++/CMakeLists.txt +16 -0
- examples/c++/main.cpp +128 -0
- examples/data/fricp/fricpm3reg_pc.ply +0 -0
- examples/data/fricp/fricpm3trans.txt +4 -0
- examples/data/fricp/source.ply +0 -0
- examples/data/fricp/target.ply +0 -0
- examples/data/spare/source.obj +0 -0
- examples/data/spare/target.obj +0 -0
- examples/python/example_python.py +172 -0
- output/.gitkeep +1 -0
- python/pyregister.cpp +88 -0
- requirements.txt +5 -0
- scripts/compile.sh +5 -0
.gitattributes
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
*.so filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
*.stl filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
*.pm filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
*.om filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 构建目录
|
| 2 |
+
build/
|
| 3 |
+
|
| 4 |
+
# CMake 生成文件
|
| 5 |
+
CMakeFiles/
|
| 6 |
+
CMakeCache.txt
|
| 7 |
+
cmake_install.cmake
|
| 8 |
+
Makefile
|
| 9 |
+
|
| 10 |
+
# Visual Studio 编译产物
|
| 11 |
+
*.o
|
| 12 |
+
*.exe
|
| 13 |
+
*.pdb
|
| 14 |
+
*.lib
|
| 15 |
+
*.dll
|
| 16 |
+
.vs/
|
| 17 |
+
*.vcxproj*
|
| 18 |
+
*.sln
|
| 19 |
+
|
| 20 |
+
# Build outputs
|
| 21 |
+
build/
|
| 22 |
+
*.pdb
|
| 23 |
+
*.lib
|
| 24 |
+
*.ipch
|
| 25 |
+
.vs/
|
| 26 |
+
|
| 27 |
+
# VSCode 配置目录
|
| 28 |
+
.vscode/
|
3rdparty/nanoflann.hpp
ADDED
|
@@ -0,0 +1,1305 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/***********************************************************************
|
| 2 |
+
* Software License Agreement (BSD License)
|
| 3 |
+
*
|
| 4 |
+
* Copyright 2008-2009 Marius Muja ([email protected]). All rights reserved.
|
| 5 |
+
* Copyright 2008-2009 David G. Lowe ([email protected]). All rights reserved.
|
| 6 |
+
* Copyright 2011 Jose Luis Blanco ([email protected]).
|
| 7 |
+
* All rights reserved.
|
| 8 |
+
*
|
| 9 |
+
* THE BSD LICENSE
|
| 10 |
+
*
|
| 11 |
+
* Redistribution and use in source and binary forms, with or without
|
| 12 |
+
* modification, are permitted provided that the following conditions
|
| 13 |
+
* are met:
|
| 14 |
+
*
|
| 15 |
+
* 1. Redistributions of source code must retain the above copyright
|
| 16 |
+
* notice, this list of conditions and the following disclaimer.
|
| 17 |
+
* 2. Redistributions in binary form must reproduce the above copyright
|
| 18 |
+
* notice, this list of conditions and the following disclaimer in the
|
| 19 |
+
* documentation and/or other materials provided with the distribution.
|
| 20 |
+
*
|
| 21 |
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
| 22 |
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
| 23 |
+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
| 24 |
+
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
| 25 |
+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
| 26 |
+
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| 27 |
+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| 28 |
+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 29 |
+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
| 30 |
+
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 31 |
+
*************************************************************************/
|
| 32 |
+
|
| 33 |
+
#ifndef NANOFLANN_HPP_
|
| 34 |
+
#define NANOFLANN_HPP_
|
| 35 |
+
|
| 36 |
+
#include <vector>
|
| 37 |
+
#include <cassert>
|
| 38 |
+
#include <algorithm>
|
| 39 |
+
#include <stdexcept>
|
| 40 |
+
#include <cstdio> // for fwrite()
|
| 41 |
+
#include <cmath> // for fabs(),...
|
| 42 |
+
#include <limits>
|
| 43 |
+
|
| 44 |
+
// Avoid conflicting declaration of min/max macros in windows headers
|
| 45 |
+
#if !defined(NOMINMAX) && (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64))
|
| 46 |
+
# define NOMINMAX
|
| 47 |
+
# ifdef max
|
| 48 |
+
# undef max
|
| 49 |
+
# undef min
|
| 50 |
+
# endif
|
| 51 |
+
#endif
|
| 52 |
+
|
| 53 |
+
namespace nanoflann
|
| 54 |
+
{
|
| 55 |
+
/** @addtogroup nanoflann_grp nanoflann C++ library for ANN
|
| 56 |
+
* @{ */
|
| 57 |
+
|
| 58 |
+
/** Library version: 0xMmP (M=Major,m=minor,P=path) */
|
| 59 |
+
#define NANOFLANN_VERSION 0x113
|
| 60 |
+
|
| 61 |
+
/** @addtogroup result_sets_grp Result set classes
|
| 62 |
+
* @{ */
|
| 63 |
+
template <typename DistanceType, typename IndexType = size_t, typename CountType = size_t>
|
| 64 |
+
class KNNResultSet
|
| 65 |
+
{
|
| 66 |
+
IndexType * indices;
|
| 67 |
+
DistanceType* dists;
|
| 68 |
+
CountType capacity;
|
| 69 |
+
CountType count;
|
| 70 |
+
|
| 71 |
+
public:
|
| 72 |
+
inline KNNResultSet(CountType capacity_) : capacity(capacity_), count(0)
|
| 73 |
+
{
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
inline void init(IndexType* indices_, DistanceType* dists_)
|
| 77 |
+
{
|
| 78 |
+
indices = indices_;
|
| 79 |
+
dists = dists_;
|
| 80 |
+
count = 0;
|
| 81 |
+
dists[capacity-1] = (std::numeric_limits<DistanceType>::max)();
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
inline CountType size() const
|
| 85 |
+
{
|
| 86 |
+
return count;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
inline bool full() const
|
| 90 |
+
{
|
| 91 |
+
return count == capacity;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
inline void addPoint(DistanceType dist, IndexType index)
|
| 96 |
+
{
|
| 97 |
+
CountType i;
|
| 98 |
+
for (i=count; i>0; --i) {
|
| 99 |
+
#ifdef NANOFLANN_FIRST_MATCH // If defined and two poins have the same distance, the one with the lowest-index will be returned first.
|
| 100 |
+
if ( (dists[i-1]>dist) || ((dist==dists[i-1])&&(indices[i-1]>index)) ) {
|
| 101 |
+
#else
|
| 102 |
+
if (dists[i-1]>dist) {
|
| 103 |
+
#endif
|
| 104 |
+
if (i<capacity) {
|
| 105 |
+
dists[i] = dists[i-1];
|
| 106 |
+
indices[i] = indices[i-1];
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
+
else break;
|
| 110 |
+
}
|
| 111 |
+
if (i<capacity) {
|
| 112 |
+
dists[i] = dist;
|
| 113 |
+
indices[i] = index;
|
| 114 |
+
}
|
| 115 |
+
if (count<capacity) count++;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
inline DistanceType worstDist() const
|
| 119 |
+
{
|
| 120 |
+
return dists[capacity-1];
|
| 121 |
+
}
|
| 122 |
+
};
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
/**
|
| 126 |
+
* A result-set class used when performing a radius based search.
|
| 127 |
+
*/
|
| 128 |
+
template <typename DistanceType, typename IndexType = size_t>
|
| 129 |
+
class RadiusResultSet
|
| 130 |
+
{
|
| 131 |
+
public:
|
| 132 |
+
const DistanceType radius;
|
| 133 |
+
|
| 134 |
+
std::vector<std::pair<IndexType,DistanceType> >& m_indices_dists;
|
| 135 |
+
|
| 136 |
+
inline RadiusResultSet(DistanceType radius_, std::vector<std::pair<IndexType,DistanceType> >& indices_dists) : radius(radius_), m_indices_dists(indices_dists)
|
| 137 |
+
{
|
| 138 |
+
init();
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
inline ~RadiusResultSet() { }
|
| 142 |
+
|
| 143 |
+
inline void init() { clear(); }
|
| 144 |
+
inline void clear() { m_indices_dists.clear(); }
|
| 145 |
+
|
| 146 |
+
inline size_t size() const { return m_indices_dists.size(); }
|
| 147 |
+
|
| 148 |
+
inline bool full() const { return true; }
|
| 149 |
+
|
| 150 |
+
inline void addPoint(DistanceType dist, IndexType index)
|
| 151 |
+
{
|
| 152 |
+
if (dist<radius)
|
| 153 |
+
m_indices_dists.push_back(std::make_pair<IndexType,DistanceType>(index,dist));
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
inline DistanceType worstDist() const { return radius; }
|
| 157 |
+
|
| 158 |
+
/** Clears the result set and adjusts the search radius. */
|
| 159 |
+
inline void set_radius_and_clear( const DistanceType r )
|
| 160 |
+
{
|
| 161 |
+
radius = r;
|
| 162 |
+
clear();
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
/**
|
| 166 |
+
* Find the worst result (furtherest neighbor) without copying or sorting
|
| 167 |
+
* Pre-conditions: size() > 0
|
| 168 |
+
*/
|
| 169 |
+
std::pair<IndexType,DistanceType> worst_item() const
|
| 170 |
+
{
|
| 171 |
+
if (m_indices_dists.empty()) throw std::runtime_error("Cannot invoke RadiusResultSet::worst_item() on an empty list of results.");
|
| 172 |
+
typedef typename std::vector<std::pair<IndexType,DistanceType> >::const_iterator DistIt;
|
| 173 |
+
DistIt it = std::max_element(m_indices_dists.begin(), m_indices_dists.end());
|
| 174 |
+
return *it;
|
| 175 |
+
}
|
| 176 |
+
};
|
| 177 |
+
|
| 178 |
+
/** operator "<" for std::sort() */
|
| 179 |
+
struct IndexDist_Sorter
|
| 180 |
+
{
|
| 181 |
+
/** PairType will be typically: std::pair<IndexType,DistanceType> */
|
| 182 |
+
template <typename PairType>
|
| 183 |
+
inline bool operator()(const PairType &p1, const PairType &p2) const {
|
| 184 |
+
return p1.second < p2.second;
|
| 185 |
+
}
|
| 186 |
+
};
|
| 187 |
+
|
| 188 |
+
/** @} */
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
/** @addtogroup loadsave_grp Load/save auxiliary functions
|
| 192 |
+
* @{ */
|
| 193 |
+
template<typename T>
|
| 194 |
+
void save_value(FILE* stream, const T& value, size_t count = 1)
|
| 195 |
+
{
|
| 196 |
+
fwrite(&value, sizeof(value),count, stream);
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
template<typename T>
|
| 200 |
+
void save_value(FILE* stream, const std::vector<T>& value)
|
| 201 |
+
{
|
| 202 |
+
size_t size = value.size();
|
| 203 |
+
fwrite(&size, sizeof(size_t), 1, stream);
|
| 204 |
+
fwrite(&value[0], sizeof(T), size, stream);
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
template<typename T>
|
| 208 |
+
void load_value(FILE* stream, T& value, size_t count = 1)
|
| 209 |
+
{
|
| 210 |
+
size_t read_cnt = fread(&value, sizeof(value), count, stream);
|
| 211 |
+
if (read_cnt != count) {
|
| 212 |
+
throw std::runtime_error("Cannot read from file");
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
template<typename T>
|
| 218 |
+
void load_value(FILE* stream, std::vector<T>& value)
|
| 219 |
+
{
|
| 220 |
+
size_t size;
|
| 221 |
+
size_t read_cnt = fread(&size, sizeof(size_t), 1, stream);
|
| 222 |
+
if (read_cnt!=1) {
|
| 223 |
+
throw std::runtime_error("Cannot read from file");
|
| 224 |
+
}
|
| 225 |
+
value.resize(size);
|
| 226 |
+
read_cnt = fread(&value[0], sizeof(T), size, stream);
|
| 227 |
+
if (read_cnt!=size) {
|
| 228 |
+
throw std::runtime_error("Cannot read from file");
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
/** @} */
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
/** @addtogroup metric_grp Metric (distance) classes
|
| 235 |
+
* @{ */
|
| 236 |
+
|
| 237 |
+
template<typename T> inline T abs(T x) { return (x<0) ? -x : x; }
|
| 238 |
+
template<> inline int abs<int>(int x) { return ::abs(x); }
|
| 239 |
+
template<> inline float abs<float>(float x) { return fabsf(x); }
|
| 240 |
+
template<> inline double abs<double>(double x) { return fabs(x); }
|
| 241 |
+
template<> inline long double abs<long double>(long double x) { return fabsl(x); }
|
| 242 |
+
|
| 243 |
+
/** Manhattan distance functor (generic version, optimized for high-dimensionality data sets).
|
| 244 |
+
* Corresponding distance traits: nanoflann::metric_L1
|
| 245 |
+
* \tparam T Type of the elements (e.g. double, float, uint8_t)
|
| 246 |
+
* \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t)
|
| 247 |
+
*/
|
| 248 |
+
template<class T, class DataSource, typename _DistanceType = T>
|
| 249 |
+
struct L1_Adaptor
|
| 250 |
+
{
|
| 251 |
+
typedef T ElementType;
|
| 252 |
+
typedef _DistanceType DistanceType;
|
| 253 |
+
|
| 254 |
+
const DataSource &data_source;
|
| 255 |
+
|
| 256 |
+
L1_Adaptor(const DataSource &_data_source) : data_source(_data_source) { }
|
| 257 |
+
|
| 258 |
+
inline DistanceType operator()(const T* a, const size_t b_idx, size_t size, DistanceType worst_dist = -1) const
|
| 259 |
+
{
|
| 260 |
+
DistanceType result = DistanceType();
|
| 261 |
+
const T* last = a + size;
|
| 262 |
+
const T* lastgroup = last - 3;
|
| 263 |
+
size_t d = 0;
|
| 264 |
+
|
| 265 |
+
/* Process 4 items with each loop for efficiency. */
|
| 266 |
+
while (a < lastgroup) {
|
| 267 |
+
const DistanceType diff0 = nanoflann::abs(a[0] - data_source.kdtree_get_pt(b_idx,d++));
|
| 268 |
+
const DistanceType diff1 = nanoflann::abs(a[1] - data_source.kdtree_get_pt(b_idx,d++));
|
| 269 |
+
const DistanceType diff2 = nanoflann::abs(a[2] - data_source.kdtree_get_pt(b_idx,d++));
|
| 270 |
+
const DistanceType diff3 = nanoflann::abs(a[3] - data_source.kdtree_get_pt(b_idx,d++));
|
| 271 |
+
result += diff0 + diff1 + diff2 + diff3;
|
| 272 |
+
a += 4;
|
| 273 |
+
if ((worst_dist>0)&&(result>worst_dist)) {
|
| 274 |
+
return result;
|
| 275 |
+
}
|
| 276 |
+
}
|
| 277 |
+
/* Process last 0-3 components. Not needed for standard vector lengths. */
|
| 278 |
+
while (a < last) {
|
| 279 |
+
result += nanoflann::abs( *a++ - data_source.kdtree_get_pt(b_idx,d++) );
|
| 280 |
+
}
|
| 281 |
+
return result;
|
| 282 |
+
}
|
| 283 |
+
|
| 284 |
+
template <typename U, typename V>
|
| 285 |
+
inline DistanceType accum_dist(const U a, const V b, int dim) const
|
| 286 |
+
{
|
| 287 |
+
return (a-b)*(a-b);
|
| 288 |
+
}
|
| 289 |
+
};
|
| 290 |
+
|
| 291 |
+
/** Squared Euclidean distance functor (generic version, optimized for high-dimensionality data sets).
|
| 292 |
+
* Corresponding distance traits: nanoflann::metric_L2
|
| 293 |
+
* \tparam T Type of the elements (e.g. double, float, uint8_t)
|
| 294 |
+
* \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t)
|
| 295 |
+
*/
|
| 296 |
+
template<class T, class DataSource, typename _DistanceType = T>
|
| 297 |
+
struct L2_Adaptor
|
| 298 |
+
{
|
| 299 |
+
typedef T ElementType;
|
| 300 |
+
typedef _DistanceType DistanceType;
|
| 301 |
+
|
| 302 |
+
const DataSource &data_source;
|
| 303 |
+
|
| 304 |
+
L2_Adaptor(const DataSource &_data_source) : data_source(_data_source) { }
|
| 305 |
+
|
| 306 |
+
inline DistanceType operator()(const T* a, const size_t b_idx, size_t size, DistanceType worst_dist = -1) const
|
| 307 |
+
{
|
| 308 |
+
DistanceType result = DistanceType();
|
| 309 |
+
const T* last = a + size;
|
| 310 |
+
const T* lastgroup = last - 3;
|
| 311 |
+
size_t d = 0;
|
| 312 |
+
|
| 313 |
+
/* Process 4 items with each loop for efficiency. */
|
| 314 |
+
while (a < lastgroup) {
|
| 315 |
+
const DistanceType diff0 = a[0] - data_source.kdtree_get_pt(b_idx,d++);
|
| 316 |
+
const DistanceType diff1 = a[1] - data_source.kdtree_get_pt(b_idx,d++);
|
| 317 |
+
const DistanceType diff2 = a[2] - data_source.kdtree_get_pt(b_idx,d++);
|
| 318 |
+
const DistanceType diff3 = a[3] - data_source.kdtree_get_pt(b_idx,d++);
|
| 319 |
+
result += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3;
|
| 320 |
+
a += 4;
|
| 321 |
+
if ((worst_dist>0)&&(result>worst_dist)) {
|
| 322 |
+
return result;
|
| 323 |
+
}
|
| 324 |
+
}
|
| 325 |
+
/* Process last 0-3 components. Not needed for standard vector lengths. */
|
| 326 |
+
while (a < last) {
|
| 327 |
+
const DistanceType diff0 = *a++ - data_source.kdtree_get_pt(b_idx,d++);
|
| 328 |
+
result += diff0 * diff0;
|
| 329 |
+
}
|
| 330 |
+
return result;
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
+
template <typename U, typename V>
|
| 334 |
+
inline DistanceType accum_dist(const U a, const V b, int dim) const
|
| 335 |
+
{
|
| 336 |
+
return (a-b)*(a-b);
|
| 337 |
+
}
|
| 338 |
+
};
|
| 339 |
+
|
| 340 |
+
/** Squared Euclidean distance functor (suitable for low-dimensionality datasets, like 2D or 3D point clouds)
|
| 341 |
+
* Corresponding distance traits: nanoflann::metric_L2_Simple
|
| 342 |
+
* \tparam T Type of the elements (e.g. double, float, uint8_t)
|
| 343 |
+
* \tparam DistanceType Type of distance variables (must be signed) (e.g. float, double, int64_t)
|
| 344 |
+
*/
|
| 345 |
+
template<class T, class DataSource, typename _DistanceType = T>
|
| 346 |
+
struct L2_Simple_Adaptor
|
| 347 |
+
{
|
| 348 |
+
typedef T ElementType;
|
| 349 |
+
typedef _DistanceType DistanceType;
|
| 350 |
+
|
| 351 |
+
const DataSource &data_source;
|
| 352 |
+
|
| 353 |
+
L2_Simple_Adaptor(const DataSource &_data_source) : data_source(_data_source) { }
|
| 354 |
+
|
| 355 |
+
inline DistanceType operator()(const T* a, const size_t b_idx, size_t size) const {
|
| 356 |
+
return data_source.kdtree_distance(a,b_idx,size);
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
template <typename U, typename V>
|
| 360 |
+
inline DistanceType accum_dist(const U a, const V b, int dim) const
|
| 361 |
+
{
|
| 362 |
+
dim = dim;
|
| 363 |
+
return (a-b)*(a-b);
|
| 364 |
+
}
|
| 365 |
+
};
|
| 366 |
+
|
| 367 |
+
/** Metaprogramming helper traits class for the L1 (Manhattan) metric */
|
| 368 |
+
struct metric_L1 {
|
| 369 |
+
template<class T, class DataSource>
|
| 370 |
+
struct traits {
|
| 371 |
+
typedef L1_Adaptor<T,DataSource> distance_t;
|
| 372 |
+
};
|
| 373 |
+
};
|
| 374 |
+
/** Metaprogramming helper traits class for the L2 (Euclidean) metric */
|
| 375 |
+
struct metric_L2 {
|
| 376 |
+
template<class T, class DataSource>
|
| 377 |
+
struct traits {
|
| 378 |
+
typedef L2_Adaptor<T,DataSource> distance_t;
|
| 379 |
+
};
|
| 380 |
+
};
|
| 381 |
+
/** Metaprogramming helper traits class for the L2_simple (Euclidean) metric */
|
| 382 |
+
struct metric_L2_Simple {
|
| 383 |
+
template<class T, class DataSource>
|
| 384 |
+
struct traits {
|
| 385 |
+
typedef L2_Simple_Adaptor<T,DataSource> distance_t;
|
| 386 |
+
};
|
| 387 |
+
};
|
| 388 |
+
|
| 389 |
+
/** @} */
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
/** @addtogroup param_grp Parameter structs
|
| 394 |
+
* @{ */
|
| 395 |
+
|
| 396 |
+
/** Parameters (see http://code.google.com/p/nanoflann/ for help choosing the parameters)
|
| 397 |
+
*/
|
| 398 |
+
struct KDTreeSingleIndexAdaptorParams
|
| 399 |
+
{
|
| 400 |
+
KDTreeSingleIndexAdaptorParams(size_t _leaf_max_size = 10, int dim_ = -1) :
|
| 401 |
+
leaf_max_size(_leaf_max_size), dim(dim_)
|
| 402 |
+
{}
|
| 403 |
+
|
| 404 |
+
size_t leaf_max_size;
|
| 405 |
+
int dim;
|
| 406 |
+
};
|
| 407 |
+
|
| 408 |
+
/** Search options for KDTreeSingleIndexAdaptor::findNeighbors() */
|
| 409 |
+
struct SearchParams
|
| 410 |
+
{
|
| 411 |
+
/** Note: The first argument (checks_IGNORED_) is ignored, but kept for compatibility with the FLANN interface */
|
| 412 |
+
SearchParams(int checks_IGNORED_ = 32, float eps_ = 0, bool sorted_ = true ) :
|
| 413 |
+
eps(eps_), sorted(sorted_) {
|
| 414 |
+
checks_IGNORED_ = checks_IGNORED_;
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
int checks; //!< Ignored parameter (Kept for compatibility with the FLANN interface).
|
| 418 |
+
float eps; //!< search for eps-approximate neighbours (default: 0)
|
| 419 |
+
bool sorted; //!< only for radius search, require neighbours sorted by distance (default: true)
|
| 420 |
+
};
|
| 421 |
+
/** @} */
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
/** @addtogroup memalloc_grp Memory allocation
|
| 425 |
+
* @{ */
|
| 426 |
+
|
| 427 |
+
/**
|
| 428 |
+
* Allocates (using C's malloc) a generic type T.
|
| 429 |
+
*
|
| 430 |
+
* Params:
|
| 431 |
+
* count = number of instances to allocate.
|
| 432 |
+
* Returns: pointer (of type T*) to memory buffer
|
| 433 |
+
*/
|
| 434 |
+
template <typename T>
|
| 435 |
+
inline T* allocate(size_t count = 1)
|
| 436 |
+
{
|
| 437 |
+
T* mem = (T*) ::malloc(sizeof(T)*count);
|
| 438 |
+
return mem;
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
/**
|
| 443 |
+
* Pooled storage allocator
|
| 444 |
+
*
|
| 445 |
+
* The following routines allow for the efficient allocation of storage in
|
| 446 |
+
* small chunks from a specified pool. Rather than allowing each structure
|
| 447 |
+
* to be freed individually, an entire pool of storage is freed at once.
|
| 448 |
+
* This method has two advantages over just using malloc() and free(). First,
|
| 449 |
+
* it is far more efficient for allocating small objects, as there is
|
| 450 |
+
* no overhead for remembering all the information needed to free each
|
| 451 |
+
* object or consolidating fragmented memory. Second, the decision about
|
| 452 |
+
* how long to keep an object is made at the time of allocation, and there
|
| 453 |
+
* is no need to track down all the objects to free them.
|
| 454 |
+
*
|
| 455 |
+
*/
|
| 456 |
+
|
| 457 |
+
const size_t WORDSIZE=16;
|
| 458 |
+
const size_t BLOCKSIZE=8192;
|
| 459 |
+
|
| 460 |
+
class PooledAllocator
|
| 461 |
+
{
|
| 462 |
+
/* We maintain memory alignment to word boundaries by requiring that all
|
| 463 |
+
allocations be in multiples of the machine wordsize. */
|
| 464 |
+
/* Size of machine word in bytes. Must be power of 2. */
|
| 465 |
+
/* Minimum number of bytes requested at a time from the system. Must be multiple of WORDSIZE. */
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
size_t remaining; /* Number of bytes left in current block of storage. */
|
| 469 |
+
void* base; /* Pointer to base of current block of storage. */
|
| 470 |
+
void* loc; /* Current location in block to next allocate memory. */
|
| 471 |
+
size_t blocksize;
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
public:
|
| 475 |
+
size_t usedMemory;
|
| 476 |
+
size_t wastedMemory;
|
| 477 |
+
|
| 478 |
+
/**
|
| 479 |
+
Default constructor. Initializes a new pool.
|
| 480 |
+
*/
|
| 481 |
+
PooledAllocator(const size_t blocksize = BLOCKSIZE)
|
| 482 |
+
{
|
| 483 |
+
this->blocksize = blocksize;
|
| 484 |
+
remaining = 0;
|
| 485 |
+
base = NULL;
|
| 486 |
+
|
| 487 |
+
usedMemory = 0;
|
| 488 |
+
wastedMemory = 0;
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
/**
|
| 492 |
+
* Destructor. Frees all the memory allocated in this pool.
|
| 493 |
+
*/
|
| 494 |
+
~PooledAllocator()
|
| 495 |
+
{
|
| 496 |
+
while (base != NULL) {
|
| 497 |
+
void *prev = *((void**) base); /* Get pointer to prev block. */
|
| 498 |
+
::free(base);
|
| 499 |
+
base = prev;
|
| 500 |
+
}
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
/**
|
| 504 |
+
* Returns a pointer to a piece of new memory of the given size in bytes
|
| 505 |
+
* allocated from the pool.
|
| 506 |
+
*/
|
| 507 |
+
void* malloc(const size_t req_size)
|
| 508 |
+
{
|
| 509 |
+
/* Round size up to a multiple of wordsize. The following expression
|
| 510 |
+
only works for WORDSIZE that is a power of 2, by masking last bits of
|
| 511 |
+
incremented size to zero.
|
| 512 |
+
*/
|
| 513 |
+
const size_t size = (req_size + (WORDSIZE - 1)) & ~(WORDSIZE - 1);
|
| 514 |
+
|
| 515 |
+
/* Check whether a new block must be allocated. Note that the first word
|
| 516 |
+
of a block is reserved for a pointer to the previous block.
|
| 517 |
+
*/
|
| 518 |
+
if (size > remaining) {
|
| 519 |
+
|
| 520 |
+
wastedMemory += remaining;
|
| 521 |
+
|
| 522 |
+
/* Allocate new storage. */
|
| 523 |
+
const size_t blocksize = (size + sizeof(void*) + (WORDSIZE-1) > BLOCKSIZE) ?
|
| 524 |
+
size + sizeof(void*) + (WORDSIZE-1) : BLOCKSIZE;
|
| 525 |
+
|
| 526 |
+
// use the standard C malloc to allocate memory
|
| 527 |
+
void* m = ::malloc(blocksize);
|
| 528 |
+
if (!m) {
|
| 529 |
+
fprintf(stderr,"Failed to allocate memory.\n");
|
| 530 |
+
return NULL;
|
| 531 |
+
}
|
| 532 |
+
|
| 533 |
+
/* Fill first word of new block with pointer to previous block. */
|
| 534 |
+
((void**) m)[0] = base;
|
| 535 |
+
base = m;
|
| 536 |
+
|
| 537 |
+
size_t shift = 0;
|
| 538 |
+
//int size_t = (WORDSIZE - ( (((size_t)m) + sizeof(void*)) & (WORDSIZE-1))) & (WORDSIZE-1);
|
| 539 |
+
|
| 540 |
+
remaining = blocksize - sizeof(void*) - shift;
|
| 541 |
+
loc = ((char*)m + sizeof(void*) + shift);
|
| 542 |
+
}
|
| 543 |
+
void* rloc = loc;
|
| 544 |
+
loc = (char*)loc + size;
|
| 545 |
+
remaining -= size;
|
| 546 |
+
|
| 547 |
+
usedMemory += size;
|
| 548 |
+
|
| 549 |
+
return rloc;
|
| 550 |
+
}
|
| 551 |
+
|
| 552 |
+
/**
|
| 553 |
+
* Allocates (using this pool) a generic type T.
|
| 554 |
+
*
|
| 555 |
+
* Params:
|
| 556 |
+
* count = number of instances to allocate.
|
| 557 |
+
* Returns: pointer (of type T*) to memory buffer
|
| 558 |
+
*/
|
| 559 |
+
template <typename T>
|
| 560 |
+
T* allocate(const size_t count = 1)
|
| 561 |
+
{
|
| 562 |
+
T* mem = (T*) this->malloc(sizeof(T)*count);
|
| 563 |
+
return mem;
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
};
|
| 567 |
+
/** @} */
|
| 568 |
+
|
| 569 |
+
|
| 570 |
+
/** @addtogroup kdtrees_grp KD-tree classes and adaptors
|
| 571 |
+
* @{ */
|
| 572 |
+
|
| 573 |
+
/** kd-tree index
|
| 574 |
+
*
|
| 575 |
+
* Contains the k-d trees and other information for indexing a set of points
|
| 576 |
+
* for nearest-neighbor matching.
|
| 577 |
+
*
|
| 578 |
+
* The class "DatasetAdaptor" must provide the following interface (can be non-virtual, inlined methods):
|
| 579 |
+
*
|
| 580 |
+
* \code
|
| 581 |
+
* // Must return the number of data points
|
| 582 |
+
* inline size_t kdtree_get_point_count() const { ... }
|
| 583 |
+
*
|
| 584 |
+
* // Must return the Euclidean (L2) distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class:
|
| 585 |
+
* inline DistanceType kdtree_distance(const T *p1, const size_t idx_p2,size_t size) const { ... }
|
| 586 |
+
*
|
| 587 |
+
* // Must return the dim'th component of the idx'th point in the class:
|
| 588 |
+
* inline T kdtree_get_pt(const size_t idx, int dim) const { ... }
|
| 589 |
+
*
|
| 590 |
+
* // Optional bounding-box computation: return false to default to a standard bbox computation loop.
|
| 591 |
+
* // Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it again.
|
| 592 |
+
* // Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 for point clouds)
|
| 593 |
+
* template <class BBOX>
|
| 594 |
+
* bool kdtree_get_bbox(BBOX &bb) const
|
| 595 |
+
* {
|
| 596 |
+
* bb[0].low = ...; bb[0].high = ...; // 0th dimension limits
|
| 597 |
+
* bb[1].low = ...; bb[1].high = ...; // 1st dimension limits
|
| 598 |
+
* ...
|
| 599 |
+
* return true;
|
| 600 |
+
* }
|
| 601 |
+
*
|
| 602 |
+
* \endcode
|
| 603 |
+
*
|
| 604 |
+
* \tparam IndexType Will be typically size_t or int
|
| 605 |
+
*/
|
| 606 |
+
template <typename Distance, class DatasetAdaptor,int DIM = -1, typename IndexType = size_t>
|
| 607 |
+
class KDTreeSingleIndexAdaptor
|
| 608 |
+
{
|
| 609 |
+
public:
|
| 610 |
+
typedef typename Distance::ElementType ElementType;
|
| 611 |
+
typedef typename Distance::DistanceType DistanceType;
|
| 612 |
+
protected:
|
| 613 |
+
|
| 614 |
+
/**
|
| 615 |
+
* Array of indices to vectors in the dataset.
|
| 616 |
+
*/
|
| 617 |
+
std::vector<IndexType> vind;
|
| 618 |
+
|
| 619 |
+
size_t m_leaf_max_size;
|
| 620 |
+
|
| 621 |
+
|
| 622 |
+
/**
|
| 623 |
+
* The dataset used by this index
|
| 624 |
+
*/
|
| 625 |
+
const DatasetAdaptor &dataset; //!< The source of our data
|
| 626 |
+
|
| 627 |
+
const KDTreeSingleIndexAdaptorParams index_params;
|
| 628 |
+
|
| 629 |
+
size_t m_size;
|
| 630 |
+
int dim; //!< Dimensionality of each data point
|
| 631 |
+
|
| 632 |
+
|
| 633 |
+
/*--------------------- Internal Data Structures --------------------------*/
|
| 634 |
+
struct Node
|
| 635 |
+
{
|
| 636 |
+
union {
|
| 637 |
+
struct
|
| 638 |
+
{
|
| 639 |
+
/**
|
| 640 |
+
* Indices of points in leaf node
|
| 641 |
+
*/
|
| 642 |
+
IndexType left, right;
|
| 643 |
+
} lr;
|
| 644 |
+
struct
|
| 645 |
+
{
|
| 646 |
+
/**
|
| 647 |
+
* Dimension used for subdivision.
|
| 648 |
+
*/
|
| 649 |
+
int divfeat;
|
| 650 |
+
/**
|
| 651 |
+
* The values used for subdivision.
|
| 652 |
+
*/
|
| 653 |
+
DistanceType divlow, divhigh;
|
| 654 |
+
} sub;
|
| 655 |
+
};
|
| 656 |
+
/**
|
| 657 |
+
* The child nodes.
|
| 658 |
+
*/
|
| 659 |
+
Node* child1, * child2;
|
| 660 |
+
};
|
| 661 |
+
typedef Node* NodePtr;
|
| 662 |
+
|
| 663 |
+
|
| 664 |
+
struct Interval
|
| 665 |
+
{
|
| 666 |
+
ElementType low, high;
|
| 667 |
+
};
|
| 668 |
+
|
| 669 |
+
typedef std::vector<Interval> BoundingBox;
|
| 670 |
+
|
| 671 |
+
|
| 672 |
+
/** This record represents a branch point when finding neighbors in
|
| 673 |
+
the tree. It contains a record of the minimum distance to the query
|
| 674 |
+
point, as well as the node at which the search resumes.
|
| 675 |
+
*/
|
| 676 |
+
template <typename T, typename DistanceType>
|
| 677 |
+
struct BranchStruct
|
| 678 |
+
{
|
| 679 |
+
T node; /* Tree node at which search resumes */
|
| 680 |
+
DistanceType mindist; /* Minimum distance to query for all nodes below. */
|
| 681 |
+
|
| 682 |
+
BranchStruct() {}
|
| 683 |
+
BranchStruct(const T& aNode, DistanceType dist) : node(aNode), mindist(dist) {}
|
| 684 |
+
|
| 685 |
+
inline bool operator<(const BranchStruct<T, DistanceType>& rhs) const
|
| 686 |
+
{
|
| 687 |
+
return mindist<rhs.mindist;
|
| 688 |
+
}
|
| 689 |
+
};
|
| 690 |
+
|
| 691 |
+
/**
|
| 692 |
+
* Array of k-d trees used to find neighbours.
|
| 693 |
+
*/
|
| 694 |
+
NodePtr root_node;
|
| 695 |
+
typedef BranchStruct<NodePtr, DistanceType> BranchSt;
|
| 696 |
+
typedef BranchSt* Branch;
|
| 697 |
+
|
| 698 |
+
BoundingBox root_bbox;
|
| 699 |
+
|
| 700 |
+
/**
|
| 701 |
+
* Pooled memory allocator.
|
| 702 |
+
*
|
| 703 |
+
* Using a pooled memory allocator is more efficient
|
| 704 |
+
* than allocating memory directly when there is a large
|
| 705 |
+
* number small of memory allocations.
|
| 706 |
+
*/
|
| 707 |
+
PooledAllocator pool;
|
| 708 |
+
|
| 709 |
+
public:
|
| 710 |
+
|
| 711 |
+
Distance distance;
|
| 712 |
+
|
| 713 |
+
/**
|
| 714 |
+
* KDTree constructor
|
| 715 |
+
*
|
| 716 |
+
* Params:
|
| 717 |
+
* inputData = dataset with the input features
|
| 718 |
+
* params = parameters passed to the kdtree algorithm (see http://code.google.com/p/nanoflann/ for help choosing the parameters)
|
| 719 |
+
*/
|
| 720 |
+
KDTreeSingleIndexAdaptor(const int dimensionality, const DatasetAdaptor& inputData, const KDTreeSingleIndexAdaptorParams& params = KDTreeSingleIndexAdaptorParams() ) :
|
| 721 |
+
dataset(inputData), index_params(params), distance(inputData)
|
| 722 |
+
{
|
| 723 |
+
m_size = dataset.kdtree_get_point_count();
|
| 724 |
+
dim = dimensionality;
|
| 725 |
+
if (DIM>0) dim=DIM;
|
| 726 |
+
else {
|
| 727 |
+
if (params.dim>0) dim = params.dim;
|
| 728 |
+
}
|
| 729 |
+
m_leaf_max_size = params.leaf_max_size;
|
| 730 |
+
|
| 731 |
+
// Create a permutable array of indices to the input vectors.
|
| 732 |
+
init_vind();
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
/**
|
| 736 |
+
* Standard destructor
|
| 737 |
+
*/
|
| 738 |
+
~KDTreeSingleIndexAdaptor()
|
| 739 |
+
{
|
| 740 |
+
}
|
| 741 |
+
|
| 742 |
+
/**
|
| 743 |
+
* Builds the index
|
| 744 |
+
*/
|
| 745 |
+
void buildIndex()
|
| 746 |
+
{
|
| 747 |
+
init_vind();
|
| 748 |
+
computeBoundingBox(root_bbox);
|
| 749 |
+
root_node = divideTree(0, m_size, root_bbox ); // construct the tree
|
| 750 |
+
}
|
| 751 |
+
|
| 752 |
+
/**
|
| 753 |
+
* Returns size of index.
|
| 754 |
+
*/
|
| 755 |
+
size_t size() const
|
| 756 |
+
{
|
| 757 |
+
return m_size;
|
| 758 |
+
}
|
| 759 |
+
|
| 760 |
+
/**
|
| 761 |
+
* Returns the length of an index feature.
|
| 762 |
+
*/
|
| 763 |
+
size_t veclen() const
|
| 764 |
+
{
|
| 765 |
+
return static_cast<size_t>(DIM>0 ? DIM : dim);
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
/**
|
| 769 |
+
* Computes the inde memory usage
|
| 770 |
+
* Returns: memory used by the index
|
| 771 |
+
*/
|
| 772 |
+
size_t usedMemory() const
|
| 773 |
+
{
|
| 774 |
+
return pool.usedMemory+pool.wastedMemory+dataset.kdtree_get_point_count()*sizeof(IndexType); // pool memory and vind array memory
|
| 775 |
+
}
|
| 776 |
+
|
| 777 |
+
/** \name Query methods
|
| 778 |
+
* @{ */
|
| 779 |
+
|
| 780 |
+
/**
|
| 781 |
+
* Find set of nearest neighbors to vec[0:dim-1]. Their indices are stored inside
|
| 782 |
+
* the result object.
|
| 783 |
+
*
|
| 784 |
+
* Params:
|
| 785 |
+
* result = the result object in which the indices of the nearest-neighbors are stored
|
| 786 |
+
* vec = the vector for which to search the nearest neighbors
|
| 787 |
+
*
|
| 788 |
+
* \tparam RESULTSET Should be any ResultSet<DistanceType>
|
| 789 |
+
* \sa knnSearch, radiusSearch
|
| 790 |
+
*/
|
| 791 |
+
template <typename RESULTSET>
|
| 792 |
+
void findNeighbors(RESULTSET& result, const ElementType* vec, const SearchParams& searchParams) const
|
| 793 |
+
{
|
| 794 |
+
assert(vec);
|
| 795 |
+
float epsError = 1+searchParams.eps;
|
| 796 |
+
|
| 797 |
+
std::vector<DistanceType> dists( (DIM>0 ? DIM : dim) ,0);
|
| 798 |
+
DistanceType distsq = computeInitialDistances(vec, dists);
|
| 799 |
+
searchLevel(result, vec, root_node, distsq, dists, epsError); // "count_leaf" parameter removed since was neither used nor returned to the user.
|
| 800 |
+
}
|
| 801 |
+
|
| 802 |
+
/**
|
| 803 |
+
* Find the "num_closest" nearest neighbors to the \a query_point[0:dim-1]. Their indices are stored inside
|
| 804 |
+
* the result object.
|
| 805 |
+
* \sa radiusSearch, findNeighbors
|
| 806 |
+
* \note nChecks_IGNORED is ignored but kept for compatibility with the original FLANN interface.
|
| 807 |
+
*/
|
| 808 |
+
inline void knnSearch(const ElementType *query_point, const size_t num_closest, IndexType *out_indices, DistanceType *out_distances_sq) const
|
| 809 |
+
{
|
| 810 |
+
nanoflann::KNNResultSet<DistanceType,IndexType> resultSet(num_closest);
|
| 811 |
+
resultSet.init(out_indices, out_distances_sq);
|
| 812 |
+
this->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
|
| 813 |
+
}
|
| 814 |
+
|
| 815 |
+
/**
|
| 816 |
+
* Find all the neighbors to \a query_point[0:dim-1] within a maximum radius.
|
| 817 |
+
* The output is given as a vector of pairs, of which the first element is a point index and the second the corresponding distance.
|
| 818 |
+
* Previous contents of \a IndicesDists are cleared.
|
| 819 |
+
*
|
| 820 |
+
* If searchParams.sorted==true, the output list is sorted by ascending distances.
|
| 821 |
+
*
|
| 822 |
+
* For a better performance, it is advisable to do a .reserve() on the vector if you have any wild guess about the number of expected matches.
|
| 823 |
+
*
|
| 824 |
+
* \sa knnSearch, findNeighbors
|
| 825 |
+
* \return The number of points within the given radius (i.e. indices.size() or dists.size() )
|
| 826 |
+
*/
|
| 827 |
+
size_t radiusSearch(const ElementType *query_point,const DistanceType radius, std::vector<std::pair<IndexType,DistanceType> >& IndicesDists, const SearchParams& searchParams) const
|
| 828 |
+
{
|
| 829 |
+
RadiusResultSet<DistanceType,IndexType> resultSet(radius,IndicesDists);
|
| 830 |
+
this->findNeighbors(resultSet, query_point, searchParams);
|
| 831 |
+
|
| 832 |
+
if (searchParams.sorted)
|
| 833 |
+
std::sort(IndicesDists.begin(),IndicesDists.end(), IndexDist_Sorter() );
|
| 834 |
+
|
| 835 |
+
return resultSet.size();
|
| 836 |
+
}
|
| 837 |
+
|
| 838 |
+
/** @} */
|
| 839 |
+
|
| 840 |
+
private:
|
| 841 |
+
/** Make sure the auxiliary list \a vind has the same size than the current dataset, and re-generate if size has changed. */
|
| 842 |
+
void init_vind()
|
| 843 |
+
{
|
| 844 |
+
// Create a permutable array of indices to the input vectors.
|
| 845 |
+
m_size = dataset.kdtree_get_point_count();
|
| 846 |
+
if (vind.size()!=m_size)
|
| 847 |
+
{
|
| 848 |
+
vind.resize(m_size);
|
| 849 |
+
for (size_t i = 0; i < m_size; i++) vind[i] = i;
|
| 850 |
+
}
|
| 851 |
+
}
|
| 852 |
+
|
| 853 |
+
/// Helper accessor to the dataset points:
|
| 854 |
+
inline ElementType dataset_get(size_t idx, int component) const {
|
| 855 |
+
return dataset.kdtree_get_pt(idx,component);
|
| 856 |
+
}
|
| 857 |
+
|
| 858 |
+
|
| 859 |
+
void save_tree(FILE* stream, NodePtr tree)
|
| 860 |
+
{
|
| 861 |
+
save_value(stream, *tree);
|
| 862 |
+
if (tree->child1!=NULL) {
|
| 863 |
+
save_tree(stream, tree->child1);
|
| 864 |
+
}
|
| 865 |
+
if (tree->child2!=NULL) {
|
| 866 |
+
save_tree(stream, tree->child2);
|
| 867 |
+
}
|
| 868 |
+
}
|
| 869 |
+
|
| 870 |
+
|
| 871 |
+
void load_tree(FILE* stream, NodePtr& tree)
|
| 872 |
+
{
|
| 873 |
+
tree = pool.allocate<Node>();
|
| 874 |
+
load_value(stream, *tree);
|
| 875 |
+
if (tree->child1!=NULL) {
|
| 876 |
+
load_tree(stream, tree->child1);
|
| 877 |
+
}
|
| 878 |
+
if (tree->child2!=NULL) {
|
| 879 |
+
load_tree(stream, tree->child2);
|
| 880 |
+
}
|
| 881 |
+
}
|
| 882 |
+
|
| 883 |
+
|
| 884 |
+
void computeBoundingBox(BoundingBox& bbox)
|
| 885 |
+
{
|
| 886 |
+
bbox.resize((DIM>0 ? DIM : dim));
|
| 887 |
+
if (dataset.kdtree_get_bbox(bbox))
|
| 888 |
+
{
|
| 889 |
+
// Done! It was implemented in derived class
|
| 890 |
+
}
|
| 891 |
+
else
|
| 892 |
+
{
|
| 893 |
+
for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
|
| 894 |
+
bbox[i].low =
|
| 895 |
+
bbox[i].high = dataset_get(0,i);
|
| 896 |
+
}
|
| 897 |
+
const size_t N = dataset.kdtree_get_point_count();
|
| 898 |
+
for (size_t k=1; k<N; ++k) {
|
| 899 |
+
for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
|
| 900 |
+
if (dataset_get(k,i)<bbox[i].low) bbox[i].low = dataset_get(k,i);
|
| 901 |
+
if (dataset_get(k,i)>bbox[i].high) bbox[i].high = dataset_get(k,i);
|
| 902 |
+
}
|
| 903 |
+
}
|
| 904 |
+
}
|
| 905 |
+
}
|
| 906 |
+
|
| 907 |
+
|
| 908 |
+
/**
|
| 909 |
+
* Create a tree node that subdivides the list of vecs from vind[first]
|
| 910 |
+
* to vind[last]. The routine is called recursively on each sublist.
|
| 911 |
+
* Place a pointer to this new tree node in the location pTree.
|
| 912 |
+
*
|
| 913 |
+
* Params: pTree = the new node to create
|
| 914 |
+
* first = index of the first vector
|
| 915 |
+
* last = index of the last vector
|
| 916 |
+
*/
|
| 917 |
+
NodePtr divideTree(const IndexType left, const IndexType right, BoundingBox& bbox)
|
| 918 |
+
{
|
| 919 |
+
NodePtr node = pool.allocate<Node>(); // allocate memory
|
| 920 |
+
|
| 921 |
+
/* If too few exemplars remain, then make this a leaf node. */
|
| 922 |
+
if ( (right-left) <= m_leaf_max_size) {
|
| 923 |
+
node->child1 = node->child2 = NULL; /* Mark as leaf node. */
|
| 924 |
+
node->lr.left = left;
|
| 925 |
+
node->lr.right = right;
|
| 926 |
+
|
| 927 |
+
// compute bounding-box of leaf points
|
| 928 |
+
for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
|
| 929 |
+
bbox[i].low = dataset_get(vind[left],i);
|
| 930 |
+
bbox[i].high = dataset_get(vind[left],i);
|
| 931 |
+
}
|
| 932 |
+
for (IndexType k=left+1; k<right; ++k) {
|
| 933 |
+
for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
|
| 934 |
+
if (bbox[i].low>dataset_get(vind[k],i)) bbox[i].low=dataset_get(vind[k],i);
|
| 935 |
+
if (bbox[i].high<dataset_get(vind[k],i)) bbox[i].high=dataset_get(vind[k],i);
|
| 936 |
+
}
|
| 937 |
+
}
|
| 938 |
+
}
|
| 939 |
+
else {
|
| 940 |
+
IndexType idx;
|
| 941 |
+
int cutfeat;
|
| 942 |
+
DistanceType cutval;
|
| 943 |
+
middleSplit_(&vind[0]+left, right-left, idx, cutfeat, cutval, bbox);
|
| 944 |
+
|
| 945 |
+
node->sub.divfeat = cutfeat;
|
| 946 |
+
|
| 947 |
+
BoundingBox left_bbox(bbox);
|
| 948 |
+
left_bbox[cutfeat].high = cutval;
|
| 949 |
+
node->child1 = divideTree(left, left+idx, left_bbox);
|
| 950 |
+
|
| 951 |
+
BoundingBox right_bbox(bbox);
|
| 952 |
+
right_bbox[cutfeat].low = cutval;
|
| 953 |
+
node->child2 = divideTree(left+idx, right, right_bbox);
|
| 954 |
+
|
| 955 |
+
node->sub.divlow = left_bbox[cutfeat].high;
|
| 956 |
+
node->sub.divhigh = right_bbox[cutfeat].low;
|
| 957 |
+
|
| 958 |
+
for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
|
| 959 |
+
bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low);
|
| 960 |
+
bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high);
|
| 961 |
+
}
|
| 962 |
+
}
|
| 963 |
+
|
| 964 |
+
return node;
|
| 965 |
+
}
|
| 966 |
+
|
| 967 |
+
void computeMinMax(IndexType* ind, IndexType count, int element, ElementType& min_elem, ElementType& max_elem)
|
| 968 |
+
{
|
| 969 |
+
min_elem = dataset_get(ind[0],element);
|
| 970 |
+
max_elem = dataset_get(ind[0],element);
|
| 971 |
+
for (IndexType i=1; i<count; ++i) {
|
| 972 |
+
ElementType val = dataset_get(ind[i],element);
|
| 973 |
+
if (val<min_elem) min_elem = val;
|
| 974 |
+
if (val>max_elem) max_elem = val;
|
| 975 |
+
}
|
| 976 |
+
}
|
| 977 |
+
|
| 978 |
+
void middleSplit(IndexType* ind, IndexType count, IndexType& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
|
| 979 |
+
{
|
| 980 |
+
// find the largest span from the approximate bounding box
|
| 981 |
+
ElementType max_span = bbox[0].high-bbox[0].low;
|
| 982 |
+
cutfeat = 0;
|
| 983 |
+
cutval = (bbox[0].high+bbox[0].low)/2;
|
| 984 |
+
for (int i=1; i<(DIM>0 ? DIM : dim); ++i) {
|
| 985 |
+
ElementType span = bbox[i].low-bbox[i].low;
|
| 986 |
+
if (span>max_span) {
|
| 987 |
+
max_span = span;
|
| 988 |
+
cutfeat = i;
|
| 989 |
+
cutval = (bbox[i].high+bbox[i].low)/2;
|
| 990 |
+
}
|
| 991 |
+
}
|
| 992 |
+
|
| 993 |
+
// compute exact span on the found dimension
|
| 994 |
+
ElementType min_elem, max_elem;
|
| 995 |
+
computeMinMax(ind, count, cutfeat, min_elem, max_elem);
|
| 996 |
+
cutval = (min_elem+max_elem)/2;
|
| 997 |
+
max_span = max_elem - min_elem;
|
| 998 |
+
|
| 999 |
+
// check if a dimension of a largest span exists
|
| 1000 |
+
size_t k = cutfeat;
|
| 1001 |
+
for (size_t i=0; i<(DIM>0 ? DIM : dim); ++i) {
|
| 1002 |
+
if (i==k) continue;
|
| 1003 |
+
ElementType span = bbox[i].high-bbox[i].low;
|
| 1004 |
+
if (span>max_span) {
|
| 1005 |
+
computeMinMax(ind, count, i, min_elem, max_elem);
|
| 1006 |
+
span = max_elem - min_elem;
|
| 1007 |
+
if (span>max_span) {
|
| 1008 |
+
max_span = span;
|
| 1009 |
+
cutfeat = i;
|
| 1010 |
+
cutval = (min_elem+max_elem)/2;
|
| 1011 |
+
}
|
| 1012 |
+
}
|
| 1013 |
+
}
|
| 1014 |
+
IndexType lim1, lim2;
|
| 1015 |
+
planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
|
| 1016 |
+
|
| 1017 |
+
if (lim1>count/2) index = lim1;
|
| 1018 |
+
else if (lim2<count/2) index = lim2;
|
| 1019 |
+
else index = count/2;
|
| 1020 |
+
}
|
| 1021 |
+
|
| 1022 |
+
|
| 1023 |
+
void middleSplit_(IndexType* ind, IndexType count, IndexType& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
|
| 1024 |
+
{
|
| 1025 |
+
const DistanceType EPS=static_cast<DistanceType>(0.00001);
|
| 1026 |
+
ElementType max_span = bbox[0].high-bbox[0].low;
|
| 1027 |
+
for (int i=1; i<(DIM>0 ? DIM : dim); ++i) {
|
| 1028 |
+
ElementType span = bbox[i].high-bbox[i].low;
|
| 1029 |
+
if (span>max_span) {
|
| 1030 |
+
max_span = span;
|
| 1031 |
+
}
|
| 1032 |
+
}
|
| 1033 |
+
ElementType max_spread = -1;
|
| 1034 |
+
cutfeat = 0;
|
| 1035 |
+
for (int i=0; i<(DIM>0 ? DIM : dim); ++i) {
|
| 1036 |
+
ElementType span = bbox[i].high-bbox[i].low;
|
| 1037 |
+
if (span>(1-EPS)*max_span) {
|
| 1038 |
+
ElementType min_elem, max_elem;
|
| 1039 |
+
computeMinMax(ind, count, cutfeat, min_elem, max_elem);
|
| 1040 |
+
ElementType spread = max_elem-min_elem;;
|
| 1041 |
+
if (spread>max_spread) {
|
| 1042 |
+
cutfeat = i;
|
| 1043 |
+
max_spread = spread;
|
| 1044 |
+
}
|
| 1045 |
+
}
|
| 1046 |
+
}
|
| 1047 |
+
// split in the middle
|
| 1048 |
+
DistanceType split_val = (bbox[cutfeat].low+bbox[cutfeat].high)/2;
|
| 1049 |
+
ElementType min_elem, max_elem;
|
| 1050 |
+
computeMinMax(ind, count, cutfeat, min_elem, max_elem);
|
| 1051 |
+
|
| 1052 |
+
if (split_val<min_elem) cutval = min_elem;
|
| 1053 |
+
else if (split_val>max_elem) cutval = max_elem;
|
| 1054 |
+
else cutval = split_val;
|
| 1055 |
+
|
| 1056 |
+
IndexType lim1, lim2;
|
| 1057 |
+
planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
|
| 1058 |
+
|
| 1059 |
+
if (lim1>count/2) index = lim1;
|
| 1060 |
+
else if (lim2<count/2) index = lim2;
|
| 1061 |
+
else index = count/2;
|
| 1062 |
+
}
|
| 1063 |
+
|
| 1064 |
+
|
| 1065 |
+
/**
|
| 1066 |
+
* Subdivide the list of points by a plane perpendicular on axe corresponding
|
| 1067 |
+
* to the 'cutfeat' dimension at 'cutval' position.
|
| 1068 |
+
*
|
| 1069 |
+
* On return:
|
| 1070 |
+
* dataset[ind[0..lim1-1]][cutfeat]<cutval
|
| 1071 |
+
* dataset[ind[lim1..lim2-1]][cutfeat]==cutval
|
| 1072 |
+
* dataset[ind[lim2..count]][cutfeat]>cutval
|
| 1073 |
+
*/
|
| 1074 |
+
void planeSplit(IndexType* ind, const IndexType count, int cutfeat, DistanceType cutval, IndexType& lim1, IndexType& lim2)
|
| 1075 |
+
{
|
| 1076 |
+
/* Move vector indices for left subtree to front of list. */
|
| 1077 |
+
IndexType left = 0;
|
| 1078 |
+
IndexType right = count-1;
|
| 1079 |
+
for (;; ) {
|
| 1080 |
+
while (left<=right && dataset_get(ind[left],cutfeat)<cutval) ++left;
|
| 1081 |
+
while (right && left<=right && dataset_get(ind[right],cutfeat)>=cutval) --right;
|
| 1082 |
+
if (left>right || !right) break; // "!right" was added to support unsigned Index types
|
| 1083 |
+
std::swap(ind[left], ind[right]);
|
| 1084 |
+
++left;
|
| 1085 |
+
--right;
|
| 1086 |
+
}
|
| 1087 |
+
/* If either list is empty, it means that all remaining features
|
| 1088 |
+
* are identical. Split in the middle to maintain a balanced tree.
|
| 1089 |
+
*/
|
| 1090 |
+
lim1 = left;
|
| 1091 |
+
right = count-1;
|
| 1092 |
+
for (;; ) {
|
| 1093 |
+
while (left<=right && dataset_get(ind[left],cutfeat)<=cutval) ++left;
|
| 1094 |
+
while (right && left<=right && dataset_get(ind[right],cutfeat)>cutval) --right;
|
| 1095 |
+
if (left>right || !right) break; // "!right" was added to support unsigned Index types
|
| 1096 |
+
std::swap(ind[left], ind[right]);
|
| 1097 |
+
++left;
|
| 1098 |
+
--right;
|
| 1099 |
+
}
|
| 1100 |
+
lim2 = left;
|
| 1101 |
+
}
|
| 1102 |
+
|
| 1103 |
+
DistanceType computeInitialDistances(const ElementType* vec, std::vector<DistanceType>& dists) const
|
| 1104 |
+
{
|
| 1105 |
+
assert(vec);
|
| 1106 |
+
DistanceType distsq = 0.0;
|
| 1107 |
+
|
| 1108 |
+
for (int i = 0; i < (DIM>0 ? DIM : dim); ++i) {
|
| 1109 |
+
if (vec[i] < root_bbox[i].low) {
|
| 1110 |
+
dists[i] = distance.accum_dist(vec[i], root_bbox[i].low, i);
|
| 1111 |
+
distsq += dists[i];
|
| 1112 |
+
}
|
| 1113 |
+
if (vec[i] > root_bbox[i].high) {
|
| 1114 |
+
dists[i] = distance.accum_dist(vec[i], root_bbox[i].high, i);
|
| 1115 |
+
distsq += dists[i];
|
| 1116 |
+
}
|
| 1117 |
+
}
|
| 1118 |
+
|
| 1119 |
+
return distsq;
|
| 1120 |
+
}
|
| 1121 |
+
|
| 1122 |
+
/**
|
| 1123 |
+
* Performs an exact search in the tree starting from a node.
|
| 1124 |
+
* \tparam RESULTSET Should be any ResultSet<DistanceType>
|
| 1125 |
+
*/
|
| 1126 |
+
template <class RESULTSET>
|
| 1127 |
+
void searchLevel(RESULTSET& result_set, const ElementType* vec, const NodePtr node, DistanceType mindistsq,
|
| 1128 |
+
std::vector<DistanceType>& dists, const float epsError) const
|
| 1129 |
+
{
|
| 1130 |
+
/* If this is a leaf node, then do check and return. */
|
| 1131 |
+
if ((node->child1 == NULL)&&(node->child2 == NULL)) {
|
| 1132 |
+
//count_leaf += (node->lr.right-node->lr.left); // Removed since was neither used nor returned to the user.
|
| 1133 |
+
DistanceType worst_dist = result_set.worstDist();
|
| 1134 |
+
for (IndexType i=node->lr.left; i<node->lr.right; ++i) {
|
| 1135 |
+
const IndexType index = vind[i];// reorder... : i;
|
| 1136 |
+
DistanceType dist = distance(vec, index, (DIM>0 ? DIM : dim));
|
| 1137 |
+
if (dist<worst_dist) {
|
| 1138 |
+
result_set.addPoint(dist,vind[i]);
|
| 1139 |
+
}
|
| 1140 |
+
}
|
| 1141 |
+
return;
|
| 1142 |
+
}
|
| 1143 |
+
|
| 1144 |
+
/* Which child branch should be taken first? */
|
| 1145 |
+
int idx = node->sub.divfeat;
|
| 1146 |
+
ElementType val = vec[idx];
|
| 1147 |
+
DistanceType diff1 = val - node->sub.divlow;
|
| 1148 |
+
DistanceType diff2 = val - node->sub.divhigh;
|
| 1149 |
+
|
| 1150 |
+
NodePtr bestChild;
|
| 1151 |
+
NodePtr otherChild;
|
| 1152 |
+
DistanceType cut_dist;
|
| 1153 |
+
if ((diff1+diff2)<0) {
|
| 1154 |
+
bestChild = node->child1;
|
| 1155 |
+
otherChild = node->child2;
|
| 1156 |
+
cut_dist = distance.accum_dist(val, node->sub.divhigh, idx);
|
| 1157 |
+
}
|
| 1158 |
+
else {
|
| 1159 |
+
bestChild = node->child2;
|
| 1160 |
+
otherChild = node->child1;
|
| 1161 |
+
cut_dist = distance.accum_dist( val, node->sub.divlow, idx);
|
| 1162 |
+
}
|
| 1163 |
+
|
| 1164 |
+
/* Call recursively to search next level down. */
|
| 1165 |
+
searchLevel(result_set, vec, bestChild, mindistsq, dists, epsError);
|
| 1166 |
+
|
| 1167 |
+
DistanceType dst = dists[idx];
|
| 1168 |
+
mindistsq = mindistsq + cut_dist - dst;
|
| 1169 |
+
dists[idx] = cut_dist;
|
| 1170 |
+
if (mindistsq*epsError<=result_set.worstDist()) {
|
| 1171 |
+
searchLevel(result_set, vec, otherChild, mindistsq, dists, epsError);
|
| 1172 |
+
}
|
| 1173 |
+
dists[idx] = dst;
|
| 1174 |
+
}
|
| 1175 |
+
|
| 1176 |
+
|
| 1177 |
+
void saveIndex(FILE* stream)
|
| 1178 |
+
{
|
| 1179 |
+
save_value(stream, m_size);
|
| 1180 |
+
save_value(stream, dim);
|
| 1181 |
+
save_value(stream, root_bbox);
|
| 1182 |
+
save_value(stream, m_leaf_max_size);
|
| 1183 |
+
save_value(stream, vind);
|
| 1184 |
+
save_tree(stream, root_node);
|
| 1185 |
+
}
|
| 1186 |
+
|
| 1187 |
+
void loadIndex(FILE* stream)
|
| 1188 |
+
{
|
| 1189 |
+
load_value(stream, m_size);
|
| 1190 |
+
load_value(stream, dim);
|
| 1191 |
+
load_value(stream, root_bbox);
|
| 1192 |
+
load_value(stream, m_leaf_max_size);
|
| 1193 |
+
load_value(stream, vind);
|
| 1194 |
+
load_tree(stream, root_node);
|
| 1195 |
+
}
|
| 1196 |
+
|
| 1197 |
+
}; // class KDTree
|
| 1198 |
+
|
| 1199 |
+
|
| 1200 |
+
/** A simple KD-tree adaptor for working with data directly stored in an Eigen Matrix, without duplicating the data storage.
|
| 1201 |
+
* Each row in the matrix represents a point in the state space.
|
| 1202 |
+
*
|
| 1203 |
+
* Example of usage:
|
| 1204 |
+
* \code
|
| 1205 |
+
* Eigen::Matrix<num_t,Dynamic,Dynamic> mat;
|
| 1206 |
+
* // Fill out "mat"...
|
| 1207 |
+
*
|
| 1208 |
+
* typedef KDTreeEigenMatrixAdaptor< Eigen::Matrix<num_t,Dynamic,Dynamic> > my_kd_tree_t;
|
| 1209 |
+
* const int max_leaf = 10;
|
| 1210 |
+
* my_kd_tree_t mat_index(dimdim, mat, max_leaf );
|
| 1211 |
+
* mat_index.index->buildIndex();
|
| 1212 |
+
* mat_index.index->...
|
| 1213 |
+
* \endcode
|
| 1214 |
+
*
|
| 1215 |
+
* \tparam DIM If set to >0, it specifies a compile-time fixed dimensionality for the points in the data set, allowing more compiler optimizations.
|
| 1216 |
+
* \tparam Distance The distance metric to use: nanoflann::metric_L1, nanoflann::metric_L2, nanoflann::metric_L2_Simple, etc.
|
| 1217 |
+
* \tparam IndexType The type for indices in the KD-tree index (typically, size_t of int)
|
| 1218 |
+
*/
|
| 1219 |
+
template <class MatrixType, int DIM = -1, class Distance = nanoflann::metric_L2, typename IndexType = size_t>
|
| 1220 |
+
struct KDTreeEigenMatrixAdaptor
|
| 1221 |
+
{
|
| 1222 |
+
typedef KDTreeEigenMatrixAdaptor<MatrixType,DIM,Distance> self_t;
|
| 1223 |
+
typedef typename MatrixType::Scalar num_t;
|
| 1224 |
+
typedef typename Distance::template traits<num_t,self_t>::distance_t metric_t;
|
| 1225 |
+
typedef KDTreeSingleIndexAdaptor< metric_t,self_t,DIM,IndexType> index_t;
|
| 1226 |
+
|
| 1227 |
+
index_t* index; //! The kd-tree index for the user to call its methods as usual with any other FLANN index.
|
| 1228 |
+
|
| 1229 |
+
/// Constructor: takes a const ref to the matrix object with the data points
|
| 1230 |
+
KDTreeEigenMatrixAdaptor(const int dimensionality, const MatrixType &mat, const int leaf_max_size = 10) : m_data_matrix(mat)
|
| 1231 |
+
{
|
| 1232 |
+
const size_t dims = mat.cols();
|
| 1233 |
+
if (DIM>0 && static_cast<int>(dims)!=DIM)
|
| 1234 |
+
throw std::runtime_error("Data set dimensionality does not match the 'DIM' template argument");
|
| 1235 |
+
index = new index_t( dims, *this /* adaptor */, nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size, dims ) );
|
| 1236 |
+
index->buildIndex();
|
| 1237 |
+
}
|
| 1238 |
+
|
| 1239 |
+
~KDTreeEigenMatrixAdaptor() {
|
| 1240 |
+
delete index;
|
| 1241 |
+
}
|
| 1242 |
+
|
| 1243 |
+
const MatrixType &m_data_matrix;
|
| 1244 |
+
|
| 1245 |
+
/** Query for the \a num_closest closest points to a given point (entered as query_point[0:dim-1]).
|
| 1246 |
+
* Note that this is a short-cut method for index->findNeighbors().
|
| 1247 |
+
* The user can also call index->... methods as desired.
|
| 1248 |
+
* \note nChecks_IGNORED is ignored but kept for compatibility with the original FLANN interface.
|
| 1249 |
+
*/
|
| 1250 |
+
inline void query(const num_t *query_point, const size_t num_closest, IndexType *out_indices, num_t *out_distances_sq, const int nChecks_IGNORED = 10) const
|
| 1251 |
+
{
|
| 1252 |
+
nanoflann::KNNResultSet<typename MatrixType::Scalar,IndexType> resultSet(num_closest);
|
| 1253 |
+
resultSet.init(out_indices, out_distances_sq);
|
| 1254 |
+
index->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
|
| 1255 |
+
}
|
| 1256 |
+
|
| 1257 |
+
/** @name Interface expected by KDTreeSingleIndexAdaptor
|
| 1258 |
+
* @{ */
|
| 1259 |
+
|
| 1260 |
+
const self_t & derived() const {
|
| 1261 |
+
return *this;
|
| 1262 |
+
}
|
| 1263 |
+
self_t & derived() {
|
| 1264 |
+
return *this;
|
| 1265 |
+
}
|
| 1266 |
+
|
| 1267 |
+
// Must return the number of data points
|
| 1268 |
+
inline size_t kdtree_get_point_count() const {
|
| 1269 |
+
return m_data_matrix.rows();
|
| 1270 |
+
}
|
| 1271 |
+
|
| 1272 |
+
// Returns the distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class:
|
| 1273 |
+
inline num_t kdtree_distance(const num_t *p1, const size_t idx_p2,size_t size) const
|
| 1274 |
+
{
|
| 1275 |
+
num_t s=0;
|
| 1276 |
+
for (size_t i=0; i<size; i++) {
|
| 1277 |
+
const num_t d= p1[i]-m_data_matrix.coeff(idx_p2,i);
|
| 1278 |
+
s+=d*d;
|
| 1279 |
+
}
|
| 1280 |
+
return s;
|
| 1281 |
+
}
|
| 1282 |
+
|
| 1283 |
+
// Returns the dim'th component of the idx'th point in the class:
|
| 1284 |
+
inline num_t kdtree_get_pt(const size_t idx, int dim) const {
|
| 1285 |
+
return m_data_matrix.coeff(idx,dim);
|
| 1286 |
+
}
|
| 1287 |
+
|
| 1288 |
+
// Optional bounding-box computation: return false to default to a standard bbox computation loop.
|
| 1289 |
+
// Return true if the BBOX was already computed by the class and returned in "bb" so it can be avoided to redo it again.
|
| 1290 |
+
// Look at bb.size() to find out the expected dimensionality (e.g. 2 or 3 for point clouds)
|
| 1291 |
+
template <class BBOX>
|
| 1292 |
+
bool kdtree_get_bbox(BBOX &bb) const {
|
| 1293 |
+
return false;
|
| 1294 |
+
}
|
| 1295 |
+
|
| 1296 |
+
/** @} */
|
| 1297 |
+
|
| 1298 |
+
}; // end of KDTreeEigenMatrixAdaptor
|
| 1299 |
+
/** @} */
|
| 1300 |
+
|
| 1301 |
+
/** @} */ // end of grouping
|
| 1302 |
+
} // end of NS
|
| 1303 |
+
|
| 1304 |
+
|
| 1305 |
+
#endif /* NANOFLANN_HPP_ */
|
CMakeLists.txt
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmake_minimum_required(VERSION 3.16)
|
| 2 |
+
project(Lightweight3DRegLib)
|
| 3 |
+
|
| 4 |
+
set(CMAKE_CXX_STANDARD 17)
|
| 5 |
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
| 6 |
+
|
| 7 |
+
if(NOT CMAKE_BUILD_TYPE)
|
| 8 |
+
set(CMAKE_BUILD_TYPE Release)
|
| 9 |
+
endif()
|
| 10 |
+
|
| 11 |
+
# 第三方库路径
|
| 12 |
+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
| 13 |
+
|
| 14 |
+
# --- 第三方库
|
| 15 |
+
find_package(Eigen3 3.4.0 REQUIRED)
|
| 16 |
+
message(STATUS "Found Eigen3 version: ${EIGEN3_VERSION_STRING}")
|
| 17 |
+
find_package(OpenMesh REQUIRED)
|
| 18 |
+
find_package(OpenMP REQUIRED)
|
| 19 |
+
|
| 20 |
+
# --- 添加 pybind11 支持 ---
|
| 21 |
+
find_package(pybind11 CONFIG REQUIRED)
|
| 22 |
+
|
| 23 |
+
option(USEPARDISO "Use Pardiso solver" ON)
|
| 24 |
+
|
| 25 |
+
# --- 源文件
|
| 26 |
+
file(GLOB_RECURSE SOURCES
|
| 27 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/core/*.cpp"
|
| 28 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/io/*.cpp"
|
| 29 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/*.cpp"
|
| 30 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/geodesic/*.cpp"
|
| 31 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/preprocess/*.cpp"
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
file(GLOB_RECURSE HEADERS
|
| 35 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/*.h"
|
| 36 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/core/*.h"
|
| 37 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/io/*.h"
|
| 38 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/*.h"
|
| 39 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/geodesic/*.h"
|
| 40 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/preprocess/*.h"
|
| 41 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/*.h"
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
# --- 创建静态库
|
| 45 |
+
add_library(Lightweight3DRegLib STATIC ${SOURCES} ${HEADERS})
|
| 46 |
+
|
| 47 |
+
target_precompile_headers(Lightweight3DRegLib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp/pch.h")
|
| 48 |
+
|
| 49 |
+
set_target_properties(Lightweight3DRegLib PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
| 50 |
+
|
| 51 |
+
# --- include 路径
|
| 52 |
+
target_include_directories(Lightweight3DRegLib
|
| 53 |
+
PUBLIC
|
| 54 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp"
|
| 55 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/core"
|
| 56 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/io"
|
| 57 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools"
|
| 58 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/geodesic"
|
| 59 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/preprocess"
|
| 60 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/3rdparty"
|
| 61 |
+
${EIGEN3_INCLUDE_DIRS}
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
# --- 链接库
|
| 65 |
+
target_link_libraries(Lightweight3DRegLib
|
| 66 |
+
PUBLIC OpenMeshCore OpenMeshTools
|
| 67 |
+
PUBLIC OpenMP::OpenMP_CXX
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
# --- Pardiso / MKL
|
| 71 |
+
if(USEPARDISO AND ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")
|
| 72 |
+
if(EXISTS /opt/intel/oneapi/mkl/latest/include/mkl_pardiso.h)
|
| 73 |
+
target_compile_definitions(Lightweight3DRegLib PUBLIC USE_PARDISO)
|
| 74 |
+
target_include_directories(Lightweight3DRegLib SYSTEM PUBLIC "/opt/intel/oneapi/mkl/latest/include")
|
| 75 |
+
target_compile_options(Lightweight3DRegLib PUBLIC "-m64")
|
| 76 |
+
target_link_libraries(Lightweight3DRegLib PRIVATE
|
| 77 |
+
"-Wl,--start-group /opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_intel_lp64.a"
|
| 78 |
+
"/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_gnu_thread.a"
|
| 79 |
+
"/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_core.a"
|
| 80 |
+
"-Wl,--end-group -lgomp -lpthread -lm -ldl"
|
| 81 |
+
)
|
| 82 |
+
else()
|
| 83 |
+
message("Cannot find Intel MKL at /opt/intel/oneapi/mkl. Using Eigen LDLT.")
|
| 84 |
+
endif()
|
| 85 |
+
endif()
|
| 86 |
+
|
| 87 |
+
# --- 添加 Python 模块 ---
|
| 88 |
+
pybind11_add_module(pyregister ${CMAKE_CURRENT_SOURCE_DIR}/python/pyregister.cpp)
|
| 89 |
+
|
| 90 |
+
target_link_libraries(pyregister
|
| 91 |
+
PRIVATE
|
| 92 |
+
Lightweight3DRegLib
|
| 93 |
+
OpenMeshCore
|
| 94 |
+
OpenMeshTools
|
| 95 |
+
OpenMP::OpenMP_CXX
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
target_include_directories(pyregister
|
| 99 |
+
PRIVATE
|
| 100 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp"
|
| 101 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/core"
|
| 102 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/io"
|
| 103 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools"
|
| 104 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/geodesic"
|
| 105 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/cpp/tools/preprocess"
|
| 106 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/3rdparty"
|
| 107 |
+
${EIGEN3_INCLUDE_DIRS}
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
target_precompile_headers(pyregister REUSE_FROM Lightweight3DRegLib)
|
| 111 |
+
|
| 112 |
+
if(USEPARDISO AND ${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")
|
| 113 |
+
if(EXISTS /opt/intel/oneapi/mkl/latest/include/mkl_pardiso.h)
|
| 114 |
+
target_compile_definitions(pyregister PRIVATE USE_PARDISO)
|
| 115 |
+
target_include_directories(pyregister SYSTEM PRIVATE "/opt/intel/oneapi/mkl/latest/include")
|
| 116 |
+
target_compile_options(pyregister PRIVATE "-m64")
|
| 117 |
+
target_link_libraries(pyregister PRIVATE
|
| 118 |
+
"-Wl,--start-group /opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_intel_lp64.a"
|
| 119 |
+
"/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_gnu_thread.a"
|
| 120 |
+
"/opt/intel/oneapi/mkl/latest/lib/intel64/libmkl_core.a"
|
| 121 |
+
"-Wl,--end-group -lgomp -lpthread -lm -ldl"
|
| 122 |
+
)
|
| 123 |
+
endif()
|
| 124 |
+
endif()
|
| 125 |
+
|
| 126 |
+
set_target_properties(pyregister PROPERTIES
|
| 127 |
+
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/python
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
# =====================================================================
|
| 132 |
+
# --- 添加示例可执行程序 (examples/c++/main.cpp) ---
|
| 133 |
+
# =====================================================================
|
| 134 |
+
file(GLOB EXAMPLE_SOURCES
|
| 135 |
+
"${CMAKE_CURRENT_SOURCE_DIR}/examples/c++/*.cpp"
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
add_executable(Lightweight3DRegDemo ${EXAMPLE_SOURCES})
|
| 140 |
+
|
| 141 |
+
target_precompile_headers(Lightweight3DRegDemo PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp/pch.h")
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
target_link_libraries(Lightweight3DRegDemo
|
| 145 |
+
PRIVATE
|
| 146 |
+
Lightweight3DRegLib
|
| 147 |
+
OpenMeshCore
|
| 148 |
+
OpenMeshTools
|
| 149 |
+
OpenMP::OpenMP_CXX
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
set_target_properties(Lightweight3DRegDemo PROPERTIES
|
| 155 |
+
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples
|
| 156 |
+
)
|
README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Lowweight Register
|
| 3 |
+
emoji: 🚀
|
| 4 |
+
colorFrom: purple
|
| 5 |
+
colorTo: red
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 5.47.2
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
license: apache-2.0
|
| 11 |
+
short_description: '00000'
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
|
@@ -0,0 +1,638 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
import glob
|
| 5 |
+
import numpy as np
|
| 6 |
+
import trimesh
|
| 7 |
+
import plotly.graph_objs as go
|
| 8 |
+
import gradio as gr
|
| 9 |
+
from plyfile import PlyData
|
| 10 |
+
|
| 11 |
+
# ------------------ Load pyregister module ------------------
|
| 12 |
+
so_candidates = glob.glob("build/python/pyregister*.so")
|
| 13 |
+
if not so_candidates:
|
| 14 |
+
os.system("bash build.sh")
|
| 15 |
+
so_candidates = glob.glob("build/python/pyregister*.so")
|
| 16 |
+
if not so_candidates:
|
| 17 |
+
raise FileNotFoundError("pyregister build failed, no .so found in build/python")
|
| 18 |
+
sys.path.append("build/python")
|
| 19 |
+
|
| 20 |
+
import pyregister
|
| 21 |
+
|
| 22 |
+
# ------------------ Example data ------------------
|
| 23 |
+
EXAMPLES = {
|
| 24 |
+
"Rigid": {"target": "./examples/data/fricp/target.ply", "source": "./examples/data/fricp/source.ply"},
|
| 25 |
+
"NonRigid": {"target": "./examples/data/spare/target.obj", "source": "./examples/data/spare/source.obj"},
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
FRICP_PARAS = {
|
| 29 |
+
"useinit": False,
|
| 30 |
+
"fileinit": "",
|
| 31 |
+
"maxiter": 100,
|
| 32 |
+
"stop": 1e-5
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
SPARE_PARAS = {
|
| 36 |
+
"iters": 30,
|
| 37 |
+
"stopcoarse": 1e-3,
|
| 38 |
+
"stopfine": 1e-4,
|
| 39 |
+
"use_landmark": False,
|
| 40 |
+
"src": [],
|
| 41 |
+
"tar": []
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
def load_example(example_type):
|
| 45 |
+
target_path = EXAMPLES[example_type]["target"]
|
| 46 |
+
source_path = EXAMPLES[example_type]["source"]
|
| 47 |
+
method = example_type
|
| 48 |
+
return source_path, target_path, method
|
| 49 |
+
|
| 50 |
+
def read_mesh(file_path):
|
| 51 |
+
ext = os.path.splitext(file_path)[1].lower()
|
| 52 |
+
if ext == ".ply":
|
| 53 |
+
plydata = PlyData.read(file_path)
|
| 54 |
+
vertex = plydata["vertex"]
|
| 55 |
+
vertices = np.stack([vertex["x"], vertex["y"], vertex["z"]], axis=-1)
|
| 56 |
+
faces = None
|
| 57 |
+
if "face" in plydata and len(plydata["face"].data) > 0:
|
| 58 |
+
face_list = [f[0] for f in plydata["face"].data]
|
| 59 |
+
if len(face_list) > 0:
|
| 60 |
+
faces = np.array(face_list)
|
| 61 |
+
if faces.ndim == 1:
|
| 62 |
+
faces = faces.reshape(-1, 3)
|
| 63 |
+
return vertices, faces
|
| 64 |
+
elif ext == ".obj":
|
| 65 |
+
mesh = trimesh.load(file_path, process=False)
|
| 66 |
+
return np.asarray(mesh.vertices), (np.asarray(mesh.faces) if mesh.faces.size > 0 else None)
|
| 67 |
+
else:
|
| 68 |
+
raise ValueError(f"Unsupported file type: {ext}")
|
| 69 |
+
|
| 70 |
+
def plot_source_target_mesh(target_file, source_file, alpha_target=1.0, alpha_source=1.0, scatter_mode=False):
|
| 71 |
+
target_v, target_f = read_mesh(target_file)
|
| 72 |
+
source_v, source_f = read_mesh(source_file)
|
| 73 |
+
|
| 74 |
+
fig = go.Figure()
|
| 75 |
+
lighting_opts = dict(ambient=0.5, diffuse=0.8, specular=0.6, roughness=0.25)
|
| 76 |
+
light_pos = dict(x=100, y=200, z=50)
|
| 77 |
+
|
| 78 |
+
target_color = "crimson"
|
| 79 |
+
|
| 80 |
+
if scatter_mode:
|
| 81 |
+
|
| 82 |
+
fig.add_trace(go.Scatter3d(
|
| 83 |
+
x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
|
| 84 |
+
mode="markers",
|
| 85 |
+
|
| 86 |
+
marker=dict(size=0.6, color=target_color, opacity=1.0),
|
| 87 |
+
name="Target Points",
|
| 88 |
+
hoverinfo="text",
|
| 89 |
+
text=[
|
| 90 |
+
f"<b><span style='color:black;'>Target ID:</span></b> {i}"
|
| 91 |
+
f"<br><span style='font-size:12px;'>x={x:.3f}, y={y:.3f}, z={z:.3f}</span>"
|
| 92 |
+
for i, (x, y, z) in enumerate(target_v)
|
| 93 |
+
],
|
| 94 |
+
hoverlabel=dict(
|
| 95 |
+
bgcolor="white",
|
| 96 |
+
bordercolor="black",
|
| 97 |
+
font=dict(color="black", size=13, family="Arial", weight="bold")
|
| 98 |
+
)
|
| 99 |
+
))
|
| 100 |
+
|
| 101 |
+
fig.add_trace(go.Scatter3d(
|
| 102 |
+
x=source_v[:, 0], y=source_v[:, 1], z=source_v[:, 2],
|
| 103 |
+
mode="markers",
|
| 104 |
+
|
| 105 |
+
marker=dict(size=0.6, color="limegreen", opacity=1.0),
|
| 106 |
+
name="Source Points",
|
| 107 |
+
hoverinfo="text",
|
| 108 |
+
text=[
|
| 109 |
+
f"<b><span style='color:red;'>Source ID:</span></b> {i}"
|
| 110 |
+
f"<br><span style='font-size:12px;'>x={x:.3f}, y={y:.3f}, z={z:.3f}</span>"
|
| 111 |
+
for i, (x, y, z) in enumerate(source_v)
|
| 112 |
+
],
|
| 113 |
+
hoverlabel=dict(
|
| 114 |
+
bgcolor="white",
|
| 115 |
+
bordercolor="limegreen",
|
| 116 |
+
font=dict(color="red", size=13, family="Arial", weight="bold")
|
| 117 |
+
)
|
| 118 |
+
))
|
| 119 |
+
|
| 120 |
+
else:
|
| 121 |
+
|
| 122 |
+
if target_f is not None:
|
| 123 |
+
fig.add_trace(go.Mesh3d(
|
| 124 |
+
x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
|
| 125 |
+
i=target_f[:, 0], j=target_f[:, 1], k=target_f[:, 2],
|
| 126 |
+
color="khaki", opacity=alpha_target, name="Target Mesh",
|
| 127 |
+
lighting=lighting_opts, lightposition=light_pos,
|
| 128 |
+
hovertemplate="<b>Target Surface</b><br>x=%{x:.3f}<br>y=%{y:.3f}<br>z=%{z:.3f}<extra></extra>",
|
| 129 |
+
))
|
| 130 |
+
else:
|
| 131 |
+
fig.add_trace(go.Scatter3d(
|
| 132 |
+
x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
|
| 133 |
+
mode="markers",
|
| 134 |
+
marker=dict(size=0.6, color=target_color, opacity=alpha_target),
|
| 135 |
+
name="Target Points"
|
| 136 |
+
))
|
| 137 |
+
|
| 138 |
+
if source_f is not None:
|
| 139 |
+
fig.add_trace(go.Mesh3d(
|
| 140 |
+
x=source_v[:, 0], y=source_v[:, 1], z=source_v[:, 2],
|
| 141 |
+
i=source_f[:, 0], j=source_f[:, 1], k=source_f[:, 2],
|
| 142 |
+
color="darkseagreen", opacity=alpha_source, name="Source Mesh",
|
| 143 |
+
lighting=lighting_opts, lightposition=light_pos,
|
| 144 |
+
hovertemplate="<b>Source Surface</b><br>x=%{x:.3f}<br>y=%{y:.3f}<br>z=%{z:.3f}<extra></extra>",
|
| 145 |
+
))
|
| 146 |
+
else:
|
| 147 |
+
fig.add_trace(go.Scatter3d(
|
| 148 |
+
x=source_v[:, 0], y=source_v[:, 1], z=source_v[:, 2],
|
| 149 |
+
mode="markers",
|
| 150 |
+
marker=dict(size=0.6, color="limegreen", opacity=alpha_source),
|
| 151 |
+
name="Source Points"
|
| 152 |
+
))
|
| 153 |
+
|
| 154 |
+
fig.update_layout(
|
| 155 |
+
height=600, width=600, margin=dict(l=0, r=0, t=40, b=0),
|
| 156 |
+
scene=dict(aspectmode="data"), showlegend=True
|
| 157 |
+
)
|
| 158 |
+
return fig
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def plot_result_target_mesh(target_file, result_file):
|
| 163 |
+
target_v, target_f = read_mesh(target_file)
|
| 164 |
+
result_v, result_f = read_mesh(result_file)
|
| 165 |
+
|
| 166 |
+
fig = go.Figure()
|
| 167 |
+
lighting_opts = dict(ambient=0.5, diffuse=0.8, specular=0.6, roughness=0.25)
|
| 168 |
+
light_pos = dict(x=100, y=200, z=50)
|
| 169 |
+
|
| 170 |
+
target_mesh_color = "khaki"
|
| 171 |
+
result_mesh_color = "lightblue"
|
| 172 |
+
|
| 173 |
+
if target_f is not None:
|
| 174 |
+
fig.add_trace(go.Mesh3d(
|
| 175 |
+
x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
|
| 176 |
+
i=target_f[:, 0], j=target_f[:, 1], k=target_f[:, 2],
|
| 177 |
+
color=target_mesh_color, opacity=1.0, name="Target Mesh",
|
| 178 |
+
lighting=lighting_opts, lightposition=light_pos
|
| 179 |
+
))
|
| 180 |
+
else:
|
| 181 |
+
fig.add_trace(go.Scatter3d(
|
| 182 |
+
x=target_v[:, 0], y=target_v[:, 1], z=target_v[:, 2],
|
| 183 |
+
mode="markers",
|
| 184 |
+
marker=dict(size=0.6, color="crimson", opacity=1.0),
|
| 185 |
+
name="Target Points (Red)"
|
| 186 |
+
))
|
| 187 |
+
|
| 188 |
+
if result_f is not None:
|
| 189 |
+
fig.add_trace(go.Mesh3d(
|
| 190 |
+
x=result_v[:, 0], y=result_v[:, 1], z=result_v[:, 2],
|
| 191 |
+
i=result_f[:, 0], j=result_f[:, 1], k=result_f[:, 2],
|
| 192 |
+
color=result_mesh_color, opacity=1.0, name="Result Mesh",
|
| 193 |
+
lighting=lighting_opts, lightposition=light_pos
|
| 194 |
+
))
|
| 195 |
+
else:
|
| 196 |
+
fig.add_trace(go.Scatter3d(
|
| 197 |
+
x=result_v[:, 0], y=result_v[:, 1], z=result_v[:, 2],
|
| 198 |
+
mode="markers",
|
| 199 |
+
marker=dict(size=0.6, color="royalblue", opacity=1.0),
|
| 200 |
+
name="Result Points (Blue)"
|
| 201 |
+
))
|
| 202 |
+
|
| 203 |
+
fig.update_layout(height=600, width=600, margin=dict(l=0, r=0, t=40, b=0),
|
| 204 |
+
scene=dict(aspectmode="data"), showlegend=True)
|
| 205 |
+
return fig
|
| 206 |
+
|
| 207 |
+
# ------------------ Registration ------------------
|
| 208 |
+
def register_and_visualize_with_zip(target_file, source_file, output_dir, method,
|
| 209 |
+
useinit=False, fileinit=None, maxiter=100, stop=1e-5,
|
| 210 |
+
iters=30, stopcoarse=1e-3, stopfine=1e-4):
|
| 211 |
+
if target_file is None or source_file is None:
|
| 212 |
+
raise gr.Error("Please upload both target and source point cloud files first!")
|
| 213 |
+
|
| 214 |
+
target_path = target_file.name
|
| 215 |
+
source_path = source_file.name
|
| 216 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 217 |
+
|
| 218 |
+
# === Rigid ===
|
| 219 |
+
if method == "Rigid":
|
| 220 |
+
reg = pyregister.RigidFricpRegistration()
|
| 221 |
+
FRICP_PARAS["useinit"] = bool(useinit)
|
| 222 |
+
FRICP_PARAS["fileinit"] = fileinit.name if useinit and fileinit else ""
|
| 223 |
+
FRICP_PARAS["maxiter"] = int(maxiter)
|
| 224 |
+
FRICP_PARAS["stop"] = float(stop)
|
| 225 |
+
|
| 226 |
+
reg.Paras_init(FRICP_PARAS["useinit"],
|
| 227 |
+
FRICP_PARAS["fileinit"],
|
| 228 |
+
FRICP_PARAS["maxiter"],
|
| 229 |
+
FRICP_PARAS["stop"])
|
| 230 |
+
output_file = "FRICP_res.ply"
|
| 231 |
+
|
| 232 |
+
# === NonRigid ===
|
| 233 |
+
else:
|
| 234 |
+
reg = pyregister.NonrigidSpareRegistration()
|
| 235 |
+
SPARE_PARAS["iters"] = int(iters)
|
| 236 |
+
SPARE_PARAS["stopcoarse"] = float(stopcoarse)
|
| 237 |
+
SPARE_PARAS["stopfine"] = float(stopfine)
|
| 238 |
+
|
| 239 |
+
reg.Paras_init(
|
| 240 |
+
SPARE_PARAS["iters"],
|
| 241 |
+
SPARE_PARAS["stopcoarse"],
|
| 242 |
+
SPARE_PARAS["stopfine"],
|
| 243 |
+
SPARE_PARAS["use_landmark"],
|
| 244 |
+
SPARE_PARAS["src"],
|
| 245 |
+
SPARE_PARAS["tar"]
|
| 246 |
+
)
|
| 247 |
+
output_file = "spare_res.ply"
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
reg.Reg(target_path, source_path, output_dir)
|
| 251 |
+
|
| 252 |
+
result_path = os.path.join(output_dir, output_file)
|
| 253 |
+
fig_result = plot_result_target_mesh(target_path, result_path)
|
| 254 |
+
|
| 255 |
+
import zipfile
|
| 256 |
+
zip_path = os.path.join(output_dir, "results.zip")
|
| 257 |
+
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf:
|
| 258 |
+
for f in os.listdir(output_dir):
|
| 259 |
+
if f.endswith(".ply"):
|
| 260 |
+
zipf.write(os.path.join(output_dir, f), arcname=f)
|
| 261 |
+
|
| 262 |
+
return fig_result, zip_path
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
# ------------------ Reregister ------------------
|
| 266 |
+
def reuse_last_result_as_source(target_file, output_dir):
|
| 267 |
+
if target_file is None:
|
| 268 |
+
raise gr.Error("Please upload the target point cloud file first!")
|
| 269 |
+
if not os.path.exists(output_dir):
|
| 270 |
+
raise gr.Error("Output directory does not exist!")
|
| 271 |
+
|
| 272 |
+
result_candidates = []
|
| 273 |
+
for fname in ["FRICP_res.ply", "spare_res.ply"]:
|
| 274 |
+
fpath = os.path.join(output_dir, fname)
|
| 275 |
+
if os.path.exists(fpath):
|
| 276 |
+
result_candidates.append(fpath)
|
| 277 |
+
if not result_candidates:
|
| 278 |
+
raise gr.Error("No previous registration result found. Please run registration first!")
|
| 279 |
+
|
| 280 |
+
result_candidates.sort(key=os.path.getmtime, reverse=True)
|
| 281 |
+
latest_result = result_candidates[0]
|
| 282 |
+
|
| 283 |
+
fig = plot_source_target_mesh(target_file.name, latest_result)
|
| 284 |
+
return latest_result, fig
|
| 285 |
+
|
| 286 |
+
# ------------------ Utility ------------------
|
| 287 |
+
def reset_parameters():
|
| 288 |
+
"""
|
| 289 |
+
Reset all registration parameters (Rigid + NonRigid + Landmarks)
|
| 290 |
+
and clear any manual inputs in the UI.
|
| 291 |
+
This also resets the 'Use Landmarks' checkbox and hides the landmark UI group.
|
| 292 |
+
"""
|
| 293 |
+
|
| 294 |
+
# --- Reset Rigid (FRICP) parameters ---
|
| 295 |
+
FRICP_PARAS.update({
|
| 296 |
+
"useinit": False,
|
| 297 |
+
"fileinit": "",
|
| 298 |
+
"maxiter": 100,
|
| 299 |
+
"stop": 1e-5
|
| 300 |
+
})
|
| 301 |
+
|
| 302 |
+
# --- Reset NonRigid (Spare) parameters ---
|
| 303 |
+
SPARE_PARAS.update({
|
| 304 |
+
"iters": 30,
|
| 305 |
+
"stopcoarse": 1e-3,
|
| 306 |
+
"stopfine": 1e-4,
|
| 307 |
+
"use_landmark": False,
|
| 308 |
+
"src": [],
|
| 309 |
+
"tar": []
|
| 310 |
+
})
|
| 311 |
+
|
| 312 |
+
print("[reset] All parameters reset.")
|
| 313 |
+
print("[reset] FRICP_PARAS:", FRICP_PARAS)
|
| 314 |
+
print("[reset] SPARE_PARAS:", SPARE_PARAS)
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
return (
|
| 318 |
+
FRICP_PARAS["useinit"], # Checkbox
|
| 319 |
+
None, # File input reset
|
| 320 |
+
FRICP_PARAS["maxiter"],
|
| 321 |
+
FRICP_PARAS["stop"],
|
| 322 |
+
SPARE_PARAS["iters"],
|
| 323 |
+
SPARE_PARAS["stopcoarse"],
|
| 324 |
+
SPARE_PARAS["stopfine"],
|
| 325 |
+
gr.update(value=""),
|
| 326 |
+
gr.update(value=""),
|
| 327 |
+
gr.update(value=False),
|
| 328 |
+
gr.update(visible=False)
|
| 329 |
+
)
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
def clear_all():
|
| 333 |
+
"""
|
| 334 |
+
Full reset for all UI elements:
|
| 335 |
+
- Clears uploaded files
|
| 336 |
+
- Resets plots
|
| 337 |
+
- Resets all parameters (Rigid + NonRigid + Landmark)
|
| 338 |
+
- Resets landmark checkbox & hides group
|
| 339 |
+
"""
|
| 340 |
+
|
| 341 |
+
FRICP_PARAS.update({
|
| 342 |
+
"useinit": False,
|
| 343 |
+
"fileinit": "",
|
| 344 |
+
"maxiter": 100,
|
| 345 |
+
"stop": 1e-5
|
| 346 |
+
})
|
| 347 |
+
SPARE_PARAS.update({
|
| 348 |
+
"iters": 30,
|
| 349 |
+
"stopcoarse": 1e-3,
|
| 350 |
+
"stopfine": 1e-4,
|
| 351 |
+
"use_landmark": False,
|
| 352 |
+
"src": [],
|
| 353 |
+
"tar": []
|
| 354 |
+
})
|
| 355 |
+
|
| 356 |
+
print("[clear] All files, parameters, and landmarks cleared.")
|
| 357 |
+
return (
|
| 358 |
+
None, # target_input
|
| 359 |
+
None, # source_input
|
| 360 |
+
None, # upload_plot
|
| 361 |
+
None, # result_plot
|
| 362 |
+
FRICP_PARAS["useinit"], # fricp_useinit
|
| 363 |
+
None, # fricp_fileinit
|
| 364 |
+
FRICP_PARAS["maxiter"],
|
| 365 |
+
FRICP_PARAS["stop"],
|
| 366 |
+
SPARE_PARAS["iters"],
|
| 367 |
+
SPARE_PARAS["stopcoarse"],
|
| 368 |
+
SPARE_PARAS["stopfine"],
|
| 369 |
+
gr.update(value=""),
|
| 370 |
+
gr.update(value=""),
|
| 371 |
+
gr.update(value=False),
|
| 372 |
+
gr.update(visible=False)
|
| 373 |
+
)
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
def visualize_and_store(target_file, source_file, scatter_mode=False):
|
| 377 |
+
if target_file is None or source_file is None:
|
| 378 |
+
return None, None, None
|
| 379 |
+
target_v, _ = read_mesh(target_file.name)
|
| 380 |
+
source_v, _ = read_mesh(source_file.name)
|
| 381 |
+
fig = plot_source_target_mesh(target_file.name, source_file.name, scatter_mode=scatter_mode)
|
| 382 |
+
|
| 383 |
+
return fig, source_v, target_v
|
| 384 |
+
|
| 385 |
+
def clear_landmarks():
|
| 386 |
+
return [], [], "", ""
|
| 387 |
+
def highlight_landmarks_on_mesh(target_file, source_file, src_text, tar_text):
|
| 388 |
+
src_v, _ = read_mesh(source_file.name)
|
| 389 |
+
tar_v, _ = read_mesh(target_file.name)
|
| 390 |
+
src_ids = [int(i) for i in src_text.split(",") if i.strip().isdigit()]
|
| 391 |
+
tar_ids = [int(i) for i in tar_text.split(",") if i.strip().isdigit()]
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
fig = plot_source_target_mesh(target_file.name, source_file.name)
|
| 395 |
+
|
| 396 |
+
# === Source landmarks ===
|
| 397 |
+
if len(src_ids) > 0:
|
| 398 |
+
pts = src_v[src_ids]
|
| 399 |
+
fig.add_trace(go.Scatter3d(
|
| 400 |
+
x=pts[:, 0], y=pts[:, 1], z=pts[:, 2],
|
| 401 |
+
mode="markers+text",
|
| 402 |
+
text=[str(i) for i in src_ids],
|
| 403 |
+
textposition="top center",
|
| 404 |
+
textfont=dict(color="black", size=10, family="Arial"),
|
| 405 |
+
marker=dict(
|
| 406 |
+
size=6,
|
| 407 |
+
color="limegreen",
|
| 408 |
+
opacity=0.9,
|
| 409 |
+
line=dict(width=2, color="white"),
|
| 410 |
+
symbol="circle"
|
| 411 |
+
),
|
| 412 |
+
name="Source Landmarks",
|
| 413 |
+
hoverinfo="text",
|
| 414 |
+
hovertext=[
|
| 415 |
+
f"<b>Source ID:</b> {i}<br>x={x:.3f}, y={y:.3f}, z={z:.3f}"
|
| 416 |
+
for i, (x, y, z) in zip(src_ids, pts)
|
| 417 |
+
]
|
| 418 |
+
))
|
| 419 |
+
|
| 420 |
+
# === Target landmarks ===
|
| 421 |
+
if len(tar_ids) > 0:
|
| 422 |
+
pts = tar_v[tar_ids]
|
| 423 |
+
fig.add_trace(go.Scatter3d(
|
| 424 |
+
x=pts[:, 0], y=pts[:, 1], z=pts[:, 2],
|
| 425 |
+
mode="markers+text",
|
| 426 |
+
text=[str(i) for i in tar_ids],
|
| 427 |
+
textposition="top center",
|
| 428 |
+
textfont=dict(color="black", size=10, family="Arial"),
|
| 429 |
+
marker=dict(
|
| 430 |
+
size=6,
|
| 431 |
+
color="crimson",
|
| 432 |
+
opacity=0.9,
|
| 433 |
+
line=dict(width=2, color="white"),
|
| 434 |
+
symbol="circle"
|
| 435 |
+
),
|
| 436 |
+
name="Target Landmarks",
|
| 437 |
+
hoverinfo="text",
|
| 438 |
+
hovertext=[
|
| 439 |
+
f"<b>Target ID:</b> {i}<br>x={x:.3f}, y={y:.3f}, z={z:.3f}"
|
| 440 |
+
for i, (x, y, z) in zip(tar_ids, pts)
|
| 441 |
+
]
|
| 442 |
+
))
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
fig.update_layout(
|
| 446 |
+
scene=dict(
|
| 447 |
+
xaxis=dict(visible=False),
|
| 448 |
+
yaxis=dict(visible=False),
|
| 449 |
+
zaxis=dict(visible=False)
|
| 450 |
+
),
|
| 451 |
+
legend=dict(bgcolor="rgba(255,255,255,0.6)"),
|
| 452 |
+
margin=dict(l=0, r=0, t=40, b=0),
|
| 453 |
+
title="Landmark Highlight View"
|
| 454 |
+
)
|
| 455 |
+
|
| 456 |
+
return fig
|
| 457 |
+
|
| 458 |
+
def start_landmark_selection(target_file, source_file):
|
| 459 |
+
if target_file is None or source_file is None:
|
| 460 |
+
raise gr.Error("Please upload both Source and Target first!")
|
| 461 |
+
|
| 462 |
+
fig = plot_source_target_mesh(target_file.name, source_file.name, scatter_mode=True)
|
| 463 |
+
return fig
|
| 464 |
+
def update_landmark_ids(src_text, tar_text):
|
| 465 |
+
src_ids = [int(i) for i in src_text.split(",") if i.strip().isdigit()]
|
| 466 |
+
tar_ids = [int(i) for i in tar_text.split(",") if i.strip().isdigit()]
|
| 467 |
+
return src_ids, tar_ids, src_text, tar_text
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
def set_landmarks_to_registration(src_ids, tar_ids):
|
| 471 |
+
if not src_ids or not tar_ids:
|
| 472 |
+
raise gr.Error("Please select both Source and Target points first!")
|
| 473 |
+
if len(src_ids) != len(tar_ids):
|
| 474 |
+
raise gr.Error(f"Landmark count mismatch: Source={len(src_ids)} vs Target={len(tar_ids)}")
|
| 475 |
+
|
| 476 |
+
SPARE_PARAS["use_landmark"] = True
|
| 477 |
+
SPARE_PARAS["src"] = [int(i) for i in src_ids]
|
| 478 |
+
SPARE_PARAS["tar"] = [int(i) for i in tar_ids]
|
| 479 |
+
|
| 480 |
+
return f"✅ Landmarks staged: {len(src_ids)} Source ↔ {len(tar_ids)} Target. Will be used in NonRigid registration."
|
| 481 |
+
|
| 482 |
+
# ------------------ UI ------------------
|
| 483 |
+
CSS_FIX = """
|
| 484 |
+
html, body, .gradio-container {
|
| 485 |
+
min-height: 100vh;
|
| 486 |
+
overflow-y: auto;
|
| 487 |
+
}
|
| 488 |
+
"""
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
with gr.Blocks(css=CSS_FIX) as demo:
|
| 493 |
+
gr.Markdown("## Point Cloud / Mesh Registration Visualization Tool")
|
| 494 |
+
|
| 495 |
+
src_ids_state = gr.State([])
|
| 496 |
+
tar_ids_state = gr.State([])
|
| 497 |
+
src_vertices_state = gr.State(None)
|
| 498 |
+
tar_vertices_state = gr.State(None)
|
| 499 |
+
|
| 500 |
+
with gr.Row():
|
| 501 |
+
with gr.Column(scale=1):
|
| 502 |
+
|
| 503 |
+
source_input = gr.File(label="Source File", file_types=[".ply", ".obj"])
|
| 504 |
+
target_input = gr.File(label="Target File", file_types=[".ply", ".obj"])
|
| 505 |
+
method_dropdown = gr.Dropdown(label="Registration Method",
|
| 506 |
+
choices=["Rigid", "NonRigid"], value="Rigid")
|
| 507 |
+
|
| 508 |
+
with gr.Accordion("Rigid Parameters", open=False):
|
| 509 |
+
fricp_useinit = gr.Checkbox(label="Use initial transform?", value=False)
|
| 510 |
+
fricp_fileinit = gr.File(label="Initial transform file", file_types=[".txt"])
|
| 511 |
+
fricp_maxiter = gr.Number(label="Max iterations", value=100)
|
| 512 |
+
fricp_stop = gr.Number(label="Stop threshold", value=1e-5)
|
| 513 |
+
|
| 514 |
+
|
| 515 |
+
with gr.Accordion("NonRigid Parameters", open=False):
|
| 516 |
+
spare_iters = gr.Number(label="Iterations", value=30)
|
| 517 |
+
spare_stopcoarse = gr.Number(label="Stop Coarse", value=1e-3)
|
| 518 |
+
spare_stopfine = gr.Number(label="Stop Fine", value=1e-4)
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
use_landmark_cb = gr.Checkbox(label="Use Landmarks", value=False)
|
| 522 |
+
|
| 523 |
+
|
| 524 |
+
with gr.Group(visible=False) as landmark_group:
|
| 525 |
+
gr.Markdown("### Landmark Point Selection")
|
| 526 |
+
selected_src = gr.Textbox(
|
| 527 |
+
label="Selected Source IDs (comma-separated)",
|
| 528 |
+
placeholder="e.g. 12,45,89", interactive=True)
|
| 529 |
+
selected_tar = gr.Textbox(
|
| 530 |
+
label="Selected Target IDs (comma-separated)",
|
| 531 |
+
placeholder="e.g. 7,42,105", interactive=True)
|
| 532 |
+
|
| 533 |
+
with gr.Row():
|
| 534 |
+
start_landmark_btn = gr.Button("Start Landmark Selection", elem_classes="primary")
|
| 535 |
+
highlight_btn = gr.Button("Highlight Landmarks")
|
| 536 |
+
clear_landmark_btn = gr.Button("🧹 Clear Selections")
|
| 537 |
+
|
| 538 |
+
select_button = gr.Button("Confirm Landmarks to Registration", elem_classes="primary")
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
output_dir = gr.Textbox(label="Output Directory", value="./output/", placeholder="./output/")
|
| 542 |
+
with gr.Row():
|
| 543 |
+
run_button = gr.Button("Run Registration", elem_classes="primary")
|
| 544 |
+
rerun_button = gr.Button("Reregister (Use Last Result as Source)", elem_classes="primary")
|
| 545 |
+
clear_button = gr.Button("Clear", elem_classes="secondary")
|
| 546 |
+
reset_button = gr.Button("Reset Parameters", elem_classes="primary")
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
with gr.Row():
|
| 550 |
+
example_dropdown = gr.Dropdown(label="Load Example Data",
|
| 551 |
+
choices=["Rigid", "NonRigid"], value="Rigid")
|
| 552 |
+
example_button = gr.Button("Load Example")
|
| 553 |
+
|
| 554 |
+
example_button.click(fn=load_example,
|
| 555 |
+
inputs=[example_dropdown],
|
| 556 |
+
outputs=[source_input, target_input, method_dropdown])
|
| 557 |
+
|
| 558 |
+
|
| 559 |
+
with gr.Column(scale=2):
|
| 560 |
+
upload_plot = gr.Plot(label="Source vs Target Mesh")
|
| 561 |
+
result_plot = gr.Plot(label="Result vs Target Mesh")
|
| 562 |
+
download_button = gr.File(label="Download Result Directory",
|
| 563 |
+
file_types=[".zip"], interactive=False)
|
| 564 |
+
|
| 565 |
+
|
| 566 |
+
reset_button.click(
|
| 567 |
+
fn=reset_parameters,
|
| 568 |
+
inputs=None,
|
| 569 |
+
outputs=[
|
| 570 |
+
fricp_useinit, fricp_fileinit, fricp_maxiter, fricp_stop,
|
| 571 |
+
spare_iters, spare_stopcoarse, spare_stopfine,
|
| 572 |
+
selected_src, selected_tar,
|
| 573 |
+
use_landmark_cb, landmark_group
|
| 574 |
+
]
|
| 575 |
+
)
|
| 576 |
+
|
| 577 |
+
def toggle_landmark_visibility(use_landmark):
|
| 578 |
+
SPARE_PARAS["use_landmark"] = bool(use_landmark)
|
| 579 |
+
print(f"[UI] use_landmark set to {SPARE_PARAS['use_landmark']}")
|
| 580 |
+
return gr.update(visible=use_landmark)
|
| 581 |
+
|
| 582 |
+
use_landmark_cb.change(fn=toggle_landmark_visibility,
|
| 583 |
+
inputs=[use_landmark_cb],
|
| 584 |
+
outputs=[landmark_group])
|
| 585 |
+
|
| 586 |
+
clear_button.click(
|
| 587 |
+
fn=clear_all,
|
| 588 |
+
inputs=None,
|
| 589 |
+
outputs=[
|
| 590 |
+
target_input, source_input, upload_plot, result_plot,
|
| 591 |
+
fricp_useinit, fricp_fileinit, fricp_maxiter, fricp_stop,
|
| 592 |
+
spare_iters, spare_stopcoarse, spare_stopfine,
|
| 593 |
+
selected_src, selected_tar,
|
| 594 |
+
use_landmark_cb, landmark_group
|
| 595 |
+
]
|
| 596 |
+
)
|
| 597 |
+
|
| 598 |
+
|
| 599 |
+
target_input.change(fn=visualize_and_store,
|
| 600 |
+
inputs=[target_input, source_input],
|
| 601 |
+
outputs=[upload_plot, src_vertices_state, tar_vertices_state])
|
| 602 |
+
source_input.change(fn=visualize_and_store,
|
| 603 |
+
inputs=[target_input, source_input],
|
| 604 |
+
outputs=[upload_plot, src_vertices_state, tar_vertices_state])
|
| 605 |
+
|
| 606 |
+
start_landmark_btn.click(fn=start_landmark_selection,
|
| 607 |
+
inputs=[target_input, source_input],
|
| 608 |
+
outputs=[upload_plot])
|
| 609 |
+
|
| 610 |
+
highlight_btn.click(fn=highlight_landmarks_on_mesh,
|
| 611 |
+
inputs=[target_input, source_input, selected_src, selected_tar],
|
| 612 |
+
outputs=[upload_plot])
|
| 613 |
+
|
| 614 |
+
clear_landmark_btn.click(fn=clear_landmarks,
|
| 615 |
+
inputs=None,
|
| 616 |
+
outputs=[src_ids_state, tar_ids_state, selected_src, selected_tar])
|
| 617 |
+
|
| 618 |
+
select_button.click(fn=update_landmark_ids,
|
| 619 |
+
inputs=[selected_src, selected_tar],
|
| 620 |
+
outputs=[src_ids_state, tar_ids_state, selected_src, selected_tar]
|
| 621 |
+
).then(fn=set_landmarks_to_registration,
|
| 622 |
+
inputs=[src_ids_state, tar_ids_state],
|
| 623 |
+
outputs=[gr.Textbox(label="Landmark Setup Info")])
|
| 624 |
+
|
| 625 |
+
run_button.click(fn=register_and_visualize_with_zip,
|
| 626 |
+
inputs=[target_input, source_input, output_dir, method_dropdown,
|
| 627 |
+
fricp_useinit, fricp_fileinit, fricp_maxiter, fricp_stop,
|
| 628 |
+
spare_iters, spare_stopcoarse, spare_stopfine],
|
| 629 |
+
outputs=[result_plot, download_button])
|
| 630 |
+
|
| 631 |
+
rerun_button.click(fn=reuse_last_result_as_source,
|
| 632 |
+
inputs=[target_input, output_dir],
|
| 633 |
+
outputs=[source_input, upload_plot])
|
| 634 |
+
|
| 635 |
+
if __name__ == "__main__":
|
| 636 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|
| 637 |
+
|
| 638 |
+
|
apt.txt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
build-essential
|
| 3 |
+
cmake
|
| 4 |
+
g++
|
| 5 |
+
git
|
| 6 |
+
wget
|
| 7 |
+
curl
|
| 8 |
+
make
|
| 9 |
+
pkg-config
|
| 10 |
+
libeigen3-dev
|
| 11 |
+
libopenmesh-dev
|
| 12 |
+
pybind11-dev
|
| 13 |
+
python3-dev
|
| 14 |
+
python3-pip
|
| 15 |
+
|
| 16 |
+
|
build.sh
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -e
|
| 3 |
+
|
| 4 |
+
# 安装系统依赖
|
| 5 |
+
apt-get update
|
| 6 |
+
xargs -a apt.txt apt-get install -y
|
| 7 |
+
|
| 8 |
+
# 清理并创建 build 目录
|
| 9 |
+
rm -rf build
|
| 10 |
+
mkdir build
|
| 11 |
+
cd build
|
| 12 |
+
|
| 13 |
+
# 生成 Makefile,指定 Python
|
| 14 |
+
cmake .. -DPYTHON_EXECUTABLE=$(which python3)
|
| 15 |
+
|
| 16 |
+
# 编译
|
| 17 |
+
cmake --build . --config Release -j$(nproc)
|
| 18 |
+
|
| 19 |
+
# 回到主目录
|
| 20 |
+
cd ..
|
| 21 |
+
|
| 22 |
+
echo "✅ Build complete. .so is in build/python/"
|
cmake/FindEigen3.cmake
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FIND_PATH( EIGEN3_INCLUDE_DIRS Eigen/Geometry
|
| 2 |
+
$ENV{EIGEN3DIR}/include
|
| 3 |
+
/usr/local/include/eigen3
|
| 4 |
+
/usr/local/include
|
| 5 |
+
/usr/local/X11R6/include
|
| 6 |
+
/usr/X11R6/include
|
| 7 |
+
/usr/X11/include
|
| 8 |
+
/usr/include/X11
|
| 9 |
+
/usr/include/eigen3/
|
| 10 |
+
/usr/include
|
| 11 |
+
/opt/X11/include
|
| 12 |
+
/opt/include
|
| 13 |
+
${CMAKE_SOURCE_DIR}/external/eigen/include
|
| 14 |
+
${CMAKE_SOURCE_DIR}/include/eigen3)
|
| 15 |
+
|
| 16 |
+
SET(EIGEN3_FOUND "NO")
|
| 17 |
+
IF(EIGEN3_INCLUDE_DIRS)
|
| 18 |
+
SET(EIGEN3_FOUND "YES")
|
| 19 |
+
ENDIF()
|
cmake/FindNanoFlann.cmake
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FIND_PATH(
|
| 2 |
+
# filled variable
|
| 3 |
+
NANOFLANN_INCLUDE_DIR
|
| 4 |
+
# what I am looking for?
|
| 5 |
+
nanoflann.hpp
|
| 6 |
+
# where should I look?
|
| 7 |
+
$ENV{NANOFLANN_DIR}
|
| 8 |
+
${CMAKE_SOURCE_DIR}/include)
|
| 9 |
+
|
| 10 |
+
IF(NANOFLANN_INCLUDE_DIR)
|
| 11 |
+
SET(NANOFLANN_FOUND TRUE)
|
| 12 |
+
else()
|
| 13 |
+
SET(NANOFLANN_FOUND FALSE)
|
| 14 |
+
ENDIF()
|
| 15 |
+
|
| 16 |
+
IF(NANOFLANN_FOUND)
|
| 17 |
+
IF(NOT CMAKE_FIND_QUIETLY)
|
| 18 |
+
MESSAGE(STATUS "Found NanoFlann: ${NANOFLANN_INCLUDE_DIR}")
|
| 19 |
+
ENDIF()
|
| 20 |
+
ELSE()
|
| 21 |
+
IF(NANOFLANN_FOUND_REQUIRED)
|
| 22 |
+
MESSAGE(FATAL_ERROR "Could not find NanoFlann")
|
| 23 |
+
ENDIF()
|
| 24 |
+
ENDIF()
|
cmake/FindOpenMesh.cmake
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# - Try to find OpenMesh
|
| 2 |
+
# This is a minimal FindOpenMesh.cmake for systems with libopenmesh-dev installed
|
| 3 |
+
|
| 4 |
+
# 查找头文件
|
| 5 |
+
find_path(OPENMESH_INCLUDE_DIR OpenMesh/Core/IO/MeshIO.hh
|
| 6 |
+
PATHS /usr/include /usr/include/OpenMesh
|
| 7 |
+
PATH_SUFFIXES OpenMesh)
|
| 8 |
+
|
| 9 |
+
# 查找库文件
|
| 10 |
+
find_library(OPENMESH_CORE_LIBRARY OpenMeshCore
|
| 11 |
+
PATHS /usr/lib /usr/lib/x86_64-linux-gnu)
|
| 12 |
+
|
| 13 |
+
find_library(OPENMESH_TOOLS_LIBRARY OpenMeshTools
|
| 14 |
+
PATHS /usr/lib /usr/lib/x86_64-linux-gnu)
|
| 15 |
+
|
| 16 |
+
# 设置变量给 CMake 使用
|
| 17 |
+
set(OPENMESH_LIBRARIES ${OPENMESH_CORE_LIBRARY} ${OPENMESH_TOOLS_LIBRARY})
|
| 18 |
+
set(OPENMESH_FOUND TRUE)
|
| 19 |
+
|
| 20 |
+
# 输出信息
|
| 21 |
+
if(OPENMESH_FOUND)
|
| 22 |
+
message(STATUS "Found OpenMesh:")
|
| 23 |
+
message(STATUS " Include dir: ${OPENMESH_INCLUDE_DIR}")
|
| 24 |
+
message(STATUS " Libraries: ${OPENMESH_LIBRARIES}")
|
| 25 |
+
else()
|
| 26 |
+
message(FATAL_ERROR "OpenMesh not found")
|
| 27 |
+
endif()
|
cpp/core/nonrigid_spare_registration.cpp
ADDED
|
@@ -0,0 +1,1013 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#include "nonrigid_spare_registration.h"
|
| 3 |
+
#include "tools.h"
|
| 4 |
+
#include "robust_norm.h"
|
| 5 |
+
#include <median.h>
|
| 6 |
+
|
| 7 |
+
#if (__cplusplus >= 201402L) || (defined(_MSC_VER) && _MSC_VER >= 1800)
|
| 8 |
+
#define MAKE_UNIQUE std::make_unique
|
| 9 |
+
#else
|
| 10 |
+
#define MAKE_UNIQUE company::make_unique
|
| 11 |
+
#endif
|
| 12 |
+
|
| 13 |
+
/// @param Source (one 3D point per column)
|
| 14 |
+
/// @param Target (one 3D point per column)
|
| 15 |
+
/// @param Confidence weights
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
NonrigidSpareRegistration::NonrigidSpareRegistration() {
|
| 19 |
+
};
|
| 20 |
+
|
| 21 |
+
NonrigidSpareRegistration::~NonrigidSpareRegistration()
|
| 22 |
+
{
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
void NonrigidSpareRegistration::Initialize()
|
| 26 |
+
{
|
| 27 |
+
|
| 28 |
+
//welsch weight paramete
|
| 29 |
+
InitWelschParam();
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
int knn_num_neighbor = 6;
|
| 33 |
+
|
| 34 |
+
if(!pars_.use_geodesic_dist)
|
| 35 |
+
{
|
| 36 |
+
src_knn_indices_.resize(knn_num_neighbor, n_src_vertex_);
|
| 37 |
+
KDtree* src_tree = new KDtree(src_points_);
|
| 38 |
+
#pragma omp parallel for
|
| 39 |
+
for(int i = 0; i < n_src_vertex_; i++)
|
| 40 |
+
{
|
| 41 |
+
int* out_indices = new int[knn_num_neighbor+1];
|
| 42 |
+
Scalar *out_dists = new Scalar[knn_num_neighbor+1];
|
| 43 |
+
src_tree->query(src_points_.col(i).data(), knn_num_neighbor+1, out_indices, out_dists);
|
| 44 |
+
for(int j = 0; j < knn_num_neighbor; j++)
|
| 45 |
+
{
|
| 46 |
+
src_knn_indices_(j, i) = out_indices[j+1];
|
| 47 |
+
}
|
| 48 |
+
delete[] out_indices;
|
| 49 |
+
delete[] out_dists;
|
| 50 |
+
}
|
| 51 |
+
delete src_tree;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
Scalar sample_radius;
|
| 56 |
+
|
| 57 |
+
if(pars_.use_geodesic_dist)
|
| 58 |
+
sample_radius = src_sample_nodes.SampleAndConstuct(*src_mesh_, pars_.uni_sample_radio, src_points_);
|
| 59 |
+
else
|
| 60 |
+
sample_radius = src_sample_nodes.SampleAndConstuctFPS(*src_mesh_, pars_.uni_sample_radio, src_points_, src_knn_indices_, 4, 8);
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
num_sample_nodes = src_sample_nodes.nodeSize();
|
| 64 |
+
pars_.num_sample_nodes = num_sample_nodes;
|
| 65 |
+
|
| 66 |
+
X_.resize(12 * num_sample_nodes); X_.setZero();
|
| 67 |
+
align_coeff_PV0_.resize(3 * n_src_vertex_, 12 * num_sample_nodes);
|
| 68 |
+
nodes_P_.resize(n_src_vertex_ * 3);
|
| 69 |
+
|
| 70 |
+
nodes_R_.resize(9 * num_sample_nodes); nodes_R_.setZero();
|
| 71 |
+
rigid_coeff_L_.resize(12 * num_sample_nodes, 12 * num_sample_nodes);
|
| 72 |
+
rigid_coeff_J_.resize(12 * num_sample_nodes, 9 * num_sample_nodes);
|
| 73 |
+
|
| 74 |
+
std::vector<Triplet> coeffv(4 * num_sample_nodes);
|
| 75 |
+
std::vector<Triplet> coeffL(9 * num_sample_nodes);
|
| 76 |
+
std::vector<Triplet> coeffJ(9 * num_sample_nodes);
|
| 77 |
+
for (int i = 0; i < num_sample_nodes; i++)
|
| 78 |
+
{
|
| 79 |
+
// X_
|
| 80 |
+
X_[12 * i] = 1.0;
|
| 81 |
+
X_[12 * i + 4] = 1.0;
|
| 82 |
+
X_[12 * i + 8] = 1.0;
|
| 83 |
+
|
| 84 |
+
// nodes_R_
|
| 85 |
+
nodes_R_[9 * i] = 1.0;
|
| 86 |
+
nodes_R_[9 * i + 4] = 1.0;
|
| 87 |
+
nodes_R_[9 * i + 8] = 1.0;
|
| 88 |
+
|
| 89 |
+
for (int j = 0; j < 9; j++)
|
| 90 |
+
{
|
| 91 |
+
// rigid_coeff_L_
|
| 92 |
+
coeffL[9 * i + j] = Triplet(12 * i + j, 12 * i + j, 1.0);
|
| 93 |
+
// rigid_coeff_J_
|
| 94 |
+
coeffJ[9 * i + j] = Triplet(12 * i + j, 9 * i + j, 1.0);
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
rigid_coeff_L_.setFromTriplets(coeffL.begin(), coeffL.end());
|
| 98 |
+
rigid_coeff_J_.setFromTriplets(coeffJ.begin(), coeffJ.end());
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
// update coefficient matrices
|
| 102 |
+
src_sample_nodes.initWeight(align_coeff_PV0_, nodes_P_,
|
| 103 |
+
reg_coeff_B_, reg_right_D_, reg_cwise_weights_);
|
| 104 |
+
|
| 105 |
+
num_graph_edges = reg_cwise_weights_.rows();
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
// update ARAP coeffs
|
| 110 |
+
FullInARAPCoeff();
|
| 111 |
+
|
| 112 |
+
local_rotations_.resize(3, n_src_vertex_ * 3);
|
| 113 |
+
|
| 114 |
+
if(pars_.use_geodesic_dist)
|
| 115 |
+
{
|
| 116 |
+
num_edges = src_mesh_->n_halfedges();
|
| 117 |
+
arap_right_.resize(3*src_mesh_->n_halfedges());
|
| 118 |
+
arap_right_fine_.resize(3 * src_mesh_->n_halfedges());
|
| 119 |
+
}
|
| 120 |
+
else{
|
| 121 |
+
num_edges = knn_num_neighbor*n_src_vertex_;
|
| 122 |
+
arap_right_.resize(3*knn_num_neighbor*n_src_vertex_);
|
| 123 |
+
arap_right_fine_.resize(3 * knn_num_neighbor*n_src_vertex_);
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
InitRotations();
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
sampling_indices_.clear();
|
| 133 |
+
|
| 134 |
+
// start points
|
| 135 |
+
size_t startIndex = 0;
|
| 136 |
+
sampling_indices_.push_back(startIndex);
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
// FPS to get sampling points in align term
|
| 140 |
+
|
| 141 |
+
VectorX minDistances(n_src_vertex_);
|
| 142 |
+
minDistances.setConstant(std::numeric_limits<Scalar>::max());
|
| 143 |
+
minDistances[startIndex] = 0;
|
| 144 |
+
|
| 145 |
+
vertex_sample_indices_.resize(n_src_vertex_, -1);
|
| 146 |
+
vertex_sample_indices_[startIndex] = 0;
|
| 147 |
+
|
| 148 |
+
// repeat select farthest points
|
| 149 |
+
while (sampling_indices_.size() < align_sampling_num_) {
|
| 150 |
+
// calculate the distance between each point with the sampling points set.
|
| 151 |
+
#pragma omp parallel for
|
| 152 |
+
for (size_t i = 0; i < n_src_vertex_; ++i) {
|
| 153 |
+
if(i==startIndex)
|
| 154 |
+
continue;
|
| 155 |
+
|
| 156 |
+
Scalar dist = (src_points_.col(startIndex) - src_points_.col(i)).norm();
|
| 157 |
+
if(dist < minDistances[i])
|
| 158 |
+
minDistances[i] = dist;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
// choose farthest point
|
| 162 |
+
int maxDistanceIndex;
|
| 163 |
+
minDistances.maxCoeff(&maxDistanceIndex);
|
| 164 |
+
minDistances[maxDistanceIndex] = 0;
|
| 165 |
+
|
| 166 |
+
// add the farthest point into the sampling points set.
|
| 167 |
+
sampling_indices_.push_back(maxDistanceIndex);
|
| 168 |
+
startIndex= maxDistanceIndex;
|
| 169 |
+
vertex_sample_indices_[startIndex] = sampling_indices_.size()-1;
|
| 170 |
+
}
|
| 171 |
+
if(pars_.use_landmark && pars_.landmark_src.size() > 0)
|
| 172 |
+
{
|
| 173 |
+
CalcLandmarkCoeff();
|
| 174 |
+
vertex_landmark_indices_.resize(n_src_vertex_, -1);
|
| 175 |
+
for(int i = 0; i < pars_.landmark_src.size(); i++)
|
| 176 |
+
{
|
| 177 |
+
vertex_landmark_indices_[pars_.landmark_src[i]] = i;
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
}
|
| 181 |
+
void NonrigidSpareRegistration::CalcLandmarkCoeff()
|
| 182 |
+
{
|
| 183 |
+
size_t n_landmarks = pars_.landmark_src.size();
|
| 184 |
+
tar_landmarks_.resize(n_landmarks*3);
|
| 185 |
+
RowMajorSparseMatrix landmark_coeff(n_landmarks*3, 12*num_sample_nodes);
|
| 186 |
+
std::vector<Triplet> coeffs;
|
| 187 |
+
for(int idx = 0; idx < n_landmarks; idx++)
|
| 188 |
+
{
|
| 189 |
+
int src_idx = pars_.landmark_src[idx];
|
| 190 |
+
for (int k = 0; k < 3; k++)
|
| 191 |
+
{
|
| 192 |
+
for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, src_idx*3+k); it; ++it)
|
| 193 |
+
{
|
| 194 |
+
coeffs.push_back(Triplet(idx*3+k, it.col(), it.value()));
|
| 195 |
+
}
|
| 196 |
+
tar_landmarks_[idx*3+k] = tar_points_(k, pars_.landmark_tar[idx]) - nodes_P_[src_idx*3+k];
|
| 197 |
+
}
|
| 198 |
+
}
|
| 199 |
+
landmark_coeff.setFromTriplets(coeffs.begin(), coeffs.end());
|
| 200 |
+
landmark_mul_ = landmark_coeff.transpose() * landmark_coeff;
|
| 201 |
+
landmark_right_ = landmark_coeff.transpose() * tar_landmarks_;
|
| 202 |
+
|
| 203 |
+
std::vector<Triplet> coeffs_fine;
|
| 204 |
+
landmark_right_fine_.resize(n_src_vertex_*3);
|
| 205 |
+
landmark_mul_fine_.resize(n_src_vertex_*3, n_src_vertex_*3);
|
| 206 |
+
landmark_right_fine_.setZero();
|
| 207 |
+
for(int idx = 0; idx < n_landmarks; idx++)
|
| 208 |
+
{
|
| 209 |
+
int src_idx = pars_.landmark_src[idx];
|
| 210 |
+
for (int k = 0; k < 3; k++)
|
| 211 |
+
{
|
| 212 |
+
coeffs_fine.push_back(Triplet(src_idx*3+k, src_idx*3+k, 1.0));
|
| 213 |
+
landmark_right_fine_[src_idx*3+k] = tar_points_(k, pars_.landmark_tar[idx]);
|
| 214 |
+
}
|
| 215 |
+
}
|
| 216 |
+
landmark_mul_fine_.setFromTriplets(coeffs_fine.begin(), coeffs_fine.end());
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
void NonrigidSpareRegistration::InitWelschParam()
|
| 220 |
+
{
|
| 221 |
+
// welsch parameters
|
| 222 |
+
weight_d_.resize(n_src_vertex_*3);
|
| 223 |
+
weight_d_.setOnes();
|
| 224 |
+
|
| 225 |
+
// Initialize correspondences
|
| 226 |
+
InitCorrespondence(correspondence_pairs_);
|
| 227 |
+
|
| 228 |
+
VectorX init_nus(correspondence_pairs_.size());
|
| 229 |
+
#pragma omp parallel for
|
| 230 |
+
for(size_t i = 0; i < correspondence_pairs_.size(); i++)
|
| 231 |
+
{
|
| 232 |
+
Vector3 closet = correspondence_pairs_[i].position;
|
| 233 |
+
init_nus[i] = (src_mesh_->point(src_mesh_->vertex_handle(correspondence_pairs_[i].src_idx))
|
| 234 |
+
- Vec3(closet[0], closet[1], closet[2])).norm();
|
| 235 |
+
}
|
| 236 |
+
igl::median(init_nus, pars_.Data_nu);
|
| 237 |
+
|
| 238 |
+
if(pars_.calc_gt_err&&n_src_vertex_ == n_tar_vertex_)
|
| 239 |
+
{
|
| 240 |
+
VectorX gt_err(n_src_vertex_);
|
| 241 |
+
for(int i = 0; i < n_src_vertex_; i++)
|
| 242 |
+
{
|
| 243 |
+
gt_err[i] = (src_mesh_->point(src_mesh_->vertex_handle(i)) - tar_mesh_->point(tar_mesh_->vertex_handle(i))).norm();
|
| 244 |
+
}
|
| 245 |
+
pars_.init_gt_mean_errs = std::sqrt(gt_err.squaredNorm()/n_src_vertex_);
|
| 246 |
+
pars_.init_gt_max_errs = gt_err.maxCoeff();
|
| 247 |
+
}
|
| 248 |
+
}
|
| 249 |
+
void NonrigidSpareRegistration::InitwithLandmark()
|
| 250 |
+
{
|
| 251 |
+
// Scalar energy=0., landmark_err=0., reg_err=0., rot_err=0., arap_err=0.;
|
| 252 |
+
VectorX prevV = VectorX::Zero(n_src_vertex_ * 3);
|
| 253 |
+
|
| 254 |
+
// welsch_sweight
|
| 255 |
+
bool run_once = true;
|
| 256 |
+
|
| 257 |
+
Timer time;
|
| 258 |
+
Timer::EventID begin_time, run_time;
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
w_smo = optimize_w_smo;
|
| 262 |
+
|
| 263 |
+
// pars_.each_energys.push_back(0.0);
|
| 264 |
+
// pars_.each_gt_max_errs.push_back(pars_.init_gt_max_errs);
|
| 265 |
+
// pars_.each_gt_mean_errs.push_back(pars_.init_gt_mean_errs);
|
| 266 |
+
// pars_.each_iters.push_back(0);
|
| 267 |
+
// pars_.each_times.push_back(pars_.non_rigid_init_time);
|
| 268 |
+
// pars_.each_term_energy.push_back(Vector4(0, 0, 0, 0));
|
| 269 |
+
|
| 270 |
+
Scalar gt_err;
|
| 271 |
+
|
| 272 |
+
VectorX prev_X = X_;
|
| 273 |
+
|
| 274 |
+
begin_time = time.get_time();
|
| 275 |
+
|
| 276 |
+
#ifdef DEBUG
|
| 277 |
+
double find_cp_time = 0.0;
|
| 278 |
+
double construct_mat_time = 0.0;
|
| 279 |
+
double solve_eq_time = 0.0;
|
| 280 |
+
double calc_energy_time = 0.0;
|
| 281 |
+
double robust_weight_time = 0.0;
|
| 282 |
+
double update_r_time = 0.0;
|
| 283 |
+
#endif
|
| 284 |
+
|
| 285 |
+
std::cout << "init A" << std::endl;
|
| 286 |
+
RowMajorSparseMatrix A_fixed_coeff = optimize_w_smo * reg_coeff_B_.transpose() * reg_cwise_weights_.asDiagonal() * reg_coeff_B_ + optimize_w_rot * rigid_coeff_L_ + optimize_w_landmark * landmark_mul_; // + optimize_w_arap * arap_coeff_mul_;
|
| 287 |
+
|
| 288 |
+
int out_iter = 0;
|
| 289 |
+
while (out_iter < pars_.max_outer_iters)
|
| 290 |
+
{
|
| 291 |
+
#ifdef DEBUG
|
| 292 |
+
Timer::EventID inner_start_time = time.get_time();
|
| 293 |
+
#endif
|
| 294 |
+
|
| 295 |
+
// int welsch_iter;
|
| 296 |
+
int total_inner_iters = 0;
|
| 297 |
+
|
| 298 |
+
// std::cout << "out_iter" << out_iter << std::endl;
|
| 299 |
+
|
| 300 |
+
mat_A0_ = A_fixed_coeff;
|
| 301 |
+
vec_b_ = optimize_w_smo * reg_coeff_B_.transpose() * reg_cwise_weights_.asDiagonal() * reg_right_D_ + optimize_w_rot * rigid_coeff_J_ * nodes_R_ + optimize_w_landmark * landmark_right_; // + optimize_w_arap * arap_coeff_.transpose() * arap_right_;
|
| 302 |
+
|
| 303 |
+
#ifdef DEBUG
|
| 304 |
+
Timer::EventID end_construct_eq = time.get_time();
|
| 305 |
+
eps_time1 = time.elapsed_time(end_robust_weight, end_construct_eq);
|
| 306 |
+
construct_mat_time += eps_time1;
|
| 307 |
+
#endif
|
| 308 |
+
|
| 309 |
+
if (run_once)
|
| 310 |
+
{
|
| 311 |
+
solver_.analyzePattern(mat_A0_);
|
| 312 |
+
run_once = false;
|
| 313 |
+
}
|
| 314 |
+
solver_.factorize(mat_A0_);
|
| 315 |
+
X_ = solver_.solve(vec_b_);
|
| 316 |
+
|
| 317 |
+
run_time = time.get_time();
|
| 318 |
+
double eps_time = time.elapsed_time(begin_time, run_time);
|
| 319 |
+
|
| 320 |
+
#ifdef DEBUG
|
| 321 |
+
eps_time1 = time.elapsed_time(end_construct_eq, run_time);
|
| 322 |
+
solve_eq_time += eps_time1;
|
| 323 |
+
#endif
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
#ifdef DEBUG
|
| 327 |
+
energy = CalcEnergy(align_err, reg_err, rot_err, arap_err, reg_cwise_weights_);
|
| 328 |
+
// std::cout << "energy = " << energy << std::endl;
|
| 329 |
+
#endif
|
| 330 |
+
// std::cout << "X = " << X_ << X_.sum() << std::endl;
|
| 331 |
+
deformed_points_ = align_coeff_PV0_ * X_ + nodes_P_;
|
| 332 |
+
// std::cout << "diff = " << (deformed_points_ - prevV).norm() << std::endl;
|
| 333 |
+
|
| 334 |
+
#ifdef DEBUG
|
| 335 |
+
Timer::EventID end_calc_energy = time.get_time();
|
| 336 |
+
eps_time1 = time.elapsed_time(run_time, end_calc_energy);
|
| 337 |
+
calc_energy_time += eps_time1;
|
| 338 |
+
#endif
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
#ifdef DEBUG
|
| 343 |
+
Timer::EventID end_update_r = time.get_time();
|
| 344 |
+
eps_time1 = time.elapsed_time(end_calc_energy, end_update_r);
|
| 345 |
+
update_r_time += eps_time1;
|
| 346 |
+
#endif
|
| 347 |
+
|
| 348 |
+
CalcLocalRotations(0);
|
| 349 |
+
CalcNodeRotations();
|
| 350 |
+
CalcDeformedNormals();
|
| 351 |
+
|
| 352 |
+
if (n_src_vertex_ == n_tar_vertex_)
|
| 353 |
+
gt_err = (deformed_points_ - Eigen::Map<VectorX>(tar_points_.data(), 3 * n_src_vertex_)).squaredNorm();
|
| 354 |
+
|
| 355 |
+
// save results
|
| 356 |
+
// pars_.each_gt_mean_errs.push_back(gt_err);
|
| 357 |
+
// pars_.each_gt_max_errs.push_back(0);
|
| 358 |
+
// pars_.each_energys.push_back(energy);
|
| 359 |
+
// pars_.each_iters.push_back(total_inner_iters);
|
| 360 |
+
// pars_.each_term_energy.push_back(Vector4(0.0, reg_err, rot_err, arap_err));
|
| 361 |
+
|
| 362 |
+
if((deformed_points_ - prevV).norm()/sqrtf(n_src_vertex_) < pars_.stop_coarse)
|
| 363 |
+
{
|
| 364 |
+
break;
|
| 365 |
+
}
|
| 366 |
+
prevV = deformed_points_;
|
| 367 |
+
out_iter++;
|
| 368 |
+
|
| 369 |
+
#ifdef DEBUG
|
| 370 |
+
Timer::EventID end_find_cp2 = time.get_time();
|
| 371 |
+
eps_time1 = time.elapsed_time(end_calc_energy, end_find_cp2);
|
| 372 |
+
find_cp_time += eps_time1;
|
| 373 |
+
#endif
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
#ifdef DEBUG
|
| 377 |
+
std::cout << "find cp time = " << find_cp_time
|
| 378 |
+
<< "\nconstruct_mat_timem = " << construct_mat_time
|
| 379 |
+
<< "\nsolve_eq_time = " << solve_eq_time
|
| 380 |
+
<< "\ncalc_energy_time = " << calc_energy_time
|
| 381 |
+
<< "\nrobust_weight_time = " << robust_weight_time
|
| 382 |
+
<< "\nupdate_r_time = " << update_r_time
|
| 383 |
+
<< "\nacculate_iter = " << out_iter << std::endl;
|
| 384 |
+
#endif
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
Scalar NonrigidSpareRegistration::DoNonRigid()
|
| 389 |
+
{
|
| 390 |
+
// Data term parameters
|
| 391 |
+
Scalar nu1 = pars_.Data_initk * pars_.Data_nu;
|
| 392 |
+
if(pars_.landmark_src.size()>0)
|
| 393 |
+
{
|
| 394 |
+
optimize_w_landmark = 1.0/pars_.landmark_src.size();
|
| 395 |
+
optimize_w_smo = pars_.w_smo/reg_coeff_B_.rows();
|
| 396 |
+
optimize_w_rot = pars_.w_rot/num_sample_nodes;
|
| 397 |
+
optimize_w_arap = pars_.w_arap_coarse/arap_coeff_.rows();
|
| 398 |
+
std::cout << "Init Stage: optimize w_landmark = " << optimize_w_landmark << " | w_smo = " << optimize_w_smo << " | w_rot = " << optimize_w_rot << " | w_arap = " << optimize_w_arap << std::endl;
|
| 399 |
+
InitwithLandmark();
|
| 400 |
+
}
|
| 401 |
+
optimize_w_align = 1.0;
|
| 402 |
+
//A parameter used to balance the influence of each energy component on the total energy.
|
| 403 |
+
optimize_w_smo = pars_.w_smo/reg_coeff_B_.rows() * sampling_indices_.size();
|
| 404 |
+
optimize_w_rot = pars_.w_rot/num_sample_nodes * sampling_indices_.size();
|
| 405 |
+
optimize_w_arap = pars_.w_arap_coarse/arap_coeff_.rows() * sampling_indices_.size();
|
| 406 |
+
std::cout << "Coarse Stage: optimize w_align = " << optimize_w_align << " | w_smo = " << optimize_w_smo << " | w_rot = " << optimize_w_rot << " | w_arap = " << optimize_w_arap << std::endl;
|
| 407 |
+
GraphCoarseReg(nu1);//nu1 is the parameter of welsch kernel
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
optimize_w_align = 1.0;
|
| 411 |
+
optimize_w_arap = pars_.w_arap_fine /arap_coeff_fine_.rows() * n_src_vertex_;
|
| 412 |
+
std::cout << "Fine Stage: optimize w_align = " << optimize_w_align << " | w_arap = " << optimize_w_arap << std::endl;
|
| 413 |
+
PointwiseFineReg(nu1);//nu1 is still the parameter of welsch kernel
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
// set the final deformed points to the source mesh
|
| 417 |
+
//and return the final gt error if needed,if not return -1
|
| 418 |
+
deformed_points_3X_ = Eigen::Map<Eigen::Matrix<double, 3, Eigen::Dynamic>>(deformed_points_.data(), 3, n_src_vertex_);
|
| 419 |
+
return 0;
|
| 420 |
+
}
|
| 421 |
+
|
| 422 |
+
|
| 423 |
+
void NonrigidSpareRegistration::CalcNodeRotations()
|
| 424 |
+
{
|
| 425 |
+
#pragma omp parallel for
|
| 426 |
+
for (int i = 0; i < num_sample_nodes; i++)
|
| 427 |
+
{
|
| 428 |
+
Matrix33 rot;
|
| 429 |
+
Eigen::JacobiSVD<Matrix33> svd(Eigen::Map<Matrix33>(X_.data()+12*i, 3,3), Eigen::ComputeFullU | Eigen::ComputeFullV);
|
| 430 |
+
if (svd.matrixU().determinant()*svd.matrixV().determinant() < 0.0) {
|
| 431 |
+
Vector3 S = Vector3::Ones(); S(2) = -1.0;
|
| 432 |
+
rot = svd.matrixU()*S.asDiagonal()*svd.matrixV().transpose();
|
| 433 |
+
}
|
| 434 |
+
else {
|
| 435 |
+
rot = svd.matrixU()*svd.matrixV().transpose();
|
| 436 |
+
}
|
| 437 |
+
nodes_R_.segment(9 * i, 9) = Eigen::Map<VectorX>(rot.data(), 9);
|
| 438 |
+
}
|
| 439 |
+
}
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
void NonrigidSpareRegistration::FullInARAPCoeff()
|
| 445 |
+
{
|
| 446 |
+
arap_laplace_weights_.resize(n_src_vertex_);
|
| 447 |
+
Timer timer;
|
| 448 |
+
|
| 449 |
+
if(pars_.use_geodesic_dist)
|
| 450 |
+
{
|
| 451 |
+
for (int i = 0; i < n_src_vertex_; i++)
|
| 452 |
+
{
|
| 453 |
+
int nn = 0;
|
| 454 |
+
OpenMesh::VertexHandle vh = src_mesh_->vertex_handle(i);
|
| 455 |
+
for (auto vv = src_mesh_->vv_begin(vh); vv != src_mesh_->vv_end(vh); vv++)
|
| 456 |
+
{
|
| 457 |
+
nn++;
|
| 458 |
+
}
|
| 459 |
+
arap_laplace_weights_[i] = 1.0 / nn;
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
std::vector<Triplet> coeffs;
|
| 463 |
+
std::vector<Triplet> coeffs_fine;
|
| 464 |
+
for (int i = 0; i < src_mesh_->n_halfedges(); i++)
|
| 465 |
+
{
|
| 466 |
+
int src_idx = src_mesh_->from_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
|
| 467 |
+
int tar_idx = src_mesh_->to_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
|
| 468 |
+
Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
for (int k = 0; k < 3; k++)
|
| 472 |
+
{
|
| 473 |
+
for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, src_idx*3+k); it; ++it)
|
| 474 |
+
{
|
| 475 |
+
coeffs.push_back(Triplet(i*3+k, it.col(), w*it.value()));
|
| 476 |
+
}
|
| 477 |
+
for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, tar_idx*3+k); it; ++it)
|
| 478 |
+
{
|
| 479 |
+
coeffs.push_back(Triplet(i*3+k, it.col(), -w*it.value()));
|
| 480 |
+
}
|
| 481 |
+
}
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
coeffs_fine.push_back(Triplet(i * 3, src_idx * 3, w));
|
| 485 |
+
coeffs_fine.push_back(Triplet(i * 3 + 1, src_idx * 3 + 1, w));
|
| 486 |
+
coeffs_fine.push_back(Triplet(i * 3 + 2, src_idx * 3 + 2, w));
|
| 487 |
+
coeffs_fine.push_back(Triplet(i * 3, tar_idx * 3, -w));
|
| 488 |
+
coeffs_fine.push_back(Triplet(i * 3 + 1, tar_idx * 3 + 1, -w));
|
| 489 |
+
coeffs_fine.push_back(Triplet(i * 3 + 2, tar_idx * 3 + 2, -w));
|
| 490 |
+
}
|
| 491 |
+
|
| 492 |
+
|
| 493 |
+
arap_coeff_.resize(src_mesh_->n_halfedges()*3, num_sample_nodes * 12);
|
| 494 |
+
arap_coeff_.setFromTriplets(coeffs.begin(), coeffs.end());
|
| 495 |
+
arap_coeff_mul_ = arap_coeff_.transpose() * arap_coeff_;
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
arap_coeff_fine_.resize(src_mesh_->n_halfedges() * 3, n_src_vertex_ * 3);
|
| 499 |
+
arap_coeff_fine_.setFromTriplets(coeffs_fine.begin(), coeffs_fine.end());
|
| 500 |
+
arap_coeff_mul_fine_ = arap_coeff_fine_.transpose() * arap_coeff_fine_;
|
| 501 |
+
}
|
| 502 |
+
else
|
| 503 |
+
{
|
| 504 |
+
int nn = src_knn_indices_.rows();
|
| 505 |
+
for (int i = 0; i < n_src_vertex_; i++)
|
| 506 |
+
{
|
| 507 |
+
arap_laplace_weights_[i] = 1.0 / nn;
|
| 508 |
+
}
|
| 509 |
+
|
| 510 |
+
std::vector<Triplet> coeffs;
|
| 511 |
+
std::vector<Triplet> coeffs_fine;
|
| 512 |
+
for(int src_idx = 0; src_idx < n_src_vertex_; src_idx++)
|
| 513 |
+
{
|
| 514 |
+
for(int j = 0; j < nn; j++)
|
| 515 |
+
{
|
| 516 |
+
int i = src_idx*nn+j;
|
| 517 |
+
int tar_idx = src_knn_indices_(j, src_idx);
|
| 518 |
+
Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
for (int k = 0; k < 3; k++)
|
| 522 |
+
{
|
| 523 |
+
for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, src_idx*3+k); it; ++it)
|
| 524 |
+
{
|
| 525 |
+
coeffs.push_back(Triplet(i*3+k, it.col(), w*it.value()));
|
| 526 |
+
}
|
| 527 |
+
for (RowMajorSparseMatrix::InnerIterator it(align_coeff_PV0_, tar_idx*3+k); it; ++it)
|
| 528 |
+
{
|
| 529 |
+
coeffs.push_back(Triplet(i*3+k, it.col(), -w*it.value()));
|
| 530 |
+
}
|
| 531 |
+
}
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
coeffs_fine.push_back(Triplet(i * 3, src_idx * 3, w));
|
| 535 |
+
coeffs_fine.push_back(Triplet(i * 3 + 1, src_idx * 3 + 1, w));
|
| 536 |
+
coeffs_fine.push_back(Triplet(i * 3 + 2, src_idx * 3 + 2, w));
|
| 537 |
+
coeffs_fine.push_back(Triplet(i * 3, tar_idx * 3, -w));
|
| 538 |
+
coeffs_fine.push_back(Triplet(i * 3 + 1, tar_idx * 3 + 1, -w));
|
| 539 |
+
coeffs_fine.push_back(Triplet(i * 3 + 2, tar_idx * 3 + 2, -w));
|
| 540 |
+
}
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
arap_coeff_.resize(n_src_vertex_*nn*3, num_sample_nodes * 12);
|
| 544 |
+
arap_coeff_.setFromTriplets(coeffs.begin(), coeffs.end());
|
| 545 |
+
arap_coeff_mul_ = arap_coeff_.transpose() * arap_coeff_;
|
| 546 |
+
|
| 547 |
+
|
| 548 |
+
arap_coeff_fine_.resize(n_src_vertex_*nn * 3, n_src_vertex_ * 3);
|
| 549 |
+
arap_coeff_fine_.setFromTriplets(coeffs_fine.begin(), coeffs_fine.end());
|
| 550 |
+
arap_coeff_mul_fine_ = arap_coeff_fine_.transpose() * arap_coeff_fine_;
|
| 551 |
+
}
|
| 552 |
+
}
|
| 553 |
+
|
| 554 |
+
|
| 555 |
+
void NonrigidSpareRegistration::CalcARAPRight()
|
| 556 |
+
{
|
| 557 |
+
if(pars_.use_geodesic_dist)
|
| 558 |
+
{
|
| 559 |
+
#pragma omp parallel for
|
| 560 |
+
for (int i = 0; i < src_mesh_->n_halfedges(); i++)
|
| 561 |
+
{
|
| 562 |
+
int src_idx = src_mesh_->from_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
|
| 563 |
+
int tar_idx = src_mesh_->to_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
|
| 564 |
+
|
| 565 |
+
Vector3 vij = local_rotations_.block(0, 3 * src_idx,3, 3) * (src_points_.col(src_idx) - src_points_.col(tar_idx));
|
| 566 |
+
|
| 567 |
+
Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
|
| 568 |
+
|
| 569 |
+
|
| 570 |
+
arap_right_[i * 3] = w*(vij[0] - nodes_P_[src_idx * 3] + nodes_P_[tar_idx * 3]);
|
| 571 |
+
arap_right_[i * 3 + 1] = w*(vij[1] - nodes_P_[src_idx * 3 + 1] + nodes_P_[tar_idx * 3 + 1]);
|
| 572 |
+
arap_right_[i * 3 + 2] = w*(vij[2] - nodes_P_[src_idx * 3 + 2] + nodes_P_[tar_idx * 3 + 2]);
|
| 573 |
+
}
|
| 574 |
+
}
|
| 575 |
+
else
|
| 576 |
+
{
|
| 577 |
+
int nn = src_knn_indices_.rows();
|
| 578 |
+
#pragma omp parallel for
|
| 579 |
+
for (int src_idx = 0; src_idx < n_src_vertex_; src_idx++)
|
| 580 |
+
{
|
| 581 |
+
for (int j = 0; j < nn; j++)
|
| 582 |
+
{
|
| 583 |
+
int i = src_idx*nn + j;
|
| 584 |
+
int tar_idx = src_knn_indices_(j, src_idx);
|
| 585 |
+
|
| 586 |
+
Vector3 vij = local_rotations_.block(0, 3 * src_idx,3, 3) * (src_points_.col(src_idx) - src_points_.col(tar_idx));
|
| 587 |
+
|
| 588 |
+
Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
|
| 589 |
+
|
| 590 |
+
|
| 591 |
+
arap_right_[i * 3] = w*(vij[0] - nodes_P_[src_idx * 3] + nodes_P_[tar_idx * 3]);
|
| 592 |
+
arap_right_[i * 3 + 1] = w*(vij[1] - nodes_P_[src_idx * 3 + 1] + nodes_P_[tar_idx * 3 + 1]);
|
| 593 |
+
arap_right_[i * 3 + 2] = w*(vij[2] - nodes_P_[src_idx * 3 + 2] + nodes_P_[tar_idx * 3 + 2]);
|
| 594 |
+
}
|
| 595 |
+
}
|
| 596 |
+
}
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
void NonrigidSpareRegistration::CalcARAPRightFine()
|
| 600 |
+
{
|
| 601 |
+
if(pars_.use_geodesic_dist)
|
| 602 |
+
{
|
| 603 |
+
#pragma omp parallel for
|
| 604 |
+
for (int i = 0; i < src_mesh_->n_halfedges(); i++)
|
| 605 |
+
{
|
| 606 |
+
int src_idx = src_mesh_->from_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
|
| 607 |
+
int tar_idx = src_mesh_->to_vertex_handle(src_mesh_->halfedge_handle(i)).idx();
|
| 608 |
+
|
| 609 |
+
Vector3 vij = local_rotations_.block(0, 3 * src_idx, 3, 3) * (src_points_.col(src_idx) - src_points_.col(tar_idx));
|
| 610 |
+
|
| 611 |
+
Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
|
| 612 |
+
|
| 613 |
+
arap_right_fine_[i * 3] = w*(vij[0]);
|
| 614 |
+
arap_right_fine_[i * 3 + 1] = w*(vij[1]);
|
| 615 |
+
arap_right_fine_[i * 3 + 2] = w*(vij[2]);
|
| 616 |
+
}
|
| 617 |
+
}
|
| 618 |
+
else
|
| 619 |
+
{
|
| 620 |
+
int nn = src_knn_indices_.rows();
|
| 621 |
+
#pragma omp parallel for
|
| 622 |
+
for(int src_idx = 0; src_idx < n_src_vertex_; src_idx++)
|
| 623 |
+
{
|
| 624 |
+
for(int j = 0; j < nn; j++)
|
| 625 |
+
{
|
| 626 |
+
int tar_idx = src_knn_indices_(j, src_idx);
|
| 627 |
+
int i = src_idx*nn+j;
|
| 628 |
+
|
| 629 |
+
Vector3 vij = local_rotations_.block(0, 3 * src_idx, 3, 3) * (src_points_.col(src_idx) - src_points_.col(tar_idx));
|
| 630 |
+
|
| 631 |
+
Scalar w = sqrtf(arap_laplace_weights_[src_idx]);
|
| 632 |
+
|
| 633 |
+
arap_right_fine_[i * 3] = w*(vij[0]);
|
| 634 |
+
arap_right_fine_[i * 3 + 1] = w*(vij[1]);
|
| 635 |
+
arap_right_fine_[i * 3 + 2] = w*(vij[2]);
|
| 636 |
+
}
|
| 637 |
+
}
|
| 638 |
+
}
|
| 639 |
+
}
|
| 640 |
+
|
| 641 |
+
|
| 642 |
+
void NonrigidSpareRegistration::InitRotations()
|
| 643 |
+
{
|
| 644 |
+
local_rotations_.resize(3, n_src_vertex_ * 3);
|
| 645 |
+
local_rotations_.setZero();
|
| 646 |
+
#pragma omp parallel for
|
| 647 |
+
for (int i = 0; i < n_src_vertex_; i++)
|
| 648 |
+
{
|
| 649 |
+
local_rotations_(0, i * 3) = 1;
|
| 650 |
+
local_rotations_(1, i * 3 + 1) = 1;
|
| 651 |
+
local_rotations_(2, i * 3 + 2) = 1;
|
| 652 |
+
}
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
+
|
| 656 |
+
void NonrigidSpareRegistration::CalcLocalRotations(bool isCoarseAlign)
|
| 657 |
+
{
|
| 658 |
+
|
| 659 |
+
#pragma omp parallel for
|
| 660 |
+
for (int i = 0; i < n_src_vertex_; i++)
|
| 661 |
+
{
|
| 662 |
+
Matrix33 sum;
|
| 663 |
+
sum.setZero();
|
| 664 |
+
|
| 665 |
+
int nn = 0;
|
| 666 |
+
if(pars_.use_geodesic_dist)
|
| 667 |
+
{
|
| 668 |
+
OpenMesh::VertexHandle vh = src_mesh_->vertex_handle(i);
|
| 669 |
+
for (auto vv = src_mesh_->vv_begin(vh); vv != src_mesh_->vv_end(vh); vv++)
|
| 670 |
+
{
|
| 671 |
+
int neighbor_idx = vv->idx();
|
| 672 |
+
Vector3 dv = src_points_.col(i) - src_points_.col(neighbor_idx);
|
| 673 |
+
Vector3 new_dv = deformed_points_.segment(3 * i, 3) - deformed_points_.segment(3 * neighbor_idx, 3);
|
| 674 |
+
sum += dv * new_dv.transpose();
|
| 675 |
+
nn++;
|
| 676 |
+
}
|
| 677 |
+
}
|
| 678 |
+
else
|
| 679 |
+
{
|
| 680 |
+
nn = src_knn_indices_.rows();
|
| 681 |
+
for(int j = 0; j < nn; j++)
|
| 682 |
+
{
|
| 683 |
+
int neighbor_idx = src_knn_indices_(j, i);
|
| 684 |
+
Vector3 dv = src_points_.col(i) - src_points_.col(neighbor_idx);
|
| 685 |
+
Vector3 new_dv = deformed_points_.segment(3 * i, 3) - deformed_points_.segment(3 * neighbor_idx, 3);
|
| 686 |
+
sum += dv * new_dv.transpose();
|
| 687 |
+
}
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
+
|
| 691 |
+
sum*= 1.0*optimize_w_arap/nn;
|
| 692 |
+
|
| 693 |
+
if(!isCoarseAlign)
|
| 694 |
+
{
|
| 695 |
+
int tar_idx = correspondence_pairs_[i].tar_idx;
|
| 696 |
+
Vector3 d = deformed_points_.segment(3 * i, 3) - tar_points_.col(tar_idx);
|
| 697 |
+
Scalar c = (target_normals_.col(tar_idx) + deformed_normals_.col(i)).dot(d);
|
| 698 |
+
Scalar d_norm2 = d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
|
| 699 |
+
Vector3 h = deformed_normals_.col(i) - c*d/d_norm2;
|
| 700 |
+
|
| 701 |
+
Scalar w = optimize_w_align*d_norm2*weight_d_[i];
|
| 702 |
+
sum += w * src_normals_.col(i) * h.transpose();
|
| 703 |
+
}
|
| 704 |
+
else if(vertex_sample_indices_[i] >= 0)
|
| 705 |
+
{
|
| 706 |
+
int tar_idx = correspondence_pairs_[vertex_sample_indices_[i]].tar_idx;
|
| 707 |
+
Vector3 d = deformed_points_.segment(3 * i, 3) - tar_points_.col(tar_idx);
|
| 708 |
+
Scalar c = (target_normals_.col(tar_idx) + deformed_normals_.col(i)).dot(d);
|
| 709 |
+
Scalar d_norm2 = d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
|
| 710 |
+
Vector3 h = deformed_normals_.col(i) - c*d/d_norm2;
|
| 711 |
+
|
| 712 |
+
Scalar w = optimize_w_align*d_norm2*weight_d_[vertex_sample_indices_[i]];
|
| 713 |
+
sum += w * src_normals_.col(i) * h.transpose();
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
|
| 717 |
+
Eigen::JacobiSVD<Matrix33> svd(sum, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
| 718 |
+
|
| 719 |
+
if (svd.matrixU().determinant()*svd.matrixV().determinant() < 0.0) {
|
| 720 |
+
Vector3 S = Vector3::Ones(); S(2) = -1.0;
|
| 721 |
+
sum = svd.matrixV()*S.asDiagonal()*svd.matrixU().transpose();
|
| 722 |
+
}
|
| 723 |
+
else {
|
| 724 |
+
sum = svd.matrixV()*svd.matrixU().transpose();
|
| 725 |
+
}
|
| 726 |
+
for (int s = 0; s < 3; s++)
|
| 727 |
+
{
|
| 728 |
+
for (int t = 0; t < 3; t++)
|
| 729 |
+
{
|
| 730 |
+
local_rotations_(s, 3 * i + t) = sum(s, t);
|
| 731 |
+
}
|
| 732 |
+
}
|
| 733 |
+
}
|
| 734 |
+
}
|
| 735 |
+
|
| 736 |
+
void NonrigidSpareRegistration::CalcDeformedNormals()
|
| 737 |
+
{
|
| 738 |
+
#pragma omp parallel for
|
| 739 |
+
for (int i = 0; i < n_src_vertex_; i++)
|
| 740 |
+
{
|
| 741 |
+
deformed_normals_.col(i) = local_rotations_.block(0, i * 3, 3, 3) * src_normals_.col(i);
|
| 742 |
+
}
|
| 743 |
+
}
|
| 744 |
+
|
| 745 |
+
void NonrigidSpareRegistration::CalcNormalsSum()
|
| 746 |
+
{
|
| 747 |
+
#pragma omp parallel for
|
| 748 |
+
for(int i = 0; i < correspondence_pairs_.size(); i++)
|
| 749 |
+
{
|
| 750 |
+
int sidx = correspondence_pairs_[i].src_idx;
|
| 751 |
+
int tidx = correspondence_pairs_[i].tar_idx;
|
| 752 |
+
int j = 0;
|
| 753 |
+
for (RowMajorSparseMatrix::InnerIterator it(normals_sum_, i); it; ++it)
|
| 754 |
+
{
|
| 755 |
+
it.valueRef() = deformed_normals_(j, sidx) + target_normals_(j, tidx);
|
| 756 |
+
j++;
|
| 757 |
+
}
|
| 758 |
+
}
|
| 759 |
+
}
|
| 760 |
+
|
| 761 |
+
|
| 762 |
+
void NonrigidSpareRegistration::InitNormalsSum()
|
| 763 |
+
{
|
| 764 |
+
std::vector<Triplet> coeffs(3 * correspondence_pairs_.size());
|
| 765 |
+
normals_sum_.resize(correspondence_pairs_.size(), 3*n_src_vertex_);
|
| 766 |
+
normals_sum_.setZero();
|
| 767 |
+
|
| 768 |
+
#pragma omp parallel for
|
| 769 |
+
for(int i = 0; i < correspondence_pairs_.size(); i++)
|
| 770 |
+
{
|
| 771 |
+
int sidx = correspondence_pairs_[i].src_idx;
|
| 772 |
+
int tidx = correspondence_pairs_[i].tar_idx;
|
| 773 |
+
coeffs[i * 3] = Triplet(i, 3 * sidx, deformed_normals_(0, sidx) + target_normals_(0, tidx));
|
| 774 |
+
coeffs[i * 3 + 1] = Triplet(i, 3 * sidx + 1, deformed_normals_(1, sidx) + target_normals_(1, tidx));
|
| 775 |
+
coeffs[i * 3 + 2] = Triplet(i, 3 * sidx + 2, deformed_normals_(2, sidx) + target_normals_(2, tidx));
|
| 776 |
+
}
|
| 777 |
+
normals_sum_.setFromTriplets(coeffs.begin(), coeffs.end());
|
| 778 |
+
}
|
| 779 |
+
|
| 780 |
+
void NonrigidSpareRegistration::PointwiseFineReg(Scalar nu1)
|
| 781 |
+
{
|
| 782 |
+
|
| 783 |
+
Scalar energy=-1., align_err=-1., arap_err=-1.;
|
| 784 |
+
|
| 785 |
+
VectorX prevV = VectorX::Zero(n_src_vertex_ * 3);
|
| 786 |
+
|
| 787 |
+
bool run_once = true;
|
| 788 |
+
|
| 789 |
+
|
| 790 |
+
// Smooth term parameters
|
| 791 |
+
w_align = optimize_w_align;
|
| 792 |
+
w_smo = optimize_w_smo;
|
| 793 |
+
w_align = optimize_w_align *(2.0*nu1*nu1);
|
| 794 |
+
|
| 795 |
+
|
| 796 |
+
Scalar gt_err = -1;
|
| 797 |
+
|
| 798 |
+
double construct_mat_time = 0.0;
|
| 799 |
+
double solve_eq_time = 0.0;
|
| 800 |
+
|
| 801 |
+
|
| 802 |
+
int out_iter = 0;
|
| 803 |
+
while (out_iter < pars_.max_outer_iters)
|
| 804 |
+
{
|
| 805 |
+
// Find clost points
|
| 806 |
+
FindClosestPoints(target_tree_,deformed_points_,correspondence_pairs_);
|
| 807 |
+
// according correspondence_pairs to update corres_U0_;
|
| 808 |
+
corres_U0_.setZero();
|
| 809 |
+
weight_d_.resize(n_src_vertex_);
|
| 810 |
+
|
| 811 |
+
|
| 812 |
+
for (size_t i = 0; i < correspondence_pairs_.size(); i++)
|
| 813 |
+
{
|
| 814 |
+
corres_U0_.segment(i * 3, 3) = correspondence_pairs_[i].position;
|
| 815 |
+
weight_d_[i] = correspondence_pairs_[i].min_dist2;
|
| 816 |
+
int tar_idx = correspondence_pairs_[i].tar_idx;
|
| 817 |
+
if(deformed_normals_.col(i).dot(target_normals_.col(tar_idx))<0)
|
| 818 |
+
weight_d_[i] = -1;
|
| 819 |
+
}
|
| 820 |
+
|
| 821 |
+
// update weight
|
| 822 |
+
|
| 823 |
+
welsch_weight(weight_d_, nu1);
|
| 824 |
+
|
| 825 |
+
if (run_once == true && pars_.use_landmark == true)
|
| 826 |
+
{
|
| 827 |
+
weight_d_.setOnes();
|
| 828 |
+
}
|
| 829 |
+
|
| 830 |
+
// construct matrix A0 and pre-decompose
|
| 831 |
+
if(out_iter==0)
|
| 832 |
+
InitNormalsSum();
|
| 833 |
+
else
|
| 834 |
+
CalcNormalsSum();
|
| 835 |
+
|
| 836 |
+
RowMajorSparseMatrix normals_sum_mul = normals_sum_.transpose() * weight_d_.asDiagonal()* normals_sum_;
|
| 837 |
+
mat_A0_ = optimize_w_align * normals_sum_mul
|
| 838 |
+
+ optimize_w_arap * arap_coeff_mul_fine_;
|
| 839 |
+
|
| 840 |
+
CalcARAPRightFine();
|
| 841 |
+
|
| 842 |
+
vec_b_ = optimize_w_align * normals_sum_mul * corres_U0_
|
| 843 |
+
+ optimize_w_arap * arap_coeff_fine_.transpose() * arap_right_fine_;
|
| 844 |
+
|
| 845 |
+
|
| 846 |
+
if (run_once)
|
| 847 |
+
{
|
| 848 |
+
solver_.analyzePattern(mat_A0_);
|
| 849 |
+
run_once = false;
|
| 850 |
+
}
|
| 851 |
+
solver_.factorize(mat_A0_);
|
| 852 |
+
deformed_points_ = solver_.solve(vec_b_);
|
| 853 |
+
CalcLocalRotations(false);
|
| 854 |
+
CalcDeformedNormals();
|
| 855 |
+
if((deformed_points_ - prevV).norm()/sqrtf(n_src_vertex_) < pars_.stop_fine)
|
| 856 |
+
{
|
| 857 |
+
break;
|
| 858 |
+
}
|
| 859 |
+
prevV = deformed_points_;
|
| 860 |
+
out_iter++;
|
| 861 |
+
}
|
| 862 |
+
}
|
| 863 |
+
|
| 864 |
+
void NonrigidSpareRegistration::GraphCoarseReg(Scalar nu1)
|
| 865 |
+
{
|
| 866 |
+
Scalar energy=0., align_err=0., reg_err=0., rot_err=0., arap_err=0.;
|
| 867 |
+
VectorX prevV = VectorX::Zero(n_src_vertex_ * 3);
|
| 868 |
+
|
| 869 |
+
// welsch_sweight
|
| 870 |
+
bool run_once = true;
|
| 871 |
+
|
| 872 |
+
|
| 873 |
+
w_align = optimize_w_align;
|
| 874 |
+
w_smo = optimize_w_smo;
|
| 875 |
+
w_align = optimize_w_align *(2.0*nu1*nu1);
|
| 876 |
+
|
| 877 |
+
Scalar gt_err;
|
| 878 |
+
|
| 879 |
+
VectorX prev_X = X_;
|
| 880 |
+
|
| 881 |
+
RowMajorSparseMatrix A_fixed_coeff = optimize_w_smo * reg_coeff_B_.transpose() * reg_cwise_weights_.asDiagonal() * reg_coeff_B_ + optimize_w_rot * rigid_coeff_L_ + optimize_w_arap * arap_coeff_mul_;
|
| 882 |
+
int out_iter = 0;
|
| 883 |
+
while (out_iter < pars_.max_outer_iters)
|
| 884 |
+
{
|
| 885 |
+
|
| 886 |
+
|
| 887 |
+
correspondence_pairs_.clear();
|
| 888 |
+
FindClosestPoints(target_tree_,correspondence_pairs_, deformed_points_, sampling_indices_);
|
| 889 |
+
corres_U0_.setZero();
|
| 890 |
+
weight_d_.resize(correspondence_pairs_.size());
|
| 891 |
+
weight_d_.setConstant(-1);
|
| 892 |
+
|
| 893 |
+
#pragma omp parallel for
|
| 894 |
+
for (size_t i = 0; i < correspondence_pairs_.size(); i++)
|
| 895 |
+
{
|
| 896 |
+
corres_U0_.segment(correspondence_pairs_[i].src_idx * 3, 3) = correspondence_pairs_[i].position;
|
| 897 |
+
weight_d_[i] = correspondence_pairs_[i].min_dist2;
|
| 898 |
+
if(deformed_normals_.col(correspondence_pairs_[i].src_idx).dot(target_normals_.col(correspondence_pairs_[i].tar_idx))<0)
|
| 899 |
+
weight_d_[i] = -1;
|
| 900 |
+
}
|
| 901 |
+
|
| 902 |
+
// update weight
|
| 903 |
+
|
| 904 |
+
welsch_weight(weight_d_, nu1);
|
| 905 |
+
|
| 906 |
+
if (run_once == true && pars_.use_landmark == true)
|
| 907 |
+
{
|
| 908 |
+
weight_d_.setOnes();
|
| 909 |
+
}
|
| 910 |
+
|
| 911 |
+
if(out_iter==0)
|
| 912 |
+
InitNormalsSum();
|
| 913 |
+
else
|
| 914 |
+
CalcNormalsSum();
|
| 915 |
+
|
| 916 |
+
diff_UP_ = (corres_U0_ - nodes_P_);
|
| 917 |
+
|
| 918 |
+
RowMajorSparseMatrix weight_NPV = normals_sum_ * align_coeff_PV0_;
|
| 919 |
+
|
| 920 |
+
mat_A0_ = optimize_w_align * weight_NPV.transpose() * weight_d_.asDiagonal() * weight_NPV + A_fixed_coeff;
|
| 921 |
+
|
| 922 |
+
CalcARAPRight();
|
| 923 |
+
vec_b_ = optimize_w_align * weight_NPV.transpose() * weight_d_.asDiagonal() * normals_sum_ * diff_UP_ + optimize_w_smo * reg_coeff_B_.transpose() * reg_cwise_weights_.asDiagonal() * reg_right_D_ + optimize_w_rot * rigid_coeff_J_ * nodes_R_ + optimize_w_arap * arap_coeff_.transpose() * arap_right_;
|
| 924 |
+
|
| 925 |
+
|
| 926 |
+
if (run_once)
|
| 927 |
+
{
|
| 928 |
+
solver_.analyzePattern(mat_A0_);
|
| 929 |
+
run_once = false;
|
| 930 |
+
}
|
| 931 |
+
solver_.factorize(mat_A0_);
|
| 932 |
+
X_ = solver_.solve(vec_b_);
|
| 933 |
+
deformed_points_ = align_coeff_PV0_ * X_ + nodes_P_;
|
| 934 |
+
CalcLocalRotations(true);
|
| 935 |
+
CalcNodeRotations();
|
| 936 |
+
CalcDeformedNormals();
|
| 937 |
+
|
| 938 |
+
if (n_src_vertex_ == n_tar_vertex_)
|
| 939 |
+
gt_err = (deformed_points_ - Eigen::Map<VectorX>(tar_points_.data(), 3 * n_src_vertex_)).squaredNorm();
|
| 940 |
+
|
| 941 |
+
|
| 942 |
+
if((deformed_points_ - prevV).norm()/sqrtf(n_src_vertex_) < pars_.stop_coarse)
|
| 943 |
+
{
|
| 944 |
+
break;
|
| 945 |
+
}
|
| 946 |
+
prevV = deformed_points_;
|
| 947 |
+
out_iter++;
|
| 948 |
+
}
|
| 949 |
+
|
| 950 |
+
}
|
| 951 |
+
|
| 952 |
+
void NonrigidSpareRegistration::Paras_init(
|
| 953 |
+
int iters,
|
| 954 |
+
double stopcoarse,
|
| 955 |
+
double stopfine,
|
| 956 |
+
bool uselandmark,
|
| 957 |
+
std::vector<int> src,
|
| 958 |
+
std::vector<int> tar)
|
| 959 |
+
{
|
| 960 |
+
paras.max_outer_iters = iters;
|
| 961 |
+
paras.stop_coarse = stopcoarse;
|
| 962 |
+
paras.stop_fine = stopfine;
|
| 963 |
+
|
| 964 |
+
if (uselandmark && !src.empty() && src.size() == tar.size()) {
|
| 965 |
+
paras.use_landmark = true;
|
| 966 |
+
paras.landmark_src = src;
|
| 967 |
+
paras.landmark_tar = tar;
|
| 968 |
+
} else {
|
| 969 |
+
paras.use_landmark = false;
|
| 970 |
+
paras.landmark_src.clear();
|
| 971 |
+
paras.landmark_tar.clear();
|
| 972 |
+
}
|
| 973 |
+
}
|
| 974 |
+
|
| 975 |
+
|
| 976 |
+
void NonrigidSpareRegistration::Register()
|
| 977 |
+
{
|
| 978 |
+
if(src_mesh.n_vertices()==0 || tar_mesh.n_vertices()==0)
|
| 979 |
+
exit(0);
|
| 980 |
+
|
| 981 |
+
if(src_mesh.n_vertices() != tar_mesh.n_vertices())
|
| 982 |
+
paras.calc_gt_err = false;
|
| 983 |
+
|
| 984 |
+
if(src_mesh.n_faces()==0)
|
| 985 |
+
paras.use_geodesic_dist = false;
|
| 986 |
+
|
| 987 |
+
if(paras.use_landmark)
|
| 988 |
+
read_landmark(landmark_file.c_str(), paras.landmark_src, paras.landmark_tar);
|
| 989 |
+
if(normalize)// The default value of `normalize` is `true`.
|
| 990 |
+
scale = mesh_scaling(src_mesh, tar_mesh);
|
| 991 |
+
pars_ = paras;
|
| 992 |
+
Timer time;
|
| 993 |
+
std::cout << "registration to initial... (mesh scale: " << scale << ")" << std::endl;
|
| 994 |
+
Timer::EventID time1 = time.get_time();
|
| 995 |
+
Init_data();
|
| 996 |
+
Initialize();
|
| 997 |
+
Timer::EventID time2 = time.get_time();
|
| 998 |
+
std::cout << "non-rigid registration... (graph node number: " << pars_.num_sample_nodes << ")" << std::endl;
|
| 999 |
+
DoNonRigid();
|
| 1000 |
+
Timer::EventID time3 = time.get_time();
|
| 1001 |
+
std::cout << "Registration done!\ninitialize time : "
|
| 1002 |
+
<< time.elapsed_time(time1, time2) << " s \tnon-rigid reg running time = " << time.elapsed_time(time2, time3) << " s" << std::endl;
|
| 1003 |
+
return;
|
| 1004 |
+
}
|
| 1005 |
+
|
| 1006 |
+
void NonrigidSpareRegistration::Reg(const std::string& file_target,
|
| 1007 |
+
const std::string& file_source,
|
| 1008 |
+
const std::string& out_path )
|
| 1009 |
+
{
|
| 1010 |
+
Read_data(file_target, file_source);
|
| 1011 |
+
Register();
|
| 1012 |
+
Output_data(out_path, "spare");
|
| 1013 |
+
}
|
cpp/core/nonrigid_spare_registration.h
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef NONRIGIDREG_SPARE_H
|
| 2 |
+
#define NONRIGIDREG_SPARE_H
|
| 3 |
+
|
| 4 |
+
#include "nodeSampler.h"
|
| 5 |
+
#include "registration.h"
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
#ifdef USE_PARDISO
|
| 11 |
+
#include <Eigen/PardisoSupport>
|
| 12 |
+
#endif
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class NonrigidSpareRegistration : public Registration {
|
| 16 |
+
public:
|
| 17 |
+
NonrigidSpareRegistration();
|
| 18 |
+
~NonrigidSpareRegistration();
|
| 19 |
+
|
| 20 |
+
// Adjusted parameters
|
| 21 |
+
spare::Parameters pars_;
|
| 22 |
+
|
| 23 |
+
// Non-rigid energy function parameters
|
| 24 |
+
VectorX weight_d_; // robust weight for alignment α_i
|
| 25 |
+
RowMajorSparseMatrix mat_A0_; // symmetric coefficient matrix for linear equations
|
| 26 |
+
VectorX vec_b_; // right-hand side for linear equations
|
| 27 |
+
|
| 28 |
+
// Welsch parameters
|
| 29 |
+
Scalar nu;
|
| 30 |
+
|
| 31 |
+
#ifdef USE_PARDISO
|
| 32 |
+
Eigen::PardisoLDLT<RowMajorSparseMatrix, Eigen::Lower> solver_;
|
| 33 |
+
#else
|
| 34 |
+
Eigen::SimplicialLDLT<RowMajorSparseMatrix, Eigen::Lower> solver_;
|
| 35 |
+
#endif
|
| 36 |
+
|
| 37 |
+
// Sampling parameters
|
| 38 |
+
int num_sample_nodes; // (r,) number of sample nodes
|
| 39 |
+
int num_graph_edges;
|
| 40 |
+
int num_edges;
|
| 41 |
+
|
| 42 |
+
// Node storage structure
|
| 43 |
+
svr::nodeSampler src_sample_nodes;
|
| 44 |
+
|
| 45 |
+
// Variables
|
| 46 |
+
VectorX X_; // (12r,) transformations of sample nodes
|
| 47 |
+
|
| 48 |
+
// Alignment term
|
| 49 |
+
VectorX nodes_P_; // (3n,) all sample nodes' coordinates
|
| 50 |
+
RowMajorSparseMatrix align_coeff_PV0_; // (3n, 12r) coefficient matrix F
|
| 51 |
+
|
| 52 |
+
// Smooth term
|
| 53 |
+
RowMajorSparseMatrix reg_coeff_B_; // (6|E_G|, 12r) smoothness between nodes
|
| 54 |
+
VectorX reg_right_D_; // (6|E_G|,) coordinate differences between xi and xj
|
| 55 |
+
VectorX reg_cwise_weights_; // (6|E_G|,) smooth weights
|
| 56 |
+
|
| 57 |
+
// landmark term
|
| 58 |
+
RowMajorSparseMatrix landmark_mul_; // (12r, 12r)
|
| 59 |
+
VectorX tar_landmarks_; // (3, l)
|
| 60 |
+
VectorX landmark_right_; // (12r,)
|
| 61 |
+
RowMajorSparseMatrix landmark_mul_fine_; // (3n, 3n)
|
| 62 |
+
VectorX landmark_right_fine_; // (3n,)
|
| 63 |
+
// Rotation matrix term
|
| 64 |
+
VectorX nodes_R_; // (9r,) proj(A)
|
| 65 |
+
RowMajorSparseMatrix rigid_coeff_L_; // (12r, 12r) H
|
| 66 |
+
RowMajorSparseMatrix rigid_coeff_J_; // (9r, 12r) Y
|
| 67 |
+
VectorX diff_UP_; // auxiliary matrix
|
| 68 |
+
|
| 69 |
+
// ARAP term (coarse)
|
| 70 |
+
VectorX arap_laplace_weights_; // (6E,)
|
| 71 |
+
Matrix3X local_rotations_; // (3n, 3) R
|
| 72 |
+
RowMajorSparseMatrix arap_coeff_; // (6E, 12r) B
|
| 73 |
+
RowMajorSparseMatrix arap_coeff_mul_; // (12r, 12r) BᵀB
|
| 74 |
+
VectorX arap_right_; // (6E,) L
|
| 75 |
+
|
| 76 |
+
// ARAP term (fine)
|
| 77 |
+
RowMajorSparseMatrix arap_coeff_fine_; // (6E, 3n) B
|
| 78 |
+
RowMajorSparseMatrix arap_coeff_mul_fine_;// (3n, 3n) BᵀB
|
| 79 |
+
VectorX arap_right_fine_; // (6E,) Y
|
| 80 |
+
|
| 81 |
+
// Point clouds
|
| 82 |
+
RowMajorSparseMatrix normals_sum_; // (n, 3n) N for alignment term
|
| 83 |
+
|
| 84 |
+
// Sampling points & vertices relation
|
| 85 |
+
std::vector<size_t> sampling_indices_;
|
| 86 |
+
std::vector<int> vertex_sample_indices_;
|
| 87 |
+
std::vector<int> vertex_landmark_indices_;
|
| 88 |
+
// KNN-neighbor indices for source points (if no faces)
|
| 89 |
+
Eigen::MatrixXi src_knn_indices_;
|
| 90 |
+
|
| 91 |
+
int align_sampling_num_ = 3000;
|
| 92 |
+
|
| 93 |
+
// Weights of terms during optimization
|
| 94 |
+
Scalar w_align;
|
| 95 |
+
Scalar w_smo;
|
| 96 |
+
Scalar optimize_w_align;
|
| 97 |
+
Scalar optimize_w_smo;
|
| 98 |
+
Scalar optimize_w_rot;
|
| 99 |
+
Scalar optimize_w_arap;
|
| 100 |
+
Scalar optimize_w_landmark;
|
| 101 |
+
// Initialization & main steps
|
| 102 |
+
void Initialize();
|
| 103 |
+
void InitFromInput(Mesh& src_mesh, Mesh& tar_mesh, spare::Parameters& paras);
|
| 104 |
+
virtual Scalar DoNonRigid();
|
| 105 |
+
|
| 106 |
+
// Welsch weighting
|
| 107 |
+
//void welsch_weight(VectorX& r, Scalar p);
|
| 108 |
+
void InitWelschParam();
|
| 109 |
+
void InitwithLandmark();
|
| 110 |
+
|
| 111 |
+
// Rotation calculations
|
| 112 |
+
void CalcNodeRotations();
|
| 113 |
+
void InitRotations();
|
| 114 |
+
void CalcLocalRotations(bool isCoarseAlign);
|
| 115 |
+
|
| 116 |
+
// ARAP calculations
|
| 117 |
+
void FullInARAPCoeff();
|
| 118 |
+
void CalcARAPRight();
|
| 119 |
+
void CalcARAPRightFine();
|
| 120 |
+
// Normal calculations
|
| 121 |
+
void CalcDeformedNormals();
|
| 122 |
+
void InitNormalsSum();
|
| 123 |
+
void CalcNormalsSum();
|
| 124 |
+
void CalcLandmarkCoeff();
|
| 125 |
+
// Optimization steps
|
| 126 |
+
void PointwiseFineReg(Scalar nu1);
|
| 127 |
+
void GraphCoarseReg(Scalar nu1);
|
| 128 |
+
void Paras_init(int iters = 30 ,double stopcoarse=1e-3,double stopfine=1e-4,bool uselandmark = false,std::vector<int> src=std::vector<int>(),std::vector<int> tar=std::vector<int>());
|
| 129 |
+
|
| 130 |
+
void Register()override;
|
| 131 |
+
void Reg(const std::string& file_target,
|
| 132 |
+
const std::string& file_source,
|
| 133 |
+
const std::string& out_path)override;
|
| 134 |
+
std::string landmark_file;
|
| 135 |
+
spare::Parameters paras;
|
| 136 |
+
bool normalize = true;
|
| 137 |
+
|
| 138 |
+
};
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
#endif
|
cpp/core/registration.cpp
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "registration.h"
|
| 2 |
+
#include "io.h"
|
| 3 |
+
#include "tools.h"
|
| 4 |
+
#include <median.h>
|
| 5 |
+
|
| 6 |
+
void Registration::FindClosestPoints(VPairs & corres)
|
| 7 |
+
{
|
| 8 |
+
corres.resize(n_src_vertex_);
|
| 9 |
+
|
| 10 |
+
#pragma omp parallel for
|
| 11 |
+
for (int i = 0; i < n_src_vertex_; i++)
|
| 12 |
+
{
|
| 13 |
+
Scalar mini_dist2;
|
| 14 |
+
int idx = target_tree_->closest(src_mesh_->point(src_mesh_->vertex_handle(i)).data(), mini_dist2);
|
| 15 |
+
Closest c;
|
| 16 |
+
c.src_idx = i;
|
| 17 |
+
c.position = tar_points_.col(idx);
|
| 18 |
+
c.normal = Vec2Eigen(tar_mesh_->normal(tar_mesh_->vertex_handle(idx)));
|
| 19 |
+
c.min_dist2 = mini_dist2;
|
| 20 |
+
c.tar_idx = idx;
|
| 21 |
+
corres[i] = c;
|
| 22 |
+
}
|
| 23 |
+
}
|
| 24 |
+
void Registration::FindClosestPoints(KDtree* target_tree_tem,VectorX & deformed_v,VPairs & corres)
|
| 25 |
+
{
|
| 26 |
+
corres.resize(n_src_vertex_);
|
| 27 |
+
#pragma omp parallel for
|
| 28 |
+
for (int i = 0; i < n_src_vertex_; i++)
|
| 29 |
+
{
|
| 30 |
+
Scalar mini_dist2;
|
| 31 |
+
int idx = target_tree_tem->closest(deformed_v.data() + 3*i, mini_dist2);
|
| 32 |
+
Closest c;
|
| 33 |
+
c.src_idx = i;
|
| 34 |
+
c.position = tar_points_.col(idx);
|
| 35 |
+
c.min_dist2 = mini_dist2;
|
| 36 |
+
c.tar_idx = idx;
|
| 37 |
+
corres[i] = c;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
}
|
| 41 |
+
void Registration::FindClosestPoints(KDtree* target_tree_tem,VPairs & corres, VectorX & deformed_v, std::vector<size_t>& sample_indices)
|
| 42 |
+
{
|
| 43 |
+
corres.resize(sample_indices.size());
|
| 44 |
+
#pragma omp parallel for
|
| 45 |
+
for(int i = 0; i < sample_indices.size(); i++)
|
| 46 |
+
{
|
| 47 |
+
int sidx = sample_indices[i];
|
| 48 |
+
Scalar mini_dist2;
|
| 49 |
+
int tidx = target_tree_tem->closest(deformed_v.data() + 3*sidx, mini_dist2);
|
| 50 |
+
Closest c;
|
| 51 |
+
c.src_idx = sidx;
|
| 52 |
+
c.position = tar_points_.col(tidx);
|
| 53 |
+
c.min_dist2 = mini_dist2;
|
| 54 |
+
c.tar_idx = tidx;
|
| 55 |
+
corres[i] = c;
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
double Registration::FindKnearestMed(const KDtree& kdtree,
|
| 59 |
+
const Matrix3X& X, int nk)
|
| 60 |
+
{
|
| 61 |
+
Eigen::VectorXd X_nearest(X.cols());
|
| 62 |
+
#pragma omp parallel for
|
| 63 |
+
for(int i = 0; i<X.cols(); i++)
|
| 64 |
+
{
|
| 65 |
+
int* id = new int[nk];
|
| 66 |
+
double *dist = new double[nk];
|
| 67 |
+
kdtree.query(X.col(i).data(), nk, id, dist);
|
| 68 |
+
Eigen::VectorXd k_dist = Eigen::Map<Eigen::VectorXd>(dist, nk);
|
| 69 |
+
igl::median(k_dist.tail(nk-1), X_nearest[i]);
|
| 70 |
+
delete[]id;
|
| 71 |
+
delete[]dist;
|
| 72 |
+
}
|
| 73 |
+
double med;
|
| 74 |
+
igl::median(X_nearest, med);
|
| 75 |
+
return sqrt(med);
|
| 76 |
+
}
|
| 77 |
+
void Registration::LandMarkCorres(VPairs & corres)
|
| 78 |
+
{
|
| 79 |
+
corres.clear();
|
| 80 |
+
if (pars_.landmark_src.size() != pars_.landmark_tar.size())
|
| 81 |
+
{
|
| 82 |
+
std::cout << "Error: landmark data wrong!!" << std::endl;
|
| 83 |
+
}
|
| 84 |
+
n_landmark_nodes_ = pars_.landmark_tar.size();
|
| 85 |
+
for (int i = 0; i < n_landmark_nodes_; i++)
|
| 86 |
+
{
|
| 87 |
+
Closest c;
|
| 88 |
+
c.src_idx = pars_.landmark_src[i];
|
| 89 |
+
OpenMesh::VertexHandle vh = tar_mesh_->vertex_handle(pars_.landmark_tar[i]);
|
| 90 |
+
|
| 91 |
+
if (c.src_idx > n_src_vertex_ || c.src_idx < 0)
|
| 92 |
+
std::cout << "Error: source index in Landmark is out of range!" << std::endl;
|
| 93 |
+
if (vh.idx() < 0)
|
| 94 |
+
std::cout << "Error: target index in Landmark is out of range!" << std::endl;
|
| 95 |
+
|
| 96 |
+
c.position = Vec2Eigen(tar_mesh_->point(vh));
|
| 97 |
+
c.normal = Vec2Eigen(tar_mesh_->normal(vh));
|
| 98 |
+
corres.push_back(c);
|
| 99 |
+
}
|
| 100 |
+
std::cout << " use landmark and landmark is ... " << pars_.landmark_src.size() << std::endl;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
bool Registration::read_landmark(const char* filename, std::vector<int>& landmark_src, std::vector<int>& landmark_tar)
|
| 105 |
+
{
|
| 106 |
+
std::ifstream in(filename);
|
| 107 |
+
std::cout << "filename = " << filename << std::endl;
|
| 108 |
+
if (!in)
|
| 109 |
+
{
|
| 110 |
+
std::cout << "Can't open the landmark file!!" << std::endl;
|
| 111 |
+
return false;
|
| 112 |
+
}
|
| 113 |
+
int x, y;
|
| 114 |
+
landmark_src.clear();
|
| 115 |
+
landmark_tar.clear();
|
| 116 |
+
while (!in.eof())
|
| 117 |
+
{
|
| 118 |
+
if (in >> x >> y) {
|
| 119 |
+
landmark_src.push_back(x);
|
| 120 |
+
landmark_tar.push_back(y);
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
+
in.close();
|
| 124 |
+
std::cout << "landmark_src = " << landmark_src.size() << " tar = " << landmark_tar.size() << std::endl;
|
| 125 |
+
return true;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
void Registration::InitCorrespondence(VPairs & corres)
|
| 130 |
+
{
|
| 131 |
+
if(pars_.use_landmark)
|
| 132 |
+
{
|
| 133 |
+
corres.clear();
|
| 134 |
+
for(size_t i = 0; i < pars_.landmark_src.size(); i++)
|
| 135 |
+
{
|
| 136 |
+
Closest c;
|
| 137 |
+
c.src_idx = pars_.landmark_src[i];
|
| 138 |
+
c.tar_idx = pars_.landmark_tar[i];
|
| 139 |
+
c.position = tar_points_.col(c.tar_idx);
|
| 140 |
+
c.normal = Vec2Eigen(tar_mesh_->normal(tar_mesh_->vertex_handle(c.tar_idx)));
|
| 141 |
+
corres.push_back(c);
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
else
|
| 145 |
+
{
|
| 146 |
+
FindClosestPoints(corres);
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
void Registration::Init_data()
|
| 150 |
+
{
|
| 151 |
+
src_mesh_ = new Mesh;
|
| 152 |
+
tar_mesh_ = new Mesh;
|
| 153 |
+
src_mesh_ = &src_mesh;
|
| 154 |
+
tar_mesh_ = &tar_mesh;
|
| 155 |
+
deformed_mesh =src_mesh;
|
| 156 |
+
deformed_mesh_=&deformed_mesh;
|
| 157 |
+
n_src_vertex_ = src_mesh_->n_vertices();
|
| 158 |
+
n_tar_vertex_ = tar_mesh_->n_vertices();
|
| 159 |
+
tar_points_.resize(3, n_tar_vertex_);
|
| 160 |
+
target_normals_.resize(3, n_tar_vertex_);
|
| 161 |
+
for (int i = 0; i < n_tar_vertex_; i++)
|
| 162 |
+
{
|
| 163 |
+
auto vh = tar_mesh_->vertex_handle(i);
|
| 164 |
+
|
| 165 |
+
// 顶点坐标
|
| 166 |
+
tar_points_(0, i) = tar_mesh_->point(vh)[0];
|
| 167 |
+
tar_points_(1, i) = tar_mesh_->point(vh)[1];
|
| 168 |
+
tar_points_(2, i) = tar_mesh_->point(vh)[2];
|
| 169 |
+
|
| 170 |
+
// 顶点法向量
|
| 171 |
+
target_normals_(0, i) = tar_mesh_->normal(vh)[0];
|
| 172 |
+
target_normals_(1, i) = tar_mesh_->normal(vh)[1];
|
| 173 |
+
target_normals_(2, i) = tar_mesh_->normal(vh)[2];
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
// construct kd Tree
|
| 177 |
+
target_tree_ = new KDtree(tar_points_);
|
| 178 |
+
src_points_.resize(3, n_src_vertex_);
|
| 179 |
+
src_normals_.resize(3, n_src_vertex_);
|
| 180 |
+
corres_U0_.resize(3* n_src_vertex_);
|
| 181 |
+
#pragma omp parallel for
|
| 182 |
+
for (int i = 0; i < n_src_vertex_; i++)
|
| 183 |
+
{
|
| 184 |
+
Vec3 p = src_mesh_->point(src_mesh_->vertex_handle(i));
|
| 185 |
+
src_points_(0, i) = p[0];
|
| 186 |
+
src_points_(1, i) = p[1];
|
| 187 |
+
src_points_(2, i) = p[2];
|
| 188 |
+
Vec3 n = src_mesh_->normal(src_mesh_->vertex_handle(i));
|
| 189 |
+
src_normals_(0, i) = n[0];
|
| 190 |
+
src_normals_(1, i) = n[1];
|
| 191 |
+
src_normals_(2, i) = n[2];
|
| 192 |
+
}
|
| 193 |
+
deformed_normals_ = src_normals_;
|
| 194 |
+
deformed_points_ = Eigen::Map<VectorX>(src_points_.data(), 3 * n_src_vertex_);
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
void Registration::Read_data(const std::string& file_target,
|
| 198 |
+
const std::string& file_source)
|
| 199 |
+
{
|
| 200 |
+
read_by_openmesh(file_source, src_mesh);
|
| 201 |
+
read_by_openmesh(file_target, tar_mesh);
|
| 202 |
+
return;
|
| 203 |
+
}
|
| 204 |
+
void Registration::Read_data(const Matrix3X &target_p,const Matrix3X &source_p,const Matrix3X &target_n,const Matrix3X &source_n)
|
| 205 |
+
{
|
| 206 |
+
SetMeshPoints(&src_mesh, source_p, source_n);
|
| 207 |
+
SetMeshPoints(&tar_mesh, target_p, target_n);
|
| 208 |
+
return;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
void Registration::Read_data(const Mesh& tar,const Mesh& src)
|
| 213 |
+
{
|
| 214 |
+
src_mesh=src;
|
| 215 |
+
tar_mesh=tar;
|
| 216 |
+
return;
|
| 217 |
+
}
|
| 218 |
+
Scalar Registration::SetMeshPoints(Mesh* mesh, const Matrix3X &point, const Matrix3X &point_n)
|
| 219 |
+
{
|
| 220 |
+
int n_vertices = point.cols();
|
| 221 |
+
mesh->request_vertex_normals(); // 确保法向量可以设置
|
| 222 |
+
|
| 223 |
+
bool use_input_normals = (point_n.cols() == n_vertices);
|
| 224 |
+
if (!use_input_normals) {
|
| 225 |
+
std::cout << "[Info] 法向量数据不正确或为空,正在自动计算法向量..." << std::endl;
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
// 判断 mesh 是否为空
|
| 229 |
+
bool mesh_is_empty = (mesh->n_vertices() == 0);
|
| 230 |
+
std::vector<Mesh::VertexHandle> vhandles(n_vertices);
|
| 231 |
+
|
| 232 |
+
if (mesh_is_empty) {
|
| 233 |
+
// 空 mesh:添加顶点
|
| 234 |
+
for (int i = 0; i < n_vertices; i++) {
|
| 235 |
+
vhandles[i] = mesh->add_vertex(Vec3(point(0, i), point(1, i), point(2, i)));
|
| 236 |
+
}
|
| 237 |
+
} else {
|
| 238 |
+
// 已有 mesh:直接覆盖顶点位置
|
| 239 |
+
if (mesh->n_vertices() != n_vertices) {
|
| 240 |
+
std::cerr << "[Warning] mesh 顶点数量和要替换的mesh不匹配" << std::endl;
|
| 241 |
+
}
|
| 242 |
+
int idx = 0;
|
| 243 |
+
for (auto v : mesh->vertices()) {
|
| 244 |
+
if (idx >= n_vertices) break;
|
| 245 |
+
mesh->set_point(v, Vec3(point(0, idx), point(1, idx), point(2, idx)));
|
| 246 |
+
vhandles[idx] = v;
|
| 247 |
+
idx++;
|
| 248 |
+
}
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
// 设置法向量
|
| 252 |
+
#pragma omp parallel for
|
| 253 |
+
for (int i = 0; i < n_vertices; i++) {
|
| 254 |
+
if (use_input_normals) {
|
| 255 |
+
Vec3 n(point_n(0, i), point_n(1, i), point_n(2, i));
|
| 256 |
+
mesh->set_normal(vhandles[i], n);
|
| 257 |
+
} else {
|
| 258 |
+
Vec3 n(0.0, 0.0, 0.0); // 临时零向量,后续自动计算
|
| 259 |
+
mesh->set_normal(vhandles[i], n);
|
| 260 |
+
}
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
// 如果没有输入法向量,则自动计算
|
| 264 |
+
if (!use_input_normals) {
|
| 265 |
+
mesh->update_normals();
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
return 0;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
void Registration::Output_data(const std::string& out_path,const std::string& method_name)
|
| 272 |
+
{
|
| 273 |
+
Scalar gt_err = SetMeshPoints(deformed_mesh_, deformed_points_3X_, deformed_normals_);
|
| 274 |
+
std::string out_file;
|
| 275 |
+
out_file = out_path + method_name+"_res.ply";
|
| 276 |
+
write_by_openmesh(out_file.c_str(), deformed_mesh, scale);
|
| 277 |
+
std::cout<< "write the result to " << out_file << "\n" << std::endl;
|
| 278 |
+
return;
|
| 279 |
+
}
|
cpp/core/registration.h
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef REGISTRATION_H
|
| 2 |
+
#define REGISTRATION_H
|
| 3 |
+
#include "Types.h"
|
| 4 |
+
#include "parameters.h"
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Registration
|
| 10 |
+
{
|
| 11 |
+
public:
|
| 12 |
+
Registration() = default;
|
| 13 |
+
virtual ~Registration() = default;
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
Matrix3X src_points_; // (3,n)
|
| 17 |
+
Matrix3X src_normals_; // (3,n)
|
| 18 |
+
Matrix3X src_vert_colors_; // (3,n)
|
| 19 |
+
Matrix3X deformed_normals_; // (3,n)
|
| 20 |
+
Matrix3X deformed_points_3X_;
|
| 21 |
+
VectorX deformed_points_; // (3n,)
|
| 22 |
+
Matrix3X tar_points_; // (3,n);
|
| 23 |
+
Matrix3X target_normals_; // (3,n)
|
| 24 |
+
Matrix3X tar_vert_colors; // (3,n)
|
| 25 |
+
|
| 26 |
+
double scale=1;
|
| 27 |
+
Mesh src_mesh;
|
| 28 |
+
Mesh tar_mesh;
|
| 29 |
+
Mesh deformed_mesh;
|
| 30 |
+
Mesh* src_mesh_;
|
| 31 |
+
Mesh* tar_mesh_;
|
| 32 |
+
Mesh* deformed_mesh_;
|
| 33 |
+
int n_src_vertex_;
|
| 34 |
+
int n_tar_vertex_;
|
| 35 |
+
int n_landmark_nodes_;
|
| 36 |
+
KDtree* target_tree_; // correspondence paras
|
| 37 |
+
spare::Parameters pars_;
|
| 38 |
+
|
| 39 |
+
std::string res_trans_path_;
|
| 40 |
+
MatrixXX res_trans;
|
| 41 |
+
|
| 42 |
+
struct Closest{
|
| 43 |
+
int src_idx; // vertex index from source model
|
| 44 |
+
int tar_idx; // face index from target model
|
| 45 |
+
Vector3 position;
|
| 46 |
+
Vector3 normal;
|
| 47 |
+
Scalar min_dist2;
|
| 48 |
+
};
|
| 49 |
+
typedef std::vector<Closest> VPairs;
|
| 50 |
+
VPairs correspondence_pairs_;
|
| 51 |
+
|
| 52 |
+
VectorX corres_U0_; // all correspondence points (3, n)
|
| 53 |
+
|
| 54 |
+
void InitCorrespondence(VPairs & corres);
|
| 55 |
+
void FindClosestPoints(VPairs & corres);
|
| 56 |
+
void FindClosestPoints(KDtree* target_tree_tem,VectorX & deformed_v,VPairs & corres);
|
| 57 |
+
void FindClosestPoints(KDtree* target_tree_tem,VPairs & corres, VectorX & deformed_v, std::vector<size_t>& sample_indices);
|
| 58 |
+
double FindKnearestMed(const KDtree& kdtree,
|
| 59 |
+
const Matrix3X& X, int nk);
|
| 60 |
+
void LandMarkCorres(VPairs & correspondence_pairs);
|
| 61 |
+
bool read_landmark(const char* filename, std::vector<int>& landmark_src, std::vector<int>& landmark_tar);
|
| 62 |
+
virtual void Read_data(const std::string& file_target,
|
| 63 |
+
const std::string& file_source) ;
|
| 64 |
+
virtual void Read_data(const Matrix3X &target_p,const Matrix3X &source_p,const Matrix3X &target_n,const Matrix3X &source_n) ;
|
| 65 |
+
virtual void Read_data(const Mesh& tar,const Mesh& src);
|
| 66 |
+
|
| 67 |
+
virtual void Register(){};
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
virtual void Init_data();
|
| 72 |
+
Scalar SetMeshPoints(Mesh* mesh, const Matrix3X &point,const Matrix3X &point_n);
|
| 73 |
+
virtual void Output_data(const std::string& out_path,const std::string& method_name) ;
|
| 74 |
+
virtual void Reg(const std::string& file_target,
|
| 75 |
+
const std::string& file_source,
|
| 76 |
+
const std::string& out_path){};
|
| 77 |
+
};
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
#endif // REGISTRATION_H
|
| 83 |
+
|
| 84 |
+
|
cpp/core/rigid_fricp_registration.cpp
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "rigid_fricp_registration.h"
|
| 2 |
+
#include "tools.h"
|
| 3 |
+
#include "robust_norm.h"
|
| 4 |
+
#include <median.h>
|
| 5 |
+
#include "io.h"
|
| 6 |
+
|
| 7 |
+
AffineMatrix3 RigidFricpRegistration::LogMatrix(const AffineMatrix3& T)
|
| 8 |
+
{
|
| 9 |
+
Eigen::RealSchur<AffineMatrix3> schur(T);
|
| 10 |
+
AffineMatrix3 U = schur.matrixU();
|
| 11 |
+
AffineMatrix3 R = schur.matrixT();
|
| 12 |
+
std::vector<bool> selected(3, true);
|
| 13 |
+
Matrix33 mat_B = Matrix33::Zero(3, 3);
|
| 14 |
+
Matrix33 mat_V = Matrix33::Identity(3, 3);
|
| 15 |
+
|
| 16 |
+
for (int i = 0; i < 3; i++)
|
| 17 |
+
{
|
| 18 |
+
if (selected[i] && fabs(R(i, i) - 1)> SAME_THRESHOLD)
|
| 19 |
+
{
|
| 20 |
+
int pair_second = -1;
|
| 21 |
+
for (int j = i + 1; j <3; j++)
|
| 22 |
+
{
|
| 23 |
+
if (fabs(R(j, j) - R(i, i)) < SAME_THRESHOLD)
|
| 24 |
+
{
|
| 25 |
+
pair_second = j;
|
| 26 |
+
selected[j] = false;
|
| 27 |
+
break;
|
| 28 |
+
}
|
| 29 |
+
}
|
| 30 |
+
if (pair_second > 0)
|
| 31 |
+
{
|
| 32 |
+
selected[i] = false;
|
| 33 |
+
R(i, i) = R(i, i) < -1 ? -1 : R(i, i);
|
| 34 |
+
double theta = acos(R(i, i));
|
| 35 |
+
if (R(i, pair_second) < 0)
|
| 36 |
+
{
|
| 37 |
+
theta = -theta;
|
| 38 |
+
}
|
| 39 |
+
mat_B(i, pair_second) += theta;
|
| 40 |
+
mat_B(pair_second, i) += -theta;
|
| 41 |
+
mat_V(i, pair_second) += -theta / 2;
|
| 42 |
+
mat_V(pair_second, i) += theta / 2;
|
| 43 |
+
double coeff = 1 - (theta * R(i, pair_second)) / (2 * (1 - R(i, i)));
|
| 44 |
+
mat_V(i, i) += -coeff;
|
| 45 |
+
mat_V(pair_second, pair_second) += -coeff;
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
AffineMatrix3 LogTrim = AffineMatrix3::Zero();
|
| 51 |
+
LogTrim.block(0, 0, 3, 3) = mat_B;
|
| 52 |
+
LogTrim.block(0, 3, 3, 1) = mat_V * R.block(0, 3, 3, 1);
|
| 53 |
+
AffineMatrix3 res = U * LogTrim * U.transpose();
|
| 54 |
+
return res;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
Affine3d RigidFricpRegistration::point_to_point(Matrix3X& X,
|
| 58 |
+
Matrix3X& Y,
|
| 59 |
+
const VectorX& w) {
|
| 60 |
+
int dim = X.rows();
|
| 61 |
+
/// Normalize weight vector
|
| 62 |
+
Eigen::VectorXd w_normalized = w / w.sum();
|
| 63 |
+
/// De-mean
|
| 64 |
+
Eigen::VectorXd X_mean(dim), Y_mean(dim);
|
| 65 |
+
for (int i = 0; i<dim; ++i) {
|
| 66 |
+
X_mean(i) = (X.row(i).array()*w_normalized.transpose().array()).sum();
|
| 67 |
+
Y_mean(i) = (Y.row(i).array()*w_normalized.transpose().array()).sum();
|
| 68 |
+
}
|
| 69 |
+
X.colwise() -= X_mean;
|
| 70 |
+
Y.colwise() -= Y_mean;
|
| 71 |
+
/// Compute transformation
|
| 72 |
+
Affine3d transformation;
|
| 73 |
+
MatrixXX sigma = X * w_normalized.asDiagonal() * Y.transpose();
|
| 74 |
+
Eigen::JacobiSVD<MatrixXX> svd(sigma, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
| 75 |
+
if (svd.matrixU().determinant()*svd.matrixV().determinant() < 0.0) {
|
| 76 |
+
Vector3 S = Vector3::Ones(dim); S(dim-1) = -1.0;
|
| 77 |
+
transformation.linear() = svd.matrixV()*S.asDiagonal()*svd.matrixU().transpose();
|
| 78 |
+
}
|
| 79 |
+
else {
|
| 80 |
+
transformation.linear() = svd.matrixV()*svd.matrixU().transpose();
|
| 81 |
+
}
|
| 82 |
+
transformation.translation() = Y_mean - transformation.linear()*X_mean;
|
| 83 |
+
/// Re-apply mean
|
| 84 |
+
X.colwise() += X_mean;
|
| 85 |
+
Y.colwise() += Y_mean;
|
| 86 |
+
/// Return transformation
|
| 87 |
+
return transformation;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
Eigen::Affine3d RigidFricpRegistration::point_to_plane(Eigen::Matrix3Xd& X,
|
| 91 |
+
Eigen::Matrix3Xd& Y,
|
| 92 |
+
const Eigen::Matrix3Xd& Norm,
|
| 93 |
+
const Eigen::VectorXd& w,
|
| 94 |
+
const Eigen::VectorXd& u) {
|
| 95 |
+
/// Normalize weight vector
|
| 96 |
+
Eigen::VectorXd w_normalized = w / w.sum();
|
| 97 |
+
/// De-mean
|
| 98 |
+
Eigen::Vector3d X_mean;
|
| 99 |
+
for (int i = 0; i<3; ++i)
|
| 100 |
+
X_mean(i) = (X.row(i).array()*w_normalized.transpose().array()).sum();
|
| 101 |
+
X.colwise() -= X_mean;
|
| 102 |
+
Y.colwise() -= X_mean;
|
| 103 |
+
/// Prepare LHS and RHS
|
| 104 |
+
Matrix66 LHS = Matrix66::Zero();
|
| 105 |
+
Vector6 RHS = Vector6::Zero();
|
| 106 |
+
Block33 TL = LHS.topLeftCorner<3, 3>();
|
| 107 |
+
Block33 TR = LHS.topRightCorner<3, 3>();
|
| 108 |
+
Block33 BR = LHS.bottomRightCorner<3, 3>();
|
| 109 |
+
Eigen::MatrixXd C = Eigen::MatrixXd::Zero(3, X.cols());
|
| 110 |
+
|
| 111 |
+
#pragma omp parallel
|
| 112 |
+
{
|
| 113 |
+
#pragma omp for
|
| 114 |
+
for (int i = 0; i<X.cols(); i++) {
|
| 115 |
+
C.col(i) = X.col(i).cross(Norm.col(i));
|
| 116 |
+
}
|
| 117 |
+
#pragma omp sections nowait
|
| 118 |
+
{
|
| 119 |
+
#pragma omp section
|
| 120 |
+
for (int i = 0; i<X.cols(); i++) TL.selfadjointView<Eigen::Upper>().rankUpdate(C.col(i), w(i));
|
| 121 |
+
#pragma omp section
|
| 122 |
+
for (int i = 0; i<X.cols(); i++) TR += (C.col(i)*Norm.col(i).transpose())*w(i);
|
| 123 |
+
#pragma omp section
|
| 124 |
+
for (int i = 0; i<X.cols(); i++) BR.selfadjointView<Eigen::Upper>().rankUpdate(Norm.col(i), w(i));
|
| 125 |
+
#pragma omp section
|
| 126 |
+
for (int i = 0; i<C.cols(); i++) {
|
| 127 |
+
double dist_to_plane = -((X.col(i) - Y.col(i)).dot(Norm.col(i)) - u(i))*w(i);
|
| 128 |
+
RHS.head<3>() += C.col(i)*dist_to_plane;
|
| 129 |
+
RHS.tail<3>() += Norm.col(i)*dist_to_plane;
|
| 130 |
+
}
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
LHS = LHS.selfadjointView<Eigen::Upper>();
|
| 134 |
+
/// Compute transformation
|
| 135 |
+
Eigen::Affine3d transformation;
|
| 136 |
+
Eigen::LDLT<Matrix66> ldlt(LHS);
|
| 137 |
+
RHS = ldlt.solve(RHS);
|
| 138 |
+
transformation = Eigen::AngleAxisd(RHS(0), Eigen::Vector3d::UnitX()) *
|
| 139 |
+
Eigen::AngleAxisd(RHS(1), Eigen::Vector3d::UnitY()) *
|
| 140 |
+
Eigen::AngleAxisd(RHS(2), Eigen::Vector3d::UnitZ());
|
| 141 |
+
transformation.translation() = RHS.tail<3>();
|
| 142 |
+
|
| 143 |
+
/// Apply transformation
|
| 144 |
+
/// Re-apply mean
|
| 145 |
+
X.colwise() += X_mean;
|
| 146 |
+
Y.colwise() += X_mean;
|
| 147 |
+
transformation.translation() += X_mean - transformation.linear()*X_mean;
|
| 148 |
+
/// Return transformation
|
| 149 |
+
return transformation;
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
void RigidFricpRegistration::point_to_point(Matrix3X& X, Matrix3X& Y,Matrix3X& Z, Vector3& source_mean_,
|
| 153 |
+
Vector3& target_mean_, ICP::Parameters& par){
|
| 154 |
+
/// Build kd-tree
|
| 155 |
+
KDtree kdtree(Y);
|
| 156 |
+
/// Buffers
|
| 157 |
+
n_src_vertex_ = X.cols();
|
| 158 |
+
Matrix3X Q = Matrix3X::Zero(3, X.cols());
|
| 159 |
+
VectorX W = VectorX::Zero(X.cols());
|
| 160 |
+
deformed_points_ = VectorX::Zero(3 * n_src_vertex_);
|
| 161 |
+
Affine3d T;
|
| 162 |
+
if (par.use_init)
|
| 163 |
+
{
|
| 164 |
+
T.matrix() = par.init_trans;
|
| 165 |
+
}
|
| 166 |
+
else
|
| 167 |
+
{
|
| 168 |
+
T = Affine3d::Identity();
|
| 169 |
+
}
|
| 170 |
+
MatrixXX To1 = T.matrix();
|
| 171 |
+
MatrixXX To2 = T.matrix();
|
| 172 |
+
//Anderson Acc para
|
| 173 |
+
AndersonAcceleration accelerator_;
|
| 174 |
+
Affine3d SVD_T = T;
|
| 175 |
+
double energy = .0, last_energy = std::numeric_limits<double>::max();
|
| 176 |
+
//ground truth point clouds
|
| 177 |
+
Matrix3X X_gt = X;
|
| 178 |
+
if(par.has_groundtruth)
|
| 179 |
+
{
|
| 180 |
+
Vector3 temp_trans = par.gt_trans.col(3).head(3);
|
| 181 |
+
X_gt.colwise() += source_mean_;
|
| 182 |
+
X_gt = par.gt_trans.block(0, 0, 3, 3) * X_gt;
|
| 183 |
+
X_gt.colwise() += temp_trans - target_mean_;
|
| 184 |
+
}
|
| 185 |
+
//output para
|
| 186 |
+
std::string file_out = par.out_path;
|
| 187 |
+
double gt_mse = 0.0;
|
| 188 |
+
// dynamic welsch paras
|
| 189 |
+
double nu1 = 1, nu2 = 1;
|
| 190 |
+
Matrix3X X_deformed = T * X;
|
| 191 |
+
deformed_points_ = Eigen::Map<const VectorX>(X_deformed.data(), 3 * n_src_vertex_);
|
| 192 |
+
FindClosestPoints(target_tree_,deformed_points_,correspondence_pairs_);
|
| 193 |
+
#pragma omp parallel for
|
| 194 |
+
for (int i = 0; i<n_src_vertex_; ++i)
|
| 195 |
+
{
|
| 196 |
+
W[i] = correspondence_pairs_[i].min_dist2;
|
| 197 |
+
Q.col(i) = correspondence_pairs_[i].position;
|
| 198 |
+
}
|
| 199 |
+
//dynamic welsch, calc k-nearest points with itself;
|
| 200 |
+
nu2 = par.nu_end_k * FindKnearestMed(kdtree, Y, 7);
|
| 201 |
+
double med1;
|
| 202 |
+
Eigen::VectorXd W_sqrt = W.array().sqrt();
|
| 203 |
+
igl::median(W_sqrt, med1);
|
| 204 |
+
|
| 205 |
+
nu1 = par.nu_begin_k * med1;
|
| 206 |
+
nu1 = nu1>nu2? nu1:nu2;
|
| 207 |
+
|
| 208 |
+
//AA init
|
| 209 |
+
accelerator_.init(par.anderson_m, (3 + 1) * (3 + 1), LogMatrix(T.matrix()).data());
|
| 210 |
+
|
| 211 |
+
bool stop1 = false;
|
| 212 |
+
while(!stop1)
|
| 213 |
+
{
|
| 214 |
+
/// run ICP
|
| 215 |
+
int icp = 0;
|
| 216 |
+
for (; icp<par.max_icp; ++icp)
|
| 217 |
+
{
|
| 218 |
+
bool accept_aa = false;
|
| 219 |
+
energy = get_energy(W, nu1);
|
| 220 |
+
|
| 221 |
+
if (energy < last_energy)
|
| 222 |
+
{
|
| 223 |
+
last_energy = energy;
|
| 224 |
+
accept_aa = true;
|
| 225 |
+
}
|
| 226 |
+
else
|
| 227 |
+
{
|
| 228 |
+
accelerator_.replace(LogMatrix(SVD_T.matrix()).data());
|
| 229 |
+
X_deformed = SVD_T * X;
|
| 230 |
+
deformed_points_ = Eigen::Map<const VectorX>(X_deformed.data(), 3 * n_src_vertex_);
|
| 231 |
+
FindClosestPoints(target_tree_,deformed_points_,correspondence_pairs_);
|
| 232 |
+
#pragma omp parallel for
|
| 233 |
+
for (int i = 0; i<n_src_vertex_; ++i)
|
| 234 |
+
{
|
| 235 |
+
W[i] = correspondence_pairs_[i].min_dist2;
|
| 236 |
+
Q.col(i) = correspondence_pairs_[i].position;
|
| 237 |
+
}
|
| 238 |
+
last_energy = get_energy(W, nu1);
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
if(par.has_groundtruth)
|
| 244 |
+
{
|
| 245 |
+
gt_mse = (T*X - X_gt).squaredNorm()/n_src_vertex_;
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
robust_weight(W, nu1);
|
| 249 |
+
// Rotation and translation update
|
| 250 |
+
Eigen::VectorXd W_sqrt = W.array().sqrt();
|
| 251 |
+
T = point_to_point(X, Q, W_sqrt);
|
| 252 |
+
|
| 253 |
+
//Anderson Acc
|
| 254 |
+
SVD_T = T;
|
| 255 |
+
|
| 256 |
+
AffineMatrix3 Trans = (Eigen::Map<const AffineMatrix3>(accelerator_.compute(LogMatrix(T.matrix()).data()).data(), 3+1, 3+1)).exp();
|
| 257 |
+
T.linear() = Trans.block(0,0,3,3);
|
| 258 |
+
T.translation() = Trans.block(0,3,3,1);
|
| 259 |
+
X_deformed = T * X;
|
| 260 |
+
deformed_points_ = Eigen::Map<const VectorX>(X_deformed.data(), 3 * n_src_vertex_);
|
| 261 |
+
FindClosestPoints(target_tree_,deformed_points_,correspondence_pairs_);
|
| 262 |
+
#pragma omp parallel for
|
| 263 |
+
for (int i = 0; i<n_src_vertex_; ++i)
|
| 264 |
+
{
|
| 265 |
+
W[i] = correspondence_pairs_[i].min_dist2;
|
| 266 |
+
Q.col(i) = correspondence_pairs_[i].position;
|
| 267 |
+
}
|
| 268 |
+
/// Stopping criteria
|
| 269 |
+
double stop2 = (T.matrix() - To2).norm();
|
| 270 |
+
To2 = T.matrix();
|
| 271 |
+
if(stop2 < par.stop)
|
| 272 |
+
{
|
| 273 |
+
break;
|
| 274 |
+
}
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
stop1 = fabs(nu1 - nu2)<SAME_THRESHOLD? true: false;
|
| 278 |
+
nu1 = nu1*par.nu_alpha > nu2? nu1*par.nu_alpha : nu2;
|
| 279 |
+
|
| 280 |
+
accelerator_.reset(LogMatrix(T.matrix()).data());
|
| 281 |
+
last_energy = std::numeric_limits<double>::max();
|
| 282 |
+
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
///calc convergence energy
|
| 286 |
+
|
| 287 |
+
last_energy = get_energy(W, nu1);
|
| 288 |
+
Z = T * X;
|
| 289 |
+
gt_mse = (Z-X_gt).squaredNorm()/n_src_vertex_;
|
| 290 |
+
T.translation() += - T.rotation() * source_mean_ + target_mean_;
|
| 291 |
+
Z.colwise() += target_mean_;
|
| 292 |
+
|
| 293 |
+
///save convergence result
|
| 294 |
+
par.convergence_energy = last_energy;
|
| 295 |
+
par.convergence_gt_mse = gt_mse;
|
| 296 |
+
par.res_trans = T.matrix();
|
| 297 |
+
}
|
| 298 |
+
|
| 299 |
+
void RigidFricpRegistration::point_to_plane(Eigen::Matrix3Xd& X,
|
| 300 |
+
Eigen::Matrix3Xd& Y, Eigen::Matrix3Xd& norm_x, Eigen::Matrix3Xd& norm_y,
|
| 301 |
+
Eigen::Vector3d& source_mean_, Eigen::Vector3d& target_mean_,
|
| 302 |
+
ICP::Parameters &par) {
|
| 303 |
+
/// Build kd-tree
|
| 304 |
+
KDtree kdtree(Y);
|
| 305 |
+
/// Buffers
|
| 306 |
+
Eigen::Matrix3Xd Qp = Eigen::Matrix3Xd::Zero(3, X.cols());
|
| 307 |
+
Eigen::Matrix3Xd Qn = Eigen::Matrix3Xd::Zero(3, X.cols());
|
| 308 |
+
Eigen::VectorXd W = Eigen::VectorXd::Zero(X.cols());
|
| 309 |
+
Eigen::Matrix3Xd ori_X = X;
|
| 310 |
+
Affine3d T;
|
| 311 |
+
if (par.use_init) T.matrix() = par.init_trans;
|
| 312 |
+
else T = Affine3d::Identity();
|
| 313 |
+
AffineMatrix3 To1 = T.matrix();
|
| 314 |
+
X = T*X;
|
| 315 |
+
|
| 316 |
+
Eigen::Matrix3Xd X_gt = X;
|
| 317 |
+
if(par.has_groundtruth)
|
| 318 |
+
{
|
| 319 |
+
Eigen::Vector3d temp_trans = par.gt_trans.block(0, 3, 3, 1);
|
| 320 |
+
X_gt = ori_X;
|
| 321 |
+
X_gt.colwise() += source_mean_;
|
| 322 |
+
X_gt = par.gt_trans.block(0, 0, 3, 3) * X_gt;
|
| 323 |
+
X_gt.colwise() += temp_trans - target_mean_;
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
double gt_mse = 0.0;
|
| 327 |
+
|
| 328 |
+
//Anderson Acc para
|
| 329 |
+
AndersonAcceleration accelerator_;
|
| 330 |
+
Affine3d LG_T = T;
|
| 331 |
+
double energy = 0.0, prev_res = std::numeric_limits<double>::max(), res = 0.0;
|
| 332 |
+
|
| 333 |
+
// Find closest point
|
| 334 |
+
#pragma omp parallel for
|
| 335 |
+
for (int i = 0; i<X.cols(); ++i) {
|
| 336 |
+
int id = kdtree.closest(X.col(i).data());
|
| 337 |
+
Qp.col(i) = Y.col(id);
|
| 338 |
+
Qn.col(i) = norm_y.col(id);
|
| 339 |
+
W[i] = std::abs(Qn.col(i).transpose() * (X.col(i) - Qp.col(i)));
|
| 340 |
+
}
|
| 341 |
+
|
| 342 |
+
bool stop1 = false;
|
| 343 |
+
while(!stop1)
|
| 344 |
+
{
|
| 345 |
+
/// ICP
|
| 346 |
+
for(int icp=0; icp<par.max_icp; ++icp) {
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
bool accept_aa = false;
|
| 350 |
+
W = W.array().square();//如果后续合并逻辑,就要去掉
|
| 351 |
+
energy = get_energy( W, par.p);
|
| 352 |
+
|
| 353 |
+
Eigen::VectorXd test_w = (X-Qp).colwise().norm();
|
| 354 |
+
if(par.has_groundtruth)
|
| 355 |
+
{
|
| 356 |
+
gt_mse = (X - X_gt).squaredNorm()/X.cols();
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
/// Compute weights
|
| 360 |
+
W = W.array().square();
|
| 361 |
+
robust_weight( W, par.p);
|
| 362 |
+
/// Rotation and translation update
|
| 363 |
+
T = point_to_plane(X, Qp, Qn, W, Eigen::VectorXd::Zero(X.cols()))*T;
|
| 364 |
+
/// Find closest point
|
| 365 |
+
#pragma omp parallel for
|
| 366 |
+
for(int i=0; i<X.cols(); i++) {
|
| 367 |
+
X.col(i) = T * ori_X.col(i);
|
| 368 |
+
int id = kdtree.closest(X.col(i).data());
|
| 369 |
+
Qp.col(i) = Y.col(id);
|
| 370 |
+
Qn.col(i) = norm_y.col(id);
|
| 371 |
+
W[i] = std::abs(Qn.col(i).transpose() * (X.col(i) - Qp.col(i)));
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
/// Stopping criteria
|
| 375 |
+
double stop2 = (T.matrix() - To1).norm();
|
| 376 |
+
To1 = T.matrix();
|
| 377 |
+
if(stop2 < par.stop) break;
|
| 378 |
+
}
|
| 379 |
+
stop1 = true;
|
| 380 |
+
}
|
| 381 |
+
|
| 382 |
+
par.res_trans = T.matrix();
|
| 383 |
+
|
| 384 |
+
///calc convergence energy
|
| 385 |
+
W = (Qn.array()*(X - Qp).array()).colwise().sum().abs().transpose();
|
| 386 |
+
W = W.array().square();
|
| 387 |
+
energy = get_energy(W, par.p);
|
| 388 |
+
gt_mse = (X - X_gt).squaredNorm() / X.cols();
|
| 389 |
+
T.translation().noalias() += -T.rotation()*source_mean_ + target_mean_;
|
| 390 |
+
X.colwise() += target_mean_;
|
| 391 |
+
norm_x = T.rotation()*norm_x;
|
| 392 |
+
|
| 393 |
+
///save convergence result
|
| 394 |
+
par.convergence_energy = energy;
|
| 395 |
+
par.convergence_gt_mse = gt_mse;
|
| 396 |
+
par.res_trans = T.matrix();
|
| 397 |
+
|
| 398 |
+
}
|
| 399 |
+
void RigidFricpRegistration::Paras_init(bool useinit,std::string fileinit,int maxiter,double stop)
|
| 400 |
+
{
|
| 401 |
+
pars.use_init = useinit;//not use the initial transformation
|
| 402 |
+
file_init_ = fileinit;
|
| 403 |
+
pars.max_icp=maxiter;
|
| 404 |
+
pars.stop=stop;
|
| 405 |
+
|
| 406 |
+
}
|
| 407 |
+
void RigidFricpRegistration::use_init_transform(bool a)
|
| 408 |
+
{
|
| 409 |
+
if(a)
|
| 410 |
+
{
|
| 411 |
+
MatrixXX init_trans;
|
| 412 |
+
read_transMat(init_trans, file_init_);
|
| 413 |
+
init_trans.block(0, 3, 3, 1) /= scale;
|
| 414 |
+
init_trans.block(0,3,3,1) += init_trans.block(0,0,3,3)*source_mean_ - target_mean_;
|
| 415 |
+
pars.use_init = true;
|
| 416 |
+
pars.init_trans = init_trans;
|
| 417 |
+
//spars.init_trans = init_trans;
|
| 418 |
+
}
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
void RigidFricpRegistration::Register()
|
| 424 |
+
{
|
| 425 |
+
// normalization
|
| 426 |
+
scale = mesh_scaling(src_mesh, tar_mesh);
|
| 427 |
+
Init_data();
|
| 428 |
+
//scale=pointwise_normalize(tar_points_,src_points_,source_mean_,target_mean_);
|
| 429 |
+
use_init_transform(pars.use_init);
|
| 430 |
+
//--- Execute registration
|
| 431 |
+
std::cout << "begin registration..." << std::endl;
|
| 432 |
+
point_to_point(src_points_, tar_points_, deformed_points_3X_,source_mean_, target_mean_, pars);
|
| 433 |
+
res_trans = pars.res_trans;
|
| 434 |
+
std::cout << "Registration done!" <<std::endl;
|
| 435 |
+
return ;
|
| 436 |
+
}
|
| 437 |
+
void RigidFricpRegistration::Reg(const std::string& file_target,
|
| 438 |
+
const std::string& file_source,
|
| 439 |
+
const std::string& out_path)
|
| 440 |
+
{
|
| 441 |
+
Read_data(file_target, file_source);
|
| 442 |
+
Register();
|
| 443 |
+
Output_data(out_path,"FRICP");
|
| 444 |
+
return;
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
|
cpp/core/rigid_fricp_registration.h
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
#ifndef FRICP_H
|
| 3 |
+
#define FRICP_H
|
| 4 |
+
|
| 5 |
+
#include <AndersonAcceleration.h>
|
| 6 |
+
#define TUPLE_SCALE 0.95
|
| 7 |
+
#define TUPLE_MAX_CNT 1000
|
| 8 |
+
#include "registration.h"
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class RigidFricpRegistration:public Registration
|
| 16 |
+
{
|
| 17 |
+
public:
|
| 18 |
+
RigidFricpRegistration(){};
|
| 19 |
+
~RigidFricpRegistration(){};
|
| 20 |
+
std::string file_init_ ;
|
| 21 |
+
VectorN source_mean_, target_mean_;
|
| 22 |
+
double time;
|
| 23 |
+
ICP::Parameters pars;
|
| 24 |
+
void use_init_transform(bool a);
|
| 25 |
+
AffineMatrix3 LogMatrix(const AffineMatrix3& T);
|
| 26 |
+
Affine3d point_to_point(Matrix3X& X,
|
| 27 |
+
Matrix3X& Y,
|
| 28 |
+
const VectorX& w) ;
|
| 29 |
+
Eigen::Affine3d point_to_plane(Eigen::Matrix3Xd& X,
|
| 30 |
+
Eigen::Matrix3Xd& Y,
|
| 31 |
+
const Eigen::Matrix3Xd& Norm,
|
| 32 |
+
const Eigen::VectorXd& w,
|
| 33 |
+
const Eigen::VectorXd& u) ;
|
| 34 |
+
void point_to_point(Matrix3X& X, Matrix3X& Y,Matrix3X& Z, Vector3& source_mean_,
|
| 35 |
+
Vector3& target_mean_, ICP::Parameters& par);
|
| 36 |
+
void point_to_plane(Eigen::Matrix3Xd& X,
|
| 37 |
+
Eigen::Matrix3Xd& Y, Eigen::Matrix3Xd& norm_x, Eigen::Matrix3Xd& norm_y,
|
| 38 |
+
Eigen::Vector3d& source_mean_, Eigen::Vector3d& target_mean_,
|
| 39 |
+
ICP::Parameters &par) ;
|
| 40 |
+
void Paras_init(bool useinit =false,std::string fileinit=" ",int maxiter=100,double stop=1e-5);
|
| 41 |
+
void Register()override;
|
| 42 |
+
void Reg(const std::string& file_target,
|
| 43 |
+
const std::string& file_source,
|
| 44 |
+
const std::string& out_path)override;
|
| 45 |
+
};
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
#endif
|
cpp/io/io.h
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef IO_H
|
| 2 |
+
#define IO_H
|
| 3 |
+
#include <cstdio>
|
| 4 |
+
#include "pch.h"
|
| 5 |
+
#include "Types.h"
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
inline bool read_by_openmesh(const std::string filename, Mesh& mesh)
|
| 9 |
+
{
|
| 10 |
+
OpenMesh::IO::Options opt_read = OpenMesh::IO::Options::VertexNormal;
|
| 11 |
+
mesh.request_vertex_normals();
|
| 12 |
+
bool read_OK = OpenMesh::IO::read_mesh(mesh, filename,opt_read);
|
| 13 |
+
|
| 14 |
+
std::cout << "filename = " << filename << std::endl;
|
| 15 |
+
if (read_OK)
|
| 16 |
+
{
|
| 17 |
+
mesh.request_vertex_status();
|
| 18 |
+
mesh.request_edge_status();
|
| 19 |
+
mesh.request_face_status();
|
| 20 |
+
|
| 21 |
+
mesh.request_face_normals();
|
| 22 |
+
|
| 23 |
+
mesh.update_face_normals();
|
| 24 |
+
if(mesh.n_faces()>0)
|
| 25 |
+
mesh.update_vertex_normals();
|
| 26 |
+
|
| 27 |
+
Vec3 MeshScales;
|
| 28 |
+
MeshScales[0] = 0.0; MeshScales[1] = 0.0; MeshScales[2] = 0.0;
|
| 29 |
+
for (Mesh::VertexIter v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
|
| 30 |
+
{
|
| 31 |
+
MeshScales += mesh.point(*v_it);
|
| 32 |
+
}
|
| 33 |
+
MeshScales /= mesh.n_vertices();
|
| 34 |
+
return true;
|
| 35 |
+
}
|
| 36 |
+
std::cout << "#vertices = " << mesh.n_vertices() << std::endl;
|
| 37 |
+
return false;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
//---openmesh for output
|
| 41 |
+
inline bool write_by_openmesh(const char* filename, Mesh& mesh, Scalar scale)
|
| 42 |
+
{
|
| 43 |
+
for (Mesh::VertexIter v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
|
| 44 |
+
{
|
| 45 |
+
|
| 46 |
+
mesh.point(*v_it) = mesh.point(*v_it)*scale;
|
| 47 |
+
}
|
| 48 |
+
OpenMesh::IO::Options opt_write = OpenMesh::IO::Options::VertexNormal;
|
| 49 |
+
bool ok = OpenMesh::IO::write_mesh(mesh, filename,opt_write);
|
| 50 |
+
return ok;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
// If it exists, read the initial rigid transformation matrix.
|
| 54 |
+
template <class MatrixType>
|
| 55 |
+
bool read_transMat(MatrixType& trans, const std::string& filename)
|
| 56 |
+
{
|
| 57 |
+
std::ifstream input(filename);
|
| 58 |
+
std::string line;
|
| 59 |
+
int rows, cols;
|
| 60 |
+
std::vector<std::vector<double>> total_data;
|
| 61 |
+
while (getline(input, line)) {
|
| 62 |
+
if(line[0] == 'V' || line[0] == 'M')
|
| 63 |
+
continue;
|
| 64 |
+
std::istringstream iss(line);
|
| 65 |
+
std::vector<double> lineVec;
|
| 66 |
+
while (iss) {
|
| 67 |
+
double item;
|
| 68 |
+
if (iss >> item)
|
| 69 |
+
lineVec.push_back(item);
|
| 70 |
+
}
|
| 71 |
+
cols = lineVec.size();
|
| 72 |
+
total_data.push_back(lineVec);
|
| 73 |
+
}
|
| 74 |
+
if (total_data.size() == 0)
|
| 75 |
+
{
|
| 76 |
+
std::cout << filename << " is empty !! " << std::endl;
|
| 77 |
+
return false;
|
| 78 |
+
}
|
| 79 |
+
rows = total_data.size();
|
| 80 |
+
trans.resize(rows, cols);
|
| 81 |
+
std::cout << "rows = " << rows << " cols = " << cols << std::endl;
|
| 82 |
+
for (int i = 0; i < rows; i++)
|
| 83 |
+
{
|
| 84 |
+
for (int j = 0; j < cols; j++)
|
| 85 |
+
{
|
| 86 |
+
trans(i, j) = total_data[i][j];
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
input.close();
|
| 90 |
+
std::cout << "read trans = \n" << trans << std::endl;
|
| 91 |
+
return true;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
#endif // IO_H
|
cpp/pch.h
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
#include <Eigen/Dense>
|
| 3 |
+
#include <vector>
|
| 4 |
+
#include <utility>
|
| 5 |
+
#include <iostream>
|
| 6 |
+
#include <string>
|
| 7 |
+
#include <nanoflann.hpp>
|
| 8 |
+
#include "nanoflann.h"
|
| 9 |
+
#include <unsupported/Eigen/MatrixFunctions>
|
| 10 |
+
#include <limits>
|
| 11 |
+
#include <type_traits>
|
| 12 |
+
#include <time.h>
|
| 13 |
+
#include <fstream>
|
| 14 |
+
#include <algorithm>
|
| 15 |
+
#include <Eigen/Sparse>
|
| 16 |
+
#include <OpenMesh/Core/Geometry/VectorT.hh>
|
| 17 |
+
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
|
| 18 |
+
#include <OpenMesh/Core/IO/MeshIO.hh>
|
| 19 |
+
#include <cmath>
|
| 20 |
+
#include "Types.h"
|
| 21 |
+
#include "OmpHelper.h"
|
cpp/tools/AndersonAcceleration.h
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef ANDERSONACCELERATION_H_
|
| 2 |
+
#define ANDERSONACCELERATION_H_
|
| 3 |
+
|
| 4 |
+
#include "Types.h"
|
| 5 |
+
#include <cassert>
|
| 6 |
+
#include <algorithm>
|
| 7 |
+
#include <vector>
|
| 8 |
+
#include <omp.h>
|
| 9 |
+
#include <fstream>
|
| 10 |
+
|
| 11 |
+
class AndersonAcceleration
|
| 12 |
+
{
|
| 13 |
+
public:
|
| 14 |
+
AndersonAcceleration()
|
| 15 |
+
:m_(-1), dim_(-1), iter_(-1), col_idx_(-1) {}
|
| 16 |
+
|
| 17 |
+
void replace(const Scalar *u)
|
| 18 |
+
{
|
| 19 |
+
current_u_ = Eigen::Map<const VectorX>(u, dim_);
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
const VectorX& compute(const Scalar* g)
|
| 23 |
+
{
|
| 24 |
+
assert(iter_ >= 0);
|
| 25 |
+
|
| 26 |
+
Eigen::Map<const VectorX> G(g, dim_);
|
| 27 |
+
current_F_ = G - current_u_;
|
| 28 |
+
|
| 29 |
+
if (iter_ == 0)
|
| 30 |
+
{
|
| 31 |
+
prev_dF_.col(0) = -current_F_;
|
| 32 |
+
prev_dG_.col(0) = -G;
|
| 33 |
+
current_u_ = G;
|
| 34 |
+
}
|
| 35 |
+
else
|
| 36 |
+
{
|
| 37 |
+
prev_dF_.col(col_idx_) += current_F_;
|
| 38 |
+
prev_dG_.col(col_idx_) += G;
|
| 39 |
+
|
| 40 |
+
Scalar eps = 1e-14;
|
| 41 |
+
Scalar scale = std::max(eps, prev_dF_.col(col_idx_).norm());
|
| 42 |
+
dF_scale_(col_idx_) = scale;
|
| 43 |
+
prev_dF_.col(col_idx_) /= scale;
|
| 44 |
+
|
| 45 |
+
int m_k = std::min(m_, iter_);
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
if (m_k == 1)
|
| 49 |
+
{
|
| 50 |
+
theta_(0) = 0;
|
| 51 |
+
Scalar dF_sqrnorm = prev_dF_.col(col_idx_).squaredNorm();
|
| 52 |
+
M_(0, 0) = dF_sqrnorm;
|
| 53 |
+
Scalar dF_norm = std::sqrt(dF_sqrnorm);
|
| 54 |
+
|
| 55 |
+
if (dF_norm > eps) {
|
| 56 |
+
theta_(0) = (prev_dF_.col(col_idx_) / dF_norm).dot(current_F_ / dF_norm);
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
else
|
| 60 |
+
{
|
| 61 |
+
// Update the normal equation matrix, for the column and row corresponding to the new dF column
|
| 62 |
+
VectorX new_inner_prod = (prev_dF_.col(col_idx_).transpose() * prev_dF_.block(0, 0, dim_, m_k)).transpose();
|
| 63 |
+
M_.block(col_idx_, 0, 1, m_k) = new_inner_prod.transpose();
|
| 64 |
+
M_.block(0, col_idx_, m_k, 1) = new_inner_prod;
|
| 65 |
+
|
| 66 |
+
// Solve normal equation
|
| 67 |
+
cod_.compute(M_.block(0, 0, m_k, m_k));
|
| 68 |
+
theta_.head(m_k) = cod_.solve(prev_dF_.block(0, 0, dim_, m_k).transpose() * current_F_);
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
// Use rescaled theata to compute new u
|
| 72 |
+
current_u_ = G - prev_dG_.block(0, 0, dim_, m_k) * ((theta_.head(m_k).array() / dF_scale_.head(m_k).array()).matrix());
|
| 73 |
+
col_idx_ = (col_idx_ + 1) % m_;
|
| 74 |
+
prev_dF_.col(col_idx_) = -current_F_;
|
| 75 |
+
prev_dG_.col(col_idx_) = -G;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
iter_++;
|
| 79 |
+
return current_u_;
|
| 80 |
+
}
|
| 81 |
+
void reset(const Scalar *u)
|
| 82 |
+
{
|
| 83 |
+
iter_ = 0;
|
| 84 |
+
col_idx_ = 0;
|
| 85 |
+
current_u_ = Eigen::Map<const VectorX>(u, dim_);
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
// m: number of previous iterations used
|
| 89 |
+
// d: dimension of variables
|
| 90 |
+
// u0: initial variable values
|
| 91 |
+
void init(int m, int d, const Scalar* u0)
|
| 92 |
+
{
|
| 93 |
+
assert(m > 0);
|
| 94 |
+
m_ = m;
|
| 95 |
+
dim_ = d;
|
| 96 |
+
current_u_.resize(d);
|
| 97 |
+
current_F_.resize(d);
|
| 98 |
+
prev_dG_.resize(d, m);
|
| 99 |
+
prev_dF_.resize(d, m);
|
| 100 |
+
M_.resize(m, m);
|
| 101 |
+
theta_.resize(m);
|
| 102 |
+
dF_scale_.resize(m);
|
| 103 |
+
current_u_ = Eigen::Map<const VectorX>(u0, d);
|
| 104 |
+
iter_ = 0;
|
| 105 |
+
col_idx_ = 0;
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
private:
|
| 109 |
+
VectorX current_u_;
|
| 110 |
+
VectorX current_F_;
|
| 111 |
+
MatrixXX prev_dG_;
|
| 112 |
+
MatrixXX prev_dF_;
|
| 113 |
+
MatrixXX M_; // Normal equations matrix for the computing theta
|
| 114 |
+
VectorX theta_; // theta value computed from normal equations
|
| 115 |
+
VectorX dF_scale_; // The scaling factor for each column of prev_dF
|
| 116 |
+
Eigen::CompleteOrthogonalDecomposition<MatrixXX> cod_;
|
| 117 |
+
|
| 118 |
+
int m_; // Number of previous iterates used for Andreson Acceleration
|
| 119 |
+
int dim_; // Dimension of variables
|
| 120 |
+
int iter_; // Iteration count since initialization
|
| 121 |
+
int col_idx_; // Index for history matrix column to store the next value
|
| 122 |
+
int m_k_;
|
| 123 |
+
|
| 124 |
+
Eigen::Matrix4d current_T_;
|
| 125 |
+
Eigen::Matrix4d current_F_T_;
|
| 126 |
+
|
| 127 |
+
MatrixXX T_prev_dF_;
|
| 128 |
+
MatrixXX T_prev_dG_;
|
| 129 |
+
};
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
#endif /* ANDERSONACCELERATION_H_ */
|
cpp/tools/OmpHelper.h
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef OMPHELPER_H_
|
| 2 |
+
#define OMPHELPER_H_
|
| 3 |
+
|
| 4 |
+
#define USE_OPENMP
|
| 5 |
+
|
| 6 |
+
#ifdef USE_OPENMP
|
| 7 |
+
#include <omp.h>
|
| 8 |
+
#ifdef USE_MSVC
|
| 9 |
+
#define OMP_PARALLEL __pragma(omp parallel)
|
| 10 |
+
#define OMP_FOR __pragma(omp for)
|
| 11 |
+
#define OMP_SINGLE __pragma(omp single)
|
| 12 |
+
#define OMP_SECTIONS __pragma(omp sections)
|
| 13 |
+
#define OMP_SECTION __pragma(omp section)
|
| 14 |
+
#else
|
| 15 |
+
#define OMP_PARALLEL _Pragma("omp parallel")
|
| 16 |
+
#define OMP_FOR _Pragma("omp for")
|
| 17 |
+
#define OMP_SINGLE _Pragma("omp single")
|
| 18 |
+
#define OMP_SECTIONS _Pragma("omp sections")
|
| 19 |
+
#define OMP_SECTION _Pragma("omp section")
|
| 20 |
+
#endif
|
| 21 |
+
#else
|
| 22 |
+
#include <ctime>
|
| 23 |
+
#define OMP_PARALLEL
|
| 24 |
+
#define OMP_FOR
|
| 25 |
+
#define OMP_SINGLE
|
| 26 |
+
#define OMP_SECTIONS
|
| 27 |
+
#define OMP_SECTION
|
| 28 |
+
#endif
|
| 29 |
+
|
| 30 |
+
#include <cassert>
|
| 31 |
+
#include <vector>
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class Timer
|
| 36 |
+
{
|
| 37 |
+
public:
|
| 38 |
+
|
| 39 |
+
typedef int EventID;
|
| 40 |
+
|
| 41 |
+
EventID get_time()
|
| 42 |
+
{
|
| 43 |
+
EventID id = time_values_.size();
|
| 44 |
+
|
| 45 |
+
#ifdef USE_OPENMP
|
| 46 |
+
time_values_.push_back(omp_get_wtime());
|
| 47 |
+
#else
|
| 48 |
+
time_values_.push_back(clock());
|
| 49 |
+
#endif
|
| 50 |
+
|
| 51 |
+
return id;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
double elapsed_time(EventID event1, EventID event2)
|
| 55 |
+
{
|
| 56 |
+
assert(event1 >= 0 && event1 < static_cast<EventID>(time_values_.size()));
|
| 57 |
+
assert(event2 >= 0 && event2 < static_cast<EventID>(time_values_.size()));
|
| 58 |
+
|
| 59 |
+
#ifdef USE_OPENMP
|
| 60 |
+
return time_values_[event2] - time_values_[event1];
|
| 61 |
+
#else
|
| 62 |
+
return double(time_values_[event2] - time_values_[event1]) / CLOCKS_PER_SEC;
|
| 63 |
+
#endif
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
void reset()
|
| 67 |
+
{
|
| 68 |
+
time_values_.clear();
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
private:
|
| 72 |
+
#ifdef USE_OPENMP
|
| 73 |
+
std::vector<double> time_values_;
|
| 74 |
+
#else
|
| 75 |
+
std::vector<clock_t> time_values_;
|
| 76 |
+
#endif
|
| 77 |
+
};
|
| 78 |
+
|
| 79 |
+
#endif /* OMPHELPER_H_ */
|
cpp/tools/Types.h
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef TYPES_H
|
| 2 |
+
#define TYPES_H
|
| 3 |
+
|
| 4 |
+
#include <Eigen/Dense>
|
| 5 |
+
#include <Eigen/Sparse>
|
| 6 |
+
#include <OpenMesh/Core/Geometry/VectorT.hh>
|
| 7 |
+
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
|
| 8 |
+
#include <OpenMesh/Core/IO/MeshIO.hh>
|
| 9 |
+
|
| 10 |
+
#include <iostream>
|
| 11 |
+
#include <fstream>
|
| 12 |
+
#include <iomanip>
|
| 13 |
+
|
| 14 |
+
#include <stdlib.h>
|
| 15 |
+
#include <stdio.h>
|
| 16 |
+
#include <cstdio>
|
| 17 |
+
|
| 18 |
+
#include <algorithm>
|
| 19 |
+
#include <math.h>
|
| 20 |
+
#include <cmath>
|
| 21 |
+
|
| 22 |
+
#include <vector>
|
| 23 |
+
#include <list>
|
| 24 |
+
#include <set>
|
| 25 |
+
#include <queue>
|
| 26 |
+
#include <map>
|
| 27 |
+
#include <string>
|
| 28 |
+
|
| 29 |
+
#include <time.h>
|
| 30 |
+
#include <assert.h>
|
| 31 |
+
#include <cstddef>
|
| 32 |
+
#include <limits>
|
| 33 |
+
|
| 34 |
+
//#define USE_FLOAT_SCALAR
|
| 35 |
+
|
| 36 |
+
#define SAME_THRESHOLD 1e-6
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
#ifdef USE_FLOAT_SCALAR
|
| 40 |
+
typedef float Scalar;
|
| 41 |
+
#else
|
| 42 |
+
typedef double Scalar;
|
| 43 |
+
#endif
|
| 44 |
+
|
| 45 |
+
typedef Eigen::SparseMatrix<Scalar, Eigen::ColMajor> ColMajorSparseMatrix;
|
| 46 |
+
typedef Eigen::SparseMatrix<Scalar, Eigen::RowMajor> RowMajorSparseMatrix;
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
#ifdef EIGEN_DONT_ALIGN
|
| 50 |
+
#define EIGEN_ALIGNMENT Eigen::DontAlign
|
| 51 |
+
#else
|
| 52 |
+
#define EIGEN_ALIGNMENT Eigen::AutoAlign
|
| 53 |
+
#endif
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
typedef Eigen::Triplet<Scalar> Triplet;
|
| 57 |
+
|
| 58 |
+
// ======================================================
|
| 59 |
+
// 基础模板别名
|
| 60 |
+
// ======================================================
|
| 61 |
+
template <int Rows, int Cols, int Options = (Eigen::ColMajor | Eigen::AutoAlign)>
|
| 62 |
+
using MatrixT = Eigen::Matrix<Scalar, Rows, Cols, Options>;
|
| 63 |
+
|
| 64 |
+
// ======================
|
| 65 |
+
// 向量类型
|
| 66 |
+
// ======================
|
| 67 |
+
using Vector2 = MatrixT<2,1>;
|
| 68 |
+
using Vector3 = MatrixT<3,1>;
|
| 69 |
+
using Vector4 = MatrixT<4,1>;
|
| 70 |
+
using Vector6 = MatrixT<6,1>;
|
| 71 |
+
using VectorX = MatrixT<Eigen::Dynamic,1>;
|
| 72 |
+
using VectorN = Vector3; // 别名(等价于 Vector3)
|
| 73 |
+
|
| 74 |
+
// ======================
|
| 75 |
+
// 固定尺寸矩阵
|
| 76 |
+
// ======================
|
| 77 |
+
using Matrix22 = MatrixT<2,2>;
|
| 78 |
+
using Matrix23 = MatrixT<2,3>;
|
| 79 |
+
using Matrix32 = MatrixT<3,2>;
|
| 80 |
+
using Matrix33 = MatrixT<3,3>;
|
| 81 |
+
using Matrix34 = MatrixT<3,4>;
|
| 82 |
+
using Matrix44 = MatrixT<4,4>;
|
| 83 |
+
using Matrix66 = MatrixT<6,6>;
|
| 84 |
+
using EigenMatrix12 = MatrixT<12,12>;
|
| 85 |
+
|
| 86 |
+
// ======================
|
| 87 |
+
// 动态尺寸矩阵
|
| 88 |
+
// ======================
|
| 89 |
+
using Matrix2X = MatrixT<2,Eigen::Dynamic>;
|
| 90 |
+
using Matrix3X = MatrixT<3,Eigen::Dynamic>;
|
| 91 |
+
using Matrix4X = MatrixT<4,Eigen::Dynamic>;
|
| 92 |
+
using MatrixX2 = MatrixT<Eigen::Dynamic,2>;
|
| 93 |
+
using MatrixX3 = MatrixT<Eigen::Dynamic,3>;
|
| 94 |
+
using MatrixXX = MatrixT<Eigen::Dynamic,Eigen::Dynamic>;
|
| 95 |
+
using Vertices = Matrix3X; // 顶点矩阵,3×N
|
| 96 |
+
|
| 97 |
+
// ======================
|
| 98 |
+
// 特殊变换与几何类型
|
| 99 |
+
// ======================
|
| 100 |
+
using Affine3 = Eigen::Transform<Scalar,3,Eigen::Affine>;
|
| 101 |
+
using Affine3d = Affine3; // 同义,方便兼容
|
| 102 |
+
using AffineMatrix3 = Matrix44; // 4×4 变换矩阵
|
| 103 |
+
|
| 104 |
+
using EigenAngleAxis = Eigen::AngleAxis<Scalar>;
|
| 105 |
+
using EigenQuaternion = Eigen::Quaternion<Scalar, Eigen::DontAlign>;
|
| 106 |
+
|
| 107 |
+
// ======================
|
| 108 |
+
// 块类型
|
| 109 |
+
// ======================
|
| 110 |
+
using Block33 = Eigen::Block<Matrix66,3,3>;
|
| 111 |
+
|
| 112 |
+
// Conversion between a 3d vector type to Eigen::Vector3d
|
| 113 |
+
template<typename Vec_T>
|
| 114 |
+
inline Vector3 to_eigen_vec3(const Vec_T &vec)
|
| 115 |
+
{
|
| 116 |
+
return Vector3(vec[0], vec[1], vec[2]);
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
template<typename Vec_T>
|
| 121 |
+
inline Vec_T from_eigen_vec3(const Vector3 &vec)
|
| 122 |
+
{
|
| 123 |
+
Vec_T v;
|
| 124 |
+
v[0] = vec(0);
|
| 125 |
+
v[1] = vec(1);
|
| 126 |
+
v[2] = vec(2);
|
| 127 |
+
|
| 128 |
+
return v;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
enum VertexState
|
| 132 |
+
{
|
| 133 |
+
OUTSIDE,
|
| 134 |
+
FRONT,
|
| 135 |
+
INSIDE
|
| 136 |
+
};
|
| 137 |
+
|
| 138 |
+
struct TriTraits : public OpenMesh::DefaultTraits {
|
| 139 |
+
#ifdef USE_FLOAT_SCALAR
|
| 140 |
+
typedef OpenMesh::Vec3f Point;
|
| 141 |
+
typedef OpenMesh::Vec3f Normal;
|
| 142 |
+
#else
|
| 143 |
+
typedef OpenMesh::Vec3d Point;
|
| 144 |
+
typedef OpenMesh::Vec3d Normal;
|
| 145 |
+
#endif
|
| 146 |
+
typedef OpenMesh::Vec4f Color;
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
VertexAttributes(OpenMesh::Attributes::Status);
|
| 151 |
+
FaceAttributes(OpenMesh::Attributes::Status);
|
| 152 |
+
EdgeAttributes(OpenMesh::Attributes::Status);
|
| 153 |
+
HalfedgeAttributes(OpenMesh::Attributes::Status);
|
| 154 |
+
|
| 155 |
+
VertexTraits
|
| 156 |
+
{
|
| 157 |
+
VertexT() : geodesic_distance(1e100), state(OUTSIDE),incident_point(0.0),
|
| 158 |
+
saddle_or_boundary(false)
|
| 159 |
+
{};
|
| 160 |
+
public:
|
| 161 |
+
Scalar geodesic_distance;
|
| 162 |
+
VertexState state;
|
| 163 |
+
OpenMesh::FaceHandle incident_face;
|
| 164 |
+
Scalar incident_point;
|
| 165 |
+
bool saddle_or_boundary;
|
| 166 |
+
};
|
| 167 |
+
FaceTraits
|
| 168 |
+
{
|
| 169 |
+
FaceT() : corner_angles(0,0,0)
|
| 170 |
+
{};
|
| 171 |
+
public:
|
| 172 |
+
Vector3 corner_angles;
|
| 173 |
+
};
|
| 174 |
+
EdgeTraits
|
| 175 |
+
{
|
| 176 |
+
EdgeT() : length(0.0)
|
| 177 |
+
{};
|
| 178 |
+
public:
|
| 179 |
+
Scalar length;
|
| 180 |
+
};
|
| 181 |
+
};
|
| 182 |
+
typedef OpenMesh::TriMesh_ArrayKernelT<TriTraits> Mesh;
|
| 183 |
+
#ifdef USE_FLOAT_SCALAR
|
| 184 |
+
typedef OpenMesh::Vec3f Vec3;
|
| 185 |
+
#else
|
| 186 |
+
typedef OpenMesh::Vec3d Vec3;
|
| 187 |
+
#endif
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
class Matrix3333 // 3x3 matrix: each element is a 3x3 matrix
|
| 191 |
+
{
|
| 192 |
+
public:
|
| 193 |
+
Matrix3333();
|
| 194 |
+
Matrix3333(const Matrix3333& other);
|
| 195 |
+
~Matrix3333() {}
|
| 196 |
+
|
| 197 |
+
void SetZero(); // [0 0 0; 0 0 0; 0 0 0]; 0 = 3x3 zeros
|
| 198 |
+
void SetIdentity(); //[I 0 0; 0 I 0; 0 0 I]; 0 = 3x3 zeros, I = 3x3 identity
|
| 199 |
+
|
| 200 |
+
// operators
|
| 201 |
+
Matrix33& operator() (int row, int col);
|
| 202 |
+
Matrix3333 operator+ (const Matrix3333& plus);
|
| 203 |
+
Matrix3333 operator- (const Matrix3333& minus);
|
| 204 |
+
Matrix3333 operator* (const Matrix33& multi);
|
| 205 |
+
friend Matrix3333 operator* (const Matrix33& multi1, Matrix3333& multi2);
|
| 206 |
+
Matrix3333 operator* (Scalar multi);
|
| 207 |
+
friend Matrix3333 operator* (Scalar multi1, Matrix3333& multi2);
|
| 208 |
+
Matrix3333 transpose();
|
| 209 |
+
Matrix33 Contract(const Matrix33& multi); // this operator is commutative
|
| 210 |
+
Matrix3333 Contract(Matrix3333& multi);
|
| 211 |
+
|
| 212 |
+
//protected:
|
| 213 |
+
|
| 214 |
+
Matrix33 mat[3][3];
|
| 215 |
+
};
|
| 216 |
+
|
| 217 |
+
class Matrix2222 // 2x2 matrix: each element is a 2x2 matrix
|
| 218 |
+
{
|
| 219 |
+
public:
|
| 220 |
+
Matrix2222();
|
| 221 |
+
Matrix2222(const Matrix2222& other);
|
| 222 |
+
~Matrix2222() {}
|
| 223 |
+
|
| 224 |
+
void SetZero(); // [0 0; 0 0]; 0 = 2x2 zeros
|
| 225 |
+
void SetIdentity(); //[I 0; 0 I;]; 0 = 2x2 zeros, I = 2x2 identity
|
| 226 |
+
|
| 227 |
+
// operators and basic functions
|
| 228 |
+
Matrix22& operator() (int row, int col);
|
| 229 |
+
Matrix2222 operator+ (const Matrix2222& plus);
|
| 230 |
+
Matrix2222 operator- (const Matrix2222& minus);
|
| 231 |
+
Matrix2222 operator* (const Matrix22& multi);
|
| 232 |
+
friend Matrix2222 operator* (const Matrix22& multi1, Matrix2222& multi2);
|
| 233 |
+
Matrix2222 operator* (Scalar multi);
|
| 234 |
+
friend Matrix2222 operator* (Scalar multi1, Matrix2222& multi2);
|
| 235 |
+
Matrix2222 transpose();
|
| 236 |
+
Matrix22 Contract(const Matrix22& multi); // this operator is commutative
|
| 237 |
+
Matrix2222 Contract(Matrix2222& multi);
|
| 238 |
+
|
| 239 |
+
protected:
|
| 240 |
+
|
| 241 |
+
Matrix22 mat[2][2];
|
| 242 |
+
};
|
| 243 |
+
|
| 244 |
+
// dst = src1 \kron src2
|
| 245 |
+
void directProduct(Matrix3333& dst, const Matrix33& src1, const Matrix33& src2);
|
| 246 |
+
void directProduct(Matrix2222& dst, const Matrix22& src1, const Matrix22& src2);
|
| 247 |
+
#endif // TYPES_H
|
| 248 |
+
///////////////////////////////////////////////////////////////////////////////
|
cpp/tools/geodesic/geodesic_algorithm_base.h
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef GEODESIC_ALGORITHM_BASE
|
| 2 |
+
#define GEODESIC_ALGORITHM_BASE
|
| 3 |
+
|
| 4 |
+
#include "geodesic_algorithm_exact_elements.h"
|
| 5 |
+
#include "geodesic_constants_and_simple_functions.h"
|
| 6 |
+
#define hfid0 0
|
| 7 |
+
#define hfid1 1
|
| 8 |
+
|
| 9 |
+
namespace geodesic{
|
| 10 |
+
class GeodesicAlgorithmBase
|
| 11 |
+
{
|
| 12 |
+
public:
|
| 13 |
+
vertex_pointer opposite_vertex(edge_pointer e, vertex_pointer v)
|
| 14 |
+
{
|
| 15 |
+
halfedge_handle hf0 = this->mesh()->halfedge_handle(e, hfid0);
|
| 16 |
+
if(this->mesh()->from_vertex_handle(hf0).idx()==v.idx())
|
| 17 |
+
return this->mesh()->to_vertex_handle(hf0);
|
| 18 |
+
else
|
| 19 |
+
return this->mesh()->from_vertex_handle(hf0);
|
| 20 |
+
};
|
| 21 |
+
|
| 22 |
+
vertex_pointer opposite_vertex(face_pointer f, edge_pointer e)
|
| 23 |
+
{
|
| 24 |
+
halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
|
| 25 |
+
hf = this->mesh()->face_handle(hf)==f? hf : this->mesh()->opposite_halfedge_handle(hf);
|
| 26 |
+
return this->mesh()->to_vertex_handle(this->mesh()->next_halfedge_handle(hf));
|
| 27 |
+
};
|
| 28 |
+
|
| 29 |
+
bool belongs_v(edge_pointer e, vertex_pointer v)
|
| 30 |
+
{
|
| 31 |
+
halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
|
| 32 |
+
return this->mesh()->from_vertex_handle(hf) == v ||
|
| 33 |
+
this->mesh()->to_vertex_handle(hf) == v;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
edge_pointer next_edge(face_pointer f, edge_pointer e, vertex_pointer v)
|
| 37 |
+
{
|
| 38 |
+
|
| 39 |
+
halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
|
| 40 |
+
hf = this->mesh()->face_handle(hf)==f? hf : this->mesh()->opposite_halfedge_handle(hf);
|
| 41 |
+
halfedge_handle next_hf;
|
| 42 |
+
if(this->mesh()->from_vertex_handle(hf)==v)
|
| 43 |
+
{
|
| 44 |
+
next_hf = this->mesh()->next_halfedge_handle(this->mesh()->next_halfedge_handle(hf));
|
| 45 |
+
}
|
| 46 |
+
else if(this->mesh()->to_vertex_handle(hf)==v)
|
| 47 |
+
next_hf = this->mesh()->next_halfedge_handle(hf);
|
| 48 |
+
else
|
| 49 |
+
{
|
| 50 |
+
std::cout << "next_edge e and v has no connection" << std::endl;
|
| 51 |
+
exit(1);
|
| 52 |
+
}
|
| 53 |
+
return this->mesh()->edge_handle(next_hf);
|
| 54 |
+
};
|
| 55 |
+
|
| 56 |
+
Scalar vertex_angle(face_pointer f, vertex_pointer v)
|
| 57 |
+
{
|
| 58 |
+
halfedge_handle hf0 = this->mesh()->halfedge_handle(f);
|
| 59 |
+
vertex_pointer v0 = this->mesh()->from_vertex_handle(hf0);
|
| 60 |
+
vertex_pointer v1 = this->mesh()->to_vertex_handle(hf0);
|
| 61 |
+
vertex_pointer v2 = this->mesh()->to_vertex_handle(this->mesh()->next_halfedge_handle(hf0));
|
| 62 |
+
Scalar l1 = (this->mesh()->point(v0) - this->mesh()->point(v1)).norm();
|
| 63 |
+
Scalar l2 = (this->mesh()->point(v1) - this->mesh()->point(v2)).norm();
|
| 64 |
+
Scalar l3 = (this->mesh()->point(v2) - this->mesh()->point(v0)).norm();
|
| 65 |
+
if(v0.idx()==v.idx())
|
| 66 |
+
{
|
| 67 |
+
return angle_from_edges(l2, l3, l1);
|
| 68 |
+
}
|
| 69 |
+
if(v1.idx()==v.idx())
|
| 70 |
+
{
|
| 71 |
+
return angle_from_edges(l3, l1, l2);
|
| 72 |
+
}
|
| 73 |
+
if(v2.idx()==v.idx())
|
| 74 |
+
{
|
| 75 |
+
return angle_from_edges(l1, l2, l3);
|
| 76 |
+
}
|
| 77 |
+
};
|
| 78 |
+
|
| 79 |
+
void update_edgelen()
|
| 80 |
+
{
|
| 81 |
+
for(auto eit = this->mesh()->edges_begin(); eit != this->mesh()->edges_end(); eit++)
|
| 82 |
+
{
|
| 83 |
+
halfedge_handle hf = this->mesh()->halfedge_handle(*eit, hfid0);
|
| 84 |
+
Vec3 v1 = this->mesh()->point(this->mesh()->from_vertex_handle(hf));
|
| 85 |
+
Vec3 v2 = this->mesh()->point(this->mesh()->to_vertex_handle(hf));
|
| 86 |
+
this->mesh()->data(*eit).length = (v1-v2).norm();
|
| 87 |
+
}
|
| 88 |
+
};
|
| 89 |
+
|
| 90 |
+
void build_adjacencies();
|
| 91 |
+
|
| 92 |
+
edge_pointer opposite_edge(face_pointer f, vertex_pointer v)
|
| 93 |
+
{
|
| 94 |
+
for(auto e_it = this->mesh()->fe_begin(f); e_it != this->mesh()->fe_end(f); ++e_it)
|
| 95 |
+
{
|
| 96 |
+
edge_pointer e = *e_it;
|
| 97 |
+
if(!belongs_v(e, v))
|
| 98 |
+
{
|
| 99 |
+
return e;
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
};
|
| 103 |
+
|
| 104 |
+
face_pointer opposite_face(edge_pointer e, face_pointer f)
|
| 105 |
+
{
|
| 106 |
+
halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
|
| 107 |
+
return this->mesh()->face_handle(hf) == f?
|
| 108 |
+
this->mesh()->face_handle(this->mesh()->opposite_halfedge_handle(hf))
|
| 109 |
+
:this->mesh()->face_handle(hf);
|
| 110 |
+
};
|
| 111 |
+
|
| 112 |
+
void initialize(Mesh* mesh);
|
| 113 |
+
|
| 114 |
+
GeodesicAlgorithmBase(Mesh* mesh)
|
| 115 |
+
{
|
| 116 |
+
initialize(mesh);
|
| 117 |
+
};
|
| 118 |
+
|
| 119 |
+
GeodesicAlgorithmBase(){m_mesh = NULL;};
|
| 120 |
+
|
| 121 |
+
virtual ~GeodesicAlgorithmBase(){};
|
| 122 |
+
|
| 123 |
+
virtual void print_statistics() //print info about timing and memory usage in the propagation step of the algorithm
|
| 124 |
+
{
|
| 125 |
+
std::cout << "propagation step took " << m_time_consumed << " seconds " << std::endl;
|
| 126 |
+
};
|
| 127 |
+
|
| 128 |
+
Mesh* mesh(){return m_mesh;};
|
| 129 |
+
|
| 130 |
+
// propagate a window
|
| 131 |
+
bool compute_propagated_parameters(Scalar pseudo_x,
|
| 132 |
+
Scalar pseudo_y,
|
| 133 |
+
Scalar start,
|
| 134 |
+
Scalar end, //start/end of the interval
|
| 135 |
+
Scalar alpha, //corner angle
|
| 136 |
+
Scalar L, //length of the new edge
|
| 137 |
+
interval_pointer candidates,
|
| 138 |
+
Scalar d); //if it is the last interval on the edge
|
| 139 |
+
|
| 140 |
+
// intersection point on an edge
|
| 141 |
+
Scalar compute_positive_intersection(Scalar start,
|
| 142 |
+
Scalar pseudo_x,
|
| 143 |
+
Scalar pseudo_y,
|
| 144 |
+
Scalar sin_alpha,
|
| 145 |
+
Scalar cos_alpha);
|
| 146 |
+
|
| 147 |
+
inline bool calculate_triangle_parameters(list_pointer &list, Triangle &Tri); // calculate the parameters of the triangle to be propagated
|
| 148 |
+
|
| 149 |
+
list_pointer interval_list_0(edge_pointer e)
|
| 150 |
+
{
|
| 151 |
+
return &m_edge_interval_lists_0[e.idx()];
|
| 152 |
+
};
|
| 153 |
+
|
| 154 |
+
list_pointer interval_list_1(edge_pointer e)
|
| 155 |
+
{
|
| 156 |
+
return &m_edge_interval_lists_1[e.idx()];
|
| 157 |
+
};
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
protected:
|
| 161 |
+
|
| 162 |
+
Mesh* m_mesh;
|
| 163 |
+
|
| 164 |
+
Triangle Tri; // the triangle to be propagated
|
| 165 |
+
|
| 166 |
+
IntervalList wl_left, wl_right;
|
| 167 |
+
|
| 168 |
+
std::vector<IntervalList> m_edge_interval_lists_0; // windows propagated from adjacent_face[0] of the edge
|
| 169 |
+
std::vector<IntervalList> m_edge_interval_lists_1; // windows propagated from adjacent_face[1] of the edge
|
| 170 |
+
|
| 171 |
+
};
|
| 172 |
+
|
| 173 |
+
inline void GeodesicAlgorithmBase::initialize(Mesh* mesh)
|
| 174 |
+
{
|
| 175 |
+
m_mesh = mesh;
|
| 176 |
+
m_edge_interval_lists_0.resize(mesh->n_edges());
|
| 177 |
+
m_edge_interval_lists_1.resize(mesh->n_edges());
|
| 178 |
+
|
| 179 |
+
// initialize statistics
|
| 180 |
+
m_queue_max_size = 0;
|
| 181 |
+
m_windows_propagation = 0;
|
| 182 |
+
m_windows_wavefront = 0;
|
| 183 |
+
m_windows_peak = 0;
|
| 184 |
+
|
| 185 |
+
// initialize window lists, similar to half-edge structure
|
| 186 |
+
for (unsigned i = 0; i < m_edge_interval_lists_0.size(); ++i)
|
| 187 |
+
{
|
| 188 |
+
edge_pointer edge = mesh->edge_handle(i);
|
| 189 |
+
m_edge_interval_lists_0[i].initialize(edge);
|
| 190 |
+
m_edge_interval_lists_1[i].initialize(edge);
|
| 191 |
+
interval_list_0(edge)->start_vertex() = mesh->from_vertex_handle(mesh->halfedge_handle(edge, hfid0));
|
| 192 |
+
interval_list_1(edge)->start_vertex() = mesh->from_vertex_handle(mesh->halfedge_handle(edge, hfid1));
|
| 193 |
+
}
|
| 194 |
+
// verify list links
|
| 195 |
+
for (unsigned i = 0; i < mesh->n_faces(); ++i)
|
| 196 |
+
{
|
| 197 |
+
face_pointer f = (mesh->face_handle(i));
|
| 198 |
+
vertex_pointer v[3];
|
| 199 |
+
size_t j = 0;
|
| 200 |
+
for (auto e_it = mesh->fe_begin(f); e_it != mesh->fe_end(f); ++e_it)
|
| 201 |
+
{
|
| 202 |
+
edge_pointer e = *e_it;
|
| 203 |
+
if (mesh->face_handle(mesh->halfedge_handle(e, hfid0)) == f)
|
| 204 |
+
v[j] = interval_list_0(e)->start_vertex();
|
| 205 |
+
else
|
| 206 |
+
v[j] = interval_list_1(e)->start_vertex();
|
| 207 |
+
|
| 208 |
+
if ((interval_list_0(e)->start_vertex().idx() < 0) || (interval_list_1(e)->start_vertex().idx() < 0))
|
| 209 |
+
{
|
| 210 |
+
std::cout << "list link error" << std::endl;
|
| 211 |
+
exit(1);
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
if (interval_list_0(e)->start_vertex() == interval_list_1(e)->start_vertex())
|
| 215 |
+
{
|
| 216 |
+
std::cout << "list link error" << std::endl;
|
| 217 |
+
exit(1);
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
if (!((belongs_v(e, interval_list_0(e)->start_vertex())) &&
|
| 221 |
+
(belongs_v(e, interval_list_1(e)->start_vertex()))))
|
| 222 |
+
{
|
| 223 |
+
std::cout << "list link error" << std::endl;
|
| 224 |
+
exit(1);
|
| 225 |
+
}
|
| 226 |
+
j++;
|
| 227 |
+
}
|
| 228 |
+
if ((v[0].idx() >-1 && v[0] == v[1]) || (v[0].idx() >-1 && v[0] == v[2]) || (v[1].idx() >-1 && v[1] == v[2]))
|
| 229 |
+
{
|
| 230 |
+
std::cout << "list link error" << std::endl;
|
| 231 |
+
exit(1);
|
| 232 |
+
}
|
| 233 |
+
}
|
| 234 |
+
update_edgelen();
|
| 235 |
+
build_adjacencies();
|
| 236 |
+
};
|
| 237 |
+
|
| 238 |
+
inline Scalar GeodesicAlgorithmBase::compute_positive_intersection(Scalar start,
|
| 239 |
+
Scalar pseudo_x,
|
| 240 |
+
Scalar pseudo_y,
|
| 241 |
+
Scalar sin_alpha,
|
| 242 |
+
Scalar cos_alpha)
|
| 243 |
+
{
|
| 244 |
+
//assert(pseudo_y < 0);
|
| 245 |
+
assert(pseudo_y <= 0);
|
| 246 |
+
|
| 247 |
+
Scalar denominator = sin_alpha*(pseudo_x - start) - cos_alpha*pseudo_y;
|
| 248 |
+
if (denominator < 0.0)
|
| 249 |
+
{
|
| 250 |
+
return -1.0;
|
| 251 |
+
}
|
| 252 |
+
|
| 253 |
+
Scalar numerator = -pseudo_y*start;
|
| 254 |
+
|
| 255 |
+
if (numerator < 1e-30)
|
| 256 |
+
{
|
| 257 |
+
return 0.0;
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
if (denominator < 1e-30)
|
| 261 |
+
{
|
| 262 |
+
return -1.0;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
return numerator / denominator;
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
inline bool GeodesicAlgorithmBase::compute_propagated_parameters(Scalar pseudo_x,
|
| 269 |
+
Scalar pseudo_y,
|
| 270 |
+
Scalar begin,
|
| 271 |
+
Scalar end, //start/end of the interval
|
| 272 |
+
Scalar alpha, //corner angle
|
| 273 |
+
Scalar L, //length of the new edge
|
| 274 |
+
interval_pointer candidates,
|
| 275 |
+
Scalar d)
|
| 276 |
+
{
|
| 277 |
+
assert(pseudo_y <= 0.0);
|
| 278 |
+
assert(begin <= end);
|
| 279 |
+
assert(begin >= 0);
|
| 280 |
+
|
| 281 |
+
++m_windows_propagation; // Statistics
|
| 282 |
+
|
| 283 |
+
interval_pointer p = candidates;
|
| 284 |
+
|
| 285 |
+
Scalar sin_alpha = sin(alpha);
|
| 286 |
+
Scalar cos_alpha = cos(alpha);
|
| 287 |
+
|
| 288 |
+
//important: for the first_interval, this function returns zero only if the new edge is "visible" from the source
|
| 289 |
+
//if the new edge can be covered only after turn_over, the value is negative (-1.0)
|
| 290 |
+
Scalar L1 = compute_positive_intersection(begin,
|
| 291 |
+
pseudo_x,
|
| 292 |
+
pseudo_y,
|
| 293 |
+
sin_alpha,
|
| 294 |
+
cos_alpha);
|
| 295 |
+
|
| 296 |
+
if (L1 < 0 || L1 >= L) // Does not produce a window on the edge
|
| 297 |
+
return false;
|
| 298 |
+
|
| 299 |
+
Scalar L2 = compute_positive_intersection(end,
|
| 300 |
+
pseudo_x,
|
| 301 |
+
pseudo_y,
|
| 302 |
+
sin_alpha,
|
| 303 |
+
cos_alpha);
|
| 304 |
+
|
| 305 |
+
if (L2 < 0 || L2 >= L) // Covers vertex
|
| 306 |
+
{
|
| 307 |
+
p->start() = L1;
|
| 308 |
+
p->stop() = L;
|
| 309 |
+
p->pseudo_x() = cos_alpha*pseudo_x + sin_alpha*pseudo_y;
|
| 310 |
+
p->pseudo_y() = -sin_alpha*pseudo_x + cos_alpha*pseudo_y;
|
| 311 |
+
assert(p->pseudo_y() <= 0.0);
|
| 312 |
+
|
| 313 |
+
return true;
|
| 314 |
+
}
|
| 315 |
+
else
|
| 316 |
+
{
|
| 317 |
+
// Does not cover vertex
|
| 318 |
+
p->start() = L1;
|
| 319 |
+
p->stop() = L2;
|
| 320 |
+
p->pseudo_x() = cos_alpha*pseudo_x + sin_alpha*pseudo_y;
|
| 321 |
+
p->pseudo_y() = -sin_alpha*pseudo_x + cos_alpha*pseudo_y;
|
| 322 |
+
assert(p->pseudo_y() <= 0.0);
|
| 323 |
+
|
| 324 |
+
return true;
|
| 325 |
+
}
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
inline bool GeodesicAlgorithmBase::calculate_triangle_parameters(list_pointer &list, Triangle &Tri) // Calculate the parameters of the triangle to be propagated
|
| 329 |
+
{
|
| 330 |
+
OpenMesh::HalfedgeHandle hf0 = this->mesh()->halfedge_handle(list->edge(), hfid0);
|
| 331 |
+
OpenMesh::HalfedgeHandle hf1 = this->mesh()->halfedge_handle(list->edge(), hfid1);
|
| 332 |
+
size_t adjface_size=0;
|
| 333 |
+
if(this->mesh()->face_handle(hf0).idx()>-1)
|
| 334 |
+
adjface_size++;
|
| 335 |
+
if(this->mesh()->face_handle(hf1).idx()>-1)
|
| 336 |
+
adjface_size++;
|
| 337 |
+
|
| 338 |
+
if (adjface_size > 1)
|
| 339 |
+
{
|
| 340 |
+
Tri.bottom_edge = list->edge();
|
| 341 |
+
|
| 342 |
+
if (list == interval_list_0(Tri.bottom_edge))
|
| 343 |
+
Tri.face = this->mesh()->face_handle(hf1);
|
| 344 |
+
else
|
| 345 |
+
Tri.face = this->mesh()->face_handle(hf0);
|
| 346 |
+
|
| 347 |
+
Tri.top_vertex = opposite_vertex(Tri.face, Tri.bottom_edge);
|
| 348 |
+
Tri.left_vertex = list->start_vertex();
|
| 349 |
+
Tri.right_vertex = opposite_vertex(Tri.bottom_edge, Tri.left_vertex);
|
| 350 |
+
|
| 351 |
+
Tri.left_edge = next_edge(Tri.face, Tri.bottom_edge, Tri.left_vertex);
|
| 352 |
+
Tri.right_edge = next_edge(Tri.face, Tri.bottom_edge, Tri.right_vertex);
|
| 353 |
+
|
| 354 |
+
Tri.top_alpha = vertex_angle(Tri.face, Tri.top_vertex);
|
| 355 |
+
Tri.left_alpha = vertex_angle(Tri.face, Tri.left_vertex);
|
| 356 |
+
Tri.right_alpha = vertex_angle(Tri.face, Tri.right_vertex);
|
| 357 |
+
|
| 358 |
+
if (this->mesh()->face_handle(this->mesh()->halfedge_handle(Tri.left_edge, hfid0)) == Tri.face)
|
| 359 |
+
Tri.left_list = interval_list_0(Tri.left_edge);
|
| 360 |
+
else
|
| 361 |
+
Tri.left_list = interval_list_1(Tri.left_edge);
|
| 362 |
+
|
| 363 |
+
if (this->mesh()->face_handle(this->mesh()->halfedge_handle(Tri.right_edge, hfid0)) == Tri.face)
|
| 364 |
+
Tri.right_list = interval_list_0(Tri.right_edge);
|
| 365 |
+
else
|
| 366 |
+
Tri.right_list = interval_list_1(Tri.right_edge);
|
| 367 |
+
|
| 368 |
+
return false;
|
| 369 |
+
}
|
| 370 |
+
else
|
| 371 |
+
{
|
| 372 |
+
return true;
|
| 373 |
+
}
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
inline void GeodesicAlgorithmBase::build_adjacencies()
|
| 378 |
+
{
|
| 379 |
+
// define m_turn_around_flag for vertices
|
| 380 |
+
std::vector<Scalar> total_vertex_angle(this->mesh()->n_vertices(), 0);
|
| 381 |
+
for(auto f_it = this->mesh()->faces_begin(); f_it != this->mesh()->faces_end(); ++f_it)
|
| 382 |
+
{
|
| 383 |
+
halfedge_handle hf0 = this->mesh()->halfedge_handle(*f_it);
|
| 384 |
+
vertex_pointer v0 = this->mesh()->from_vertex_handle(hf0);
|
| 385 |
+
vertex_pointer v1 = this->mesh()->to_vertex_handle(hf0);
|
| 386 |
+
vertex_pointer v2 = this->mesh()->to_vertex_handle(this->mesh()->next_halfedge_handle(hf0));
|
| 387 |
+
Scalar l1 = (this->mesh()->point(v0) - this->mesh()->point(v1)).norm();
|
| 388 |
+
Scalar l2 = (this->mesh()->point(v1) - this->mesh()->point(v2)).norm();
|
| 389 |
+
Scalar l3 = (this->mesh()->point(v2) - this->mesh()->point(v0)).norm();
|
| 390 |
+
|
| 391 |
+
total_vertex_angle[v0.idx()] += angle_from_edges(l2, l3, l1);
|
| 392 |
+
total_vertex_angle[v1.idx()] += angle_from_edges(l3, l1, l2);
|
| 393 |
+
total_vertex_angle[v2.idx()] += angle_from_edges(l1, l2, l3);
|
| 394 |
+
}
|
| 395 |
+
|
| 396 |
+
for(auto v_it = this->mesh()->vertices_begin(); v_it != this->mesh()->vertices_end(); ++v_it)
|
| 397 |
+
{
|
| 398 |
+
vertex_pointer v = *v_it;
|
| 399 |
+
this->mesh()->data(v).saddle_or_boundary = (total_vertex_angle[v.idx()] > 2.0*M_PI - 1e-5);
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
for(auto e_it = this->mesh()->edges_begin(); e_it != this->mesh()->edges_end(); ++e_it)
|
| 403 |
+
{
|
| 404 |
+
edge_pointer e = *e_it;
|
| 405 |
+
if(this->mesh()->is_boundary(e))
|
| 406 |
+
{
|
| 407 |
+
halfedge_handle hf = this->mesh()->halfedge_handle(e, hfid0);
|
| 408 |
+
this->mesh()->data(this->mesh()->from_vertex_handle(hf)).saddle_or_boundary = true;
|
| 409 |
+
this->mesh()->data(this->mesh()->to_vertex_handle(hf)).saddle_or_boundary = true;
|
| 410 |
+
}
|
| 411 |
+
}
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
}//geodesic
|
| 416 |
+
|
| 417 |
+
#endif
|
cpp/tools/geodesic/geodesic_algorithm_exact.h
ADDED
|
@@ -0,0 +1,918 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef GEODESIC_ALGORITHM_EXACT
|
| 2 |
+
#define GEODESIC_ALGORITHM_EXACT
|
| 3 |
+
|
| 4 |
+
#include "geodesic_memory.h"
|
| 5 |
+
#include "geodesic_algorithm_base.h"
|
| 6 |
+
#include "geodesic_algorithm_exact_elements.h"
|
| 7 |
+
#include <string.h>
|
| 8 |
+
|
| 9 |
+
namespace geodesic {
|
| 10 |
+
|
| 11 |
+
class GeodesicAlgorithmExact : public GeodesicAlgorithmBase
|
| 12 |
+
{
|
| 13 |
+
public:
|
| 14 |
+
|
| 15 |
+
// basic functions related to class
|
| 16 |
+
GeodesicAlgorithmExact(Mesh* mesh) :
|
| 17 |
+
GeodesicAlgorithmBase(mesh),
|
| 18 |
+
m_memory_allocator(mesh->n_edges(), mesh->n_edges())
|
| 19 |
+
{ori_mesh = mesh;};
|
| 20 |
+
|
| 21 |
+
// construct neighboring mesh around src within m_radius, if no mesh is constructed,
|
| 22 |
+
// increase m_radius with increase_radio
|
| 23 |
+
GeodesicAlgorithmExact(Mesh* mesh, size_t src, Scalar m_radius)
|
| 24 |
+
{
|
| 25 |
+
ori_mesh = mesh;
|
| 26 |
+
Mesh* sub_mesh = new Mesh;
|
| 27 |
+
bool ok = construct_submesh(sub_mesh, src, m_radius);
|
| 28 |
+
if(ok)
|
| 29 |
+
{
|
| 30 |
+
GeodesicAlgorithmBase::initialize(sub_mesh);
|
| 31 |
+
m_memory_allocator.reset(sub_mesh->n_edges(), sub_mesh->n_edges());
|
| 32 |
+
}
|
| 33 |
+
else
|
| 34 |
+
{
|
| 35 |
+
std::cerr << "Error:Some points cannot be covered under the specified radius, please increase the radius" << std::endl;
|
| 36 |
+
exit(1);
|
| 37 |
+
}
|
| 38 |
+
};
|
| 39 |
+
~GeodesicAlgorithmExact() {};
|
| 40 |
+
void clear() {
|
| 41 |
+
m_memory_allocator.clear();
|
| 42 |
+
for(auto v_it = this->mesh()->vertices_begin();v_it != this->mesh()->vertices_end(); ++v_it)
|
| 43 |
+
{
|
| 44 |
+
this->mesh()->data(*v_it).geodesic_distance = GEODESIC_INF;
|
| 45 |
+
}
|
| 46 |
+
};
|
| 47 |
+
|
| 48 |
+
// main entry
|
| 49 |
+
void propagate(unsigned source, std::vector<size_t>& idxs);
|
| 50 |
+
|
| 51 |
+
// print the resulting statistics
|
| 52 |
+
void print_statistics();
|
| 53 |
+
private:
|
| 54 |
+
|
| 55 |
+
// simple functions
|
| 56 |
+
void initialize_propagation_data();
|
| 57 |
+
void create_pseudo_source_windows(vertex_pointer &v, bool UpdateFIFOQueue);
|
| 58 |
+
void erase_from_queue(vertex_pointer& v);
|
| 59 |
+
|
| 60 |
+
// propagate a windows list (Rule 1)
|
| 61 |
+
void find_separating_point(list_pointer &list); // find the separating point of the windows and the list
|
| 62 |
+
void propagate_windows_to_two_edges(list_pointer &list); // propagates windows to two edges accross a triangle face
|
| 63 |
+
|
| 64 |
+
// pairwise windows checking (Rule 2)
|
| 65 |
+
void check_with_vertices(list_pointer &list);
|
| 66 |
+
windows_state check_between_two_windows(interval_pointer &w1, interval_pointer &w2); // Check two neighbouring crossing windows on same edge
|
| 67 |
+
void pairwise_windows_checking(list_pointer &list); // Check crossing windows on same edge
|
| 68 |
+
|
| 69 |
+
// main operation
|
| 70 |
+
void propagate_one_windows_list(list_pointer &list);
|
| 71 |
+
|
| 72 |
+
// construct neighboring mesh
|
| 73 |
+
bool construct_submesh(Mesh* sub_mesh, size_t source_idx, Scalar radius);
|
| 74 |
+
|
| 75 |
+
// member variables
|
| 76 |
+
std::set<vertex_pointer> m_vertex_queue;
|
| 77 |
+
std::queue<list_pointer> m_list_queue; // FIFO queue for lists
|
| 78 |
+
MemoryAllocator<Interval> m_memory_allocator; // quickly allocate and deallocate intervals
|
| 79 |
+
Scalar neighbor_radius;
|
| 80 |
+
|
| 81 |
+
Eigen::VectorXi SubVidxfromMesh;
|
| 82 |
+
std::vector<int> MeshVidxfromSub;
|
| 83 |
+
|
| 84 |
+
Mesh* ori_mesh;
|
| 85 |
+
unsigned m_source;
|
| 86 |
+
};
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
//----------------- simple functions ---------------------
|
| 90 |
+
inline void GeodesicAlgorithmExact::initialize_propagation_data()
|
| 91 |
+
{
|
| 92 |
+
clear();
|
| 93 |
+
|
| 94 |
+
// initialize source's parameters
|
| 95 |
+
vertex_pointer source = (this->mesh()->vertex_handle(m_source));
|
| 96 |
+
this->mesh()->data(source).geodesic_distance = 0;
|
| 97 |
+
this->mesh()->data(source).state = VertexState::INSIDE;
|
| 98 |
+
|
| 99 |
+
// initialize windows around source
|
| 100 |
+
create_pseudo_source_windows(source, false);
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
inline void GeodesicAlgorithmExact::erase_from_queue(vertex_pointer& v)
|
| 104 |
+
{
|
| 105 |
+
assert(m_vertex_queue.count(v) <= 1);
|
| 106 |
+
|
| 107 |
+
std::multiset<vertex_pointer>::iterator it = m_vertex_queue.find(v);
|
| 108 |
+
if (it != m_vertex_queue.end())
|
| 109 |
+
m_vertex_queue.erase(it);
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
inline void GeodesicAlgorithmExact::create_pseudo_source_windows(vertex_pointer &pseudo_source, bool inside_traversed_area)
|
| 113 |
+
{
|
| 114 |
+
// update vertices around pseudo_source
|
| 115 |
+
for (auto e_it = this->mesh()->ve_begin(pseudo_source); e_it != this->mesh()->ve_end(pseudo_source); ++e_it)
|
| 116 |
+
{
|
| 117 |
+
edge_pointer edge_it = *e_it;
|
| 118 |
+
vertex_pointer vert_it = opposite_vertex(edge_it, pseudo_source);
|
| 119 |
+
|
| 120 |
+
Scalar distance = this->mesh()->data(pseudo_source).geodesic_distance
|
| 121 |
+
+ this->mesh()->data(edge_it).length;
|
| 122 |
+
|
| 123 |
+
if (distance < this->mesh()->data(vert_it).geodesic_distance)
|
| 124 |
+
{
|
| 125 |
+
m_vertex_queue.erase(vert_it);
|
| 126 |
+
|
| 127 |
+
this->mesh()->data(vert_it).geodesic_distance = distance;
|
| 128 |
+
if (this->mesh()->data(vert_it).state == VertexState::OUTSIDE)
|
| 129 |
+
this->mesh()->data(vert_it).state = VertexState::FRONT;
|
| 130 |
+
|
| 131 |
+
this->mesh()->data(vert_it).incident_face = this->mesh()->face_handle(this->mesh()->halfedge_handle(edge_it, hfid0));
|
| 132 |
+
edge_pointer next_edge = geodesic::GeodesicAlgorithmBase::next_edge(
|
| 133 |
+
this->mesh()->data(vert_it).incident_face,edge_it, pseudo_source);
|
| 134 |
+
this->mesh()->data(vert_it).incident_point =
|
| 135 |
+
(this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(next_edge, hfid0)) == pseudo_source) ?
|
| 136 |
+
0 : this->mesh()->data(next_edge).length;
|
| 137 |
+
|
| 138 |
+
m_vertex_queue.insert(vert_it);
|
| 139 |
+
}
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
// update pseudo_source windows around pseudo_source
|
| 143 |
+
for(auto f_it = this->mesh()->vf_begin(pseudo_source); f_it != this->mesh()->vf_end(pseudo_source); ++f_it)
|
| 144 |
+
{
|
| 145 |
+
face_pointer face_it = *f_it;
|
| 146 |
+
edge_pointer edge_it = geodesic::GeodesicAlgorithmBase::opposite_edge(face_it, pseudo_source);
|
| 147 |
+
list_pointer list = (this->mesh()->face_handle(this->mesh()->halfedge_handle(edge_it, hfid0))==face_it)?
|
| 148 |
+
interval_list_0(edge_it) : interval_list_1(edge_it);
|
| 149 |
+
|
| 150 |
+
// create a window
|
| 151 |
+
interval_pointer candidate = new Interval;
|
| 152 |
+
|
| 153 |
+
candidate->start() = 0;
|
| 154 |
+
candidate->stop() = this->mesh()->data(edge_it).length;
|
| 155 |
+
candidate->d() = this->mesh()->data(pseudo_source).geodesic_distance;
|
| 156 |
+
Scalar angle = geodesic::GeodesicAlgorithmBase::vertex_angle(face_it, list->start_vertex());
|
| 157 |
+
Scalar length = this->mesh()->data(geodesic::GeodesicAlgorithmBase::next_edge
|
| 158 |
+
(face_it, edge_it,list->start_vertex())).length;
|
| 159 |
+
candidate->pseudo_x() = cos(angle) * length;
|
| 160 |
+
candidate->pseudo_y() = -sin(angle) * length;
|
| 161 |
+
|
| 162 |
+
// insert into list
|
| 163 |
+
list->push_back(candidate);
|
| 164 |
+
|
| 165 |
+
// push into M_LIST_QUEUE if inside traversed area
|
| 166 |
+
vertex_pointer v0 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(edge_it, hfid0));
|
| 167 |
+
vertex_pointer v1 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(edge_it, hfid1));
|
| 168 |
+
if ((inside_traversed_area) &&
|
| 169 |
+
((this->mesh()->data(v0).state != VertexState::FRONT)
|
| 170 |
+
|| (this->mesh()->data(v1).state != VertexState::FRONT)))
|
| 171 |
+
m_list_queue.push(list);
|
| 172 |
+
|
| 173 |
+
// Statistics
|
| 174 |
+
++m_windows_wavefront;
|
| 175 |
+
if (m_windows_peak < m_windows_wavefront)
|
| 176 |
+
m_windows_peak = m_windows_wavefront;
|
| 177 |
+
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
//----------------- propagate a windows list (Rule 1) ---------------------
|
| 182 |
+
inline void GeodesicAlgorithmExact::find_separating_point(list_pointer &list)
|
| 183 |
+
{
|
| 184 |
+
const Scalar LOCAL_EPSILON = 1e-20 * this->mesh()->data(list->edge()).length; // numerical issue
|
| 185 |
+
|
| 186 |
+
Scalar L = this->mesh()->data(Tri.left_edge).length;
|
| 187 |
+
Scalar top_x = L * cos(Tri.left_alpha);
|
| 188 |
+
Scalar top_y = L * sin(Tri.left_alpha);
|
| 189 |
+
|
| 190 |
+
Scalar temp_geodesic = GEODESIC_INF;
|
| 191 |
+
face_pointer temp_face_handle = this->mesh()->data(Tri.top_vertex).incident_face;
|
| 192 |
+
Scalar temp_incident_point = this->mesh()->data(Tri.top_vertex).incident_point;
|
| 193 |
+
|
| 194 |
+
interval_pointer iter = list->begin();
|
| 195 |
+
|
| 196 |
+
Scalar wlist_sp = 0;
|
| 197 |
+
Scalar wlist_pseudo_x = 0;
|
| 198 |
+
Scalar wlist_pseudo_y = 0;
|
| 199 |
+
|
| 200 |
+
while (iter != NULL)
|
| 201 |
+
{
|
| 202 |
+
interval_pointer &w = iter;
|
| 203 |
+
|
| 204 |
+
Scalar w_sp = w->pseudo_x() - w->pseudo_y() * ((top_x - w->pseudo_x()) / (top_y - w->pseudo_y()));
|
| 205 |
+
Scalar distance = GEODESIC_INF;
|
| 206 |
+
|
| 207 |
+
// shortest path from the window
|
| 208 |
+
if ((w_sp - w->start() > LOCAL_EPSILON) && (w_sp - w->stop() < -LOCAL_EPSILON))
|
| 209 |
+
{
|
| 210 |
+
distance = w->d() + sqrt((top_x - w->pseudo_x()) * (top_x - w->pseudo_x()) + (top_y - w->pseudo_y()) * (top_y - w->pseudo_y()));
|
| 211 |
+
w->shortest_distance() = distance;
|
| 212 |
+
}
|
| 213 |
+
else if (w_sp - w->start() <= LOCAL_EPSILON)
|
| 214 |
+
{
|
| 215 |
+
distance = w->d() + sqrt((top_x - w->start()) * (top_x - w->start()) + top_y * top_y) + sqrt((w->start() - w->pseudo_x()) * (w->start() - w->pseudo_x()) + w->pseudo_y() * w->pseudo_y());
|
| 216 |
+
w->shortest_distance() = distance;
|
| 217 |
+
w_sp = w->start();
|
| 218 |
+
}
|
| 219 |
+
else if (w_sp - w->stop() >= -LOCAL_EPSILON)
|
| 220 |
+
{
|
| 221 |
+
distance = w->d() + sqrt((top_x - w->stop()) * (top_x - w->stop()) + top_y * top_y) + sqrt((w->stop() - w->pseudo_x()) * (w->stop() - w->pseudo_x()) + w->pseudo_y() * w->pseudo_y());
|
| 222 |
+
w->shortest_distance() = distance;
|
| 223 |
+
w_sp = w->stop();
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
// update information at top_t
|
| 227 |
+
if (distance < temp_geodesic)
|
| 228 |
+
{
|
| 229 |
+
temp_geodesic = distance;
|
| 230 |
+
temp_face_handle = Tri.face;
|
| 231 |
+
vertex_pointer v0 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(list->edge(), hfid0));
|
| 232 |
+
temp_incident_point = (list->start_vertex() == v0) ?
|
| 233 |
+
w_sp : this->mesh()->data(list->edge()).length - w_sp;
|
| 234 |
+
wlist_sp = w_sp;
|
| 235 |
+
wlist_pseudo_x = w->pseudo_x();
|
| 236 |
+
wlist_pseudo_y = w->pseudo_y();
|
| 237 |
+
}
|
| 238 |
+
w->sp() = w_sp;
|
| 239 |
+
|
| 240 |
+
iter = iter->next();
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
// update top_vertex and M_VERTEX_QUEUE
|
| 244 |
+
if (temp_geodesic < this->mesh()->data(Tri.top_vertex).geodesic_distance)
|
| 245 |
+
{
|
| 246 |
+
if (this->mesh()->data(Tri.top_vertex).state == VertexState::FRONT) erase_from_queue(Tri.top_vertex);
|
| 247 |
+
this->mesh()->data(Tri.top_vertex).geodesic_distance = temp_geodesic;
|
| 248 |
+
this->mesh()->data(Tri.top_vertex).incident_face = temp_face_handle;
|
| 249 |
+
this->mesh()->data(Tri.top_vertex).incident_point = temp_incident_point;
|
| 250 |
+
if (this->mesh()->data(Tri.top_vertex).state == VertexState::FRONT)
|
| 251 |
+
{
|
| 252 |
+
m_vertex_queue.insert(Tri.top_vertex);
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
if ((this->mesh()->data(Tri.top_vertex).state == VertexState::INSIDE)
|
| 256 |
+
&& (this->mesh()->data(Tri.top_vertex).saddle_or_boundary))
|
| 257 |
+
create_pseudo_source_windows(Tri.top_vertex, true); // handle saddle vertex
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
list->sp() = wlist_sp;
|
| 261 |
+
list->pseudo_x() = wlist_pseudo_x;
|
| 262 |
+
list->pseudo_y() = wlist_pseudo_y;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
inline void GeodesicAlgorithmExact::propagate_windows_to_two_edges(list_pointer &list)
|
| 266 |
+
{
|
| 267 |
+
const Scalar LOCAL_EPSILON = 1e-8 * this->mesh()->data(list->edge()).length; // numerical issue
|
| 268 |
+
|
| 269 |
+
interval_pointer iter = list->begin();
|
| 270 |
+
interval_pointer iter_t;
|
| 271 |
+
|
| 272 |
+
enum PropagationDirection
|
| 273 |
+
{
|
| 274 |
+
LEFT,
|
| 275 |
+
RIGHT,
|
| 276 |
+
BOTH
|
| 277 |
+
};
|
| 278 |
+
|
| 279 |
+
PropagationDirection direction;
|
| 280 |
+
|
| 281 |
+
while (!list->empty() && (iter != NULL))
|
| 282 |
+
{
|
| 283 |
+
interval_pointer &w = iter;
|
| 284 |
+
assert(w->start() <= w->stop());
|
| 285 |
+
|
| 286 |
+
if (w->sp() < list->sp() - LOCAL_EPSILON)
|
| 287 |
+
{
|
| 288 |
+
// only propagate to left edge
|
| 289 |
+
Scalar Intersect_X, Intersect_Y;
|
| 290 |
+
|
| 291 |
+
// judge the positions of the two windows
|
| 292 |
+
CalculateIntersectionPoint(list->pseudo_x(), list->pseudo_y(), list->sp(), 0, w->pseudo_x(), w->pseudo_y(), w->stop(), 0, Intersect_X, Intersect_Y);
|
| 293 |
+
if ((w->stop() < list->sp()) || ((Intersect_Y <= 0) && (Intersect_Y >= list->pseudo_y()) && (Intersect_Y >= w->pseudo_y())))
|
| 294 |
+
{
|
| 295 |
+
direction = PropagationDirection::LEFT;
|
| 296 |
+
}
|
| 297 |
+
else
|
| 298 |
+
{
|
| 299 |
+
direction = PropagationDirection::BOTH;
|
| 300 |
+
}
|
| 301 |
+
}
|
| 302 |
+
else if (w->sp() > list->sp() + LOCAL_EPSILON)
|
| 303 |
+
{
|
| 304 |
+
// only propagate to right edge
|
| 305 |
+
Scalar Intersect_X, Intersect_Y;
|
| 306 |
+
|
| 307 |
+
// judge the positions of the two windows
|
| 308 |
+
CalculateIntersectionPoint(list->pseudo_x(), list->pseudo_y(), list->sp(), 0, w->pseudo_x(), w->pseudo_y(), w->start(), 0, Intersect_X, Intersect_Y);
|
| 309 |
+
if ((w->start() > list->sp())||((Intersect_Y <= 0) && (Intersect_Y >= list->pseudo_y()) && (Intersect_Y >= w->pseudo_y())))
|
| 310 |
+
{
|
| 311 |
+
direction = PropagationDirection::RIGHT;
|
| 312 |
+
}
|
| 313 |
+
else
|
| 314 |
+
{
|
| 315 |
+
direction = PropagationDirection::BOTH;
|
| 316 |
+
}
|
| 317 |
+
}
|
| 318 |
+
else
|
| 319 |
+
{
|
| 320 |
+
// propagate to both edges
|
| 321 |
+
direction = PropagationDirection::BOTH;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
bool ValidPropagation;
|
| 325 |
+
interval_pointer right_w;
|
| 326 |
+
|
| 327 |
+
switch (direction) {
|
| 328 |
+
case PropagationDirection::LEFT:
|
| 329 |
+
ValidPropagation = compute_propagated_parameters(w->pseudo_x(),
|
| 330 |
+
w->pseudo_y(),
|
| 331 |
+
w->start(),
|
| 332 |
+
w->stop(),
|
| 333 |
+
Tri.left_alpha,
|
| 334 |
+
this->mesh()->data(Tri.left_edge).length,
|
| 335 |
+
w,
|
| 336 |
+
w->d());
|
| 337 |
+
|
| 338 |
+
iter_t = iter->next();
|
| 339 |
+
if (ValidPropagation)
|
| 340 |
+
{
|
| 341 |
+
list->erase(w);
|
| 342 |
+
wl_left.push_back(w);
|
| 343 |
+
}
|
| 344 |
+
else
|
| 345 |
+
{
|
| 346 |
+
list->erase(w);
|
| 347 |
+
delete w;
|
| 348 |
+
--m_windows_wavefront;
|
| 349 |
+
}
|
| 350 |
+
iter = iter_t;
|
| 351 |
+
break;
|
| 352 |
+
|
| 353 |
+
case PropagationDirection::RIGHT:
|
| 354 |
+
ValidPropagation = compute_propagated_parameters(this->mesh()->data(Tri.bottom_edge).length - w->pseudo_x(),
|
| 355 |
+
w->pseudo_y(),
|
| 356 |
+
this->mesh()->data(Tri.bottom_edge).length - w->stop(),
|
| 357 |
+
this->mesh()->data(Tri.bottom_edge).length - w->start(),
|
| 358 |
+
Tri.right_alpha,
|
| 359 |
+
this->mesh()->data(Tri.right_edge).length,
|
| 360 |
+
w,
|
| 361 |
+
w->d());
|
| 362 |
+
|
| 363 |
+
iter_t = iter->next();
|
| 364 |
+
if (ValidPropagation)
|
| 365 |
+
{
|
| 366 |
+
Scalar length = this->mesh()->data(Tri.right_edge).length; // invert window
|
| 367 |
+
Scalar start = length - w->stop();
|
| 368 |
+
w->stop() = length - w->start();
|
| 369 |
+
w->start() = start;
|
| 370 |
+
w->pseudo_x() = length - w->pseudo_x();
|
| 371 |
+
|
| 372 |
+
list->erase(w);
|
| 373 |
+
wl_right.push_back(w);
|
| 374 |
+
}
|
| 375 |
+
else
|
| 376 |
+
{
|
| 377 |
+
list->erase(w);
|
| 378 |
+
delete w;
|
| 379 |
+
--m_windows_wavefront;
|
| 380 |
+
}
|
| 381 |
+
iter = iter_t;
|
| 382 |
+
break;
|
| 383 |
+
|
| 384 |
+
case PropagationDirection:: BOTH:
|
| 385 |
+
right_w = new Interval;
|
| 386 |
+
memcpy(right_w, w, sizeof(Interval));
|
| 387 |
+
|
| 388 |
+
ValidPropagation = compute_propagated_parameters(w->pseudo_x(),
|
| 389 |
+
w->pseudo_y(),
|
| 390 |
+
w->start(),
|
| 391 |
+
w->stop(),
|
| 392 |
+
geodesic::GeodesicAlgorithmBase::vertex_angle(Tri.face, Tri.left_vertex),
|
| 393 |
+
this->mesh()->data(Tri.left_edge).length,
|
| 394 |
+
w,
|
| 395 |
+
w->d());
|
| 396 |
+
|
| 397 |
+
iter_t = iter->next();
|
| 398 |
+
if (ValidPropagation)
|
| 399 |
+
{
|
| 400 |
+
list->erase(w);
|
| 401 |
+
wl_left.push_back(w);
|
| 402 |
+
}
|
| 403 |
+
else
|
| 404 |
+
{
|
| 405 |
+
list->erase(w);
|
| 406 |
+
delete w;
|
| 407 |
+
--m_windows_wavefront;
|
| 408 |
+
}
|
| 409 |
+
iter = iter_t;
|
| 410 |
+
|
| 411 |
+
ValidPropagation = compute_propagated_parameters(this->mesh()->data(Tri.bottom_edge).length - right_w->pseudo_x(),
|
| 412 |
+
right_w->pseudo_y(),
|
| 413 |
+
this->mesh()->data(Tri.bottom_edge).length - right_w->stop(),
|
| 414 |
+
this->mesh()->data(Tri.bottom_edge).length - right_w->start(),
|
| 415 |
+
geodesic::GeodesicAlgorithmBase::vertex_angle(Tri.face, Tri.right_vertex),
|
| 416 |
+
this->mesh()->data(Tri.right_edge).length,
|
| 417 |
+
right_w,
|
| 418 |
+
right_w->d());
|
| 419 |
+
|
| 420 |
+
if (ValidPropagation)
|
| 421 |
+
{
|
| 422 |
+
// invert window
|
| 423 |
+
Scalar length = this->mesh()->data(Tri.right_edge).length;
|
| 424 |
+
Scalar start = length - right_w->stop();
|
| 425 |
+
right_w->stop() = length - right_w->start();
|
| 426 |
+
right_w->start() = start;
|
| 427 |
+
right_w->pseudo_x() = length - right_w->pseudo_x();
|
| 428 |
+
|
| 429 |
+
wl_right.push_back(right_w);
|
| 430 |
+
|
| 431 |
+
++m_windows_wavefront;
|
| 432 |
+
if (m_windows_peak < m_windows_wavefront)
|
| 433 |
+
m_windows_peak = m_windows_wavefront;
|
| 434 |
+
}
|
| 435 |
+
else
|
| 436 |
+
{
|
| 437 |
+
delete right_w;
|
| 438 |
+
}
|
| 439 |
+
break;
|
| 440 |
+
|
| 441 |
+
default:
|
| 442 |
+
break;
|
| 443 |
+
}
|
| 444 |
+
}
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
//----------------- pairwise windows checking (Rule 2) ----------------------
|
| 448 |
+
inline void GeodesicAlgorithmExact::check_with_vertices(list_pointer &list)
|
| 449 |
+
{
|
| 450 |
+
if (list->empty()) return;
|
| 451 |
+
|
| 452 |
+
interval_pointer iter = list->begin();
|
| 453 |
+
interval_pointer iter_t;
|
| 454 |
+
|
| 455 |
+
while ((!list->empty()) && (iter != NULL))
|
| 456 |
+
{
|
| 457 |
+
interval_pointer &w = iter;
|
| 458 |
+
bool w_survive = true;
|
| 459 |
+
|
| 460 |
+
edge_pointer e = list->edge();
|
| 461 |
+
vertex_pointer v1 = list->start_vertex();
|
| 462 |
+
vertex_pointer v2 = opposite_vertex(e, v1);
|
| 463 |
+
Scalar d1 = GEODESIC_INF;
|
| 464 |
+
|
| 465 |
+
d1 = w->d() + sqrt((w->stop() - w->pseudo_x()) * (w->stop() - w->pseudo_x()) + w->pseudo_y() * w->pseudo_y());
|
| 466 |
+
if (this->mesh()->data(v1).geodesic_distance + w->stop() < d1)
|
| 467 |
+
w_survive = false;
|
| 468 |
+
|
| 469 |
+
d1 = w->d() + sqrt((w->start() - w->pseudo_x()) * (w->start() - w->pseudo_x()) + w->pseudo_y() * w->pseudo_y());
|
| 470 |
+
if (this->mesh()->data(v2).geodesic_distance + this->mesh()->data(e).length - w->start() < d1)
|
| 471 |
+
w_survive = false;
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
iter_t = iter;
|
| 475 |
+
iter = iter->next();
|
| 476 |
+
|
| 477 |
+
if (!w_survive)
|
| 478 |
+
{
|
| 479 |
+
list->erase(iter_t);
|
| 480 |
+
delete iter_t;
|
| 481 |
+
--m_windows_wavefront;
|
| 482 |
+
}
|
| 483 |
+
}
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
inline windows_state GeodesicAlgorithmExact::check_between_two_windows(interval_pointer &w1, interval_pointer &w2)
|
| 487 |
+
{
|
| 488 |
+
Scalar NUMERCIAL_EPSILON = 1 - 1e-12;
|
| 489 |
+
// we implement the discussed 6 cases as follows for simplicity
|
| 490 |
+
|
| 491 |
+
if ((w1->start() >= w2->start()) && (w1->start() <= w2->stop())) // w1->start
|
| 492 |
+
{
|
| 493 |
+
Scalar Intersect_X, Intersect_Y;
|
| 494 |
+
|
| 495 |
+
// judge the order of the two windows
|
| 496 |
+
CalculateIntersectionPoint(w2->pseudo_x(), w2->pseudo_y(), w1->start(), 0, w1->pseudo_x(), w1->pseudo_y(), w1->stop(), 0, Intersect_X, Intersect_Y);
|
| 497 |
+
|
| 498 |
+
if ((Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
|
| 499 |
+
{
|
| 500 |
+
Scalar d1, d2;
|
| 501 |
+
d1 = w1->d() + sqrt((w1->start() - w1->pseudo_x()) * (w1->start() - w1->pseudo_x()) + (w1->pseudo_y()) * (w1->pseudo_y()));
|
| 502 |
+
d2 = w2->d() + sqrt((w1->start() - w2->pseudo_x()) * (w1->start() - w2->pseudo_x()) + (w2->pseudo_y()) * (w2->pseudo_y()));
|
| 503 |
+
|
| 504 |
+
if (d2 < d1 * NUMERCIAL_EPSILON)
|
| 505 |
+
return w1_invalid;
|
| 506 |
+
if (d1 < d2 * NUMERCIAL_EPSILON)
|
| 507 |
+
w2->start() = w1->start();
|
| 508 |
+
}
|
| 509 |
+
}
|
| 510 |
+
|
| 511 |
+
if ((w1->stop() >= w2->start()) && (w1->stop() <= w2->stop())) // w1->stop
|
| 512 |
+
{
|
| 513 |
+
Scalar Intersect_X, Intersect_Y;
|
| 514 |
+
|
| 515 |
+
// judge the order of the two windows
|
| 516 |
+
CalculateIntersectionPoint(w2->pseudo_x(), w2->pseudo_y(), w1->stop(), 0, w1->pseudo_x(), w1->pseudo_y(), w1->start(), 0, Intersect_X, Intersect_Y);
|
| 517 |
+
|
| 518 |
+
if ((Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
|
| 519 |
+
{
|
| 520 |
+
Scalar d1, d2;
|
| 521 |
+
d1 = w1->d() + sqrt((w1->stop() - w1->pseudo_x()) * (w1->stop() - w1->pseudo_x()) + (w1->pseudo_y()) * (w1->pseudo_y()));
|
| 522 |
+
d2 = w2->d() + sqrt((w1->stop() - w2->pseudo_x()) * (w1->stop() - w2->pseudo_x()) + (w2->pseudo_y()) * (w2->pseudo_y()));
|
| 523 |
+
|
| 524 |
+
if (d2 < d1 * NUMERCIAL_EPSILON)
|
| 525 |
+
return w1_invalid;
|
| 526 |
+
if (d1 < d2 * NUMERCIAL_EPSILON)
|
| 527 |
+
w2->stop() = w1->stop();
|
| 528 |
+
}
|
| 529 |
+
}
|
| 530 |
+
|
| 531 |
+
if ((w2->start() >= w1->start()) && (w2->start() <= w1->stop())) // w2->start
|
| 532 |
+
{
|
| 533 |
+
Scalar Intersect_X, Intersect_Y;
|
| 534 |
+
|
| 535 |
+
// judge the previous order of the two windows
|
| 536 |
+
CalculateIntersectionPoint(w1->pseudo_x(), w1->pseudo_y(), w2->start(), 0, w2->pseudo_x(), w2->pseudo_y(), w2->stop(), 0, Intersect_X, Intersect_Y);
|
| 537 |
+
|
| 538 |
+
if ((Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
|
| 539 |
+
{
|
| 540 |
+
Scalar d1, d2;
|
| 541 |
+
d1 = w1->d() + sqrt((w2->start() - w1->pseudo_x()) * (w2->start() - w1->pseudo_x()) + (w1->pseudo_y()) * (w1->pseudo_y()));
|
| 542 |
+
d2 = w2->d() + sqrt((w2->start() - w2->pseudo_x()) * (w2->start() - w2->pseudo_x()) + (w2->pseudo_y()) * (w2->pseudo_y()));
|
| 543 |
+
|
| 544 |
+
if (d1 < d2 * NUMERCIAL_EPSILON)
|
| 545 |
+
return w2_invalid;
|
| 546 |
+
if (d2 < d1 * NUMERCIAL_EPSILON)
|
| 547 |
+
w1->start() = w2->start();
|
| 548 |
+
}
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
if ((w2->stop() >= w1->start()) && (w2->stop() <= w1->stop())) // w2->stop
|
| 552 |
+
{
|
| 553 |
+
Scalar Intersect_X, Intersect_Y;
|
| 554 |
+
|
| 555 |
+
// judge the previous order of the two windows
|
| 556 |
+
CalculateIntersectionPoint(w1->pseudo_x(), w1->pseudo_y(), w2->stop(), 0, w2->pseudo_x(), w2->pseudo_y(), w2->start(), 0, Intersect_X, Intersect_Y);
|
| 557 |
+
|
| 558 |
+
if ((Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
|
| 559 |
+
{
|
| 560 |
+
Scalar d1, d2;
|
| 561 |
+
d1 = w1->d() + sqrt((w2->stop() - w1->pseudo_x()) * (w2->stop() - w1->pseudo_x()) + (w1->pseudo_y()) * (w1->pseudo_y()));
|
| 562 |
+
d2 = w2->d() + sqrt((w2->stop() - w2->pseudo_x()) * (w2->stop() - w2->pseudo_x()) + (w2->pseudo_y()) * (w2->pseudo_y()));
|
| 563 |
+
|
| 564 |
+
if (d1 < d2 * NUMERCIAL_EPSILON)
|
| 565 |
+
return w2_invalid;
|
| 566 |
+
if (d2 < d1 * NUMERCIAL_EPSILON)
|
| 567 |
+
w1->stop() = w2->stop();
|
| 568 |
+
}
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
if (w1->start() >= w2->stop())
|
| 572 |
+
{
|
| 573 |
+
Scalar Intersect_X, Intersect_Y;
|
| 574 |
+
|
| 575 |
+
// judge the previous order of the two windows
|
| 576 |
+
CalculateIntersectionPoint(w1->pseudo_x(), w1->pseudo_y(), w1->start(), 0, w2->pseudo_x(), w2->pseudo_y(), w2->stop(), 0, Intersect_X, Intersect_Y);
|
| 577 |
+
|
| 578 |
+
face_pointer f = opposite_face(Tri.bottom_edge, Tri.face);
|
| 579 |
+
edge_pointer e = next_edge(f, Tri.bottom_edge, Tri.left_vertex);
|
| 580 |
+
Scalar angle = vertex_angle(f, Tri.left_vertex);
|
| 581 |
+
Scalar Cx = this->mesh()->data(e).length * cos(angle);
|
| 582 |
+
Scalar Cy = this->mesh()->data(e).length * -sin(angle);
|
| 583 |
+
|
| 584 |
+
if ((PointInTriangle(Intersect_X, Intersect_Y, this->mesh()->data(Tri.bottom_edge).length, Cx, Cy))
|
| 585 |
+
&& (Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
|
| 586 |
+
{
|
| 587 |
+
Scalar d1, d2;
|
| 588 |
+
d1 = w1->d() + sqrt((Intersect_X - w1->pseudo_x()) * (Intersect_X - w1->pseudo_x()) + (Intersect_Y - w1->pseudo_y()) * (Intersect_Y - w1->pseudo_y()));
|
| 589 |
+
d2 = w2->d() + sqrt((Intersect_X - w2->pseudo_x()) * (Intersect_X - w2->pseudo_x()) + (Intersect_Y - w2->pseudo_y()) * (Intersect_Y - w2->pseudo_y()));
|
| 590 |
+
|
| 591 |
+
if (d1 < d2 * NUMERCIAL_EPSILON)
|
| 592 |
+
return w2_invalid;
|
| 593 |
+
if (d2 < d1 * NUMERCIAL_EPSILON)
|
| 594 |
+
return w1_invalid;
|
| 595 |
+
}
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
if (w2->start() >= w1->stop())
|
| 599 |
+
{
|
| 600 |
+
Scalar Intersect_X, Intersect_Y;
|
| 601 |
+
|
| 602 |
+
// judge the previous order of the two windows
|
| 603 |
+
CalculateIntersectionPoint(w2->pseudo_x(), w2->pseudo_y(), w2->start(), 0, w1->pseudo_x(), w1->pseudo_y(), w1->stop(), 0, Intersect_X, Intersect_Y);
|
| 604 |
+
|
| 605 |
+
face_pointer f = opposite_face(Tri.bottom_edge, Tri.face);
|
| 606 |
+
edge_pointer e = next_edge(f, Tri.bottom_edge, Tri.left_vertex);
|
| 607 |
+
Scalar angle = vertex_angle(f, Tri.left_vertex);
|
| 608 |
+
Scalar Cx = this->mesh()->data(e).length * cos(angle);
|
| 609 |
+
Scalar Cy = this->mesh()->data(e).length * -sin(angle);
|
| 610 |
+
|
| 611 |
+
if ((PointInTriangle(Intersect_X, Intersect_Y, this->mesh()->data(Tri.bottom_edge).length, Cx, Cy))
|
| 612 |
+
&& (Intersect_Y <= 0) && (Intersect_Y >= w1->pseudo_y()) && (Intersect_Y >= w2->pseudo_y()))
|
| 613 |
+
{
|
| 614 |
+
Scalar d1, d2;
|
| 615 |
+
d1 = w1->d() + sqrt((Intersect_X - w1->pseudo_x()) * (Intersect_X - w1->pseudo_x()) + (Intersect_Y - w1->pseudo_y()) * (Intersect_Y - w1->pseudo_y()));
|
| 616 |
+
d2 = w2->d() + sqrt((Intersect_X - w2->pseudo_x()) * (Intersect_X - w2->pseudo_x()) + (Intersect_Y - w2->pseudo_y()) * (Intersect_Y - w2->pseudo_y()));
|
| 617 |
+
|
| 618 |
+
if (d1 < d2 - NUMERCIAL_EPSILON)
|
| 619 |
+
return w2_invalid;
|
| 620 |
+
if (d2 < d1 - NUMERCIAL_EPSILON)
|
| 621 |
+
return w1_invalid;
|
| 622 |
+
}
|
| 623 |
+
}
|
| 624 |
+
|
| 625 |
+
return both_valid;
|
| 626 |
+
}
|
| 627 |
+
|
| 628 |
+
inline void GeodesicAlgorithmExact::pairwise_windows_checking(list_pointer &list)
|
| 629 |
+
{
|
| 630 |
+
if (list->empty()) return;
|
| 631 |
+
|
| 632 |
+
interval_pointer iter = list->begin();
|
| 633 |
+
interval_pointer next, iter_t;
|
| 634 |
+
|
| 635 |
+
next = iter->next();
|
| 636 |
+
|
| 637 |
+
// traverse successive pairs of windows
|
| 638 |
+
while ((!list->empty()) && (next != NULL))
|
| 639 |
+
{
|
| 640 |
+
windows_state ws = check_between_two_windows(iter, next);
|
| 641 |
+
|
| 642 |
+
switch (ws)
|
| 643 |
+
{
|
| 644 |
+
case geodesic::w1_invalid:
|
| 645 |
+
iter_t = iter;
|
| 646 |
+
if (iter == list->begin())
|
| 647 |
+
{
|
| 648 |
+
iter = iter->next();
|
| 649 |
+
}
|
| 650 |
+
else
|
| 651 |
+
{
|
| 652 |
+
iter = iter->previous();
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
+
list->erase(iter_t);
|
| 656 |
+
delete iter_t;
|
| 657 |
+
--m_windows_wavefront;
|
| 658 |
+
break;
|
| 659 |
+
|
| 660 |
+
case geodesic::w2_invalid:
|
| 661 |
+
list->erase(next);
|
| 662 |
+
delete next;
|
| 663 |
+
--m_windows_wavefront;
|
| 664 |
+
break;
|
| 665 |
+
|
| 666 |
+
case geodesic::both_valid:
|
| 667 |
+
iter = iter->next();
|
| 668 |
+
break;
|
| 669 |
+
|
| 670 |
+
default:
|
| 671 |
+
break;
|
| 672 |
+
}
|
| 673 |
+
|
| 674 |
+
next = iter->next();
|
| 675 |
+
}
|
| 676 |
+
}
|
| 677 |
+
|
| 678 |
+
//------------------------- main operation ----------------------------
|
| 679 |
+
inline void GeodesicAlgorithmExact::propagate_one_windows_list(list_pointer &list)
|
| 680 |
+
{
|
| 681 |
+
if (list->empty()) return;
|
| 682 |
+
OpenMesh::HalfedgeHandle hf0 = this->mesh()->halfedge_handle(list->edge(), hfid0);
|
| 683 |
+
OpenMesh::HalfedgeHandle hf1 = this->mesh()->halfedge_handle(list->edge(), hfid1);
|
| 684 |
+
if (this->mesh()->face_handle(hf0).idx()>-1 && this->mesh()->face_handle(hf1).idx()>-1)
|
| 685 |
+
{
|
| 686 |
+
// Rule 2: pairwise windows checking
|
| 687 |
+
check_with_vertices(list);
|
| 688 |
+
pairwise_windows_checking(list);
|
| 689 |
+
|
| 690 |
+
// Rule 1: "One Angle Two Sides"
|
| 691 |
+
find_separating_point(list);
|
| 692 |
+
propagate_windows_to_two_edges(list);
|
| 693 |
+
}
|
| 694 |
+
}
|
| 695 |
+
|
| 696 |
+
//-------------------------- main entry --------------------------
|
| 697 |
+
inline void GeodesicAlgorithmExact::propagate(unsigned source, std::vector<size_t>& idxs)
|
| 698 |
+
{
|
| 699 |
+
// initialization
|
| 700 |
+
m_source = SubVidxfromMesh[source];
|
| 701 |
+
initialize_propagation_data();
|
| 702 |
+
while (!m_vertex_queue.empty())
|
| 703 |
+
{
|
| 704 |
+
// (1) pop a vertex from M_VERTEX_QUEUE
|
| 705 |
+
vertex_pointer vert = *m_vertex_queue.begin();
|
| 706 |
+
m_vertex_queue.erase(m_vertex_queue.begin());
|
| 707 |
+
|
| 708 |
+
// (2) update wavefront
|
| 709 |
+
this->mesh()->data(vert).state = VertexState::INSIDE;
|
| 710 |
+
for(auto e_it = this->mesh()->ve_begin(vert); e_it != this->mesh()->ve_end(vert); ++e_it)
|
| 711 |
+
{
|
| 712 |
+
vertex_pointer vert_it = opposite_vertex(*e_it, vert);
|
| 713 |
+
if (this->mesh()->data(vert_it).state == VertexState::OUTSIDE)
|
| 714 |
+
this->mesh()->data(vert_it).state = VertexState::FRONT;
|
| 715 |
+
}
|
| 716 |
+
|
| 717 |
+
// (3) handle saddle vertex
|
| 718 |
+
if (this->mesh()->data(vert).saddle_or_boundary) create_pseudo_source_windows(vert, false);
|
| 719 |
+
|
| 720 |
+
// (4) push window lists on the wavefront incident to v into M_LIST_QUEUE
|
| 721 |
+
for(auto e_it = this->mesh()->ve_begin(vert); e_it != this->mesh()->ve_end(vert); ++e_it)
|
| 722 |
+
{
|
| 723 |
+
edge_pointer edge_it = *e_it;
|
| 724 |
+
if (!interval_list_0(edge_it)->empty())
|
| 725 |
+
{
|
| 726 |
+
m_list_queue.push(interval_list_0(edge_it));
|
| 727 |
+
}
|
| 728 |
+
if (!interval_list_1(edge_it)->empty())
|
| 729 |
+
{
|
| 730 |
+
m_list_queue.push(interval_list_1(edge_it));
|
| 731 |
+
}
|
| 732 |
+
}
|
| 733 |
+
|
| 734 |
+
|
| 735 |
+
for(auto f_it = this->mesh()->vf_begin(vert); f_it != this->mesh()->vf_end(vert); ++f_it)
|
| 736 |
+
{
|
| 737 |
+
edge_pointer edge_it = opposite_edge(*f_it, vert);
|
| 738 |
+
bool two_adjface = (this->mesh()->face_handle(this->mesh()->halfedge_handle(edge_it, hfid0)).idx()>-1)
|
| 739 |
+
&& (this->mesh()->face_handle(this->mesh()->halfedge_handle(edge_it, hfid1)).idx()>-1);
|
| 740 |
+
vertex_pointer vert_it;
|
| 741 |
+
if(two_adjface)
|
| 742 |
+
{
|
| 743 |
+
face_pointer faceid = opposite_face(edge_it, *f_it);
|
| 744 |
+
vert_it = opposite_vertex(faceid, edge_it);
|
| 745 |
+
}
|
| 746 |
+
if (!two_adjface || (this->mesh()->data(vert_it).state != VertexState::OUTSIDE))
|
| 747 |
+
{
|
| 748 |
+
if (!interval_list_0(edge_it)->empty())
|
| 749 |
+
{
|
| 750 |
+
m_list_queue.push(interval_list_0(edge_it));
|
| 751 |
+
}
|
| 752 |
+
if (!interval_list_1(edge_it)->empty())
|
| 753 |
+
{
|
| 754 |
+
m_list_queue.push(interval_list_1(edge_it));
|
| 755 |
+
}
|
| 756 |
+
}
|
| 757 |
+
}
|
| 758 |
+
|
| 759 |
+
|
| 760 |
+
// (5) propagate window lists in a FIFO order
|
| 761 |
+
while (!m_list_queue.empty())
|
| 762 |
+
{
|
| 763 |
+
// pop an list from M_LIST_QUEUE
|
| 764 |
+
list_pointer list = m_list_queue.front();
|
| 765 |
+
|
| 766 |
+
m_list_queue.pop();
|
| 767 |
+
|
| 768 |
+
bool is_boundary = calculate_triangle_parameters(list, Tri);
|
| 769 |
+
if (!is_boundary)
|
| 770 |
+
{
|
| 771 |
+
// propagate the window list using Rule 1 and 2
|
| 772 |
+
wl_left.clear(); wl_right.clear();
|
| 773 |
+
propagate_one_windows_list(list);
|
| 774 |
+
|
| 775 |
+
// merge windows lists
|
| 776 |
+
if (!wl_left.empty())
|
| 777 |
+
{
|
| 778 |
+
// in VTP, both "PrimeMerge" and "SecondMerge" connect window lists in an order-free way
|
| 779 |
+
if (!Tri.left_list->empty())
|
| 780 |
+
{
|
| 781 |
+
Tri.left_list->begin()->previous() = wl_left.end();
|
| 782 |
+
wl_left.end()->next() = Tri.left_list->begin();
|
| 783 |
+
Tri.left_list->begin() = wl_left.begin();
|
| 784 |
+
}
|
| 785 |
+
else
|
| 786 |
+
{
|
| 787 |
+
Tri.left_list->begin() = wl_left.begin();
|
| 788 |
+
Tri.left_list->end() = wl_left.end();
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
// push updated list into M_LIST_QUEUE
|
| 792 |
+
vertex_pointer v0 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(Tri.left_edge, hfid0));
|
| 793 |
+
vertex_pointer v1 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(Tri.left_edge, hfid1));
|
| 794 |
+
if (((this->mesh()->data(v0).state == VertexState::INSIDE)
|
| 795 |
+
|| (this->mesh()->data(v1).state == VertexState::INSIDE))
|
| 796 |
+
&& (!Tri.left_list->empty()))
|
| 797 |
+
{
|
| 798 |
+
m_list_queue.push(Tri.left_list);
|
| 799 |
+
}
|
| 800 |
+
}
|
| 801 |
+
|
| 802 |
+
if (!wl_right.empty())
|
| 803 |
+
{
|
| 804 |
+
// in VTP, both "PrimeMerge" and "SecondMerge" connect window lists in an order-free way
|
| 805 |
+
if (!Tri.right_list->empty())
|
| 806 |
+
{
|
| 807 |
+
Tri.right_list->end()->next() = wl_right.begin();
|
| 808 |
+
wl_right.begin()->previous() = Tri.right_list->end();
|
| 809 |
+
Tri.right_list->end() = wl_right.end();
|
| 810 |
+
}
|
| 811 |
+
else
|
| 812 |
+
{
|
| 813 |
+
Tri.right_list->begin() = wl_right.begin();
|
| 814 |
+
Tri.right_list->end() = wl_right.end();
|
| 815 |
+
}
|
| 816 |
+
|
| 817 |
+
// push updated list into M_LIST_QUEUE
|
| 818 |
+
vertex_pointer v0 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(Tri.right_edge, hfid0));
|
| 819 |
+
vertex_pointer v1 = this->mesh()->from_vertex_handle(this->mesh()->halfedge_handle(Tri.right_edge, hfid1));
|
| 820 |
+
if (((this->mesh()->data(v0).state == VertexState::INSIDE)
|
| 821 |
+
|| (this->mesh()->data(v1).state == VertexState::INSIDE)) && (!Tri.right_list->empty()))
|
| 822 |
+
m_list_queue.push(Tri.right_list);
|
| 823 |
+
}
|
| 824 |
+
}
|
| 825 |
+
|
| 826 |
+
list->clear();
|
| 827 |
+
}
|
| 828 |
+
// statistics
|
| 829 |
+
if (m_vertex_queue.size() > m_queue_max_size)
|
| 830 |
+
m_queue_max_size = m_vertex_queue.size();
|
| 831 |
+
}
|
| 832 |
+
idxs.clear();
|
| 833 |
+
for(auto v_it = this->mesh()->vertices_begin(); v_it != this->mesh()->vertices_end(); ++v_it)
|
| 834 |
+
{
|
| 835 |
+
idxs.push_back(MeshVidxfromSub[v_it->idx()]);
|
| 836 |
+
this->ori_mesh->data(this->ori_mesh->vertex_handle(MeshVidxfromSub[v_it->idx()])).geodesic_distance
|
| 837 |
+
= this->mesh()->data(*v_it).geodesic_distance;
|
| 838 |
+
}
|
| 839 |
+
}
|
| 840 |
+
|
| 841 |
+
// construct sub mesh
|
| 842 |
+
inline bool GeodesicAlgorithmExact::construct_submesh(Mesh* sub_mesh, size_t source_idx, Scalar radius)
|
| 843 |
+
{
|
| 844 |
+
std::queue<size_t> vertexlist;
|
| 845 |
+
vertexlist.push(source_idx);
|
| 846 |
+
Vec3 srcp = ori_mesh->point(ori_mesh->vertex_handle(source_idx));
|
| 847 |
+
std::vector<bool> visited(ori_mesh->n_vertices(), false);
|
| 848 |
+
std::vector<bool> added_face(ori_mesh->n_faces(), false);
|
| 849 |
+
SubVidxfromMesh.resize(ori_mesh->n_vertices());
|
| 850 |
+
SubVidxfromMesh.setConstant(-1);
|
| 851 |
+
MeshVidxfromSub.clear();
|
| 852 |
+
visited[source_idx] = true;
|
| 853 |
+
while(!vertexlist.empty())
|
| 854 |
+
{
|
| 855 |
+
size_t vidx = vertexlist.front();
|
| 856 |
+
vertexlist.pop();
|
| 857 |
+
OpenMesh::VertexHandle vh = ori_mesh->vertex_handle(vidx);
|
| 858 |
+
Vec3 vp = ori_mesh->point(vh);
|
| 859 |
+
if((srcp - vp).norm() < radius)
|
| 860 |
+
{
|
| 861 |
+
vertex_pointer new_v = sub_mesh->add_vertex(vp);
|
| 862 |
+
SubVidxfromMesh[vh.idx()] = new_v.idx();
|
| 863 |
+
MeshVidxfromSub.push_back(vh.idx());
|
| 864 |
+
for(auto vv_it = ori_mesh->vv_begin(vh); vv_it != ori_mesh->vv_end(vh); vv_it++)
|
| 865 |
+
{
|
| 866 |
+
if(!visited[vv_it->idx()])
|
| 867 |
+
{
|
| 868 |
+
vertexlist.push(vv_it->idx());
|
| 869 |
+
visited[vv_it->idx()] = true;
|
| 870 |
+
}
|
| 871 |
+
}
|
| 872 |
+
for(auto vf_it = ori_mesh->vf_begin(vh); vf_it != ori_mesh->vf_end(vh); vf_it++)
|
| 873 |
+
{
|
| 874 |
+
halfedge_handle hf = ori_mesh->halfedge_handle(*vf_it);
|
| 875 |
+
if(!added_face[vf_it->idx()])
|
| 876 |
+
{
|
| 877 |
+
vertex_pointer vh = ori_mesh->from_vertex_handle(hf);
|
| 878 |
+
vertex_pointer nextv = ori_mesh->to_vertex_handle(hf);
|
| 879 |
+
vertex_pointer thirdv = ori_mesh->to_vertex_handle(ori_mesh->next_halfedge_handle(hf));
|
| 880 |
+
if(SubVidxfromMesh[vh.idx()] >= 0
|
| 881 |
+
&& SubVidxfromMesh[nextv.idx()] >= 0
|
| 882 |
+
&& SubVidxfromMesh[thirdv.idx()] >= 0)
|
| 883 |
+
{
|
| 884 |
+
std::vector<vertex_pointer> vertices;
|
| 885 |
+
vertices.push_back(sub_mesh->vertex_handle(SubVidxfromMesh[vh.idx()]));
|
| 886 |
+
vertices.push_back(sub_mesh->vertex_handle(SubVidxfromMesh[nextv.idx()]));
|
| 887 |
+
vertices.push_back(sub_mesh->vertex_handle(SubVidxfromMesh[thirdv.idx()]));
|
| 888 |
+
sub_mesh->add_face(vertices);
|
| 889 |
+
added_face[vf_it->idx()] = true;
|
| 890 |
+
}
|
| 891 |
+
}
|
| 892 |
+
}
|
| 893 |
+
}
|
| 894 |
+
}
|
| 895 |
+
|
| 896 |
+
sub_mesh->delete_isolated_vertices();
|
| 897 |
+
sub_mesh->garbage_collection();
|
| 898 |
+
if(sub_mesh->n_vertices() > 0)
|
| 899 |
+
return true;
|
| 900 |
+
else
|
| 901 |
+
return false;
|
| 902 |
+
}
|
| 903 |
+
|
| 904 |
+
//---------------------- print statistics --------------------------
|
| 905 |
+
inline void GeodesicAlgorithmExact::print_statistics()
|
| 906 |
+
{
|
| 907 |
+
GeodesicAlgorithmBase::print_statistics();
|
| 908 |
+
|
| 909 |
+
Scalar memory = sizeof(Interval);
|
| 910 |
+
|
| 911 |
+
//std::cout << std::endl;
|
| 912 |
+
std::cout << "Peak number of intervals on wave-front " << m_windows_peak << std::endl;
|
| 913 |
+
std::cout << "uses about " << memory * m_windows_peak / 1e6 << "MB of memory" << std::endl;
|
| 914 |
+
std::cout << "total interval propagation number " << m_windows_propagation << std::endl;
|
| 915 |
+
std::cout << "maximum interval queue size is " << m_queue_max_size << std::endl;
|
| 916 |
+
}
|
| 917 |
+
} //geodesic
|
| 918 |
+
#endif
|
cpp/tools/geodesic/geodesic_algorithm_exact_elements.h
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef GEODESIC_ALGORITHM_EXACT_ELEMENTS
|
| 2 |
+
#define GEODESIC_ALGORITHM_EXACT_ELEMENTS
|
| 3 |
+
|
| 4 |
+
#include "../tools/Types.h"
|
| 5 |
+
|
| 6 |
+
namespace geodesic {
|
| 7 |
+
|
| 8 |
+
class Interval;
|
| 9 |
+
class IntervalList;
|
| 10 |
+
typedef Interval* interval_pointer;
|
| 11 |
+
typedef IntervalList* list_pointer;
|
| 12 |
+
typedef OpenMesh::FaceHandle face_pointer;
|
| 13 |
+
typedef OpenMesh::EdgeHandle edge_pointer;
|
| 14 |
+
typedef OpenMesh::VertexHandle vertex_pointer;
|
| 15 |
+
typedef OpenMesh::HalfedgeHandle halfedge_handle;
|
| 16 |
+
|
| 17 |
+
struct Triangle // Components of a face to be propagated
|
| 18 |
+
{
|
| 19 |
+
face_pointer face; // Current Face
|
| 20 |
+
|
| 21 |
+
edge_pointer bottom_edge, // Edges
|
| 22 |
+
left_edge,
|
| 23 |
+
right_edge;
|
| 24 |
+
|
| 25 |
+
vertex_pointer top_vertex, // Vertices
|
| 26 |
+
left_vertex,
|
| 27 |
+
right_vertex;
|
| 28 |
+
|
| 29 |
+
Scalar top_alpha,
|
| 30 |
+
left_alpha,
|
| 31 |
+
right_alpha; // Angles
|
| 32 |
+
|
| 33 |
+
list_pointer left_list,
|
| 34 |
+
right_list; // Lists
|
| 35 |
+
};
|
| 36 |
+
|
| 37 |
+
class Interval //interval of the edge
|
| 38 |
+
{
|
| 39 |
+
public:
|
| 40 |
+
|
| 41 |
+
Interval() {};
|
| 42 |
+
~Interval() {};
|
| 43 |
+
|
| 44 |
+
Scalar& start() { return m_start; };
|
| 45 |
+
Scalar& stop() { return m_stop; };
|
| 46 |
+
Scalar& d() { return m_d; };
|
| 47 |
+
Scalar& pseudo_x() { return m_pseudo_x; };
|
| 48 |
+
Scalar& pseudo_y() { return m_pseudo_y; };
|
| 49 |
+
|
| 50 |
+
Scalar& sp() { return m_sp; };
|
| 51 |
+
|
| 52 |
+
Scalar& shortest_distance() { return m_shortest_distance; }
|
| 53 |
+
|
| 54 |
+
interval_pointer& next() { return m_next; };
|
| 55 |
+
interval_pointer& previous() { return m_previous; };
|
| 56 |
+
|
| 57 |
+
private:
|
| 58 |
+
Scalar m_start; //initial point of the interval on the edge
|
| 59 |
+
Scalar m_stop;
|
| 60 |
+
Scalar m_d; //distance from the source to the pseudo-source
|
| 61 |
+
Scalar m_pseudo_x; //coordinates of the pseudo-source in the local coordinate system
|
| 62 |
+
Scalar m_pseudo_y; //y-coordinate should be always negative
|
| 63 |
+
|
| 64 |
+
Scalar m_sp; //separating point
|
| 65 |
+
|
| 66 |
+
Scalar m_shortest_distance; //shortest distance from the interval to top_vertex, for numerical precision issue
|
| 67 |
+
|
| 68 |
+
interval_pointer m_next; //pointer to the next interval in the list
|
| 69 |
+
interval_pointer m_previous; //pointer to the previous interval in the list
|
| 70 |
+
};
|
| 71 |
+
|
| 72 |
+
class IntervalList //list of the of intervals of the given edge
|
| 73 |
+
{
|
| 74 |
+
public:
|
| 75 |
+
IntervalList() { /*m_start = NULL; m_edge = NULL;*/ m_sp = -1; m_begin = m_end = NULL; }
|
| 76 |
+
~IntervalList() {};
|
| 77 |
+
|
| 78 |
+
void clear() { m_begin = m_end = NULL; }
|
| 79 |
+
void initialize(edge_pointer e) { m_edge = e; }
|
| 80 |
+
|
| 81 |
+
vertex_pointer& start_vertex() { return m_start; }
|
| 82 |
+
edge_pointer& edge() { return m_edge; }
|
| 83 |
+
|
| 84 |
+
Scalar& sp() { return m_sp; };
|
| 85 |
+
|
| 86 |
+
Scalar& pseudo_x() { return m_pseudo_x; };
|
| 87 |
+
Scalar& pseudo_y() { return m_pseudo_y; };
|
| 88 |
+
|
| 89 |
+
// List operation
|
| 90 |
+
interval_pointer& begin() { return m_begin; }
|
| 91 |
+
|
| 92 |
+
interval_pointer& end() { return m_end; }
|
| 93 |
+
|
| 94 |
+
bool empty() { return m_begin == NULL; }
|
| 95 |
+
|
| 96 |
+
void push_back(interval_pointer & w)
|
| 97 |
+
{
|
| 98 |
+
if (!m_end)
|
| 99 |
+
{
|
| 100 |
+
w->previous() = NULL;
|
| 101 |
+
w->next() = NULL;
|
| 102 |
+
m_begin = m_end = w;
|
| 103 |
+
}
|
| 104 |
+
else
|
| 105 |
+
{
|
| 106 |
+
w->next() = NULL;
|
| 107 |
+
w->previous() = m_end;
|
| 108 |
+
m_end->next() = w;
|
| 109 |
+
m_end = w;
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
void erase(interval_pointer & w)
|
| 114 |
+
{
|
| 115 |
+
if ((w == m_begin) && (w == m_end))
|
| 116 |
+
{
|
| 117 |
+
m_begin = m_end = NULL;
|
| 118 |
+
}
|
| 119 |
+
else if (w == m_begin)
|
| 120 |
+
{
|
| 121 |
+
m_begin = m_begin->next();
|
| 122 |
+
m_begin->previous() = NULL;
|
| 123 |
+
}
|
| 124 |
+
else if (w == m_end)
|
| 125 |
+
{
|
| 126 |
+
m_end = m_end->previous();
|
| 127 |
+
m_end->next() = NULL;
|
| 128 |
+
}
|
| 129 |
+
else
|
| 130 |
+
{
|
| 131 |
+
w->previous()->next() = w->next();
|
| 132 |
+
w->next()->previous() = w->previous();
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
private:
|
| 137 |
+
|
| 138 |
+
edge_pointer m_edge; //edge that owns this list
|
| 139 |
+
vertex_pointer m_start; //vertex from which the interval list starts
|
| 140 |
+
|
| 141 |
+
interval_pointer m_begin;
|
| 142 |
+
interval_pointer m_end;
|
| 143 |
+
|
| 144 |
+
Scalar m_pseudo_x;
|
| 145 |
+
Scalar m_pseudo_y;
|
| 146 |
+
|
| 147 |
+
Scalar m_sp; //separating point
|
| 148 |
+
};
|
| 149 |
+
|
| 150 |
+
} //geodesic
|
| 151 |
+
|
| 152 |
+
#endif
|
cpp/tools/geodesic/geodesic_constants_and_simple_functions.h
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef GEODESIC_CONSTANTS
|
| 2 |
+
#define GEODESIC_CONSTANTS
|
| 3 |
+
|
| 4 |
+
// some constants and simple math functions
|
| 5 |
+
#include "../tools/Types.h"
|
| 6 |
+
namespace geodesic{
|
| 7 |
+
|
| 8 |
+
#ifndef M_PI
|
| 9 |
+
#define M_PI 3.14159265358979323846
|
| 10 |
+
#endif
|
| 11 |
+
|
| 12 |
+
Scalar const GEODESIC_INF = 1e100;
|
| 13 |
+
|
| 14 |
+
// statistics
|
| 15 |
+
Scalar m_time_consumed; //how much time does the propagation step takes
|
| 16 |
+
unsigned m_queue_max_size; //used for statistics
|
| 17 |
+
unsigned m_windows_propagation; // how many time a window is propagated
|
| 18 |
+
unsigned m_windows_wavefront; // the number of windows on the wavefront
|
| 19 |
+
unsigned m_windows_peak; // the maximum number of windows, used to calculate the memory
|
| 20 |
+
|
| 21 |
+
// two windows' states after checking
|
| 22 |
+
enum windows_state
|
| 23 |
+
{
|
| 24 |
+
w1_invalid,
|
| 25 |
+
w2_invalid,
|
| 26 |
+
both_valid
|
| 27 |
+
};
|
| 28 |
+
|
| 29 |
+
inline Scalar cos_from_edges(Scalar const a, //compute the cosine of the angle given the lengths of the edges
|
| 30 |
+
Scalar const b,
|
| 31 |
+
Scalar const c)
|
| 32 |
+
{
|
| 33 |
+
assert(a > 1e-50);
|
| 34 |
+
assert(b > 1e-50);
|
| 35 |
+
assert(c > 1e-50);
|
| 36 |
+
|
| 37 |
+
Scalar result = (b * b + c * c - a * a)/(2.0 * b * c);
|
| 38 |
+
result = result>-1.0?result:-1.0;
|
| 39 |
+
return result <1.0 ? result:1.0;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
inline Scalar angle_from_edges(Scalar const a, //compute the cosine of the angle given the lengths of the edges
|
| 43 |
+
Scalar const b,
|
| 44 |
+
Scalar const c)
|
| 45 |
+
{
|
| 46 |
+
return acos(cos_from_edges(a, b, c));
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
template<class Points, class Faces>
|
| 50 |
+
inline bool read_mesh_from_file(char* filename,
|
| 51 |
+
Points& points,
|
| 52 |
+
Faces& faces,
|
| 53 |
+
std::vector<int> &realIndex,
|
| 54 |
+
int& originalVertNum)
|
| 55 |
+
{
|
| 56 |
+
std::ifstream file(filename);
|
| 57 |
+
assert(file.is_open());
|
| 58 |
+
if(!file.is_open()) return false;
|
| 59 |
+
|
| 60 |
+
char type;
|
| 61 |
+
std::string curLine;
|
| 62 |
+
Scalar coord[3];
|
| 63 |
+
unsigned int vtxIdx[3];
|
| 64 |
+
std::map<std::string, int> mapForDuplicate;
|
| 65 |
+
originalVertNum = 0;
|
| 66 |
+
|
| 67 |
+
while(getline(file, curLine))
|
| 68 |
+
{
|
| 69 |
+
if (curLine.size() < 2) continue;
|
| 70 |
+
if (curLine[0] == 'v' && curLine[1] != 't')
|
| 71 |
+
{
|
| 72 |
+
std::map<std::string, int>::iterator pos = mapForDuplicate.find(curLine);
|
| 73 |
+
if (pos == mapForDuplicate.end())
|
| 74 |
+
{
|
| 75 |
+
int oldSize = mapForDuplicate.size();
|
| 76 |
+
realIndex.push_back(oldSize);
|
| 77 |
+
mapForDuplicate[curLine] = oldSize;
|
| 78 |
+
sscanf(curLine.c_str(), "v %lf %lf %lf", &coord[0], &coord[1], &coord[2]);
|
| 79 |
+
for (int i = 0;i < 3;++i) points.push_back(coord[i]);
|
| 80 |
+
}
|
| 81 |
+
else
|
| 82 |
+
{
|
| 83 |
+
realIndex.push_back(pos->second);
|
| 84 |
+
}
|
| 85 |
+
++originalVertNum;
|
| 86 |
+
}
|
| 87 |
+
else if (curLine[0] == 'f')
|
| 88 |
+
{
|
| 89 |
+
unsigned tex;
|
| 90 |
+
if (curLine.find('/') != std::string::npos)
|
| 91 |
+
sscanf(curLine.c_str(), "f %d/%d %d/%d %d/%d", &vtxIdx[0], &tex, &vtxIdx[1], &tex, &vtxIdx[2], &tex);
|
| 92 |
+
else
|
| 93 |
+
sscanf(curLine.c_str(), "f %d %d %d", &vtxIdx[0], &vtxIdx[1], &vtxIdx[2]);
|
| 94 |
+
|
| 95 |
+
vtxIdx[0] = realIndex[vtxIdx[0]-1];
|
| 96 |
+
vtxIdx[1] = realIndex[vtxIdx[1]-1];
|
| 97 |
+
vtxIdx[2] = realIndex[vtxIdx[2]-1];
|
| 98 |
+
if (vtxIdx[0] == vtxIdx[1] || vtxIdx[0] == vtxIdx[2] || vtxIdx[1] == vtxIdx[2]) continue;
|
| 99 |
+
|
| 100 |
+
for (int i = 0;i < 3;++i) faces.push_back(vtxIdx[i]);
|
| 101 |
+
}
|
| 102 |
+
}
|
| 103 |
+
file.close();
|
| 104 |
+
|
| 105 |
+
printf("There are %d non-coincidence vertices.\n", points.size() / 3);
|
| 106 |
+
|
| 107 |
+
return true;
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
inline void CalculateIntersectionPoint(Scalar X1, Scalar Y1, // compute intersection point of two windows
|
| 113 |
+
Scalar X2, Scalar Y2,
|
| 114 |
+
Scalar X3, Scalar Y3,
|
| 115 |
+
Scalar X4, Scalar Y4,
|
| 116 |
+
Scalar &Intersect_X, Scalar &Intersect_Y)
|
| 117 |
+
{
|
| 118 |
+
Scalar A1, B1, C1, A2, B2, C2;
|
| 119 |
+
A1 = Y2 - Y1;
|
| 120 |
+
B1 = X1 - X2;
|
| 121 |
+
C1 = X2 * Y1 - X1 * Y2;
|
| 122 |
+
A2 = Y4 - Y3;
|
| 123 |
+
B2 = X3 - X4;
|
| 124 |
+
C2 = X4 * Y3 - X3 * Y4;
|
| 125 |
+
|
| 126 |
+
Intersect_X = (B2 * C1 - B1 * C2) / (A2 * B1 - A1 * B2);
|
| 127 |
+
Intersect_Y = (A1 * C2 - A2 * C1) / (A2 * B1 - A1 * B2);
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
inline bool PointInTriangle(Scalar &X, Scalar &Y, // judge if a point is inside a triangle
|
| 132 |
+
//Scalar Ax, Scalar Ay, // 0, 0
|
| 133 |
+
Scalar &Bx, //Scalar By, // By = 0
|
| 134 |
+
Scalar &Cx, Scalar &Cy)
|
| 135 |
+
{
|
| 136 |
+
Scalar dot00 = Cx * Cx + Cy * Cy;// dot00 = dot(v0, v0)
|
| 137 |
+
Scalar dot01 = Cx * Bx;// dot01 = dot(v0, v1)
|
| 138 |
+
Scalar dot02 = Cx * X + Cy * Y;// dot02 = dot(v0, v2)
|
| 139 |
+
Scalar dot11 = Bx * Bx; // dot11 = dot(v1, v1)
|
| 140 |
+
Scalar dot12 = Bx * X; // dot12 = dot(v1, v2)
|
| 141 |
+
|
| 142 |
+
Scalar invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
| 143 |
+
Scalar u = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
| 144 |
+
Scalar v = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
| 145 |
+
|
| 146 |
+
//return (u >= 0) && (v >= 0) && (u + v < 1);
|
| 147 |
+
return (u >= 1e-10) && (v >= 1e-10) && (u + v < 1 - 1e-10);
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
} //geodesic
|
| 152 |
+
|
| 153 |
+
#endif //GEODESIC_CONSTANTS_20071231
|
cpp/tools/geodesic/geodesic_memory.h
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef _GEODESIC_MEMORY
|
| 2 |
+
#define _GEODESIC_MEMORY
|
| 3 |
+
|
| 4 |
+
//two fast and simple memory allocators
|
| 5 |
+
|
| 6 |
+
#include "../tools/Types.h"
|
| 7 |
+
#include <memory>
|
| 8 |
+
|
| 9 |
+
namespace geodesic{
|
| 10 |
+
|
| 11 |
+
template<class T> //quickly allocates multiple elements of a given type; no deallocation
|
| 12 |
+
class SimlpeMemoryAllocator
|
| 13 |
+
{
|
| 14 |
+
public:
|
| 15 |
+
typedef T* pointer;
|
| 16 |
+
|
| 17 |
+
SimlpeMemoryAllocator(unsigned block_size = 0,
|
| 18 |
+
unsigned max_number_of_blocks = 0)
|
| 19 |
+
{
|
| 20 |
+
reset(block_size,
|
| 21 |
+
max_number_of_blocks);
|
| 22 |
+
};
|
| 23 |
+
|
| 24 |
+
~SimlpeMemoryAllocator(){};
|
| 25 |
+
|
| 26 |
+
void reset(unsigned block_size,
|
| 27 |
+
unsigned max_number_of_blocks)
|
| 28 |
+
{
|
| 29 |
+
m_block_size = block_size;
|
| 30 |
+
m_max_number_of_blocks = max_number_of_blocks;
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
m_current_position = 0;
|
| 34 |
+
|
| 35 |
+
m_storage.reserve(max_number_of_blocks);
|
| 36 |
+
m_storage.resize(1);
|
| 37 |
+
m_storage[0].resize(block_size);
|
| 38 |
+
};
|
| 39 |
+
|
| 40 |
+
pointer allocate(unsigned const n) //allocate n units
|
| 41 |
+
{
|
| 42 |
+
assert(n < m_block_size);
|
| 43 |
+
|
| 44 |
+
if(m_current_position + n >= m_block_size)
|
| 45 |
+
{
|
| 46 |
+
m_storage.push_back( std::vector<T>() );
|
| 47 |
+
m_storage.back().resize(m_block_size);
|
| 48 |
+
m_current_position = 0;
|
| 49 |
+
}
|
| 50 |
+
pointer result = & m_storage.back()[m_current_position];
|
| 51 |
+
m_current_position += n;
|
| 52 |
+
|
| 53 |
+
return result;
|
| 54 |
+
};
|
| 55 |
+
private:
|
| 56 |
+
std::vector<std::vector<T> > m_storage;
|
| 57 |
+
unsigned m_block_size; //size of a single block
|
| 58 |
+
unsigned m_max_number_of_blocks; //maximum allowed number of blocks
|
| 59 |
+
unsigned m_current_position; //first unused element inside the current block
|
| 60 |
+
};
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
template<class T> //quickly allocates and deallocates single elements of a given type
|
| 64 |
+
class MemoryAllocator
|
| 65 |
+
{
|
| 66 |
+
public:
|
| 67 |
+
typedef T* pointer;
|
| 68 |
+
|
| 69 |
+
MemoryAllocator(unsigned block_size = 1024,
|
| 70 |
+
unsigned max_number_of_blocks = 1024)
|
| 71 |
+
{
|
| 72 |
+
reset(block_size,
|
| 73 |
+
max_number_of_blocks);
|
| 74 |
+
};
|
| 75 |
+
|
| 76 |
+
~MemoryAllocator(){};
|
| 77 |
+
|
| 78 |
+
void clear()
|
| 79 |
+
{
|
| 80 |
+
reset(m_block_size,
|
| 81 |
+
m_max_number_of_blocks);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
void reset(unsigned block_size,
|
| 85 |
+
unsigned max_number_of_blocks)
|
| 86 |
+
{
|
| 87 |
+
m_block_size = block_size;
|
| 88 |
+
m_max_number_of_blocks = max_number_of_blocks;
|
| 89 |
+
|
| 90 |
+
assert(m_block_size > 0);
|
| 91 |
+
assert(m_max_number_of_blocks > 0);
|
| 92 |
+
|
| 93 |
+
m_current_position = 0;
|
| 94 |
+
|
| 95 |
+
m_storage.reserve(max_number_of_blocks);
|
| 96 |
+
m_storage.resize(1);
|
| 97 |
+
m_storage[0].resize(block_size);
|
| 98 |
+
|
| 99 |
+
m_deleted.clear();
|
| 100 |
+
m_deleted.reserve(2*block_size);
|
| 101 |
+
};
|
| 102 |
+
|
| 103 |
+
pointer allocate() //allocates single unit of memory
|
| 104 |
+
{
|
| 105 |
+
pointer result;
|
| 106 |
+
if(m_deleted.empty())
|
| 107 |
+
{
|
| 108 |
+
if(m_current_position + 1 >= m_block_size)
|
| 109 |
+
{
|
| 110 |
+
m_storage.push_back( std::vector<T>() );
|
| 111 |
+
m_storage.back().resize(m_block_size);
|
| 112 |
+
m_current_position = 0;
|
| 113 |
+
}
|
| 114 |
+
result = & m_storage.back()[m_current_position];
|
| 115 |
+
++m_current_position;
|
| 116 |
+
}
|
| 117 |
+
else
|
| 118 |
+
{
|
| 119 |
+
result = m_deleted.back();
|
| 120 |
+
m_deleted.pop_back();
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
return result;
|
| 124 |
+
};
|
| 125 |
+
|
| 126 |
+
void deallocate(pointer p) //allocate n units
|
| 127 |
+
{
|
| 128 |
+
if(m_deleted.size() < m_deleted.capacity())
|
| 129 |
+
{
|
| 130 |
+
m_deleted.push_back(p);
|
| 131 |
+
}
|
| 132 |
+
};
|
| 133 |
+
|
| 134 |
+
private:
|
| 135 |
+
std::vector<std::vector<T> > m_storage;
|
| 136 |
+
unsigned m_block_size; //size of a single block
|
| 137 |
+
unsigned m_max_number_of_blocks; //maximum allowed number of blocks
|
| 138 |
+
unsigned m_current_position; //first unused element inside the current block
|
| 139 |
+
|
| 140 |
+
std::vector<pointer> m_deleted; //pointers to deleted elemets
|
| 141 |
+
};
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
class OutputBuffer
|
| 145 |
+
{
|
| 146 |
+
public:
|
| 147 |
+
OutputBuffer():
|
| 148 |
+
m_num_bytes(0)
|
| 149 |
+
{}
|
| 150 |
+
|
| 151 |
+
void clear()
|
| 152 |
+
{
|
| 153 |
+
m_num_bytes = 0;
|
| 154 |
+
m_buffer = std::auto_ptr<Scalar>();
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
template<class T>
|
| 158 |
+
T* allocate(unsigned n)
|
| 159 |
+
{
|
| 160 |
+
Scalar wanted = n*sizeof(T);
|
| 161 |
+
if(wanted > m_num_bytes)
|
| 162 |
+
{
|
| 163 |
+
unsigned new_size = (unsigned) ceil(wanted / (Scalar)sizeof(Scalar));
|
| 164 |
+
m_buffer = std::auto_ptr<Scalar>(new Scalar[new_size]);
|
| 165 |
+
m_num_bytes = new_size*sizeof(Scalar);
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
return (T*)m_buffer.get();
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
template <class T>
|
| 172 |
+
T* get()
|
| 173 |
+
{
|
| 174 |
+
return (T*)m_buffer.get();
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
template<class T>
|
| 178 |
+
unsigned capacity()
|
| 179 |
+
{
|
| 180 |
+
return (unsigned)floor((Scalar)m_num_bytes/(Scalar)sizeof(T));
|
| 181 |
+
};
|
| 182 |
+
|
| 183 |
+
private:
|
| 184 |
+
|
| 185 |
+
std::auto_ptr<Scalar> m_buffer;
|
| 186 |
+
unsigned m_num_bytes;
|
| 187 |
+
};
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
} //geodesic
|
| 191 |
+
|
| 192 |
+
#endif
|
cpp/tools/median.h
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef MEDIAN_H
|
| 2 |
+
#define MEDIAN_H
|
| 3 |
+
// This file is part of libigl, a simple c++ geometry processing library.
|
| 4 |
+
//
|
| 5 |
+
// Copyright (C) 2013 Alec Jacobson <[email protected]>
|
| 6 |
+
//
|
| 7 |
+
// This Source Code Form is subject to the terms of the Mozilla Public License
|
| 8 |
+
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
| 9 |
+
// obtain one at http://mozilla.org/MPL/2.0/.
|
| 10 |
+
#include <Eigen/Dense>
|
| 11 |
+
#include <vector>
|
| 12 |
+
namespace igl
|
| 13 |
+
{
|
| 14 |
+
template <typename DerivedM>
|
| 15 |
+
void matrix_to_list(const Eigen::DenseBase<DerivedM>& M,
|
| 16 |
+
std::vector<typename DerivedM::Scalar> &V)
|
| 17 |
+
{
|
| 18 |
+
using namespace std;
|
| 19 |
+
V.resize(M.size());
|
| 20 |
+
// loop over cols then rows
|
| 21 |
+
for(int j =0; j<M.cols();j++)
|
| 22 |
+
{
|
| 23 |
+
for(int i = 0; i < M.rows();i++)
|
| 24 |
+
{
|
| 25 |
+
V[i+j*M.rows()] = M(i,j);
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
// Compute the median of an eigen vector
|
| 30 |
+
//
|
| 31 |
+
// Inputs:
|
| 32 |
+
// V #V list of unsorted values
|
| 33 |
+
// Outputs:
|
| 34 |
+
// m median of those values
|
| 35 |
+
// Returns true on success, false on failure
|
| 36 |
+
template <typename DerivedV, typename mType>
|
| 37 |
+
bool median(
|
| 38 |
+
const Eigen::MatrixBase<DerivedV> & V, mType & m)
|
| 39 |
+
{
|
| 40 |
+
using namespace std;
|
| 41 |
+
if(V.size() == 0)
|
| 42 |
+
{
|
| 43 |
+
return false;
|
| 44 |
+
}
|
| 45 |
+
vector<typename DerivedV::Scalar> vV;
|
| 46 |
+
matrix_to_list(V,vV);
|
| 47 |
+
// http://stackoverflow.com/a/1719155/148668
|
| 48 |
+
size_t n = vV.size()/2;
|
| 49 |
+
nth_element(vV.begin(),vV.begin()+n,vV.end());
|
| 50 |
+
if(vV.size()%2==0)
|
| 51 |
+
{
|
| 52 |
+
nth_element(vV.begin(),vV.begin()+n-1,vV.end());
|
| 53 |
+
m = 0.5*(vV[n]+vV[n-1]);
|
| 54 |
+
}else
|
| 55 |
+
{
|
| 56 |
+
m = vV[n];
|
| 57 |
+
}
|
| 58 |
+
return true;
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
#endif
|
cpp/tools/nanoflann.h
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#pragma once
|
| 2 |
+
#include <nanoflann.hpp>
|
| 3 |
+
#include <vector>
|
| 4 |
+
#include <Eigen/Dense>
|
| 5 |
+
#include <iostream>
|
| 6 |
+
#include "Types.h"
|
| 7 |
+
|
| 8 |
+
//Collection of the nanoflann-related parts in the two methods
|
| 9 |
+
|
| 10 |
+
namespace nanoflann {
|
| 11 |
+
|
| 12 |
+
/// KD-tree adaptor for working with data directly stored in an Eigen Matrix, without duplicating the data storage.
|
| 13 |
+
/// This code is adapted from the KDTreeEigenMatrixAdaptor class of nanoflann.hpp
|
| 14 |
+
template <class MatrixType, int DIM = -1, class Distance = nanoflann::metric_L2, typename IndexType = int>
|
| 15 |
+
struct KDTreeAdaptor {
|
| 16 |
+
typedef KDTreeAdaptor<MatrixType, DIM, Distance> self_t;
|
| 17 |
+
typedef typename MatrixType::Scalar num_t;
|
| 18 |
+
typedef typename Distance::template traits<num_t, self_t>::distance_t metric_t;
|
| 19 |
+
typedef KDTreeSingleIndexAdaptor< metric_t, self_t, DIM, IndexType> index_t;
|
| 20 |
+
|
| 21 |
+
index_t* index;
|
| 22 |
+
const MatrixType &m_data_matrix;
|
| 23 |
+
|
| 24 |
+
KDTreeAdaptor(const MatrixType &mat, const int leaf_max_size = 10) : m_data_matrix(mat) {
|
| 25 |
+
const size_t dims = mat.rows();
|
| 26 |
+
index = new index_t(dims, *this, nanoflann::KDTreeSingleIndexAdaptorParams(leaf_max_size, dims));
|
| 27 |
+
index->buildIndex();
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
~KDTreeAdaptor() { delete index; }
|
| 31 |
+
|
| 32 |
+
/// Query for the num_closest closest points to a given point (entered as query_point[0:dim-1]).
|
| 33 |
+
inline void query(const num_t *query_point, const size_t num_closest, IndexType *out_indices, num_t *out_distances_sq) const {
|
| 34 |
+
nanoflann::KNNResultSet<num_t, IndexType> resultSet(num_closest);
|
| 35 |
+
resultSet.init(out_indices, out_distances_sq);
|
| 36 |
+
index->findNeighbors(resultSet, query_point, nanoflann::SearchParams());
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/// Query for the closest point to a given query.
|
| 40 |
+
inline IndexType closest(const num_t *query_point, num_t& out_distance_sq) const {
|
| 41 |
+
IndexType out_index;
|
| 42 |
+
query(query_point, 1, &out_index, &out_distance_sq);
|
| 43 |
+
return out_index;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
inline IndexType closest(const num_t *query_point) const {
|
| 48 |
+
num_t dummy;
|
| 49 |
+
return closest(query_point, dummy); // 调用上面的 2 参数版本
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
/// Radius search
|
| 53 |
+
inline size_t findInRadius(const num_t *query_point, const num_t radius, std::vector<std::pair<IndexType, num_t>>& out_idxdist) const {
|
| 54 |
+
out_idxdist.clear();
|
| 55 |
+
index->radiusSearch(query_point, radius, out_idxdist, nanoflann::SearchParams());
|
| 56 |
+
return out_idxdist.size();
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
const self_t & derived() const { return *this; }
|
| 60 |
+
self_t & derived() { return *this; }
|
| 61 |
+
|
| 62 |
+
inline size_t kdtree_get_point_count() const { return m_data_matrix.cols(); }
|
| 63 |
+
|
| 64 |
+
/// Returns the distance between the vector "p1[0:size-1]" and the data point with index "idx_p2" stored in the class:
|
| 65 |
+
inline num_t kdtree_distance(const num_t *p1, const size_t idx_p2, size_t size) const {
|
| 66 |
+
num_t s = 0;
|
| 67 |
+
for (size_t i = 0; i < size; i++) {
|
| 68 |
+
num_t d = p1[i] - m_data_matrix.coeff(i, idx_p2);
|
| 69 |
+
s += d * d;
|
| 70 |
+
}
|
| 71 |
+
return s;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/// Returns the dim'th component of the idx'th point in the class:
|
| 75 |
+
inline num_t kdtree_get_pt(const size_t idx, int dim) const {
|
| 76 |
+
return m_data_matrix.coeff(dim, idx);
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
/// Optional bounding-box computation
|
| 80 |
+
template <class BBOX> bool kdtree_get_bbox(BBOX&) const { return false; }
|
| 81 |
+
};
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
/// Adapter for std::vector<Eigen::Vector3d>
|
| 85 |
+
struct StdVecOfEigenVector3dRefAdapter {
|
| 86 |
+
public:
|
| 87 |
+
typedef double Scalar; // or float, depending on your Vector3 type
|
| 88 |
+
typedef Eigen::Vector3d Vector3;
|
| 89 |
+
|
| 90 |
+
StdVecOfEigenVector3dRefAdapter(const std::vector<Vector3>& pps) : pts(pps) {};
|
| 91 |
+
const std::vector<Vector3>& pts;
|
| 92 |
+
|
| 93 |
+
inline size_t kdtree_get_point_count() const { return pts.size(); }
|
| 94 |
+
|
| 95 |
+
inline Scalar kdtree_get_pt(const size_t idx, int dim) const {
|
| 96 |
+
if (dim == 0) return pts[idx].x();
|
| 97 |
+
else if (dim == 1) return pts[idx].y();
|
| 98 |
+
else return pts[idx].z();
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
template <class BBOX>
|
| 102 |
+
bool kdtree_get_bbox(BBOX&) const { return false; }
|
| 103 |
+
};
|
| 104 |
+
|
| 105 |
+
} // namespace nanoflann
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
typedef nanoflann::KDTreeAdaptor<Matrix3X, -1, nanoflann::metric_L2_Simple> KDtree;
|
cpp/tools/nodeSampler.cpp
ADDED
|
@@ -0,0 +1,638 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//#pragma once
|
| 2 |
+
#include "nodeSampler.h"
|
| 3 |
+
#include "tools.h"
|
| 4 |
+
#include "OmpHelper.h"
|
| 5 |
+
#include "geodesic_algorithm_exact.h"
|
| 6 |
+
#include "nanoflann.h"
|
| 7 |
+
|
| 8 |
+
namespace svr
|
| 9 |
+
{
|
| 10 |
+
// Define helper functions
|
| 11 |
+
static auto square = [](const Scalar argu) { return argu * argu; };
|
| 12 |
+
static auto cube = [](const Scalar argu) { return argu * argu * argu; };
|
| 13 |
+
static auto max = [](const Scalar lhs, const Scalar rhs) { return lhs > rhs ? lhs : rhs; };
|
| 14 |
+
|
| 15 |
+
//------------------------------------------------------------------------
|
| 16 |
+
// Node Sampling based on geodesic distance metric
|
| 17 |
+
//
|
| 18 |
+
// Note that this member function samples nodes along some axis.
|
| 19 |
+
// Each node is not covered by any other node. And distance between each
|
| 20 |
+
// pair of nodes is at least sampling radius.
|
| 21 |
+
//------------------------------------------------------------------------
|
| 22 |
+
// Local geodesic calculation
|
| 23 |
+
Scalar nodeSampler::SampleAndConstuctAxis(Mesh &mesh, Scalar sampleRadiusRatio, sampleAxis axis)
|
| 24 |
+
{
|
| 25 |
+
// Save numbers of vertex and edge
|
| 26 |
+
m_meshVertexNum = mesh.n_vertices();
|
| 27 |
+
m_meshEdgeNum = mesh.n_edges();
|
| 28 |
+
m_mesh = & mesh;
|
| 29 |
+
|
| 30 |
+
// Calculate average edge length of bound mesh
|
| 31 |
+
for (size_t i = 0; i < m_meshEdgeNum; ++i)
|
| 32 |
+
{
|
| 33 |
+
OpenMesh::EdgeHandle eh = mesh.edge_handle(i);
|
| 34 |
+
Scalar edgeLen = mesh.calc_edge_length(eh);
|
| 35 |
+
m_averageEdgeLen += edgeLen;
|
| 36 |
+
}
|
| 37 |
+
m_averageEdgeLen /= m_meshEdgeNum;
|
| 38 |
+
|
| 39 |
+
// Sampling radius is calculated as averageEdgeLen multiplied by sampleRadiusRatio
|
| 40 |
+
m_sampleRadius = sampleRadiusRatio * m_averageEdgeLen;
|
| 41 |
+
|
| 42 |
+
// Reorder mesh vertex along axis
|
| 43 |
+
std::vector<size_t> vertexReorderedAlongAxis(m_meshVertexNum);
|
| 44 |
+
size_t vertexIdx = 0;
|
| 45 |
+
std::generate(vertexReorderedAlongAxis.begin(), vertexReorderedAlongAxis.end(), [&vertexIdx]() -> size_t { return vertexIdx++; });
|
| 46 |
+
std::sort(vertexReorderedAlongAxis.begin(), vertexReorderedAlongAxis.end(), [&mesh, axis](const size_t &lhs, const size_t &rhs) -> bool {
|
| 47 |
+
size_t lhsIdx = lhs;
|
| 48 |
+
size_t rhsIdx = rhs;
|
| 49 |
+
OpenMesh::VertexHandle vhl = mesh.vertex_handle(lhsIdx);
|
| 50 |
+
OpenMesh::VertexHandle vhr = mesh.vertex_handle(rhsIdx);
|
| 51 |
+
Mesh::Point vl = mesh.point(vhl);
|
| 52 |
+
Mesh::Point vr = mesh.point(vhr);
|
| 53 |
+
return vl[axis] > vr[axis];
|
| 54 |
+
});
|
| 55 |
+
|
| 56 |
+
// Sample nodes using radius of m_sampleRadius
|
| 57 |
+
size_t firstVertexIdx = vertexReorderedAlongAxis[0];
|
| 58 |
+
VertexNodeIdx.resize(m_meshVertexNum);
|
| 59 |
+
VertexNodeIdx.setConstant(-1);
|
| 60 |
+
VertexNodeIdx[firstVertexIdx] = 0;
|
| 61 |
+
size_t cur_node_idx = 0;
|
| 62 |
+
|
| 63 |
+
m_vertexGraph.resize(m_meshVertexNum);
|
| 64 |
+
VectorX weight_sum = VectorX::Zero(m_meshVertexNum);
|
| 65 |
+
|
| 66 |
+
for (auto &vertexIdx : vertexReorderedAlongAxis)
|
| 67 |
+
{
|
| 68 |
+
if(VertexNodeIdx[vertexIdx] < 0 && m_vertexGraph.at(vertexIdx).empty())
|
| 69 |
+
{
|
| 70 |
+
m_nodeContainer.emplace_back(cur_node_idx, vertexIdx);
|
| 71 |
+
VertexNodeIdx[vertexIdx] = cur_node_idx;
|
| 72 |
+
|
| 73 |
+
std::vector<size_t> neighbor_verts;
|
| 74 |
+
geodesic::GeodesicAlgorithmExact geoalg(&mesh, vertexIdx, m_sampleRadius);
|
| 75 |
+
geoalg.propagate(vertexIdx, neighbor_verts);
|
| 76 |
+
for(size_t i = 0; i < neighbor_verts.size(); i++)
|
| 77 |
+
{
|
| 78 |
+
int neighIdx = neighbor_verts[i];
|
| 79 |
+
Scalar geodist = mesh.data(mesh.vertex_handle(neighIdx)).geodesic_distance;
|
| 80 |
+
if(geodist < m_sampleRadius)
|
| 81 |
+
{
|
| 82 |
+
Scalar weight = std::pow(1-std::pow(geodist/m_sampleRadius, 2), 3);
|
| 83 |
+
m_vertexGraph.at(neighIdx).emplace(std::pair<int, Scalar>(cur_node_idx, weight));
|
| 84 |
+
weight_sum[neighIdx] += weight;
|
| 85 |
+
}
|
| 86 |
+
}
|
| 87 |
+
cur_node_idx++;
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
m_nodeGraph.resize(cur_node_idx);
|
| 92 |
+
for (auto &vertexIdx : vertexReorderedAlongAxis)
|
| 93 |
+
{
|
| 94 |
+
for(auto &node: m_vertexGraph[vertexIdx])
|
| 95 |
+
{
|
| 96 |
+
size_t nodeIdx = node.first;
|
| 97 |
+
for(auto &neighNode: m_vertexGraph[vertexIdx])
|
| 98 |
+
{
|
| 99 |
+
size_t neighNodeIdx = neighNode.first;
|
| 100 |
+
if(nodeIdx != neighNodeIdx)
|
| 101 |
+
{
|
| 102 |
+
m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(neighNodeIdx, 1.0));
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
m_vertexGraph.at(vertexIdx).at(nodeIdx) /= weight_sum[vertexIdx];
|
| 106 |
+
}
|
| 107 |
+
}
|
| 108 |
+
return m_sampleRadius;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
//------------------------------------------------------------------------
|
| 112 |
+
// Node Sampling based on geodesic distance metric
|
| 113 |
+
//
|
| 114 |
+
// Note that this member function samples nodes along some axis.
|
| 115 |
+
// Each node is not covered by any other node. And distance between each
|
| 116 |
+
// pair of nodes is at least sampling radius.
|
| 117 |
+
//------------------------------------------------------------------------
|
| 118 |
+
// Local geodesic calculation
|
| 119 |
+
Scalar nodeSampler::SampleAndConstuct(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points)
|
| 120 |
+
{
|
| 121 |
+
// Save numbers of vertex and edge
|
| 122 |
+
m_meshVertexNum = mesh.n_vertices();
|
| 123 |
+
m_meshEdgeNum = mesh.n_edges();
|
| 124 |
+
m_mesh = & mesh;
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
// Calculate average edge length of bound mesh
|
| 128 |
+
for (size_t i = 0; i < m_meshEdgeNum; ++i)
|
| 129 |
+
{
|
| 130 |
+
OpenMesh::EdgeHandle eh = mesh.edge_handle(i);
|
| 131 |
+
Scalar edgeLen = mesh.calc_edge_length(eh);
|
| 132 |
+
m_averageEdgeLen += edgeLen;
|
| 133 |
+
}
|
| 134 |
+
m_averageEdgeLen /= m_meshEdgeNum;
|
| 135 |
+
|
| 136 |
+
// Sampling radius is calculated as averageEdgeLen multiplied by sampleRadiusRatio
|
| 137 |
+
m_sampleRadius = sampleRadiusRatio * m_averageEdgeLen;
|
| 138 |
+
|
| 139 |
+
// de-mean
|
| 140 |
+
Vector3 means = src_points.rowwise().mean();
|
| 141 |
+
Matrix3X demean_src_points = src_points.colwise() - means;
|
| 142 |
+
Matrix33 covariance = demean_src_points * demean_src_points.transpose();
|
| 143 |
+
|
| 144 |
+
Eigen::SelfAdjointEigenSolver<Matrix33> eigensolver(covariance);
|
| 145 |
+
Vector3 eigen_values = eigensolver.eigenvalues();
|
| 146 |
+
int max_idx = 2;
|
| 147 |
+
if(eigen_values[0] > eigen_values[max_idx]) max_idx = 0;
|
| 148 |
+
if(eigen_values[1] > eigen_values[max_idx]) max_idx = 1;
|
| 149 |
+
|
| 150 |
+
Vector3 main_axis = eigensolver.eigenvectors().col(max_idx);
|
| 151 |
+
|
| 152 |
+
VectorX projection = demean_src_points.transpose() * main_axis;
|
| 153 |
+
|
| 154 |
+
projection_indices.resize(projection.size());
|
| 155 |
+
projection_indices.setLinSpaced(projection.size(), 0, projection.size() - 1);
|
| 156 |
+
|
| 157 |
+
auto compare = [&projection](int i, int j) {
|
| 158 |
+
return projection(i) < projection(j);
|
| 159 |
+
};
|
| 160 |
+
|
| 161 |
+
std::sort(projection_indices.data(), projection_indices.data() + projection_indices.size(), compare);
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
// Sample nodes using radius of m_sampleRadius
|
| 165 |
+
size_t firstVertexIdx = projection_indices[0];
|
| 166 |
+
VertexNodeIdx.resize(m_meshVertexNum);
|
| 167 |
+
VertexNodeIdx.setConstant(-1);
|
| 168 |
+
VertexNodeIdx[firstVertexIdx] = 0;
|
| 169 |
+
size_t cur_node_idx = 0;
|
| 170 |
+
|
| 171 |
+
m_vertexGraph.resize(m_meshVertexNum);
|
| 172 |
+
VectorX weight_sum = VectorX::Zero(m_meshVertexNum);
|
| 173 |
+
|
| 174 |
+
for(int idx = 0; idx < projection_indices.size(); idx++)
|
| 175 |
+
{
|
| 176 |
+
int vertexIdx = projection_indices[idx];
|
| 177 |
+
if(VertexNodeIdx[vertexIdx] < 0 && m_vertexGraph.at(vertexIdx).empty())
|
| 178 |
+
{
|
| 179 |
+
m_nodeContainer.emplace_back(cur_node_idx, vertexIdx);
|
| 180 |
+
VertexNodeIdx[vertexIdx] = cur_node_idx;
|
| 181 |
+
|
| 182 |
+
std::vector<size_t> neighbor_verts;
|
| 183 |
+
geodesic::GeodesicAlgorithmExact geoalg(&mesh, vertexIdx, m_sampleRadius);
|
| 184 |
+
geoalg.propagate(vertexIdx, neighbor_verts);
|
| 185 |
+
for(size_t i = 0; i < neighbor_verts.size(); i++)
|
| 186 |
+
{
|
| 187 |
+
int neighIdx = neighbor_verts[i];
|
| 188 |
+
Scalar geodist = mesh.data(mesh.vertex_handle(neighIdx)).geodesic_distance;
|
| 189 |
+
if(geodist < m_sampleRadius)
|
| 190 |
+
{
|
| 191 |
+
Scalar weight = std::pow(1-std::pow(geodist/m_sampleRadius, 2), 3);
|
| 192 |
+
m_vertexGraph.at(neighIdx).emplace(std::pair<int, Scalar>(cur_node_idx, weight));
|
| 193 |
+
weight_sum[neighIdx] += weight;
|
| 194 |
+
}
|
| 195 |
+
}
|
| 196 |
+
cur_node_idx++;
|
| 197 |
+
}
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
m_nodeGraph.resize(cur_node_idx);
|
| 201 |
+
for(int idx = 0; idx < projection_indices.size(); idx++)
|
| 202 |
+
{
|
| 203 |
+
int vertexIdx = projection_indices[idx];
|
| 204 |
+
for(auto &node: m_vertexGraph[vertexIdx])
|
| 205 |
+
{
|
| 206 |
+
size_t nodeIdx = node.first;
|
| 207 |
+
for(auto &neighNode: m_vertexGraph[vertexIdx])
|
| 208 |
+
{
|
| 209 |
+
size_t neighNodeIdx = neighNode.first;
|
| 210 |
+
if(nodeIdx != neighNodeIdx)
|
| 211 |
+
{
|
| 212 |
+
m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(neighNodeIdx, 1.0));
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
m_vertexGraph.at(vertexIdx).at(nodeIdx) /= weight_sum[vertexIdx];
|
| 216 |
+
}
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
// // check neighbors
|
| 222 |
+
// for(int nodeIdx = 0; nodeIdx < cur_node_idx; nodeIdx++)
|
| 223 |
+
// {
|
| 224 |
+
// int num_neighbors = m_nodeGraph[nodeIdx].size();
|
| 225 |
+
// // for (auto &eachNeighbor : m_nodeGraph[nodeIdx])
|
| 226 |
+
// // {
|
| 227 |
+
// // num_neighbors++;
|
| 228 |
+
// // }
|
| 229 |
+
// if(num_neighbors<2)
|
| 230 |
+
// {
|
| 231 |
+
|
| 232 |
+
// VectorX dists(cur_node_idx);
|
| 233 |
+
// #pragma omp parallel for
|
| 234 |
+
// for(int ni = 0; ni < cur_node_idx; ni++)
|
| 235 |
+
// {
|
| 236 |
+
// int vidx0 = getNodeVertexIdx(nodeIdx);
|
| 237 |
+
// int vidx1 = getNodeVertexIdx(ni);
|
| 238 |
+
// Scalar dist = (src_points.col(vidx0) - src_points.col(vidx1)).squaredNorm();
|
| 239 |
+
// dists[ni] = dist;
|
| 240 |
+
// }
|
| 241 |
+
// dists[nodeIdx] = 1e10;
|
| 242 |
+
// for(int k = 0; k < 6; k++)
|
| 243 |
+
// {
|
| 244 |
+
// int min_idx = -1;
|
| 245 |
+
// dists.minCoeff(&min_idx);
|
| 246 |
+
// m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(min_idx, 1.0));
|
| 247 |
+
// dists[min_idx] = 1e10;
|
| 248 |
+
// }
|
| 249 |
+
// }
|
| 250 |
+
// }
|
| 251 |
+
// Eigen::VectorXi vnn(cur_node_idx);
|
| 252 |
+
// // check vertex neighbors
|
| 253 |
+
// for(int vidx = 0; vidx < cur_node_idx; vidx++)
|
| 254 |
+
// {
|
| 255 |
+
// // std::cout << "vidx.neighbor_node = " << m_vertexGraph.at(vidx).size() << std::endl;
|
| 256 |
+
// vnn[vidx] = m_nodeGraph.at(vidx).size();
|
| 257 |
+
// }
|
| 258 |
+
// std::cout << "v neighbor max = " << vnn.maxCoeff() << " min = " << vnn.minCoeff() << std::endl;
|
| 259 |
+
return m_sampleRadius;
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
Scalar nodeSampler::SampleAndConstuctForSrcPoints(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points, const Eigen::MatrixXi & src_knn_indices)
|
| 264 |
+
{
|
| 265 |
+
// Save numbers of vertex and edge
|
| 266 |
+
m_meshVertexNum = src_points.cols();
|
| 267 |
+
int knn_num_neighbor = src_knn_indices.rows();
|
| 268 |
+
m_meshEdgeNum = m_meshVertexNum*knn_num_neighbor;
|
| 269 |
+
m_mesh = & mesh;
|
| 270 |
+
|
| 271 |
+
// Calculate average edge length of bound mesh
|
| 272 |
+
for (size_t i = 0; i < m_meshVertexNum; ++i)
|
| 273 |
+
{
|
| 274 |
+
for(size_t j = 0; j < knn_num_neighbor; j++)
|
| 275 |
+
{
|
| 276 |
+
Scalar edgeLen = (src_points.col(i) - src_points.col(src_knn_indices(j, i))).norm();
|
| 277 |
+
m_averageEdgeLen += edgeLen;
|
| 278 |
+
}
|
| 279 |
+
}
|
| 280 |
+
m_averageEdgeLen /= m_meshEdgeNum;
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
// Sampling radius is calculated as averageEdgeLen multiplied by sampleRadiusRatio
|
| 284 |
+
m_sampleRadius = sampleRadiusRatio * m_averageEdgeLen;
|
| 285 |
+
|
| 286 |
+
// de-mean
|
| 287 |
+
Vector3 means = src_points.rowwise().mean();
|
| 288 |
+
Matrix3X demean_src_points = src_points.colwise() - means;
|
| 289 |
+
Matrix33 covariance = demean_src_points * demean_src_points.transpose();
|
| 290 |
+
|
| 291 |
+
Eigen::SelfAdjointEigenSolver<Matrix33> eigensolver(covariance);
|
| 292 |
+
Vector3 eigen_values = eigensolver.eigenvalues();
|
| 293 |
+
int max_idx = 2;
|
| 294 |
+
if(eigen_values[0] > eigen_values[max_idx]) max_idx = 0;
|
| 295 |
+
if(eigen_values[1] > eigen_values[max_idx]) max_idx = 1;
|
| 296 |
+
|
| 297 |
+
Vector3 main_axis = eigensolver.eigenvectors().col(max_idx);
|
| 298 |
+
|
| 299 |
+
VectorX projection = demean_src_points.transpose() * main_axis;
|
| 300 |
+
|
| 301 |
+
projection_indices.resize(projection.size());
|
| 302 |
+
projection_indices.setLinSpaced(projection.size(), 0, projection.size() - 1);
|
| 303 |
+
|
| 304 |
+
auto compare = [&projection](int i, int j) {
|
| 305 |
+
return projection(i) < projection(j);
|
| 306 |
+
};
|
| 307 |
+
|
| 308 |
+
std::sort(projection_indices.data(), projection_indices.data() + projection_indices.size(), compare);
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
// Sample nodes using radius of m_sampleRadius
|
| 312 |
+
size_t firstVertexIdx = projection_indices[0];
|
| 313 |
+
VertexNodeIdx.resize(m_meshVertexNum);
|
| 314 |
+
VertexNodeIdx.setConstant(-1);
|
| 315 |
+
VertexNodeIdx[firstVertexIdx] = 0;
|
| 316 |
+
size_t cur_node_idx = 0;
|
| 317 |
+
|
| 318 |
+
m_vertexGraph.resize(m_meshVertexNum);
|
| 319 |
+
VectorX weight_sum = VectorX::Zero(m_meshVertexNum);
|
| 320 |
+
|
| 321 |
+
for(int idx = 0; idx < projection_indices.size(); idx++)
|
| 322 |
+
{
|
| 323 |
+
int vertexIdx = projection_indices[idx];
|
| 324 |
+
if(VertexNodeIdx[vertexIdx] < 0 && m_vertexGraph.at(vertexIdx).empty())
|
| 325 |
+
{
|
| 326 |
+
m_nodeContainer.emplace_back(cur_node_idx, vertexIdx);
|
| 327 |
+
VertexNodeIdx[vertexIdx] = cur_node_idx;
|
| 328 |
+
|
| 329 |
+
std::queue<size_t> neighbor_verts;
|
| 330 |
+
neighbor_verts.push(vertexIdx);
|
| 331 |
+
Eigen::VectorXi visited(m_meshVertexNum);
|
| 332 |
+
visited.setZero();
|
| 333 |
+
visited[vertexIdx] = 1;
|
| 334 |
+
while(!neighbor_verts.empty())
|
| 335 |
+
{
|
| 336 |
+
size_t vidx = neighbor_verts.front();
|
| 337 |
+
neighbor_verts.pop();
|
| 338 |
+
Scalar dist = (src_points.col(vertexIdx) - src_points.col(vidx)).norm();
|
| 339 |
+
|
| 340 |
+
if(dist < m_sampleRadius)
|
| 341 |
+
{
|
| 342 |
+
Scalar weight = std::pow(1-std::pow(dist/m_sampleRadius, 2), 3);
|
| 343 |
+
m_vertexGraph.at(vidx).emplace(std::pair<int, Scalar>(cur_node_idx, weight));
|
| 344 |
+
weight_sum[vidx] += weight;
|
| 345 |
+
for(int j = 0; j < knn_num_neighbor; j++)
|
| 346 |
+
{
|
| 347 |
+
int neighbor_vidx = src_knn_indices(j, vidx);
|
| 348 |
+
if(visited[neighbor_vidx]==0)
|
| 349 |
+
{
|
| 350 |
+
neighbor_verts.push(neighbor_vidx);
|
| 351 |
+
visited[neighbor_vidx]= 1;
|
| 352 |
+
}
|
| 353 |
+
}
|
| 354 |
+
}
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
cur_node_idx++;
|
| 358 |
+
}
|
| 359 |
+
}
|
| 360 |
+
|
| 361 |
+
m_nodeGraph.resize(cur_node_idx);
|
| 362 |
+
for(int idx = 0; idx < projection_indices.size(); idx++)
|
| 363 |
+
{
|
| 364 |
+
int vertexIdx = projection_indices[idx];
|
| 365 |
+
for(auto &node: m_vertexGraph[vertexIdx])
|
| 366 |
+
{
|
| 367 |
+
size_t nodeIdx = node.first;
|
| 368 |
+
for(auto &neighNode: m_vertexGraph[vertexIdx])
|
| 369 |
+
{
|
| 370 |
+
size_t neighNodeIdx = neighNode.first;
|
| 371 |
+
if(nodeIdx != neighNodeIdx)
|
| 372 |
+
{
|
| 373 |
+
m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(neighNodeIdx, 1.0));
|
| 374 |
+
}
|
| 375 |
+
}
|
| 376 |
+
m_vertexGraph.at(vertexIdx).at(nodeIdx) /= weight_sum[vertexIdx];
|
| 377 |
+
}
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
// // check neighbors
|
| 381 |
+
// for(int nodeIdx = 0; nodeIdx < cur_node_idx; nodeIdx++)
|
| 382 |
+
// {
|
| 383 |
+
// int num_neighbors = 0;
|
| 384 |
+
// for (auto &eachNeighbor : m_nodeGraph[nodeIdx])
|
| 385 |
+
// {
|
| 386 |
+
// num_neighbors++;
|
| 387 |
+
// }
|
| 388 |
+
// if(num_neighbors==0)
|
| 389 |
+
// {
|
| 390 |
+
|
| 391 |
+
// VectorX dists(cur_node_idx);
|
| 392 |
+
// #pragma omp parallel for
|
| 393 |
+
// for(int ni = 0; ni < cur_node_idx; ni++)
|
| 394 |
+
// {
|
| 395 |
+
// int vidx0 = getNodeVertexIdx(nodeIdx);
|
| 396 |
+
// int vidx1 = getNodeNeighborSize(ni);
|
| 397 |
+
// Scalar dist = (src_points.col(vidx0) - src_points.col(vidx1)).squaredNorm();
|
| 398 |
+
// dists[ni] = dist;
|
| 399 |
+
// }
|
| 400 |
+
// dists[nodeIdx] = 1e10;
|
| 401 |
+
// int min_idx = -1;
|
| 402 |
+
// dists.minCoeff(&min_idx);
|
| 403 |
+
// m_nodeGraph.at(nodeIdx).emplace(std::pair<int, Scalar>(min_idx, 1.0));
|
| 404 |
+
// }
|
| 405 |
+
// }
|
| 406 |
+
return m_sampleRadius;
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
Scalar nodeSampler::SampleAndConstuctFPS(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points, const Eigen::MatrixXi & src_knn_indices, int num_vn, int num_nn)
|
| 411 |
+
{
|
| 412 |
+
m_meshVertexNum = src_points.cols();
|
| 413 |
+
int knn_num_neighbor = src_knn_indices.rows();
|
| 414 |
+
m_meshEdgeNum = m_meshVertexNum*knn_num_neighbor;
|
| 415 |
+
m_mesh = & mesh;
|
| 416 |
+
|
| 417 |
+
// Calculate average edge length of bound mesh
|
| 418 |
+
for (size_t i = 0; i < m_meshVertexNum; ++i)
|
| 419 |
+
{
|
| 420 |
+
for(size_t j = 0; j < knn_num_neighbor; j++)
|
| 421 |
+
{
|
| 422 |
+
Scalar edgeLen = (src_points.col(i) - src_points.col(src_knn_indices(j, i))).norm();
|
| 423 |
+
m_averageEdgeLen += edgeLen;
|
| 424 |
+
}
|
| 425 |
+
}
|
| 426 |
+
m_averageEdgeLen /= m_meshEdgeNum;
|
| 427 |
+
|
| 428 |
+
// Sampling radius is calculated as averageEdgeLen multiplied by sampleRadiusRatio
|
| 429 |
+
m_sampleRadius = sampleRadiusRatio * m_averageEdgeLen;
|
| 430 |
+
|
| 431 |
+
// start points
|
| 432 |
+
size_t startIndex = 0;
|
| 433 |
+
// FPS to get sampling points in align term
|
| 434 |
+
VectorX minDistances(m_meshVertexNum);
|
| 435 |
+
minDistances.setConstant(std::numeric_limits<Scalar>::max());
|
| 436 |
+
minDistances[startIndex] = 0;
|
| 437 |
+
m_nodeContainer.emplace_back(startIndex, 0);
|
| 438 |
+
|
| 439 |
+
Scalar minimal_dist = 1e10;
|
| 440 |
+
int cur_node_idx = 1;
|
| 441 |
+
// repeat select farthest points
|
| 442 |
+
while (minimal_dist > m_sampleRadius) {
|
| 443 |
+
// calculate the distance between each point with the sampling points set.
|
| 444 |
+
#pragma omp parallel for
|
| 445 |
+
for (size_t i = 0; i < m_meshVertexNum; ++i) {
|
| 446 |
+
if(i==startIndex)
|
| 447 |
+
continue;
|
| 448 |
+
|
| 449 |
+
Scalar dist = (src_points.col(startIndex) - src_points.col(i)).norm();
|
| 450 |
+
if(dist < minDistances[i])
|
| 451 |
+
minDistances[i] = dist;
|
| 452 |
+
}
|
| 453 |
+
|
| 454 |
+
// choose farthest point
|
| 455 |
+
int maxDistanceIndex;
|
| 456 |
+
minimal_dist = minDistances.maxCoeff(&maxDistanceIndex);
|
| 457 |
+
minDistances[maxDistanceIndex] = 0;
|
| 458 |
+
|
| 459 |
+
// add the farthest point into the sampling points set.
|
| 460 |
+
startIndex = maxDistanceIndex;
|
| 461 |
+
m_nodeContainer.emplace_back(cur_node_idx, startIndex);
|
| 462 |
+
cur_node_idx++;
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
Matrix3X node_positions(3, cur_node_idx);
|
| 466 |
+
for(int i = 0; i < cur_node_idx; i++)
|
| 467 |
+
{
|
| 468 |
+
int vidx = getNodeVertexIdx(i);
|
| 469 |
+
node_positions.col(i) = src_points.col(vidx);
|
| 470 |
+
}
|
| 471 |
+
KDtree node_tree(node_positions);
|
| 472 |
+
|
| 473 |
+
// For each vertex, find num_vn-closest nodes
|
| 474 |
+
m_vertexGraph.resize(m_meshVertexNum);
|
| 475 |
+
VectorX weight_sum(m_meshVertexNum);
|
| 476 |
+
weight_sum.setZero();
|
| 477 |
+
#pragma omp parallel for
|
| 478 |
+
for(int vidx = 0; vidx < m_meshVertexNum; vidx++)
|
| 479 |
+
{
|
| 480 |
+
std::vector<int> out_indices(num_vn);
|
| 481 |
+
std::vector<Scalar> out_dists(num_vn);
|
| 482 |
+
node_tree.query(src_points.col(vidx).data(), num_vn, out_indices.data(), out_dists.data());
|
| 483 |
+
for(int k = 0; k < num_vn; k++)
|
| 484 |
+
{
|
| 485 |
+
Scalar weight = std::pow(1-std::pow(out_dists[k]/m_sampleRadius, 2), 3);
|
| 486 |
+
m_vertexGraph.at(vidx).emplace(std::pair<int, Scalar>(out_indices[k], weight));
|
| 487 |
+
weight_sum[vidx] += weight;
|
| 488 |
+
}
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
#pragma omp parallel for
|
| 492 |
+
for(int vidx = 0; vidx < m_meshVertexNum; vidx++)
|
| 493 |
+
{
|
| 494 |
+
for(auto &neighNode: m_vertexGraph[vidx])
|
| 495 |
+
{
|
| 496 |
+
size_t neighNodeIdx = neighNode.first;
|
| 497 |
+
m_vertexGraph.at(vidx).at(neighNodeIdx) /= weight_sum[vidx];
|
| 498 |
+
}
|
| 499 |
+
}
|
| 500 |
+
|
| 501 |
+
|
| 502 |
+
// For each node, find num_nn-closest nodes
|
| 503 |
+
m_nodeGraph.resize(cur_node_idx);
|
| 504 |
+
#pragma omp parallel for
|
| 505 |
+
for(int nidx = 0; nidx < cur_node_idx; nidx++)
|
| 506 |
+
{
|
| 507 |
+
std::vector<int> out_indices(num_nn+1);
|
| 508 |
+
std::vector<Scalar> out_dists(num_nn+1);
|
| 509 |
+
int vidx = getNodeVertexIdx(nidx);
|
| 510 |
+
node_tree.query(src_points.col(vidx).data(), num_nn+1, out_indices.data(), out_dists.data());
|
| 511 |
+
for(int k = 0; k < num_nn; k++)
|
| 512 |
+
{
|
| 513 |
+
m_nodeGraph.at(nidx).emplace(std::pair<int, Scalar>(out_indices[k+1], 1.0));
|
| 514 |
+
}
|
| 515 |
+
}
|
| 516 |
+
return m_sampleRadius;
|
| 517 |
+
}
|
| 518 |
+
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
|
| 523 |
+
void nodeSampler::initWeight(RowMajorSparseMatrix& matPV, VectorX & matP, RowMajorSparseMatrix& matB, VectorX& matD, VectorX& smoothw)
|
| 524 |
+
{
|
| 525 |
+
Timer time;
|
| 526 |
+
std::vector<Eigen::Triplet<Scalar>> coeff;
|
| 527 |
+
matP.setZero();
|
| 528 |
+
Eigen::VectorXi nonzero_num = Eigen::VectorXi::Zero(m_mesh->n_vertices());
|
| 529 |
+
// data coeff
|
| 530 |
+
for (size_t vertexIdx = 0; vertexIdx < m_meshVertexNum; ++vertexIdx)
|
| 531 |
+
{
|
| 532 |
+
Mesh::Point vi = m_mesh->point(m_mesh->vertex_handle(vertexIdx));
|
| 533 |
+
for (auto &eachNeighbor : m_vertexGraph[vertexIdx])
|
| 534 |
+
{
|
| 535 |
+
size_t nodeIdx = eachNeighbor.first;
|
| 536 |
+
Scalar weight = m_vertexGraph.at(vertexIdx).at(nodeIdx);
|
| 537 |
+
Mesh::Point pj = m_mesh->point(m_mesh->vertex_handle(getNodeVertexIdx(nodeIdx)));
|
| 538 |
+
|
| 539 |
+
for (int k = 0; k < 3; k++)
|
| 540 |
+
{
|
| 541 |
+
coeff.push_back(Eigen::Triplet<Scalar>(3 * vertexIdx + k, nodeIdx * 12 + k, weight * (vi[0] - pj[0])));
|
| 542 |
+
coeff.push_back(Eigen::Triplet<Scalar>(3 * vertexIdx + k, nodeIdx * 12 + k + 3, weight * (vi[1] - pj[1])));
|
| 543 |
+
coeff.push_back(Eigen::Triplet<Scalar>(3 * vertexIdx + k, nodeIdx * 12 + k + 6, weight * (vi[2] - pj[2])));
|
| 544 |
+
coeff.push_back(Eigen::Triplet<Scalar>(3 * vertexIdx + k, nodeIdx * 12 + k + 9, weight * 1.0));
|
| 545 |
+
}
|
| 546 |
+
|
| 547 |
+
matP[vertexIdx * 3] += weight * pj[0];
|
| 548 |
+
matP[vertexIdx * 3 + 1] += weight * pj[1];
|
| 549 |
+
matP[vertexIdx * 3 + 2] += weight * pj[2];
|
| 550 |
+
}
|
| 551 |
+
nonzero_num[vertexIdx] = m_vertexGraph[vertexIdx].size();
|
| 552 |
+
}
|
| 553 |
+
matPV.setFromTriplets(coeff.begin(), coeff.end());
|
| 554 |
+
|
| 555 |
+
|
| 556 |
+
// smooth coeff
|
| 557 |
+
coeff.clear();
|
| 558 |
+
int max_edge_num = nodeSize() * (nodeSize()-1);
|
| 559 |
+
matB.resize(max_edge_num * 3, 12 * nodeSize());
|
| 560 |
+
matD.resize(max_edge_num * 3);
|
| 561 |
+
smoothw.resize(max_edge_num*3);
|
| 562 |
+
smoothw.setZero();
|
| 563 |
+
matD.setZero();
|
| 564 |
+
int edge_id = 0;
|
| 565 |
+
|
| 566 |
+
for (size_t nodeIdx = 0; nodeIdx < m_nodeContainer.size(); ++nodeIdx)
|
| 567 |
+
{
|
| 568 |
+
size_t vIdx0 = getNodeVertexIdx(nodeIdx);
|
| 569 |
+
Mesh::VertexHandle vh0 = m_mesh->vertex_handle(vIdx0);
|
| 570 |
+
Mesh::Point v0 = m_mesh->point(vh0);
|
| 571 |
+
|
| 572 |
+
for (auto &eachNeighbor : m_nodeGraph[nodeIdx])
|
| 573 |
+
{
|
| 574 |
+
size_t neighborIdx = eachNeighbor.first;
|
| 575 |
+
size_t vIdx1 = getNodeVertexIdx(neighborIdx);
|
| 576 |
+
Mesh::Point v1 = m_mesh->point(m_mesh->vertex_handle(vIdx1));
|
| 577 |
+
Mesh::Point dv = v0 - v1;
|
| 578 |
+
int k = edge_id;
|
| 579 |
+
|
| 580 |
+
for (int t = 0; t < 3; t++)
|
| 581 |
+
{
|
| 582 |
+
coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, neighborIdx * 12 + t, dv[0]));
|
| 583 |
+
coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, neighborIdx * 12 + t + 3, dv[1]));
|
| 584 |
+
coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, neighborIdx * 12 + t + 6, dv[2]));
|
| 585 |
+
coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, neighborIdx * 12 + t + 9, 1.0));
|
| 586 |
+
coeff.push_back(Eigen::Triplet<Scalar>(k * 3 + t, nodeIdx * 12 + t + 9, -1.0));
|
| 587 |
+
}
|
| 588 |
+
|
| 589 |
+
Scalar dist = dv.norm();
|
| 590 |
+
if(dist > 0)
|
| 591 |
+
{
|
| 592 |
+
smoothw[k * 3] = smoothw[k * 3 + 1] = smoothw[k * 3 + 2] = 1.0 / dist;
|
| 593 |
+
}
|
| 594 |
+
else
|
| 595 |
+
{
|
| 596 |
+
//smoothw[k * 3] = 0.0;
|
| 597 |
+
std::cout << "node repeat";
|
| 598 |
+
exit(1);
|
| 599 |
+
}
|
| 600 |
+
matD[k * 3] = dv[0];
|
| 601 |
+
matD[k * 3 + 1] = dv[1];
|
| 602 |
+
matD[k * 3 + 2] = dv[2];
|
| 603 |
+
edge_id++;
|
| 604 |
+
}
|
| 605 |
+
}
|
| 606 |
+
matB.setFromTriplets(coeff.begin(), coeff.end());
|
| 607 |
+
matD.conservativeResize(edge_id*3);
|
| 608 |
+
matB.conservativeResize(edge_id*3, matPV.cols());
|
| 609 |
+
smoothw.conservativeResize(edge_id*3);
|
| 610 |
+
smoothw *= edge_id/(smoothw.sum()/3.0);
|
| 611 |
+
}
|
| 612 |
+
|
| 613 |
+
|
| 614 |
+
void nodeSampler::print_nodes(Mesh & mesh, std::string file_path)
|
| 615 |
+
{
|
| 616 |
+
std::string namev = file_path + "nodes.obj";
|
| 617 |
+
std::ofstream out1(namev);
|
| 618 |
+
for (size_t i = 0; i < m_nodeContainer.size(); i++)
|
| 619 |
+
{
|
| 620 |
+
int vexid = m_nodeContainer[i].second;
|
| 621 |
+
out1 << "v " << mesh.point(mesh.vertex_handle(vexid))[0] << " " << mesh.point(mesh.vertex_handle(vexid))[1]
|
| 622 |
+
<< " " << mesh.point(mesh.vertex_handle(vexid))[2] << std::endl;
|
| 623 |
+
}
|
| 624 |
+
Eigen::VectorXi nonzero_num = Eigen::VectorXi::Zero(m_nodeContainer.size());
|
| 625 |
+
for (size_t nodeIdx = 0; nodeIdx < m_nodeContainer.size(); ++nodeIdx)
|
| 626 |
+
{
|
| 627 |
+
for (auto &eachNeighbor : m_nodeGraph[nodeIdx])
|
| 628 |
+
{
|
| 629 |
+
size_t neighborIdx = eachNeighbor.first;
|
| 630 |
+
out1 << "l " << nodeIdx+1 << " " << neighborIdx+1 << std::endl;
|
| 631 |
+
}
|
| 632 |
+
nonzero_num[nodeIdx] = m_nodeGraph[nodeIdx].size();
|
| 633 |
+
}
|
| 634 |
+
std::cout << "node neighbor min = " << nonzero_num.minCoeff() << " max = "
|
| 635 |
+
<< nonzero_num.maxCoeff() << " average = " << nonzero_num.mean() << std::endl;
|
| 636 |
+
out1.close();
|
| 637 |
+
}
|
| 638 |
+
}
|
cpp/tools/nodeSampler.h
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef NODESAMPLER_H
|
| 2 |
+
#define NODESAMPLER_H
|
| 3 |
+
#include "Types.h"
|
| 4 |
+
|
| 5 |
+
namespace svr
|
| 6 |
+
{
|
| 7 |
+
//------------------------------------------------------------------------
|
| 8 |
+
// Define neighborIter class
|
| 9 |
+
//------------------------------------------------------------------------
|
| 10 |
+
class neighborIter
|
| 11 |
+
{
|
| 12 |
+
public:
|
| 13 |
+
neighborIter(const std::map<size_t, Scalar> &nodeNeighbors)
|
| 14 |
+
{
|
| 15 |
+
m_neighborIter = nodeNeighbors.begin();
|
| 16 |
+
m_neighborEnd = nodeNeighbors.end();
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
neighborIter& operator++()
|
| 20 |
+
{
|
| 21 |
+
if (m_neighborIter != m_neighborEnd)
|
| 22 |
+
++m_neighborIter;
|
| 23 |
+
return *this;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
neighborIter operator++(int)
|
| 27 |
+
{
|
| 28 |
+
neighborIter tempIter(*this);
|
| 29 |
+
++*this;
|
| 30 |
+
return tempIter;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
const std::pair<const size_t, Scalar>& operator*() { return *m_neighborIter; }
|
| 34 |
+
std::map<size_t, Scalar>::const_iterator operator->() { return m_neighborIter; }
|
| 35 |
+
bool is_valid() { return m_neighborIter != m_neighborEnd; }
|
| 36 |
+
size_t getIndex() { return m_neighborIter->first; }
|
| 37 |
+
Scalar getWeight() { return m_neighborIter->second; }
|
| 38 |
+
|
| 39 |
+
private:
|
| 40 |
+
std::map<size_t, Scalar>::const_iterator m_neighborIter;
|
| 41 |
+
std::map<size_t, Scalar>::const_iterator m_neighborEnd;
|
| 42 |
+
};
|
| 43 |
+
|
| 44 |
+
//------------------------------------------------------------------------
|
| 45 |
+
// Define node sampling class
|
| 46 |
+
//------------------------------------------------------------------------
|
| 47 |
+
class nodeSampler
|
| 48 |
+
{
|
| 49 |
+
public:
|
| 50 |
+
enum sampleAxis { X_AXIS, Y_AXIS, Z_AXIS };
|
| 51 |
+
nodeSampler() {};
|
| 52 |
+
|
| 53 |
+
// return sample radius
|
| 54 |
+
Scalar SampleAndConstuct(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points);
|
| 55 |
+
Scalar SampleAndConstuctAxis(Mesh &mesh, Scalar sampleRadiusRatio, sampleAxis axis);
|
| 56 |
+
|
| 57 |
+
Scalar SampleAndConstuctForSrcPoints(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points, const Eigen::MatrixXi & src_knn_indices);
|
| 58 |
+
Scalar SampleAndConstuctFPS(Mesh &mesh, Scalar sampleRadiusRatio, const Matrix3X & src_points, const Eigen::MatrixXi & src_knn_indices, int num_vn, int num_nn);
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
void updateWeight(Mesh &mesh);
|
| 62 |
+
void constructGraph(bool is_uniform);
|
| 63 |
+
|
| 64 |
+
size_t nodeSize() const { return m_nodeContainer.size(); }
|
| 65 |
+
|
| 66 |
+
neighborIter getNodeNodeIter(size_t nodeIdx) const { return neighborIter(m_nodeGraph[nodeIdx]); }
|
| 67 |
+
neighborIter getVertexNodeIter(size_t vertexIdx) const { return neighborIter(m_vertexGraph[vertexIdx]); }
|
| 68 |
+
size_t getNodeVertexIdx(size_t nodeIdx) const { return m_nodeContainer.at(nodeIdx).second; }
|
| 69 |
+
|
| 70 |
+
size_t getVertexNeighborSize(size_t vertexIdx) const { return m_vertexGraph.at(vertexIdx).size(); }
|
| 71 |
+
size_t getNodeNeighborSize(size_t nodeIdx) const { return m_nodeGraph.at(nodeIdx).size(); }
|
| 72 |
+
void initWeight(RowMajorSparseMatrix& matPV, VectorX & matP,
|
| 73 |
+
RowMajorSparseMatrix& matB, VectorX& matD, VectorX& smoothw);
|
| 74 |
+
void print_nodes(Mesh & mesh, std::string file_path);
|
| 75 |
+
|
| 76 |
+
private:
|
| 77 |
+
size_t m_meshVertexNum = 0;
|
| 78 |
+
size_t m_meshEdgeNum = 0;
|
| 79 |
+
Scalar m_averageEdgeLen = 0.0f;
|
| 80 |
+
Scalar m_sampleRadius = 0.0f;
|
| 81 |
+
std::vector<Scalar> non_unisamples_Radius;
|
| 82 |
+
Mesh * m_mesh;
|
| 83 |
+
|
| 84 |
+
public:
|
| 85 |
+
std::vector<std::pair<size_t, size_t>> m_nodeContainer;
|
| 86 |
+
std::vector<std::map<size_t, Scalar>> m_vertexGraph; // vertex node graph
|
| 87 |
+
std::vector<std::map<size_t, Scalar>> m_nodeGraph;
|
| 88 |
+
std::vector<VectorX> m_geoDistContainer;
|
| 89 |
+
Eigen::VectorXi VertexNodeIdx;
|
| 90 |
+
Eigen::VectorXi projection_indices;
|
| 91 |
+
|
| 92 |
+
};
|
| 93 |
+
}
|
| 94 |
+
#endif
|
cpp/tools/parameters.h
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef PARAMETERS_H
|
| 2 |
+
#define PARAMETERS_H
|
| 3 |
+
|
| 4 |
+
#include "Types.h"
|
| 5 |
+
|
| 6 |
+
//Parameter repository for different methods
|
| 7 |
+
|
| 8 |
+
// parameters for FRICP
|
| 9 |
+
namespace ICP{
|
| 10 |
+
struct Parameters {
|
| 11 |
+
double p; /// paramter of the robust function/// para k
|
| 12 |
+
int max_icp; /// max ICP iteration
|
| 13 |
+
double stop; /// stopping criteria
|
| 14 |
+
std::string out_path;
|
| 15 |
+
int anderson_m;
|
| 16 |
+
MatrixXX init_trans;
|
| 17 |
+
MatrixXX gt_trans;
|
| 18 |
+
bool has_groundtruth;
|
| 19 |
+
double convergence_energy;
|
| 20 |
+
double convergence_gt_mse;
|
| 21 |
+
MatrixXX res_trans;
|
| 22 |
+
double nu_begin_k;
|
| 23 |
+
double nu_end_k;
|
| 24 |
+
bool use_init;
|
| 25 |
+
double nu_alpha;
|
| 26 |
+
Parameters() :
|
| 27 |
+
p(0.1),
|
| 28 |
+
max_icp(100),
|
| 29 |
+
stop(1e-5),
|
| 30 |
+
anderson_m(5),
|
| 31 |
+
has_groundtruth(false),
|
| 32 |
+
convergence_energy(0.0),
|
| 33 |
+
convergence_gt_mse(0.0),
|
| 34 |
+
nu_begin_k(3),
|
| 35 |
+
nu_end_k(1.0 / (3.0 * sqrt(3.0))),
|
| 36 |
+
use_init(false),
|
| 37 |
+
nu_alpha(1.0 / 2)
|
| 38 |
+
{
|
| 39 |
+
gt_trans = Eigen::Matrix4d::Identity();
|
| 40 |
+
init_trans = Eigen::MatrixXd(); // 或指定大小
|
| 41 |
+
res_trans = Eigen::MatrixXd();
|
| 42 |
+
}
|
| 43 |
+
};
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
namespace spare{
|
| 47 |
+
// parameters for spare non-rigid registration
|
| 48 |
+
struct Parameters
|
| 49 |
+
{
|
| 50 |
+
int max_outer_iters; // nonrigid max iters
|
| 51 |
+
Scalar w_smo; // smoothness weight
|
| 52 |
+
Scalar w_rot; // rotation matrix weight
|
| 53 |
+
Scalar w_arap_coarse; // ARAP weight for coarse alignment
|
| 54 |
+
Scalar w_arap_fine; // ARAP weight for fine alignment
|
| 55 |
+
bool use_landmark;
|
| 56 |
+
bool calc_gt_err; // calculate ground truth error (DEBUG)
|
| 57 |
+
std::vector<int> landmark_src;
|
| 58 |
+
std::vector<int> landmark_tar;
|
| 59 |
+
Scalar Data_nu;
|
| 60 |
+
Scalar Data_initk;
|
| 61 |
+
Scalar stop_coarse;
|
| 62 |
+
Scalar stop_fine;
|
| 63 |
+
// Sample para
|
| 64 |
+
Scalar uni_sample_radio; // uniform sample radio
|
| 65 |
+
bool use_geodesic_dist;
|
| 66 |
+
int num_sample_nodes;
|
| 67 |
+
// record the initial error
|
| 68 |
+
Scalar init_gt_mean_errs;
|
| 69 |
+
Scalar init_gt_max_errs;
|
| 70 |
+
Scalar mesh_scale;
|
| 71 |
+
Parameters() // default
|
| 72 |
+
{
|
| 73 |
+
max_outer_iters = 30;
|
| 74 |
+
w_smo = 0.01; // smooth
|
| 75 |
+
w_rot = 1e-4; // orth
|
| 76 |
+
w_arap_coarse = 500; // 10;
|
| 77 |
+
w_arap_fine = 200;
|
| 78 |
+
use_landmark = false;
|
| 79 |
+
calc_gt_err = true;
|
| 80 |
+
Data_nu = 0.0;
|
| 81 |
+
Data_initk = 1;
|
| 82 |
+
stop_coarse = 1e-3;
|
| 83 |
+
stop_fine = 1e-4;
|
| 84 |
+
// Sample para
|
| 85 |
+
uni_sample_radio = 10;
|
| 86 |
+
use_geodesic_dist = true;
|
| 87 |
+
init_gt_mean_errs = .0;
|
| 88 |
+
}
|
| 89 |
+
};
|
| 90 |
+
};
|
| 91 |
+
|
| 92 |
+
#endif // PARAMETERS_H
|
cpp/tools/robust_norm.h
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef ROBUST_NORM_H_
|
| 2 |
+
#define ROBUST_NORM_H_
|
| 3 |
+
|
| 4 |
+
#include "registration.h"
|
| 5 |
+
|
| 6 |
+
inline void welsch_weight(Eigen::VectorXd& r, double p)
|
| 7 |
+
{
|
| 8 |
+
#pragma omp parallel for
|
| 9 |
+
for (int i = 0; i < r.size(); ++i)
|
| 10 |
+
{
|
| 11 |
+
r[i] = (r[i] >= 0) ? std::exp(-r[i] / (2 * p * p)) : 0.0;
|
| 12 |
+
}
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
inline double welsch_energy(const Eigen::VectorXd& r, double p)
|
| 16 |
+
{
|
| 17 |
+
double energy = 0.0;
|
| 18 |
+
#pragma omp parallel for reduction(+:energy)
|
| 19 |
+
for (int i = 0; i < r.size(); ++i)
|
| 20 |
+
{
|
| 21 |
+
energy += 1.0 - std::exp(-r[i] / (2 * p * p));
|
| 22 |
+
}
|
| 23 |
+
return energy;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
inline void robust_weight(Eigen::VectorXd& r, double p)
|
| 27 |
+
{
|
| 28 |
+
welsch_weight(r, p);
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
inline double get_energy(const Eigen::VectorXd& r, double p)
|
| 32 |
+
{
|
| 33 |
+
return welsch_energy(r, p);
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
#endif
|
cpp/tools/tools.cpp
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "tools.h"
|
| 2 |
+
#include <iostream>
|
| 3 |
+
#include "pch.h"
|
| 4 |
+
#ifdef __linux__
|
| 5 |
+
#include <unistd.h>
|
| 6 |
+
#include <sys/types.h>
|
| 7 |
+
#include <sys/stat.h>
|
| 8 |
+
#endif
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
Scalar mesh_scaling(Mesh& src_mesh, Mesh& tar_mesh)
|
| 12 |
+
{
|
| 13 |
+
Vec3 max(-1e12, -1e12, -1e12);
|
| 14 |
+
Vec3 min(1e12, 1e12, 1e12);
|
| 15 |
+
for(auto it = src_mesh.vertices_begin(); it != src_mesh.vertices_end(); it++)
|
| 16 |
+
{
|
| 17 |
+
for(int j = 0; j < 3; j++)
|
| 18 |
+
{
|
| 19 |
+
if(src_mesh.point(*it)[j] > max[j])
|
| 20 |
+
{
|
| 21 |
+
max[j] = src_mesh.point(*it)[j];
|
| 22 |
+
}
|
| 23 |
+
if(src_mesh.point(*it)[j] < min[j])
|
| 24 |
+
{
|
| 25 |
+
min[j] = src_mesh.point(*it)[j];
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
for(auto it = tar_mesh.vertices_begin(); it != tar_mesh.vertices_end(); it++)
|
| 31 |
+
{
|
| 32 |
+
for(int j = 0; j < 3; j++)
|
| 33 |
+
{
|
| 34 |
+
if(tar_mesh.point(*it)[j] > max[j])
|
| 35 |
+
{
|
| 36 |
+
max[j] = tar_mesh.point(*it)[j];
|
| 37 |
+
}
|
| 38 |
+
if(tar_mesh.point(*it)[j] < min[j])
|
| 39 |
+
{
|
| 40 |
+
min[j] = tar_mesh.point(*it)[j];
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
Scalar scale = (max-min).norm();
|
| 45 |
+
|
| 46 |
+
for(auto it = src_mesh.vertices_begin(); it != src_mesh.vertices_end(); it++)
|
| 47 |
+
{
|
| 48 |
+
Vec3 p = src_mesh.point(*it);
|
| 49 |
+
p = p/scale;
|
| 50 |
+
src_mesh.set_point(*it, p);
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
for(auto it = tar_mesh.vertices_begin(); it != tar_mesh.vertices_end(); it++)
|
| 54 |
+
{
|
| 55 |
+
Vec3 p = tar_mesh.point(*it);
|
| 56 |
+
p = p/scale;
|
| 57 |
+
tar_mesh.set_point(*it, p);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
return scale;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
Vector3 Vec2Eigen(Vec3 s)
|
| 66 |
+
{
|
| 67 |
+
return Vector3(s[0], s[1], s[2]);
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
#ifdef __linux__
|
| 72 |
+
bool my_mkdir(std::string file_path)
|
| 73 |
+
{
|
| 74 |
+
if(access(file_path.c_str(), 06))
|
| 75 |
+
{
|
| 76 |
+
std::cout << "file_path : (" << file_path << ") didn't exist or no write ability!!" << std::endl;
|
| 77 |
+
if(mkdir(file_path.c_str(), S_IRWXU))
|
| 78 |
+
{
|
| 79 |
+
std::cout << "mkdir " << file_path << " is wrong! please check upper path " << std::endl;
|
| 80 |
+
exit(0);
|
| 81 |
+
}
|
| 82 |
+
std::cout<< "mkdir " << file_path << " success!! " << std::endl;
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
#endif
|
cpp/tools/tools.h
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#ifndef TOOL_H_
|
| 2 |
+
#define TOOL_H_
|
| 3 |
+
#include "Types.h"
|
| 4 |
+
// Convert Mesh to libigl format to calculate geodesic distance
|
| 5 |
+
|
| 6 |
+
Scalar mesh_scaling(Mesh& src_mesh, Mesh& tar_mesh);
|
| 7 |
+
Vector3 Vec2Eigen(Vec3 s);
|
| 8 |
+
#ifdef __linux__
|
| 9 |
+
bool my_mkdir(std::string file_path);
|
| 10 |
+
#endif
|
| 11 |
+
|
| 12 |
+
#endif
|
docs/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
0
|
examples/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
0
|
examples/c++/CMakeLists.txt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmake_minimum_required(VERSION 3.16)
|
| 2 |
+
project(MyExternalApp LANGUAGES CXX)
|
| 3 |
+
|
| 4 |
+
set(CMAKE_CXX_STANDARD 17)
|
| 5 |
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
| 6 |
+
if(NOT CMAKE_BUILD_TYPE)
|
| 7 |
+
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
|
| 8 |
+
endif()
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
add_subdirectory(002hugging_face)
|
| 14 |
+
add_executable(MyApp main.cpp)
|
| 15 |
+
target_link_libraries(MyApp PRIVATE Lightweight3DRegLib)
|
| 16 |
+
|
examples/c++/main.cpp
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <filesystem>
|
| 2 |
+
#include <iostream>
|
| 3 |
+
#include <string>
|
| 4 |
+
|
| 5 |
+
#include <OpenMesh/Core/IO/MeshIO.hh>
|
| 6 |
+
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
|
| 7 |
+
#include <Eigen/Dense>
|
| 8 |
+
|
| 9 |
+
#include "nonrigid_spare_registration.h"
|
| 10 |
+
#include "rigid_fricp_registration.h"
|
| 11 |
+
#include "Types.h"
|
| 12 |
+
|
| 13 |
+
namespace fs = std::filesystem;
|
| 14 |
+
typedef OpenMesh::TriMesh_ArrayKernelT<> MeshType;
|
| 15 |
+
using Matrix3X = Eigen::Matrix<double, 3, Eigen::Dynamic>;
|
| 16 |
+
|
| 17 |
+
// ---------------------- 辅助函数 ----------------------
|
| 18 |
+
bool EnsureDirectoryExists(const std::string& path)
|
| 19 |
+
{
|
| 20 |
+
if (!fs::exists(path)) {
|
| 21 |
+
if (!fs::create_directories(path)) {
|
| 22 |
+
std::cerr << "Failed to create directory: " << path << std::endl;
|
| 23 |
+
return false;
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
return true;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
bool ReadMeshVerticesNormals(const std::string& filename, Matrix3X& vertices, Matrix3X& normals)
|
| 30 |
+
{
|
| 31 |
+
MeshType mesh;
|
| 32 |
+
OpenMesh::IO::Options opt = OpenMesh::IO::Options::VertexNormal;
|
| 33 |
+
|
| 34 |
+
if (!OpenMesh::IO::read_mesh(mesh, filename, opt)) {
|
| 35 |
+
std::cerr << "Failed to read mesh: " << filename << std::endl;
|
| 36 |
+
return false;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
// 如果没有法向量,自动计算
|
| 40 |
+
if (!mesh.has_vertex_normals()) {
|
| 41 |
+
mesh.request_face_normals();
|
| 42 |
+
mesh.request_vertex_normals();
|
| 43 |
+
mesh.update_normals();
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
size_t n_vertices = mesh.n_vertices();
|
| 47 |
+
if (n_vertices == 0) {
|
| 48 |
+
std::cerr << "Error: mesh has no vertices!" << std::endl;
|
| 49 |
+
return false;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
vertices.resize(3, n_vertices);
|
| 53 |
+
normals.resize(3, n_vertices);
|
| 54 |
+
|
| 55 |
+
for (auto v : mesh.vertices()) {
|
| 56 |
+
auto point = mesh.point(v);
|
| 57 |
+
auto normal = mesh.normal(v);
|
| 58 |
+
vertices.col(v.idx()) << point[0], point[1], point[2];
|
| 59 |
+
normals.col(v.idx()) << normal[0], normal[1], normal[2];
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
return true;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
// ---------------------- main ----------------------
|
| 66 |
+
int main()
|
| 67 |
+
{
|
| 68 |
+
// 定义输出路径
|
| 69 |
+
std::string outpath0 = "./outpath0/";
|
| 70 |
+
std::string outpath1 = "./outpath1/";
|
| 71 |
+
std::string outpath2 = "./outpath2/";
|
| 72 |
+
|
| 73 |
+
// 创建输出文件夹
|
| 74 |
+
for (const auto& path : {outpath0, outpath1, outpath2}) {
|
| 75 |
+
if (!EnsureDirectoryExists(path)) return -1;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
Matrix3X target_points, target_normals;
|
| 79 |
+
Matrix3X source_points, source_normals;
|
| 80 |
+
Matrix3X deformed_points;
|
| 81 |
+
|
| 82 |
+
// ------------------- 直接从文件注册 -------------------
|
| 83 |
+
{
|
| 84 |
+
RigidFricpRegistration fricp;
|
| 85 |
+
NonrigidSpareRegistration spare;
|
| 86 |
+
|
| 87 |
+
fricp.Reg("./data/target.ply","./data/source.ply", outpath0);
|
| 88 |
+
spare.Reg("./data/target.obj","./data/source.obj", outpath0);
|
| 89 |
+
} // fricp 和 spare 对象作用域结束,自动销毁
|
| 90 |
+
|
| 91 |
+
// ------------------- 带参数注册 -------------------
|
| 92 |
+
{
|
| 93 |
+
RigidFricpRegistration fricp;
|
| 94 |
+
NonrigidSpareRegistration spare;
|
| 95 |
+
|
| 96 |
+
fricp.Paras_init(false,"",80,1e-5);
|
| 97 |
+
fricp.Reg("./data/target.ply","./data/source.ply", outpath1);
|
| 98 |
+
|
| 99 |
+
spare.Paras_init(50,1e-4,1e-5);
|
| 100 |
+
spare.Reg("./data/target.obj","./data/source.obj", outpath1);
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
// ------------------- 使用顶点和法向量直接注册 -------------------
|
| 104 |
+
if (!ReadMeshVerticesNormals("./data/target.ply", target_points, target_normals)) return -1;
|
| 105 |
+
if (!ReadMeshVerticesNormals("./data/source.ply", source_points, source_normals)) return -1;
|
| 106 |
+
|
| 107 |
+
{
|
| 108 |
+
RigidFricpRegistration fricp;
|
| 109 |
+
fricp.Read_data(target_points, source_points, target_normals, source_normals);
|
| 110 |
+
fricp.Register();
|
| 111 |
+
deformed_points = fricp.deformed_points_3X_;
|
| 112 |
+
fricp.Output_data(outpath2, "fricp");
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
if (!ReadMeshVerticesNormals("./data/target.obj", target_points, target_normals)) return -1;
|
| 116 |
+
if (!ReadMeshVerticesNormals("./data/source.obj", source_points, source_normals)) return -1;
|
| 117 |
+
|
| 118 |
+
{
|
| 119 |
+
NonrigidSpareRegistration spare;
|
| 120 |
+
spare.Read_data(target_points, source_points, target_normals, source_normals);
|
| 121 |
+
spare.Register();
|
| 122 |
+
deformed_points = spare.deformed_points_3X_;
|
| 123 |
+
spare.Output_data(outpath2, "spare");
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
std::cout << "Registration finished successfully!" << std::endl;
|
| 127 |
+
return 0;
|
| 128 |
+
}
|
examples/data/fricp/fricpm3reg_pc.ply
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/data/fricp/fricpm3trans.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
0.804418 0.359408 -0.473008 -0.156803
|
| 2 |
+
-0.509781 0.826441 -0.238997 0.480169
|
| 3 |
+
0.305016 0.433384 0.848023 0.0139896
|
| 4 |
+
0 0 0 1
|
examples/data/fricp/source.ply
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/data/fricp/target.ply
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/data/spare/source.obj
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/data/spare/target.obj
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
examples/python/example_python.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import numpy as np
|
| 3 |
+
import trimesh
|
| 4 |
+
from plyfile import PlyData
|
| 5 |
+
import pyregister
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def read_vertices_normals(file_path):
|
| 9 |
+
"""
|
| 10 |
+
Read vertices and normals from a point cloud or mesh file.
|
| 11 |
+
Supports formats such as .ply, .obj, .xyz, etc.
|
| 12 |
+
"""
|
| 13 |
+
ext = os.path.splitext(file_path)[1].lower()
|
| 14 |
+
|
| 15 |
+
if ext == ".ply":
|
| 16 |
+
plydata = PlyData.read(file_path)
|
| 17 |
+
vertex_data = plydata['vertex']
|
| 18 |
+
vertices = np.vstack([vertex_data['x'], vertex_data['y'], vertex_data['z']]).astype(np.float64)
|
| 19 |
+
|
| 20 |
+
normals = None
|
| 21 |
+
if {'nx', 'ny', 'nz'}.issubset(vertex_data.data.dtype.names):
|
| 22 |
+
normals = np.vstack([vertex_data['nx'], vertex_data['ny'], vertex_data['nz']]).astype(np.float64)
|
| 23 |
+
|
| 24 |
+
else:
|
| 25 |
+
mesh = trimesh.load(file_path, process=False)
|
| 26 |
+
vertices = mesh.vertices.T.astype(np.float64)
|
| 27 |
+
normals = getattr(mesh, "vertex_normals", None)
|
| 28 |
+
if normals is not None and len(normals) > 0:
|
| 29 |
+
normals = normals.T.astype(np.float64)
|
| 30 |
+
else:
|
| 31 |
+
normals = np.empty((0, 0))
|
| 32 |
+
|
| 33 |
+
return vertices, normals
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def run_rigid_file(file_target, file_source, output_path):
|
| 37 |
+
"""
|
| 38 |
+
Perform rigid registration (FRICP method) by directly reading files.
|
| 39 |
+
|
| 40 |
+
Parameters
|
| 41 |
+
----------
|
| 42 |
+
file_target : str
|
| 43 |
+
Path to the target point cloud or mesh file.
|
| 44 |
+
file_source : str
|
| 45 |
+
Path to the source point cloud or mesh file.
|
| 46 |
+
output_path : str
|
| 47 |
+
Output file path for the registered result.
|
| 48 |
+
"""
|
| 49 |
+
print(f"\n[RIGID-FILE] Registering {file_source} → {file_target}")
|
| 50 |
+
reg = pyregister.RigidFricpRegistration()
|
| 51 |
+
reg.Paras_init(useinit=False, maxiter=100, stop=1e-5)
|
| 52 |
+
reg.Reg(file_target, file_source, output_path)
|
| 53 |
+
print(f"[RIGID-FILE] Completed → {output_path}")
|
| 54 |
+
print("Deformed points shape:", reg.deformed_points_3X_.shape)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def run_rigid_numpy(target_pts, source_pts, target_n, source_n, output_path):
|
| 58 |
+
"""
|
| 59 |
+
Perform rigid registration (FRICP method) using NumPy matrices.
|
| 60 |
+
|
| 61 |
+
Parameters
|
| 62 |
+
----------
|
| 63 |
+
target_pts : np.ndarray, shape (3, N)
|
| 64 |
+
Target point cloud coordinates.
|
| 65 |
+
source_pts : np.ndarray, shape (3, N)
|
| 66 |
+
Source point cloud coordinates.
|
| 67 |
+
target_n : np.ndarray, shape (3, N) or (0, 0)
|
| 68 |
+
Target normals.
|
| 69 |
+
source_n : np.ndarray, shape (3, N) or (0, 0)
|
| 70 |
+
Source normals.
|
| 71 |
+
output_path : str
|
| 72 |
+
Output file path for the registered result.
|
| 73 |
+
"""
|
| 74 |
+
print(f"\n[RIGID-NUMPY] Registering from NumPy matrices")
|
| 75 |
+
reg = pyregister.RigidFricpRegistration()
|
| 76 |
+
reg.Paras_init()
|
| 77 |
+
reg.Read_data(target_pts, source_pts, target_n, source_n)
|
| 78 |
+
reg.Register()
|
| 79 |
+
reg.Output_data(output_path, "FRICP")
|
| 80 |
+
print(f"[RIGID-NUMPY] Completed → {output_path}")
|
| 81 |
+
print("Deformed points shape:", reg.deformed_points_3X_.shape)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def run_nonrigid_file(file_target, file_source, output_path):
|
| 85 |
+
"""
|
| 86 |
+
Perform non-rigid registration (SPARE method) by directly reading files.
|
| 87 |
+
|
| 88 |
+
Parameters
|
| 89 |
+
----------
|
| 90 |
+
file_target : str
|
| 91 |
+
Path to the target point cloud or mesh file.
|
| 92 |
+
file_source : str
|
| 93 |
+
Path to the source point cloud or mesh file.
|
| 94 |
+
output_path : str
|
| 95 |
+
Output file path for the registered result.
|
| 96 |
+
"""
|
| 97 |
+
print(f"\n[NONRIGID-FILE] Registering {file_source} → {file_target}")
|
| 98 |
+
reg = pyregister.NonrigidSpareRegistration()
|
| 99 |
+
reg.Paras_init(iters = 30 ,stopcoarse=1e-3,stopfine=1e-4, uselandmark = False);
|
| 100 |
+
reg.Reg(file_target, file_source, output_path)
|
| 101 |
+
print(f"[NONRIGID-FILE] Completed → {output_path}")
|
| 102 |
+
print("Deformed points shape:", reg.deformed_points_3X_.shape)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def run_nonrigid_numpy(target_pts, source_pts, target_n, source_n, output_path):
|
| 107 |
+
"""
|
| 108 |
+
Perform non-rigid registration (SPARE method) using NumPy matrices.
|
| 109 |
+
|
| 110 |
+
Parameters
|
| 111 |
+
----------
|
| 112 |
+
target_pts : np.ndarray, shape (3, N)
|
| 113 |
+
Target point cloud coordinates.
|
| 114 |
+
source_pts : np.ndarray, shape (3, N)
|
| 115 |
+
Source point cloud coordinates.
|
| 116 |
+
target_n : np.ndarray, shape (3, N) or (0, 0)
|
| 117 |
+
Target normals.
|
| 118 |
+
source_n : np.ndarray, shape (3, N) or (0, 0)
|
| 119 |
+
Source normals.
|
| 120 |
+
output_path : str
|
| 121 |
+
Output file path for the registered result.
|
| 122 |
+
"""
|
| 123 |
+
print(f"\n[NONRIGID-NUMPY] Registering from NumPy matrices")
|
| 124 |
+
reg = pyregister.NonrigidSpareRegistration()
|
| 125 |
+
reg.Paras_init()
|
| 126 |
+
reg.Read_data(target_pts, source_pts, target_n, source_n)
|
| 127 |
+
reg.Register()
|
| 128 |
+
reg.Output_data(output_path, "SPARE")
|
| 129 |
+
print(f"[NONRIGID-NUMPY] Completed → {output_path}")
|
| 130 |
+
print("Deformed points shape:", reg.deformed_points_3X_.shape)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def main():
|
| 135 |
+
folder = "data"
|
| 136 |
+
output_folder = os.path.join(folder, "results_register")
|
| 137 |
+
os.makedirs(output_folder, exist_ok=True)
|
| 138 |
+
target_ply = os.path.join(folder, "target.ply")
|
| 139 |
+
|
| 140 |
+
source_ply = os.path.join(folder, "source.ply")
|
| 141 |
+
|
| 142 |
+
target_obj = os.path.join(folder, "target.obj")
|
| 143 |
+
|
| 144 |
+
source_obj = os.path.join(folder, "source.obj")
|
| 145 |
+
|
| 146 |
+
# Rigid registration (file mode)
|
| 147 |
+
if os.path.exists(target_ply) and os.path.exists(source_ply):
|
| 148 |
+
run_rigid_file(target_ply, source_ply, os.path.join(output_folder, "rigid_file_result"))
|
| 149 |
+
|
| 150 |
+
# Load into NumPy and run in-memory registration
|
| 151 |
+
target_pts, target_n = read_vertices_normals(target_ply)
|
| 152 |
+
source_pts, source_n = read_vertices_normals(source_ply)
|
| 153 |
+
target_n = target_n if target_n is not None else np.empty((0, 0))
|
| 154 |
+
source_n = source_n if source_n is not None else np.empty((0, 0))
|
| 155 |
+
run_rigid_numpy(target_pts, source_pts, target_n, source_n,
|
| 156 |
+
os.path.join(output_folder, "rigid_numpy_result"))
|
| 157 |
+
|
| 158 |
+
# Non-rigid registration (file mode)
|
| 159 |
+
if os.path.exists( target_obj ) and os.path.exists(source_obj):
|
| 160 |
+
run_nonrigid_file( target_obj , source_obj, os.path.join(output_folder, "nonrigid_file_result"))
|
| 161 |
+
|
| 162 |
+
# Load into NumPy and run in-memory registration
|
| 163 |
+
target_pts, target_n = read_vertices_normals( target_obj )
|
| 164 |
+
source_pts, source_n = read_vertices_normals(source_obj)
|
| 165 |
+
target_n = target_n if target_n is not None else np.empty((0, 0))
|
| 166 |
+
source_n = source_n if source_n is not None else np.empty((0, 0))
|
| 167 |
+
run_nonrigid_numpy(target_pts, source_pts, target_n, source_n,
|
| 168 |
+
os.path.join(output_folder, "nonrigid_numpy_result"))
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
if __name__ == "__main__":
|
| 172 |
+
main()
|
output/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
0
|
python/pyregister.cpp
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include "rigid_fricp_registration.h"
|
| 2 |
+
#include "nonrigid_spare_registration.h"
|
| 3 |
+
#include "registration.h"
|
| 4 |
+
#include <pybind11/pybind11.h>
|
| 5 |
+
#include <pybind11/eigen.h>
|
| 6 |
+
#include <pybind11/stl.h> // ✅ 支持 std::vector<int>
|
| 7 |
+
#include <Eigen/Dense>
|
| 8 |
+
|
| 9 |
+
namespace py = pybind11;
|
| 10 |
+
|
| 11 |
+
// ======================= 模块定义 ==========================
|
| 12 |
+
PYBIND11_MODULE(pyregister, m) {
|
| 13 |
+
|
| 14 |
+
// -------- Registration 基类 --------
|
| 15 |
+
py::class_<Registration>(m, "Registration")
|
| 16 |
+
.def("Read_data",
|
| 17 |
+
py::overload_cast<const std::string&, const std::string&>(&Registration::Read_data),
|
| 18 |
+
py::arg("file_target"),
|
| 19 |
+
py::arg("file_source"))
|
| 20 |
+
.def("Read_data",
|
| 21 |
+
[](Registration &self,
|
| 22 |
+
const Eigen::Ref<const Eigen::MatrixXd> &target_p,
|
| 23 |
+
const Eigen::Ref<const Eigen::MatrixXd> &source_p,
|
| 24 |
+
const Eigen::Ref<const Eigen::MatrixXd> &target_n,
|
| 25 |
+
const Eigen::Ref<const Eigen::MatrixXd> &source_n)
|
| 26 |
+
{
|
| 27 |
+
std::cout << "[pybind] entering Registration::Read_data" << std::endl;
|
| 28 |
+
|
| 29 |
+
Matrix3X tp = target_p.topRows(3);
|
| 30 |
+
Matrix3X sp = source_p.topRows(3);
|
| 31 |
+
Matrix3X tn = target_n.topRows(3);
|
| 32 |
+
Matrix3X sn = source_n.topRows(3);
|
| 33 |
+
|
| 34 |
+
self.Read_data(tp, sp, tn, sn);
|
| 35 |
+
|
| 36 |
+
std::cout << "[pybind] leaving Registration::Read_data" << std::endl;
|
| 37 |
+
},
|
| 38 |
+
py::arg("target_p"),
|
| 39 |
+
py::arg("source_p"),
|
| 40 |
+
py::arg("target_n") = Eigen::MatrixXd(),
|
| 41 |
+
py::arg("source_n") = Eigen::MatrixXd()
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
.def("Register", &Registration::Register)
|
| 46 |
+
.def("Output_data", &Registration::Output_data,
|
| 47 |
+
py::arg("out_path"),
|
| 48 |
+
py::arg("method_name"));
|
| 49 |
+
|
| 50 |
+
// -------- 暴露 spare::Parameters --------
|
| 51 |
+
py::class_<spare::Parameters>(m, "SpareParameters")
|
| 52 |
+
.def(py::init<>())
|
| 53 |
+
.def_readwrite("use_landmark", &spare::Parameters::use_landmark)
|
| 54 |
+
.def_readwrite("landmark_src", &spare::Parameters::landmark_src)
|
| 55 |
+
.def_readwrite("landmark_tar", &spare::Parameters::landmark_tar);
|
| 56 |
+
|
| 57 |
+
// -------- Rigid FRICP Registration --------
|
| 58 |
+
py::class_<RigidFricpRegistration, Registration>(m, "RigidFricpRegistration")
|
| 59 |
+
.def(py::init<>())
|
| 60 |
+
.def("Reg", &RigidFricpRegistration::Reg,
|
| 61 |
+
py::arg("file_target"),
|
| 62 |
+
py::arg("file_source"),
|
| 63 |
+
py::arg("out_path"))
|
| 64 |
+
.def("Paras_init", &RigidFricpRegistration::Paras_init,
|
| 65 |
+
py::arg("useinit") = false,
|
| 66 |
+
py::arg("fileinit") = " ",
|
| 67 |
+
py::arg("maxiter") = 100,
|
| 68 |
+
py::arg("stop") = 1e-5)
|
| 69 |
+
.def_readwrite("deformed_points_3X_", &RigidFricpRegistration::deformed_points_3X_);
|
| 70 |
+
|
| 71 |
+
// -------- Nonrigid Spare Registration --------
|
| 72 |
+
py::class_<NonrigidSpareRegistration, Registration>(m, "NonrigidSpareRegistration")
|
| 73 |
+
.def(py::init<>())
|
| 74 |
+
.def("Reg", &NonrigidSpareRegistration::Reg,
|
| 75 |
+
py::arg("file_target"),
|
| 76 |
+
py::arg("file_source"),
|
| 77 |
+
py::arg("out_path"))
|
| 78 |
+
.def("Paras_init", &NonrigidSpareRegistration::Paras_init,
|
| 79 |
+
py::arg("iters") = 30,
|
| 80 |
+
py::arg("stopcoarse") = 1e-3,
|
| 81 |
+
py::arg("stopfine") = 1e-4,
|
| 82 |
+
py::arg("uselandmark") = false,
|
| 83 |
+
py::arg("src") = std::vector<int>(),
|
| 84 |
+
py::arg("tar") = std::vector<int>())
|
| 85 |
+
|
| 86 |
+
.def_readwrite("deformed_points_3X_", &NonrigidSpareRegistration::deformed_points_3X_)
|
| 87 |
+
.def_readwrite("paras", &NonrigidSpareRegistration::paras); // ✅ 暴露 paras
|
| 88 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
numpy
|
| 2 |
+
gradio
|
| 3 |
+
trimesh
|
| 4 |
+
plotly
|
| 5 |
+
plyfile
|
scripts/compile.sh
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cd ..
|
| 2 |
+
mkdir build
|
| 3 |
+
cd build
|
| 4 |
+
cmake -DCMAKE_BUILD_TYPE=Release ..
|
| 5 |
+
make -j4
|