diff --git a/.svn/pristine/02/02a9849daa7cb9c4a9c689c60a01d0d0973ba683.svn-base b/.svn/pristine/02/02a9849daa7cb9c4a9c689c60a01d0d0973ba683.svn-base new file mode 100644 index 0000000..dc6b993 --- /dev/null +++ b/.svn/pristine/02/02a9849daa7cb9c4a9c689c60a01d0d0973ba683.svn-base @@ -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) diff --git a/.svn/pristine/10/1030b86587f750dc525d03b394a0c558d9d62919.svn-base b/.svn/pristine/10/1030b86587f750dc525d03b394a0c558d9d62919.svn-base new file mode 100644 index 0000000..e6f5206 --- /dev/null +++ b/.svn/pristine/10/1030b86587f750dc525d03b394a0c558d9d62919.svn-base @@ -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) diff --git a/.svn/pristine/66/66a56d4135fe60e9b00e19afe6443095d981cd2f.svn-base b/.svn/pristine/66/66a56d4135fe60e9b00e19afe6443095d981cd2f.svn-base new file mode 100644 index 0000000..c51603d --- /dev/null +++ b/.svn/pristine/66/66a56d4135fe60e9b00e19afe6443095d981cd2f.svn-base @@ -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 /configure --prefix= + 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) diff --git a/.svn/pristine/79/79ae6048f94ca5180fe9a6eeef3485391f68de2a.svn-base b/.svn/pristine/79/79ae6048f94ca5180fe9a6eeef3485391f68de2a.svn-base new file mode 100644 index 0000000..eb71b53 --- /dev/null +++ b/.svn/pristine/79/79ae6048f94ca5180fe9a6eeef3485391f68de2a.svn-base @@ -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 +using std::string; + +#include +using std::cout; +using std::endl; + +#include + +#include +namespace po = boost::program_options; + +#include "slam6d/fbr/panorama.h" + +#include +#include + +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& 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& 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& 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(&start)->default_value(0), + "start at scan (i.e., neglects the first scans) " + "[ATTENTION: counting naturally starts with 0]") + ("end,e", po::value(&end)->default_value(-1), + "end after scan ") + ("format,f", po::value(&iotype)->default_value(UOS), + "using shared library 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(&maxDist)->default_value(-1), + "neglegt all data points with a distance larger than 'units") + ("min,m", po::value(&minDist)->default_value(-1), + "neglegt all data points with a distance smaller than 'units") + ("scanserver,S", po::value(&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(&width)->default_value(1280), + "width of panorama") + ("height,h", po::value(&height)->default_value(960), + "height of panorama") + ("panorama,p", po::value(&ptype)-> + default_value(fbr::EQUIRECTANGULAR), "panorama type (EQUIRECTANGULAR, " + "CYLINDRICAL, MERCATOR, RECTILINEAR, PANNINI, STEREOGRAPHIC, ZAXIS, " + "CONIC)") + ("num-images,N", po::value(&nImages)->default_value(1), + "number of images used for some projections") + ("proj-param,P", po::value(&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(&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(&stype)-> + default_value(PYR_MEAN_SHIFT), "segmentation method (THRESHOLD, " + "ADAPTIVE_THRESHOLD, PYR_MEAN_SHIFT, WATERSHED)") + ("marker,K", po::value(&marker), + "marker mask for watershed segmentation") + ("thresh,T", po::value(&thresh), + "threshold for threshold segmentation") + ("maxlevel,X", po::value(&maxlevel), + "maximum level for meanshift segmentation") + ("radius,R", po::value(&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(&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_ it; + it = scan.begin(); + 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_ it = source.begin(); + it != source.end(); ++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 values are larger than it + vector sorted(source.cols*source.rows); + int i = 0; + for (cv::MatIterator_ it = source.begin(); + it != source.end(); ++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_ src = source.begin(); + cv::MatIterator_ dst = result.begin(); + cv::MatIterator_ end = source.end(); + 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> &segmented_points, + cv::Mat &img, vector > > 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(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> &segmented_points, + cv::Mat &img, vector > > 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> histogram(256); + + for (i = 0; i < res.rows; i++) { + for (j = 0; j < res.cols; j++) { + idx = res.at(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> &segmented_points, + cv::Mat &img, vector > > 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(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> &segmented_points, + string &marker, cv::Mat &img, vector > > extendedMap) +{ + int i, j, idx; + cv::Mat markerMask = cv::imread(marker, 0); + vector > contours; + vector 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(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> &segmented_points, string &segdir) +{ + unsigned int i; + + vector 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::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> 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()); + } +} diff --git a/.svn/pristine/7e/7e4cb56ff76301b55a470749d6d46aa20b918500.svn-base b/.svn/pristine/7e/7e4cb56ff76301b55a470749d6d46aa20b918500.svn-base new file mode 100644 index 0000000..6ffec73 --- /dev/null +++ b/.svn/pristine/7e/7e4cb56ff76301b55a470749d6d46aa20b918500.svn-base @@ -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 \ No newline at end of file diff --git a/.svn/pristine/bd/bdb4df0b64a5b2583826a9e1d6e7ce7dbbc54d3d.svn-base b/.svn/pristine/bd/bdb4df0b64a5b2583826a9e1d6e7ce7dbbc54d3d.svn-base new file mode 100644 index 0000000..3dfa927 --- /dev/null +++ b/.svn/pristine/bd/bdb4df0b64a5b2583826a9e1d6e7ce7dbbc54d3d.svn-base @@ -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}) diff --git a/.svn/wc.db b/.svn/wc.db index c584bf1..1cb2af3 100644 Binary files a/.svn/wc.db and b/.svn/wc.db differ diff --git a/CMakeLists.txt b/CMakeLists.txt index 35aed9d..c51603d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,7 +164,16 @@ 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) @@ -370,6 +379,7 @@ 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) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0292987..6ffec73 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -29,6 +29,8 @@ 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 diff --git a/src/scanserver/CMakeLists.txt b/src/scanserver/CMakeLists.txt index ebb4877..3dfa927 100644 --- a/src/scanserver/CMakeLists.txt +++ b/src/scanserver/CMakeLists.txt @@ -42,7 +42,7 @@ set(SERVER_LIBS ${Boost_LIBRARIES} scanclient scanio) if(UNIX) # boost::interprocess uses pthread, requiring librt - #set(SERVER_LIBS ${SERVER_LIBS} rt) + set(SERVER_LIBS ${SERVER_LIBS} rt) endif(UNIX) if(WIN32) diff --git a/src/segmentation/CMakeLists.txt b/src/segmentation/CMakeLists.txt new file mode 100644 index 0000000..dc6b993 --- /dev/null +++ b/src/segmentation/CMakeLists.txt @@ -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) diff --git a/src/segmentation/scan2segments.cc b/src/segmentation/scan2segments.cc new file mode 100644 index 0000000..eb71b53 --- /dev/null +++ b/src/segmentation/scan2segments.cc @@ -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 +using std::string; + +#include +using std::cout; +using std::endl; + +#include + +#include +namespace po = boost::program_options; + +#include "slam6d/fbr/panorama.h" + +#include +#include + +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& 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& 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& 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(&start)->default_value(0), + "start at scan (i.e., neglects the first scans) " + "[ATTENTION: counting naturally starts with 0]") + ("end,e", po::value(&end)->default_value(-1), + "end after scan ") + ("format,f", po::value(&iotype)->default_value(UOS), + "using shared library 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(&maxDist)->default_value(-1), + "neglegt all data points with a distance larger than 'units") + ("min,m", po::value(&minDist)->default_value(-1), + "neglegt all data points with a distance smaller than 'units") + ("scanserver,S", po::value(&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(&width)->default_value(1280), + "width of panorama") + ("height,h", po::value(&height)->default_value(960), + "height of panorama") + ("panorama,p", po::value(&ptype)-> + default_value(fbr::EQUIRECTANGULAR), "panorama type (EQUIRECTANGULAR, " + "CYLINDRICAL, MERCATOR, RECTILINEAR, PANNINI, STEREOGRAPHIC, ZAXIS, " + "CONIC)") + ("num-images,N", po::value(&nImages)->default_value(1), + "number of images used for some projections") + ("proj-param,P", po::value(&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(&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(&stype)-> + default_value(PYR_MEAN_SHIFT), "segmentation method (THRESHOLD, " + "ADAPTIVE_THRESHOLD, PYR_MEAN_SHIFT, WATERSHED)") + ("marker,K", po::value(&marker), + "marker mask for watershed segmentation") + ("thresh,T", po::value(&thresh), + "threshold for threshold segmentation") + ("maxlevel,X", po::value(&maxlevel), + "maximum level for meanshift segmentation") + ("radius,R", po::value(&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(&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_ it; + it = scan.begin(); + 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_ it = source.begin(); + it != source.end(); ++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 values are larger than it + vector sorted(source.cols*source.rows); + int i = 0; + for (cv::MatIterator_ it = source.begin(); + it != source.end(); ++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_ src = source.begin(); + cv::MatIterator_ dst = result.begin(); + cv::MatIterator_ end = source.end(); + 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> &segmented_points, + cv::Mat &img, vector > > 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(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> &segmented_points, + cv::Mat &img, vector > > 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> histogram(256); + + for (i = 0; i < res.rows; i++) { + for (j = 0; j < res.cols; j++) { + idx = res.at(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> &segmented_points, + cv::Mat &img, vector > > 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(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> &segmented_points, + string &marker, cv::Mat &img, vector > > extendedMap) +{ + int i, j, idx; + cv::Mat markerMask = cv::imread(marker, 0); + vector > contours; + vector 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(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> &segmented_points, string &segdir) +{ + unsigned int i; + + vector 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::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> 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()); + } +} diff --git a/src/slam6d/fbr/CMakeLists.txt b/src/slam6d/fbr/CMakeLists.txt index 03e0264..e6f5206 100644 --- a/src/slam6d/fbr/CMakeLists.txt +++ b/src/slam6d/fbr/CMakeLists.txt @@ -4,7 +4,7 @@ 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}) +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})