├── CMakeCache.linux.txt ├── CMakeCache.mac.txt ├── CMakeCache.win.txt ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── CopyAppleDependencies.cmake ├── CopyLinuxDependencies.cmake ├── CopyWindowsDependencies.cmake ├── FindEigen.cmake ├── FindNumpy.cmake ├── FindTBB.cmake └── UsefulMacros.cmake ├── docs ├── Makefile ├── make.bat └── source │ ├── _static │ └── css │ │ └── custom.css │ ├── _templates │ └── layout.html │ ├── conf.py │ ├── images │ ├── colormap_autumn.png │ ├── colormap_cool.png │ ├── colormap_gray.png │ ├── colormap_hot.png │ ├── colormap_hsv.png │ ├── colormap_jet.png │ ├── colormap_spring.png │ ├── colormap_summer.png │ └── colormap_winter.png │ ├── index.rst │ ├── install.rst │ ├── processing.rst │ ├── quickstart.rst │ ├── tutorial.rst │ ├── tutorials │ └── viewer │ │ ├── building_footprints.rst │ │ ├── geolife.rst │ │ ├── images │ │ ├── footprints_dc.png │ │ ├── footprints_dc_zoom.png │ │ ├── geolife_latlon_default.png │ │ ├── geolife_latlon_labeled.png │ │ ├── geolife_legend.png │ │ ├── geolife_utm.png │ │ ├── geolife_utm_zoomed.png │ │ ├── mobius.png │ │ ├── mobius_x.png │ │ ├── mobius_y.png │ │ ├── mobius_z.png │ │ ├── saddle_mag.png │ │ ├── saddle_n.png │ │ ├── saddle_z.png │ │ ├── semantic3d_large_i.jpg │ │ ├── semantic3d_large_labels.jpg │ │ ├── semantic3d_large_rgb.jpg │ │ ├── semantic3d_small_i.jpg │ │ ├── semantic3d_small_labels.jpg │ │ ├── semantic3d_small_rgb.jpg │ │ ├── tanks_and_temples_truck_n.jpg │ │ ├── tanks_and_temples_truck_rgb.jpg │ │ └── tutorial_banner.png │ │ ├── mobius.rst │ │ ├── saddle.rst │ │ ├── semantic3d.rst │ │ └── tanks_and_temples.rst │ └── viewer.rst ├── licenses ├── CMakeLists.txt ├── COPYING.apache2.txt ├── COPYING.gcc_rle31.txt ├── COPYING.gpl3.txt ├── COPYING.lgpl3.txt ├── LICENSE.linux.txt ├── LICENSE.mac.txt └── LICENSE.win.txt ├── pptk ├── CMakeLists.txt ├── __init__.py ├── _add_path.py ├── include │ ├── pptk_dll_export.h │ ├── progress_bar.h │ ├── python_util.h │ └── timer.h ├── kdtree │ ├── CMakeLists.txt │ ├── __init__.py │ ├── kdtree_wrapper.cpp │ └── src │ │ ├── accumulator.h │ │ ├── box.h │ │ ├── kdtree-impl.h │ │ ├── kdtree.h │ │ ├── node.h │ │ └── small_node.h ├── libs │ ├── CMakeLists.txt │ └── qt_plugins │ │ ├── CMakeLists.txt │ │ ├── platforms │ │ └── CMakeLists.txt │ │ └── xcbglintegrations │ │ └── CMakeLists.txt ├── points │ ├── CMakeLists.txt │ ├── __init__.py │ ├── expr.py │ └── points.py ├── processing │ ├── CMakeLists.txt │ ├── __init__.py │ └── estimate_normals │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ └── estimate_normals.cpp ├── vfuncs │ ├── CMakeLists.txt │ ├── __init__.py │ └── vfuncs.cpp └── viewer │ ├── CMakeLists.txt │ ├── __init__.py │ ├── background.h │ ├── box3.h │ ├── camera.h │ ├── camera_dolly.h │ ├── comm_funcs.h │ ├── floor_grid.h │ ├── look_at.h │ ├── main.cpp │ ├── octree.h │ ├── opengl_funcs.h │ ├── point_attributes.h │ ├── point_cloud.h │ ├── qt.conf │ ├── qt_camera.h │ ├── selection_box.h │ ├── splines.h │ ├── text.h │ ├── timer.h │ ├── viewer.h │ └── viewer.py ├── setup.py └── tests ├── test_estimate_normals.py ├── test_expr.py ├── test_points.py └── test_vfuncs.py /CMakeCache.linux.txt: -------------------------------------------------------------------------------- 1 | Numpy_INCLUDE_DIR:PATH=/usr/local/lib/python2.7/dist-packages/numpy/core/include/numpy 2 | Eigen_INCLUDE_DIR:PATH=/home/victlu/Sources/eigen-3.3.4 3 | Qt5_DIR:PATH=/opt/Qt5.6.1/5.6/gcc_64/lib/cmake/Qt5 4 | TBB_INCLUDE_DIR:PATH=/home/victlu/Sources/tbb2018_20170919oss/include 5 | TBB_tbb_LIBRARY:FILEPATH=/home/victlu/Sources/tbb2018_20170919oss/lib/intel64/gcc4.7/libtbb.so 6 | TBB_tbb_RUNTIME:FILEPATH=/home/victlu/Sources/tbb2018_20170919oss/lib/intel64/gcc4.7/libtbb.so 7 | TBB_tbbmalloc_LIBRARY:FILEPATH=/home/victlu/Sources/tbb2018_20170919oss/lib/intel64/gcc4.7/libtbbmalloc.so 8 | TBB_tbbmalloc_RUNTIME:FILEPATH=/home/victlu/Sources/tbb2018_20170919oss/lib/intel64/gcc4.7/libtbbmalloc.so 9 | PPTK_PATCHELF:FILEPATH=/home/victlu/Sources/install/bin/patchelf 10 | -------------------------------------------------------------------------------- /CMakeCache.mac.txt: -------------------------------------------------------------------------------- 1 | PYTHON_INCLUDE_DIR:PATH=/Applications/Canopy.app/appdata/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/include/python2.7 2 | PYTHON_LIBRARY:FILEPATH=/Applications/Canopy.app/appdata/canopy-1.4.0.1938.macosx-x86_64/Canopy.app/Contents/lib/libpython2.7.dylib 3 | Numpy_INCLUDE_DIR:PATH=/Users/victorlu/Library/Enthought/Canopy_64bit/User/lib/python2.7/site-packages/numpy/core/include/numpy 4 | Eigen_INCLUDE_DIR:PATH=/Users/victorlu/Sources/eigen-3.2.2 5 | Qt5_DIR:PATH=/Users/victorlu/Qt5.3.0/5.3/clang_64/lib/cmake/Qt5 6 | TBB_INCLUDE_DIR:PATH=/Users/victorlu/Sources/tbb2017_20170226oss/include 7 | TBB_tbb_LIBRARY:FILEPATH=/Users/victorlu/Sources/tbb2017_20170226oss/lib/libtbb.dylib 8 | TBB_tbb_RUNTIME:FILEPATH=/Users/victorlu/Sources/tbb2017_20170226oss/lib/libtbb.dylib 9 | TBB_tbbmalloc_LIBRARY:FILEPATH=/Users/victorlu/Sources/tbb2017_20170226oss/lib/libtbbmalloc.dylib 10 | TBB_tbbmalloc_RUNTIME:FILEPATH=/Users/victorlu/Sources/tbb2017_20170226oss/lib/libtbbmalloc.dylib 11 | CMAKE_CXX_COMPILER:FILEPATH=/Users/victorlu/Sources/install/bin/g++ 12 | CMAKE_C_COMPILER:FILEPATH=/Users/victorlu/Sources/install/bin/gcc 13 | -------------------------------------------------------------------------------- /CMakeCache.win.txt: -------------------------------------------------------------------------------- 1 | Numpy_INCLUDE_DIR:PATH=C:\Users\victlu.HERE\AppData\Local\Enthought\Canopy\App\appdata\canopy-1.5.1.2730.win-x86_64\Lib\site-packages\numpy\core\include\numpy 2 | PYTHON_INCLUDE_DIR:PATH=C:\Users\victlu.HERE\AppData\Local\Enthought\Canopy\App\appdata\canopy-1.5.1.2730.win-x86_64\include 3 | PYTHON_LIBRARY:FILEPATH=C:\Users\victlu.HERE\AppData\Local\Enthought\Canopy\App\appdata\canopy-1.5.1.2730.win-x86_64\libs\python27.lib 4 | Eigen_INCLUDE_DIR:PATH=F:/Sources/eigen-3.2.9 5 | TBB_INCLUDE_DIR:PATH=F:/Sources/tbb43_20141204oss/include 6 | TBB_tbb_LIBRARY:FILEPATH=F:/Sources/tbb43_20141204oss/lib/intel64/vc12/tbb.lib 7 | TBB_tbb_RUNTIME:FILEPATH=F:/Sources/tbb43_20141204oss/bin/intel64/vc12/tbb.dll 8 | TBB_tbbmalloc_LIBRARY:FILEPATH=F:/Sources/tbb43_20141204oss/lib/intel64/vc12/tbbmalloc.lib 9 | TBB_tbbmalloc_RUNTIME:FILEPATH=F:/Sources/tbb43_20141204oss/bin/intel64/vc12/tbbmalloc.dll 10 | Qt5_DIR:PATH=C:/Qt/5.4/msvc2013_64_opengl/lib/cmake/Qt5 -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(pptk) 3 | 4 | set(CMAKE_BUILD_TYPE Release) 5 | set(CMAKE_MODULE_PATH 6 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_CURRENT_SOURCE_DIR}) 7 | 8 | find_package(PythonLibs 2.7 REQUIRED) 9 | find_package(OpenGL REQUIRED) 10 | find_package(Numpy REQUIRED) 11 | find_package(TBB REQUIRED) 12 | find_package(Eigen REQUIRED) 13 | find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets Network OpenGL Core) 14 | find_package(OpenMP) 15 | 16 | # get root Qt5 folder (i.e. contains bin, lib, plugins, etc.) 17 | get_target_property(Qt5_DIR Qt5::qmake LOCATION) 18 | get_filename_component(Qt5_DIR ${Qt5_DIR} DIRECTORY) 19 | get_filename_component(Qt5_DIR ${Qt5_DIR} DIRECTORY) 20 | set(Qt5_PLUGINS_DIR ${Qt5_DIR}/plugins) 21 | 22 | # localize all dependencies (.dll, .so, or .dylib) in the following folder 23 | set(PPTK_LIBS_DIR ${PROJECT_BINARY_DIR}/pptk/libs) 24 | 25 | # use the following variable to store a list of .dll paths required by targets 26 | # built in pptk; this is useful only for building on Windows platform 27 | get_filename_component(TBB_RUNTIME_DIR ${TBB_tbb_RUNTIME} DIRECTORY) 28 | set(PPTK_DLL_DIRS 29 | ${TBB_RUNTIME_DIR} 30 | ${Qt5_DIR}/bin 31 | CACHE INTERNAL "Additional folder paths for finding .dll's") 32 | 33 | # use patchelf to modify binary RPATH when building pptk on Linux 34 | if(UNIX AND NOT APPLE) 35 | find_program(PPTK_PATCHELF patchelf) 36 | if (NOT PPTK_PATCHELF) 37 | message(FATAL_ERROR 38 | "patchelf needed for localizing library dependencies; \ 39 | please manually set the PPTK_PATCHELF variable.") 40 | endif() 41 | endif() 42 | 43 | # adds -std=gnu++11 flag when compiling with gcc 44 | set(CMAKE_CXX_STANDARD 11) 45 | 46 | include(UsefulMacros) 47 | 48 | # following lines specifies the CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS variable 49 | set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) 50 | set(CMAKE_INSTALL_OPENMP_LIBRARIES TRUE) # requires cmake 3.1 51 | include(InstallRequiredSystemLibraries) 52 | 53 | copy_file(setup.py) 54 | copy_file(LICENSE) 55 | add_subdirectory(pptk) 56 | add_subdirectory(licenses) 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2011-2017 HERE Europe B.V. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pptk - Point Processing Toolkit 2 | 3 | Copyright (C) 2011-2018 HERE Europe B.V. 4 | 5 | The Point Processing Toolkit (pptk) is a Python package for visualizing and processing 2-d/3-d point clouds. 6 | 7 | At present, pptk consists of the following features. 8 | 9 | * A 3-d point cloud viewer that 10 | - accepts any 3-column numpy array as input, 11 | - renders tens of millions of points interactively using an octree-based level of detail mechanism, 12 | - supports point selection for inspecting and annotating point data. 13 | * A fully parallelized point k-d tree that supports k-nearest neighbor queries and r-near range queries 14 | (both build and queries have been parallelized). 15 | * A normal estimation routine based on principal component analysis of point cloud neighborhoods. 16 | 17 | [Homepage](https://heremaps.github.io/pptk/index.html) 18 | 19 | ![pptk screenshots](/docs/source/tutorials/viewer/images/tutorial_banner.png) 20 | 21 | The screenshots above show various point datasets visualized using pptk. 22 | The `bildstein1` Lidar point cloud from Semantic3D (left), 23 | Beijing GPS trajectories from Geolife (middle left), 24 | `DistrictofColumbia.geojson` 2-d polygons from US building footprints (middle right), 25 | and a Mobius strip (right). 26 | For details, see the [tutorials](https://heremaps.github.io/pptk/tutorial.html). 27 | 28 | ## License 29 | 30 | Unless otherwise noted in `LICENSE` files for specific files or directories, 31 | the [LICENSE](LICENSE) in the root applies to all content in this repository. 32 | 33 | ## Install 34 | 35 | One can either install pptk directly from PyPI 36 | 37 | ``` 38 | >> pip install pptk 39 | ``` 40 | 41 | or from the .whl file that results from [building pptk from source](#build). 42 | 43 | ``` 44 | >> pip install <.whl file> 45 | ``` 46 | 47 | ## Quickstart 48 | 49 | In Python, generate 100 random 3-d points. 50 | 51 | ``` 52 | >> import numpy as np 53 | >> x = np.random.rand(100, 3) 54 | ``` 55 | 56 | Visualize. 57 | 58 | ``` 59 | >> import pptk 60 | >> v = pptk.viewer(x) 61 | ``` 62 | 63 | Set point size to 0.01. 64 | 65 | ``` 66 | >> v.set(point_size=0.01) 67 | ``` 68 | 69 | For more advanced examples, see [tutorials](https://heremaps.github.io/pptk/tutorial.html). 70 | 71 | ## Build 72 | 73 | We provide CMake scripts for automating most of the build process, but ask the 74 | user to manually prepare [dependencies](#requirements) and record their paths 75 | in the following CMake cache variables. 76 | 77 | * `Numpy_INCLUDE_DIR` 78 | * `PYTHON_INCLUDE_DIR` 79 | * `PYTHON_LIBRARY` 80 | * `Eigen_INCLUDE_DIR` 81 | * `TBB_INCLUDE_DIR` 82 | * `TBB_tbb_LIBRARY` 83 | * `TBB_tbb_RUNTIME` 84 | * `TBB_tbbmalloc_LIBRARY` 85 | * `TBB_tbbmalloc_RUNTIME` 86 | * `Qt5_DIR` 87 | 88 | To set these variables, either use one of CMake's GUIs (ccmake or cmake-gui), 89 | or provide an initial CMakeCache.txt in the target build folder 90 | (for examples of initial cache files, see the CMakeCache..txt files) 91 | 92 | ##### Requirements 93 | 94 | Listed are versions of libraries used to develop pptk, though earlier versions 95 | of these libraries may also work. 96 | 97 | * [QT](https://www.qt.io/) 5.4 98 | * [TBB](https://www.threadingbuildingblocks.org/) 4.3 99 | * [Eigen](http://eigen.tuxfamily.org) 3.2.9 100 | * [Python](https://www.python.org/) 2.7+ or 3.6+ 101 | * [Numpy](http://www.numpy.org/) 1.13 102 | 103 | ##### Windows 104 | 105 | 1. Create an empty build folder 106 | 107 | ``` 108 | >> mkdir 109 | ``` 110 | 111 | 2. Create an initial CMakeCache.txt under and use it to provide 112 | values for the CMake cache variables listed above. (e.g. see CMakeCache.win.txt) 113 | 114 | 3. Type the following... 115 | 116 | ``` 117 | >> cd 118 | >> cmake -G "NMake Makefiles" 119 | >> nmake 120 | >> python setup.py bdist_wheel 121 | >> pip install dist\<.whl file> 122 | ``` 123 | 124 | ##### Linux 125 | 126 | Similar to building on Windows. 127 | 128 | ##### Mac 129 | 130 | Similar to building on Windows. 131 | -------------------------------------------------------------------------------- /cmake/CopyAppleDependencies.cmake: -------------------------------------------------------------------------------- 1 | # usage: cmake -P CopyAppleDependencies.cmake 2 | # 3 | # paths assumed to be existing full paths 4 | 5 | include(BundleUtilities) 6 | 7 | find_program(_install_name_tool "install_name_tool") 8 | set(_target ${CMAKE_ARGV3}) 9 | set(_copy_folder ${CMAKE_ARGV4}) 10 | set(_paths "/usr/bin") 11 | get_item_rpaths(${_target} _rpaths) 12 | get_prerequisites(${_target} _prereqs 1 1 "" "${_paths}" "${_rpaths}") 13 | 14 | # delete existing rpaths in _target and 15 | # add relative path to _copy_folder as new rpath 16 | foreach(p ${_rpaths}) 17 | execute_process(COMMAND ${_install_name_tool} -delete_rpath ${p} ${_target}) 18 | endforeach() 19 | get_filename_component(_target_folder ${_target} DIRECTORY) 20 | file(RELATIVE_PATH _new_rpath ${_target_folder} ${_copy_folder}) 21 | execute_process(COMMAND 22 | ${_install_name_tool} -add_rpath "@loader_path/${_new_rpath}" ${_target}) 23 | 24 | # copy _target's dependencies to _copy_folder 25 | foreach(p ${_prereqs}) 26 | get_filename_component(y ${p} NAME) 27 | set(dst ${_copy_folder}/${y}) 28 | gp_resolve_item(${_target} ${p} "" "${_paths}" src "${_rpaths}") 29 | execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst}) 30 | execute_process(COMMAND ${_install_name_tool} -id @rpath/${y} ${dst}) 31 | if (IS_ABSOLUTE ${p}) 32 | execute_process(COMMAND ${_install_name_tool} -change ${p} @rpath/${y} ${_target}) 33 | endif() 34 | get_prerequisites(${dst} _prereqs_of_prereqs 1 0 "" "") 35 | foreach (pp ${_prereqs_of_prereqs}) 36 | get_filename_component(yy ${pp} NAME) 37 | if (IS_ABSOLUTE ${pp}) 38 | execute_process(COMMAND ${_install_name_tool} -change ${pp} @rpath/${yy} ${dst}) 39 | endif() 40 | endforeach() 41 | endforeach() 42 | -------------------------------------------------------------------------------- /cmake/CopyLinuxDependencies.cmake: -------------------------------------------------------------------------------- 1 | # usage: cmake -P CopyLinuxDependencies.cmake 2 | # 3 | # assumes full existing paths 4 | 5 | include(GetPrerequisites) 6 | 7 | set(_target_file_orig ${CMAKE_ARGV3}) 8 | set(_target_file_copy ${CMAKE_ARGV4}) 9 | set(_libs_folder ${CMAKE_ARGV5}) 10 | set(_patchelf_cmd ${CMAKE_ARGV6}) 11 | 12 | get_prerequisites(${_target_file_orig} _prereqs 1 1 "" "") 13 | foreach(p ${_prereqs}) 14 | gp_resolve_item("" ${p} "" "" src) 15 | get_filename_component(x ${src} NAME) 16 | set(dst ${_libs_folder}/${x}) 17 | execute_process(COMMAND 18 | ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst}) 19 | execute_process(COMMAND 20 | ${_patchelf_cmd} --set-rpath \$ORIGIN ${dst}) 21 | endforeach() 22 | 23 | get_filename_component(y ${_target_file_copy} DIRECTORY) 24 | file(RELATIVE_PATH _libs_folder_r ${y} ${_libs_folder}) 25 | execute_process(COMMAND 26 | ${_patchelf_cmd} --set-rpath \$ORIGIN/${_libs_folder_r} ${_target_file_copy}) 27 | -------------------------------------------------------------------------------- /cmake/CopyWindowsDependencies.cmake: -------------------------------------------------------------------------------- 1 | # usage: cmake -P CopyWindowsDependencies.cmake 2 | # <.dll folder paths> 3 | # assumes full existing paths 4 | # .dll folder paths are semicolon-separated 5 | 6 | include(GetPrerequisites) 7 | 8 | set(_target_file ${CMAKE_ARGV3}) 9 | set(_copy_folder ${CMAKE_ARGV4}) 10 | set(_dll_paths ${CMAKE_ARGV5}) 11 | get_prerequisites(${_target_file} _prereqs 1 1 "" "${_dll_paths}") 12 | foreach(p ${_prereqs}) 13 | if(NOT (p MATCHES "python[0-9]*.dll")) 14 | gp_resolve_item("" ${p} "" "${_dll_paths}" src) 15 | set(dst ${_copy_folder}) 16 | execute_process(COMMAND 17 | ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst}) 18 | endif() 19 | endforeach() -------------------------------------------------------------------------------- /cmake/FindEigen.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | set(Eigen_INCLUDE_DIR "Eigen_INCLUDE_DIR-NOTFOUND" CACHE PATH "Path containing Eigen folder") 4 | 5 | find_package_handle_standard_args(Eigen REQUIRED_VARS Eigen_INCLUDE_DIR) 6 | -------------------------------------------------------------------------------- /cmake/FindNumpy.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | set(Numpy_INCLUDE_DIR "Numpy_INCLUDE_DIR-NOTFOUND" CACHE PATH "Path of folder containing arrayobject.h") 4 | 5 | find_package_handle_standard_args(Numpy REQUIRED_VARS Numpy_INCLUDE_DIR) 6 | -------------------------------------------------------------------------------- /cmake/FindTBB.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | set(TBB_INCLUDE_DIR "TBB_INCLUDE_DIR-NOTFOUND" CACHE PATH "Path to directory containing TBB header files") 4 | set(TBB_tbb_LIBRARY "TBB_tbb_LIBRARY-NOTFOUND" CACHE FILEPATH "Path to tbb link library (i.e. tbb.lib)") 5 | set(TBB_tbbmalloc_LIBRARY "TBB_tbbmalloc_LIBRARY-NOTFOUND" CACHE FILEPATH "Path to tbbmalloc link library (i.e. tbbmalloc.lib)") 6 | set(TBB_tbb_RUNTIME "TBB_tbb_RUNTIME-NOTFOUND" CACHE FILEPATH "Path to tbb runtime library (i.e. tbb.dll)") 7 | set(TBB_tbbmalloc_RUNTIME "TBB_tbbmalloc_RUNTIME-NOTFOUND" CACHE FILEPATH "Path to tbbmalloc runtime library (i.e. tbbmalloc.dll)") 8 | 9 | find_package_handle_standard_args(TBB 10 | REQUIRED_VARS 11 | TBB_INCLUDE_DIR 12 | TBB_tbb_LIBRARY 13 | TBB_tbbmalloc_LIBRARY 14 | TBB_tbb_RUNTIME 15 | TBB_tbbmalloc_RUNTIME 16 | ) 17 | -------------------------------------------------------------------------------- /cmake/UsefulMacros.cmake: -------------------------------------------------------------------------------- 1 | 2 | macro(set_target_python_module_name target) 3 | set_target_properties(${target} PROPERTIES PREFIX "") 4 | if (WIN32) 5 | set_target_properties(${target} PROPERTIES SUFFIX ".pyd") 6 | elseif(APPLE) 7 | set_target_properties(${target} PROPERTIES SUFFIX ".so") 8 | endif (WIN32) 9 | endmacro() 10 | 11 | macro(set_target_rpath target path) 12 | if (APPLE) 13 | set_target_properties(${target} PROPERTIES INSTALL_RPATH "@loader_path/${path}" BUILD_WITH_INSTALL_RPATH TRUE) 14 | elseif (UNIX) 15 | set_target_properties(${target} PROPERTIES INSTALL_RPATH "$ORIGIN/${path}" BUILD_WITH_INSTALL_RPATH TRUE) 16 | endif(APPLE) 17 | endmacro() 18 | 19 | macro(copy_target x) 20 | set(src $) 21 | set(dst ${CMAKE_CURRENT_BINARY_DIR}) 22 | add_custom_command( 23 | TARGET ${x} 24 | POST_BUILD 25 | COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst} 26 | COMMENT "Copying ${src} to ${dst}") 27 | unset(src) 28 | unset(dst) 29 | endmacro() 30 | 31 | function(copy_target_dependencies x) 32 | if(ARGC GREATER 1) 33 | set(_target_file ${ARGV1}) 34 | get_filename_component(_target_file_name ${_target_file} NAME) 35 | else() 36 | set(_target_file $) 37 | set(_target_file_name $) 38 | endif() 39 | if(WIN32) 40 | add_custom_command(TARGET ${x} POST_BUILD 41 | COMMAND ${CMAKE_COMMAND} -P 42 | ${PROJECT_SOURCE_DIR}/cmake/CopyWindowsDependencies.cmake 43 | ${CMAKE_CURRENT_BINARY_DIR}/${_target_file_name} 44 | ${PPTK_LIBS_DIR} "${PPTK_DLL_DIRS}") 45 | elseif(APPLE) 46 | add_custom_command(TARGET ${x} POST_BUILD 47 | COMMAND ${CMAKE_COMMAND} -P 48 | ${PROJECT_SOURCE_DIR}/cmake/CopyAppleDependencies.cmake 49 | ${CMAKE_CURRENT_BINARY_DIR}/${_target_file_name} ${PPTK_LIBS_DIR}) 50 | elseif(UNIX) 51 | add_custom_command(TARGET ${x} POST_BUILD 52 | COMMAND ${CMAKE_COMMAND} -P 53 | ${PROJECT_SOURCE_DIR}/cmake/CopyLinuxDependencies.cmake 54 | ${_target_file} 55 | ${CMAKE_CURRENT_BINARY_DIR}/${_target_file_name} 56 | ${PPTK_LIBS_DIR} ${PPTK_PATCHELF}) 57 | endif() 58 | endfunction() 59 | 60 | macro(current_source_dir x) 61 | string(CONCAT ${x} "^" ${PROJECT_SOURCE_DIR} "/?") 62 | string(REGEX REPLACE ${${x}} "" ${x} ${CMAKE_CURRENT_SOURCE_DIR}) 63 | endmacro() 64 | 65 | function(copy_file x) 66 | # x should be a file path, and should not be a variable 67 | # i.e. copy_file(${var}), not copy_file(var) 68 | get_filename_component(name ${x} NAME) 69 | file(RELATIVE_PATH temp ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) 70 | if (NOT (temp STREQUAL "")) 71 | string(REGEX REPLACE "(/|\\\\)" "." temp "${temp}") 72 | string(CONCAT name "${temp}" "." "${name}") 73 | endif() 74 | if (ARGC EQUAL 2) 75 | set(${ARGV1} ${name} PARENT_SCOPE) 76 | endif() 77 | if (NOT IS_ABSOLUTE ${x}) 78 | set(src ${CMAKE_CURRENT_SOURCE_DIR}/${x}) 79 | else() 80 | set(src ${x}) 81 | endif() 82 | set(dst ${CMAKE_CURRENT_BINARY_DIR}) 83 | add_custom_target(${name} ALL 84 | COMMAND ${CMAKE_COMMAND} -E copy_if_different ${src} ${dst} 85 | COMMENT "Copying ${src} to ${dst}") 86 | endfunction() 87 | 88 | function(copy_file_with_dependencies x) 89 | copy_file(${x} _target_name) 90 | copy_target_dependencies(${_target_name} ${x}) 91 | endfunction() 92 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = pptk 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=../../pptk_docs_build 12 | set SPHINXPROJ=pptk 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/source/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | div.topnav p { 2 | margin-top: 0; 3 | margin-left: 40px; 4 | margin-right: 40px; 5 | margin-bottom: 0px; 6 | text-align: left; 7 | font-size: 0.8em; 8 | font-weight: bold; 9 | } 10 | div.bottomnav p { 11 | margin-right: 40px; 12 | margin-left: 40px; 13 | text-align: left; 14 | font-size: 0.8em; 15 | } 16 | 17 | table.image-grid, 18 | table.image-grid tr, 19 | table.image-grid col { 20 | border: hidden; 21 | } 22 | 23 | table.caption, 24 | table.caption tr, 25 | table.caption col { 26 | border: hidden; 27 | } 28 | 29 | table.caption { 30 | margin-left: 5%; 31 | margin-right: 5%; 32 | width: 90%; 33 | } 34 | 35 | table.caption td { 36 | text-align: center; 37 | font-size: smaller; 38 | font-style: italic; 39 | } 40 | 41 | .kbd.docutils.literal { 42 | border: solid #CCCCCC 1px; 43 | font-family: Arial, Helvetica, sans-serif; 44 | font-size: 90%; 45 | font-weight: normal; 46 | padding: 1px 5px 3px; 47 | white-space: nowrap; 48 | } -------------------------------------------------------------------------------- /docs/source/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {%- extends "haiku/layout.html" %} 2 | 3 | {% macro path_sequence() %} 4 |

5 | {%- block haikurel1 %} 6 | {%- endblock %} 7 | {% if title != "Contents" %} 8 | Contents 9 |   »   10 | {% for item in parents %} 11 | {{ item.title }} 12 |   »   13 | {% endfor %} 14 | {% endif %} 15 | {{ title }} 16 | {%- block haikurel2 %} 17 | {%- endblock %} 18 |

19 | {% endmacro %} 20 | 21 | {% block content %} 22 | 38 | 41 |
42 | {#{%- if display_toc %} 43 |
44 |

{{ _('Table of Contents') }}

45 | {{ toc }} 46 |
47 | {%- endif %}#} 48 | {% block body %}{% endblock %} 49 |
50 | 54 | {% endblock %} 55 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # pptk documentation build configuration file, created by 4 | # sphinx-quickstart on Fri Aug 10 11:29:10 2018. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | # import os 20 | # import sys 21 | # sys.path.insert(0, os.path.abspath('.')) 22 | 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | 'sphinx.ext.autodoc', 35 | 'sphinx.ext.napoleon', 36 | 'sphinx.ext.imgmath', 37 | 'sphinx.ext.githubpages'] 38 | 39 | def setup(app): 40 | app.add_stylesheet('css/custom.css') 41 | 42 | imgmath_image_format = 'svg' 43 | imgmath_dvisvgm = 'F:\Programs\dvisvgm-2.3.3-win64\dvisvgm.exe' 44 | autodoc_member_order = 'groupwise' 45 | autosummary_generate = True 46 | napoleon_use_rtype = False 47 | 48 | # Add any paths that contain templates here, relative to this directory. 49 | templates_path = ['_templates'] 50 | 51 | # The suffix(es) of source filenames. 52 | # You can specify multiple suffix as a list of string: 53 | # 54 | # source_suffix = ['.rst', '.md'] 55 | source_suffix = '.rst' 56 | 57 | # The master toctree document. 58 | master_doc = 'index' 59 | 60 | # General information about the project. 61 | project = u'pptk' 62 | copyright = u'2018, HERE Europe B.V.' 63 | author = u'Victor Lu' 64 | 65 | # The version info for the project you're documenting, acts as replacement for 66 | # |version| and |release|, also used in various other places throughout the 67 | # built documents. 68 | # 69 | # The short X.Y version. 70 | version = u'0.1' 71 | # The full version, including alpha/beta/rc tags. 72 | release = u'0.1.1' 73 | 74 | # The language for content autogenerated by Sphinx. Refer to documentation 75 | # for a list of supported languages. 76 | # 77 | # This is also used if you do content translation via gettext catalogs. 78 | # Usually you set "language" from the command line for these cases. 79 | language = None 80 | 81 | # List of patterns, relative to source directory, that match files and 82 | # directories to ignore when looking for source files. 83 | # This patterns also effect to html_static_path and html_extra_path 84 | exclude_patterns = [] 85 | 86 | # The name of the Pygments (syntax highlighting) style to use. 87 | pygments_style = 'sphinx' 88 | 89 | # If true, `todo` and `todoList` produce output, else they produce nothing. 90 | todo_include_todos = False 91 | 92 | 93 | # -- Options for HTML output ---------------------------------------------- 94 | 95 | # The theme to use for HTML and HTML Help pages. See the documentation for 96 | # a list of builtin themes. 97 | # 98 | html_theme = 'haiku' 99 | 100 | # Theme options are theme-specific and customize the look and feel of a theme 101 | # further. For a list of options available for each theme, see the 102 | # documentation. 103 | # 104 | # html_theme_options = {} 105 | 106 | # Add any paths that contain custom static files (such as style sheets) here, 107 | # relative to this directory. They are copied after the builtin static files, 108 | # so a file named "default.css" will overwrite the builtin "default.css". 109 | html_static_path = ['_static'] 110 | 111 | # Custom sidebar templates, must be a dictionary that maps document names 112 | # to template names. 113 | # 114 | # This is required for the alabaster theme 115 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 116 | html_sidebars = { 117 | '**': [ 118 | 'relations.html', # needs 'show_related': True theme option to display 119 | 'searchbox.html', 120 | ] 121 | } 122 | 123 | 124 | # -- Options for HTMLHelp output ------------------------------------------ 125 | 126 | # Output file base name for HTML help builder. 127 | htmlhelp_basename = 'pptkdoc' 128 | 129 | 130 | # -- Options for LaTeX output --------------------------------------------- 131 | 132 | latex_elements = { 133 | # The paper size ('letterpaper' or 'a4paper'). 134 | # 135 | # 'papersize': 'letterpaper', 136 | 137 | # The font size ('10pt', '11pt' or '12pt'). 138 | # 139 | # 'pointsize': '10pt', 140 | 141 | # Additional stuff for the LaTeX preamble. 142 | # 143 | # 'preamble': '', 144 | 145 | # Latex figure (float) alignment 146 | # 147 | # 'figure_align': 'htbp', 148 | } 149 | 150 | # Grouping the document tree into LaTeX files. List of tuples 151 | # (source start file, target name, title, 152 | # author, documentclass [howto, manual, or own class]). 153 | latex_documents = [ 154 | (master_doc, 'pptk.tex', u'pptk Documentation', 155 | u'Victor Lu, HERE Europe B.V.', 'manual'), 156 | ] 157 | 158 | 159 | # -- Options for manual page output --------------------------------------- 160 | 161 | # One entry per manual page. List of tuples 162 | # (source start file, name, description, authors, manual section). 163 | man_pages = [ 164 | (master_doc, 'pptk', u'pptk Documentation', 165 | [author], 1) 166 | ] 167 | 168 | 169 | # -- Options for Texinfo output ------------------------------------------- 170 | 171 | # Grouping the document tree into Texinfo files. List of tuples 172 | # (source start file, target name, title, author, 173 | # dir menu entry, description, category) 174 | texinfo_documents = [ 175 | (master_doc, 'pptk', u'pptk Documentation', 176 | author, 'pptk', 'One line description of project.', 177 | 'Miscellaneous'), 178 | ] 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /docs/source/images/colormap_autumn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/images/colormap_autumn.png -------------------------------------------------------------------------------- /docs/source/images/colormap_cool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/images/colormap_cool.png -------------------------------------------------------------------------------- /docs/source/images/colormap_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/images/colormap_gray.png -------------------------------------------------------------------------------- /docs/source/images/colormap_hot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/images/colormap_hot.png -------------------------------------------------------------------------------- /docs/source/images/colormap_hsv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/images/colormap_hsv.png -------------------------------------------------------------------------------- /docs/source/images/colormap_jet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/images/colormap_jet.png -------------------------------------------------------------------------------- /docs/source/images/colormap_spring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/images/colormap_spring.png -------------------------------------------------------------------------------- /docs/source/images/colormap_summer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/images/colormap_summer.png -------------------------------------------------------------------------------- /docs/source/images/colormap_winter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/images/colormap_winter.png -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. title:: Contents 2 | 3 | `GitHub repository `__ 4 | 5 | Contents 6 | ======== 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | install 12 | quickstart 13 | Tutorials 14 | viewer 15 | processing 16 | 17 | 18 | Indices and tables 19 | ================== 20 | 21 | * :ref:`genindex` 22 | * :ref:`modindex` 23 | * :ref:`search` 24 | -------------------------------------------------------------------------------- /docs/source/install.rst: -------------------------------------------------------------------------------- 1 | Installing pptk 2 | =============== 3 | 4 | Install from PyPI 5 | ----------------- 6 | 7 | Install pptk directly from PyPI 8 | 9 | >>> pip install pptk 10 | 11 | .. note:: 12 | pptk requires 64-bit Python and is only :code:`pip install`-able on versions of Python 13 | that have a corresponding pptk wheel file on `PyPI `__. 14 | 15 | Or install from the .whl file that results from 16 | :ref:`building pptk from source `. 17 | 18 | >>> pip install <.whl file> 19 | 20 | .. _build_from_source: 21 | 22 | Building from source 23 | ---------------------------------------- 24 | 25 | pptk source code is available on `GitHub `__ 26 | 27 | Build is mostly automated via CMake scripts, 28 | but the user must manually prepare :ref:`dependencies ` and record their 29 | paths in the following CMake cache variables. 30 | 31 | * Numpy_INCLUDE_DIR 32 | * PYTHON_INCLUDE_DIR 33 | * PYTHON_LIBRARY 34 | * Eigen_INCLUDE_DIR 35 | * TBB_INCLUDE_DIR 36 | * TBB_tbb_LIBRARY 37 | * TBB_tbb_RUNTIME 38 | * TBB_tbbmalloc_LIBRARY 39 | * TBB_tbbmalloc_RUNTIME 40 | * Qt5_DIR 41 | 42 | To set these variables, either use one of CMake's GUIs (ccmake or cmake-gui), 43 | or provide an initial CMakeCache.txt in the target build folder (for examples of 44 | initial cache files, see the CMakeCache..txt files) 45 | 46 | .. _requirements: 47 | 48 | Requirements 49 | ~~~~~~~~~~~~ 50 | 51 | Listed are versions of libraries used to develop pptk, 52 | though earlier versions of these libraries may also work. 53 | pptk requires 64-bit versions of these libraries. 54 | 55 | * `QT `_ 5.4 56 | * `TBB `_ 4.3 57 | * `Eigen `_ 3.2.9 58 | * `Python `_ 2.7+ or 3.6+ 59 | * `Numpy `_ 1.13 60 | 61 | Windows 62 | ~~~~~~~ 63 | 64 | Check out source code 65 | 66 | >>> git clone https://github.com/heremaps/pptk.git 67 | 68 | Create an empty build folder 69 | 70 | >>> mkdir 71 | 72 | Create an initial CMakeCache.txt under and use it to provide 73 | values for the CMake cache variables listed above. (e.g. see CMakeCache.win.txt) 74 | 75 | Type the following... 76 | 77 | >>> cd 78 | >>> cmake -G "NMake Makefiles" 79 | >>> nmake 80 | >>> python setup.py bdist_wheel 81 | >>> pip install dist\<.whl file> 82 | 83 | Linux 84 | ~~~~~ 85 | 86 | Similar to building on Windows. 87 | 88 | Mac 89 | ~~~ 90 | 91 | Similar to building on Windows. 92 | -------------------------------------------------------------------------------- /docs/source/processing.rst: -------------------------------------------------------------------------------- 1 | Processing routines 2 | =================== 3 | 4 | Normal estimation 5 | ----------------- 6 | 7 | .. autofunction:: pptk.estimate_normals 8 | 9 | k-d tree 10 | -------- 11 | 12 | .. automodule:: pptk.kdtree 13 | :private-members: 14 | 15 | .. automethod:: pptk.kdtree._build 16 | 17 | .. automethod:: pptk.kdtree._query 18 | -------------------------------------------------------------------------------- /docs/source/quickstart.rst: -------------------------------------------------------------------------------- 1 | Getting started 2 | =============== 3 | 4 | Install pptk. 5 | 6 | >>> pip install pptk 7 | 8 | .. note:: 9 | pptk requires 64-bit Python and is only :code:`pip install`-able on versions of Python 10 | that have a corresponding pptk wheel file on `PyPI `__. 11 | 12 | In Python, generate 100 random 3-d points, and 13 | 14 | .. code-block:: python 15 | 16 | >>> import numpy as np 17 | >>> x = np.random.rand(100, 3) 18 | 19 | Visualize. 20 | 21 | .. code-block:: python 22 | 23 | >>> import pptk 24 | >>> v = pptk.viewer(x) 25 | 26 | Set point size to 0.01. 27 | 28 | .. code-block:: python 29 | 30 | >>> v.set(point_size=0.01) 31 | -------------------------------------------------------------------------------- /docs/source/tutorial.rst: -------------------------------------------------------------------------------- 1 | .. title:: Tutorials 2 | 3 | Tutorials 4 | ========= 5 | 6 | Visualizing 2-d and 3-d point data 7 | ---------------------------------- 8 | 9 | .. |semantic3d_small_i| image:: tutorials/viewer/images/semantic3d_small_i.jpg 10 | :height: 200 11 | :width: 275 12 | :scale: 80% 13 | :align: middle 14 | 15 | .. |geolife_utm| image:: tutorials/viewer/images/geolife_utm.png 16 | :height: 200 17 | :width: 289 18 | :scale: 80% 19 | :align: middle 20 | 21 | .. |footprints_dc| image:: tutorials/viewer/images/footprints_dc.png 22 | :height: 200 23 | :width: 275 24 | :scale: 80% 25 | :align: middle 26 | 27 | .. |mobius| image:: tutorials/viewer/images/mobius.png 28 | :height: 200 29 | :width: 200 30 | :scale: 80% 31 | :align: middle 32 | 33 | .. rst-class:: image-grid 34 | .. table:: 35 | :widths: 280 294 280 205 36 | :align: center 37 | 38 | ==================== ============= =============== ======== 39 | |semantic3d_small_i| |geolife_utm| |footprints_dc| |mobius| 40 | ==================== ============= =============== ======== 41 | 42 | .. rst-class:: caption 43 | 44 | +---------------------------------------------------------------------------------------------------------------+ 45 | | Point datasets visualized using :py:meth:`pptk.viewer`. | 46 | | The :file:`bildstein1` Lidar point cloud from `Semantic3D `__ (left). | 47 | | Beijing GPS trajectories from | 48 | | `Geolife `__ | 49 | | (middle left). | 50 | | :file:`DistrictofColumbia.geojson` 2-d polygons from | 51 | | `US building footprints `__ (middle right). | 52 | | A Mobius strip (right). | 53 | +---------------------------------------------------------------------------------------------------------------+ 54 | 55 | :py:func:`pptk.viewer` allows interactive visualization of any point data that can be represented as a 3-column numpy array. 56 | This includes Lidar point clouds, GPS trajectories, points on a 3-d parametric surface, or even point samplings of 2-d polygons. 57 | The viewer is not tied to a specific file format. 58 | As shown in these tutorials, 59 | users of pptk may leverage the vast collection of existing Python packages for reading data from specific file formats. 60 | For example, the pandas package is very well-suited for reading .csv files, 61 | and the plyfile package for reading .ply files. 62 | The following tutorials each consider a different point dataset 63 | and provide step-by-step instructions for visualizing them using :py:func:`pptk.viewer`. 64 | 65 | **Lidar point clouds** 66 | 67 | .. toctree:: 68 | :maxdepth: 1 69 | 70 | Semantic3D dataset 71 | Tanks and Temples dataset 72 | 73 | **GPS trajectories** 74 | 75 | .. toctree:: 76 | :maxdepth: 1 77 | 78 | Geolife dataset 79 | 80 | **2-d Polygons** 81 | 82 | .. toctree:: 83 | :maxdepth: 1 84 | 85 | U.S. building footprints dataset 86 | 87 | **Parametric surfaces** 88 | 89 | .. toctree:: 90 | :maxdepth: 1 91 | 92 | Hyperbolic paraboloid 93 | Mobius strip 94 | -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/building_footprints.rst: -------------------------------------------------------------------------------- 1 | .. title:: Visualizing 2-d building footprint polygons 2 | 3 | U.S. building footprints dataset by Microsoft 4 | ============================================= 5 | 6 | Download the District of Columbia footprints from the project 7 | `website `__. 8 | 9 | Extract :code:`DistrictofColumbia.zip` to get :code:`DistrictofColumbia.geojson`. 10 | 11 | In a Python terminal, import required Python packages. 12 | Install any missing packages using :code:`pip install`. 13 | 14 | >>> import pptk 15 | >>> import json 16 | >>> import pyproj 17 | >>> import numpy as np 18 | 19 | Load :code:`DistrictofColumbia.geojson` using :py:func:`json.load`. 20 | 21 | .. code-block:: python 22 | 23 | >>> with open('DistrictofColumbia.geojson', 'rb') as fd: 24 | data = json.load(fd) 25 | 26 | Collect the polygon coordinates into a list of numpy arrays. 27 | 28 | >>> Vs = [np.array(F['geometry']['coordinates'][0]) for F in data['features']] 29 | 30 | Convert the points into UTM coordinates (District of Columbia's UTM zone is 18). 31 | 32 | >>> proj = pyproj.Proj(proj='utm', zone=18, ellps='WGS84') 33 | >>> Ws = [np.c_[proj(V[:, 0].tolist(), V[:, 1].tolist())] for V in Vs] 34 | 35 | Copy and paste the following function that converts a polygon into a point set. 36 | 37 | .. code-block:: python 38 | 39 | def sample_polygon(V, eps=0.25): 40 | # samples polygon V s.t. consecutive samples are no greater than eps apart 41 | # assumes last vertex in V is a duplicate of the first 42 | M = np.ceil(np.sqrt(np.sum(np.diff(V, axis=0) ** 2, axis = 1)) / eps) 43 | Q = [] 44 | for (m, v1, v2) in zip(M, V[: -1], V[1:]): 45 | Q.append(np.vstack([ \ 46 | np.linspace(v1[0], v2[0], m, endpoint = False), \ 47 | np.linspace(v1[1], v2[1], m, endpoint = False)]).T) 48 | Q = np.vstack(Q) 49 | return Q 50 | 51 | Then apply :py:func:`sample_polygon` to all polygons in :code:`Ws` and 52 | concatenate the vertices into a single numpy array. 53 | 54 | .. code-block:: python 55 | 56 | >>> P = np.vstack([sample_polygon(W) for W in Ws]) 57 | 58 | Add a third zero column. 59 | 60 | .. code-block:: python 61 | 62 | >>> P = np.c_[P, np.zeros(len(P))] 63 | 64 | Recenter the points to remove rendering glitches associated 65 | with rendering points with large coordinate values. 66 | 67 | .. code-block:: python 68 | 69 | >>> P -= np.mean(P, axis=0)[None, :] 70 | 71 | Visualize. 72 | 73 | .. code-block:: python 74 | 75 | >>> v = pptk.viewer(P) 76 | >>> v.set(point_size=0.1) 77 | 78 | .. |footprints_dc| image:: images/footprints_dc.png 79 | :width: 375px 80 | :align: middle 81 | 82 | .. |footprints_dc_zoom| image:: images/footprints_dc_zoom.png 83 | :width: 375px 84 | :align: middle 85 | 86 | .. rst-class:: image-grid 87 | .. table:: 88 | :widths: 390 390 89 | :align: center 90 | 91 | =============== ==================== 92 | |footprints_dc| |footprints_dc_zoom| 93 | =============== ==================== 94 | 95 | .. rst-class:: caption 96 | 97 | +----------------------------------------------------------------------------------------+ 98 | | :file:`DistrictofColumbia.geojson` polygons from Microsoft's | 99 | | `US building footprints dataset `__ | 100 | | converted into a point set and viewed using :py:meth:`pptk.viewer()` | 101 | +----------------------------------------------------------------------------------------+ 102 | 103 | The above procedure can be repeated for other .geojson files in the dataset. 104 | But note that converting polygons into a point set can become memory intensive. 105 | By the above procedure, the District of Columbia's 58,329 polygons yields 18,465,292 points. 106 | This number of points is easily handled by pptk's viewer, 107 | but the number of points may become prohibitive for larger .geojson files in the dataset. 108 | To visualize the larger .geojson files consider breaking them into smaller pieces. 109 | 110 | 111 | Though programs such as `QGIS `__ 112 | may be more suitable for visualizing building footprints, 113 | especially if one needs to overlay the footprints on satellite imagery, 114 | pptk's viewer can still at times be useful for quickly visualizing results of 115 | polygon calculations in Python. 116 | -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/footprints_dc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/footprints_dc.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/footprints_dc_zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/footprints_dc_zoom.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/geolife_latlon_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/geolife_latlon_default.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/geolife_latlon_labeled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/geolife_latlon_labeled.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/geolife_legend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/geolife_legend.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/geolife_utm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/geolife_utm.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/geolife_utm_zoomed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/geolife_utm_zoomed.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/mobius.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/mobius.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/mobius_x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/mobius_x.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/mobius_y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/mobius_y.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/mobius_z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/mobius_z.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/saddle_mag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/saddle_mag.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/saddle_n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/saddle_n.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/saddle_z.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/saddle_z.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/semantic3d_large_i.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/semantic3d_large_i.jpg -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/semantic3d_large_labels.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/semantic3d_large_labels.jpg -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/semantic3d_large_rgb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/semantic3d_large_rgb.jpg -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/semantic3d_small_i.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/semantic3d_small_i.jpg -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/semantic3d_small_labels.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/semantic3d_small_labels.jpg -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/semantic3d_small_rgb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/semantic3d_small_rgb.jpg -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/tanks_and_temples_truck_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/tanks_and_temples_truck_n.jpg -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/tanks_and_temples_truck_rgb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/tanks_and_temples_truck_rgb.jpg -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/images/tutorial_banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/docs/source/tutorials/viewer/images/tutorial_banner.png -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/mobius.rst: -------------------------------------------------------------------------------- 1 | .. title:: Visualizing a Mobius strip 2 | 3 | Mobius strip 4 | ============ 5 | 6 | Given the following parametrization of a `Mobius strip `__ 7 | 8 | .. math:: 9 | x(s,t) & = \left(1+\frac{t}{2}\cos\left(\frac{s}{2}\right)\right)\cos(s) \\ 10 | y(s,t) & = \left(1+\frac{t}{2}\cos\left(\frac{s}{2}\right)\right)\sin(s) \\ 11 | z(s,t) & = \frac{t}{2}\sin\left(\frac{s}{2}\right) 12 | 13 | where :math:`0\le s\le 2\pi` and :math:`-1\le t\le 1`. 14 | 15 | Define uniformly spaced samples in parameters :math:`s` and :math:`t`. 16 | 17 | >>> import numpy as np 18 | >>> s = np.linspace(0.0, 2 * np.pi, 1000)[None, :] 19 | >>> t = np.linspace(-1.0, 1.0, 50)[:, None] 20 | 21 | Evaluate the above parametric equations using parameter samples :code:`s` and :code:`t`. 22 | 23 | >>> x = (1 + 0.5 * t * np.cos(0.5 * s)) * np.cos(s) 24 | >>> y = (1 + 0.5 * t * np.cos(0.5 * s)) * np.sin(s) 25 | >>> z = 0.5 * t * np.sin(0.5 * s) 26 | >>> P = np.stack([x, y, z], axis=-1) 27 | 28 | Calculate normals. 29 | 30 | >>> N = np.cross(np.gradient(P, axis=1), np.gradient(P, axis=0)) 31 | >>> N /= np.sqrt(np.sum(N ** 2, axis=-1))[:, :, None] 32 | 33 | Visualize. 34 | 35 | >>> import pptk 36 | >>> v = pptk.viewer(P) 37 | >>> v.attributes(0.5 * (N.reshape(-1, 3) + 1)) 38 | >>> v.set(point_size=0.001) 39 | 40 | .. |mobius| image:: images/mobius.png 41 | :width: 256px 42 | :align: middle 43 | 44 | .. |mobius_x| image:: images/mobius_x.png 45 | :width: 256px 46 | :align: middle 47 | 48 | .. |mobius_y| image:: images/mobius_y.png 49 | :width: 256px 50 | :align: middle 51 | 52 | .. |mobius_z| image:: images/mobius_z.png 53 | :width: 256px 54 | :align: middle 55 | 56 | .. rst-class:: image-grid 57 | .. table:: 58 | :align: center 59 | :widths: 270 270 270 270 60 | 61 | ======== ========== ========== ========== 62 | |mobius| |mobius_x| |mobius_y| |mobius_z| 63 | ======== ========== ========== ========== 64 | 65 | .. rst-class:: caption 66 | 67 | +---------------------------------------------------------------------------+ 68 | | Visualization of a mobius strip using :py:meth:`pptk.viewer`. | 69 | | Points are colored by normal directions. | 70 | | And the latter three images are views along the -x, +y and -z directions. | 71 | +---------------------------------------------------------------------------+ 72 | -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/saddle.rst: -------------------------------------------------------------------------------- 1 | .. title:: Visualizing a hyperbolic paraboloid 2 | 3 | Hyperbolic paraboloid 4 | ===================== 5 | 6 | Given `hyperbolic paraboloid `_ 7 | 8 | .. math:: 9 | f(x,y) = x^2-y^2 10 | 11 | define corresponding Python function :py:func:`f` 12 | 13 | .. code-block:: python 14 | 15 | >>> def f(x, y): 16 | return x ** 2 - y ** 2 17 | 18 | Evaluate :py:func:`f` on 100 x 100 uniformly spaced samples in [-1, 1] x [-1, 1]. 19 | 20 | .. code-block:: python 21 | 22 | >>> import numpy as np 23 | >>> t = np.linspace(-1.0, 1.0, 100) 24 | >>> x, y = np.meshgrid(t, t) 25 | >>> z = f(x, y) 26 | 27 | Concatenate x, y and z into a single array of points. 28 | 29 | .. code-block:: python 30 | 31 | >>> P = np.stack([x, y, z], axis=-1).reshape(-1, 3) 32 | 33 | Visualize. 34 | 35 | .. code-block:: python 36 | 37 | >>> import pptk 38 | >>> v = pptk.viewer(P) 39 | >>> v.attributes(P[:, 2]) # color points by their z-coordinates 40 | >>> v.set(point_size=0.005) 41 | 42 | One can also calculate and visualize additional per-point attributes. 43 | 44 | .. code-block:: python 45 | 46 | >>> # calculate gradients 47 | >>> dPdx = np.stack([np.gradient(x, axis=1), np.gradient(y, axis=1), np.gradient(z, axis=1)], axis=-1) 48 | >>> dPdy = np.stack([np.gradient(x, axis=0), np.gradient(y, axis=0), np.gradient(z, axis=0)], axis=-1) 49 | 50 | >>> # calculate gradient magnitudes 51 | >>> mag = np.sqrt(np.sum(dPdx ** 2 + dPdy ** 2, axis=-1)).flatten() 52 | 53 | >>> # calculate normal vectors 54 | >>> N = np.cross(dPdx, dPdy, axis=-1) 55 | >>> N /= np.sqrt(np.sum(N ** 2, axis=-1))[:, :, None] 56 | >>> N = N.reshape(-1, 3) 57 | 58 | >>> # set per-point attributes 59 | >>> v.attributes(P[:, 2], mag, 0.5 * (N + 1)) 60 | 61 | Toggle between attributes using the :kbd:`[` and :kbd:`]` keys. 62 | 63 | .. |saddle_z| image:: images/saddle_z.png 64 | :width: 256px 65 | :align: middle 66 | 67 | .. |saddle_mag| image:: images/saddle_mag.png 68 | :width: 256px 69 | :align: middle 70 | 71 | .. |saddle_n| image:: images/saddle_n.png 72 | :width: 256px 73 | :align: middle 74 | 75 | .. rst-class:: image-grid 76 | .. table:: 77 | :widths: 275 275 275 78 | :align: center 79 | 80 | ========== ============ ========== 81 | |saddle_z| |saddle_mag| |saddle_n| 82 | ========== ============ ========== 83 | 84 | .. rst-class:: caption 85 | 86 | +------------------------------------------------------------------------------------------------------+ 87 | | Visualization of :math:`f(x,y)=x^2-y^2`. | 88 | | Points are colored by :math:`f` (left), :math:`|\nabla f|` (middle), and normal directions (right) | 89 | +------------------------------------------------------------------------------------------------------+ 90 | -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/semantic3d.rst: -------------------------------------------------------------------------------- 1 | .. title:: Visualizing the Semantic3D dataset 2 | 3 | Semantic3D 4 | ========== 5 | 6 | .. _Semantic3D: http://semantic3d.net 7 | 8 | Download the following zip files from the `Semantic3D`_ website. 9 | 10 | * :file:`bildstein_station1_xyz_intensity_rgb.7z` (0.20 GB) 11 | [`download `__] 12 | * :file:`sem8_labels_training.7z` (0.01 GB) 13 | [`download `__] 14 | 15 | Extract the zip files and check that you have at least the following files. 16 | 17 | * :file:`bildstein_station1_xyz_intensity_rgb.txt` 18 | * :file:`bildstein_station1_xyz_intensity_rgb.labels` 19 | 20 | Point clouds are stored in .txt files, and their per-point labels are stored in corresponding .label files. 21 | 22 | In a Python terminal, import the following Python packages. 23 | 24 | .. code-block:: python 25 | 26 | >>> import pptk 27 | >>> import pandas as pd 28 | >>> import numpy as np 29 | 30 | Copy and paste the following function definitions for reading Semantic3D .txt and .labels files into your Python terminal. 31 | Note these functions are merely simple wrappers around the :py:func:`pandas.read_csv` function. 32 | 33 | .. code-block:: python 34 | 35 | def read_points(f): 36 | # reads Semantic3D .txt file f into a pandas dataframe 37 | col_names = ['x', 'y', 'z', 'i', 'r', 'g', 'b'] 38 | col_dtype = {'x': np.float32, 'y': np.float32, 'z': np.float32, 'i': np.int32, 39 | 'r': np.uint8, 'g': np.uint8, 'b': np.uint8} 40 | return pd.read_csv(f, names=col_names, dtype=col_dtype, delim_whitespace=True) 41 | 42 | .. code-block:: python 43 | 44 | def read_labels(f): 45 | # reads Semantic3D .labels file f into a pandas dataframe 46 | return pd.read_csv(f, header=None)[0].values 47 | 48 | Read :file:`bildstein_station1_xyz_intensity_rgb.txt`. 49 | 50 | .. code-block:: python 51 | 52 | >>> points = read_points('bildstein_station1_xyz_intensity_rgb.txt') 53 | >>> points 54 | x y z i r g b 55 | 0 20.622999 40.276001 -1.999 -1031 127 141 154 56 | 1 20.362000 40.375000 -2.239 -941 130 141 159 57 | 2 20.360001 40.375999 -2.402 -1083 139 151 165 58 | ... ... ... ... ... ... ... ... 59 | 29697590 60.196999 82.137001 11.085 -1619 134 140 172 60 | 61 | [29697591 rows x 7 columns] 62 | 63 | Read :file:`bildstein_station1_xyz_intensity_rgb.labels`. 64 | 65 | .. code-block:: python 66 | 67 | >>> labels = read_labels('bildstein_station1_xyz_intensity_rgb.labels') 68 | >>> labels 69 | array([0, 0, 6, ..., 0, 0, 0], dtype=int64) 70 | 71 | Visualize (refer to the :doc:`viewer page <../../viewer>` for control details). 72 | 73 | >>> v = pptk.viewer(points[['x', 'y', 'z']]) 74 | >>> v.attributes(points[['r', 'g', 'b']] / 255., points['i']) 75 | >>> v.set(point_size=0.001) 76 | 77 | Visualize point labels, showing only points with non-zero labels (in this dataset label 0 denotes lack of a label). 78 | 79 | >>> mask = labels != 0 80 | >>> P = points[mask] 81 | >>> L = labels[mask] 82 | 83 | >>> v = pptk.viewer(P[['x', 'y', 'z']]) 84 | >>> v.attributes(P[['r', 'g', 'b']] / 255., P['i'], L) 85 | >>> v.set(point_size=0.001) 86 | 87 | Toggle between attributes using the :kbd:`[` and :kbd:`]` keys. 88 | 89 | .. |semantic3d_small_rgb| image:: images/semantic3d_small_rgb.jpg 90 | :width: 300px 91 | :align: middle 92 | 93 | .. |semantic3d_small_i| image:: images/semantic3d_small_i.jpg 94 | :width: 300px 95 | :align: middle 96 | 97 | .. |semantic3d_small_labels| image:: images/semantic3d_small_labels.jpg 98 | :width: 300px 99 | :align: middle 100 | 101 | .. rst-class:: image-grid 102 | .. table:: 103 | :widths: 310 310 310 104 | :align: center 105 | 106 | ====================== ==================== ========================= 107 | |semantic3d_small_rgb| |semantic3d_small_i| |semantic3d_small_labels| 108 | ====================== ==================== ========================= 109 | 110 | .. rst-class:: caption 111 | 112 | +-------------------------------------------------------------------------------+ 113 | | The `Semantic3D`_ :file:`bildstein1` point cloud, | 114 | | with 0-labeled points removed, visualized using :py:meth:`pptk.viewer`. | 115 | | Points are colored by RGB (left), intensity (middle), semantic label (right). | 116 | +-------------------------------------------------------------------------------+ 117 | 118 | Basic support for point annotation 119 | ---------------------------------- 120 | 121 | **Selecting points**. 122 | Holding :kbd:`Ctrl` while performing a :kbd:`LMB` drag creates an "additive" selection box; 123 | when the :kbd:`LMB` is released, all points in the box are added into a set of currently selected points. 124 | Holding :kbd:`Ctrl-Shift` while performing the :kbd:`LMB` drag creates a "subtractive" selection box; 125 | when the mouse button is released, all points in the box are removed from the set of currently selected points. 126 | Individual points can be added or removed from the set 127 | by left clicking on a point while holding :kbd:`Ctrl` or :kbd:`Ctrl-Shift`. 128 | Right click to deselect all. 129 | 130 | .. note:: 131 | On Mac, use :kbd:`⌘` instead of :kbd:`Ctrl` 132 | 133 | Query the selected point indices using :py:meth:`pptk.viewer.get`. 134 | 135 | .. code-block:: python 136 | 137 | >>> indices = v.get('selected') 138 | 139 | When editting a selection containing a large number of points, consider "saving" often. 140 | If the selection is ever lost (e.g. accidental deselection), 141 | the selection can be restored using :py:meth:`pptk.viewer.set`. 142 | 143 | .. code-block:: python 144 | 145 | >>> v.set(selected=indices) 146 | 147 | Visualizing very large point clouds 148 | ----------------------------------- 149 | 150 | The above procedure can be repeated for other point clouds in Semantic 3D. 151 | However, pptk viewer may fail to start for larger inputs 152 | (the actual input size depends on system and GPU memory; 153 | on certain machines this is known to happen for inputs larger than roughly 100M points). 154 | One workaround is to subsample (keep every k-th point) the input point cloud. 155 | Though imperfect, this in many cases can still give a reasonably accurate view of the point cloud. 156 | 157 | Here we show how to visualize the largest point cloud in `Semantic3D`_. 158 | 159 | * :code:`sg27_station2_intensity_rgb.7z` (2.72 GB) 160 | [`download `__] 161 | 162 | Read points 163 | (this may take a few minutes as the point cloud is quite large; 164 | on a test machine this took ~6 min to load and used ~9GB of memory). 165 | 166 | .. code-block:: python 167 | 168 | >>> points = read_points('sg27_station2_intensity_rgb.txt') 169 | >>> points 170 | x y z i r g b 171 | 0 -79.001999 15.598000 10.773 -1709 152 126 99 172 | 1 -101.685997 25.760000 15.934 -1111 32 22 21 173 | 2 -101.842003 25.563999 15.966 -1279 131 102 84 174 | ... ... ... ... ... ... ... ... 175 | 496702860 42.551998 94.640999 2.531 -1479 140 108 113 176 | 177 | [496702861 rows x 7 columns] 178 | 179 | (Optional) Consider saving points to .npy files for faster loading in the future. 180 | 181 | .. code-block:: python 182 | 183 | >>> np.save('sg27_station2.xyz.npy', points[['x', 'y', 'z']].values) 184 | >>> np.save('sg27_station2.rgb.npy', points[['r', 'g', 'b']].values) 185 | >>> np.save('sg27_station2.i.npy', points['i'].values) 186 | 187 | Read labels. 188 | 189 | .. code-block:: python 190 | 191 | >>> labels = read_labels('sg27_station2_intensity_rgb.labels') 192 | >>> labels 193 | array([2, 2, 2, ..., 6, 6, 6], dtype=int64) 194 | 195 | Subsample points and labels by keeping only every 6-th point. 196 | This leaves 82,783,811 points (just under 100M points). 197 | 198 | .. code-block:: python 199 | 200 | >>> P = points[:: 6] 201 | >>> L = labels[:: 6] 202 | 203 | Remove 0-labeled points. 204 | 205 | .. code-block:: python 206 | 207 | >>> mask = L != 0 208 | >>> P = P[mask] 209 | >>> L = L[mask] 210 | 211 | Visualize. 212 | 213 | .. code-block:: python 214 | 215 | >>> v = pptk.viewer(P[['x', 'y', 'z']]) 216 | >>> v.attributes(P[['r', 'g', 'b']] / 255., P['i'], L) 217 | >>> v.set(point_size=0.001) 218 | 219 | .. image:: images/semantic3d_large_rgb.jpg 220 | :width: 90% 221 | :align: center 222 | 223 | .. image:: images/semantic3d_large_i.jpg 224 | :width: 90% 225 | :align: center 226 | 227 | .. image:: images/semantic3d_large_labels.jpg 228 | :width: 90% 229 | :align: center 230 | 231 | .. rst-class:: caption 232 | 233 | +----------------------------------------------------------------------------------------------+ 234 | | The `Semantic3D`_ :file:`sg27_station2` point cloud visualized using :py:meth:`pptk.viewer`. | 235 | | Point cloud has been subsampled by a factor of 6 and removed of 0-labeled points. | 236 | | Points are colored by RGB (top), intensity (middle), semantic label (bottom). | 237 | +----------------------------------------------------------------------------------------------+ 238 | 239 | -------------------------------------------------------------------------------- /docs/source/tutorials/viewer/tanks_and_temples.rst: -------------------------------------------------------------------------------- 1 | .. title:: Visualizing the Tanks and Temples dataset 2 | 3 | Tanks and Temples 4 | ================= 5 | 6 | `Tank and Temples `_ 7 | is a benchmark that uses Lidar point clouds as ground truth 8 | for benchmarking the quality of image-based 3-d reconstruction algorithms. 9 | The point clouds are stored as .ply files. 10 | Here we show how to load and visualize these point clouds. 11 | 12 | Download :code:`Truck.ply` (392 MB) 13 | `[link] `__. 14 | 15 | Import required Python packages. 16 | Here we use the plyfile Python package to read .ply files. 17 | 18 | .. code-block:: python 19 | 20 | >>> import pptk 21 | >>> import numpy as np 22 | >>> import plyfile 23 | 24 | .. note:: 25 | If :code:`pip install plyfile` does not work, 26 | simply save a local copy of plyfile.py from plyfile's `github page `__. 27 | 28 | Read vertices in :code:`Truck.ply`. 29 | 30 | .. code-block:: python 31 | 32 | >>> data = plyfile.PlyData.read('Truck.ply')['vertex'] 33 | 34 | Use per-vertex attributes to make numpy arrays :code:`xyz`, :code:`rgb`, and :code:`n`. 35 | 36 | >>> xyz = np.c_[data['x'], data['y'], data['z']] 37 | >>> rgb = np.c_[data['red'], data['green'], data['blue']] 38 | >>> n = np.c_[data['nx'], data['ny'], data['nz']] 39 | 40 | Visualize. 41 | 42 | >>> v = pptk.viewer(xyz) 43 | >>> v.attributes(rgb / 255., 0.5 * (1 + n)) 44 | 45 | Use :kbd:`[` and :kbd:`]` to toggle between attributes. 46 | 47 | .. |truck_rgb| image:: images/tanks_and_temples_truck_rgb.jpg 48 | :width: 340px 49 | :align: middle 50 | 51 | .. |truck_n| image:: images/tanks_and_temples_truck_n.jpg 52 | :width: 340px 53 | :align: middle 54 | 55 | .. rst-class:: image-grid 56 | 57 | .. table:: 58 | :widths: 350 350 59 | :align: center 60 | 61 | =========== ========= 62 | |truck_rgb| |truck_n| 63 | =========== ========= 64 | 65 | .. rst-class:: caption 66 | 67 | +----------------------------------------------------------------------------------------------+ 68 | | The :file:`Truck.py` point cloud from `Tanks and Temples `_ | 69 | | visualized using :py:meth:`pptk.viewer`. Points are colored by RGB (left) and normal (right) | 70 | +----------------------------------------------------------------------------------------------+ 71 | 72 | The above procedure can be repeated for the other point clouds in the dataset. 73 | -------------------------------------------------------------------------------- /docs/source/viewer.rst: -------------------------------------------------------------------------------- 1 | Point cloud viewer 2 | ================== 3 | 4 | The :py:meth:`pptk.viewer` function enables one to directly visualize large point clouds in Python. 5 | It accepts as input any Python variable that can be cast as a 3-column numpy array (i.e. via :py:func:`np.asarray`). 6 | It interprets the columns of such input as the x, y, and z coordinates of a point cloud. 7 | 8 | The viewer itself runs as a standalone operating system process separate from Python. 9 | The user can query and manipulate the viewer via the handle that is returned by :py:meth:`pptk.viewer`. 10 | The handle encapsulates the details of communicating with the viewer (e.g. the viewer process's port number) 11 | and provides methods for querying and manipulating the viewer. 12 | 13 | The viewer supports interactive visualization of tens of millions of points via an octree-based level-of-detail renderer. 14 | At startup the viewer organizes the input points into an octree. 15 | As the viewpoint is being manipulated, 16 | the octree is used to approximate groups of far away points as single points and cull points that are outside the view frustum, 17 | thus greatly reducing the number of points being rendered. 18 | Once there are no more changes to the viewpoint, 19 | the viewer then proceeds to perform a more time consuming detailed rendering of the points. 20 | 21 | .. note:: 22 | Currently the viewer crashes for larger number of points. 23 | The actual number depends on system and GPU memory. 24 | On certain machines this is known to start happening around 100M points. 25 | 26 | Controls 27 | -------- 28 | 29 | **View manipulation**. 30 | The camera look at position is denoted by a red-green-blue cursor, 31 | with segments corresponding to x, y and z axes. 32 | Perform :kbd:`LMB` drag to rotate the viewpoint in a turntable fashion. 33 | Perform :kbd:`LMB` drag while hold :kbd:`Shift` to pan the viewpoint. 34 | Double clicking the :kbd:`LMB` moves the look at position to a point near the mouse cursor. 35 | 36 | .. note:: 37 | :kbd:`LMB`/:kbd:`RMB` stands for left/right mouse button 38 | 39 | **Point selection**. 40 | Hold :kbd:`Ctrl`/:kbd:`Ctrl-Shift` while :kbd:`LMB` dragging a box to add/remove points to the current selection. 41 | Hold :kbd:`Ctrl`/:kbd:`Ctrl-Shift` while :kbd:`LMB` clicking on a point 42 | to add/remove a *single* point from the current selection. 43 | Click the :kbd:`RMB` to clear the current selection. 44 | Use :py:meth:`pptk.viewer.get` to query the selected point indices. 45 | 46 | .. note:: 47 | If you are using a Mac, use :kbd:`⌘` in place of :kbd:`Ctrl`. 48 | 49 | **Hot keys**. 50 | 51 | ======== ======================================================== 52 | key Description 53 | ======== ======================================================== 54 | :kbd:`5` Toggle between orthographic/perspective projection 55 | :kbd:`1` Look along +y direction 56 | :kbd:`3` Look along -x direction 57 | :kbd:`7` Look along -z direction 58 | :kbd:`C` Set look at position to mean position of selected points 59 | :kbd:`[` Toggle previous attribute set 60 | :kbd:`]` Toggle next attribute set 61 | ======== ======================================================== 62 | 63 | Methods 64 | ------- 65 | 66 | .. autoclass:: pptk.viewer 67 | :members: 68 | :special-members: 69 | -------------------------------------------------------------------------------- /licenses/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | macro(remove_header x) 3 | string(REGEX REPLACE "^[^-]+" "" ${x} "${${x}}") 4 | endmacro(remove_header) 5 | 6 | macro(read_text x y) 7 | file(READ ${x} ${y}) 8 | remove_header(${y}) 9 | endmacro(read_text) 10 | 11 | macro(append_text x y) 12 | # reads text in file specified by x, removes header from text, 13 | # and then appends results to y 14 | # assumes x is a file 15 | read_text(${x} temp) 16 | string(CONCAT ${y} "${${y}}" "${temp}") 17 | endmacro(append_text) 18 | 19 | if(WIN32) 20 | read_text(LICENSE.win.txt added_text) 21 | elseif(APPLE) 22 | read_text(LICENSE.mac.txt added_text) 23 | append_text(COPYING.gcc_rle31.txt added_text) 24 | elseif(UNIX) 25 | read_text(LICENSE.linux.txt added_text) 26 | endif() 27 | 28 | append_text(COPYING.lgpl3.txt added_text) 29 | append_text(COPYING.gpl3.txt added_text) 30 | append_text(COPYING.apache2.txt added_text) 31 | 32 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/LICENSE.append.txt "${added_text}") 33 | -------------------------------------------------------------------------------- /licenses/COPYING.gcc_rle31.txt: -------------------------------------------------------------------------------- 1 | The following text is intended for inclusion in a pptk wheel file and does not 2 | apply to the present source distribution. 3 | ---- 4 | 5 | GCC RUNTIME LIBRARY EXCEPTION 6 | 7 | Version 3.1, 31 March 2009 8 | 9 | Copyright (C) 2009 Free Software Foundation, Inc. 10 | 11 | Everyone is permitted to copy and distribute verbatim copies of this 12 | license document, but changing it is not allowed. 13 | 14 | This GCC Runtime Library Exception ("Exception") is an additional 15 | permission under section 7 of the GNU General Public License, version 16 | 3 ("GPLv3"). It applies to a given file (the "Runtime Library") that 17 | bears a notice placed by the copyright holder of the file stating that 18 | the file is governed by GPLv3 along with this Exception. 19 | 20 | When you use GCC to compile a program, GCC may combine portions of 21 | certain GCC header files and runtime libraries with the compiled 22 | program. The purpose of this Exception is to allow compilation of 23 | non-GPL (including proprietary) programs to use, in this way, the 24 | header files and runtime libraries covered by this Exception. 25 | 26 | 0. Definitions. 27 | 28 | A file is an "Independent Module" if it either requires the Runtime 29 | Library for execution after a Compilation Process, or makes use of an 30 | interface provided by the Runtime Library, but is not otherwise based 31 | on the Runtime Library. 32 | 33 | "GCC" means a version of the GNU Compiler Collection, with or without 34 | modifications, governed by version 3 (or a specified later version) of 35 | the GNU General Public License (GPL) with the option of using any 36 | subsequent versions published by the FSF. 37 | 38 | "GPL-compatible Software" is software whose conditions of propagation, 39 | modification and use would permit combination with GCC in accord with 40 | the license of GCC. 41 | 42 | "Target Code" refers to output from any compiler for a real or virtual 43 | target processor architecture, in executable form or suitable for 44 | input to an assembler, loader, linker and/or execution 45 | phase. Notwithstanding that, Target Code does not include data in any 46 | format that is used as a compiler intermediate representation, or used 47 | for producing a compiler intermediate representation. 48 | 49 | The "Compilation Process" transforms code entirely represented in 50 | non-intermediate languages designed for human-written code, and/or in 51 | Java Virtual Machine byte code, into Target Code. Thus, for example, 52 | use of source code generators and preprocessors need not be considered 53 | part of the Compilation Process, since the Compilation Process can be 54 | understood as starting with the output of the generators or 55 | preprocessors. 56 | 57 | A Compilation Process is "Eligible" if it is done using GCC, alone or 58 | with other GPL-compatible software, or if it is done without using any 59 | work based on GCC. For example, using non-GPL-compatible Software to 60 | optimize any GCC intermediate representations would not qualify as an 61 | Eligible Compilation Process. 62 | 63 | 1. Grant of Additional Permission. 64 | 65 | You have permission to propagate a work of Target Code formed by 66 | combining the Runtime Library with Independent Modules, even if such 67 | propagation would otherwise violate the terms of GPLv3, provided that 68 | all Target Code was generated by Eligible Compilation Processes. You 69 | may then convey such a combination under terms of your choice, 70 | consistent with the licensing of the Independent Modules. 71 | 72 | 2. No Weakening of GCC Copyleft. 73 | 74 | The availability of this Exception does not imply any general 75 | presumption that third-party software is unaffected by the copyleft 76 | requirements of the license of GCC. 77 | 78 | -------------------------------------------------------------------------------- /licenses/COPYING.lgpl3.txt: -------------------------------------------------------------------------------- 1 | The following text is intended for inclusion in a pptk wheel file and does not 2 | apply to the present source distribution. 3 | ---- 4 | 5 | GNU LESSER GENERAL PUBLIC LICENSE 6 | Version 3, 29 June 2007 7 | 8 | Copyright (C) 2007 Free Software Foundation, Inc. 9 | Everyone is permitted to copy and distribute verbatim copies 10 | of this license document, but changing it is not allowed. 11 | 12 | 13 | This version of the GNU Lesser General Public License incorporates 14 | the terms and conditions of version 3 of the GNU General Public 15 | License, supplemented by the additional permissions listed below. 16 | 17 | 0. Additional Definitions. 18 | 19 | As used herein, "this License" refers to version 3 of the GNU Lesser 20 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 21 | General Public License. 22 | 23 | "The Library" refers to a covered work governed by this License, 24 | other than an Application or a Combined Work as defined below. 25 | 26 | An "Application" is any work that makes use of an interface provided 27 | by the Library, but which is not otherwise based on the Library. 28 | Defining a subclass of a class defined by the Library is deemed a mode 29 | of using an interface provided by the Library. 30 | 31 | A "Combined Work" is a work produced by combining or linking an 32 | Application with the Library. The particular version of the Library 33 | with which the Combined Work was made is also called the "Linked 34 | Version". 35 | 36 | The "Minimal Corresponding Source" for a Combined Work means the 37 | Corresponding Source for the Combined Work, excluding any source code 38 | for portions of the Combined Work that, considered in isolation, are 39 | based on the Application, and not on the Linked Version. 40 | 41 | The "Corresponding Application Code" for a Combined Work means the 42 | object code and/or source code for the Application, including any data 43 | and utility programs needed for reproducing the Combined Work from the 44 | Application, but excluding the System Libraries of the Combined Work. 45 | 46 | 1. Exception to Section 3 of the GNU GPL. 47 | 48 | You may convey a covered work under sections 3 and 4 of this License 49 | without being bound by section 3 of the GNU GPL. 50 | 51 | 2. Conveying Modified Versions. 52 | 53 | If you modify a copy of the Library, and, in your modifications, a 54 | facility refers to a function or data to be supplied by an Application 55 | that uses the facility (other than as an argument passed when the 56 | facility is invoked), then you may convey a copy of the modified 57 | version: 58 | 59 | a) under this License, provided that you make a good faith effort to 60 | ensure that, in the event an Application does not supply the 61 | function or data, the facility still operates, and performs 62 | whatever part of its purpose remains meaningful, or 63 | 64 | b) under the GNU GPL, with none of the additional permissions of 65 | this License applicable to that copy. 66 | 67 | 3. Object Code Incorporating Material from Library Header Files. 68 | 69 | The object code form of an Application may incorporate material from 70 | a header file that is part of the Library. You may convey such object 71 | code under terms of your choice, provided that, if the incorporated 72 | material is not limited to numerical parameters, data structure 73 | layouts and accessors, or small macros, inline functions and templates 74 | (ten or fewer lines in length), you do both of the following: 75 | 76 | a) Give prominent notice with each copy of the object code that the 77 | Library is used in it and that the Library and its use are 78 | covered by this License. 79 | 80 | b) Accompany the object code with a copy of the GNU GPL and this license 81 | document. 82 | 83 | 4. Combined Works. 84 | 85 | You may convey a Combined Work under terms of your choice that, 86 | taken together, effectively do not restrict modification of the 87 | portions of the Library contained in the Combined Work and reverse 88 | engineering for debugging such modifications, if you also do each of 89 | the following: 90 | 91 | a) Give prominent notice with each copy of the Combined Work that 92 | the Library is used in it and that the Library and its use are 93 | covered by this License. 94 | 95 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 96 | document. 97 | 98 | c) For a Combined Work that displays copyright notices during 99 | execution, include the copyright notice for the Library among 100 | these notices, as well as a reference directing the user to the 101 | copies of the GNU GPL and this license document. 102 | 103 | d) Do one of the following: 104 | 105 | 0) Convey the Minimal Corresponding Source under the terms of this 106 | License, and the Corresponding Application Code in a form 107 | suitable for, and under terms that permit, the user to 108 | recombine or relink the Application with a modified version of 109 | the Linked Version to produce a modified Combined Work, in the 110 | manner specified by section 6 of the GNU GPL for conveying 111 | Corresponding Source. 112 | 113 | 1) Use a suitable shared library mechanism for linking with the 114 | Library. A suitable mechanism is one that (a) uses at run time 115 | a copy of the Library already present on the user's computer 116 | system, and (b) will operate properly with a modified version 117 | of the Library that is interface-compatible with the Linked 118 | Version. 119 | 120 | e) Provide Installation Information, but only if you would otherwise 121 | be required to provide such information under section 6 of the 122 | GNU GPL, and only to the extent that such information is 123 | necessary to install and execute a modified version of the 124 | Combined Work produced by recombining or relinking the 125 | Application with a modified version of the Linked Version. (If 126 | you use option 4d0, the Installation Information must accompany 127 | the Minimal Corresponding Source and Corresponding Application 128 | Code. If you use option 4d1, you must provide the Installation 129 | Information in the manner specified by section 6 of the GNU GPL 130 | for conveying Corresponding Source.) 131 | 132 | 5. Combined Libraries. 133 | 134 | You may place library facilities that are a work based on the 135 | Library side by side in a single library together with other library 136 | facilities that are not Applications and are not covered by this 137 | License, and convey such a combined library under terms of your 138 | choice, if you do both of the following: 139 | 140 | a) Accompany the combined library with a copy of the same work based 141 | on the Library, uncombined with any other library facilities, 142 | conveyed under the terms of this License. 143 | 144 | b) Give prominent notice with the combined library that part of it 145 | is a work based on the Library, and explaining where to find the 146 | accompanying uncombined form of the same work. 147 | 148 | 6. Revised Versions of the GNU Lesser General Public License. 149 | 150 | The Free Software Foundation may publish revised and/or new versions 151 | of the GNU Lesser General Public License from time to time. Such new 152 | versions will be similar in spirit to the present version, but may 153 | differ in detail to address new problems or concerns. 154 | 155 | Each version is given a distinguishing version number. If the 156 | Library as you received it specifies that a certain numbered version 157 | of the GNU Lesser General Public License "or any later version" 158 | applies to it, you have the option of following the terms and 159 | conditions either of that published version or of any later version 160 | published by the Free Software Foundation. If the Library as you 161 | received it does not specify a version number of the GNU Lesser 162 | General Public License, you may choose any version of the GNU Lesser 163 | General Public License ever published by the Free Software Foundation. 164 | 165 | If the Library as you received it specifies that a proxy can decide 166 | whether future versions of the GNU Lesser General Public License shall 167 | apply, that proxy's public statement of acceptance of any version is 168 | permanent authorization for you to choose that version for the 169 | Library. 170 | 171 | -------------------------------------------------------------------------------- /licenses/LICENSE.linux.txt: -------------------------------------------------------------------------------- 1 | The following text is intended for inclusion in a pptk wheel file and does not 2 | apply to the present source distribution. 3 | ---- 4 | 5 | This binary distribution of pptk also bundles the following software: 6 | 7 | Name: Qt 5.6 8 | Files: libicudata.so.56, libicui18n.so.56, libicuuc.so.56, libQt5Core.so.5, 9 | libQt5DBus.so.5, libQt5Gui.so.5, libQt5Network.so.5, libQt5Widgets.so.5, 10 | libQt5XcbQpa.so.5, libqxcb.so, libqxcb-egl-integration.so, 11 | libqxcb-glx-integration.so 12 | Description: Bundled as dynamically linked library 13 | Availability: https://www.qt.io/ 14 | SPDX-License-Identifier: LGPL-3.0-or-later 15 | 16 | Name: Intel Threading Building Blocks 4.3 Update 2 17 | Files: libtbb.so.2, libtbbmalloc.so.2 18 | Description: Bundled as dynamically linked library 19 | Availability: https://www.threadingbuildingblocks.org/ 20 | SPDX-License-Identifier: Apache-2.0 21 | 22 | Name: zlib 23 | Files: zlib.so.1 24 | Description: Bundled as dynamically linked library 25 | Availability: zlib.net 26 | SPDX-License-Identifier: Zlib 27 | 28 | ---- 29 | 30 | Full text of license texts referred to above follows (that they are 31 | listed below does not necessarily imply the conditions apply to the 32 | present binary release): 33 | 34 | -------------------------------------------------------------------------------- /licenses/LICENSE.mac.txt: -------------------------------------------------------------------------------- 1 | The following text is intended for inclusion in a pptk wheel file and does not 2 | apply to the present source distribution. 3 | ---- 4 | 5 | This binary distribution of pptk also bundles the following software: 6 | 7 | Name: Qt 5.3 8 | Files: QtCore, QtGui, QtNetwork, QtOpenGL, QtPrintSupport, QtWidgets, 9 | libqcocoa.dylib 10 | Description: Bundled as dynamically linked library 11 | Availability: https://www.qt.io/ 12 | SPDX-License-Identifier: LGPL-3.0-or-later 13 | 14 | Name: Intel Threading Building Blocks 2017 Update 5 15 | Files: libtbb.dylib, libtbbmalloc.dylib 16 | Description: Bundled as dynamically linked library 17 | Availability: https://www.threadingbuildingblocks.org/ 18 | SPDX-License-Identifier: Apache-2.0 19 | 20 | Name: GCC runtime library 21 | Files: libgcc_s.1.dylib, libgomp.1.dylib, libstdc++.6.dylib 22 | Descriptions: Bundled as dynamically linked library 23 | Availability: https://gcc.gnu.org/ 24 | SPDX-License-Identifier: GPL-3.0-with-GCC-exception 25 | 26 | ---- 27 | 28 | Full text of license texts referred to above follows (that they are 29 | listed below does not necessarily imply the conditions apply to the 30 | present binary release): 31 | 32 | -------------------------------------------------------------------------------- /licenses/LICENSE.win.txt: -------------------------------------------------------------------------------- 1 | The following text is intended for inclusion in a pptk wheel file and does not 2 | apply to the present source distribution. 3 | ---- 4 | 5 | This binary distribution of pptk also bundles the following software: 6 | 7 | Name: Qt 5.3 8 | Files: icudt53.dll, icuin53.dll, icuuc53.dll, Qt5Core.dll, Qt5Gui.dll, 9 | Qt5Network.dll, Qt5Widgets.dll, qwindows.dll 10 | Description: Bundled as dynamically linked library 11 | Availability: https://www.qt.io/ 12 | SPDX-License-Identifier: LGPL-3.0-or-later 13 | 14 | Name: Intel Threading Building Blocks 4.3 Update 2 15 | Files: tbb.dll, tbbmalloc.dll 16 | Description: Bundled as dynamically linked library 17 | Availability: https://www.threadingbuildingblocks.org/ 18 | SPDX-License-Identifier: Apache-2.0 19 | 20 | Name: Visual C++ runtime libraries 21 | Files: msvcp120.dll, msvcr120.dll, vcomp120.dll 22 | Description: Bundled as dynamically linked library 23 | License: Microsoft Software License Terms 24 | https://docs.microsoft.com/en-us/visualstudio/productinfo/2013-redistribution-vs#visual-c-runtime-files 25 | 26 | ---- 27 | 28 | Full text of license texts referred to above follows (that they are 29 | listed below does not necessarily imply the conditions apply to the 30 | present binary release): 31 | 32 | -------------------------------------------------------------------------------- /pptk/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(kdtree) 2 | add_subdirectory(points) 3 | add_subdirectory(processing) 4 | add_subdirectory(vfuncs) 5 | add_subdirectory(viewer) 6 | add_subdirectory(libs) 7 | copy_file(__init__.py) 8 | copy_file(_add_path.py) -------------------------------------------------------------------------------- /pptk/__init__.py: -------------------------------------------------------------------------------- 1 | from . import _add_path 2 | from .viewer.viewer import * 3 | from .points.points import * 4 | from .points.expr import * 5 | from .kdtree import kdtree 6 | from .processing.estimate_normals.estimate_normals import estimate_normals 7 | -------------------------------------------------------------------------------- /pptk/_add_path.py: -------------------------------------------------------------------------------- 1 | import os 2 | import os.path 3 | import sys 4 | import platform 5 | 6 | 7 | def _add_path(): 8 | _root_dir = os.path.dirname(os.path.realpath(__file__)) 9 | 10 | # manual specify list of dll directories, with paths relative to _root_dir 11 | _dll_dirs = ['libs'] 12 | 13 | if platform.system() == 'Windows': 14 | os.environ.setdefault('PATH', '') 15 | paths = os.environ['PATH'].split(';') 16 | for x in _dll_dirs: 17 | x = os.path.join(_root_dir, x) 18 | if os.path.isdir(x) and x not in paths: 19 | paths = [x] + paths 20 | os.environ['PATH'] = ';'.join(paths) 21 | 22 | 23 | _add_path() 24 | -------------------------------------------------------------------------------- /pptk/include/pptk_dll_export.h: -------------------------------------------------------------------------------- 1 | #ifndef __PPTK_DLL_EXPORT_H__ 2 | #define __PPTK_DLL_EXPORT_H__ 3 | 4 | #if defined(_WIN32) || defined(_WIN64) 5 | #ifdef __cplusplus 6 | #define PPTK_DLL_EXPORT extern "C" __declspec(dllexport) 7 | #else 8 | #define PPTK_DLL_EXPORT __declspec(dllexport) 9 | #endif 10 | #else 11 | #define PPTK_DLL_EXPORT 12 | #endif 13 | 14 | #endif // __PPTK_DLL_EXPORT_H__ 15 | -------------------------------------------------------------------------------- /pptk/include/progress_bar.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROGRESS_BAR_H__ 2 | #define __PROGRESS_BAR_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class ProgressWheel { 9 | public: 10 | ProgressWheel() : c_("\\|/-"), counter_(0) {} 11 | void tick() { counter_ = (counter_ + 1) % 4; } 12 | char get_char() const { return c_[counter_]; } 13 | 14 | private: 15 | const std::string c_; 16 | int counter_; 17 | }; 18 | 19 | template 20 | class ProgressBar { 21 | public: 22 | ProgressBar(T num_iters) : len_(20), curr_iter_(0), num_iters_(num_iters) {} 23 | ProgressBar(T num_iters, int len) 24 | : len_(len), curr_iter_(0), num_iters_(num_iters) {} 25 | 26 | void update(T curr_iter) { 27 | wheel_.tick(); 28 | curr_iter_ = curr_iter; 29 | } 30 | 31 | std::string get_string() const { 32 | float p = (float)curr_iter_ / num_iters_; 33 | if (num_iters_ == 0) p = 1.0f; 34 | int n = std::max(0, std::min(len_, (int)(p * len_))); 35 | if (curr_iter_ == num_iters_) n = len_; 36 | std::stringstream ss; 37 | ss << (int)(p * 100); 38 | return "[" + std::string(n, '=') 39 | + std::string(len_ - n, '-') 40 | + wheel_.get_char() + "] " + ss.str() + "%"; 41 | } 42 | 43 | private: 44 | ProgressWheel wheel_; 45 | int len_; // progress bar length (# char) 46 | T curr_iter_; 47 | T num_iters_; 48 | }; 49 | 50 | #endif // __PROGRESS_BAR_H__ -------------------------------------------------------------------------------- /pptk/include/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIMER_H 2 | #define _TIMER_H 3 | #if defined(_WIN32) || defined(__CYGWIN__) 4 | #ifndef _WIN32 5 | #define PCTIMER_NO_WIN32 6 | #endif /* WIN32 */ 7 | #define WIN32_LEAN_AND_MEAN 8 | #include // req'd for QueryPerformance[...] 9 | #ifdef PCTIMER_NO_WIN32 10 | #undef PCTIMER_NO_WIN32 11 | #undef _WIN32 12 | #endif /* PCTIMER_NO_WIN32 */ 13 | __inline double getTime() { 14 | static LARGE_INTEGER pcount, pcfreq; 15 | static int initflag; 16 | if (!initflag) { 17 | QueryPerformanceFrequency(&pcfreq); 18 | initflag++; 19 | } 20 | QueryPerformanceCounter(&pcount); 21 | return (double)pcount.QuadPart / (double)pcfreq.QuadPart; 22 | } 23 | #else /* Not Win32/Cygwin */ 24 | #include 25 | #include 26 | 27 | __inline double getTime() { 28 | struct timeval tv; 29 | gettimeofday(&tv, NULL); 30 | return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; 31 | } 32 | #endif 33 | #endif /* _TIMER_H */ 34 | -------------------------------------------------------------------------------- /pptk/kdtree/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # # Uncomment the following lines to run as standalone cmake script 2 | # cmake_minimum_required(VERSION 3.0) 3 | # project(kdtree) 4 | 5 | # set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" ${CMAKE_CURRENT_SOURCE_DIR}) 6 | # find_package(PythonLibs 2.7 REQUIRED) 7 | # find_package(Numpy REQUIRED) 8 | # find_package(TBB REQUIRED) 9 | # include(UsefulMacros) 10 | 11 | add_library(kdtree SHARED kdtree_wrapper.cpp) 12 | set_target_python_module_name(kdtree) 13 | if (WIN32) 14 | target_compile_options(kdtree PRIVATE /bigobj) 15 | target_link_libraries(kdtree ${PYTHON_LIBRARY}) 16 | elseif(APPLE) 17 | set_target_properties(kdtree PROPERTIES 18 | LINK_FLAGS "-undefined dynamic_lookup") 19 | elseif(UNIX) 20 | set_target_properties(kdtree PROPERTIES 21 | LINK_FLAGS "-static-libstdc++") 22 | endif() 23 | target_compile_definitions(kdtree PRIVATE -DUSE_TBB) 24 | target_include_directories(kdtree PRIVATE 25 | ../include # for python_util.h 26 | src # for k-d tree source code 27 | ${TBB_INCLUDE_DIR} 28 | ${PYTHON_INCLUDE_DIR} 29 | ${Numpy_INCLUDE_DIR} ) 30 | target_link_libraries(kdtree 31 | ${TBB_tbb_LIBRARY} 32 | ${TBB_tbbmalloc_LIBRARY}) 33 | copy_target(kdtree) 34 | copy_target_dependencies(kdtree) 35 | copy_file(__init__.py) 36 | 37 | -------------------------------------------------------------------------------- /pptk/kdtree/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/pptk/kdtree/__init__.py -------------------------------------------------------------------------------- /pptk/kdtree/src/accumulator.h: -------------------------------------------------------------------------------- 1 | /** TODO: license boiler plate here 2 | * 3 | * By Victor Lu (victor.1.lu@here.com) 4 | */ 5 | 6 | #ifndef __ACCUMULATOR_H__ 7 | #define __ACCUMULATOR_H__ 8 | 9 | #include 10 | 11 | namespace pointkd { 12 | // specify how element types are converted to floating point number 13 | template ::value> 14 | struct Accumulator {}; 15 | 16 | template 17 | struct Accumulator { 18 | typedef float Type; 19 | }; 20 | template <> 21 | struct Accumulator { 22 | typedef float Type; 23 | }; 24 | template <> 25 | struct Accumulator { 26 | typedef double Type; 27 | }; 28 | } // namespace pointkd 29 | 30 | #endif //__ACCUMULATOR_H__ 31 | -------------------------------------------------------------------------------- /pptk/kdtree/src/box.h: -------------------------------------------------------------------------------- 1 | /** TODO: license boiler plate here 2 | * 3 | * By Victor Lu (victor.1.lu@here.com) 4 | */ 5 | 6 | #ifndef __BOX_H__ 7 | #define __BOX_H__ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "accumulator.h" 13 | 14 | #ifdef max 15 | #undef max 16 | #endif 17 | 18 | #ifdef min 19 | #undef min 20 | #endif 21 | 22 | namespace pointkd { 23 | template 24 | class Box { 25 | public: 26 | Box() { Reset(); } 27 | 28 | Box(const std::vector& points) { 29 | Reset(); 30 | AddPoints(points); 31 | } 32 | 33 | Box(const std::vector& points, const std::vector& indices) { 34 | Reset(); 35 | AddPoints(points, indices); 36 | } 37 | 38 | Box(const T* points, const std::vector& indices) { 39 | Reset(); 40 | AddPoints(points, indices); 41 | } 42 | 43 | Box(const T* points, std::size_t num_points) { 44 | Reset(); 45 | AddPoints(points, num_points); 46 | } 47 | 48 | Box(const Box& other) { 49 | for (int i = 0; i < dim; i++) { 50 | min_[i] = other.min_[i]; 51 | max_[i] = other.max_[i]; 52 | } 53 | } 54 | 55 | template 56 | Box(const Box& other) { 57 | for (int i = 0; i < dim; i++) { 58 | min_[i] = (U)other.min(i); 59 | max_[i] = (U)other.max(i); 60 | if (min_[i] > max_[i]) // guard against numerical errors that may have 61 | // occurred during type conversion from T->U 62 | std::swap(min_[i], max_[i]); 63 | } 64 | } 65 | 66 | ~Box() {} 67 | 68 | Box& operator=(const Box& other) { 69 | for (int i = 0; i < dim; i++) { 70 | min_[i] = other.min_[i]; 71 | max_[i] = other.max_[i]; 72 | } 73 | return *this; 74 | } 75 | 76 | void AddPoints(const T* points, std::size_t num_points) { 77 | // assumes points is array of size num_points * dim 78 | for (std::size_t i = 0; i < num_points; i++) { 79 | this->AddPoint(&points[i * dim]); 80 | } 81 | } 82 | 83 | void AddPoints(const std::vector& points) { 84 | // assumes points.size() % dim == 0 85 | AddPoints(&points[0], points.size() / dim); 86 | } 87 | 88 | void AddPoints(const T* points, const std::vector& indices) { 89 | for (std::size_t i = 0; i < indices.size(); i++) { 90 | // assumes indices[i]*dim+(0...dim-1) is not out of bounds 91 | this->AddPoint(&points[indices[i] * dim]); 92 | } 93 | } 94 | 95 | void AddPoints(const std::vector& points, 96 | const std::vector& indices) { 97 | for (std::size_t i = 0; i < indices.size(); i++) { 98 | // assumes indices[i] is between 0 and points.size() / dim 99 | this->AddPoint(&points[indices[i] * dim]); 100 | } 101 | } 102 | 103 | void AddPoint(const T* point) { 104 | // point is array of type T[dim] 105 | for (int i = 0; i < dim; i++) { 106 | min_[i] = std::min(point[i], min_[i]); 107 | max_[i] = std::max(point[i], max_[i]); 108 | } 109 | } 110 | 111 | void Reset() { 112 | for (int i = 0; i < dim; i++) min_[i] = std::numeric_limits::max(); 113 | for (int i = 0; i < dim; i++) max_[i] = std::numeric_limits::lowest(); 114 | } 115 | 116 | bool IsPoint() const { 117 | bool is_point = true; 118 | for (int i = 0; i < dim; i++) { 119 | if (min_[i] != max_[i]) { 120 | is_point = false; 121 | break; 122 | } 123 | } 124 | return is_point; 125 | } 126 | 127 | template 128 | bool Contains(const U* point) const { 129 | for (int i = 0; i < dim; i++) { 130 | if ((T)point[i] < min_[i]) return false; 131 | if ((T)point[i] > max_[i]) return false; 132 | } 133 | return true; 134 | } 135 | 136 | // const getter methods 137 | double width(int i) const { return (double)max_[i] - (double)min_[i]; } 138 | T min(int i) const { return min_[i]; } 139 | T max(int i) const { return max_[i]; } 140 | T val(int i, int side) const { return (side == 0 ? min_[i] : max_[i]); } 141 | 142 | // non-const getter methods 143 | T& min(int i) { return min_[i]; } 144 | T& max(int i) { return max_[i]; } 145 | T& val(int i, int side) { 146 | if (side == 0) 147 | return min_[i]; 148 | else 149 | return max_[i]; 150 | } 151 | 152 | private: 153 | T min_[dim]; 154 | T max_[dim]; 155 | }; // class Box 156 | 157 | template 158 | void MinDist2Vec(DistT* v, const Q* q, const Box& box) { 159 | for (int i = 0; i < dim; i++) { 160 | DistT d_min = (DistT)box.min(i) - (DistT)q[i]; 161 | DistT d_max = (DistT)box.max(i) - (DistT)q[i]; 162 | if (d_min > (DistT)0.0) 163 | v[i] = d_min * d_min; 164 | else if (d_max < (DistT)0.0) 165 | v[i] = d_max * d_max; 166 | else 167 | v[i] = (DistT)0.0; 168 | } 169 | } 170 | 171 | template 172 | void MaxDist2Vec(DistT* v, const Q* q, const Box& box) { 173 | for (int i = 0; i < dim; i++) { 174 | DistT d_min = (DistT)box.min(i) - (DistT)q[i]; 175 | d_min = d_min * d_min; 176 | DistT d_max = (DistT)box.max(i) - (DistT)q[i]; 177 | d_max = d_max * d_max; 178 | v[i] = std::max(d_min, d_max); 179 | } 180 | } 181 | 182 | template 183 | DistT MinDist2(const Q* p, const Box& box) { 184 | DistT min_vector[dim]; 185 | MinDist2Vec(min_vector, p, box); 186 | DistT dist2 = (DistT)0.0; 187 | for (int i = 0; i < dim; i++) dist2 += min_vector[i]; 188 | return dist2; 189 | } 190 | 191 | template 192 | DistT MaxDist2(const Q* p, const Box& box) { 193 | DistT max_vector[dim]; 194 | MaxDist2Vec(max_vector, p, box); 195 | DistT dist2 = (DistT)0.0; 196 | for (int i = 0; i < dim; i++) dist2 += max_vector[i]; 197 | return dist2; 198 | } 199 | 200 | template 201 | typename Accumulator::Type MinDist2(const Box& a, 202 | const Box& b) { 203 | typedef typename Accumulator::Type DistT; 204 | DistT dist = 0.0; 205 | for (int i = 0; i < dim; i++) { 206 | DistT temp = std::max((DistT)b.min(i) - (DistT)a.max(i), 207 | (DistT)a.min(i) - (DistT)b.max(i)); 208 | temp = std::max(temp, (DistT)0.0); // temp is 0.0 => intervals overlap 209 | dist += temp * temp; 210 | } 211 | return dist; 212 | } 213 | 214 | template 215 | typename Accumulator::Type MaxDist2(const Box& a, 216 | const Box& b) { 217 | typedef typename Accumulator::Type DistT; 218 | DistT dist = 0.0; 219 | for (int i = 0; i < dim; i++) { 220 | T temp = std::max((DistT)a.max(i) - (DistT)b.min(i), 221 | (DistT)b.max(i) - (DistT)a.min(i)); 222 | dist += temp * temp; 223 | } 224 | return dist; 225 | } 226 | 227 | template 228 | void MinDistVec(typename Accumulator::Type* vec, const Box& a, 229 | const Box& b) { 230 | // assumes vec has already been allocated 231 | typedef typename Accumulator::Type DistT; 232 | for (int i = 0; i < dim; i++) { 233 | DistT temp = std::max((DistT)b.min(i) - (DistT)a.max(i), 234 | (DistT)a.min(i) - (DistT)b.max(i)); 235 | temp = std::max(temp, (DistT)0.0); // temp is 0.0 => intervals overlap 236 | vec[i] = temp; 237 | } 238 | } 239 | 240 | template 241 | std::ostream& operator<<(std::ostream& os, const Box& b) { 242 | os << "[" << b.min(0) << ", " << b.max(0) << "]x[" << b.min(1) << ", " 243 | << b.max(1) << "]x[" << b.min(2) << ", " << b.max(2) << "]"; 244 | return os; 245 | } 246 | } // namespace pointkd 247 | 248 | #endif // __BOX_H__ 249 | -------------------------------------------------------------------------------- /pptk/kdtree/src/kdtree.h: -------------------------------------------------------------------------------- 1 | /** TODO: license boiler plate here 2 | * 3 | * By Victor Lu (victor.1.lu@here.com) 4 | */ 5 | 6 | #ifndef __KDTREE_H__ 7 | #define __KDTREE_H__ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "accumulator.h" 13 | #include "box.h" 14 | #include "node.h" 15 | #include "small_node.h" 16 | 17 | #ifdef USE_TBB 18 | #include "tbb/scalable_allocator.h" 19 | #endif // USE_TBB 20 | 21 | namespace pointkd { 22 | // note to user: do not rely on Indices being equal to vector 23 | #ifdef USE_TBB 24 | typedef std::vector > Indices; 25 | #else 26 | typedef std::vector Indices; 27 | #endif 28 | 29 | struct BuildParams { 30 | BuildParams() 31 | : num_proc(-1), 32 | serial_cutoff(0), 33 | max_leaf_size(10), 34 | empty_split_threshold(0.2) {} 35 | int num_proc; 36 | int serial_cutoff; 37 | // leaf nodes have at most this many points 38 | int max_leaf_size; 39 | // perform empty split if resulting gap ratio greater than threshold 40 | // threshold must be non-negative 41 | double empty_split_threshold; 42 | }; 43 | 44 | template 45 | class KdTree { 46 | public: 47 | struct Pair; 48 | typedef typename Accumulator::Type DistT; 49 | typedef Box BoxT; 50 | typedef Node NodeT; 51 | #ifdef USE_TBB 52 | typedef std::vector > Pairs; 53 | #else 54 | typedef std::vector Pairs; 55 | #endif // USE_TBB 56 | typedef std::priority_queue PriorityQueue; 57 | 58 | struct Pair { 59 | Pair() : index(-1), dist2(0) {} 60 | Pair(int index, DistT dist2) : index(index), dist2(dist2) {} 61 | bool operator<(const Pair& other) const { return dist2 < other.dist2; } 62 | int index; 63 | DistT dist2; 64 | }; 65 | 66 | KdTree() : root_(NULL) {} 67 | 68 | /** 69 | * Assumes points stored in array-of-struct format. 70 | */ 71 | KdTree(const std::vector& points, 72 | const BuildParams build_params = BuildParams()); 73 | 74 | KdTree(const T* points, int num_points, 75 | const BuildParams build_params = BuildParams()); 76 | 77 | KdTree(const KdTree& other); 78 | 79 | ~KdTree(); 80 | 81 | KdTree& operator=(const KdTree& other); 82 | 83 | /** Serializes k-d tree nodes into an array of small nodes. 84 | * 85 | * Returns a reference to the resulting array. 86 | * Subsequent queries are performed on nodes in this array. 87 | */ 88 | const std::vector >& SmallNodes(); 89 | 90 | /** Finds k-nearest neighbors to query point. 91 | * 92 | * Assumes query_point is a Q[dim] array. Contents of results should be 93 | * interpreted as 0-based integer indices into the array of points that 94 | * this k-d tree was originally built on. Overwrites any existing content 95 | * in results. 96 | * 97 | * Optional argument r is used to require neighbors have distance from 98 | * query_point strictly less than r. 99 | */ 100 | template 101 | void KNearestNeighbors(Indices& results, const Q* query_point, int k, 102 | DistT r = inf()) const; 103 | 104 | /** Finds k-nearest neighbors of a set of query points. 105 | * 106 | * Assumes query points are stored in an array-of-struct format (i.e. the 107 | * i-th component of the j-th point is given by queries[i + j * dim]). 108 | * Contents of results should be interpreted as 0-based integer indices into 109 | * the array of points that this k-d tree was originally built on. Clears 110 | * any existing content in results and resizes it to the number of query 111 | * points in queries. 112 | * 113 | * Optional argument r is used to require neighbors have distance from 114 | * query_point strictly less than r. 115 | * 116 | * By default, queries are performed in parallel using a number of threads 117 | * determined by Intel TBB. To explicitly set the number of threads call 118 | * tbb::init_task_scheduler(num_threads) before calling this function. 119 | */ 120 | template 121 | void KNearestNeighbors(std::vector& results, 122 | const std::vector& queries, int k, 123 | DistT r = inf()) const; 124 | 125 | template 126 | void KNearestNeighbors(std::vector& results, const Q* queries, 127 | int num_queries, int k, DistT r = inf()) const; 128 | 129 | /** Finds k-nearest neighbors to a point from the original point cloud. 130 | * 131 | * Assumes query_index is a 0-based integer index into the array of points 132 | * that the k-d tree was originally built on. Contents of results should 133 | * likewise be interpreted as 0-based integer indices into the original array 134 | * of points. Overwrites any existing content in results. Excludes 135 | * query_index itself from results. 136 | * 137 | * Optional argument r is used to require neighbors have distance from 138 | * query_point strictly less than r. 139 | */ 140 | void KNearestNeighborsSelf(Indices& results, int query_index, int k, 141 | DistT r = inf()) const; 142 | 143 | /** Finds k-nearest neighbors to points from the original point cloud. 144 | * 145 | * Assumes query_indices is a vector of 0-based integer indices into the 146 | * array of points that the k-d tree was originally built on. Contents of 147 | * results should be likewise interpreted as 0-based integer indices into the 148 | * original array of points. Clears any existing content in results and 149 | * resizes it to be the size of query_indices. Excludes indices in 150 | * query_indices from results. 151 | * 152 | * Optional argument r is used to require neighbors have distance from 153 | * query_point strictly less than r. 154 | * 155 | * By default, queries are performed in parallel using a number of threads 156 | * determined by Intel TBB. To explicitly set the number of threads call 157 | * tbb::init_task_scheduler(num_threads) before calling this function. 158 | */ 159 | void KNearestNeighborsSelf(std::vector& results, 160 | const Indices& query_indices, int k, 161 | DistT r = inf()) const; 162 | 163 | void KNearestNeighborsSelf(std::vector& results, 164 | const int* query_indices, int num_queries, int k, 165 | DistT r = inf()) const; 166 | 167 | /** Finds r-near neighbors to query point 168 | * 169 | * Finds points whose distance to query_point are strictly less than r. 170 | * Assumes query_point is a Q[dim] array. Replaces any existing content 171 | * in results with 0-based integer indices into the array of points that the 172 | * k-d tree was originally built on. 173 | */ 174 | template 175 | void RNearNeighbors(Indices& results, const Q* query_point, DistT r) const; 176 | 177 | /** Finds r-near neighbors to a set of query points 178 | * 179 | * For each query point in queries, finds points whose distance to the query 180 | * point is strictly less than r. Assumes queries organized in an 181 | * array-of-struct format (i.e. the i-th coordinate of the j-th query point 182 | * is given by queries[i + j * dim]). Clears any existing content in results 183 | * and resizes it to the number of query points. The contents of results 184 | * should be interpreted as 0-based integer indices into the array of points 185 | * that this k-d tree was originally built on. 186 | */ 187 | template 188 | void RNearNeighbors(std::vector& results, 189 | const std::vector& queries, DistT r) const; 190 | 191 | template 192 | void RNearNeighbors(std::vector& results, const Q* queries, 193 | int num_queries, DistT r) const; 194 | 195 | /** Finds r-near neighbors to a point in the original point cloud. 196 | * 197 | * Finds points whose distance to the query point is strictly less than r. 198 | * Uses the "query_index-th" point from the original point cloud as the query 199 | * point. Replaces any existing content in results with 0-based integer 200 | * indices into the original point cloud. 201 | */ 202 | void RNearNeighborsSelf(Indices& results, int query_index, DistT r) const; 203 | 204 | /** Finds r-near neighbors to points in the original point cloud. 205 | * 206 | * For each query point, finds points whose distance to the query point is 207 | * strictly less than r. Uses the query_indices[i]-th point from the 208 | * original point cloud as the i-th query point. Clears any existing 209 | * content in results and resizes it to the number of query points. Fills 210 | * the i-th item in results with the i-th query point's r-near neighbors, 211 | * which are represented by 0-based integer indices into the original point 212 | * cloud. 213 | * 214 | * By default, queries are performed in parallel using a number of threads 215 | * determined by Intel TBB. To explicitly set the number of threads call 216 | * tbb::init_task_scheduler(num_threads) before calling this function. 217 | */ 218 | void RNearNeighborsSelf(std::vector& results, 219 | const Indices& query_indices, DistT r) const; 220 | 221 | void RNearNeighborsSelf(std::vector& results, 222 | const int* query_indices, int num_queries, 223 | DistT r) const; 224 | 225 | // TODO: support slice(begin,end,stride) as query objects? 226 | 227 | static DistT inf() { return std::numeric_limits::infinity(); } 228 | const int dimension() const { return dim; } 229 | const int num_points() const { return points_.size() / dim; } 230 | const NodeT* root() const { return root_; } 231 | const Box& bounding_box() const { return bounding_box_; } 232 | const std::vector& points() const { return points_; } 233 | const std::vector& indices() const { return indices_; } 234 | const std::vector& reverse_indices() const { return reverse_indices_; } 235 | 236 | private: 237 | NodeT* root_; 238 | Box bounding_box_; 239 | 240 | // reordered copy of original input points, stored in AOS format 241 | // this copy will not contain any NaN/inf points from the original input 242 | std::vector points_; 243 | // i-th point in the original input now at points_[indices_[i] * dim] 244 | std::vector indices_; 245 | // i-th point in points_ came from points[reverse_indices_[i] * dim] 246 | std::vector reverse_indices_; 247 | // serialized array of small nodes 248 | std::vector > small_nodes_; 249 | }; 250 | } // namespace pointkd 251 | 252 | #endif // __KDTREE_H__ 253 | 254 | #include "kdtree-impl.h" 255 | -------------------------------------------------------------------------------- /pptk/kdtree/src/node.h: -------------------------------------------------------------------------------- 1 | /** TODO: license boiler plate here 2 | * 3 | * By Victor Lu (victor.1.lu@here.com) 4 | */ 5 | 6 | #ifndef __NODE_H__ 7 | #define __NODE_H__ 8 | 9 | #include 10 | #include 11 | #include "accumulator.h" 12 | 13 | namespace pointkd { 14 | template 15 | struct Node { 16 | Node() {} 17 | Node(T split_val, int split_dim, int split_idx, Node* left, Node* right) 18 | : split_value(split_val), 19 | split_dim(split_dim), 20 | split_index(split_idx), 21 | left(left), 22 | right(right) {} 23 | 24 | T split_value; 25 | unsigned int split_dim : 3; 26 | unsigned int split_index : 29; 27 | Node* left; 28 | Node* right; 29 | }; 30 | 31 | template 32 | std::ostream& operator<<(std::ostream& os, const Node& node) { 33 | os << "Split(addr=" << &node << ", dim=" << node.split_dim 34 | << ", val=" << node.split_value << ", idx=" << node.split_index 35 | << ", left=" << node.left << ", right=" << node.right << ")"; 36 | return os; 37 | } 38 | } // namespace pointkd 39 | 40 | #endif // __NODE_H__ -------------------------------------------------------------------------------- /pptk/kdtree/src/small_node.h: -------------------------------------------------------------------------------- 1 | /** TODO: license boiler plate here 2 | * 3 | * By Victor Lu (victor.1.lu@here.com) 4 | */ 5 | 6 | #ifndef __SMALL_NODE_H__ 7 | #define __SMALL_NODE_H__ 8 | 9 | #include "accumulator.h" 10 | 11 | namespace pointkd { 12 | 13 | template 14 | struct SmallNode { 15 | SmallNode() : split_value(0), split_index(0), child_offset(0) {} 16 | SmallNode(T split_value_, int split_dim_, int split_index_, int child_offset_, 17 | bool has_left_child, bool has_right_child) 18 | : split_value(split_value_), 19 | split_index(split_index_), 20 | child_offset(child_offset_) { 21 | split_index = (split_index << 3) | (split_dim_ & 7); 22 | child_offset = (child_offset << 2); 23 | child_offset |= has_left_child ? 2 : 0; 24 | child_offset |= has_right_child ? 1 : 0; 25 | } 26 | int GetSplitDim() const { return (int)(split_index & 7); } 27 | int GetSplitIndex() const { return (int)(split_index >> 3); } 28 | int LeftChildIndex(int current_index) const { 29 | if (child_offset & 2) 30 | return (child_offset >> 2) + current_index; 31 | else 32 | return -1; 33 | } 34 | int RightChildIndex(int current_index) const { 35 | if ((child_offset & 3) == 3) 36 | return (child_offset >> 2) + 1 + current_index; 37 | else if ((child_offset & 3) == 1) 38 | return (child_offset >> 2) + current_index; 39 | else 40 | return -1; 41 | } 42 | T split_value; 43 | unsigned int split_index; // last 3 bits used to encode split dim 44 | unsigned int child_offset; // last 2 bits used to encode the presence of a 45 | // left and right child node 46 | }; 47 | 48 | template 49 | std::ostream& operator<<(std::ostream& os, const SmallNode& node) { 50 | os << "SmallNode(dim=" << node.GetSplitDim() << ", value=" << node.split_value 51 | << ", index=" << node.GetSplitIndex() 52 | << ", left_offset=" << node.LeftChildIndex(0) 53 | << ", right_offset=" << node.RightChildIndex(0) << ")"; 54 | return os; 55 | } 56 | 57 | } // namespace pointkd 58 | 59 | #endif // __SMALL_NODE_H__ -------------------------------------------------------------------------------- /pptk/libs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | function(copy_system_file x) 2 | # creates local copy of x in CMAKE_CURRENT_BINARY_DIR 3 | # optional second argument is a list of search paths for x 4 | if(ARGC EQUAL 2) 5 | set(y ${ARGV1}) 6 | endif() 7 | find_file(temp ${x} ${y}) 8 | copy_file(${temp} _target) 9 | if(UNIX AND NOT APPLE) 10 | add_custom_command(TARGET ${_target} POST_BUILD COMMAND 11 | ${PPTK_PATCHELF} --set-rpath \\\$\$ORIGIN 12 | ${CMAKE_CURRENT_BINARY_DIR}/${x}) 13 | endif() 14 | unset(temp CACHE) 15 | endfunction() 16 | 17 | function(copy_import_target x) 18 | get_target_property(_file_path ${x} LOCATION) 19 | get_filename_component(_file_name ${_file_path} NAME) 20 | copy_file(${_file_path} _target) 21 | if(UNIX AND NOT APPLE) 22 | add_custom_command(TARGET ${_target} POST_BUILD COMMAND 23 | ${PPTK_PATCHELF} --set-rpath \\\$\$ORIGIN 24 | ${CMAKE_CURRENT_BINARY_DIR}/${_file_name}) 25 | endif() 26 | endfunction() 27 | 28 | # install system runtime libraries 29 | if (WIN32) 30 | # for Windows, this copies msvcr*.dll, msvcp*.dll, msvcomp*.dll 31 | foreach(f ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}) 32 | copy_file(${f}) 33 | endforeach() 34 | elseif (APPLE) 35 | elseif (UNIX) 36 | # create local copies of system libraries that are required by pptk targets 37 | # but not provided by a bare bone manylinux1 platform, as specified in PEP 513 38 | if (CMAKE_LIBRARY_ARCHITECTURE) 39 | set(_paths 40 | /lib/${CMAKE_LIBRARY_ARCHITECTURE} 41 | /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}) 42 | endif() 43 | copy_system_file(libgomp.so.1 "${_paths}") 44 | copy_system_file(libz.so.1 "${_paths}") 45 | endif() 46 | 47 | add_subdirectory(qt_plugins) 48 | -------------------------------------------------------------------------------- /pptk/libs/qt_plugins/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(platforms) 2 | add_subdirectory(xcbglintegrations) 3 | -------------------------------------------------------------------------------- /pptk/libs/qt_plugins/platforms/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(WIN32) 2 | copy_file_with_dependencies(${Qt5_PLUGINS_DIR}/platforms/qwindows.dll) 3 | elseif (APPLE) 4 | copy_file_with_dependencies(${Qt5_PLUGINS_DIR}/platforms/libqcocoa.dylib) 5 | elseif (UNIX) 6 | copy_file_with_dependencies(${Qt5_PLUGINS_DIR}/platforms/libqxcb.so) 7 | endif() 8 | -------------------------------------------------------------------------------- /pptk/libs/qt_plugins/xcbglintegrations/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(UNIX AND NOT APPLE) 2 | copy_file_with_dependencies( 3 | ${Qt5_PLUGINS_DIR}/xcbglintegrations/libqxcb-egl-integration.so) 4 | copy_file_with_dependencies( 5 | ${Qt5_PLUGINS_DIR}/xcbglintegrations/libqxcb-glx-integration.so) 6 | endif() 7 | -------------------------------------------------------------------------------- /pptk/points/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | copy_file(__init__.py) 2 | copy_file(expr.py) 3 | copy_file(points.py) 4 | -------------------------------------------------------------------------------- /pptk/points/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/pptk/points/__init__.py -------------------------------------------------------------------------------- /pptk/points/points.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import time 3 | import ctypes 4 | from ..kdtree import kdtree 5 | from . import expr 6 | 7 | __all__ = [ 8 | 'Points', 9 | 'points', 10 | 'zeros', 11 | 'ones', 12 | 'empty', 13 | 'zeros_like', 14 | 'ones_like', 15 | 'empty_like', 16 | 'rand', 17 | 'load' 18 | ] 19 | 20 | 21 | class Final(type): 22 | # copied from https://stackoverflow.com/questions/16056574 23 | def __new__(cls, name, bases, classdict): 24 | for b in bases: 25 | if isinstance(b, Final): 26 | raise TypeError("type " + b.__name__ + 27 | " is not an acceptable base type") 28 | return type.__new__(cls, name, bases, dict(classdict)) 29 | 30 | 31 | def _memaddr(obj): 32 | return obj.ctypes.get_data() 33 | 34 | 35 | def points(object, dtype=None, copy=True): 36 | if not copy and isinstance(object, numpy.ndarray) \ 37 | and (dtype is None or dtype == object.dtype): 38 | ret = object.view(Points) 39 | else: 40 | temp = numpy.array(object, dtype=dtype, copy=False) 41 | ret = empty_like(temp) 42 | ret[:] = temp 43 | return ret 44 | 45 | 46 | def zeros(shape, dtype=float): 47 | ret = Points(shape=shape, dtype=dtype) 48 | ret[:] = 0 49 | return ret 50 | 51 | 52 | def ones(shape, dtype=float): 53 | ret = Points(shape=shape, dtype=dtype) 54 | ret[:] = 1 55 | return ret 56 | 57 | 58 | def empty(shape, dtype=float): 59 | return Points(shape=shape, dtype=dtype) 60 | 61 | 62 | def zeros_like(a, dtype=None): 63 | return zeros(a.shape, dtype=(a.dtype if dtype is None else dtype)) 64 | 65 | 66 | def ones_like(a, dtype=None): 67 | return zeros(a.shape, dtype=(a.dtype if dtype is None else dtype)) 68 | 69 | 70 | def empty_like(a, dtype=None): 71 | return zeros(a.shape, dtype=(a.dtype if dtype is None else dtype)) 72 | 73 | 74 | def rand(*dims): 75 | ret = empty(shape=dims, dtype=float) 76 | ret[:] = numpy.random.rand(*dims) 77 | return ret 78 | 79 | 80 | def load(file, **kwargs): 81 | # wrapper around numpy.load 82 | # TODO: this copies to numpy array, then to a Points object; 83 | # find way to avoid this extra copy 84 | return points(numpy.load(file, **kwargs)) 85 | 86 | 87 | class Points (numpy.ndarray): 88 | 89 | _last_modified = dict() 90 | 91 | # make Points non subclass-able to simplify write control 92 | # TODO: are there any use cases for subclassing Points? 93 | __metaclass__ = Final 94 | 95 | def __new__(cls, *args, **kwargs): 96 | return super(Points, cls).__new__(cls, *args, **kwargs) 97 | 98 | def __array_finalize__(self, obj): 99 | self._last_updated = None 100 | self._tree = None 101 | 102 | if obj is not None and not isinstance(obj, Points): 103 | # arrived at here via view() of a non-Points object 104 | raise TypeError('Detected attempt at creating Points-type ' 105 | 'view on non-Points object.') 106 | 107 | if obj is None and not self.flags.owndata: 108 | raise TypeError('Detected attempt at creating Points-type ' 109 | 'view on buffer object via __new__(buffer=...)') 110 | 111 | if obj is None: 112 | # arrived at here via __new__ 113 | self._memsize = self.size * self.dtype.itemsize 114 | self._memloc = _memaddr(self) 115 | elif _memaddr(self) < obj._memloc or \ 116 | _memaddr(self) >= obj._memloc + obj._memsize: 117 | # arrived at here via copy() 118 | self._memsize = self.size * self.dtype.itemsize 119 | self._memloc = _memaddr(self) 120 | else: 121 | # arrived at here via slicing/indexing 122 | # or view() of a Points object 123 | self._memsize = obj._memsize 124 | self._memloc = obj._memloc 125 | 126 | # cannot set writeable flag to False here, 127 | # because copy() performs assignment after __array_finalize__ 128 | 129 | def __init__(self, *args, **kwargs): 130 | self.flags.writeable = False 131 | 132 | def copy(self): 133 | x = super(Points, self).copy() 134 | x.flags.writeable = False 135 | return x 136 | 137 | def _record_modify_time(self): 138 | Points._last_modified[self._memloc] = time.time() 139 | 140 | def _update_kd_tree(self): 141 | # if there is no recorded last modify time for self._memloc, 142 | # then self has either not been modified yet since creation, 143 | # or _last_modified dictionary has been cleared. Either way, 144 | # the k-d tree needs updating; we set the last modify time to 145 | # the current time to trigger this. 146 | if Points._last_modified.get(self._memloc) is None: 147 | Points._last_modified[self._memloc] = time.time() 148 | 149 | # note: None < x, for any number x 150 | build_time = None 151 | if self._last_updated is None \ 152 | or self._last_updated <= Points._last_modified[self._memloc]: 153 | # note: do not need to explicitly call __del__() 154 | # as it is automatically called when overwritten 155 | build_time = time.time() 156 | self._tree = kdtree._build(self) 157 | build_time = time.time() - build_time 158 | self._last_updated = time.time() # record time *after* build 159 | return build_time 160 | 161 | def nbhds(self, queries=None, k=1, r=None, verbose=False): 162 | self._update_kd_tree() 163 | return kdtree._query(self._tree, queries=queries, k=k, dmax=r) 164 | 165 | def NBHDS(self, queries=None, k=1, r=None, verbose=False): 166 | return expr.nbhds_op(self, queries, k, r) 167 | 168 | def _guard(self, f): 169 | def f_guarded(*args, **kwargs): 170 | if self.base is not None: 171 | self.base.flags.writeable = True 172 | self.flags.writeable = True 173 | ret = None 174 | try: 175 | ret = f(*args, **kwargs) 176 | finally: 177 | self.flags.writeable = False 178 | if self.base is not None: 179 | self.base.flags.writeable = False 180 | self._record_modify_time() # record time *after* computation 181 | return ret 182 | return f_guarded 183 | 184 | # override methods that modify object content to 185 | # record timestamp, signalling need for k-d tree update 186 | 187 | # inplace arithmetic methods 188 | # e.g. +=, -=, *=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |= 189 | def __iadd__(self, other): 190 | return self._guard(super(Points, self).__iadd__)(other) 191 | 192 | def __isub__(self, other): 193 | return self._guard(super(Points, self).__isub__)(other) 194 | 195 | def __imul__(self, other): 196 | return self._guard(super(Points, self).__imul__)(other) 197 | 198 | def __idiv__(self, other): 199 | return self._guard(super(Points, self).__idiv__)(other) 200 | 201 | def __itruediv__(self, other): 202 | return self._guard(super(Points, self).__itruediv__)(other) 203 | 204 | def __ifloordiv__(self, other): 205 | return self._guard(super(Points, self).__ifloordiv__)(other) 206 | 207 | def __imod__(self, other): 208 | return self._guard(super(Points, self).__imod__)(other) 209 | 210 | def __ipow__(self, other): 211 | return self._guard(super(Points, self).__ipow__)(other) 212 | 213 | def __ilshift__(self, other): 214 | return self._guard(super(Points, self).__ilshift__)(other) 215 | 216 | def __irshift__(self, other): 217 | return self._guard(super(Points, self).__irshift__)(other) 218 | 219 | def __iand__(self, other): 220 | return self._guard(super(Points, self).__iand__)(other) 221 | 222 | def __ixor__(self, other): 223 | return self._guard(super(Points, self).__ixor__)(other) 224 | 225 | def __ior__(self, other): 226 | return self._guard(super(Points, self).__ior__)(other) 227 | 228 | # indexing and slicing operator 229 | def __setslice__(self, i, j, sequence): 230 | return self._guard(super(Points, self).__setslice__)(i, j, sequence) 231 | 232 | def __delslice__(self, i, j): 233 | return self._guard(super(Points, self).__delslice__)(i, j) 234 | 235 | def __getslice__(self, i, j): 236 | return super(Points, self).__getslice__(i, j) 237 | 238 | def __setitem__(self, key, value): 239 | return self._guard(super(Points, self).__setitem__)(key, value) 240 | 241 | def __delitem__(self, key): 242 | return self._guard(super(Points, self).__delitem__)(key) 243 | 244 | def __getitem__(self, key): 245 | if isinstance(key, expr.expression): 246 | return expr.index_op( 247 | expr._make_expression(self), key, slice(None, None, None)) 248 | elif (isinstance(key, tuple) or isinstance(key, list)) \ 249 | and any([isinstance(x, expr.expression) for x in key]): 250 | # key is a sequence containing at least one expression object 251 | if len(key) == 2 and ( 252 | isinstance(key[0], expr.expression) 253 | and isinstance(key[1], slice) 254 | or isinstance(key[0], slice) 255 | and isinstance(key[1], expr.expression)): 256 | return expr.index_op( 257 | expr._make_expression(self), key[0], key[1]) 258 | else: 259 | raise TypeError( 260 | 'unsupported combination of types in index tuple: %s' 261 | % repr((type(x) for x in key))) 262 | else: 263 | return super(Points, self).__getitem__(key) 264 | -------------------------------------------------------------------------------- /pptk/processing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | copy_file(__init__.py) 2 | add_subdirectory(estimate_normals) -------------------------------------------------------------------------------- /pptk/processing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/pptk/processing/__init__.py -------------------------------------------------------------------------------- /pptk/processing/estimate_normals/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # # Uncomment the following lines to run as standalone cmake script 2 | # cmake_minimum_required(VERSION 3.0) 3 | # project(estimate_normals) 4 | 5 | # set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake" ${CMAKE_CURRENT_SOURCE_DIR}) 6 | # find_package(PythonLibs 2.7 REQUIRED) 7 | # find_package(Numpy REQUIRED) 8 | # find_package(TBB REQUIRED) 9 | # find_package(Eigen REQUIRED) 10 | # include(UsefulMacros) 11 | # include(FindOpenMP) 12 | 13 | add_library(estimate_normals SHARED estimate_normals.cpp) 14 | set(_link_flags ${OpenMP_CXX_FLAGS}) 15 | if(WIN32) 16 | target_link_libraries(estimate_normals ${PYTHON_LIBRARY}) 17 | elseif(APPLE) 18 | set(_link_flags "${_link_flags} -undefined dynamic_lookup") 19 | elseif(UNIX) 20 | set(_link_flags "${_link_flags} -static-libstdc++") 21 | endif() 22 | set_target_properties(estimate_normals PROPERTIES 23 | COMPILE_FLAGS ${OpenMP_CXX_FLAGS} 24 | LINK_FLAGS ${_link_flags}) 25 | set_target_python_module_name(estimate_normals) 26 | target_compile_definitions(estimate_normals PRIVATE -DUSE_TBB) 27 | target_link_libraries(estimate_normals 28 | ${TBB_tbb_LIBRARY} 29 | ${TBB_tbbmalloc_LIBRARY}) 30 | target_include_directories(estimate_normals PRIVATE 31 | ../../include # for python_util.h 32 | ../../kdtree/src # for kdtree.h 33 | ${Eigen_INCLUDE_DIR} 34 | ${PYTHON_INCLUDE_DIR} 35 | ${TBB_INCLUDE_DIR} 36 | ${Numpy_INCLUDE_DIR}) 37 | copy_target(estimate_normals) 38 | copy_target_dependencies(estimate_normals) 39 | copy_file(__init__.py) 40 | 41 | -------------------------------------------------------------------------------- /pptk/processing/estimate_normals/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/pptk/processing/estimate_normals/__init__.py -------------------------------------------------------------------------------- /pptk/vfuncs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # # Uncomment the following lines to run as standalone cmake script 2 | # cmake_minimum_required(VERSION 3.0) 3 | # project(vfuncs) 4 | 5 | # set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" ${CMAKE_MODULE_PATH}) 6 | # find_package(PythonLibs 2.7 REQUIRED) 7 | # find_package(Numpy REQUIRED) 8 | # find_package(Eigen REQUIRED) 9 | # include(UsefulMacros) 10 | 11 | add_library(vfuncs SHARED vfuncs.cpp) 12 | set_target_python_module_name(vfuncs) 13 | if(WIN32) 14 | target_link_libraries(vfuncs ${PYTHON_LIBRARY}) 15 | elseif(APPLE) 16 | set_target_properties(vfuncs PROPERTIES 17 | LINK_FLAGS "-undefined dynamic_lookup") 18 | elseif(UNIX) 19 | set_target_properties(vfuncs PROPERTIES 20 | LINK_FLAGS "-static-libstdc++") 21 | endif() 22 | target_include_directories(vfuncs PRIVATE 23 | ../include # for python_util.h 24 | ${PYTHON_INCLUDE_DIR} 25 | ${Eigen_INCLUDE_DIR} 26 | ${Numpy_INCLUDE_DIR}) 27 | copy_target(vfuncs) 28 | copy_target_dependencies(vfuncs) 29 | copy_file(__init__.py) 30 | 31 | -------------------------------------------------------------------------------- /pptk/vfuncs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/pptk/vfuncs/__init__.py -------------------------------------------------------------------------------- /pptk/viewer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # # Uncomment the following lines to run as standalone cmake script 2 | # cmake_minimum_required(VERSION 2.8.11) 3 | # project(viewer) 4 | 5 | # set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake" ${CMAKE_MODULE_PATH}) 6 | # find_package(Eigen REQUIRED) 7 | # find_package(OpenGL REQUIRED) 8 | # find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets Network OpenGL Core) 9 | # include(UsefulMacros) 10 | 11 | add_executable(viewer 12 | main.cpp 13 | octree.h 14 | box3.h 15 | floor_grid.h 16 | selection_box.h 17 | background.h 18 | look_at.h 19 | point_cloud.h 20 | viewer.h 21 | qt_camera.h 22 | camera.h 23 | comm_funcs.h 24 | opengl_funcs.h 25 | text.h 26 | camera_dolly.h 27 | splines.h 28 | point_attributes.h) 29 | 30 | set_target_properties(viewer PROPERTIES AUTOMOC TRUE) 31 | target_link_libraries(viewer Qt5::Widgets Qt5::Network Qt5::OpenGL ${OPENGL_gl_LIBRARY}) 32 | target_include_directories(viewer PRIVATE ${Eigen_INCLUDE_DIR} ) 33 | copy_target(viewer) 34 | copy_target_dependencies(viewer) 35 | copy_file(viewer.py) 36 | copy_file(__init__.py) 37 | copy_file(qt.conf) 38 | 39 | -------------------------------------------------------------------------------- /pptk/viewer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heremaps/pptk/697c09ac1a5a652d43aa8c4deb98c27c3a0b77e3/pptk/viewer/__init__.py -------------------------------------------------------------------------------- /pptk/viewer/background.h: -------------------------------------------------------------------------------- 1 | #ifndef __BACKGROUND_H__ 2 | #define __BACKGROUND_H__ 3 | #include 4 | #include 5 | #include 6 | #include "opengl_funcs.h" 7 | class Background : protected OpenGLFuncs { 8 | public: 9 | Background(QWindow *window, QOpenGLContext *context) 10 | : _context(context), 11 | _window(window), 12 | _bg_color_top(0.0f, 0.0f, 0.0f, 1.0f), 13 | _bg_color_bottom(0.23f, 0.23f, 0.44f, 1.0f) { 14 | _context->makeCurrent(_window); 15 | initializeOpenGLFunctions(); 16 | _context->doneCurrent(); 17 | compileProgram(); 18 | } 19 | void draw() { 20 | glDepthMask(GL_FALSE); 21 | glDisable(GL_DEPTH_TEST); 22 | 23 | 24 | float points[12] = { 25 | 0.0f, 0.0f, 0.0f, 26 | 1.0f, 0.0f, 0.0f, 27 | 1.0f, 1.0f, 0.0f, 28 | 0.0f, 1.0f, 0.0f}; 29 | GLuint square; 30 | glGenBuffers(1, &square); 31 | glBindBuffer(GL_ARRAY_BUFFER, square); 32 | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, (GLvoid *)points, 33 | GL_STATIC_DRAW); 34 | 35 | unsigned int indices[6] = { 36 | 0, 1, 2, 37 | 0, 2, 3}; 38 | GLuint square_indices; 39 | glGenBuffers(1, &square_indices); 40 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, square_indices); 41 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * 6, 42 | (GLvoid *)indices, GL_STATIC_DRAW); 43 | 44 | _program.bind(); 45 | _program.setUniformValue("colorBottom", _bg_color_bottom); 46 | _program.setUniformValue("colorTop", _bg_color_top); 47 | _program.enableAttributeArray("position"); 48 | _program.setAttributeArray("position", GL_FLOAT, 0, 3); 49 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 50 | _program.disableAttributeArray("position"); 51 | glDeleteBuffers(1, &square); 52 | glDeleteBuffers(1, &square_indices); 53 | 54 | glEnable(GL_DEPTH_TEST); 55 | glDepthMask(GL_TRUE); 56 | } 57 | 58 | void setColorTop(QVector4D c) { _bg_color_top = c; } 59 | void setColorBottom(QVector4D c) { _bg_color_bottom = c; } 60 | QVector4D getColorTop() const { return _bg_color_top; } 61 | QVector4D getColorBottom() const { return _bg_color_bottom; } 62 | 63 | private: 64 | void compileProgram() { 65 | std::string vsCode = 66 | "#version 110\n" 67 | "\n" 68 | "attribute vec4 position;\n" 69 | "varying vec2 coordinate;\n" 70 | "void main() {\n" 71 | " gl_Position = vec4(2.0*position.xy-1.0,0,1);\n" 72 | " coordinate = position.xy;\n" 73 | "}\n"; 74 | std::string fsCode = 75 | "#version 110\n" 76 | "\n" 77 | "uniform vec4 colorBottom;\n" 78 | "uniform vec4 colorTop;\n" 79 | "varying vec2 coordinate;\n" 80 | "void main() {\n" 81 | " gl_FragColor = mix(colorBottom, colorTop, coordinate.y);\n" 82 | "}\n"; 83 | 84 | _context->makeCurrent(_window); 85 | _program.addShaderFromSourceCode(QOpenGLShader::Vertex, vsCode.c_str()); 86 | _program.addShaderFromSourceCode(QOpenGLShader::Fragment, fsCode.c_str()); 87 | _program.link(); 88 | _context->doneCurrent(); 89 | } 90 | 91 | QOpenGLContext *_context; 92 | QWindow *_window; 93 | QOpenGLShaderProgram _program; 94 | QVector4D _bg_color_top; 95 | QVector4D _bg_color_bottom; 96 | }; 97 | 98 | #endif // __BACKGROUND_H__ 99 | -------------------------------------------------------------------------------- /pptk/viewer/box3.h: -------------------------------------------------------------------------------- 1 | #ifndef __BOX3_H__ 2 | #define __BOX3_H__ 3 | #include 4 | #include 5 | #include 6 | 7 | namespace vltools { 8 | 9 | template 10 | struct Box3 { 11 | Box3() { 12 | _data[0][0] = _data[0][1] = _data[0][2] = std::numeric_limits::max(); 13 | _data[1][0] = _data[1][1] = _data[1][2] = -std::numeric_limits::max(); 14 | } 15 | Box3(const Box3& box) { 16 | _data[0][0] = box._data[0][0]; 17 | _data[0][1] = box._data[0][1]; 18 | _data[0][2] = box._data[0][2]; 19 | _data[1][0] = box._data[1][0]; 20 | _data[1][1] = box._data[1][1]; 21 | _data[1][2] = box._data[1][2]; 22 | } 23 | Box3(T xmin, T xmax, T ymin, T ymax, T zmin, T zmax) { 24 | _data[0][0] = xmin; 25 | _data[0][1] = ymin; 26 | _data[0][2] = zmin; 27 | _data[1][0] = xmax; 28 | _data[1][1] = ymax; 29 | _data[1][2] = zmax; 30 | } 31 | void addPoint(T x, T y, T z) { 32 | _data[0][0] = std::min(x, _data[0][0]); 33 | _data[0][1] = std::min(y, _data[0][1]); 34 | _data[0][2] = std::min(z, _data[0][2]); 35 | _data[1][0] = std::max(x, _data[1][0]); 36 | _data[1][1] = std::max(y, _data[1][1]); 37 | _data[1][2] = std::max(z, _data[1][2]); 38 | } 39 | void addPoints(const T* points, std::size_t numPoints) { 40 | for (std::size_t i = 0; i < numPoints; i++) { 41 | addPoint(points[3 * i + 0], points[3 * i + 1], points[3 * i + 2]); 42 | } 43 | } 44 | void addBox(const Box3& other) { 45 | _data[0][0] = std::min(other.min(0), _data[0][0]); 46 | _data[0][1] = std::min(other.min(1), _data[0][1]); 47 | _data[0][2] = std::min(other.min(2), _data[0][2]); 48 | _data[1][0] = std::max(other.max(0), _data[1][0]); 49 | _data[1][1] = std::max(other.max(1), _data[1][1]); 50 | _data[1][2] = std::max(other.max(2), _data[1][2]); 51 | } 52 | 53 | // const getter methods 54 | const T& min(std::size_t i) const { return _data[0][i]; } 55 | const T& max(std::size_t i) const { return _data[1][i]; } 56 | const T& x(std::size_t i) const { return _data[i][0]; } 57 | const T& y(std::size_t i) const { return _data[i][1]; } 58 | const T& z(std::size_t i) const { return _data[i][2]; } 59 | // non-const versions 60 | T& min(std::size_t i) { return _data[0][i]; } 61 | T& max(std::size_t i) { return _data[1][i]; } 62 | T& x(std::size_t i) { return _data[i][0]; } 63 | T& y(std::size_t i) { return _data[i][1]; } 64 | T& z(std::size_t i) { return _data[i][2]; } 65 | T _data[2][3]; 66 | }; 67 | 68 | template 69 | T minDist2(const Box3& a, const Box3& b) { 70 | T dist = 0.0; 71 | for (std::size_t i = 0; i < 3; i++) { 72 | T temp = std::max(a.min(i) - b.max(i), (T)0.0); 73 | temp = std::max(b.min(i) - a.max(i), temp); 74 | dist += temp * temp; 75 | } 76 | return dist; 77 | } 78 | 79 | template 80 | T maxDist2(const Box3& a, const Box3& b) { 81 | T dist = 0.0; 82 | for (std::size_t i = 0; i < 3; i++) { 83 | T temp = a.max(i) - b.min(i); 84 | temp = std::max(b.max(i) - a.min(i), temp); 85 | dist += temp * temp; 86 | } 87 | return dist; 88 | } 89 | 90 | template 91 | void minDistVec(T* vec, const Box3& a, const Box3& b) { 92 | for (std::size_t i = 0; i < 3; i++) { 93 | T temp = std::max(a.min(i) - b.max(i), (T)0.0); 94 | temp = std::max(b.min(i) - a.max(i), temp); 95 | vec[i] = temp; 96 | } 97 | } 98 | 99 | template 100 | std::ostream& operator<<(std::ostream& os, const Box3& b) { 101 | os << "[" 102 | << b.min(0) << ", " << b.max(0) << "]x[" 103 | << b.min(1) << ", " << b.max(1) << "]x[" 104 | << b.min(2) << ", " << b.max(2) << "]"; 105 | return os; 106 | } 107 | } // namespace vltools 108 | #endif // __BOX3_H__ 109 | -------------------------------------------------------------------------------- /pptk/viewer/camera.h: -------------------------------------------------------------------------------- 1 | #ifndef __CAMERA_H__ 2 | #define __CAMERA_H__ 3 | #include 4 | #include 5 | #define PI 3.14159265359f 6 | class Camera { 7 | public: 8 | Camera() 9 | : _theta(0.0f), 10 | _phi(0.0f), 11 | _d(1.0f), 12 | _panRate(2.0f / 300), // 2.0 per 300 pixels 13 | _zoomRate(0.8f), 14 | _rotateRate(PI / 2 / 256) // PI/2 per 256 pixels 15 | { 16 | _lookAt[0] = _lookAt[1] = _lookAt[2] = 0.0f; 17 | save(); 18 | } 19 | 20 | Camera(float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) 21 | : _theta(PI / 4.0f), 22 | _phi(PI / 4.0f), 23 | _zoomRate(0.8f), 24 | _rotateRate(PI / 2 / 256) { 25 | // set _lookAt to center bottom 26 | _lookAt[0] = (xmin + xmax) / 2.0f; 27 | _lookAt[1] = (ymin + ymax) / 2.0f; 28 | _lookAt[2] = zmin; 29 | 30 | // set _d to length of widest span 31 | _d = (std::max)(xmax - xmin, (std::max)(ymax - ymin, zmax - zmin)); 32 | 33 | _panRate = _d / 300; 34 | 35 | save(); 36 | } 37 | 38 | /*! \brief rotate 39 | * Camera is rotated based on mouse movement (dx,dy). 40 | * _phi = _phi - _rotateRate * dx 41 | * _theta = _theta + _rotateRate * dy 42 | */ 43 | void rotate(float dx, float dy) { 44 | _phi -= _rotateRate * dx; 45 | _theta += _rotateRate * dy; 46 | } 47 | 48 | /*! \brief pan 49 | * Camera is panned based on mouse movement (dx,dy). 50 | * _lookAt = _lookAt 51 | * - _right * _panRate * dx 52 | * + _up * _panRate * dy 53 | */ 54 | void pan(float dx, float dy) { 55 | float x[3]; 56 | float up[3]; 57 | computeRightVector(x, _theta, _phi); 58 | computeUpVector(up, _theta, _phi); 59 | for (int i = 0; i < 3; i++) { 60 | _lookAt[i] += _panRate * (-x[i] * dx + up[i] * dy); 61 | } 62 | } 63 | 64 | /*! \brief zoom 65 | * Camera is zoomed based on scroll by dx amount. 66 | * _d = _d * _zoomRate ^ dx 67 | */ 68 | void zoom(float dx) { _d = (std::max)(0.1f, _d * (float)pow(_zoomRate, dx)); } 69 | 70 | void save() { 71 | std::copy(_lookAt, _lookAt + 3, _saved_lookAt); 72 | _saved_theta = _theta; 73 | _saved_phi = _phi; 74 | _saved_d = _d; 75 | } 76 | 77 | void restore() { 78 | std::copy(_saved_lookAt, _saved_lookAt + 3, _lookAt); 79 | _theta = _saved_theta; 80 | _phi = _saved_phi; 81 | _d = _saved_d; 82 | } 83 | 84 | /*! \brief getCameraPosition */ 85 | void getCameraPosition(float (&p)[3]) const { 86 | computeCameraPosition(p, _lookAt, _theta, _phi, _d); 87 | } 88 | 89 | /*! \brief getLookAtPosition */ 90 | void getLookAtPosition(float (&p)[3]) const { 91 | std::copy(_lookAt, _lookAt + 3, p); 92 | } 93 | 94 | /*! \brief getRightVector */ 95 | void getRightVector(float (&v)[3]) const { 96 | computeRightVector(v, _theta, _phi); 97 | } 98 | 99 | /*! \brief getUpVector */ 100 | void getUpVector(float (&v)[3]) const { computeUpVector(v, _theta, _phi); } 101 | 102 | /*! \brief getViewVector */ 103 | void getViewVector(float (&v)[3]) const { 104 | computeViewVector(v, _theta, _phi); 105 | } 106 | 107 | float getCameraDistance() const { return _d; } 108 | float getTheta() const { return _theta; } 109 | float getPhi() const { return _phi; } 110 | float getPanRate() const { return _panRate; } 111 | float getRotateRate() const { return _rotateRate; } 112 | float getZoomRate() const { return _zoomRate; } 113 | void setPanRate(float panRate) { _panRate = panRate; } 114 | void setRotateRate(float rotateRate) { _rotateRate = rotateRate; } 115 | void setZoomRate(float zoomRate) { _zoomRate = zoomRate; } 116 | void setLookAtPosition(const float (&v)[3]) { 117 | _lookAt[0] = v[0]; 118 | _lookAt[1] = v[1]; 119 | _lookAt[2] = v[2]; 120 | } 121 | void setPhi(const float phi) { _phi = _saved_phi = phi; } 122 | void setTheta(const float theta) { _theta = _saved_theta = theta; } 123 | void setCameraDistance(const float d) { _d = _saved_d = d; } 124 | 125 | static void computeRightVector(float (&v)[3], const float theta, 126 | const float phi) { 127 | v[0] = -sin(phi); 128 | v[1] = cos(phi); 129 | v[2] = 0.0f; 130 | } 131 | 132 | static void computeUpVector(float (&v)[3], const float theta, 133 | const float phi) { 134 | v[0] = -sin(theta) * cos(phi); 135 | v[1] = -sin(theta) * sin(phi); 136 | v[2] = cos(theta); 137 | } 138 | 139 | static void computeViewVector(float (&v)[3], const float theta, 140 | const float phi) { 141 | v[0] = cos(theta) * cos(phi); 142 | v[1] = cos(theta) * sin(phi); 143 | v[2] = sin(theta); 144 | } 145 | 146 | private: 147 | static void computeCameraPosition(float (&p)[3], const float (&lookAt)[3], 148 | const float theta, const float phi, 149 | const float d) { 150 | float v[3]; 151 | computeViewVector(v, theta, phi); 152 | for (int i = 0; i < 3; i++) { 153 | p[i] = lookAt[i] + d * v[i]; 154 | } 155 | } 156 | 157 | static void computeCameraFrame(float (&x)[3], float (&y)[3], float (&z)[3], 158 | const float theta, const float phi) { 159 | computeRightVector(x, theta, phi); 160 | computeUpVector(y, theta, phi); 161 | computeViewVector(z, theta, phi); 162 | } 163 | 164 | static void computeCameraMatrix(float (&m)[16], float (&lookAt)[3], 165 | float theta, float phi, float d) { 166 | float x[3]; 167 | float y[3]; 168 | float z[3]; 169 | computeCameraFrame(x, y, z, theta, phi); 170 | 171 | // first column 172 | m[0] = x[0]; 173 | m[1] = y[0]; 174 | m[2] = z[0]; 175 | m[3] = 0.0f; 176 | 177 | // second column 178 | m[4] = x[1]; 179 | m[5] = y[1]; 180 | m[6] = z[1]; 181 | m[7] = 0.0f; 182 | 183 | // third column 184 | m[8] = x[2]; 185 | m[9] = y[2]; 186 | m[10] = z[2]; 187 | m[11] = 0.0f; 188 | 189 | // fourth column 190 | m[12] = -(lookAt[0] + z[0] * d); 191 | m[13] = -(lookAt[1] + z[1] * d); 192 | m[14] = -(lookAt[2] + z[2] * d); 193 | m[15] = 1.0f; 194 | } 195 | 196 | float _lookAt[3]; 197 | float _theta; // angle of elevation 198 | float _phi; // azimuthal angle 199 | float _d; // camera distance from _lookAt 200 | 201 | float _saved_lookAt[3]; 202 | float _saved_theta; 203 | float _saved_phi; 204 | float _saved_d; 205 | 206 | float _panRate; 207 | float _zoomRate; 208 | float _rotateRate; 209 | }; 210 | 211 | #endif // __CAMERA_H__ 212 | -------------------------------------------------------------------------------- /pptk/viewer/camera_dolly.h: -------------------------------------------------------------------------------- 1 | #ifndef __CAMERADOLLY_H__ 2 | #define __CAMERADOLLY_H__ 3 | #include 4 | #include 5 | #include 6 | #include "splines.h" 7 | #include "timer.h" 8 | 9 | class CameraPose { 10 | public: 11 | CameraPose() : _look_at(), _phi(0.0f), _theta(0.0f), _d(1.0f) {} 12 | CameraPose(const QVector3D& p, float phi, float theta, float d) 13 | : _look_at(p), _phi(phi), _theta(theta), _d(d) {} 14 | 15 | // getter functions 16 | const QVector3D& lookAt() const { return _look_at; } 17 | float phi() const { return _phi; } 18 | float theta() const { return _theta; } 19 | float d() const { return _d; } 20 | 21 | // setter functions 22 | void setLookAt(const QVector3D& p) { _look_at = p; } 23 | void setPhi(float phi) { _phi = phi; } 24 | void setTheta(float theta) { _theta = theta; } 25 | void setD(float d) { _d = d; } 26 | 27 | private: 28 | QVector3D _look_at; 29 | float _phi; 30 | float _theta; 31 | float _d; 32 | }; 33 | 34 | struct CameraPosesSOA { 35 | CameraPosesSOA(std::vector& poses) { 36 | x.resize(poses.size()); 37 | y.resize(poses.size()); 38 | z.resize(poses.size()); 39 | phi.resize(poses.size()); 40 | theta.resize(poses.size()); 41 | d.resize(poses.size()); 42 | for (int i = 0; i < (int)poses.size(); i++) { 43 | x[i] = poses[i].lookAt().x(); 44 | y[i] = poses[i].lookAt().y(); 45 | z[i] = poses[i].lookAt().z(); 46 | phi[i] = poses[i].phi(); 47 | theta[i] = poses[i].theta(); 48 | d[i] = poses[i].d(); 49 | } 50 | } 51 | std::vector x; 52 | std::vector y; 53 | std::vector z; 54 | std::vector phi; 55 | std::vector theta; 56 | std::vector d; 57 | }; 58 | 59 | class CameraDolly { 60 | public: 61 | enum InterpolationType { CONSTANT, LINEAR, CUBIC_NATURAL, CUBIC_PERIODIC }; 62 | 63 | CameraDolly() : _interp_type(LINEAR), _repeat(false), _active(false) { 64 | check_and_init(); 65 | compute_splines(); 66 | } 67 | 68 | /*! \brief CameraDolly 69 | * Assumes key poses sorted in increasing time 70 | * Assumes ts.size() == poses.size() and ts.size() > 0 71 | * Assumes ts consists of unique time instances 72 | * TODO: consider introducing a CameraPose class 73 | * i.e. for consolidating lookAt, phi, theta, d parameters 74 | */ 75 | CameraDolly(const std::vector& ts, 76 | const std::vector& poses, 77 | InterpolationType interp = LINEAR, bool repeat = false) 78 | : _ts(ts), 79 | _poses(poses), 80 | _interp_type(interp), 81 | _repeat(repeat), 82 | _active(false) { 83 | check_and_init(); 84 | compute_splines(); 85 | } 86 | 87 | ~CameraDolly() { 88 | delete _look_at_x; 89 | delete _look_at_y; 90 | delete _look_at_z; 91 | delete _phi; 92 | delete _theta; 93 | delete _d; 94 | } 95 | 96 | // actions 97 | void start() { 98 | _active = true; 99 | _timer = vltools::getTime(); 100 | _current_time = 101 | _start_time; // this shouldn't be necessary, but just in case 102 | } 103 | void stop() { _active = false; } 104 | 105 | // dolly states 106 | void getTimeAndPose(float& t, CameraPose& p) { 107 | step(); 108 | t = _current_time; 109 | p = getPose(t); 110 | } 111 | 112 | float getTime() { 113 | step(); 114 | return _current_time; 115 | } 116 | CameraPose getPose() { 117 | step(); 118 | return getPose(_current_time); 119 | } 120 | CameraPose getPose(float t) const { 121 | CameraPose pose; 122 | QVector3D look_at(_look_at_x->eval(t), _look_at_y->eval(t), 123 | _look_at_z->eval(t)); 124 | pose.setLookAt(look_at); 125 | pose.setPhi(_phi->eval(t)); 126 | pose.setTheta(_theta->eval(t)); 127 | pose.setD(_d->eval(t)); 128 | return pose; 129 | } 130 | bool done() const { return !_active; } 131 | 132 | // getters 133 | const std::vector& ts() const { return _ts; } 134 | const std::vector& poses() const { return _poses; } 135 | float startTime() const { return _start_time; } 136 | float endTime() const { return _end_time; } 137 | 138 | // setters 139 | void setStartTime(float t) { 140 | _start_time = qMin(qMax(t, _ts.front()), _end_time); 141 | } 142 | void setEndTime(float t) { 143 | _end_time = qMin(qMax(t, _start_time), _ts.back()); 144 | } 145 | void setInterpType(InterpolationType type) { 146 | if (type != _interp_type) { 147 | delete _look_at_x; 148 | delete _look_at_y; 149 | delete _look_at_z; 150 | delete _phi; 151 | delete _theta; 152 | delete _d; 153 | _interp_type = type; 154 | compute_splines(); 155 | } 156 | } 157 | void setRepeat(bool b) { _repeat = b; } 158 | 159 | private: 160 | void check_and_init() { 161 | if (_ts.size() == _poses.size() && !_ts.empty()) { 162 | _start_time = _ts.front(); 163 | _end_time = _ts.back(); 164 | _current_time = _start_time; 165 | } else { 166 | _ts.clear(); 167 | _ts.push_back(0.0); 168 | _poses.clear(); 169 | _poses.push_back(CameraPose()); 170 | _start_time = 0.0; 171 | _end_time = 0.0; 172 | _current_time = _start_time; 173 | } 174 | } 175 | 176 | void step() { 177 | // modifies _current_time and _active 178 | if (_active) { 179 | float elapsed = (float)(vltools::getTime() - _timer); 180 | if (_repeat) 181 | _current_time = fmod(elapsed, _end_time - _start_time) + _start_time; 182 | else { 183 | _current_time = elapsed + _start_time; 184 | if (_current_time >= _end_time) { 185 | _current_time = _end_time; 186 | _active = false; 187 | } 188 | } 189 | } 190 | } 191 | 192 | void compute_splines() { 193 | if (_interp_type == LINEAR) { 194 | interpolate_linear(); 195 | } else if (_interp_type == CUBIC_NATURAL) { 196 | interpolate_cubic(CubicSpline::NATURAL); 197 | } else if (_interp_type == CUBIC_PERIODIC) { 198 | interpolate_cubic(CubicSpline::PERIODIC); 199 | } else { 200 | // _interp_type == CONSTANT 201 | interpolate_const(); 202 | } 203 | } 204 | 205 | void interpolate_const() { 206 | CameraPosesSOA posesSOA(_poses); 207 | _look_at_x = new ConstantSpline(_ts, posesSOA.x); 208 | _look_at_y = new ConstantSpline(_ts, posesSOA.y); 209 | _look_at_z = new ConstantSpline(_ts, posesSOA.z); 210 | _phi = new ConstantSpline(_ts, posesSOA.phi); 211 | _theta = new ConstantSpline(_ts, posesSOA.theta); 212 | _d = new ConstantSpline(_ts, posesSOA.d); 213 | } 214 | 215 | void interpolate_linear() { 216 | CameraPosesSOA posesSOA(_poses); 217 | _look_at_x = new LinearSpline(_ts, posesSOA.x); 218 | _look_at_y = new LinearSpline(_ts, posesSOA.y); 219 | _look_at_z = new LinearSpline(_ts, posesSOA.z); 220 | _phi = new LinearSpline(_ts, posesSOA.phi); 221 | _theta = new LinearSpline(_ts, posesSOA.theta); 222 | _d = new LinearSpline(_ts, posesSOA.d); 223 | } 224 | 225 | void interpolate_cubic(CubicSpline::BoundaryBehavior b) { 226 | CameraPosesSOA posesSOA(_poses); 227 | _look_at_x = new CubicSpline(_ts, posesSOA.x, b); 228 | _look_at_y = new CubicSpline(_ts, posesSOA.y, b); 229 | _look_at_z = new CubicSpline(_ts, posesSOA.z, b); 230 | _phi = new CubicSpline(_ts, posesSOA.phi, b); 231 | _theta = new CubicSpline(_ts, posesSOA.theta, b); 232 | _d = new CubicSpline(_ts, posesSOA.d, b); 233 | } 234 | 235 | std::vector _ts; 236 | std::vector _poses; 237 | 238 | Spline* _look_at_x; 239 | Spline* _look_at_y; 240 | Spline* _look_at_z; 241 | Spline* _phi; 242 | Spline* _theta; 243 | Spline* _d; 244 | 245 | float _start_time; 246 | float _end_time; 247 | float _current_time; 248 | double _timer; 249 | 250 | InterpolationType _interp_type; 251 | bool _repeat; 252 | bool _active; 253 | }; 254 | 255 | #endif // __CAMERADOLLY_H__ 256 | -------------------------------------------------------------------------------- /pptk/viewer/comm_funcs.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMFUNCS_H__ 2 | #define __COMMFUNCS_H__ 3 | #include 4 | #include 5 | 6 | namespace comm { 7 | template 8 | struct TypeCode { 9 | static const unsigned char value = 0; 10 | }; 11 | template <> 12 | struct TypeCode { 13 | static const unsigned char value = 1; 14 | }; 15 | template <> 16 | struct TypeCode { 17 | static const unsigned char value = 2; 18 | }; 19 | template <> 20 | struct TypeCode { 21 | static const unsigned char value = 3; 22 | }; 23 | template <> 24 | struct TypeCode { 25 | static const unsigned char value = 4; 26 | }; 27 | 28 | inline void receiveBytes(char* destination, const qint64 bytesExpected, 29 | QTcpSocket* clientConnection) { 30 | // notes: read() can read just part of buffer 31 | // but waitForReadyRead() unblocks only on receiving *new* data 32 | // not-yet-read data in buffer is not considered new 33 | qint64 bytesReceived = 0; 34 | while (bytesReceived < bytesExpected) { 35 | qint64 received = 36 | clientConnection->read(destination, bytesExpected - bytesReceived); 37 | if (received == 0) clientConnection->waitForReadyRead(-1); 38 | if (received == -1) { 39 | qDebug() << "error during socket read()"; 40 | exit(1); 41 | } 42 | bytesReceived += received; 43 | destination += received; 44 | } 45 | } 46 | 47 | inline void sendBytes(const char* source, const qint64 size, 48 | QTcpSocket* clientConnection) { 49 | qint64 bytesLeft = size; 50 | const char* buf = source; 51 | while (bytesLeft > 0) { 52 | qint64 bytesSent = clientConnection->write(buf, bytesLeft); 53 | if (bytesSent == -1) { 54 | qDebug() << "error during socket write()"; 55 | exit(1); 56 | } 57 | buf += bytesSent; 58 | bytesLeft -= bytesSent; 59 | clientConnection->waitForBytesWritten(); 60 | } 61 | } 62 | 63 | template 64 | void sendScalar(const T value, QTcpSocket* clientConnection) { 65 | // send data type 66 | unsigned char dataType = TypeCode::value; 67 | sendBytes((char*)&dataType, 1, clientConnection); 68 | 69 | // send number of dimensions 70 | quint64 numDims = 1; 71 | sendBytes((char*)&numDims, sizeof(quint64), clientConnection); 72 | 73 | // send dimensions 74 | quint64 numElts = 1; 75 | sendBytes((char*)&numElts, sizeof(quint64), clientConnection); 76 | 77 | // send array elements 78 | sendBytes((char*)&value, sizeof(T), clientConnection); 79 | } 80 | 81 | template 82 | void sendArray(const T* source, const quint64 size, 83 | QTcpSocket* clientConnection) { 84 | // send data type 85 | unsigned char dataType = TypeCode::value; 86 | sendBytes((char*)&dataType, 1, clientConnection); 87 | 88 | // send number of dimensions 89 | quint64 numDims = 1; 90 | sendBytes((char*)&numDims, sizeof(quint64), clientConnection); 91 | 92 | // send dimensions 93 | sendBytes((char*)&size, sizeof(quint64), clientConnection); 94 | 95 | // send array elements 96 | sendBytes((char*)source, sizeof(T) * size, clientConnection); 97 | } 98 | 99 | inline void sendError(const char* msg, const quint64 size, 100 | QTcpSocket* clientConnection) { 101 | // send data type 102 | unsigned char dataType = 0; 103 | sendBytes((char*)&dataType, 1, clientConnection); 104 | 105 | // send number of dimensions 106 | quint64 numDims = 1; 107 | sendBytes((char*)&numDims, sizeof(quint64), clientConnection); 108 | 109 | // send dimensions 110 | sendBytes((char*)&size, sizeof(quint64), clientConnection); 111 | 112 | // send array elements 113 | sendBytes((char*)msg, sizeof(char) * size, clientConnection); 114 | } 115 | 116 | template 117 | void sendMatrix(const T* source, // in column major order 118 | const quint64 numRows, const quint64 numCols, 119 | QTcpSocket* clientConnection) { 120 | // send data type 121 | unsigned char dataType = TypeCode::value; 122 | sendBytes((char*)&dataType, 1, clientConnection); 123 | 124 | // send number of dimensions 125 | quint64 numDims = 2; 126 | sendBytes((char*)&numDims, sizeof(quint64), clientConnection); 127 | 128 | // send dimensions 129 | quint64 dims[2] = {numRows, numCols}; 130 | sendBytes((char*)&dims[0], 2 * sizeof(quint64), clientConnection); 131 | 132 | // send array elements 133 | sendBytes((char*)source, sizeof(T) * numRows * numCols, clientConnection); 134 | } 135 | 136 | template 137 | void sendMultiDimArray(const T* source, const std::vector& dims, 138 | QTcpSocket* clientConnection) { 139 | // send data type 140 | unsigned char dataType = TypeCode::value; 141 | sendBytes((char*)&dataType, 1, clientConnection); 142 | 143 | // send number of dimensions 144 | quint64 numDims = dims.size(); 145 | sendBytes((char*)&numDims, sizeof(quint64), clientConnection); 146 | 147 | // send dimensions 148 | quint64 numElts = 1; 149 | for (std::size_t i = 0; i < dims.size(); i++) { 150 | sendBytes((char*)&dims[i], sizeof(quint64), clientConnection); 151 | numElts *= dims[i]; 152 | } 153 | 154 | // send array elements 155 | sendBytes((char*)source, sizeof(T) * numElts, clientConnection); 156 | } 157 | } 158 | #endif // __COMMFUNCS_H__ 159 | -------------------------------------------------------------------------------- /pptk/viewer/look_at.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOOKAT_H__ 2 | #define __LOOKAT_H__ 3 | #include 4 | #include 5 | #include 6 | #include "opengl_funcs.h" 7 | #include "qt_camera.h" 8 | 9 | class LookAt : protected OpenGLFuncs { 10 | public: 11 | LookAt(QWindow* window, QOpenGLContext* context) 12 | : _context(context), _window(window), _visible(true) { 13 | _context->makeCurrent(_window); 14 | initializeOpenGLFunctions(); 15 | _context->doneCurrent(); 16 | compileProgram(); 17 | } 18 | void draw(const QtCamera& camera) { 19 | if (!_visible) return; 20 | 21 | QVector3D lookat = camera.getLookAtPosition(); 22 | float d = 0.0625 * camera.getCameraDistance(); 23 | vltools::Box3 lookatBox(lookat.x() - d, lookat.x() + d, 24 | lookat.y() - d, lookat.y() + d, 25 | lookat.z() - d, lookat.z() + d); 26 | 27 | _program.bind(); 28 | _program.setUniformValue("mvp", camera.computeMVPMatrix(lookatBox)); 29 | _program.setUniformValue("d", d); 30 | _program.setUniformValue("lookat", lookat); 31 | 32 | float positions[18] = { 33 | 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 34 | 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 35 | 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}; 36 | float colors[18] = { 37 | 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 38 | 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 39 | 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f}; 40 | 41 | GLuint buffer_positions; 42 | glGenBuffers(1, &buffer_positions); 43 | glBindBuffer(GL_ARRAY_BUFFER, buffer_positions); 44 | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 18, positions, 45 | GL_STATIC_DRAW); 46 | _program.enableAttributeArray("position"); 47 | _program.setAttributeArray("position", GL_FLOAT, 0, 3); 48 | 49 | GLuint buffer_colors; 50 | glGenBuffers(1, &buffer_colors); 51 | glBindBuffer(GL_ARRAY_BUFFER, buffer_colors); 52 | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 18, colors, GL_STATIC_DRAW); 53 | _program.enableAttributeArray("color"); 54 | _program.setAttributeArray("color", GL_FLOAT, 0, 3); 55 | 56 | glLineWidth(2.0f); 57 | glDrawArrays(GL_LINES, 0, 6); 58 | 59 | _program.disableAttributeArray("position"); 60 | _program.disableAttributeArray("color"); 61 | glDeleteBuffers(1, &buffer_positions); 62 | glDeleteBuffers(1, &buffer_colors); 63 | } 64 | 65 | void setVisible(bool visible) { _visible = visible; } 66 | bool getVisible() const { return _visible; } 67 | 68 | private: 69 | void compileProgram() { 70 | std::string vsCode = 71 | "#version 110\n" 72 | "uniform float d;\n" 73 | "uniform vec3 lookat;\n" 74 | "uniform mat4 mvp;\n" 75 | "attribute vec3 position;\n" 76 | "attribute vec3 color;\n" 77 | "varying vec3 vcolor;\n" 78 | "void main() {\n" 79 | " gl_Position = mvp * vec4(d * position + lookat, 1);\n" 80 | " vcolor = color;\n" 81 | "}\n"; 82 | std::string fsCode = 83 | "#version 110\n" 84 | "varying vec3 vcolor;\n" 85 | "void main() {\n" 86 | " gl_FragColor = vec4(vcolor, 1);\n" 87 | "}\n"; 88 | _context->makeCurrent(_window); 89 | _program.addShaderFromSourceCode(QOpenGLShader::Vertex, vsCode.c_str()); 90 | _program.addShaderFromSourceCode(QOpenGLShader::Fragment, fsCode.c_str()); 91 | _program.link(); 92 | _context->doneCurrent(); 93 | } 94 | 95 | QOpenGLContext* _context; 96 | QWindow* _window; 97 | QOpenGLShaderProgram _program; 98 | bool _visible; 99 | }; 100 | 101 | #endif // __LOOKAT_H__ 102 | -------------------------------------------------------------------------------- /pptk/viewer/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "viewer.h" 5 | 6 | int main(int argc, char* argv[]) { 7 | if (argc != 2) { 8 | qDebug() << "usage: viewer "; 9 | return 1; 10 | } 11 | QApplication a(argc, argv); 12 | unsigned short clientPort = (unsigned short)atoi(argv[1]); 13 | Viewer viewer(clientPort); 14 | viewer.resize(512, 512); 15 | viewer.create(); 16 | viewer.show(); 17 | 18 | return a.exec(); 19 | } 20 | -------------------------------------------------------------------------------- /pptk/viewer/opengl_funcs.h: -------------------------------------------------------------------------------- 1 | #ifndef __OPENGLFUNCS_H__ 2 | #define __OPENGLFUNCS_H__ 3 | #include 4 | 5 | class OpenGLFuncs : public QOpenGLFunctions { 6 | // extends QOpenGLFunctions with some helper and error checking functions 7 | public: 8 | OpenGLFuncs() : QOpenGLFunctions() {} 9 | 10 | OpenGLFuncs(QOpenGLContext* context) : QOpenGLFunctions(context) {} 11 | 12 | GLint getBufferSize(GLuint bufferId) { 13 | GLint bufferSize = 0; 14 | glBindBuffer(GL_ARRAY_BUFFER, bufferId); 15 | glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufferSize); 16 | return bufferSize; 17 | } 18 | 19 | void checkError() { 20 | GLenum e = glGetError(); 21 | switch (e) { 22 | case GL_INVALID_ENUM: 23 | qDebug() << "GLenum argument out of range"; 24 | break; 25 | case GL_INVALID_VALUE: 26 | qDebug() << "Numeric argument out of range"; 27 | break; 28 | case GL_INVALID_OPERATION: 29 | qDebug() << "Operation illegal in current state"; 30 | break; 31 | case GL_STACK_OVERFLOW: 32 | qDebug() << "Command would cause a stack overflow"; 33 | break; 34 | case GL_STACK_UNDERFLOW: 35 | qDebug() << "Command would cause a stack underflow"; 36 | break; 37 | case GL_OUT_OF_MEMORY: 38 | qDebug() << "Not enough memory left to execute command"; 39 | break; 40 | case GL_NO_ERROR: 41 | qDebug() << "No error"; 42 | } 43 | } 44 | 45 | void printFramebufferStatus() { 46 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 47 | switch (status) { 48 | case GL_FRAMEBUFFER_COMPLETE: 49 | std::cout << "GL_FRAMEBUFFER_COMPLETE" << std::endl; 50 | break; 51 | case GL_FRAMEBUFFER_UNDEFINED: 52 | std::cout << "GL_FRAMEBUFFER_UNDEFINED" << std::endl; 53 | break; 54 | case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 55 | std::cout << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT" << std::endl; 56 | break; 57 | case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 58 | std::cout << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT" 59 | << std::endl; 60 | break; 61 | case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: 62 | std::cout << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER" << std::endl; 63 | break; 64 | case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: 65 | std::cout << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER" << std::endl; 66 | break; 67 | case GL_FRAMEBUFFER_UNSUPPORTED: 68 | std::cout << "GL_FRAMEBUFFER_UNSUPPORTED" << std::endl; 69 | break; 70 | case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: 71 | std::cout << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" << std::endl; 72 | break; 73 | case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: 74 | std::cout << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS" << std::endl; 75 | break; 76 | } 77 | } 78 | }; 79 | 80 | #endif // __OPENGLFUNCS_H__ 81 | -------------------------------------------------------------------------------- /pptk/viewer/point_attributes.h: -------------------------------------------------------------------------------- 1 | #ifndef __POINTATTRIBUTES_H__ 2 | #define __POINTATTRIBUTES_H__ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "octree.h" 9 | 10 | class PointAttributes { 11 | private: 12 | std::vector > _attr; 13 | std::vector _attr_size; 14 | std::vector _attr_dim; 15 | std::size_t _curr_idx; 16 | 17 | public: 18 | PointAttributes() 19 | : _attr(1, std::vector(4, 1.0f)), 20 | _attr_size(1, 1), 21 | _attr_dim(1, 4), 22 | _curr_idx(0) {} 23 | 24 | bool set(const std::vector& attr, quint64 attr_size, 25 | quint64 attr_dim) { 26 | if (attr.size() != attr_size * attr_dim) return false; 27 | _attr.clear(); 28 | _attr_size.clear(); 29 | _attr_dim.clear(); 30 | _attr.resize(1, attr); 31 | _attr_size.resize(1, attr_size); 32 | _attr_dim.resize(1, attr_dim); 33 | _curr_idx = 0; 34 | return true; 35 | } 36 | 37 | bool set(const std::vector& data, const Octree& octree) { 38 | // overwrites existing attributes 39 | unsigned int num_points = octree.getNumPoints(); 40 | 41 | // fill in _attr* arrays 42 | if (!_unpack(data, num_points)) return false; 43 | 44 | // nothing to do if there are no points in octree 45 | if (num_points == 0) return true; 46 | 47 | // set points to white if no attributes received 48 | if (_attr.empty()) { 49 | reset(); 50 | return true; 51 | } 52 | 53 | // for each attribute set 54 | for (std::size_t i = 0; i < _attr.size(); i++) { 55 | if (_attr_size[i] == 1) continue; 56 | 57 | // reorder attributes according to octree 58 | _reorder(i, octree); 59 | 60 | // compute LOD averages 61 | _compute_LOD(i, octree); 62 | } 63 | _curr_idx = 0; 64 | return true; 65 | } 66 | 67 | void reset() { 68 | _attr.clear(); 69 | _attr_size.clear(); 70 | _attr_dim.clear(); 71 | _attr.resize(1, std::vector(4, 1.0f)); 72 | _attr_size.resize(1, 1); 73 | _attr_dim.resize(1, 4); 74 | _curr_idx = 0; 75 | } 76 | 77 | const std::vector& operator[](int i) const { return _attr[i]; } 78 | 79 | float operator()(int i, int j) const { 80 | // return j-th component of i-th attribute in current attribute set 81 | quint64 dim = _attr_dim[_curr_idx]; 82 | quint64 size = _attr_size[_curr_idx]; 83 | if (size == 1) 84 | return _attr[_curr_idx][j]; 85 | else 86 | return _attr[_curr_idx][i * dim + j]; 87 | } 88 | 89 | float operator()(int k, int i, int j) const { 90 | // return j-th component of i-th attribute in k-th attribute set 91 | quint64 dim = _attr_dim[k]; 92 | quint64 size = _attr_size[k]; 93 | if (size == 1) 94 | return _attr[k][j]; 95 | else 96 | return _attr[k][i * dim + j]; 97 | } 98 | 99 | std::size_t currentIndex() const { return _curr_idx; } 100 | quint64 size(int i) const { return _attr_size[i]; } 101 | quint64 dim(int i) const { return _attr_dim[i]; } 102 | std::size_t numAttributes() const { return _attr.size(); } 103 | void setCurrentIndex(std::size_t i) { 104 | if (i < _attr.size() && i >= 0) _curr_idx = i; 105 | } 106 | 107 | private: 108 | bool _unpack(const std::vector& data, unsigned int expected_size) { 109 | if (data.empty()) return false; 110 | 111 | // initialize ptr into data stream 112 | const char* ptr = (char*)&data[0]; 113 | const char* ptr_end = ptr + data.size(); 114 | 115 | // get number of attribute sets 116 | quint64 num_attr; 117 | if (!_unpack_number(num_attr, ptr, ptr_end)) return false; 118 | 119 | // parse attribute sets 120 | std::vector > attr(num_attr); 121 | std::vector attr_size(num_attr); 122 | std::vector attr_dim(num_attr); 123 | for (quint64 i = 0; i < num_attr; i++) { 124 | // record attribute size of current set 125 | if (!_unpack_number(attr_size[i], ptr, ptr_end)) return false; 126 | if (attr_size[i] != expected_size && attr_size[i] != 1) return false; 127 | 128 | // record attribute dimension of current set 129 | if (!_unpack_number(attr_dim[i], ptr, ptr_end)) return false; 130 | 131 | // record attribute values 132 | attr[i].resize(attr_size[i] * attr_dim[i]); 133 | if (!_unpack_array(attr[i], ptr, ptr_end)) return false; 134 | } 135 | _attr.swap(attr); 136 | _attr_size.swap(attr_size); 137 | _attr_dim.swap(attr_dim); 138 | return true; 139 | } 140 | 141 | void _reorder(std::size_t attr_idx, const Octree& octree) { 142 | // assumes _attr_size[attr_idx] > 1 143 | std::vector& attr = _attr[attr_idx]; 144 | std::vector temp(attr.size()); 145 | quint64 dim = _attr_dim[attr_idx]; 146 | for (quint64 j = 0; j < _attr_size[attr_idx]; j++) 147 | for (quint64 k = 0; k < dim; k++) 148 | temp[j * dim + k] = attr[octree.getIndices()[j] * dim + k]; 149 | attr.swap(temp); 150 | } 151 | 152 | void _compute_LOD(std::size_t attr_idx, const Octree& octree) { 153 | // make space for LOD averages 154 | std::size_t num_centroids = 155 | octree.getPointPos().size() / 3 - octree.getNumPoints(); 156 | _attr[attr_idx].resize(_attr[attr_idx].size() + 157 | _attr_dim[attr_idx] * num_centroids); 158 | if (false && _attr_dim[attr_idx] == 4) 159 | // disable LOD approximation of alpha transparency for now 160 | _compute_rgba_LOD_helper(attr_idx, octree.getRoot()); 161 | else 162 | _compute_LOD_helper(attr_idx, octree.getRoot()); 163 | } 164 | 165 | void _compute_rgba_LOD_helper(std::size_t attr_idx, 166 | const Octree::Node* node) { 167 | if (node == NULL) return; 168 | 169 | std::vector& attr = _attr[attr_idx]; 170 | quint64 dim = _attr_dim[attr_idx]; 171 | float* dst = &attr[dim * node->centroid_index]; 172 | dst[0] = dst[1] = dst[2] = 0.0f; 173 | dst[3] = 1.0f; 174 | if (node->is_leaf) { 175 | QVector3D x = QVector3D(0.0f, 0.0f, 0.0f); 176 | float w = 1.0f; 177 | for (unsigned int i = 0; i < node->point_count; i++) 178 | _accumulate_rgba(x, w, &attr[4 * (node->point_index + i)]); 179 | _xw_to_rgba(dst, x, w, node->point_count); 180 | } else { // !node->is_leaf 181 | //_compute_inner_rgba(dst, node); 182 | QVector3D x = QVector3D(0.0f, 0.0f, 0.0f); 183 | float w = 1.0f; 184 | unsigned int num_children = 0; 185 | for (unsigned int i = 0; i < 8; i++) { 186 | if (node->children[i] == NULL) continue; 187 | num_children++; 188 | _compute_rgba_LOD_helper(attr_idx, node->children[i]); 189 | _accumulate_rgba(x, w, &attr[4 * node->children[i]->centroid_index]); 190 | } 191 | _xw_to_rgba(dst, x, w, num_children); 192 | } 193 | } 194 | 195 | void _compute_LOD_helper(std::size_t attr_idx, const Octree::Node* node) { 196 | if (node == NULL) return; 197 | 198 | std::vector& attr = _attr[attr_idx]; 199 | quint64 dim = _attr_dim[attr_idx]; 200 | float* dst = &attr[dim * node->centroid_index]; 201 | std::fill_n(dst, dim, 0.0f); 202 | if (node->is_leaf) { 203 | for (unsigned int i = 0; i < node->point_count; i++) 204 | for (quint64 j = 0; j < dim; j++) 205 | dst[j] += attr[(node->point_index + i) * dim + j]; 206 | for (quint64 i = 0; i < dim; i++) dst[i] /= node->point_count; 207 | } else { // !node->is_leaf 208 | std::vector w(8, 0.0f); 209 | for (std::size_t i = 0; i < 8; i++) { 210 | if (!node->children[i]) continue; 211 | w[i] = (float)node->children[i]->point_count / node->point_count; 212 | } 213 | for (unsigned int i = 0; i < 8; i++) { 214 | Octree::Node* child = node->children[i]; 215 | if (!child) continue; 216 | _compute_LOD_helper(attr_idx, child); 217 | float w = (float)node->children[i]->point_count / node->point_count; 218 | for (quint64 j = 0; j < dim; j++) 219 | dst[j] += w * attr[child->centroid_index * dim + j]; 220 | } 221 | } 222 | } 223 | 224 | inline void _accumulate_rgba(QVector3D& x, float& w, const float* v) { 225 | for (int i = 0; i < 3; i++) x[i] += v[i]; 226 | w *= (1.0f - v[3]); 227 | } 228 | 229 | inline void _xw_to_rgba(float* dst, const QVector3D& x, const float& w, 230 | unsigned int n) const { 231 | dst[0] = x[0] / n; 232 | dst[1] = x[1] / n; 233 | dst[2] = x[2] / n; 234 | dst[3] = 1.0f - w; 235 | } 236 | 237 | template 238 | bool _unpack_number(T& v, const char*& ptr, const char* const ptr_end) { 239 | // returns false if attempting to read beyond end of stream [ptr, ptr_end) 240 | if (ptr + sizeof(T) > ptr_end) { 241 | return false; 242 | } else { 243 | v = *(T*)ptr; 244 | ptr += sizeof(T); 245 | return true; 246 | } 247 | } 248 | 249 | template 250 | bool _unpack_array(std::vector& v, const char*& ptr, 251 | const char* const ptr_end) { 252 | if (ptr + sizeof(T) * v.size() > ptr_end) { 253 | return false; 254 | } else { 255 | std::copy((const T*)ptr, (const T*)(ptr + sizeof(T) * v.size()), 256 | v.begin()); 257 | ptr += sizeof(T) * v.size(); 258 | return true; 259 | } 260 | } 261 | }; 262 | 263 | #endif // __POINTATTRIBUTES_H__ 264 | -------------------------------------------------------------------------------- /pptk/viewer/qt.conf: -------------------------------------------------------------------------------- 1 | [Paths] 2 | Plugins=../libs/qt_plugins 3 | -------------------------------------------------------------------------------- /pptk/viewer/qt_camera.h: -------------------------------------------------------------------------------- 1 | #ifndef QMOUSECONTROLLEDCAMERA_H 2 | #define QMOUSECONTROLLEDCAMERA_H 3 | #include "camera.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "box3.h" 9 | class QtCamera : public Camera 10 | { 11 | // adapter class that utilizes Qt features 12 | // (Parent class Camera is Qt-independent) 13 | public: 14 | enum ProjectionMode {PERSPECTIVE = 0, ORTHOGRAPHIC = 1}; 15 | enum ViewAxis {ARBITRARY_AXIS, X_AXIS, Y_AXIS, Z_AXIS}; 16 | 17 | QtCamera() : Camera(), 18 | _vfov(pi()/4.0f), _aspect_ratio(1.0f), _projection_mode(PERSPECTIVE), _view_axis(ARBITRARY_AXIS) {} 19 | QtCamera(float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) : 20 | Camera(xmin, xmax, ymin, ymax, zmin, zmax), 21 | _vfov(pi()/4.0f), _aspect_ratio(1.0f), _projection_mode(PERSPECTIVE), _view_axis(ARBITRARY_AXIS) {} 22 | QtCamera(const QVector3D & lo, const QVector3D & hi) : 23 | Camera(lo.x(), hi.x(), lo.y(), hi.y(), lo.z(), hi.z()), 24 | _vfov(pi()/4.0f), _aspect_ratio(1.0f), _projection_mode(PERSPECTIVE), _view_axis(ARBITRARY_AXIS) {} 25 | QtCamera(const vltools::Box3 & box) : 26 | Camera(box.x(0), box.x(1), box.y(0), box.y(1), box.z(0), box.z(1)), 27 | _vfov(pi()/4.0f), _aspect_ratio(1.0f), _projection_mode(PERSPECTIVE), _view_axis(ARBITRARY_AXIS) {} 28 | 29 | QVector3D getCameraPosition() const { 30 | float p[3]; 31 | Camera::getCameraPosition(p); 32 | return QVector3D(p[0], p[1], p[2]); 33 | } 34 | QVector3D getLookAtPosition() const { 35 | float p[3]; 36 | Camera::getLookAtPosition(p); 37 | return QVector3D(p[0], p[1], p[2]); 38 | } 39 | QVector3D getRightVector() const { 40 | float v[3]; 41 | Camera::getRightVector(v); 42 | return QVector3D(v[0], v[1], v[2]); 43 | } 44 | QVector3D getUpVector() const { 45 | float v[3]; 46 | Camera::getUpVector(v); 47 | return QVector3D(v[0], v[1], v[2]); 48 | } 49 | QVector3D getViewVector() const { 50 | float v[3]; 51 | Camera::getViewVector(v); 52 | return QVector3D(v[0], v[1], v[2]); 53 | } 54 | void setLookAtPosition(const QVector3D & p) { 55 | float buf[3] = {p.x(), p.y(), p.z()}; 56 | Camera::setLookAtPosition(buf); 57 | } 58 | 59 | using Camera::computeRightVector; 60 | using Camera::computeUpVector; 61 | using Camera::computeViewVector; 62 | using Camera::getCameraDistance; 63 | using Camera::getCameraPosition; 64 | using Camera::getLookAtPosition; 65 | using Camera::getPanRate; 66 | using Camera::getPhi; 67 | using Camera::getRightVector; 68 | using Camera::getRotateRate; 69 | using Camera::getTheta; 70 | using Camera::getUpVector; 71 | using Camera::getViewVector; 72 | using Camera::getZoomRate; 73 | using Camera::pan; 74 | using Camera::restore; 75 | using Camera::rotate; 76 | using Camera::save; 77 | using Camera::setCameraDistance; 78 | using Camera::setLookAtPosition; 79 | using Camera::setPanRate; 80 | using Camera::setPhi; 81 | using Camera::setRotateRate; 82 | using Camera::setTheta; 83 | using Camera::setZoomRate; 84 | using Camera::zoom; 85 | 86 | void pan(QVector2D delta) { 87 | // delta in ndc scale 88 | if (delta.x() == 0.0f && delta.y() == 0.0f) 89 | return; 90 | float h = getCameraDistance() * tan (0.5f * _vfov); 91 | float w = _aspect_ratio * h; 92 | delta *= QVector2D(w, h) / getPanRate(); 93 | Camera::pan(delta.x(), delta.y()); 94 | } 95 | void rotate(QVector2D delta) { 96 | // delta in screen space pixel scale 97 | if (delta.x() == 0.0f && delta.y() == 0.0f) 98 | return; 99 | if (_view_axis != ARBITRARY_AXIS) 100 | _view_axis = ARBITRARY_AXIS; 101 | Camera::rotate(delta.x(), delta.y()); 102 | } 103 | float getTop() const 104 | { 105 | float t = tan(0.5f * _vfov); 106 | if (_projection_mode == ORTHOGRAPHIC) 107 | t *= getCameraDistance(); 108 | return t; 109 | } 110 | float getRight() const 111 | { 112 | return _aspect_ratio * getTop(); 113 | } 114 | float getAspectRatio() const {return _aspect_ratio;} 115 | float getVerticalFOV() const {return _vfov;} 116 | ProjectionMode getProjectionMode() const {return _projection_mode;} 117 | ViewAxis getViewAxis() const {return _view_axis;} 118 | 119 | void setAspectRatio(float aspect_ratio) {_aspect_ratio = aspect_ratio;} 120 | void setVerticalFOV(float vfov) {_vfov = vfov;} 121 | void setProjectionMode(ProjectionMode mode) {_projection_mode = mode;} 122 | void setViewAxis(ViewAxis axis) 123 | { 124 | if (axis == X_AXIS) { 125 | setPhi(0.0f); 126 | setTheta(0.0f); 127 | } else if (axis == Y_AXIS) { 128 | setPhi(-0.5f * pi()); 129 | setTheta(0.0f); 130 | } else if (axis == Z_AXIS) { 131 | setPhi(-0.5f * pi()); 132 | setTheta(0.5f * pi()); 133 | } 134 | _view_axis = axis; 135 | } 136 | 137 | QMatrix4x4 computeMVPMatrix(const vltools::Box3 & box) const { 138 | QMatrix4x4 matrix; 139 | matrix.setToIdentity(); 140 | float d_near, d_far; computeNearFar(d_near, d_far, box); 141 | if (_projection_mode == PERSPECTIVE) { 142 | matrix.perspective(_vfov / pi() * 180.0f, _aspect_ratio, std::max(0.1f, 0.8f * d_near), 1.2f * d_far); 143 | } else { 144 | float t = getCameraDistance() * tan(0.5f * _vfov); 145 | float r = _aspect_ratio * t; 146 | matrix.ortho(-r, r, -t, t, 0.8f * d_near, 1.2f * d_far); 147 | } 148 | matrix.lookAt(getCameraPosition(), getLookAtPosition(), getUpVector()); 149 | return matrix; 150 | } 151 | 152 | private: 153 | void computeNearFar(float & d_near, float & d_far, const vltools::Box3 & box) const { 154 | d_near = std::numeric_limits::max(); 155 | d_far = -std::numeric_limits::max(); 156 | 157 | QVector3D view = getViewVector(); 158 | QVector3D eye = getCameraPosition(); 159 | for (std::size_t i = 0; i < 2; i++) { 160 | for (std::size_t j = 0; j < 2; j++) { 161 | for (std::size_t k = 0; k < 2; k++) { 162 | QVector3D corner(box.x(i), box.y(j), box.z(k)); 163 | float t = QVector3D::dotProduct(corner - eye, -view); 164 | d_near = std::min(d_near, t); 165 | d_far = std::max(d_far, t); 166 | } 167 | } 168 | } 169 | } 170 | static float pi() {return atan2(0.0, -1.0);} 171 | float _vfov; // vertical fov in radians 172 | float _aspect_ratio; // width / height 173 | ProjectionMode _projection_mode; 174 | ViewAxis _view_axis; 175 | }; 176 | 177 | #endif // QMOUSECONTROLLEDCAMERA_H 178 | -------------------------------------------------------------------------------- /pptk/viewer/selection_box.h: -------------------------------------------------------------------------------- 1 | #ifndef __SELECTIONBOX_H__ 2 | #define __SELECTIONBOX_H__ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "opengl_funcs.h" 9 | 10 | class SelectionBox : protected OpenGLFuncs { 11 | public: 12 | enum SelectMode { ADD = 0, SUB = 1, NONE = 2 }; 13 | 14 | SelectionBox(QWindow* window, QOpenGLContext* context) 15 | : _context(context), _window(window), _select_mode(NONE) { 16 | _context->makeCurrent(_window); 17 | initializeOpenGLFunctions(); 18 | _context->doneCurrent(); 19 | compileProgram(); 20 | } 21 | 22 | void draw() { 23 | if (_select_mode == NONE) return; 24 | glDisable(GL_DEPTH_TEST); 25 | glDepthMask(GL_FALSE); 26 | 27 | GLuint buffer_square; 28 | glGenBuffers(1, &buffer_square); 29 | glBindBuffer(GL_ARRAY_BUFFER, buffer_square); 30 | float points[12] = {0.0f, 0.0f, 0.0f, 31 | 1.0f, 0.0f, 0.0f, 32 | 1.0f, 1.0f, 0.0f, 33 | 0.0f, 1.0f, 0.0f}; 34 | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, (GLvoid*)points, 35 | GL_STATIC_DRAW); 36 | 37 | GLuint buffer_indices; 38 | glGenBuffers(1, &buffer_indices); 39 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_indices); 40 | unsigned int indices[5] = {0, 1, 2, 3, 0}; 41 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * 5, indices, 42 | GL_STATIC_DRAW); 43 | 44 | _program.bind(); 45 | _program.setUniformValue("box_min", _box.topLeft()); 46 | _program.setUniformValue("box_max", _box.bottomRight()); 47 | _program.enableAttributeArray("position"); 48 | _program.setAttributeArray("position", GL_FLOAT, 0, 3); 49 | glDrawElements(GL_LINE_STRIP, 5, GL_UNSIGNED_INT, (GLvoid*)0); 50 | _program.disableAttributeArray("position"); 51 | glDeleteBuffers(1, &buffer_square); 52 | glDeleteBuffers(1, &buffer_indices); 53 | 54 | glEnable(GL_DEPTH_TEST); 55 | glDepthMask(GL_TRUE); 56 | } 57 | 58 | void click(QPointF p, SelectMode select_mode) { 59 | _select_mode = select_mode; 60 | _anchor = p; 61 | _box = QRectF(p, p); 62 | } 63 | 64 | void drag(QPointF p) { 65 | _box = QRectF(p, _anchor); 66 | _box = _box.normalized(); 67 | } 68 | 69 | void release() { 70 | _select_mode = NONE; 71 | _box.setWidth(0.0f); 72 | _box.setHeight(0.0f); 73 | } 74 | 75 | bool active() const { return _select_mode != NONE; } 76 | 77 | bool empty() const { return _box.isEmpty(); } 78 | 79 | const QRectF& getBox() const { return _box; } 80 | 81 | SelectMode getType() const { return _select_mode; } 82 | 83 | private: 84 | void compileProgram() { 85 | std::string vsCode = 86 | "#version 110\n" 87 | "uniform vec2 box_min;\n" 88 | "uniform vec2 box_max;\n" 89 | "attribute vec3 position;\n" 90 | "void main() {\n" 91 | " gl_Position = vec4(position.xy * (box_max - box_min) + box_min, 0, 1);\n" 92 | "}\n"; 93 | std::string fsCode = 94 | "#version 110\n" 95 | "void main() {\n" 96 | " gl_FragColor = vec4(1, 1, 0, 1);\n" 97 | "}\n"; 98 | _context->makeCurrent(_window); 99 | _program.addShaderFromSourceCode(QOpenGLShader::Vertex, vsCode.c_str()); 100 | _program.addShaderFromSourceCode(QOpenGLShader::Fragment, fsCode.c_str()); 101 | _program.link(); 102 | _context->doneCurrent(); 103 | } 104 | 105 | QOpenGLContext* _context; 106 | QWindow* _window; 107 | QOpenGLShaderProgram _program; 108 | 109 | SelectMode _select_mode; 110 | QPointF _anchor; 111 | QRectF _box; 112 | }; 113 | 114 | #endif // __SELECTIONBOX_H__ 115 | -------------------------------------------------------------------------------- /pptk/viewer/splines.h: -------------------------------------------------------------------------------- 1 | #ifndef __SPLINES_H__ 2 | #define __SPLINES_H__ 3 | #include "Eigen/Dense" 4 | #include "Eigen/Sparse" 5 | 6 | template 7 | class Spline { 8 | public: 9 | Spline(const std::vector& ts, const std::vector& vs) 10 | : _ts(ts), _vs(vs) { 11 | checkAndInit(); 12 | } 13 | 14 | virtual ~Spline() = 0; 15 | 16 | virtual T eval(T t) const = 0; 17 | 18 | const std::vector& ts() const { return _ts; } 19 | const std::vector& vs() const { return _vs; } 20 | 21 | protected: 22 | static bool checkTs(std::vector& ts) { 23 | // returns true if ts is: 24 | // 1. non-empty 25 | // 2. sorted in increasing order 26 | // 3. unique 27 | for (std::size_t i = 1; i < ts.size(); i++) 28 | if (ts[i] <= ts[i - 1]) return false; 29 | return !ts.empty(); 30 | } 31 | 32 | void checkAndInit() { 33 | // checks that _ts and _vs are valid. 34 | // if not, make this spline into a constant zero function 35 | if (!checkTs(_ts) || _ts.size() != _vs.size()) { 36 | _ts.clear(); 37 | _ts.push_back(0.0f); 38 | _vs.clear(); 39 | _vs.push_back(0.0f); 40 | } 41 | } 42 | 43 | std::vector _ts; 44 | std::vector _vs; 45 | }; 46 | 47 | template 48 | inline Spline::~Spline() {} 49 | 50 | template 51 | class ConstantSpline : public Spline { 52 | using Spline::_ts; 53 | using Spline::_vs; 54 | 55 | public: 56 | ConstantSpline(const std::vector& ts, const std::vector& vs) 57 | : Spline(ts, vs) {} 58 | 59 | ~ConstantSpline() {} 60 | 61 | T eval(T t) const { 62 | int idx = std::upper_bound(_ts.begin(), _ts.end(), t) - _ts.begin(); 63 | if (idx == 0) 64 | return _vs.front(); 65 | else if (idx == (int)_ts.size()) 66 | return _vs.back(); 67 | else 68 | // at this point, we know ts.size() > 1 and 0 < idx < _ts.size() 69 | return _vs[idx - 1]; 70 | } 71 | }; 72 | 73 | template 74 | class LinearSpline : public Spline { 75 | using Spline::_ts; 76 | using Spline::_vs; 77 | 78 | public: 79 | LinearSpline(const std::vector& ts, const std::vector& vs) 80 | : Spline(ts, vs) {} 81 | 82 | ~LinearSpline() {} 83 | 84 | T eval(T t) const { 85 | int idx = std::upper_bound(_ts.begin(), _ts.end(), t) - _ts.begin(); 86 | if (idx == 0) 87 | return _vs.front(); 88 | else if (idx == (int)_ts.size()) 89 | return _vs.back(); 90 | else { 91 | // at this point, we know ts.size() > 1 and 0 < idx < _ts.size() 92 | float dt = (t - _ts[idx - 1]) / (_ts[idx] - _ts[idx - 1]); 93 | return (1.0f - dt) * _vs[idx - 1] + dt * _vs[idx]; 94 | } 95 | } 96 | }; 97 | 98 | template 99 | class CubicSpline : public Spline { 100 | using Spline::_ts; 101 | using Spline::_vs; 102 | 103 | public: 104 | typedef Eigen::Triplet Triplet; 105 | typedef Eigen::SparseMatrix SpMat; 106 | enum BoundaryBehavior { NATURAL, PERIODIC }; 107 | CubicSpline(const std::vector& ts, const std::vector& vs, 108 | BoundaryBehavior boundaryBehavior = NATURAL) 109 | : Spline(ts, vs), _boundary_behavior(boundaryBehavior) { 110 | calculateCoefficients(); 111 | } 112 | 113 | ~CubicSpline() {} 114 | 115 | T eval(T t) const { 116 | int idx = std::upper_bound(_ts.begin(), _ts.end(), t) - _ts.begin(); 117 | if (idx == 0) 118 | return _vs.front(); 119 | else if (idx == (int)_ts.size()) 120 | return _vs.back(); 121 | else { 122 | // at this point, we know ts.size() > 1 and 0 < idx < _ts.size() 123 | float dt = (t - _ts[idx - 1]) / (_ts[idx] - _ts[idx - 1]); 124 | float c[4] = {_vs[idx - 1], 125 | _coeffs_1[idx - 1], 126 | _coeffs_2[idx - 1], 127 | _vs[idx]}; 128 | float a[4] = {1.0f, 3.0f, 3.0f, 1.0f}; 129 | float v = 0.0f; 130 | for (int i = 0; i < 4; i++) 131 | v += c[i] * a[i] * powf(1.0f - dt, 132 | 3.0f - (float)i) * powf(dt, (float)i); 133 | return v; 134 | } 135 | } 136 | 137 | private: 138 | void setupLinearSystem(SpMat& A, Eigen::VectorXf& b) { 139 | // assumes _ts.size() > 1 140 | int num_intervals = (int)_ts.size() - 1; 141 | int num_knots = num_intervals - 1; 142 | int n = 2 * num_intervals; 143 | 144 | // precompute interval lengths 145 | std::vector delta_inv(num_intervals); 146 | std::vector delta_inv_2(num_intervals); 147 | for (int i = 0; i < num_intervals; i++) { 148 | float temp = _ts[i + 1] - _ts[i]; 149 | delta_inv[i] = 1.0f / temp; 150 | delta_inv_2[i] = delta_inv[i] * delta_inv[i]; 151 | } 152 | 153 | // calculate non-zero entries of A and b 154 | b.resize(n); 155 | std::vector triplets; 156 | for (int i = 0; i < num_knots; i++) { 157 | // first derivative continuity 158 | triplets.push_back(Triplet(2 * i, 2 * i + 1, -3.0f * delta_inv[i])); 159 | triplets.push_back(Triplet(2 * i, 2 * (i + 1), -3.0f * delta_inv[i + 1])); 160 | b(2 * i) = -3.0f * _vs[i + 1] * delta_inv[i + 1] + 161 | -3.0f * _vs[i + 1] * delta_inv[i]; 162 | 163 | // second derivative continuity 164 | triplets.push_back(Triplet(2 * i + 1, 2 * i, 6.0f * delta_inv_2[i])); 165 | triplets.push_back( 166 | Triplet(2 * i + 1, 2 * i + 1, -12.0f * delta_inv_2[i])); 167 | triplets.push_back( 168 | Triplet(2 * i + 1, 2 * (i + 1), 12.0f * delta_inv_2[i + 1])); 169 | triplets.push_back( 170 | Triplet(2 * i + 1, 2 * (i + 1) + 1, -6.0f * delta_inv_2[i + 1])); 171 | b(2 * i + 1) = 6.0f * _vs[i + 1] * delta_inv_2[i + 1] - 172 | 6.0f * _vs[i + 1] * delta_inv_2[i]; 173 | } 174 | if (_boundary_behavior == PERIODIC) { 175 | // first derivative continuity 176 | triplets.push_back(Triplet(n - 2, 0, 3.0f * delta_inv.front())); 177 | triplets.push_back(Triplet(n - 2, n - 1, 3.0f * delta_inv.back())); 178 | b(n - 2) = 3.0f * _vs.back() * delta_inv.back() + 179 | 3.0f * _vs.front() * delta_inv.front(); 180 | 181 | // second derivative continuity 182 | triplets.push_back(Triplet(n - 1, 0, -12.0f * delta_inv_2.front())); 183 | triplets.push_back(Triplet(n - 1, 1, 6.0f * delta_inv_2.front())); 184 | triplets.push_back(Triplet(n - 1, n - 2, -6.0f * delta_inv_2.back())); 185 | triplets.push_back(Triplet(n - 1, n - 1, 12.0f * delta_inv_2.back())); 186 | b(n - 1) = 6.0f * _vs.back() * delta_inv_2.back() - 187 | 6.0f * _vs.front() * delta_inv_2.front(); 188 | } else if (_boundary_behavior == NATURAL) { 189 | triplets.push_back(Triplet(n - 2, 0, -12.0f * delta_inv_2.front())); 190 | triplets.push_back(Triplet(n - 2, 1, 6.0f * delta_inv_2.front())); 191 | b(n - 2) = -6.0f * _vs.front() * delta_inv_2.front(); 192 | 193 | triplets.push_back(Triplet(n - 1, n - 2, 6.0f * delta_inv_2.back())); 194 | triplets.push_back(Triplet(n - 1, n - 1, -12.0f * delta_inv_2.back())); 195 | b(n - 1) = -6.0f * _vs.back() * delta_inv_2.back(); 196 | } 197 | A.resize(n, n); 198 | A.setFromTriplets(triplets.begin(), triplets.end()); 199 | } 200 | 201 | void calculateCoefficients() { 202 | int num_intervals = (int)_ts.size() - 1; 203 | if (num_intervals > 0) { 204 | SpMat A; 205 | Eigen::VectorXf b, x; 206 | setupLinearSystem(A, b); 207 | // what's wrong with the following? 208 | // Eigen::SparseLU > solver; 209 | // solver.analyzePattern(A); 210 | // solver.factorize(A); 211 | // x = solver.solve(b); 212 | x = Eigen::MatrixXf(A).colPivHouseholderQr().solve(b); 213 | _coeffs_1.resize(num_intervals); 214 | _coeffs_2.resize(num_intervals); 215 | for (int i = 0; i < num_intervals; i++) { 216 | _coeffs_1[i] = x(2 * i); 217 | _coeffs_2[i] = x(2 * i + 1); 218 | } 219 | } 220 | } 221 | 222 | std::vector _coeffs_1; 223 | std::vector _coeffs_2; 224 | BoundaryBehavior _boundary_behavior; 225 | }; 226 | 227 | #endif // __SPLINES_H__ 228 | -------------------------------------------------------------------------------- /pptk/viewer/text.h: -------------------------------------------------------------------------------- 1 | #ifndef __MI_OPENGL_TEXT_H__ 2 | #define __MI_OPENGL_TEXT_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "opengl_funcs.h" 16 | 17 | /* following text rendering code adapted from libs/opengl/Text.h and 18 | libs/opengl/Text.cpp of mifit project: https://code.google.com/p/mifit/ */ 19 | 20 | class QChar; 21 | class QFont; 22 | class QFontMetrics; 23 | class QString; 24 | 25 | const int TEXTURE_SIZE = 256; 26 | 27 | class Text : public OpenGLFuncs { 28 | struct CharData { 29 | GLuint textureId; 30 | uint width; 31 | uint height; 32 | GLfloat s[2]; 33 | GLfloat t[2]; 34 | }; 35 | 36 | public: 37 | Text(QWindow* window, QOpenGLContext* context, const QFont& f) 38 | : _context(context), 39 | _window(window), 40 | font(f), 41 | fontMetrics(f), 42 | pixelFont(f), 43 | pixelFontMetrics(f), 44 | xOffset(1), 45 | yOffset(1) { 46 | _context->makeCurrent(_window); 47 | initializeOpenGLFunctions(); 48 | _context->doneCurrent(); 49 | 50 | // font sizes in units of pixels 51 | // (I don't really know how this works... this is a hack) 52 | if (_window->devicePixelRatio() != 1.0) 53 | pixelFont.setPixelSize( 54 | qRound(_window->devicePixelRatio() * font.pointSize())); 55 | pixelFontMetrics = QFontMetrics(pixelFont); 56 | } 57 | 58 | virtual ~Text() { clearCache(); } 59 | 60 | void clearCache() { 61 | if (_context == NULL) return; 62 | _context->makeCurrent(_window); 63 | foreach (GLuint texture, textures) 64 | glDeleteTextures(1, &texture); 65 | _context->doneCurrent(); 66 | textures.clear(); 67 | characters.clear(); 68 | } 69 | 70 | const QFont& getFont() const { return font; } 71 | 72 | const QFontMetrics& getFontMetrics() const { return fontMetrics; } 73 | 74 | QSizeF computeTextSize(const QString& text) { 75 | QSizeF sz; 76 | for (int i = 0; i < text.length(); ++i) { 77 | CharData& c = createCharacter(text[i]); 78 | sz.setHeight(qMax(sz.height(), (qreal)c.height)); 79 | sz.setWidth(sz.width() + c.width); 80 | } 81 | return sz; 82 | } 83 | 84 | QRectF renderText(float x, float y, const QString& text, 85 | const QVector4D& color = QVector4D(1, 1, 1, 1)) { 86 | if (_context == NULL) return QRectF(); 87 | 88 | x = 2.0f * x / _window->width() - 1.0f; 89 | y = 2.0f * y / _window->height() - 1.0f; 90 | 91 | glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT); 92 | glPushMatrix(); 93 | glEnable(GL_TEXTURE_2D); 94 | glEnable(GL_BLEND); 95 | glDisable(GL_DEPTH_TEST); 96 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 97 | glUseProgram(0); 98 | 99 | GLuint texture = 0; 100 | glLoadIdentity(); 101 | glTranslatef(x, y, 0); 102 | glColor4f(color.x(), color.y(), color.z(), color.w()); 103 | QRectF rect(QPointF(x, y), QPointF(x, y)); 104 | for (int i = 0; i < text.length(); ++i) { 105 | CharData& c = createCharacter(text[i]); 106 | 107 | if (texture != c.textureId) { 108 | texture = c.textureId; 109 | glBindTexture(GL_TEXTURE_2D, texture); 110 | } 111 | 112 | float w = c.width * 2.0f / _window->width(); 113 | float h = c.height * 2.0f / _window->height(); 114 | 115 | rect.setHeight(qMax(rect.height(), (qreal)c.height)); 116 | rect.setWidth(rect.width() + c.width); 117 | 118 | glBegin(GL_QUADS); 119 | glTexCoord2f(c.s[0], c.t[0]); 120 | glVertex2f(0, 0); 121 | 122 | glTexCoord2f(c.s[1], c.t[0]); 123 | glVertex2f(w, 0); 124 | 125 | glTexCoord2f(c.s[1], c.t[1]); 126 | glVertex2f(w, h); 127 | 128 | glTexCoord2f(c.s[0], c.t[1]); 129 | glVertex2f(0, h); 130 | glEnd(); 131 | 132 | glTranslatef(w, 0, 0); 133 | } 134 | 135 | glPopMatrix(); 136 | glPopAttrib(); 137 | 138 | return rect; 139 | } 140 | 141 | private: 142 | void allocateTexture() { 143 | GLuint texture; 144 | glGenTextures(1, &texture); 145 | glBindTexture(GL_TEXTURE_2D, texture); 146 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 147 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 148 | 149 | glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, TEXTURE_SIZE, TEXTURE_SIZE, 0, 150 | GL_RGBA, GL_UNSIGNED_BYTE, 0); 151 | 152 | textures += texture; 153 | } 154 | 155 | CharData& createCharacter(QChar c) { 156 | ushort unicodeC = c.unicode(); 157 | if (characters.contains(unicodeC)) return characters[unicodeC]; 158 | 159 | if (textures.empty()) allocateTexture(); 160 | 161 | GLuint texture = textures.last(); 162 | 163 | GLsizei width = pixelFontMetrics.width(c); 164 | GLsizei height = pixelFontMetrics.height(); 165 | 166 | QPixmap pixmap(width, height); 167 | pixmap.fill(Qt::transparent); 168 | 169 | QPainter painter; 170 | painter.begin(&pixmap); 171 | painter.setRenderHints(QPainter::HighQualityAntialiasing | 172 | QPainter::TextAntialiasing); 173 | painter.setFont(pixelFont); 174 | painter.setPen(Qt::white); 175 | 176 | painter.drawText(0, pixelFontMetrics.ascent(), c); 177 | painter.end(); 178 | QImage image = pixmap.toImage().mirrored(); 179 | 180 | if (xOffset + width >= TEXTURE_SIZE) { 181 | xOffset = 1; 182 | yOffset += height; 183 | } 184 | if (yOffset + height >= TEXTURE_SIZE) { 185 | allocateTexture(); 186 | yOffset = 1; 187 | } 188 | 189 | glBindTexture(GL_TEXTURE_2D, texture); 190 | glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, GL_RGBA, 191 | GL_UNSIGNED_BYTE, image.bits()); 192 | 193 | CharData& character = characters[unicodeC]; 194 | character.textureId = texture; 195 | character.width = fontMetrics.width(c); 196 | character.height = fontMetrics.height(); 197 | character.s[0] = static_cast(xOffset) / TEXTURE_SIZE; 198 | character.t[0] = static_cast(yOffset) / TEXTURE_SIZE; 199 | character.s[1] = static_cast(xOffset + width) / TEXTURE_SIZE; 200 | character.t[1] = static_cast(yOffset + height) / TEXTURE_SIZE; 201 | 202 | xOffset += width; 203 | return character; 204 | } 205 | 206 | QOpenGLContext* _context; 207 | QWindow* _window; 208 | 209 | QFont font; 210 | QFontMetrics fontMetrics; 211 | 212 | QFont pixelFont; 213 | QFontMetrics pixelFontMetrics; 214 | 215 | QHash characters; 216 | QList textures; 217 | 218 | GLint xOffset; 219 | GLint yOffset; 220 | }; 221 | 222 | #endif // __MI_OPENGL_TEXT_H__ 223 | -------------------------------------------------------------------------------- /pptk/viewer/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef __TIMER_H__ 2 | #define __TIMER_H__ 3 | namespace vltools { 4 | #if defined(_WIN32) || defined(__CYGWIN__) 5 | #ifndef _WIN32 6 | #define PCTIMER_NO_WIN32 7 | #endif /* WIN32 */ 8 | #define WIN32_LEAN_AND_MEAN 9 | #include // req'd for QueryPerformance[...] 10 | #ifdef PCTIMER_NO_WIN32 11 | #undef PCTIMER_NO_WIN32 12 | #undef _WIN32 13 | #endif /* PCTIMER_NO_WIN32 */ 14 | __inline double getTime() { 15 | static LARGE_INTEGER pcount, pcfreq; 16 | static int initflag; 17 | if (!initflag) { 18 | QueryPerformanceFrequency(&pcfreq); 19 | initflag++; 20 | } 21 | QueryPerformanceCounter(&pcount); 22 | return (double)pcount.QuadPart / (double)pcfreq.QuadPart; 23 | } 24 | #else /* Not Win32/Cygwin */ 25 | #include 26 | #include 27 | 28 | __inline double getTime() { 29 | struct timeval tv; 30 | gettimeofday(&tv, NULL); 31 | return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; 32 | } 33 | #endif 34 | } // namespace vltools 35 | #endif // __TIMER_H__ 36 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from distutils.extension import Extension 3 | import subprocess 4 | import os 5 | import os.path 6 | import shutil 7 | import platform 8 | 9 | from pip._internal import wheel 10 | 11 | wheel_tags = wheel.pep425tags.get_supported()[0] 12 | 13 | system_type = platform.system() 14 | 15 | license_text = b'' 16 | with open('LICENSE', 'rb') as fd: 17 | license_text = license_text + fd.read() 18 | with open(os.path.join('licenses', 'LICENSE.append.txt'), 'rb') as fd: 19 | license_text = license_text + fd.read() 20 | with open(os.path.join('pptk', 'LICENSE'), 'wb') as fd: 21 | fd.write(license_text) 22 | 23 | def make_mod(x): 24 | if system_type == 'Windows': 25 | return x + '.pyd' 26 | elif system_type == 'Linux': 27 | return x + '.so' 28 | elif system_type == 'Darwin': 29 | return x + '.so' 30 | else: 31 | raise RuntimeError('Unknown system type %s', system_type) 32 | 33 | 34 | def make_lib(x, version_suffix=''): 35 | if system_type == 'Windows': 36 | return x + '.dll' 37 | elif system_type == 'Linux': 38 | return 'lib' + x + '.so' + version_suffix 39 | elif system_type == 'Darwin': 40 | return 'lib' + x + '.dylib' 41 | else: 42 | raise RuntimeError('Unknown system type %s', system_type) 43 | 44 | 45 | def make_exe(x): 46 | if system_type == 'Windows': 47 | return x + '.exe' 48 | else: 49 | return x 50 | 51 | 52 | def list_libs(): 53 | libs_dir = os.path.join('pptk', 'libs') 54 | exclude_list = ['Makefile', 'cmake_install.cmake'] 55 | return [f for f in os.listdir(libs_dir) 56 | if os.path.isfile(os.path.join(libs_dir, f)) 57 | and f not in exclude_list] 58 | 59 | 60 | setup( 61 | name='pptk', 62 | version='0.1.1', 63 | description='A Python package for facilitating point cloud processing.', 64 | author='HERE Europe B.V.', 65 | classifiers=[ 66 | 'Intended Audience :: Science/Research', 67 | 'Topic :: Scientific/Engineering', 68 | 'License :: OSI Approved :: MIT License'], 69 | license='MIT', 70 | install_requires=['numpy'], 71 | project_urls={ 72 | 'Source': 'https://github.com/heremaps/pptk'}, 73 | packages=find_packages(), 74 | package_data={ 75 | 'pptk': [ 76 | os.path.join('libs', f) for f in list_libs()] + [ 77 | 'LICENSE', 78 | os.path.join('libs', 79 | 'qt_plugins', 'platforms', make_lib('*', '*')), 80 | os.path.join('libs', 81 | 'qt_plugins', 'xcbglintegrations', make_lib('*', '*')) 82 | ], 83 | 'pptk.kdtree': [make_mod('kdtree')], 84 | 'pptk.processing.estimate_normals': [make_mod('estimate_normals')], 85 | 'pptk.vfuncs': [make_mod('vfuncs')], 86 | 'pptk.viewer': [make_exe('viewer'), 'qt.conf']}, 87 | options={'bdist_wheel': { 88 | 'python_tag': wheel_tags[0], 89 | 'plat_name': wheel_tags[2]}}) 90 | -------------------------------------------------------------------------------- /tests/test_estimate_normals.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import pptk 3 | import numpy as np 4 | 5 | 6 | class TestEstimateNormals(unittest.TestCase): 7 | def test_rand100(self): 8 | np.random.seed(0) 9 | x = pptk.rand(100, 3) 10 | 11 | pptk.estimate_normals(x, 5, 0.2, 12 | verbose=False) 13 | pptk.estimate_normals(x, 5, 0.2, 14 | output_eigenvalues=True, 15 | verbose=False) 16 | pptk.estimate_normals(x, 5, 0.2, 17 | output_all_eigenvectors=True, 18 | verbose=False) 19 | pptk.estimate_normals(x, 5, 0.2, 20 | output_eigenvalues=True, 21 | output_all_eigenvectors=True, 22 | verbose=False) 23 | 24 | x = np.float32(x) 25 | pptk.estimate_normals(x, 5, 0.2, 26 | verbose=False) 27 | pptk.estimate_normals(x, 5, 0.2, 28 | output_eigenvalues=True, 29 | verbose=False) 30 | pptk.estimate_normals(x, 5, 0.2, 31 | output_all_eigenvectors=True, 32 | verbose=False) 33 | pptk.estimate_normals(x, 5, 0.2, 34 | output_eigenvalues=True, 35 | output_all_eigenvectors=True, 36 | verbose=False) 37 | 38 | def test_output_types(self): 39 | # test all 8 combinations of output_* switches 40 | 41 | y = np.zeros((10, 3), dtype=np.float64) 42 | y[:, 0] = np.arange(10) 43 | k = 4 44 | r = 3 45 | evals = np.array([2. / 3] + [5. / 4] * 8 + [2. / 3]) 46 | evecs = np.array([[1., 0., 0.]] * 10) 47 | nbhd_sizes = np.array([3] + [4] * 8 + [3]) 48 | 49 | # combo 1: (0, 0, 0) 50 | z = pptk.estimate_normals(y, k, r, 51 | output_eigenvalues=False, 52 | output_all_eigenvectors=False, 53 | output_neighborhood_sizes=False, 54 | verbose=False) 55 | self.assertTrue(isinstance(z, np.ndarray) and z.shape == (10, 3)) 56 | 57 | # combo 2: (0, 0, 1) 58 | z = pptk.estimate_normals(y, k, r, 59 | output_eigenvalues=False, 60 | output_all_eigenvectors=False, 61 | output_neighborhood_sizes=True, 62 | verbose=False) 63 | self.assertTrue( 64 | isinstance(z, tuple) and len(z) == 3 and 65 | [type(i) for i in z] == [np.ndarray, type(None), np.ndarray] and 66 | z[0].shape == (10, 3) and z[2].shape == (10, )) 67 | self.assertTrue(np.all(z[2] == nbhd_sizes)) 68 | 69 | # combo 3: (0, 1, 0) 70 | z = pptk.estimate_normals(y, k, r, 71 | output_eigenvalues=False, 72 | output_all_eigenvectors=True, 73 | output_neighborhood_sizes=False, 74 | verbose=False) 75 | self.assertTrue(isinstance(z, np.ndarray) and z.shape == (10, 3, 3)) 76 | 77 | # combo 4: (0, 1, 1) 78 | z = pptk.estimate_normals(y, k, r, 79 | output_eigenvalues=False, 80 | output_all_eigenvectors=True, 81 | output_neighborhood_sizes=True, 82 | verbose=False) 83 | self.assertTrue( 84 | isinstance(z, tuple) and len(z) == 3 and 85 | [type(i) for i in z] == [np.ndarray, type(None), np.ndarray] and 86 | z[0].shape == (10, 3, 3) and z[2].shape == (10, )) 87 | self.assertTrue(np.allclose(z[0][:, -1, :], evecs)) 88 | self.assertTrue(np.all(z[2] == nbhd_sizes)) 89 | 90 | # combo 5: (1, 0, 0) 91 | z = pptk.estimate_normals(y, k, r, 92 | output_eigenvalues=True, 93 | output_all_eigenvectors=False, 94 | output_neighborhood_sizes=False, 95 | verbose=False) 96 | self.assertTrue( 97 | isinstance(z, tuple) and len(z) == 3 and 98 | [type(i) for i in z] == [np.ndarray, np.ndarray, type(None)] and 99 | z[0].shape == (10, 3) and z[1].shape == (10, )) 100 | 101 | # combo 6: (1, 0, 1) 102 | z = pptk.estimate_normals(y, k, r, 103 | output_eigenvalues=True, 104 | output_all_eigenvectors=False, 105 | output_neighborhood_sizes=True, 106 | verbose=False) 107 | self.assertTrue( 108 | isinstance(z, tuple) and len(z) == 3 and 109 | [type(i) for i in z] == [np.ndarray, np.ndarray, np.ndarray] and 110 | [i.shape for i in z] == [(10, 3), (10, ), (10, )]) 111 | self.assertTrue(np.all(z[2] == nbhd_sizes)) 112 | 113 | # combo 7: (1, 1, 0) 114 | z = pptk.estimate_normals(y, k, r, 115 | output_eigenvalues=True, 116 | output_all_eigenvectors=True, 117 | output_neighborhood_sizes=False, 118 | verbose=False) 119 | self.assertTrue( 120 | isinstance(z, tuple) and len(z) == 3 and 121 | [type(i) for i in z] == [np.ndarray, np.ndarray, type(None)] and 122 | z[0].shape == (10, 3, 3) and z[1].shape == (10, 3)) 123 | self.assertTrue(np.allclose(z[0][:, -1, :], evecs)) 124 | self.assertTrue(np.allclose(z[1][:, -1], evals)) 125 | 126 | # combo 8: (1, 1, 1) 127 | z = pptk.estimate_normals(y, k, r, 128 | output_eigenvalues=True, 129 | output_all_eigenvectors=True, 130 | output_neighborhood_sizes=True, 131 | verbose=False) 132 | self.assertTrue( 133 | isinstance(z, tuple) and len(z) == 3 and 134 | [type(i) for i in z] == [np.ndarray, np.ndarray, np.ndarray] and 135 | [i.shape for i in z] == [(10, 3, 3), (10, 3), (10, )]) 136 | self.assertTrue(np.allclose(z[0][:, -1, :], evecs)) 137 | self.assertTrue(np.allclose(z[1][:, -1], evals)) 138 | self.assertTrue(np.all(z[2] == nbhd_sizes)) 139 | 140 | def test_neighborhood_types(self): 141 | pass 142 | 143 | def test_subsample(self): 144 | y = np.zeros((10, 3), dtype=np.float64) 145 | y[:, 0] = np.arange(10) 146 | k = 4 147 | r = 3 148 | evals = np.array([2. / 3] + [5. / 4] * 8 + [2. / 3]) 149 | evecs = np.array([[1., 0., 0.]] * 10) 150 | nbhd_sizes = np.array([3] + [4] * 8 + [3]) 151 | 152 | s = [0, 3, 4, -1] 153 | 154 | mask = np.zeros(10, dtype=np.bool) 155 | mask[s] = True 156 | z = pptk.estimate_normals(y, k, r, 157 | subsample=mask, 158 | output_eigenvalues=True, 159 | output_all_eigenvectors=True, 160 | output_neighborhood_sizes=True, 161 | verbose=False) 162 | self.assertTrue(np.allclose(z[0][:, -1, :], evecs[s])) 163 | self.assertTrue(np.allclose(z[1][:, -1], evals[s])) 164 | self.assertTrue(np.all(z[2] == nbhd_sizes[s])) 165 | 166 | mask = np.zeros(2, dtype=np.bool) 167 | with self.assertRaises(ValueError): 168 | pptk.estimate_normals(y, k, r, 169 | subsample=mask, 170 | output_eigenvalues=True, 171 | output_all_eigenvectors=True, 172 | output_neighborhood_sizes=True, 173 | verbose=False) 174 | 175 | z = pptk.estimate_normals(y, k, r, 176 | subsample=s, 177 | output_eigenvalues=True, 178 | output_all_eigenvectors=True, 179 | output_neighborhood_sizes=True, 180 | verbose=False) 181 | self.assertTrue(np.allclose(z[0][:, -1, :], evecs[s])) 182 | self.assertTrue(np.allclose(z[1][:, -1], evals[s])) 183 | self.assertTrue(np.all(z[2] == nbhd_sizes[s])) 184 | 185 | if __name__ == '__main__': 186 | unittest.main() 187 | -------------------------------------------------------------------------------- /tests/test_expr.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import pptk 3 | import numpy as np 4 | 5 | 6 | class TestCentroids(unittest.TestCase): 7 | def test_centroids(self): 8 | np.random.seed(0) 9 | x = np.float32(pptk.rand(100, 3)) 10 | n = x.NBHDS(k=3) 11 | y = np.vstack(pptk.MEAN(x[n], axis=0).evaluate()) 12 | self.assertTrue(y.shape == (100, 3)) 13 | 14 | 15 | if __name__ == '__main__': 16 | unittest.main() 17 | --------------------------------------------------------------------------------