update svn to revision 718
This commit is contained in:
parent
2fa4039b88
commit
b135839c2a
13 changed files with 1844 additions and 2 deletions
|
@ -0,0 +1,7 @@
|
||||||
|
IF(WITH_SEGMENTATION)
|
||||||
|
add_executable(scan2segments scan2segments.cc)
|
||||||
|
|
||||||
|
FIND_PACKAGE(OpenCV REQUIRED)
|
||||||
|
|
||||||
|
target_link_libraries(scan2segments scan ANN fbr_cv_io fbr_panorama fbr_feature fbr_feature_matcher fbr_registration ${Boost_LIBRARIES} ${OpenCV_LIBS})
|
||||||
|
ENDIF(WITH_SEGMENTATION)
|
|
@ -0,0 +1,23 @@
|
||||||
|
FIND_PACKAGE(OpenCV REQUIRED)
|
||||||
|
|
||||||
|
SET(FBR_IO_SRC scan_cv.cc)
|
||||||
|
add_library(fbr_cv_io STATIC ${FBR_IO_SRC})
|
||||||
|
|
||||||
|
SET(FBR_PANORAMA_SRC panorama.cc)
|
||||||
|
add_library(fbr_panorama STATIC ${FBR_PANORAMA_SRC} fbr_global.cc)
|
||||||
|
|
||||||
|
SET(FBR_FEATURE_SRC feature.cc)
|
||||||
|
add_library(fbr_feature STATIC ${FBR_FEATURE_SRC})
|
||||||
|
|
||||||
|
SET(FBR_FEATURE_MATCHER_SRC feature_matcher.cc)
|
||||||
|
add_library(fbr_feature_matcher STATIC ${FBR_FEATURE_MATCHER_SRC})
|
||||||
|
|
||||||
|
SET(FBR_REGISTRATION_SRC registration.cc)
|
||||||
|
add_library(fbr_registration STATIC ${FBR_REGISTRATION_SRC})
|
||||||
|
|
||||||
|
IF(WITH_FBR)
|
||||||
|
SET(FBR_LIBS scan ANN ${OpenCV_LIBS})
|
||||||
|
|
||||||
|
add_executable(featurebasedregistration feature_based_registration.cc fbr_global.cc)
|
||||||
|
target_link_libraries(featurebasedregistration fbr_cv_io fbr_panorama fbr_feature fbr_feature_matcher fbr_registration ${FBR_LIBS})
|
||||||
|
ENDIF(WITH_FBR)
|
|
@ -0,0 +1,420 @@
|
||||||
|
cmake_minimum_required (VERSION 2.6.1)
|
||||||
|
SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/3rdparty/CMakeModules" ${CMAKE_MODULE_PATH})
|
||||||
|
project (Slam6D)
|
||||||
|
|
||||||
|
#include_directories(OPENGL_INCLUDE_DIR)
|
||||||
|
IF(WIN32)
|
||||||
|
set(Boost_USE_STATIC_LIBS TRUE)
|
||||||
|
ELSE(WIN32)
|
||||||
|
set(Boost_USE_STATIC_LIBS FALSE)
|
||||||
|
ENDIF(WIN32)
|
||||||
|
|
||||||
|
SET(Boost_ADDITIONAL_VERSIONS "1.42" "1.42.0" "1.44" "1.44.0" "1.45.0" "1.45" "1.46" "1.46.1" "1.47.0" "1.47" "1.48" "1.49")
|
||||||
|
IF(WIN32)
|
||||||
|
# for some unknown reason no one variant works on all windows platforms
|
||||||
|
find_package( Boost COMPONENTS serialization graph regex filesystem system thread date_time REQUIRED)
|
||||||
|
ELSE(WIN32)
|
||||||
|
find_package( Boost COMPONENTS serialization graph regex filesystem system thread date_time REQUIRED)
|
||||||
|
ENDIF(WIN32)
|
||||||
|
|
||||||
|
if(Boost_FOUND)
|
||||||
|
link_directories(${BOOST_LIBRARY_DIRS})
|
||||||
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# Declare Options and modify build accordingly ##
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
## FreeGLUT
|
||||||
|
OPTION(WITH_FREEGLUT "Whether freeglut is available. This enables iterative drawing in show. ON/OFF" ON)
|
||||||
|
|
||||||
|
IF(WITH_FREEGLUT)
|
||||||
|
MESSAGE(STATUS "With freeglut")
|
||||||
|
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_FREEGLUT")
|
||||||
|
ELSE(WITH_FREEGLUT)
|
||||||
|
MESSAGE(STATUS "Without freeglut")
|
||||||
|
ENDIF(WITH_FREEGLUT)
|
||||||
|
|
||||||
|
## Show
|
||||||
|
OPTION(WITH_SHOW "Whether to build Show. This is the Visualization program of slam6d. ON/OFF" ON)
|
||||||
|
|
||||||
|
IF(WITH_SHOW)
|
||||||
|
FIND_PACKAGE(OpenGL REQUIRED)
|
||||||
|
FIND_PACKAGE(GLUT REQUIRED)
|
||||||
|
MESSAGE(STATUS "With show")
|
||||||
|
ELSE(WITH_SHOW)
|
||||||
|
# SET (WITH_OCTREE_DISPLAY "ON" CACHE INTERNAL "" FORCE)
|
||||||
|
MESSAGE(STATUS "Without show")
|
||||||
|
ENDIF(WITH_SHOW)
|
||||||
|
|
||||||
|
## WXShow
|
||||||
|
OPTION(WITH_WXSHOW "Whether to build WXShow. This is the wxwidgets variant of Show. ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_WXSHOW)
|
||||||
|
FIND_PACKAGE(OpenGL REQUIRED)
|
||||||
|
FIND_PACKAGE(GLUT REQUIRED)
|
||||||
|
find_package(wxWidgets COMPONENTS core base gl REQUIRED)
|
||||||
|
# set wxWidgets_wxrc_EXECUTABLE to be ignored in the configuration
|
||||||
|
SET (wxWidgets_wxrc_EXECUTABLE " " CACHE INTERNAL "" FORCE)
|
||||||
|
# wxWidgets include (this will do all the magic to configure everything)
|
||||||
|
include( ${wxWidgets_USE_FILE})
|
||||||
|
MESSAGE(STATUS "With wxshow")
|
||||||
|
ELSE(WITH_XWSHOW)
|
||||||
|
MESSAGE(STATUS "Without wxshow")
|
||||||
|
ENDIF(WITH_WXSHOW)
|
||||||
|
|
||||||
|
## Shapes
|
||||||
|
OPTION(WITH_SHAPE_DETECTION "Whether to build shapes and planes executable for detecting planes. ON/OFF" OFF)
|
||||||
|
IF(WITH_SHAPE_DETECTION)
|
||||||
|
MESSAGE(STATUS "With shape detection")
|
||||||
|
ELSE(WITH_SHAPE_DETECTION)
|
||||||
|
MESSAGE(STATUS "Without shape detection")
|
||||||
|
ENDIF(WITH_SHAPE_DETECTION)
|
||||||
|
|
||||||
|
## Interior reconstruction
|
||||||
|
option(WITH_MODEL "Whether to build model executable for modelling interior environments. ON/OFF" OFF)
|
||||||
|
|
||||||
|
if(WITH_MODEL)
|
||||||
|
message(STATUS "With interior reconstruction")
|
||||||
|
else(WITH_MODEL)
|
||||||
|
message(STATUS "Without interior reconstruction")
|
||||||
|
endif(WITH_MODEL)
|
||||||
|
|
||||||
|
## Thermo
|
||||||
|
OPTION(WITH_THERMO "Whether to build executables for mutual calibration of laser scanner and camera. ON/OFF" OFF)
|
||||||
|
IF(WITH_THERMO)
|
||||||
|
FIND_PACKAGE(OpenCV REQUIRED)
|
||||||
|
add_subdirectory(3rdparty/cvblob)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/3rdparty/cvblob)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/3rdparty/cvblob)
|
||||||
|
MESSAGE(STATUS "With thermo")
|
||||||
|
ELSE(WITH_THERMO)
|
||||||
|
MESSAGE(STATUS "Without thermo")
|
||||||
|
ENDIF(WITH_THERMO)
|
||||||
|
|
||||||
|
## Octree
|
||||||
|
OPTION(WITH_OCTREE_DISPLAY "Whether to use octree display for efficiently culling scans ON/OFF" ON)
|
||||||
|
|
||||||
|
IF(WITH_OCTREE_DISPLAY)
|
||||||
|
MESSAGE(STATUS "Using octree display")
|
||||||
|
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_GL_POINTS")
|
||||||
|
ELSE(WITH_OCTREE_DISPLAY)
|
||||||
|
MESSAGE(STATUS "Using displaylists: Warning potentially much slower")
|
||||||
|
ENDIF(WITH_OCTREE_DISPLAY)
|
||||||
|
#SET (WITH_OCTREE_DISPLAY ${WITH_OCTREE_DISPLAY} CACHE BOOL
|
||||||
|
#"Whether to use octree display for efficiently culling scans ON/OFF" FORCE)
|
||||||
|
|
||||||
|
|
||||||
|
## Octree
|
||||||
|
OPTION(WITH_COMPACT_OCTREE "Whether to use the compact octree display ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_COMPACT_OCTREE)
|
||||||
|
MESSAGE(STATUS "Using compact octrees")
|
||||||
|
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_COMPACT_TREE")
|
||||||
|
ELSE(WITH_COMPACT_OCTREE)
|
||||||
|
MESSAGE(STATUS "Not using compact octreees: Warning uses more memory")
|
||||||
|
ENDIF(WITH_COMPACT_OCTREE)
|
||||||
|
|
||||||
|
## Glee?
|
||||||
|
OPTION(WITH_GLEE "Whether to use OpenGL extensions, requires glee. ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_GLEE)
|
||||||
|
MESSAGE(STATUS "Using opengl extensions")
|
||||||
|
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_GLEE")
|
||||||
|
ELSE(WITH_GLEE)
|
||||||
|
MESSAGE(STATUS "Not using opengl extensions")
|
||||||
|
ENDIF(WITH_GLEE)
|
||||||
|
|
||||||
|
## Gridder
|
||||||
|
OPTION(WITH_GRIDDER "Whether to build the 2DGridder binary ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_GRIDDER)
|
||||||
|
MESSAGE(STATUS "With 2DGridder")
|
||||||
|
ELSE(WITH_GRIDDER)
|
||||||
|
MESSAGE(STATUS "Without 2DGridder")
|
||||||
|
ENDIF(WITH_GRIDDER)
|
||||||
|
|
||||||
|
## Dynamic VELOSLAM
|
||||||
|
OPTION(WITH_VELOSLAM "Whether to build the Velodyne data processing (veloslam/veloshow) ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_VELOSLAM)
|
||||||
|
MESSAGE(STATUS "With VELOSLAM")
|
||||||
|
ELSE(WITH_VELOSLAM)
|
||||||
|
MESSAGE(STATUS "Without VELOSLAM")
|
||||||
|
ENDIF(WITH_VELOSLAM)
|
||||||
|
|
||||||
|
## Home-made Laserscanner
|
||||||
|
OPTION(WITH_DAVID_3D_SCANNER "Whether to build the David scanner app for homemade laser scanners binary ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_DAVID_3D_SCANNER)
|
||||||
|
MESSAGE(STATUS "With David scanner")
|
||||||
|
ELSE(WITH_DAVID_3D_SCANNER)
|
||||||
|
MESSAGE(STATUS "Without David scanner")
|
||||||
|
ENDIF(WITH_DAVID_3D_SCANNER)
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
OPTION(WITH_TOOLS "Whether to build additional tools like convergence frame_to_graph etc. ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_TOOLS)
|
||||||
|
MESSAGE(STATUS "With Tools")
|
||||||
|
ELSE(WITH_TOOLS)
|
||||||
|
MESSAGE(STATUS "Without Tools")
|
||||||
|
ENDIF(WITH_TOOLS)
|
||||||
|
|
||||||
|
## Segmentation
|
||||||
|
|
||||||
|
OPTION(WITH_SEGMENTATION "Whether to build scan segmantion program ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_SEGMENTATION)
|
||||||
|
MESSAGE(STATUS "With segmentation")
|
||||||
|
find_package (Boost COMPONENTS program_options REQUIRED)
|
||||||
|
ELSE(WITH_SEGMENTATION)
|
||||||
|
MESSAGE(STATUS "Without segmentation")
|
||||||
|
ENDIF(WITH_SEGMENTATION)
|
||||||
|
|
||||||
|
## CAD matching
|
||||||
|
OPTION (WITH_CAD "Wether to build with CAD import lib ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF (WITH_CAD)
|
||||||
|
MESSAGE (STATUS "With CAD import")
|
||||||
|
find_package (Boost COMPONENTS program_options filesystem REQUIRED)
|
||||||
|
ELSE (WITH_CAD)
|
||||||
|
MESSAGE (STATUS "Without CAD import")
|
||||||
|
ENDIF (WITH_CAD)
|
||||||
|
|
||||||
|
## RivLib
|
||||||
|
OPTION(WITH_RIVLIB "Whether the RIEGL rivlib is present ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_RIVLIB)
|
||||||
|
MESSAGE(STATUS "Compiling a scan IO for RXP files")
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/3rdparty)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/3rdparty)
|
||||||
|
SET(RIEGL_DIR ${CMAKE_SOURCE_DIR}/3rdparty/riegl/)
|
||||||
|
IF(WIN32)
|
||||||
|
SET(RIVLIB ${RIEGL_DIR}libscanlib-mt.lib ${RIEGL_DIR}libctrllib-mt.lib ${RIEGL_DIR}libboost_system-mt-1_43_0-vns.lib)
|
||||||
|
ELSE(WIN32)
|
||||||
|
SET(RIVLIB ${RIEGL_DIR}libscanlib-mt-s.a ${RIEGL_DIR}libctrllib-mt-s.a ${RIEGL_DIR}libboost_system-mt-s-1_43_0-vns.a pthread)
|
||||||
|
ENDIF(WIN32)
|
||||||
|
FIND_PACKAGE(LibXml2 )
|
||||||
|
|
||||||
|
ELSE(WITH_RIVLIB)
|
||||||
|
MESSAGE(STATUS "Do NOT compile a scan IO for RXP")
|
||||||
|
ENDIF(WITH_RIVLIB)
|
||||||
|
|
||||||
|
## CUDA support, TODO depend on CUDA_FIND
|
||||||
|
OPTION(WITH_CUDA "Compile with CUDA support" OFF)
|
||||||
|
IF(WITH_CUDA)
|
||||||
|
MESSAGE(STATUS "Compiling WITH CUDA support")
|
||||||
|
FIND_PACKAGE(CUDA)
|
||||||
|
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_CUDA")
|
||||||
|
ELSE(WITH_CUDA)
|
||||||
|
MESSAGE(STATUS "Compiling WITHOUT CUDA support")
|
||||||
|
ENDIF(WITH_CUDA)
|
||||||
|
|
||||||
|
## PMD
|
||||||
|
OPTION(WITH_PMD "Whether to build the PMD tools like grabVideoAnd3D calibrate etc. ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_PMD)
|
||||||
|
FIND_PACKAGE(OpenGL REQUIRED)
|
||||||
|
MESSAGE(STATUS "With Tools")
|
||||||
|
ELSE(WITH_PMD)
|
||||||
|
MESSAGE(STATUS "Without Tools")
|
||||||
|
ENDIF(WITH_PMD)
|
||||||
|
|
||||||
|
## FBR
|
||||||
|
OPTION(WITH_FBR "Whether to compile feature based registration. ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_FBR)
|
||||||
|
MESSAGE(STATUS "With FBR ")
|
||||||
|
ELSE(WITH_FBR)
|
||||||
|
MESSAGE(STATUS "Without FBR")
|
||||||
|
ENDIF(WITH_FBR)
|
||||||
|
|
||||||
|
## Special treatment for system specifics
|
||||||
|
IF(APPLE)
|
||||||
|
add_definitions(-Dfopen64=fopen)
|
||||||
|
ENDIF(APPLE)
|
||||||
|
|
||||||
|
## Multiple Cores
|
||||||
|
IF(APPLE)
|
||||||
|
SET(PROCESSOR_COUNT 2)
|
||||||
|
ELSE(APPLE)
|
||||||
|
INCLUDE(CountProcessors)
|
||||||
|
SET(NUMBER_OF_CPUS "${PROCESSOR_COUNT}" CACHE STRING "The number of processors to use (default: ${PROCESSOR_COUNT})" )
|
||||||
|
ENDIF(APPLE)
|
||||||
|
|
||||||
|
# OPEN
|
||||||
|
FIND_PACKAGE(OpenMP)
|
||||||
|
IF(OPENMP_FOUND)
|
||||||
|
OPTION(WITH_OPENMP "Whether to use parallel processing capabilities of OPENMP. ON/OFF" ON)
|
||||||
|
ENDIF(OPENMP_FOUND)
|
||||||
|
|
||||||
|
IF(OPENMP_FOUND AND WITH_OPENMP)
|
||||||
|
MESSAGE(STATUS "With OpenMP ")
|
||||||
|
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMAX_OPENMP_NUM_THREADS=${PROCESSOR_COUNT} -DOPENMP_NUM_THREADS=${NUMBER_OF_CPUS} ${OpenMP_CXX_FLAGS} -DOPENMP")
|
||||||
|
ELSE(OPENMP_FOUND AND WITH_OPENMP)
|
||||||
|
MESSAGE(STATUS "Without OpenMP")
|
||||||
|
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMAX_OPENMP_NUM_THREADS=1 -DOPENMP_NUM_THREADS=1")
|
||||||
|
ENDIF(OPENMP_FOUND AND WITH_OPENMP)
|
||||||
|
|
||||||
|
## TORO
|
||||||
|
OPTION(WITH_TORO "Whether to use TORO. ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_TORO)
|
||||||
|
IF(WIN32)
|
||||||
|
SET(Subversion_SVN_EXECUTABLE "svn.exe")
|
||||||
|
ENDIF(WIN32)
|
||||||
|
cmake_minimum_required (VERSION 2.8)
|
||||||
|
include(ExternalProject)
|
||||||
|
ExternalProject_Add(toro3d
|
||||||
|
SVN_REPOSITORY https://www.openslam.org/data/svn/toro/trunk
|
||||||
|
SOURCE_DIR "${CMAKE_SOURCE_DIR}/3rdparty/toro"
|
||||||
|
CONFIGURE_COMMAND ""
|
||||||
|
BUILD_COMMAND make
|
||||||
|
BUILD_IN_SOURCE 1
|
||||||
|
INSTALL_COMMAND cp ${CMAKE_SOURCE_DIR}/3rdparty/toro/toro3d ${CMAKE_SOURCE_DIR}/bin/
|
||||||
|
)
|
||||||
|
MESSAGE(STATUS "With TORO ")
|
||||||
|
ELSE(WITH_TORO)
|
||||||
|
MESSAGE(STATUS "Without TORO")
|
||||||
|
ENDIF(WITH_TORO)
|
||||||
|
|
||||||
|
|
||||||
|
## HOGMAN
|
||||||
|
OPTION(WITH_HOGMAN "Whether to use HOGMAN. ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_HOGMAN)
|
||||||
|
# dependant on libqt4-devi
|
||||||
|
find_package( Qt4 REQUIRED)
|
||||||
|
# CMake of earlier versions do not have external project capabilities
|
||||||
|
cmake_minimum_required (VERSION 2.8)
|
||||||
|
include(ExternalProject)
|
||||||
|
ExternalProject_Add(hogman
|
||||||
|
SVN_REPOSITORY https://svn.openslam.org/data/svn/hog-man/trunk
|
||||||
|
SOURCE_DIR "${CMAKE_SOURCE_DIR}/3rdparty/hogman"
|
||||||
|
CONFIGURE_COMMAND <SOURCE_DIR>/configure --prefix=<INSTALL_DIR>
|
||||||
|
BUILD_COMMAND LD_LIBRARY_PATH=${CMAKE_SOURCE_DIR}/3rdparty/hogman/lib make
|
||||||
|
BUILD_IN_SOURCE 1
|
||||||
|
INSTALL_COMMAND cp ${CMAKE_SOURCE_DIR}/3rdparty/hogman/bin/hogman3d ${CMAKE_SOURCE_DIR}/bin/ &&
|
||||||
|
cp ${CMAKE_SOURCE_DIR}/3rdparty/hogman/lib/libhogman_csparse.so ${CMAKE_SOURCE_DIR}/lib/ &&
|
||||||
|
cp ${CMAKE_SOURCE_DIR}/3rdparty/hogman/lib/libhogman_graph_optimizer_hogman.so ${CMAKE_SOURCE_DIR}/lib/ &&
|
||||||
|
cp ${CMAKE_SOURCE_DIR}/3rdparty/hogman/lib/libhogman_graph_optimizer.so ${CMAKE_SOURCE_DIR}/lib/ &&
|
||||||
|
cp ${CMAKE_SOURCE_DIR}/3rdparty/hogman/lib/libhogman_graph.so ${CMAKE_SOURCE_DIR}/lib/ &&
|
||||||
|
cp ${CMAKE_SOURCE_DIR}/3rdparty/hogman/lib/libhogman_graph_viewer.so ${CMAKE_SOURCE_DIR}/lib/ &&
|
||||||
|
cp ${CMAKE_SOURCE_DIR}/3rdparty/hogman/lib/libhogman_math.so ${CMAKE_SOURCE_DIR}/lib/ &&
|
||||||
|
cp ${CMAKE_SOURCE_DIR}/3rdparty/hogman/lib/libhogman_qglviewer.so ${CMAKE_SOURCE_DIR}/lib/ &&
|
||||||
|
cp ${CMAKE_SOURCE_DIR}/3rdparty/hogman/lib/libhogman_stuff.so ${CMAKE_SOURCE_DIR}/lib/
|
||||||
|
)
|
||||||
|
MESSAGE(STATUS "With HOGMAN: Currently hogman needs to be compiled manually, please make sure hogman3d is somewhere in your PATH")
|
||||||
|
ELSE(WITH_HOGMAN)
|
||||||
|
MESSAGE(STATUS "Without HOGMAN")
|
||||||
|
ENDIF(WITH_HOGMAN)
|
||||||
|
|
||||||
|
OPTION(EXPORT_SHARED_LIBS "Whether to build additional shared libraries for use in other projects. ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(EXPORT_SHARED_LIBS)
|
||||||
|
MESSAGE(STATUS "exporting additional libraries")
|
||||||
|
ELSE(EXPORT_SHARED_LIBS)
|
||||||
|
MESSAGE(STATUS "not exporting libraries")
|
||||||
|
ENDIF(EXPORT_SHARED_LIBS)
|
||||||
|
|
||||||
|
OPTION(WITH_METRICS "Whether to use metrics in slam6d. ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_METRICS)
|
||||||
|
MESSAGE(STATUS "With metrics in slam6d.")
|
||||||
|
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWITH_METRICS")
|
||||||
|
ELSE(WITH_METRICS)
|
||||||
|
MESSAGE(STATUS "Without metrics in slam6d.")
|
||||||
|
ENDIF(WITH_METRICS)
|
||||||
|
|
||||||
|
IF(WIN32)
|
||||||
|
SET(ADDITIONAL_CFLAGS "-O2" CACHE STRING"Additional flags given to the compiler ()" )
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/3rdparty/windows/)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/3rdparty/windows)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/3rdparty/windows/x64)
|
||||||
|
add_library(XGetopt STATIC ${CMAKE_SOURCE_DIR}/3rdparty/windows/XGetopt.cpp)
|
||||||
|
SET(CMAKE_STATIC_LIBRARY_SUFFIX "32.lib")
|
||||||
|
ELSE(WIN32)
|
||||||
|
SET(ADDITIONAL_CFLAGS "-O3 -std=c++0x -msse3 -Wall -finline-functions -Wno-unused-but-set-variable -Wno-write-strings -Wno-char-subscripts -Wno-unused-result" CACHE STRING"Additional flags given to the compiler (-O3 -Wall -finline-functions -Wno-write-strings)" )
|
||||||
|
# Add include path for OpenGL without GL/-prefix
|
||||||
|
# to avoid the include incompatibility between MACOS
|
||||||
|
# and linux
|
||||||
|
FIND_PATH(OPENGL_INC gl.h /usr/include/GL)
|
||||||
|
include_directories(${OPENGL_INC})
|
||||||
|
ENDIF(WIN32)
|
||||||
|
|
||||||
|
# Add OpenGL includes for MACOS if needed
|
||||||
|
# The OSX OpenGL frameworks natively supports freeglut extensions
|
||||||
|
IF(APPLE)
|
||||||
|
include_directories(/System/Library/Frameworks/GLUT.framework/Headers)
|
||||||
|
include_directories(/System/Library/Frameworks/OpenGL.framework/Headers)
|
||||||
|
ENDIF(APPLE)
|
||||||
|
|
||||||
|
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ADDITIONAL_CFLAGS}")
|
||||||
|
|
||||||
|
# Hide CMake variables
|
||||||
|
SET (CMAKE_INSTALL_PREFIX "/usr/local" CACHE INTERNAL "" FORCE)
|
||||||
|
SET (CMAKE_BUILD_TYPE "" CACHE INTERNAL "" FORCE)
|
||||||
|
|
||||||
|
|
||||||
|
# Set output directories for libraries and executables
|
||||||
|
SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib )
|
||||||
|
SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/obj )
|
||||||
|
SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin )
|
||||||
|
|
||||||
|
# Set include and link dirs ...
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/include)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/3rdparty/)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/3rdparty/glui)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/3rdparty/wxthings/include/)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/3rdparty/ann_1.1.1_modified/include)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/3rdparty/ann_1.1.1_modified/src)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/obj)
|
||||||
|
link_directories(${CMAKE_SOURCE_DIR}/lib)
|
||||||
|
|
||||||
|
add_subdirectory(3rdparty)
|
||||||
|
add_subdirectory(src/slam6d)
|
||||||
|
add_subdirectory(src/scanio)
|
||||||
|
add_subdirectory(src/scanserver)
|
||||||
|
add_subdirectory(src/segmentation)
|
||||||
|
add_subdirectory(src/veloslam)
|
||||||
|
add_subdirectory(src/show)
|
||||||
|
add_subdirectory(src/grid)
|
||||||
|
add_subdirectory(src/pmd)
|
||||||
|
add_subdirectory(src/shapes)
|
||||||
|
add_subdirectory(src/thermo)
|
||||||
|
IF(WITH_FBR)
|
||||||
|
add_subdirectory(src/slam6d/fbr)
|
||||||
|
ENDIF(WITH_FBR)
|
||||||
|
add_subdirectory(src/scanner)
|
||||||
|
add_subdirectory(src/model)
|
||||||
|
|
||||||
|
IF(EXPORT_SHARED_LIBS)
|
||||||
|
## Compiling a shared library containing all of the project
|
||||||
|
add_library(slam SHARED src/slam6d/icp6D.cc)
|
||||||
|
target_link_libraries(slam scanlib_s ANN_s sparse_s newmat_s)
|
||||||
|
ENDIF(EXPORT_SHARED_LIBS)
|
||||||
|
|
||||||
|
MESSAGE (STATUS "Build environment is set up!")
|
||||||
|
|
||||||
|
|
||||||
|
# hack to "circumvent" Debug and Release folders that are created under visual studio
|
||||||
|
# this is why the INSTALL target has to be used in visual studio
|
||||||
|
IF(MSVC)
|
||||||
|
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/bin/Release/ CONFIGURATIONS Release DESTINATION ${CMAKE_SOURCE_DIR}/windows FILES_MATCHING PATTERN "*.dll" PATTERN "*.exe")
|
||||||
|
IF( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||||
|
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/3rdparty/windows/x64/ CONFIGURATIONS Release DESTINATION ${CMAKE_SOURCE_DIR}/windows FILES_MATCHING PATTERN "*.dll" PATTERN "*.exe")
|
||||||
|
ELSE( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||||
|
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/3rdparty/windows/ CONFIGURATIONS Release DESTINATION ${CMAKE_SOURCE_DIR}/windows FILES_MATCHING PATTERN "*.dll" PATTERN "*.exe")
|
||||||
|
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||||
|
|
||||||
|
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/bin/Debug/ CONFIGURATIONS Debug DESTINATION ${CMAKE_SOURCE_DIR}/windows FILES_MATCHING PATTERN "*.dll" PATTERN "*.exe")
|
||||||
|
IF( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||||
|
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/3rdparty/windows/x64/ CONFIGURATIONS Debug DESTINATION ${CMAKE_SOURCE_DIR}/windows FILES_MATCHING PATTERN "*.dll" PATTERN "*.exe")
|
||||||
|
ELSE( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||||
|
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/3rdparty/windows/ CONFIGURATIONS Debug DESTINATION ${CMAKE_SOURCE_DIR}/windows FILES_MATCHING PATTERN "*.dll" PATTERN "*.exe")
|
||||||
|
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
||||||
|
ENDIF(MSVC)
|
|
@ -0,0 +1,640 @@
|
||||||
|
/*
|
||||||
|
* scan_red implementation
|
||||||
|
*
|
||||||
|
* Copyright (C) Johannes Schauer
|
||||||
|
*
|
||||||
|
* Released under the GPL version 3.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "slam6d/scan.h"
|
||||||
|
#include "slam6d/globals.icc"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
using std::cout;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
#include "slam6d/fbr/panorama.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
enum image_type {M_RANGE, M_INTENSITY};
|
||||||
|
|
||||||
|
enum segment_method {THRESHOLD, ADAPTIVE_THRESHOLD, PYR_MEAN_SHIFT, WATERSHED};
|
||||||
|
|
||||||
|
/* Function used to check that 'opt1' and 'opt2' are not specified
|
||||||
|
at the same time. */
|
||||||
|
void conflicting_options(const po::variables_map & vm,
|
||||||
|
const char *opt1, const char *opt2)
|
||||||
|
{
|
||||||
|
if (vm.count(opt1) && !vm[opt1].defaulted()
|
||||||
|
&& vm.count(opt2) && !vm[opt2].defaulted())
|
||||||
|
throw std::logic_error(string("Conflicting options '")
|
||||||
|
+ opt1 + "' and '" + opt2 + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function used to check that if 'for_what' is specified, then
|
||||||
|
'required_option' is specified too. */
|
||||||
|
void option_dependency(const po::variables_map & vm,
|
||||||
|
const char *for_what, const char *required_option)
|
||||||
|
{
|
||||||
|
if (vm.count(for_what) && !vm[for_what].defaulted())
|
||||||
|
if (vm.count(required_option) == 0
|
||||||
|
|| vm[required_option].defaulted())
|
||||||
|
throw std::logic_error(string("Option '") + for_what +
|
||||||
|
"' requires option '" +
|
||||||
|
required_option + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* validates panorama method specification
|
||||||
|
*/
|
||||||
|
namespace fbr {
|
||||||
|
void validate(boost::any& v, const std::vector<std::string>& values,
|
||||||
|
projection_method*, int) {
|
||||||
|
if (values.size() == 0)
|
||||||
|
throw std::runtime_error("Invalid model specification");
|
||||||
|
string arg = values.at(0);
|
||||||
|
if(strcasecmp(arg.c_str(), "EQUIRECTANGULAR") == 0) v = EQUIRECTANGULAR;
|
||||||
|
else if(strcasecmp(arg.c_str(), "CYLINDRICAL") == 0) v = CYLINDRICAL;
|
||||||
|
else if(strcasecmp(arg.c_str(), "MERCATOR") == 0) v = MERCATOR;
|
||||||
|
else if(strcasecmp(arg.c_str(), "RECTILINEAR") == 0) v = RECTILINEAR;
|
||||||
|
else if(strcasecmp(arg.c_str(), "PANNINI") == 0) v = PANNINI;
|
||||||
|
else if(strcasecmp(arg.c_str(), "STEREOGRAPHIC") == 0) v = STEREOGRAPHIC;
|
||||||
|
else if(strcasecmp(arg.c_str(), "ZAXIS") == 0) v = ZAXIS;
|
||||||
|
else if(strcasecmp(arg.c_str(), "CONIC") == 0) v = CONIC;
|
||||||
|
else throw std::runtime_error(std::string("projection method ") + arg + std::string(" is unknown"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* validates segmentation method specification
|
||||||
|
*/
|
||||||
|
void validate(boost::any& v, const std::vector<std::string>& values,
|
||||||
|
segment_method*, int) {
|
||||||
|
if (values.size() == 0)
|
||||||
|
throw std::runtime_error("Invalid model specification");
|
||||||
|
string arg = values.at(0);
|
||||||
|
if(strcasecmp(arg.c_str(), "THRESHOLD") == 0) v = THRESHOLD;
|
||||||
|
else if(strcasecmp(arg.c_str(), "ADAPTIVE_THRESHOLD") == 0) v = ADAPTIVE_THRESHOLD;
|
||||||
|
else if(strcasecmp(arg.c_str(), "PYR_MEAN_SHIFT") == 0) v = PYR_MEAN_SHIFT;
|
||||||
|
else if(strcasecmp(arg.c_str(), "WATERSHED") == 0) v = WATERSHED;
|
||||||
|
else throw std::runtime_error(std::string("segmentation method ") + arg + std::string(" is unknown"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* validates input type specification
|
||||||
|
*/
|
||||||
|
void validate(boost::any& v, const std::vector<std::string>& values,
|
||||||
|
IOType*, int) {
|
||||||
|
if (values.size() == 0)
|
||||||
|
throw std::runtime_error("Invalid model specification");
|
||||||
|
string arg = values.at(0);
|
||||||
|
try {
|
||||||
|
v = formatname_to_io_type(arg.c_str());
|
||||||
|
} catch (...) { // runtime_error
|
||||||
|
throw std::runtime_error("Format " + arg + " unknown.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parse commandline options, fill arguments
|
||||||
|
*/
|
||||||
|
void parse_options(int argc, char **argv, int &start, int &end,
|
||||||
|
bool &scanserver, image_type &itype, int &width, int &height,
|
||||||
|
fbr::projection_method &ptype, string &dir, IOType &iotype,
|
||||||
|
int &maxDist, int &minDist, int &nImages, int &pParam, bool &logarithm,
|
||||||
|
float &cutoff, segment_method &stype, string &marker, bool &dump_pano,
|
||||||
|
bool &dump_seg, double &thresh, int &maxlevel, int &radius)
|
||||||
|
{
|
||||||
|
po::options_description generic("Generic options");
|
||||||
|
generic.add_options()
|
||||||
|
("help,h", "output this help message");
|
||||||
|
|
||||||
|
po::options_description input("Input options");
|
||||||
|
input.add_options()
|
||||||
|
("start,s", po::value<int>(&start)->default_value(0),
|
||||||
|
"start at scan <arg> (i.e., neglects the first <arg> scans) "
|
||||||
|
"[ATTENTION: counting naturally starts with 0]")
|
||||||
|
("end,e", po::value<int>(&end)->default_value(-1),
|
||||||
|
"end after scan <arg>")
|
||||||
|
("format,f", po::value<IOType>(&iotype)->default_value(UOS),
|
||||||
|
"using shared library <arg> for input. (chose F from {uos, uos_map, "
|
||||||
|
"uos_rgb, uos_frames, uos_map_frames, old, rts, rts_map, ifp, "
|
||||||
|
"riegl_txt, riegl_rgb, riegl_bin, zahn, ply})")
|
||||||
|
("max,M", po::value<int>(&maxDist)->default_value(-1),
|
||||||
|
"neglegt all data points with a distance larger than <arg> 'units")
|
||||||
|
("min,m", po::value<int>(&minDist)->default_value(-1),
|
||||||
|
"neglegt all data points with a distance smaller than <arg> 'units")
|
||||||
|
("scanserver,S", po::value<bool>(&scanserver)->default_value(false),
|
||||||
|
"Use the scanserver as an input method and handling of scan data");
|
||||||
|
|
||||||
|
po::options_description image("Panorama image options");
|
||||||
|
image.add_options()
|
||||||
|
("range,r", "create range image")
|
||||||
|
("intensity,i", "create intensity image")
|
||||||
|
("width,w", po::value<int>(&width)->default_value(1280),
|
||||||
|
"width of panorama")
|
||||||
|
("height,h", po::value<int>(&height)->default_value(960),
|
||||||
|
"height of panorama")
|
||||||
|
("panorama,p", po::value<fbr::projection_method>(&ptype)->
|
||||||
|
default_value(fbr::EQUIRECTANGULAR), "panorama type (EQUIRECTANGULAR, "
|
||||||
|
"CYLINDRICAL, MERCATOR, RECTILINEAR, PANNINI, STEREOGRAPHIC, ZAXIS, "
|
||||||
|
"CONIC)")
|
||||||
|
("num-images,N", po::value<int>(&nImages)->default_value(1),
|
||||||
|
"number of images used for some projections")
|
||||||
|
("proj-param,P", po::value<int>(&pParam)->default_value(0),
|
||||||
|
"special projection parameter")
|
||||||
|
("dump-pano,d", po::bool_switch(&dump_pano),
|
||||||
|
"output panorama (useful to create marker image for watershed)");
|
||||||
|
|
||||||
|
po::options_description range_image("Range image options");
|
||||||
|
range_image.add_options()
|
||||||
|
("logarithm,L", po::bool_switch(&logarithm),
|
||||||
|
"use the logarithm for range image panoramas")
|
||||||
|
("cutoff,C", po::value<float>(&cutoff)->default_value(0.0), // FIXME: percentage is the wrong word
|
||||||
|
"percentage of furthest away data points to cut off to improve "
|
||||||
|
"precision for closer values (values from 0.0 to 1.0)");
|
||||||
|
|
||||||
|
po::options_description segment("Segmentation options");
|
||||||
|
segment.add_options()
|
||||||
|
("segment,g", po::value<segment_method>(&stype)->
|
||||||
|
default_value(PYR_MEAN_SHIFT), "segmentation method (THRESHOLD, "
|
||||||
|
"ADAPTIVE_THRESHOLD, PYR_MEAN_SHIFT, WATERSHED)")
|
||||||
|
("marker,K", po::value<string>(&marker),
|
||||||
|
"marker mask for watershed segmentation")
|
||||||
|
("thresh,T", po::value<double>(&thresh),
|
||||||
|
"threshold for threshold segmentation")
|
||||||
|
("maxlevel,X", po::value<int>(&maxlevel),
|
||||||
|
"maximum level for meanshift segmentation")
|
||||||
|
("radius,R", po::value<int>(&radius),
|
||||||
|
"radius for meanshift segmentation")
|
||||||
|
("dump-seg,D", po::bool_switch(&dump_seg),
|
||||||
|
"output segmentation image (for debugging)");
|
||||||
|
|
||||||
|
po::options_description hidden("Hidden options");
|
||||||
|
hidden.add_options()
|
||||||
|
("input-dir", po::value<string>(&dir), "input dir");
|
||||||
|
|
||||||
|
// all options
|
||||||
|
po::options_description all;
|
||||||
|
all.add(generic).add(input).add(image).add(range_image).add(segment).add(hidden);
|
||||||
|
|
||||||
|
// options visible with --help
|
||||||
|
po::options_description cmdline_options;
|
||||||
|
cmdline_options.add(generic).add(input).add(image).add(range_image).add(segment);
|
||||||
|
|
||||||
|
// positional argument
|
||||||
|
po::positional_options_description pd;
|
||||||
|
pd.add("input-dir", 1);
|
||||||
|
|
||||||
|
// process options
|
||||||
|
po::variables_map vm;
|
||||||
|
po::store(po::command_line_parser(argc, argv).
|
||||||
|
options(all).positional(pd).run(), vm);
|
||||||
|
po::notify(vm);
|
||||||
|
|
||||||
|
// display help
|
||||||
|
if (vm.count("help")) {
|
||||||
|
cout << cmdline_options;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// option conflicts and dependencies
|
||||||
|
conflicting_options(vm, "range", "intensity");
|
||||||
|
option_dependency(vm, "logarithm", "range");
|
||||||
|
option_dependency(vm, "cutoff", "range");
|
||||||
|
|
||||||
|
// decide between range and intensity panorama
|
||||||
|
if (vm.count("range"))
|
||||||
|
itype = M_RANGE;
|
||||||
|
else
|
||||||
|
itype = M_INTENSITY;
|
||||||
|
|
||||||
|
// option dependencies on segmentation types
|
||||||
|
if (stype == WATERSHED) {
|
||||||
|
if (!vm.count("marker"))
|
||||||
|
throw std::logic_error("watershed segmentation requires --marker to be set");
|
||||||
|
if (vm.count("thresh"))
|
||||||
|
throw std::logic_error("watershed segmentation cannot be used with --thresh");
|
||||||
|
if (vm.count("maxlevel"))
|
||||||
|
throw std::logic_error("watershed segmentation cannot be used with --maxlevel");
|
||||||
|
if (vm.count("radius"))
|
||||||
|
throw std::logic_error("watershed segmentation cannot be used with --radius");
|
||||||
|
}
|
||||||
|
if (stype == THRESHOLD) {
|
||||||
|
if (!vm.count("thresh"))
|
||||||
|
throw std::logic_error("threshold segmentation requires --thresh to be set");
|
||||||
|
if (vm.count("marker"))
|
||||||
|
throw std::logic_error("threshold segmentation cannot be used with --marker");
|
||||||
|
if (vm.count("maxlevel"))
|
||||||
|
throw std::logic_error("threshold segmentation cannot be used with --maxlevel");
|
||||||
|
if (vm.count("radius"))
|
||||||
|
throw std::logic_error("threshold segmentation cannot be used with --radius");
|
||||||
|
}
|
||||||
|
if (stype == PYR_MEAN_SHIFT) {
|
||||||
|
if (!vm.count("maxlevel"))
|
||||||
|
throw std::logic_error("mean shift segmentation requires --maxlevel to be set");
|
||||||
|
if (!vm.count("radius"))
|
||||||
|
throw std::logic_error("mean shift segmentation requires --radius to be set");
|
||||||
|
if (vm.count("thresh"))
|
||||||
|
throw std::logic_error("mean shift segmentation cannot be used with --thresh");
|
||||||
|
if (vm.count("marker"))
|
||||||
|
throw std::logic_error("mean shift segmentation cannot be used with --marker");
|
||||||
|
}
|
||||||
|
|
||||||
|
// correct pParam and nImages for certain panorama types
|
||||||
|
if (ptype == fbr::PANNINI && pParam == 0) {
|
||||||
|
pParam = 1;
|
||||||
|
if(nImages < 3) nImages = 3;
|
||||||
|
}
|
||||||
|
if (ptype == fbr::STEREOGRAPHIC && pParam == 0) {
|
||||||
|
pParam = 2;
|
||||||
|
if(nImages < 3) nImages = 3;
|
||||||
|
}
|
||||||
|
if (ptype == fbr::RECTILINEAR && nImages < 3) {
|
||||||
|
nImages = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add trailing slash to directory if not present yet
|
||||||
|
if (dir[dir.length()-1] != '/') dir = dir + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* retrieve a cv::Mat with x,y,z,r from a scan object
|
||||||
|
* functionality borrowed from scan_cv::convertScanToMat but this function
|
||||||
|
* does not allow a scanserver to be used, prints to stdout and can only
|
||||||
|
* handle a single scan
|
||||||
|
*/
|
||||||
|
cv::Mat scan2mat(Scan *source)
|
||||||
|
{
|
||||||
|
DataXYZ xyz = source->get("xyz");
|
||||||
|
DataReflectance xyz_reflectance = source->get("reflectance");
|
||||||
|
unsigned int nPoints = xyz.size();
|
||||||
|
cv::Mat scan(nPoints,1,CV_32FC(4));
|
||||||
|
scan = cv::Scalar::all(0);
|
||||||
|
cv::MatIterator_<cv::Vec4f> it;
|
||||||
|
it = scan.begin<cv::Vec4f>();
|
||||||
|
for(unsigned int i = 0; i < nPoints; i++){
|
||||||
|
float x, y, z, reflectance;
|
||||||
|
x = xyz[i][0];
|
||||||
|
y = xyz[i][1];
|
||||||
|
z = xyz[i][2];
|
||||||
|
reflectance = xyz_reflectance[i];
|
||||||
|
|
||||||
|
//normalize the reflectance
|
||||||
|
reflectance += 32;
|
||||||
|
reflectance /= 64;
|
||||||
|
reflectance -= 0.2;
|
||||||
|
reflectance /= 0.3;
|
||||||
|
if (reflectance < 0) reflectance = 0;
|
||||||
|
if (reflectance > 1) reflectance = 1;
|
||||||
|
|
||||||
|
(*it)[0] = x;
|
||||||
|
(*it)[1] = y;
|
||||||
|
(*it)[2] = z;
|
||||||
|
(*it)[3] = reflectance;
|
||||||
|
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* convert a matrix of float values (range image) to a matrix of unsigned
|
||||||
|
* eight bit characters using different techniques
|
||||||
|
*/
|
||||||
|
cv::Mat float2uchar(cv::Mat &source, bool logarithm, float cutoff)
|
||||||
|
{
|
||||||
|
cv::Mat result(source.size(), CV_8U, cv::Scalar::all(0));
|
||||||
|
float max = 0;
|
||||||
|
// find maximum value
|
||||||
|
if (cutoff == 0.0) {
|
||||||
|
// without cutoff, just iterate through all values to find the largest
|
||||||
|
for (cv::MatIterator_<float> it = source.begin<float>();
|
||||||
|
it != source.end<float>(); ++it) {
|
||||||
|
float val = *it;
|
||||||
|
if (val > max) {
|
||||||
|
max = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// when a cutoff is specified, sort all the points by value and then
|
||||||
|
// specify the max so that <cutoff> values are larger than it
|
||||||
|
vector<float> sorted(source.cols*source.rows);
|
||||||
|
int i = 0;
|
||||||
|
for (cv::MatIterator_<float> it = source.begin<float>();
|
||||||
|
it != source.end<float>(); ++it, ++i) {
|
||||||
|
sorted[i] = *it;
|
||||||
|
}
|
||||||
|
std::sort(sorted.begin(), sorted.end());
|
||||||
|
max = sorted[(int)(source.cols*source.rows*(1.0-cutoff))];
|
||||||
|
cout << "A cutoff of " << cutoff << " resulted in a max value of " << max << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::MatIterator_<float> src = source.begin<float>();
|
||||||
|
cv::MatIterator_<uchar> dst = result.begin<uchar>();
|
||||||
|
cv::MatIterator_<float> end = source.end<float>();
|
||||||
|
if (logarithm) {
|
||||||
|
// stretch values from 0 to max logarithmically over 0 to 255
|
||||||
|
// using the logarithm allows to represent smaller values with more
|
||||||
|
// precision and larger values with less
|
||||||
|
max = log(max+1);
|
||||||
|
for (; src != end; ++src, ++dst) {
|
||||||
|
float val = (log(*src+1)*255.0)/max;
|
||||||
|
if (val > 255)
|
||||||
|
*dst = 255;
|
||||||
|
else
|
||||||
|
*dst = (uchar)val;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// stretch values from 0 to max linearly over 0 to 255
|
||||||
|
for (; src != end; ++src, ++dst) {
|
||||||
|
float val = (*src*255.0)/max;
|
||||||
|
if (val > 255)
|
||||||
|
*dst = 255;
|
||||||
|
else
|
||||||
|
*dst = (uchar)val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* from grayscale image, create a binary image using a fixed threshold
|
||||||
|
*/
|
||||||
|
cv::Mat calculateThreshold(vector<vector<cv::Vec3f>> &segmented_points,
|
||||||
|
cv::Mat &img, vector<vector<vector<cv::Vec3f> > > extendedMap,
|
||||||
|
double thresh)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
cv::Mat res;
|
||||||
|
cv::threshold(img, res, thresh, 255, cv::THRESH_BINARY);
|
||||||
|
segmented_points.resize(2);
|
||||||
|
|
||||||
|
for (i = 0; i < res.rows; i++) {
|
||||||
|
for (j = 0; j < res.cols; j++) {
|
||||||
|
idx = res.at<uchar>(i,j);
|
||||||
|
if (idx != 0)
|
||||||
|
idx = 1;
|
||||||
|
segmented_points[idx].insert(segmented_points[idx].end(),
|
||||||
|
extendedMap[i][j].begin(),
|
||||||
|
extendedMap[i][j].end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* calculate the pyramid mean shift segmentation of the image
|
||||||
|
*/
|
||||||
|
cv::Mat calculatePyrMeanShift(vector<vector<cv::Vec3f>> &segmented_points,
|
||||||
|
cv::Mat &img, vector<vector<vector<cv::Vec3f> > > extendedMap,
|
||||||
|
int maxlevel, int radius)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
cv::Mat imgGray, res, tmp;
|
||||||
|
cvtColor(img, imgGray, CV_GRAY2BGR);
|
||||||
|
cv::pyrMeanShiftFiltering(imgGray, tmp, radius, radius, maxlevel);
|
||||||
|
cvtColor(tmp, res, CV_BGR2GRAY);
|
||||||
|
|
||||||
|
// some colors will be empty
|
||||||
|
// fill histogram first and then pick those entries that are not empty
|
||||||
|
vector<vector<cv::Vec3f>> histogram(256);
|
||||||
|
|
||||||
|
for (i = 0; i < res.rows; i++) {
|
||||||
|
for (j = 0; j < res.cols; j++) {
|
||||||
|
idx = res.at<uchar>(i,j);
|
||||||
|
histogram[idx].insert(histogram[idx].end(),
|
||||||
|
extendedMap[i][j].begin(),
|
||||||
|
extendedMap[i][j].end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 256; i++) {
|
||||||
|
if (!histogram[i].empty()) {
|
||||||
|
segmented_points.push_back(histogram[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* calculate the adaptive threshold
|
||||||
|
*/
|
||||||
|
cv::Mat calculateAdaptiveThreshold(vector<vector<cv::Vec3f>> &segmented_points,
|
||||||
|
cv::Mat &img, vector<vector<vector<cv::Vec3f> > > extendedMap)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
cv::Mat res;
|
||||||
|
cv::adaptiveThreshold(img, res, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 49, 5);
|
||||||
|
segmented_points.resize(2);
|
||||||
|
|
||||||
|
for (i = 0; i < res.rows; i++) {
|
||||||
|
for (j = 0; j < res.cols; j++) {
|
||||||
|
idx = res.at<uchar>(i,j);
|
||||||
|
if (idx != 0)
|
||||||
|
idx = 1;
|
||||||
|
segmented_points[idx].insert(segmented_points[idx].end(),
|
||||||
|
extendedMap[i][j].begin(),
|
||||||
|
extendedMap[i][j].end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* using a marker image, calculate the watershed segmentation
|
||||||
|
* a marker image can be created from the panorama retrieved by using the
|
||||||
|
* --dump-pano option
|
||||||
|
*/
|
||||||
|
cv::Mat calculateWatershed(vector<vector<cv::Vec3f>> &segmented_points,
|
||||||
|
string &marker, cv::Mat &img, vector<vector<vector<cv::Vec3f> > > extendedMap)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
cv::Mat markerMask = cv::imread(marker, 0);
|
||||||
|
vector<vector<cv::Point> > contours;
|
||||||
|
vector<cv::Vec4i> hierarchy;
|
||||||
|
cv::findContours(markerMask, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
|
||||||
|
if (contours.empty())
|
||||||
|
throw std::runtime_error("empty marker mask");
|
||||||
|
cv::Mat markers(markerMask.size(), CV_32S);
|
||||||
|
markers = cv::Scalar::all(0);
|
||||||
|
int compCount = 0;
|
||||||
|
for (int idx = 0; idx >= 0; idx = hierarchy[idx][0], compCount++ )
|
||||||
|
cv::drawContours(markers, contours, idx,
|
||||||
|
cv::Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);
|
||||||
|
if (compCount == 0)
|
||||||
|
throw std::runtime_error("no component found");
|
||||||
|
cv::Mat imgGray;
|
||||||
|
cvtColor(img, imgGray, CV_GRAY2BGR);
|
||||||
|
cv::watershed(imgGray, markers);
|
||||||
|
|
||||||
|
segmented_points.resize(compCount);
|
||||||
|
|
||||||
|
for (i = 0; i < markers.rows; i++) {
|
||||||
|
for (j = 0; j < markers.cols; j++) {
|
||||||
|
idx = markers.at<int>(i,j);
|
||||||
|
if (idx > 0 && idx <= compCount) {
|
||||||
|
segmented_points[idx-1].insert(segmented_points[idx-1].end(),
|
||||||
|
extendedMap[i][j].begin(),
|
||||||
|
extendedMap[i][j].end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* given a vector of segmented 3d points, write them out as uos files
|
||||||
|
*/
|
||||||
|
void write3dfiles(vector<vector<cv::Vec3f>> &segmented_points, string &segdir)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
vector<ofstream*> outfiles(segmented_points.size());
|
||||||
|
for (i = 0; i < segmented_points.size(); i++) {
|
||||||
|
std::stringstream outfilename;
|
||||||
|
outfilename << segdir << "/scan" << std::setw(3) << std::setfill('0') << i << ".3d";
|
||||||
|
outfiles[i] = new ofstream(outfilename.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < segmented_points.size(); i++) {
|
||||||
|
for (vector<cv::Vec3f>::iterator it=segmented_points[i].begin() ;
|
||||||
|
it < segmented_points[i].end();
|
||||||
|
it++) {
|
||||||
|
(*(outfiles[i])) << (*it)[0] << " " << (*it)[1] << " " << (*it)[2] << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < segmented_points.size(); i++) {
|
||||||
|
outfiles[i]->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write .pose files
|
||||||
|
// .frames files can later be generated from them using ./bin/pose2frames
|
||||||
|
void writeposefiles(int num, string &segdir, const double* rPos, const double* rPosTheta)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
std::stringstream posefilename;
|
||||||
|
posefilename << segdir << "/scan" << std::setw(3) << std::setfill('0') << i << ".pose";
|
||||||
|
ofstream posefile(posefilename.str());
|
||||||
|
posefile << rPos[0] << " " << rPos[1] << " " << rPos[2] << endl;
|
||||||
|
posefile << deg(rPosTheta[0]) << " "
|
||||||
|
<< deg(rPosTheta[1]) << " "
|
||||||
|
<< deg(rPosTheta[2]) << endl;
|
||||||
|
posefile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void createdirectory(string segdir)
|
||||||
|
{
|
||||||
|
int success = mkdir(segdir.c_str(), S_IRWXU|S_IRWXG|S_IRWXO);
|
||||||
|
|
||||||
|
if (success == 0 || errno == EEXIST) {
|
||||||
|
cout << "Writing segmentations to " << segdir << endl;
|
||||||
|
} else {
|
||||||
|
cerr << "Creating directory " << segdir << " failed" << endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// commandline arguments
|
||||||
|
int start, end;
|
||||||
|
bool scanserver;
|
||||||
|
image_type itype;
|
||||||
|
int width, height;
|
||||||
|
int maxDist, minDist;
|
||||||
|
int nImages, pParam;
|
||||||
|
fbr::projection_method ptype;
|
||||||
|
bool logarithm;
|
||||||
|
float cutoff;
|
||||||
|
string dir;
|
||||||
|
IOType iotype;
|
||||||
|
segment_method stype;
|
||||||
|
string marker;
|
||||||
|
bool dump_pano, dump_seg;
|
||||||
|
double thresh;
|
||||||
|
int maxlevel, radius;
|
||||||
|
|
||||||
|
parse_options(argc, argv, start, end, scanserver, itype, width, height,
|
||||||
|
ptype, dir, iotype, maxDist, minDist, nImages, pParam, logarithm,
|
||||||
|
cutoff, stype, marker, dump_pano, dump_seg, thresh, maxlevel,
|
||||||
|
radius);
|
||||||
|
|
||||||
|
Scan::openDirectory(scanserver, dir, iotype, start, end);
|
||||||
|
|
||||||
|
if(Scan::allScans.size() == 0) {
|
||||||
|
cerr << "No scans found. Did you use the correct format?" << endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat img, res;
|
||||||
|
string segdir;
|
||||||
|
|
||||||
|
for(ScanVector::iterator it = Scan::allScans.begin(); it != Scan::allScans.end(); ++it) {
|
||||||
|
Scan* scan = *it;
|
||||||
|
|
||||||
|
// apply optional filtering
|
||||||
|
scan->setRangeFilter(maxDist, minDist);
|
||||||
|
|
||||||
|
// create target directory
|
||||||
|
segdir = dir + "segmented" + scan->getIdentifier();
|
||||||
|
createdirectory(segdir);
|
||||||
|
|
||||||
|
// create panorama
|
||||||
|
fbr::panorama fPanorama(width, height, ptype, nImages, pParam, fbr::EXTENDED);
|
||||||
|
fPanorama.createPanorama(scan2mat(scan));
|
||||||
|
|
||||||
|
if (itype == M_RANGE) {
|
||||||
|
// the range image has to be converted from float to uchar
|
||||||
|
img = fPanorama.getRangeImage();
|
||||||
|
img = float2uchar(img, logarithm, cutoff);
|
||||||
|
} else {
|
||||||
|
// intensity image
|
||||||
|
img = fPanorama.getReflectanceImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// output panorama image
|
||||||
|
if (dump_pano)
|
||||||
|
imwrite(segdir+"/panorama.png", img);
|
||||||
|
|
||||||
|
// will store the result of the segmentation
|
||||||
|
vector<vector<cv::Vec3f>> segmented_points;
|
||||||
|
|
||||||
|
if (stype == THRESHOLD) {
|
||||||
|
res = calculateThreshold(segmented_points, img, fPanorama.getExtendedMap(), thresh);
|
||||||
|
} else if (stype == PYR_MEAN_SHIFT) {
|
||||||
|
res = calculatePyrMeanShift(segmented_points, img, fPanorama.getExtendedMap(),
|
||||||
|
maxlevel, radius);
|
||||||
|
} else if (stype == ADAPTIVE_THRESHOLD) {
|
||||||
|
res = calculateAdaptiveThreshold(segmented_points, img, fPanorama.getExtendedMap());
|
||||||
|
} else if (stype == WATERSHED) {
|
||||||
|
res = calculateWatershed(segmented_points, marker, img, fPanorama.getExtendedMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// output segmentation image
|
||||||
|
if (dump_seg)
|
||||||
|
imwrite(segdir+"/segmentation.png", res);
|
||||||
|
|
||||||
|
// write .3d and .pose files
|
||||||
|
write3dfiles(segmented_points, segdir);
|
||||||
|
writeposefiles(segmented_points.size(), segdir, scan->get_rPos(), scan->get_rPosTheta());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
Project admins
|
||||||
|
|
||||||
|
Andreas Nuechter andreas@nuechti.de
|
||||||
|
Kai Lingemann kai.lingemann@gmx.de
|
||||||
|
Dorit Borrmann d.borrmann@jacobs-university.de
|
||||||
|
|
||||||
|
List of contributors
|
||||||
|
|
||||||
|
Andreas Nuechter andreas@nuechti.de
|
||||||
|
Kai Lingemann kai.lingemann@gmx.de
|
||||||
|
Dorit Borrmann d.borrmann@jacobs-university.de
|
||||||
|
Jan Elseberg j.elseberg@jacobs-university.de
|
||||||
|
Jochen Sprickerhof jochen@sprickerhof.de
|
||||||
|
HamidReza Houshiar h.houshiar@jacobs-university.de
|
||||||
|
Sven Albrecht sven.albrecht@uni-osnabrueck.de
|
||||||
|
Stan Serebryakov cfr.ssv@gmail.com
|
||||||
|
Thomas Escher tescher@uni-osnabrueck.de
|
||||||
|
Thomas Wiemann twiemann@uni-osnabrueck.de
|
||||||
|
Alexandru Tandrau alexandru@tandrau.com
|
||||||
|
Alexandru Eugen Ichim eugen@alexichim.com
|
||||||
|
Flavia Grosan me@flaviagrosan.com
|
||||||
|
Deyuan Qiu deyuan.qiu@googlemail.com
|
||||||
|
Darko Makreshanski d.makreshanski@jacobs-university.de
|
||||||
|
Mohammad Faisal Abdullah m.faisal@jacobs-university.de
|
||||||
|
Li Ming liming751218@whu.edu.cn
|
||||||
|
Li Wei xpaulee@gmail.com
|
||||||
|
Shams Feyzabadi sh.feyzabadi@gmail.co
|
||||||
|
Vladislav Perelmann v.perelman@jacobs-university.de
|
||||||
|
Chen Long lchen.whu@gmail.com
|
||||||
|
Remuas Dumitru r.dumitru@jaocbs-university.de
|
||||||
|
Billy Okal okal.billy@googlemail.com
|
||||||
|
Razvan-George Mihalyi r.mihalyi@jacobs-university.de
|
||||||
|
Johannes Schauer j.schauer@jacobs-university.de
|
||||||
|
|
||||||
|
Further contributors
|
||||||
|
|
||||||
|
Uwe Hebbelmann, Sebastian Stock, Andre Schemschat
|
||||||
|
Hartmut Surmann
|
||||||
|
Amuz Tamrakars, Ulugbek Makhmudov
|
||||||
|
Christof Soeger, Marcel Junker, Anton Fluegge, Hannes Schulz
|
|
@ -0,0 +1,53 @@
|
||||||
|
# CLIENT LIBRARY
|
||||||
|
|
||||||
|
# build by source
|
||||||
|
set(CLIENT_SRCS
|
||||||
|
clientInterface.cc sharedScan.cc cache/cacheObject.cc
|
||||||
|
cache/cacheDataAccess.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WITH_METRICS)
|
||||||
|
set(CLIENT_SRCS ${CLIENT_SRCS} ../slam6d/metrics.cc)
|
||||||
|
endif(WITH_METRICS)
|
||||||
|
|
||||||
|
add_library(scanclient STATIC ${CLIENT_SRCS})
|
||||||
|
|
||||||
|
# add libraries
|
||||||
|
# boost::interprocess
|
||||||
|
set(CLIENT_LIBS ${Boost_LIBRARIES} pointfilter)
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
# boost::interprocess uses pthread, requiring librt
|
||||||
|
#set(CLIENT_LIBS ${CLIENT_LIBS} rt)
|
||||||
|
endif(UNIX AND NOT APPLE)
|
||||||
|
|
||||||
|
target_link_libraries(scanclient ${CLIENT_LIBS})
|
||||||
|
|
||||||
|
# SERVER EXECUTABLE
|
||||||
|
|
||||||
|
# build by source
|
||||||
|
set(SERVER_SRCS
|
||||||
|
scanserver.cc serverInterface.cc frame_io.cc serverScan.cc
|
||||||
|
cache/cacheManager.cc cache/cacheHandler.cc scanHandler.cc
|
||||||
|
temporaryHandler.cc cacheIO.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(scanserver ${SERVER_SRCS})
|
||||||
|
|
||||||
|
# add libraries
|
||||||
|
# boost::interprocess/filesystem
|
||||||
|
# scanclient basic functionality
|
||||||
|
# scanio for ScanHandler input
|
||||||
|
set(SERVER_LIBS ${Boost_LIBRARIES} scanclient scanio)
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
# boost::interprocess uses pthread, requiring librt
|
||||||
|
set(SERVER_LIBS ${SERVER_LIBS} rt)
|
||||||
|
endif(UNIX)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
# 3rd party getopt library
|
||||||
|
set(SERVER_LIBS ${SERVER_LIBS} XGetopt)
|
||||||
|
endif(WIN32)
|
||||||
|
|
||||||
|
target_link_libraries(scanserver ${SERVER_LIBS})
|
BIN
.svn/wc.db
BIN
.svn/wc.db
Binary file not shown.
|
@ -164,7 +164,16 @@ ELSE(WITH_TOOLS)
|
||||||
MESSAGE(STATUS "Without Tools")
|
MESSAGE(STATUS "Without Tools")
|
||||||
ENDIF(WITH_TOOLS)
|
ENDIF(WITH_TOOLS)
|
||||||
|
|
||||||
|
## Segmentation
|
||||||
|
|
||||||
|
OPTION(WITH_SEGMENTATION "Whether to build scan segmantion program ON/OFF" OFF)
|
||||||
|
|
||||||
|
IF(WITH_SEGMENTATION)
|
||||||
|
MESSAGE(STATUS "With segmentation")
|
||||||
|
find_package (Boost COMPONENTS program_options REQUIRED)
|
||||||
|
ELSE(WITH_SEGMENTATION)
|
||||||
|
MESSAGE(STATUS "Without segmentation")
|
||||||
|
ENDIF(WITH_SEGMENTATION)
|
||||||
|
|
||||||
## CAD matching
|
## CAD matching
|
||||||
OPTION (WITH_CAD "Wether to build with CAD import lib ON/OFF" OFF)
|
OPTION (WITH_CAD "Wether to build with CAD import lib ON/OFF" OFF)
|
||||||
|
@ -370,6 +379,7 @@ add_subdirectory(3rdparty)
|
||||||
add_subdirectory(src/slam6d)
|
add_subdirectory(src/slam6d)
|
||||||
add_subdirectory(src/scanio)
|
add_subdirectory(src/scanio)
|
||||||
add_subdirectory(src/scanserver)
|
add_subdirectory(src/scanserver)
|
||||||
|
add_subdirectory(src/segmentation)
|
||||||
add_subdirectory(src/veloslam)
|
add_subdirectory(src/veloslam)
|
||||||
add_subdirectory(src/show)
|
add_subdirectory(src/show)
|
||||||
add_subdirectory(src/grid)
|
add_subdirectory(src/grid)
|
||||||
|
|
|
@ -29,6 +29,8 @@ Vladislav Perelmann v.perelman@jacobs-university.de
|
||||||
Chen Long lchen.whu@gmail.com
|
Chen Long lchen.whu@gmail.com
|
||||||
Remuas Dumitru r.dumitru@jaocbs-university.de
|
Remuas Dumitru r.dumitru@jaocbs-university.de
|
||||||
Billy Okal okal.billy@googlemail.com
|
Billy Okal okal.billy@googlemail.com
|
||||||
|
Razvan-George Mihalyi r.mihalyi@jacobs-university.de
|
||||||
|
Johannes Schauer j.schauer@jacobs-university.de
|
||||||
|
|
||||||
Further contributors
|
Further contributors
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ set(SERVER_LIBS ${Boost_LIBRARIES} scanclient scanio)
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
# boost::interprocess uses pthread, requiring librt
|
# boost::interprocess uses pthread, requiring librt
|
||||||
#set(SERVER_LIBS ${SERVER_LIBS} rt)
|
set(SERVER_LIBS ${SERVER_LIBS} rt)
|
||||||
endif(UNIX)
|
endif(UNIX)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
|
|
7
src/segmentation/CMakeLists.txt
Normal file
7
src/segmentation/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
IF(WITH_SEGMENTATION)
|
||||||
|
add_executable(scan2segments scan2segments.cc)
|
||||||
|
|
||||||
|
FIND_PACKAGE(OpenCV REQUIRED)
|
||||||
|
|
||||||
|
target_link_libraries(scan2segments scan ANN fbr_cv_io fbr_panorama fbr_feature fbr_feature_matcher fbr_registration ${Boost_LIBRARIES} ${OpenCV_LIBS})
|
||||||
|
ENDIF(WITH_SEGMENTATION)
|
640
src/segmentation/scan2segments.cc
Normal file
640
src/segmentation/scan2segments.cc
Normal file
|
@ -0,0 +1,640 @@
|
||||||
|
/*
|
||||||
|
* scan_red implementation
|
||||||
|
*
|
||||||
|
* Copyright (C) Johannes Schauer
|
||||||
|
*
|
||||||
|
* Released under the GPL version 3.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "slam6d/scan.h"
|
||||||
|
#include "slam6d/globals.icc"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
using std::cout;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
#include "slam6d/fbr/panorama.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
enum image_type {M_RANGE, M_INTENSITY};
|
||||||
|
|
||||||
|
enum segment_method {THRESHOLD, ADAPTIVE_THRESHOLD, PYR_MEAN_SHIFT, WATERSHED};
|
||||||
|
|
||||||
|
/* Function used to check that 'opt1' and 'opt2' are not specified
|
||||||
|
at the same time. */
|
||||||
|
void conflicting_options(const po::variables_map & vm,
|
||||||
|
const char *opt1, const char *opt2)
|
||||||
|
{
|
||||||
|
if (vm.count(opt1) && !vm[opt1].defaulted()
|
||||||
|
&& vm.count(opt2) && !vm[opt2].defaulted())
|
||||||
|
throw std::logic_error(string("Conflicting options '")
|
||||||
|
+ opt1 + "' and '" + opt2 + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function used to check that if 'for_what' is specified, then
|
||||||
|
'required_option' is specified too. */
|
||||||
|
void option_dependency(const po::variables_map & vm,
|
||||||
|
const char *for_what, const char *required_option)
|
||||||
|
{
|
||||||
|
if (vm.count(for_what) && !vm[for_what].defaulted())
|
||||||
|
if (vm.count(required_option) == 0
|
||||||
|
|| vm[required_option].defaulted())
|
||||||
|
throw std::logic_error(string("Option '") + for_what +
|
||||||
|
"' requires option '" +
|
||||||
|
required_option + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* validates panorama method specification
|
||||||
|
*/
|
||||||
|
namespace fbr {
|
||||||
|
void validate(boost::any& v, const std::vector<std::string>& values,
|
||||||
|
projection_method*, int) {
|
||||||
|
if (values.size() == 0)
|
||||||
|
throw std::runtime_error("Invalid model specification");
|
||||||
|
string arg = values.at(0);
|
||||||
|
if(strcasecmp(arg.c_str(), "EQUIRECTANGULAR") == 0) v = EQUIRECTANGULAR;
|
||||||
|
else if(strcasecmp(arg.c_str(), "CYLINDRICAL") == 0) v = CYLINDRICAL;
|
||||||
|
else if(strcasecmp(arg.c_str(), "MERCATOR") == 0) v = MERCATOR;
|
||||||
|
else if(strcasecmp(arg.c_str(), "RECTILINEAR") == 0) v = RECTILINEAR;
|
||||||
|
else if(strcasecmp(arg.c_str(), "PANNINI") == 0) v = PANNINI;
|
||||||
|
else if(strcasecmp(arg.c_str(), "STEREOGRAPHIC") == 0) v = STEREOGRAPHIC;
|
||||||
|
else if(strcasecmp(arg.c_str(), "ZAXIS") == 0) v = ZAXIS;
|
||||||
|
else if(strcasecmp(arg.c_str(), "CONIC") == 0) v = CONIC;
|
||||||
|
else throw std::runtime_error(std::string("projection method ") + arg + std::string(" is unknown"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* validates segmentation method specification
|
||||||
|
*/
|
||||||
|
void validate(boost::any& v, const std::vector<std::string>& values,
|
||||||
|
segment_method*, int) {
|
||||||
|
if (values.size() == 0)
|
||||||
|
throw std::runtime_error("Invalid model specification");
|
||||||
|
string arg = values.at(0);
|
||||||
|
if(strcasecmp(arg.c_str(), "THRESHOLD") == 0) v = THRESHOLD;
|
||||||
|
else if(strcasecmp(arg.c_str(), "ADAPTIVE_THRESHOLD") == 0) v = ADAPTIVE_THRESHOLD;
|
||||||
|
else if(strcasecmp(arg.c_str(), "PYR_MEAN_SHIFT") == 0) v = PYR_MEAN_SHIFT;
|
||||||
|
else if(strcasecmp(arg.c_str(), "WATERSHED") == 0) v = WATERSHED;
|
||||||
|
else throw std::runtime_error(std::string("segmentation method ") + arg + std::string(" is unknown"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* validates input type specification
|
||||||
|
*/
|
||||||
|
void validate(boost::any& v, const std::vector<std::string>& values,
|
||||||
|
IOType*, int) {
|
||||||
|
if (values.size() == 0)
|
||||||
|
throw std::runtime_error("Invalid model specification");
|
||||||
|
string arg = values.at(0);
|
||||||
|
try {
|
||||||
|
v = formatname_to_io_type(arg.c_str());
|
||||||
|
} catch (...) { // runtime_error
|
||||||
|
throw std::runtime_error("Format " + arg + " unknown.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* parse commandline options, fill arguments
|
||||||
|
*/
|
||||||
|
void parse_options(int argc, char **argv, int &start, int &end,
|
||||||
|
bool &scanserver, image_type &itype, int &width, int &height,
|
||||||
|
fbr::projection_method &ptype, string &dir, IOType &iotype,
|
||||||
|
int &maxDist, int &minDist, int &nImages, int &pParam, bool &logarithm,
|
||||||
|
float &cutoff, segment_method &stype, string &marker, bool &dump_pano,
|
||||||
|
bool &dump_seg, double &thresh, int &maxlevel, int &radius)
|
||||||
|
{
|
||||||
|
po::options_description generic("Generic options");
|
||||||
|
generic.add_options()
|
||||||
|
("help,h", "output this help message");
|
||||||
|
|
||||||
|
po::options_description input("Input options");
|
||||||
|
input.add_options()
|
||||||
|
("start,s", po::value<int>(&start)->default_value(0),
|
||||||
|
"start at scan <arg> (i.e., neglects the first <arg> scans) "
|
||||||
|
"[ATTENTION: counting naturally starts with 0]")
|
||||||
|
("end,e", po::value<int>(&end)->default_value(-1),
|
||||||
|
"end after scan <arg>")
|
||||||
|
("format,f", po::value<IOType>(&iotype)->default_value(UOS),
|
||||||
|
"using shared library <arg> for input. (chose F from {uos, uos_map, "
|
||||||
|
"uos_rgb, uos_frames, uos_map_frames, old, rts, rts_map, ifp, "
|
||||||
|
"riegl_txt, riegl_rgb, riegl_bin, zahn, ply})")
|
||||||
|
("max,M", po::value<int>(&maxDist)->default_value(-1),
|
||||||
|
"neglegt all data points with a distance larger than <arg> 'units")
|
||||||
|
("min,m", po::value<int>(&minDist)->default_value(-1),
|
||||||
|
"neglegt all data points with a distance smaller than <arg> 'units")
|
||||||
|
("scanserver,S", po::value<bool>(&scanserver)->default_value(false),
|
||||||
|
"Use the scanserver as an input method and handling of scan data");
|
||||||
|
|
||||||
|
po::options_description image("Panorama image options");
|
||||||
|
image.add_options()
|
||||||
|
("range,r", "create range image")
|
||||||
|
("intensity,i", "create intensity image")
|
||||||
|
("width,w", po::value<int>(&width)->default_value(1280),
|
||||||
|
"width of panorama")
|
||||||
|
("height,h", po::value<int>(&height)->default_value(960),
|
||||||
|
"height of panorama")
|
||||||
|
("panorama,p", po::value<fbr::projection_method>(&ptype)->
|
||||||
|
default_value(fbr::EQUIRECTANGULAR), "panorama type (EQUIRECTANGULAR, "
|
||||||
|
"CYLINDRICAL, MERCATOR, RECTILINEAR, PANNINI, STEREOGRAPHIC, ZAXIS, "
|
||||||
|
"CONIC)")
|
||||||
|
("num-images,N", po::value<int>(&nImages)->default_value(1),
|
||||||
|
"number of images used for some projections")
|
||||||
|
("proj-param,P", po::value<int>(&pParam)->default_value(0),
|
||||||
|
"special projection parameter")
|
||||||
|
("dump-pano,d", po::bool_switch(&dump_pano),
|
||||||
|
"output panorama (useful to create marker image for watershed)");
|
||||||
|
|
||||||
|
po::options_description range_image("Range image options");
|
||||||
|
range_image.add_options()
|
||||||
|
("logarithm,L", po::bool_switch(&logarithm),
|
||||||
|
"use the logarithm for range image panoramas")
|
||||||
|
("cutoff,C", po::value<float>(&cutoff)->default_value(0.0), // FIXME: percentage is the wrong word
|
||||||
|
"percentage of furthest away data points to cut off to improve "
|
||||||
|
"precision for closer values (values from 0.0 to 1.0)");
|
||||||
|
|
||||||
|
po::options_description segment("Segmentation options");
|
||||||
|
segment.add_options()
|
||||||
|
("segment,g", po::value<segment_method>(&stype)->
|
||||||
|
default_value(PYR_MEAN_SHIFT), "segmentation method (THRESHOLD, "
|
||||||
|
"ADAPTIVE_THRESHOLD, PYR_MEAN_SHIFT, WATERSHED)")
|
||||||
|
("marker,K", po::value<string>(&marker),
|
||||||
|
"marker mask for watershed segmentation")
|
||||||
|
("thresh,T", po::value<double>(&thresh),
|
||||||
|
"threshold for threshold segmentation")
|
||||||
|
("maxlevel,X", po::value<int>(&maxlevel),
|
||||||
|
"maximum level for meanshift segmentation")
|
||||||
|
("radius,R", po::value<int>(&radius),
|
||||||
|
"radius for meanshift segmentation")
|
||||||
|
("dump-seg,D", po::bool_switch(&dump_seg),
|
||||||
|
"output segmentation image (for debugging)");
|
||||||
|
|
||||||
|
po::options_description hidden("Hidden options");
|
||||||
|
hidden.add_options()
|
||||||
|
("input-dir", po::value<string>(&dir), "input dir");
|
||||||
|
|
||||||
|
// all options
|
||||||
|
po::options_description all;
|
||||||
|
all.add(generic).add(input).add(image).add(range_image).add(segment).add(hidden);
|
||||||
|
|
||||||
|
// options visible with --help
|
||||||
|
po::options_description cmdline_options;
|
||||||
|
cmdline_options.add(generic).add(input).add(image).add(range_image).add(segment);
|
||||||
|
|
||||||
|
// positional argument
|
||||||
|
po::positional_options_description pd;
|
||||||
|
pd.add("input-dir", 1);
|
||||||
|
|
||||||
|
// process options
|
||||||
|
po::variables_map vm;
|
||||||
|
po::store(po::command_line_parser(argc, argv).
|
||||||
|
options(all).positional(pd).run(), vm);
|
||||||
|
po::notify(vm);
|
||||||
|
|
||||||
|
// display help
|
||||||
|
if (vm.count("help")) {
|
||||||
|
cout << cmdline_options;
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// option conflicts and dependencies
|
||||||
|
conflicting_options(vm, "range", "intensity");
|
||||||
|
option_dependency(vm, "logarithm", "range");
|
||||||
|
option_dependency(vm, "cutoff", "range");
|
||||||
|
|
||||||
|
// decide between range and intensity panorama
|
||||||
|
if (vm.count("range"))
|
||||||
|
itype = M_RANGE;
|
||||||
|
else
|
||||||
|
itype = M_INTENSITY;
|
||||||
|
|
||||||
|
// option dependencies on segmentation types
|
||||||
|
if (stype == WATERSHED) {
|
||||||
|
if (!vm.count("marker"))
|
||||||
|
throw std::logic_error("watershed segmentation requires --marker to be set");
|
||||||
|
if (vm.count("thresh"))
|
||||||
|
throw std::logic_error("watershed segmentation cannot be used with --thresh");
|
||||||
|
if (vm.count("maxlevel"))
|
||||||
|
throw std::logic_error("watershed segmentation cannot be used with --maxlevel");
|
||||||
|
if (vm.count("radius"))
|
||||||
|
throw std::logic_error("watershed segmentation cannot be used with --radius");
|
||||||
|
}
|
||||||
|
if (stype == THRESHOLD) {
|
||||||
|
if (!vm.count("thresh"))
|
||||||
|
throw std::logic_error("threshold segmentation requires --thresh to be set");
|
||||||
|
if (vm.count("marker"))
|
||||||
|
throw std::logic_error("threshold segmentation cannot be used with --marker");
|
||||||
|
if (vm.count("maxlevel"))
|
||||||
|
throw std::logic_error("threshold segmentation cannot be used with --maxlevel");
|
||||||
|
if (vm.count("radius"))
|
||||||
|
throw std::logic_error("threshold segmentation cannot be used with --radius");
|
||||||
|
}
|
||||||
|
if (stype == PYR_MEAN_SHIFT) {
|
||||||
|
if (!vm.count("maxlevel"))
|
||||||
|
throw std::logic_error("mean shift segmentation requires --maxlevel to be set");
|
||||||
|
if (!vm.count("radius"))
|
||||||
|
throw std::logic_error("mean shift segmentation requires --radius to be set");
|
||||||
|
if (vm.count("thresh"))
|
||||||
|
throw std::logic_error("mean shift segmentation cannot be used with --thresh");
|
||||||
|
if (vm.count("marker"))
|
||||||
|
throw std::logic_error("mean shift segmentation cannot be used with --marker");
|
||||||
|
}
|
||||||
|
|
||||||
|
// correct pParam and nImages for certain panorama types
|
||||||
|
if (ptype == fbr::PANNINI && pParam == 0) {
|
||||||
|
pParam = 1;
|
||||||
|
if(nImages < 3) nImages = 3;
|
||||||
|
}
|
||||||
|
if (ptype == fbr::STEREOGRAPHIC && pParam == 0) {
|
||||||
|
pParam = 2;
|
||||||
|
if(nImages < 3) nImages = 3;
|
||||||
|
}
|
||||||
|
if (ptype == fbr::RECTILINEAR && nImages < 3) {
|
||||||
|
nImages = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add trailing slash to directory if not present yet
|
||||||
|
if (dir[dir.length()-1] != '/') dir = dir + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* retrieve a cv::Mat with x,y,z,r from a scan object
|
||||||
|
* functionality borrowed from scan_cv::convertScanToMat but this function
|
||||||
|
* does not allow a scanserver to be used, prints to stdout and can only
|
||||||
|
* handle a single scan
|
||||||
|
*/
|
||||||
|
cv::Mat scan2mat(Scan *source)
|
||||||
|
{
|
||||||
|
DataXYZ xyz = source->get("xyz");
|
||||||
|
DataReflectance xyz_reflectance = source->get("reflectance");
|
||||||
|
unsigned int nPoints = xyz.size();
|
||||||
|
cv::Mat scan(nPoints,1,CV_32FC(4));
|
||||||
|
scan = cv::Scalar::all(0);
|
||||||
|
cv::MatIterator_<cv::Vec4f> it;
|
||||||
|
it = scan.begin<cv::Vec4f>();
|
||||||
|
for(unsigned int i = 0; i < nPoints; i++){
|
||||||
|
float x, y, z, reflectance;
|
||||||
|
x = xyz[i][0];
|
||||||
|
y = xyz[i][1];
|
||||||
|
z = xyz[i][2];
|
||||||
|
reflectance = xyz_reflectance[i];
|
||||||
|
|
||||||
|
//normalize the reflectance
|
||||||
|
reflectance += 32;
|
||||||
|
reflectance /= 64;
|
||||||
|
reflectance -= 0.2;
|
||||||
|
reflectance /= 0.3;
|
||||||
|
if (reflectance < 0) reflectance = 0;
|
||||||
|
if (reflectance > 1) reflectance = 1;
|
||||||
|
|
||||||
|
(*it)[0] = x;
|
||||||
|
(*it)[1] = y;
|
||||||
|
(*it)[2] = z;
|
||||||
|
(*it)[3] = reflectance;
|
||||||
|
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* convert a matrix of float values (range image) to a matrix of unsigned
|
||||||
|
* eight bit characters using different techniques
|
||||||
|
*/
|
||||||
|
cv::Mat float2uchar(cv::Mat &source, bool logarithm, float cutoff)
|
||||||
|
{
|
||||||
|
cv::Mat result(source.size(), CV_8U, cv::Scalar::all(0));
|
||||||
|
float max = 0;
|
||||||
|
// find maximum value
|
||||||
|
if (cutoff == 0.0) {
|
||||||
|
// without cutoff, just iterate through all values to find the largest
|
||||||
|
for (cv::MatIterator_<float> it = source.begin<float>();
|
||||||
|
it != source.end<float>(); ++it) {
|
||||||
|
float val = *it;
|
||||||
|
if (val > max) {
|
||||||
|
max = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// when a cutoff is specified, sort all the points by value and then
|
||||||
|
// specify the max so that <cutoff> values are larger than it
|
||||||
|
vector<float> sorted(source.cols*source.rows);
|
||||||
|
int i = 0;
|
||||||
|
for (cv::MatIterator_<float> it = source.begin<float>();
|
||||||
|
it != source.end<float>(); ++it, ++i) {
|
||||||
|
sorted[i] = *it;
|
||||||
|
}
|
||||||
|
std::sort(sorted.begin(), sorted.end());
|
||||||
|
max = sorted[(int)(source.cols*source.rows*(1.0-cutoff))];
|
||||||
|
cout << "A cutoff of " << cutoff << " resulted in a max value of " << max << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::MatIterator_<float> src = source.begin<float>();
|
||||||
|
cv::MatIterator_<uchar> dst = result.begin<uchar>();
|
||||||
|
cv::MatIterator_<float> end = source.end<float>();
|
||||||
|
if (logarithm) {
|
||||||
|
// stretch values from 0 to max logarithmically over 0 to 255
|
||||||
|
// using the logarithm allows to represent smaller values with more
|
||||||
|
// precision and larger values with less
|
||||||
|
max = log(max+1);
|
||||||
|
for (; src != end; ++src, ++dst) {
|
||||||
|
float val = (log(*src+1)*255.0)/max;
|
||||||
|
if (val > 255)
|
||||||
|
*dst = 255;
|
||||||
|
else
|
||||||
|
*dst = (uchar)val;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// stretch values from 0 to max linearly over 0 to 255
|
||||||
|
for (; src != end; ++src, ++dst) {
|
||||||
|
float val = (*src*255.0)/max;
|
||||||
|
if (val > 255)
|
||||||
|
*dst = 255;
|
||||||
|
else
|
||||||
|
*dst = (uchar)val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* from grayscale image, create a binary image using a fixed threshold
|
||||||
|
*/
|
||||||
|
cv::Mat calculateThreshold(vector<vector<cv::Vec3f>> &segmented_points,
|
||||||
|
cv::Mat &img, vector<vector<vector<cv::Vec3f> > > extendedMap,
|
||||||
|
double thresh)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
cv::Mat res;
|
||||||
|
cv::threshold(img, res, thresh, 255, cv::THRESH_BINARY);
|
||||||
|
segmented_points.resize(2);
|
||||||
|
|
||||||
|
for (i = 0; i < res.rows; i++) {
|
||||||
|
for (j = 0; j < res.cols; j++) {
|
||||||
|
idx = res.at<uchar>(i,j);
|
||||||
|
if (idx != 0)
|
||||||
|
idx = 1;
|
||||||
|
segmented_points[idx].insert(segmented_points[idx].end(),
|
||||||
|
extendedMap[i][j].begin(),
|
||||||
|
extendedMap[i][j].end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* calculate the pyramid mean shift segmentation of the image
|
||||||
|
*/
|
||||||
|
cv::Mat calculatePyrMeanShift(vector<vector<cv::Vec3f>> &segmented_points,
|
||||||
|
cv::Mat &img, vector<vector<vector<cv::Vec3f> > > extendedMap,
|
||||||
|
int maxlevel, int radius)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
cv::Mat imgGray, res, tmp;
|
||||||
|
cvtColor(img, imgGray, CV_GRAY2BGR);
|
||||||
|
cv::pyrMeanShiftFiltering(imgGray, tmp, radius, radius, maxlevel);
|
||||||
|
cvtColor(tmp, res, CV_BGR2GRAY);
|
||||||
|
|
||||||
|
// some colors will be empty
|
||||||
|
// fill histogram first and then pick those entries that are not empty
|
||||||
|
vector<vector<cv::Vec3f>> histogram(256);
|
||||||
|
|
||||||
|
for (i = 0; i < res.rows; i++) {
|
||||||
|
for (j = 0; j < res.cols; j++) {
|
||||||
|
idx = res.at<uchar>(i,j);
|
||||||
|
histogram[idx].insert(histogram[idx].end(),
|
||||||
|
extendedMap[i][j].begin(),
|
||||||
|
extendedMap[i][j].end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 256; i++) {
|
||||||
|
if (!histogram[i].empty()) {
|
||||||
|
segmented_points.push_back(histogram[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* calculate the adaptive threshold
|
||||||
|
*/
|
||||||
|
cv::Mat calculateAdaptiveThreshold(vector<vector<cv::Vec3f>> &segmented_points,
|
||||||
|
cv::Mat &img, vector<vector<vector<cv::Vec3f> > > extendedMap)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
cv::Mat res;
|
||||||
|
cv::adaptiveThreshold(img, res, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 49, 5);
|
||||||
|
segmented_points.resize(2);
|
||||||
|
|
||||||
|
for (i = 0; i < res.rows; i++) {
|
||||||
|
for (j = 0; j < res.cols; j++) {
|
||||||
|
idx = res.at<uchar>(i,j);
|
||||||
|
if (idx != 0)
|
||||||
|
idx = 1;
|
||||||
|
segmented_points[idx].insert(segmented_points[idx].end(),
|
||||||
|
extendedMap[i][j].begin(),
|
||||||
|
extendedMap[i][j].end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* using a marker image, calculate the watershed segmentation
|
||||||
|
* a marker image can be created from the panorama retrieved by using the
|
||||||
|
* --dump-pano option
|
||||||
|
*/
|
||||||
|
cv::Mat calculateWatershed(vector<vector<cv::Vec3f>> &segmented_points,
|
||||||
|
string &marker, cv::Mat &img, vector<vector<vector<cv::Vec3f> > > extendedMap)
|
||||||
|
{
|
||||||
|
int i, j, idx;
|
||||||
|
cv::Mat markerMask = cv::imread(marker, 0);
|
||||||
|
vector<vector<cv::Point> > contours;
|
||||||
|
vector<cv::Vec4i> hierarchy;
|
||||||
|
cv::findContours(markerMask, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
|
||||||
|
if (contours.empty())
|
||||||
|
throw std::runtime_error("empty marker mask");
|
||||||
|
cv::Mat markers(markerMask.size(), CV_32S);
|
||||||
|
markers = cv::Scalar::all(0);
|
||||||
|
int compCount = 0;
|
||||||
|
for (int idx = 0; idx >= 0; idx = hierarchy[idx][0], compCount++ )
|
||||||
|
cv::drawContours(markers, contours, idx,
|
||||||
|
cv::Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);
|
||||||
|
if (compCount == 0)
|
||||||
|
throw std::runtime_error("no component found");
|
||||||
|
cv::Mat imgGray;
|
||||||
|
cvtColor(img, imgGray, CV_GRAY2BGR);
|
||||||
|
cv::watershed(imgGray, markers);
|
||||||
|
|
||||||
|
segmented_points.resize(compCount);
|
||||||
|
|
||||||
|
for (i = 0; i < markers.rows; i++) {
|
||||||
|
for (j = 0; j < markers.cols; j++) {
|
||||||
|
idx = markers.at<int>(i,j);
|
||||||
|
if (idx > 0 && idx <= compCount) {
|
||||||
|
segmented_points[idx-1].insert(segmented_points[idx-1].end(),
|
||||||
|
extendedMap[i][j].begin(),
|
||||||
|
extendedMap[i][j].end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* given a vector of segmented 3d points, write them out as uos files
|
||||||
|
*/
|
||||||
|
void write3dfiles(vector<vector<cv::Vec3f>> &segmented_points, string &segdir)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
vector<ofstream*> outfiles(segmented_points.size());
|
||||||
|
for (i = 0; i < segmented_points.size(); i++) {
|
||||||
|
std::stringstream outfilename;
|
||||||
|
outfilename << segdir << "/scan" << std::setw(3) << std::setfill('0') << i << ".3d";
|
||||||
|
outfiles[i] = new ofstream(outfilename.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < segmented_points.size(); i++) {
|
||||||
|
for (vector<cv::Vec3f>::iterator it=segmented_points[i].begin() ;
|
||||||
|
it < segmented_points[i].end();
|
||||||
|
it++) {
|
||||||
|
(*(outfiles[i])) << (*it)[0] << " " << (*it)[1] << " " << (*it)[2] << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < segmented_points.size(); i++) {
|
||||||
|
outfiles[i]->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write .pose files
|
||||||
|
// .frames files can later be generated from them using ./bin/pose2frames
|
||||||
|
void writeposefiles(int num, string &segdir, const double* rPos, const double* rPosTheta)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
std::stringstream posefilename;
|
||||||
|
posefilename << segdir << "/scan" << std::setw(3) << std::setfill('0') << i << ".pose";
|
||||||
|
ofstream posefile(posefilename.str());
|
||||||
|
posefile << rPos[0] << " " << rPos[1] << " " << rPos[2] << endl;
|
||||||
|
posefile << deg(rPosTheta[0]) << " "
|
||||||
|
<< deg(rPosTheta[1]) << " "
|
||||||
|
<< deg(rPosTheta[2]) << endl;
|
||||||
|
posefile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void createdirectory(string segdir)
|
||||||
|
{
|
||||||
|
int success = mkdir(segdir.c_str(), S_IRWXU|S_IRWXG|S_IRWXO);
|
||||||
|
|
||||||
|
if (success == 0 || errno == EEXIST) {
|
||||||
|
cout << "Writing segmentations to " << segdir << endl;
|
||||||
|
} else {
|
||||||
|
cerr << "Creating directory " << segdir << " failed" << endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// commandline arguments
|
||||||
|
int start, end;
|
||||||
|
bool scanserver;
|
||||||
|
image_type itype;
|
||||||
|
int width, height;
|
||||||
|
int maxDist, minDist;
|
||||||
|
int nImages, pParam;
|
||||||
|
fbr::projection_method ptype;
|
||||||
|
bool logarithm;
|
||||||
|
float cutoff;
|
||||||
|
string dir;
|
||||||
|
IOType iotype;
|
||||||
|
segment_method stype;
|
||||||
|
string marker;
|
||||||
|
bool dump_pano, dump_seg;
|
||||||
|
double thresh;
|
||||||
|
int maxlevel, radius;
|
||||||
|
|
||||||
|
parse_options(argc, argv, start, end, scanserver, itype, width, height,
|
||||||
|
ptype, dir, iotype, maxDist, minDist, nImages, pParam, logarithm,
|
||||||
|
cutoff, stype, marker, dump_pano, dump_seg, thresh, maxlevel,
|
||||||
|
radius);
|
||||||
|
|
||||||
|
Scan::openDirectory(scanserver, dir, iotype, start, end);
|
||||||
|
|
||||||
|
if(Scan::allScans.size() == 0) {
|
||||||
|
cerr << "No scans found. Did you use the correct format?" << endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat img, res;
|
||||||
|
string segdir;
|
||||||
|
|
||||||
|
for(ScanVector::iterator it = Scan::allScans.begin(); it != Scan::allScans.end(); ++it) {
|
||||||
|
Scan* scan = *it;
|
||||||
|
|
||||||
|
// apply optional filtering
|
||||||
|
scan->setRangeFilter(maxDist, minDist);
|
||||||
|
|
||||||
|
// create target directory
|
||||||
|
segdir = dir + "segmented" + scan->getIdentifier();
|
||||||
|
createdirectory(segdir);
|
||||||
|
|
||||||
|
// create panorama
|
||||||
|
fbr::panorama fPanorama(width, height, ptype, nImages, pParam, fbr::EXTENDED);
|
||||||
|
fPanorama.createPanorama(scan2mat(scan));
|
||||||
|
|
||||||
|
if (itype == M_RANGE) {
|
||||||
|
// the range image has to be converted from float to uchar
|
||||||
|
img = fPanorama.getRangeImage();
|
||||||
|
img = float2uchar(img, logarithm, cutoff);
|
||||||
|
} else {
|
||||||
|
// intensity image
|
||||||
|
img = fPanorama.getReflectanceImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// output panorama image
|
||||||
|
if (dump_pano)
|
||||||
|
imwrite(segdir+"/panorama.png", img);
|
||||||
|
|
||||||
|
// will store the result of the segmentation
|
||||||
|
vector<vector<cv::Vec3f>> segmented_points;
|
||||||
|
|
||||||
|
if (stype == THRESHOLD) {
|
||||||
|
res = calculateThreshold(segmented_points, img, fPanorama.getExtendedMap(), thresh);
|
||||||
|
} else if (stype == PYR_MEAN_SHIFT) {
|
||||||
|
res = calculatePyrMeanShift(segmented_points, img, fPanorama.getExtendedMap(),
|
||||||
|
maxlevel, radius);
|
||||||
|
} else if (stype == ADAPTIVE_THRESHOLD) {
|
||||||
|
res = calculateAdaptiveThreshold(segmented_points, img, fPanorama.getExtendedMap());
|
||||||
|
} else if (stype == WATERSHED) {
|
||||||
|
res = calculateWatershed(segmented_points, marker, img, fPanorama.getExtendedMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// output segmentation image
|
||||||
|
if (dump_seg)
|
||||||
|
imwrite(segdir+"/segmentation.png", res);
|
||||||
|
|
||||||
|
// write .3d and .pose files
|
||||||
|
write3dfiles(segmented_points, segdir);
|
||||||
|
writeposefiles(segmented_points.size(), segdir, scan->get_rPos(), scan->get_rPosTheta());
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ SET(FBR_IO_SRC scan_cv.cc)
|
||||||
add_library(fbr_cv_io STATIC ${FBR_IO_SRC})
|
add_library(fbr_cv_io STATIC ${FBR_IO_SRC})
|
||||||
|
|
||||||
SET(FBR_PANORAMA_SRC panorama.cc)
|
SET(FBR_PANORAMA_SRC panorama.cc)
|
||||||
add_library(fbr_panorama STATIC ${FBR_PANORAMA_SRC})
|
add_library(fbr_panorama STATIC ${FBR_PANORAMA_SRC} fbr_global.cc)
|
||||||
|
|
||||||
SET(FBR_FEATURE_SRC feature.cc)
|
SET(FBR_FEATURE_SRC feature.cc)
|
||||||
add_library(fbr_feature STATIC ${FBR_FEATURE_SRC})
|
add_library(fbr_feature STATIC ${FBR_FEATURE_SRC})
|
||||||
|
|
Loading…
Reference in a new issue