├── CMakeLists.txt ├── README.md ├── cmake_uninstall.cmake.in ├── config.cmake.in ├── src ├── CMakeLists.txt ├── cpu.h ├── fbow.cpp ├── fbow.h ├── fbow_exports.h ├── vocabulary_creator.cpp └── vocabulary_creator.h ├── tests ├── CMakeLists.txt ├── README_DBOW2 ├── dbow2 │ ├── BowVector.h │ ├── CMakeLists.txt │ ├── FClass.h │ ├── FORB.cpp │ ├── FORB.h │ ├── FeatureVector.h │ ├── ScoringObject.cpp │ ├── ScoringObject.h │ └── TemplatedVocabulary.h ├── dbow2fbow.cpp ├── test_cpu_x86.cpp ├── test_dbow2VSfbow.cpp └── test_flann.cpp ├── utils ├── CMakeLists.txt ├── ImageMatching.py ├── dirreader.h ├── fbow_create_voc_step0.cpp ├── fbow_create_voc_step0_list.cpp ├── fbow_create_voc_step1.cpp ├── fbow_create_voc_step1_opencv.cpp ├── fbow_transform.cpp └── image_matching.cpp └── vocabularies └── orb_mur.fbow /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------- 2 | # Basic Configuration 3 | # ---------------------------------------------------------------------------- 4 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 5 | 6 | PROJECT(fbow) 7 | set(PROJECT_VERSION "0.0.1") 8 | string(REGEX MATCHALL "[0-9]" PROJECT_VERSION_PARTS "${PROJECT_VERSION}") 9 | list(GET PROJECT_VERSION_PARTS 0 PROJECT_VERSION_MAJOR) 10 | list(GET PROJECT_VERSION_PARTS 1 PROJECT_VERSION_MINOR) 11 | list(GET PROJECT_VERSION_PARTS 2 PROJECT_VERSION_PATCH) 12 | set(PROJECT_SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") 13 | 14 | set(CMAKE_MACOSX_RPATH 1) 15 | 16 | #------------------------------------------------------ 17 | # Build type 18 | #------------------------------------------------------ 19 | 20 | IF(NOT CMAKE_BUILD_TYPE ) 21 | SET( CMAKE_BUILD_TYPE "Release" ) 22 | ENDIF() 23 | 24 | #------------------------------------------------------ 25 | # Lib Names and Dirs 26 | #------------------------------------------------------ 27 | 28 | if(WIN32) 29 | # Postfix of DLLs: 30 | SET(PROJECT_DLLVERSION "${PROJECT_VERSION_MAJOR}${PROJECT_VERSION_MINOR}${PROJECT_VERSION_PATCH}") 31 | SET(RUNTIME_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin CACHE PATH "Directory for dlls and binaries") 32 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin CACHE PATH "Directory for binaries") 33 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin CACHE PATH "Directory for dlls") 34 | else() 35 | # Postfix of so's: 36 | set(PROJECT_DLLVERSION) 37 | SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_INSTALL_PREFIX}/lib/cmake/ /usr/lib/cmake /home/mjmarin/libs/ORB_SLAM2/lib/) 38 | 39 | endif() 40 | 41 | 42 | 43 | # 44 | OPTION(USE_OWN_EIGEN3 "Set to OFF to use a standard eigen3 version" ON) 45 | OPTION(BUILD_UTILS "Set to OFF to not build utils" ON) 46 | OPTION(BUILD_SHARED_LIBS "Set to OFF to build static libraries" ON) 47 | OPTION(USE_CONTRIB "Set on/off" OFF) 48 | 49 | OPTION(USE_AVX "Set on/off" ON) 50 | OPTION(USE_SSE3 "Set on/off" ON) 51 | option(FBOW_DEVINSTALL "Set to OFF to disable source installation" ON) 52 | 53 | # ---------------------------------------------------------------------------- 54 | # Find Dependencies 55 | # ---------------------------------------------------------------------------- 56 | find_package(OpenCV REQUIRED) 57 | INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) 58 | MESSAGE("OPENCVINCDIR=${OpenCV_INCLUDE_DIRS}") 59 | 60 | IF(USE_CONTRIB) 61 | add_definitions(-DUSE_CONTRIB) 62 | ENDIF() 63 | if(NOT OpenCV_VERSION VERSION_LESS "3.0") 64 | ADD_DEFINITIONS(-DOPENCV_VERSION_3) 65 | SET(OPENCV_VERSION_3 ON) 66 | ELSE() 67 | SET(OPENCV_VERSION_3 OFF) 68 | ENDIF() 69 | 70 | SET(REQUIRED_LIBRARIES ${REQUIRED_LIBRARIES} ${OpenCV_LIBS}) 71 | 72 | # ---------------------------------------------------------------------------- 73 | # PROJECT CONFIGURATION 74 | # force some variables that could be defined in the command line to be written to cache 75 | # ---------------------------------------------------------------------------- 76 | OPTION(INSTALL_DOC "Set to ON to build/install Documentation" OFF) 77 | IF (INSTALL_DOC) 78 | FIND_PACKAGE(Doxygen REQUIRED) 79 | MESSAGE( STATUS "INSTALL_DOC: ${INSTALL_DOC} ") 80 | INCLUDE("${PROJECT_SOURCE_DIR}/generateDoc.cmake") 81 | GENERATE_DOCUMENTATION(${PROJECT_SOURCE_DIR}/dox.in) 82 | ENDIF() 83 | 84 | # ---------------------------------------------------------------------------- 85 | # Uninstall target, for "make uninstall" 86 | # ---------------------------------------------------------------------------- 87 | CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) 88 | ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") 89 | 90 | # ---------------------------------------------------------------------------- 91 | # create configuration file from .in file (If you use windows take care with paths) 92 | # ---------------------------------------------------------------------------- 93 | 94 | CONFIGURE_FILE("${PROJECT_SOURCE_DIR}/config.cmake.in" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake") 95 | INSTALL(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" DESTINATION share/${PROJECT_NAME}/ ) 96 | 97 | 98 | 99 | 100 | # ---------------------------------------------------------------------------- 101 | # Program Optimization and debug (Extracted from OpenCV) 102 | # ---------------------------------------------------------------------------- 103 | set(WARNINGS_ARE_ERRORS OFF CACHE BOOL "Treat warnings as errors") 104 | set(WHOLE_PROGRAM_OPTIMIZATION OFF CACHE BOOL "Flags for whole program optimization.") 105 | 106 | set(EXTRA_C_FLAGS "") 107 | set(EXTRA_C_FLAGS_RELEASE "") 108 | set(EXTRA_C_FLAGS_DEBUG "") 109 | set(EXTRA_EXE_LINKER_FLAGS "") 110 | set(EXTRA_EXE_LINKER_FLAGS_RELEASE "") 111 | set(EXTRA_EXE_LINKER_FLAGS_DEBUG "") 112 | 113 | IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU" 114 | OR CMAKE_CXX_COMPILER_ID MATCHES "Clang" 115 | OR MINGW) 116 | set(ENABLE_PROFILING OFF CACHE BOOL "Enable profiling in the GCC compiler (Add flags: -g -pg)") 117 | set(USE_OMIT_FRAME_POINTER ON CACHE BOOL "Enable -fomit-frame-pointer for GCC") 118 | if(${CMAKE_SYSTEM_PROCESSOR} MATCHES arm*) # We can use only -O2 because the -O3 causes gcc crash 119 | set(USE_O2 ON CACHE BOOL "Enable -O2 for GCC") 120 | set(USE_FAST_MATH OFF CACHE BOOL "Enable -ffast-math for GCC") 121 | endif() 122 | if(${CMAKE_SYSTEM_PROCESSOR} MATCHES powerpc*) 123 | set(USE_O3 ON CACHE BOOL "Enable -O3 for GCC") 124 | set(USE_POWERPC ON CACHE BOOL "Enable PowerPC for GCC") 125 | endif () 126 | if(${CMAKE_SYSTEM_PROCESSOR} MATCHES amd64* OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES x86_64*) 127 | set(USE_O3 ON CACHE BOOL "Enable -O3 for GCC") 128 | set(USE_FAST_MATH OFF CACHE BOOL "Enable -ffast-math for GCC") 129 | set(USE_MMX ON CACHE BOOL "Enable MMX for GCC") 130 | set(USE_SSE ON CACHE BOOL "Enable SSE for GCC") 131 | set(USE_SSE2 ON CACHE BOOL "Enable SSE2 for GCC") 132 | set(USE_SSE3 ON CACHE BOOL "Enable SSE3 for GCC") 133 | endif() 134 | if(${CMAKE_SYSTEM_PROCESSOR} MATCHES i686* OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES x86) 135 | set(USE_O3 ON CACHE BOOL "Enable -O3 for GCC") 136 | set(USE_FAST_MATH OFF CACHE BOOL "Enable -ffast-math for GCC") 137 | set(USE_MMX ON CACHE BOOL "Enable MMX for GCC") 138 | set(USE_SSE OFF CACHE BOOL "Enable SSE for GCC") 139 | set(USE_SSE2 OFF CACHE BOOL "Enable SSE2 for GCC") 140 | set(USE_SSE3 OFF CACHE BOOL "Enable SSE3 for GCC") 141 | endif () 142 | set(USE_AVX OFF CACHE BOOL "Enable AVX for GCC") 143 | 144 | 145 | set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wall") 146 | 147 | if(WARNINGS_ARE_ERRORS) 148 | set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Werror") 149 | endif() 150 | 151 | # The -Wno-long-long is required in 64bit systems when including sytem headers. 152 | if(${CMAKE_SYSTEM_PROCESSOR} MATCHES x86_64* OR ${CMAKE_SYSTEM_PROCESSOR} MATCHES amd64*) 153 | set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -Wno-long-long") 154 | endif() 155 | 156 | # Whole program optimization 157 | if(WHOLE_PROGRAM_OPTIMIZATION) 158 | set(EXTRA_C_FLAGS_RELEASE "${EXTRA_C_FLAGS_RELEASE} -fwhole-program --combine") 159 | endif() 160 | 161 | # Other optimizations 162 | if(USE_OMIT_FRAME_POINTER) 163 | set(EXTRA_C_FLAGS_RELEASE "${EXTRA_C_FLAGS_RELEASE} -fomit-frame-pointer") 164 | endif() 165 | if(USE_O2) 166 | set(EXTRA_C_FLAGS_RELEASE "${EXTRA_C_FLAGS_RELEASE} -O2") 167 | endif() 168 | if(USE_O3) 169 | set(EXTRA_C_FLAGS_RELEASE "${EXTRA_C_FLAGS_RELEASE} -O3") 170 | endif() 171 | if(USE_FAST_MATH) 172 | set(EXTRA_C_FLAGS_RELEASE "${EXTRA_C_FLAGS_RELEASE} -ffast-math") 173 | endif() 174 | if(USE_POWERPC) 175 | set(EXTRA_C_FLAGS_RELEASE "${EXTRA_C_FLAGS_RELEASE} -mcpu=G3 -mtune=G5") 176 | endif() 177 | if(USE_MMX) 178 | set(VECTORIAL_INSTRUCTIONS "${VECTORIAL_INSTRUCTIONS} -mmmx") 179 | add_definitions(-DUSE_MMX) 180 | endif() 181 | if(USE_SSE) 182 | set(VECTORIAL_INSTRUCTIONS "${VECTORIAL_INSTRUCTIONS} -msse ") 183 | add_definitions(-DUSE_SSE) 184 | endif() 185 | if(USE_SSE2) 186 | set(VECTORIAL_INSTRUCTIONS "${VECTORIAL_INSTRUCTIONS} -msse2") 187 | add_definitions(-DUSE_SSE2) 188 | endif() 189 | if(USE_SSE3 AND NOT MINGW) # SSE3 should be disabled under MingW because it generates compiler errors 190 | set(VECTORIAL_INSTRUCTIONS "${VECTORIAL_INSTRUCTIONS} -msse3") 191 | add_definitions(-DUSE_SSE3) 192 | endif() 193 | 194 | if(USE_SSE4 AND NOT MINGW) # SSE3 should be disabled under MingW because it generates compiler errors 195 | set(VECTORIAL_INSTRUCTIONS "${VECTORIAL_INSTRUCTIONS} -msse4.1") 196 | add_definitions(-DUSE_SSE4) 197 | endif() 198 | 199 | IF(USE_AVX) 200 | set(VECTORIAL_INSTRUCTIONS "${VECTORIAL_INSTRUCTIONS} -mavx") 201 | add_definitions(-DUSE_AVX) 202 | endif() 203 | 204 | if(ENABLE_PROFILING) 205 | set(EXTRA_C_FLAGS_RELEASE "${EXTRA_C_FLAGS_RELEASE} -pg -g") 206 | else() 207 | if(NOT APPLE) 208 | set(EXTRA_C_FLAGS "${EXTRA_C_FLAGS} -ffunction-sections") 209 | endif() 210 | endif() 211 | 212 | 213 | IF(${CMAKE_SYSTEM_PROCESSOR} MATCHES armv7l) # In ARM_COrtex8 with neon, enalble vectorized operations 214 | SET(EXTRA_C_FLAGS_RELEASE "${EXTRA_C_FLAGS_RELEASE} ") 215 | ENDIF() 216 | 217 | 218 | set(CMAKE_CXX_FLAGS_RELEASE "${VECTORIAL_INSTRUCTIONS} ${EXTRA_C_FLAGS_RELEASE} -O3 -std=c++11 -Wall -DNDEBUG") 219 | set(CMAKE_CXX_FLAGS_DEBUG "${VECTORIAL_INSTRUCTIONS} -g3 -DDEBUG -D_DEBUG -std=c++11 -Wall") 220 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${VECTORIAL_INSTRUCTIONS} -O2 -g3 -DDEBUG -D_DEBUG -std=c++11 -Wall") 221 | 222 | message(STATUS "vec=${VECTORIAL_INSTRUCTIONS}") 223 | MESSAGE( STATUS "-------------------------------------------------------------------------------" ) 224 | message( STATUS "GNU COMPILER") 225 | MESSAGE( STATUS "-------------------------------------------------------------------------------" ) 226 | 227 | 228 | 229 | 230 | ELSE() # MSVC 231 | 232 | add_definitions(-DNOMINMAX) 233 | 234 | 235 | ENDIF()#END OF COMPILER SPECIFIC OPTIONS 236 | find_package(OpenMP ) 237 | if (OPENMP_FOUND) 238 | add_compile_options(-DUSE_OPENMP) 239 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") 240 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 241 | ENDIF() 242 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS} ") 243 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS} ") 244 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${CMAKE_CXX_FLAGS} ") 245 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EXTRA_EXE_LINKER_FLAGS}") 246 | SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${EXTRA_EXE_LINKER_FLAGS_RELEASE}") 247 | SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${EXTRA_EXE_LINKER_FLAGS_DEBUG}") 248 | 249 | 250 | #------------------------------------------------ 251 | # DIRS 252 | #------------------------------------------------ 253 | ADD_SUBDIRECTORY(src) 254 | IF (BUILD_UTILS) 255 | ADD_SUBDIRECTORY(utils) 256 | ADD_SUBDIRECTORY(tests) 257 | ENDIF() 258 | 259 | 260 | # ---------------------------------------------------------------------------- 261 | # display status message for important variables 262 | # ---------------------------------------------------------------------------- 263 | message( STATUS ) 264 | MESSAGE( STATUS "-------------------------------------------------------------------------------" ) 265 | message( STATUS "General configuration for ${PROJECT_NAME} ${PROJECT_VERSION}") 266 | MESSAGE( STATUS "-------------------------------------------------------------------------------" ) 267 | message( STATUS ) 268 | message(" Built as dynamic libs?:" ${BUILD_SHARED_LIBS}) 269 | message(" Compiler:" "${CMAKE_COMPILER}" "${CMAKE_CXX_COMPILER}") 270 | 271 | message( STATUS "Build Type: ${CMAKE_BUILD_TYPE}") 272 | message( STATUS "C++ flags (Release): ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}") 273 | message( STATUS "C++ flags (Debug): ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_DEBUG}") 274 | message( STATUS "C++ flags (Relase+Debug): ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") 275 | 276 | message( STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") 277 | message( STATUS "CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") 278 | message( STATUS "OPENMP FOUND: ${OPENMP_FOUND}") 279 | 280 | MESSAGE( STATUS ) 281 | MESSAGE( STATUS "CMAKE_SYSTEM_PROCESSOR = ${CMAKE_SYSTEM_PROCESSOR}" ) 282 | MESSAGE( STATUS "BUILD_SHARED_LIBS = ${BUILD_SHARED_LIBS}" ) 283 | MESSAGE( STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}" ) 284 | MESSAGE( STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}" ) 285 | MESSAGE( STATUS "CMAKE_MODULE_PATH = ${CMAKE_MODULE_PATH}" ) 286 | MESSAGE( STATUS "BUILD_UTILS= ${BUILD_UTILS}" ) 287 | MESSAGE( STATUS "BUILD_TESTS= ${BUILD_TESTS}" ) 288 | MESSAGE( STATUS "OPENCV_DIR= ${OpenCV_DIR}" ) 289 | 290 | 291 | 292 | MESSAGE( STATUS ) 293 | MESSAGE( STATUS "OpenCV_LIB_DIR=${OpenCV_LIB_DIR}") 294 | MESSAGE( STATUS "CMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}") 295 | 296 | MESSAGE( STATUS ) 297 | MESSAGE( STATUS ) 298 | MESSAGE( STATUS "Change a value with: cmake -D=" ) 299 | MESSAGE( STATUS ) 300 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FBOW 2 | ===== 3 | FBOW (Fast Bag of Words) is an extremmely optimized version of the DBow2/DBow3 libraries. The library is highly optimized to speed up the Bag of Words creation using AVX,SSE and MMX instructions. In loading a vocabulary, fbow is ~80x faster than DBOW2 (see tests directory and try). In transforming an image into a bag of words using on machines with AVX instructions, it is ~6.4x faster. 4 | 5 | ## 6 | ## Main features: 7 | * Only depends on OpenCV 8 | * Any type of descriptors allowed out of the box (binary and real) 9 | * Dictionary creation from a set of images. Bugs found in DBOW2/3 corrected. 10 | * Extremmely fast bow creation using specialized versions using AVX,SSE and MMX instructions both for binary and floating point descriptors. 11 | * Very fast load of vocabularies 12 | 13 | ## 14 | ## The main differences with DBOW2/3 are: 15 | 16 | * Not yet implemented indexing of images. 17 | 18 | ## 19 | ## Citing 20 | 21 | If you use this project in academic research you must cite us. This project is part of the ucoslam project. Visit [ucoslam.com](http://ucoslam.com) for more information 22 | 23 | 24 | ## 25 | ## Vocabularies 26 | 27 | In directory vocabularies you have one already prepared for orb. 28 | ## 29 | ## Test speed 30 | Go to test and run the program test_dbow2VSfbow 31 | ## 32 | ## License 33 | This software is distributed under MIT License 34 | -------------------------------------------------------------------------------- /cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | # ----------------------------------------------- 2 | # File that provides "make uninstall" target 3 | # We use the file 'install_manifest.txt' 4 | # ----------------------------------------------- 5 | IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 6 | MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") 7 | ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 8 | 9 | FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 10 | STRING(REGEX REPLACE "\n" ";" files "${files}") 11 | FOREACH(file ${files}) 12 | MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") 13 | # IF(EXISTS "$ENV{DESTDIR}${file}") 14 | # EXEC_PROGRAM( 15 | # "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 16 | # OUTPUT_VARIABLE rm_out 17 | # RETURN_VALUE rm_retval 18 | # ) 19 | EXECUTE_PROCESS(COMMAND rm $ENV{DESTDIR}${file}) 20 | # IF(NOT "${rm_retval}" STREQUAL 0) 21 | # MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 22 | # ENDIF(NOT "${rm_retval}" STREQUAL 0) 23 | # ELSE(EXISTS "$ENV{DESTDIR}${file}") 24 | # MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") 25 | # ENDIF(EXISTS "$ENV{DESTDIR}${file}") 26 | ENDFOREACH(file) 27 | 28 | 29 | -------------------------------------------------------------------------------- /config.cmake.in: -------------------------------------------------------------------------------- 1 | # =================================================================================== 2 | # @PROJECT_NAME@ CMake configuration file 3 | # 4 | # ** File generated automatically, do not modify ** 5 | # 6 | # Usage from an external project: 7 | # In your CMakeLists.txt, add these lines: 8 | # 9 | # FIND_PACKAGE(@PROJECT_NAME@ REQUIRED ) 10 | # TARGET_LINK_LIBRARIES(MY_TARGET_NAME ${@PROJECT_NAME@_LIBS}) 11 | # 12 | # This file will define the following variables: 13 | # - @PROJECT_NAME@_LIBS : The list of libraries to links against. 14 | # - @PROJECT_NAME@_LIB_DIR : The directory where lib files are. Calling LINK_DIRECTORIES 15 | # with this path is NOT needed. 16 | # - @PROJECT_NAME@_VERSION : The version of this PROJECT_NAME build. Example: "1.2.0" 17 | # - @PROJECT_NAME@_VERSION_MAJOR : Major version part of VERSION. Example: "1" 18 | # - @PROJECT_NAME@_VERSION_MINOR : Minor version part of VERSION. Example: "2" 19 | # - @PROJECT_NAME@_VERSION_PATCH : Patch version part of VERSION. Example: "0" 20 | # 21 | # =================================================================================== 22 | INCLUDE_DIRECTORIES("@CMAKE_INSTALL_PREFIX@/include") 23 | SET(@PROJECT_NAME@_INCLUDE_DIRS "@CMAKE_INSTALL_PREFIX@/include") 24 | 25 | LINK_DIRECTORIES("@CMAKE_INSTALL_PREFIX@/lib") 26 | SET(@PROJECT_NAME@_LIB_DIR "@CMAKE_INSTALL_PREFIX@/lib") 27 | 28 | SET(@PROJECT_NAME@_LIBS @REQUIRED_LIBRARIES@ @PROJECT_NAME@@PROJECT_DLLVERSION@) 29 | 30 | SET(@PROJECT_NAME@_FOUND YES) 31 | SET(@PROJECT_NAME@_FOUND "YES") 32 | SET(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) 33 | SET(@PROJECT_NAME@_VERSION_MAJOR @PROJECT_VERSION_MAJOR@) 34 | SET(@PROJECT_NAME@_VERSION_MINOR @PROJECT_VERSION_MINOR@) 35 | SET(@PROJECT_NAME@_VERSION_PATCH @PROJECT_VERSION_PATCH@) 36 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_DEFINITIONS(-DNOMINMAX) 2 | 3 | SET( srcs fbow.cpp vocabulary_creator.cpp ) 4 | SET( hdrs fbow.h fbow_exports.h vocabulary_creator.h cpu.h) 5 | 6 | SET (THIS_LIBNAME ${EXTRALIBNAME}fbow) 7 | 8 | 9 | ADD_LIBRARY(${THIS_LIBNAME} ${srcs} ${hdrs}) 10 | 11 | SET_TARGET_PROPERTIES(${THIS_LIBNAME} PROPERTIES # create *nix style library versions + symbolic links 12 | DEFINE_SYMBOL FBOW_DSO_EXPORTS 13 | VERSION ${PROJECT_VERSION} 14 | SOVERSION ${PROJECT_SOVERSION} 15 | CLEAN_DIRECT_OUTPUT 1 # allow creating static and shared libs without conflicts 16 | OUTPUT_NAME "${THIS_LIBNAME}${PROJECT_DLLVERSION}" # avoid conflicts between library and binary target names 17 | ) 18 | TARGET_LINK_LIBRARIES(${THIS_LIBNAME} ${OpenCV_LIBS} ${OpenMP_CXX_LIBRARIES}) 19 | 20 | INSTALL(TARGETS ${THIS_LIBNAME} 21 | RUNTIME DESTINATION bin COMPONENT main # Install the dll file in bin directory 22 | LIBRARY DESTINATION lib PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE COMPONENT main 23 | ARCHIVE DESTINATION lib COMPONENT main) # Install the dll.a file in lib directory 24 | 25 | 26 | IF(FBOW_DEVINSTALL) 27 | 28 | INSTALL(FILES ${hdrs} 29 | DESTINATION include/fbow 30 | COMPONENT main) 31 | ENDIF() 32 | 33 | 34 | #set(sources 35 | # fbow.cpp 36 | # vocabulary_creator.cpp 37 | #) 38 | 39 | #set(headers 40 | # fbow.h 41 | # fbow_exports.h 42 | # vocabulary_creator.h 43 | #) 44 | 45 | #add_library(fbow ${sources} ${headers}) 46 | 47 | #set_target_properties(fbow PROPERTIES # create *nix style library versions + symbolic links 48 | # DEFINE_SYMBOL FBOW_DSO_EXPORTS 49 | # VERSION ${PROJECT_VERSION} 50 | # SOVERSION ${PROJECT_SOVERSION} 51 | # CXX_VISIBILITY_PRESET hidden # Make all the symbols hidden, so we have to explicitly define an API 52 | # CLEAN_DIRECT_OUTPUT 1 # allow creating static and shared libs without conflicts 53 | # OUTPUT_NAME "fbow${PROJECT_DLLVERSION}" # avoid conflicts between library and binary target names 54 | #) 55 | 56 | #include(GenerateExportHeader) 57 | #generate_export_header(fbow) 58 | 59 | #target_link_libraries(fbow PUBLIC opencv_core) 60 | #target_link_libraries(fbow PRIVATE opencv_features2d) 61 | 62 | #target_include_directories(fbow PUBLIC 63 | # $ 64 | # $ 65 | # $ 66 | #) 67 | 68 | #install(TARGETS fbow EXPORT fbowConfig 69 | # RUNTIME DESTINATION bin 70 | # LIBRARY DESTINATION lib PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 71 | # ARCHIVE DESTINATION lib 72 | #) 73 | 74 | 75 | #install(EXPORT arucoConfig DESTINATION "share/aruco/cmake") 76 | -------------------------------------------------------------------------------- /src/cpu.h: -------------------------------------------------------------------------------- 1 | /* cpu.h : Author : Alexander J. Yee */ 2 | #ifndef _cpu_H 3 | #define _cpu_H 4 | #include 5 | #include 6 | #include 7 | #ifdef __ANDROID__ 8 | 9 | #else 10 | #if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) 11 | # if _WIN32 12 | #include 13 | #include 14 | # elif defined(__GNUC__) || defined(__clang__) 15 | #include 16 | #define _XCR_XFEATURE_ENABLED_MASK 0 17 | # else 18 | # error "No cpuid intrinsic defined for compiler." 19 | # endif 20 | #else 21 | # error "No cpuid intrinsic defined for processor architecture." 22 | #endif 23 | #endif //ANDROID 24 | namespace fbow{ 25 | struct cpu{ 26 | bool Vendor_AMD,Vendor_Intel;// Vendor 27 | bool OS_x64,OS_AVX,OS_AVX512;// OS Features 28 | bool HW_MMX,HW_x64,HW_ABM,HW_RDRAND,HW_BMI1,HW_BMI2,HW_ADX,HW_PREFETCHWT1,HW_MPX;// Misc. 29 | bool HW_SSE,HW_SSE2,HW_SSE3,HW_SSSE3,HW_SSE41,HW_SSE42,HW_SSE4a,HW_AES,HW_SHA;// SIMD: 128-bit 30 | bool HW_AVX,HW_XOP,HW_FMA3,HW_FMA4,HW_AVX2;// SIMD: 256-bit 31 | bool HW_AVX512_F,HW_AVX512_PF,HW_AVX512_ER,HW_AVX512_CD,HW_AVX512_VL,HW_AVX512_BW,HW_AVX512_DQ,HW_AVX512_IFMA,HW_AVX512_VBMI;// SIMD: 512-bit 32 | public: 33 | inline cpu(){ memset(this, 0, sizeof(*this)); } 34 | inline void detect_host(); 35 | inline bool isSafeAVX(){return HW_AVX && OS_AVX;} 36 | inline bool isSafeSSE(){return HW_SSE ;} 37 | inline bool isSafeMMX(){return HW_MMX ;} 38 | inline void disableAVX(){HW_AVX =false;} 39 | inline void disableMMX(){HW_MMX =false;} 40 | inline void disableSSE(){HW_SSE=false;} 41 | 42 | static inline void cpuid(int32_t out[4], int32_t x); 43 | static inline std::string get_vendor_string(); 44 | 45 | private: 46 | static bool inline detect_OS_x64(); 47 | static bool inline detect_OS_AVX(); 48 | static bool inline detect_OS_AVX512(); 49 | static inline uint64_t xgetbv(unsigned int x); 50 | }; 51 | #ifdef __ANDROID__ 52 | 53 | void cpu::cpuid(int32_t out[4], int32_t x){} 54 | #else 55 | //// MSCV 56 | #if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86) 57 | # if _WIN32 58 | void cpu::cpuid(int32_t out[4], int32_t x){ __cpuidex(out, x, 0); } 59 | uint64_t cpu::xgetbv(unsigned int x){ return _xgetbv(x); } 60 | // Detect 64-bit - Note that this snippet of code for detecting 64-bit has been copied from MSDN. 61 | typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); 62 | static inline BOOL IsWow64() 63 | { 64 | BOOL bIsWow64 = FALSE; 65 | LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); 66 | if (NULL == fnIsWow64Process) return FALSE; 67 | if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64)) return FALSE; 68 | return bIsWow64 ; 69 | } 70 | bool cpu::detect_OS_x64(){ 71 | #ifdef _M_X64 72 | return true; 73 | #else 74 | return IsWow64() != 0; 75 | #endif 76 | } 77 | //////////////////////////////////// 78 | /////GCC 79 | //////////////////////////////////// 80 | 81 | # elif defined(__GNUC__) || defined(__clang__) 82 | void cpu::cpuid(int32_t out[4], int32_t x){ __cpuid_count(x, 0, out[0], out[1], out[2], out[3]); } 83 | uint64_t cpu::xgetbv(unsigned int index){ uint32_t eax, edx; __asm__ __volatile__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(index)); return ((uint64_t)edx << 32) | eax;} 84 | //////////////////////////////////////////////////////////////////////////////// 85 | // Detect 64-bit. We only support x64 on Linux. 86 | bool cpu::detect_OS_x64(){ return true;} 87 | # endif 88 | #endif 89 | #endif 90 | 91 | //////////////////////////////////////////////////////////////////////////////// 92 | bool cpu::detect_OS_AVX(){ 93 | // Copied from: http://stackoverflow.com/a/22521619/922184 94 | #ifndef __ANDROID__ 95 | 96 | bool avxSupported = false; 97 | int cpuInfo[4]; cpuid(cpuInfo, 1); 98 | bool osUsesXSAVE_XRSTORE = (cpuInfo[2] & (1 << 27)) != 0; 99 | bool cpuAVXSuport = (cpuInfo[2] & (1 << 28)) != 0; 100 | if (osUsesXSAVE_XRSTORE && cpuAVXSuport) { uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK); avxSupported = (xcrFeatureMask & 0x6) == 0x6;} 101 | return avxSupported; 102 | #else 103 | return false; 104 | #endif 105 | } 106 | bool cpu::detect_OS_AVX512(){ 107 | 108 | #ifndef __ANDROID__ 109 | if (!detect_OS_AVX()) 110 | return false; 111 | uint64_t xcrFeatureMask = xgetbv(_XCR_XFEATURE_ENABLED_MASK); 112 | return (xcrFeatureMask & 0xe6) == 0xe6; 113 | #else 114 | return false; 115 | #endif 116 | 117 | } 118 | std::string cpu::get_vendor_string(){ int32_t CPUInfo[4]; char name[13];cpuid(CPUInfo, 0); memcpy(name + 0, &CPUInfo[1], 4);memcpy(name + 4, &CPUInfo[3], 4); memcpy(name + 8, &CPUInfo[2], 4); name[12] = '\0'; return name;} 119 | void cpu::detect_host(){ 120 | 121 | #ifndef __ANDROID__ 122 | 123 | OS_x64 = detect_OS_x64(); 124 | OS_AVX = detect_OS_AVX(); 125 | OS_AVX512 = detect_OS_AVX512(); 126 | // Vendor 127 | std::string vendor(get_vendor_string()); 128 | if (vendor == "GenuineIntel"){ Vendor_Intel = true;} 129 | else if (vendor == "AuthenticAMD"){ Vendor_AMD = true; } 130 | int info[4]; 131 | cpuid(info, 0); 132 | int nIds = info[0]; 133 | cpuid(info, 0x80000000); 134 | uint32_t nExIds = info[0]; 135 | // Detect Features 136 | if (nIds >= 0x00000001){ cpuid(info, 0x00000001); HW_MMX = (info[3] & ((int)1 << 23)) != 0; HW_SSE = (info[3] & ((int)1 << 25)) != 0; HW_SSE2 = (info[3] & ((int)1 << 26)) != 0; HW_SSE3 = (info[2] & ((int)1 << 0)) != 0; HW_SSSE3 = (info[2] & ((int)1 << 9)) != 0; HW_SSE41 = (info[2] & ((int)1 << 19)) != 0; HW_SSE42 = (info[2] & ((int)1 << 20)) != 0; HW_AES = (info[2] & ((int)1 << 25)) != 0; HW_AVX = (info[2] & ((int)1 << 28)) != 0; HW_FMA3 = (info[2] & ((int)1 << 12)) != 0; HW_RDRAND = (info[2] & ((int)1 << 30)) != 0; } 137 | if (nIds >= 0x00000007){ cpuid(info, 0x00000007); HW_AVX2 = (info[1] & ((int)1 << 5)) != 0; HW_BMI1 = (info[1] & ((int)1 << 3)) != 0; HW_BMI2 = (info[1] & ((int)1 << 8)) != 0; HW_ADX = (info[1] & ((int)1 << 19)) != 0; HW_MPX = (info[1] & ((int)1 << 14)) != 0; HW_SHA = (info[1] & ((int)1 << 29)) != 0; HW_PREFETCHWT1 = (info[2] & ((int)1 << 0)) != 0; HW_AVX512_F = (info[1] & ((int)1 << 16)) != 0; HW_AVX512_CD = (info[1] & ((int)1 << 28)) != 0; HW_AVX512_PF = (info[1] & ((int)1 << 26)) != 0; HW_AVX512_ER = (info[1] & ((int)1 << 27)) != 0; HW_AVX512_VL = (info[1] & ((int)1 << 31)) != 0; HW_AVX512_BW = (info[1] & ((int)1 << 30)) != 0; HW_AVX512_DQ = (info[1] & ((int)1 << 17)) != 0; HW_AVX512_IFMA = (info[1] & ((int)1 << 21)) != 0; HW_AVX512_VBMI = (info[2] & ((int)1 << 1)) != 0; } 138 | if (nExIds >= 0x80000001){ cpuid(info, 0x80000001); HW_x64 = (info[3] & ((int)1 << 29)) != 0; HW_ABM = (info[2] & ((int)1 << 5)) != 0; HW_SSE4a = (info[2] & ((int)1 << 6)) != 0; HW_FMA4 = (info[2] & ((int)1 << 16)) != 0; HW_XOP = (info[2] & ((int)1 << 11)) != 0; } 139 | #endif 140 | } 141 | 142 | } 143 | #endif 144 | -------------------------------------------------------------------------------- /src/fbow.cpp: -------------------------------------------------------------------------------- 1 | #include "fbow.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace fbow{ 9 | 10 | void Vocabulary::setParams(int aligment, int k, int desc_type, int desc_size, int nblocks, std::string desc_name) { 11 | auto ns= desc_name.size()(49)?desc_name.size():128; 12 | desc_name.resize(ns); 13 | 14 | std::strcpy(_params._desc_name_,desc_name.c_str()); 15 | _params._aligment=aligment; 16 | _params._m_k= k; 17 | _params._desc_type=desc_type; 18 | _params._desc_size=desc_size; 19 | _params._nblocks=nblocks; 20 | 21 | 22 | uint64_t _desc_size_bytes_al=0; 23 | uint64_t _block_size_bytes_al=0; 24 | 25 | //consider possible aligment of each descriptor adding offsets at the end 26 | _params._desc_size_bytes_wp=_params._desc_size; 27 | _desc_size_bytes_al= _params._desc_size_bytes_wp/ _params._aligment; 28 | if( _params._desc_size_bytes_wp% _params._aligment!=0) _desc_size_bytes_al++; 29 | _params._desc_size_bytes_wp= _desc_size_bytes_al* _params._aligment; 30 | 31 | 32 | int foffnbytes_alg=sizeof(uint64_t)/_params._aligment; 33 | if(sizeof(uint64_t)%_params._aligment!=0) foffnbytes_alg++; 34 | _params._feature_off_start=foffnbytes_alg*_params._aligment; 35 | _params._child_off_start=_params._feature_off_start+_params._m_k*_params._desc_size_bytes_wp ;//where do children information start from the start of the block 36 | 37 | //block: nvalid|f0 f1 .. fn|ni0 ni1 ..nin 38 | _params._block_size_bytes_wp=_params._feature_off_start+ _params._m_k * ( _params._desc_size_bytes_wp + sizeof(Vocabulary::block_node_info)); 39 | _block_size_bytes_al=_params._block_size_bytes_wp/_params._aligment; 40 | if (_params._block_size_bytes_wp%_params._aligment!=0) _block_size_bytes_al++; 41 | _params._block_size_bytes_wp= _block_size_bytes_al*_params._aligment; 42 | 43 | //give memory 44 | _params._total_size=_params._block_size_bytes_wp*_params._nblocks; 45 | _data = std::unique_ptr((char*)AlignedAlloc(_params._aligment, _params._total_size), &AlignedFree); 46 | 47 | memset(_data.get(), 0, _params._total_size); 48 | 49 | } 50 | 51 | void Vocabulary::transform(const cv::Mat &features, int level,fBow &result,fBow2&result2){ 52 | if (features.rows==0) throw std::runtime_error("Vocabulary::transform No input data"); 53 | if (features.type()!=_params._desc_type) throw std::runtime_error("Vocabulary::transform features are of different type than vocabulary"); 54 | if (features.cols * features.elemSize() !=size_t(_params._desc_size)) throw std::runtime_error("Vocabulary::transform features are of different size than the vocabulary ones"); 55 | 56 | //get host info to decide the version to execute 57 | if (!cpu_info){ 58 | cpu_info=std::make_shared(); 59 | cpu_info->detect_host(); 60 | } 61 | //decide the version to employ according to the type of features, aligment and cpu capabilities 62 | if (_params._desc_type==CV_8UC1){ 63 | //orb 64 | if (cpu_info->HW_x64){ 65 | if (_params._desc_size==32) 66 | _transform2(features,level,result,result2); 67 | //full akaze 68 | else if( _params._desc_size==61 && _params._aligment%8==0) 69 | _transform2(features,level,result,result2); 70 | //generic 71 | else 72 | _transform2(features,level,result,result2); 73 | } 74 | else _transform2(features,level,result,result2); 75 | } 76 | else if(features.type()==CV_32FC1){ 77 | if( cpu_info->isSafeAVX() && _params._aligment%32==0){ //AVX version 78 | if ( _params._desc_size==256) _transform2(features,level,result,result2);//specific for surf 256 bytes 79 | else _transform2(features,level,result,result2);//any other 80 | } 81 | if( cpu_info->isSafeSSE() && _params._aligment%16==0){//SSE version 82 | if ( _params._desc_size==256) _transform2(features,level,result,result2);//specific for surf 256 bytes 83 | else _transform2(features,level,result,result2);//any other 84 | } 85 | //generic version 86 | _transform2(features,level,result,result2); 87 | } 88 | else throw std::runtime_error("Vocabulary::transform invalid feature type. Should be CV_8UC1 or CV_32FC1"); 89 | 90 | } 91 | 92 | fBow Vocabulary::transform(const cv::Mat &features) 93 | { 94 | if (features.rows==0) throw std::runtime_error("Vocabulary::transform No input data"); 95 | if (features.type()!=_params._desc_type) throw std::runtime_error("Vocabulary::transform features are of different type than vocabulary"); 96 | if (features.cols * features.elemSize() !=size_t(_params._desc_size)) throw std::runtime_error("Vocabulary::transform features are of different size than the vocabulary ones"); 97 | 98 | //get host info to decide the version to execute 99 | if (!cpu_info){ 100 | cpu_info=std::make_shared(); 101 | cpu_info->detect_host(); 102 | } 103 | fBow result; 104 | //decide the version to employ according to the type of features, aligment and cpu capabilities 105 | if (_params._desc_type==CV_8UC1){ 106 | //orb 107 | if (cpu_info->HW_x64){ 108 | if (_params._desc_size==32) 109 | result=_transform(features); 110 | //full akaze 111 | else if( _params._desc_size==61 && _params._aligment%8==0) 112 | result=_transform(features); 113 | //generic 114 | else 115 | result=_transform(features ); 116 | } 117 | else result= _transform(features ); 118 | } 119 | else if(features.type()==CV_32FC1){ 120 | if( cpu_info->isSafeAVX() && _params._aligment%32==0){ //AVX version 121 | if ( _params._desc_size==256) result= _transform(features);//specific for surf 256 bytes 122 | else result= _transform(features);//any other 123 | } 124 | if( cpu_info->isSafeSSE() && _params._aligment%16==0){//SSE version 125 | if ( _params._desc_size==256) result= _transform(features);//specific for surf 256 bytes 126 | else result=_transform(features);//any other 127 | } 128 | //generic version 129 | result=_transform(features); 130 | } 131 | else throw std::runtime_error("Vocabulary::transform invalid feature type. Should be CV_8UC1 or CV_32FC1"); 132 | 133 | ///now, normalize 134 | //L2 135 | double norm=0; 136 | for(auto e:result) norm += e.second * e.second; 137 | 138 | if(norm > 0.0) 139 | { 140 | double inv_norm = 1./sqrt(norm); 141 | for(auto &e:result) e.second*=inv_norm ; 142 | } 143 | return result; 144 | } 145 | 146 | 147 | 148 | void Vocabulary::clear() 149 | { 150 | _data.reset(); 151 | memset(&_params,0,sizeof(_params)); 152 | _params._desc_name_[0]='\0'; 153 | } 154 | 155 | 156 | //loads/saves from a file 157 | void Vocabulary::readFromFile(const std::string &filepath){ 158 | std::ifstream file(filepath,std::ios::binary); 159 | if (!file) throw std::runtime_error("Vocabulary::readFromFile could not open:"+filepath); 160 | fromStream(file); 161 | } 162 | 163 | void Vocabulary::saveToFile(const std::string &filepath){ 164 | std::ofstream file(filepath, std::ios::binary); 165 | if (!file) throw std::runtime_error("Vocabulary::saveToFile could not open:"+filepath); 166 | toStream(file); 167 | 168 | } 169 | 170 | ///save/load to binary streams 171 | void Vocabulary::toStream(std::ostream &str)const{ 172 | //magic number 173 | uint64_t sig=55824124; 174 | str.write((char*)&sig,sizeof(sig)); 175 | //save string 176 | str.write((char*)&_params,sizeof(params)); 177 | str.write(_data.get(), _params._total_size); 178 | } 179 | 180 | void Vocabulary::fromStream(std::istream &str) 181 | { 182 | uint64_t sig; 183 | str.read((char*)&sig,sizeof(sig)); 184 | if (sig!=55824124) throw std::runtime_error("Vocabulary::fromStream invalid signature"); 185 | //read string 186 | str.read((char*)&_params,sizeof(params)); 187 | _data = std::unique_ptr((char*)AlignedAlloc(_params._aligment, _params._total_size), &AlignedFree); 188 | if (_data.get() == nullptr) throw std::runtime_error("Vocabulary::fromStream Could not allocate data"); 189 | str.read(_data.get(), _params._total_size); 190 | } 191 | 192 | double fBow::score (const fBow &v1,const fBow &v2){ 193 | 194 | 195 | fBow::const_iterator v1_it, v2_it; 196 | const fBow::const_iterator v1_end = v1.end(); 197 | const fBow::const_iterator v2_end = v2.end(); 198 | 199 | v1_it = v1.begin(); 200 | v2_it = v2.begin(); 201 | 202 | double score = 0; 203 | 204 | while(v1_it != v1_end && v2_it != v2_end) 205 | { 206 | const auto& vi = v1_it->second; 207 | const auto& wi = v2_it->second; 208 | 209 | if(v1_it->first == v2_it->first) 210 | { 211 | score += vi * wi; 212 | 213 | // move v1 and v2 forward 214 | ++v1_it; 215 | ++v2_it; 216 | } 217 | else if(v1_it->first < v2_it->first) 218 | { 219 | // move v1 forward 220 | // v1_it = v1.lower_bound(v2_it->first); 221 | while(v1_it!=v1_end&& v1_it->firstfirst) 222 | ++v1_it; 223 | } 224 | else 225 | { 226 | // move v2 forward 227 | // v2_it = v2.lower_bound(v1_it->first); 228 | while(v2_it!=v2_end && v2_it->firstfirst) 229 | ++v2_it; 230 | 231 | // v2_it = (first element >= v1_it.id) 232 | } 233 | } 234 | 235 | // ||v - w||_{L2} = sqrt( 2 - 2 * Sum(v_i * w_i) ) 236 | // for all i | v_i != 0 and w_i != 0 ) 237 | // (Nister, 2006) 238 | if(score >= 1) // rounding errors 239 | score = 1.0; 240 | else 241 | score = 1.0 - sqrt(1.0 - score); // [0..1] 242 | 243 | return score; 244 | } 245 | uint64_t fBow::hash()const{ 246 | uint64_t seed = 0; 247 | for(auto e:*this) 248 | seed^= e.first + int(e.second*1000)+ 0x9e3779b9 + (seed << 6) + (seed >> 2); 249 | 250 | return seed; 251 | 252 | } 253 | 254 | uint64_t Vocabulary::hash()const{ 255 | 256 | uint64_t seed = 0; 257 | for(uint64_t i=0;i<_params._total_size;i++) 258 | seed^= _data.get()[i] + 0x9e3779b9 + (seed << 6) + (seed >> 2); 259 | return seed; 260 | } 261 | void fBow::toStream(std::ostream &str) const { 262 | uint32_t _size=size(); 263 | str.write((char*)&_size,sizeof(_size)); 264 | for(const auto & e:*this) 265 | str.write((char*)&e,sizeof(e)); 266 | } 267 | void fBow::fromStream(std::istream &str) { 268 | clear(); 269 | uint32_t _size; 270 | str.read((char*)&_size,sizeof(_size)); 271 | for(uint32_t i=0;i<_size;i++){ 272 | std::pair e; 273 | str.read((char*)&e,sizeof(e)); 274 | insert(e); 275 | } 276 | } 277 | 278 | void fBow2::toStream(std::ostream &str) const { 279 | uint32_t _size=size(); 280 | str.write((char*)&_size,sizeof(_size)); 281 | for(const auto &e:*this){ 282 | str.write((char*)&e.first,sizeof(e.first)); 283 | //now the vector 284 | _size=e.second.size(); 285 | str.write((char*)&_size,sizeof(_size)); 286 | str.write((char*)&e.second[0],sizeof(e.second[0])*e.second.size()); 287 | } 288 | } 289 | 290 | void fBow2::fromStream(std::istream &str) { 291 | uint32_t _sizeMap,_sizeVec; 292 | std::vector vec; 293 | uint32_t key; 294 | 295 | clear(); 296 | str.read((char*)&_sizeMap,sizeof(_sizeMap)); 297 | for(uint32_t i=0;i<_sizeMap;i++){ 298 | str.read((char*)&key,sizeof(key)); 299 | str.read((char*)&_sizeVec,sizeof(_sizeVec));//vector size 300 | vec.resize(_sizeVec); 301 | str.read((char*)&vec[0],sizeof(vec[0])*_sizeVec); 302 | insert({key,vec}); 303 | } 304 | } 305 | 306 | uint64_t fBow2::hash()const{ 307 | 308 | 309 | uint64_t seed = 0; 310 | 311 | 312 | for(const auto &e:*this){ 313 | seed^= e.first + 0x9e3779b9 + (seed << 6) + (seed >> 2); 314 | for(const auto &idx:e.second) 315 | seed^= idx + 0x9e3779b9 + (seed << 6) + (seed >> 2); 316 | } 317 | 318 | return seed; 319 | 320 | } 321 | 322 | } 323 | -------------------------------------------------------------------------------- /src/fbow.h: -------------------------------------------------------------------------------- 1 | #ifndef _FBOW_VOCABULARY_H 2 | #define _FBOW_VOCABULARY_H 3 | #include "fbow_exports.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #ifndef __ANDROID__ 10 | #include 11 | #endif 12 | #include "cpu.h" 13 | namespace fbow{ 14 | 15 | //float initialized to zero. 16 | struct FBOW_API _float{ 17 | float var=0; 18 | inline float operator=(float &f){var=f;return var;} 19 | inline operator float&() {return var;} 20 | inline operator float() const{return var;} 21 | }; 22 | 23 | /**Bag of words 24 | */ 25 | struct FBOW_API fBow:std::map{ 26 | 27 | void toStream(std::ostream &str) const ; 28 | void fromStream(std::istream &str) ; 29 | 30 | //returns a hash identifying this 31 | uint64_t hash()const; 32 | //returns the similitude score between to image descriptors using L2 norm 33 | static double score(const fBow &v1, const fBow &v2); 34 | 35 | }; 36 | 37 | 38 | //Bag of words with augmented information. For each word, keeps information about the indices of the elements that have been classified into the word 39 | //it is computed at the desired level 40 | struct FBOW_API fBow2:std::map> { 41 | 42 | void toStream(std::ostream &str) const ; 43 | 44 | void fromStream(std::istream &str) ; 45 | 46 | //returns a hash identifying this 47 | uint64_t hash()const; 48 | 49 | 50 | }; 51 | 52 | /**Main class to represent a vocabulary of visual words 53 | */ 54 | class FBOW_API Vocabulary 55 | { 56 | static inline void * AlignedAlloc(int __alignment,int size){ 57 | assert(__alignment<256); 58 | 59 | unsigned char *ptr= (unsigned char*)malloc(size + __alignment); 60 | 61 | if( !ptr ) return 0; 62 | 63 | // align the pointer 64 | 65 | size_t lptr=(size_t)ptr; 66 | int off =lptr%__alignment; 67 | if (off==0) off=__alignment; 68 | 69 | ptr = ptr+off ; //move to next aligned address 70 | *(ptr-1)=(unsigned char)off; //save in prev, the offset to properly remove it 71 | return ptr; 72 | } 73 | 74 | static inline void AlignedFree(void *ptr){ 75 | if(ptr==nullptr)return; 76 | unsigned char *uptr=(unsigned char *)ptr; 77 | unsigned char off= *(uptr-1); 78 | uptr-=off; 79 | std::free(uptr); 80 | } 81 | 82 | // using Data_ptr = std::unique_ptr; 83 | 84 | friend class VocabularyCreator; 85 | 86 | public: 87 | 88 | Vocabulary(): _data((char*)nullptr,&AlignedFree){} 89 | Vocabulary(Vocabulary&&) = default; 90 | 91 | //transform the features stored as rows in the returned BagOfWords 92 | fBow transform(const cv::Mat &features); 93 | void transform(const cv::Mat &features, int level,fBow &result,fBow2&result2); 94 | 95 | 96 | //loads/saves from a file 97 | void readFromFile(const std::string &filepath); 98 | void saveToFile(const std::string &filepath); 99 | ///save/load to binary streams 100 | void toStream(std::ostream &str) const; 101 | void fromStream(std::istream &str); 102 | //returns the descriptor type (CV_8UC1, CV_32FC1 ) 103 | uint32_t getDescType()const{return _params._desc_type;} 104 | //returns desc size in bytes or 0 if not set 105 | uint32_t getDescSize()const{return _params._desc_size;} 106 | //returns the descriptor name 107 | std::string getDescName() const{ return _params._desc_name_;} 108 | //returns the branching factor (number of children per node) 109 | uint32_t getK()const{return _params._m_k;} 110 | //indicates whether this object is valid 111 | bool isValid()const{return _data.get()!=nullptr;} 112 | //total number of blocks 113 | size_t size()const{return _params._nblocks;} 114 | //removes all data 115 | void clear(); 116 | //returns a hash value idinfying the vocabulary 117 | uint64_t hash()const; 118 | 119 | private: 120 | void setParams( int aligment,int k,int desc_type,int desc_size, int nblocks,std::string desc_name) ; 121 | struct params{ 122 | char _desc_name_[50];//descriptor name. May be empty 123 | uint32_t _aligment=0,_nblocks=0 ;//memory aligment and total number of blocks 124 | uint64_t _desc_size_bytes_wp=0;//size of the descriptor(includes padding) 125 | uint64_t _block_size_bytes_wp=0;//size of a block (includes padding) 126 | uint64_t _feature_off_start=0;//within a block, where the features start 127 | uint64_t _child_off_start=0;//within a block,where the children offset part starts 128 | uint64_t _total_size=0; 129 | int32_t _desc_type=0,_desc_size=0;//original descriptor types and sizes (without padding) 130 | uint32_t _m_k=0;//number of children per node 131 | }; 132 | params _params; 133 | std::unique_ptr _data; 134 | 135 | 136 | //structure represeting a information about node in a block 137 | struct block_node_info{ 138 | uint32_t id_or_childblock; //if id ,msb is 1. 139 | float weight; 140 | inline bool isleaf()const{return ( id_or_childblock& 0x80000000);} 141 | 142 | //if not leaf, returns the block where the children are 143 | //if leaf, returns the index of the feature it represents. In case of bagofwords it must be a invalid value 144 | inline uint32_t getId()const{return ( id_or_childblock&0x7FFFFFFF);} 145 | 146 | //sets as leaf, and sets the index of the feature it represents and its weight 147 | inline void setLeaf(uint32_t id,float Weight){ 148 | assert(!(id & 0x80000000));//check msb is zero 149 | id_or_childblock=id; 150 | id_or_childblock|=0x80000000;//set the msb to one to distinguish from non leaf 151 | //now,set the weight too 152 | weight=Weight; 153 | } 154 | //sets as non leaf and sets the id of the block where the chilren are 155 | inline void setNonLeaf(uint32_t id){ 156 | //ensure the msb is 0 157 | assert( !(id & 0x80000000));//32 bits 100000000...0.check msb is not set 158 | id_or_childblock=id; 159 | } 160 | }; 161 | 162 | 163 | //a block represent all the child nodes of a parent, with its features and also information about where the child of these are in the data structure 164 | //a block structure is as follow: N|isLeaf|BlockParentId|p|F0...FN|C0W0 ... CNWN.. 165 | //N :16 bits : number of nodes in this block. Must be <=branching factor k. If N(0),feature.elemSize1()*feature.cols); } 188 | inline void getFeature(int i,cv::Mat feature){ memcpy( feature.ptr(0), _blockstart+_feature_off_start+i*_desc_size_bytes,_desc_size_bytes ); } 189 | template inline T*getFeature(int i){return (T*) (_blockstart+_feature_off_start+i*_desc_size_bytes_wp);} 190 | char *_blockstart; 191 | uint64_t _desc_size_bytes=0;//size of the descriptor(without padding) 192 | uint64_t _desc_size_bytes_wp=0;//size of the descriptor(includding padding) 193 | uint64_t _feature_off_start=0; 194 | uint64_t _child_off_start=0;//into the block,where the children offset part starts 195 | }; 196 | 197 | 198 | //returns a block structure pointing at block b 199 | inline Block getBlock(uint32_t b) { assert(_data.get() != nullptr); assert(b < _params._nblocks); return Block(_data.get() + b * _params._block_size_bytes_wp, _params._desc_size, _params._desc_size_bytes_wp, _params._feature_off_start, _params._child_off_start); } 200 | //given a block already create with getBlock, moves it to point to block b 201 | inline void setBlock(uint32_t b, Block &block) { block._blockstart = _data.get() + b * _params._block_size_bytes_wp; } 202 | 203 | //information about the cpu so that mmx,sse or avx extensions can be employed 204 | std::shared_ptr cpu_info; 205 | 206 | 207 | //////////////////////////////////////////////////////////// 208 | //base class for computing distances between feature vectors 209 | template 210 | class Lx{ 211 | public: 212 | typedef distType DType; 213 | typedef register_type TData; 214 | protected: 215 | 216 | int _nwords,_aligment,_desc_size; 217 | int _block_desc_size_bytes_wp; 218 | register_type *feature=0; 219 | public: 220 | virtual ~Lx(){if (feature!=0)AlignedFree(feature);} 221 | void setParams(int desc_size, int block_desc_size_bytes_wp){ 222 | assert(block_desc_size_bytes_wp%aligment==0); 223 | _desc_size=desc_size; 224 | _block_desc_size_bytes_wp=block_desc_size_bytes_wp; 225 | assert(_block_desc_size_bytes_wp%sizeof(register_type )==0); 226 | _nwords=_block_desc_size_bytes_wp/sizeof(register_type );//number of aligned words 227 | feature=static_cast (AlignedAlloc(aligment,_nwords*sizeof(register_type ))); 228 | memset(feature,0,_nwords*sizeof(register_type )); 229 | } 230 | inline void startwithfeature(const register_type *feat_ptr){memcpy(feature,feat_ptr,_desc_size);} 231 | virtual distType computeDist(register_type *fptr)=0; 232 | 233 | }; 234 | 235 | 236 | struct L2_generic:public Lx{ 237 | virtual ~L2_generic(){ } 238 | inline float computeDist(float *fptr){ 239 | float d=0; 240 | for(int f=0;f<_nwords;f++) d+= (feature[f]-fptr[f])*(feature[f]-fptr[f]); 241 | return d; 242 | } 243 | }; 244 | #ifdef __ANDROID__ 245 | //fake elements to allow compilation 246 | struct L2_avx_generic:public Lx{inline float computeDist(uint64_t *ptr){return std::numeric_limits::max();}}; 247 | struct L2_se3_generic:public Lx{inline float computeDist(uint64_t *ptr){return std::numeric_limits::max();}}; 248 | struct L2_sse3_16w:public Lx{inline float computeDist(uint64_t *ptr){return std::numeric_limits::max();}}; 249 | struct L2_avx_8w:public Lx{inline float computeDist(uint64_t *ptr){return std::numeric_limits::max();}}; 250 | 251 | 252 | 253 | 254 | #else 255 | struct L2_avx_generic:public Lx<__m256,float,32>{ 256 | virtual ~L2_avx_generic(){} 257 | inline float computeDist(__m256 *ptr){ 258 | __m256 sum=_mm256_setzero_ps(), sub_mult; 259 | //substract, multiply and accumulate 260 | for(int i=0;i<_nwords;i++){ 261 | sub_mult=_mm256_sub_ps(feature[i],ptr[i]); 262 | sub_mult=_mm256_mul_ps(sub_mult,sub_mult); 263 | sum=_mm256_add_ps(sum,sub_mult); 264 | } 265 | sum=_mm256_hadd_ps(sum,sum); 266 | sum=_mm256_hadd_ps(sum,sum); 267 | float *sum_ptr=(float*)∑ 268 | return sum_ptr[0]+sum_ptr[4]; 269 | } 270 | }; 271 | struct L2_se3_generic:public Lx<__m128,float,16>{ 272 | inline float computeDist(__m128 *ptr){ 273 | __m128 sum=_mm_setzero_ps(), sub_mult; 274 | //substract, multiply and accumulate 275 | for(int i=0;i<_nwords;i++){ 276 | sub_mult=_mm_sub_ps(feature[i],ptr[i]); 277 | sub_mult=_mm_mul_ps(sub_mult,sub_mult); 278 | sum=_mm_add_ps(sum,sub_mult); 279 | } 280 | sum=_mm_hadd_ps(sum,sum); 281 | sum=_mm_hadd_ps(sum,sum); 282 | float *sum_ptr=(float*)∑ 283 | return sum_ptr[0] ; 284 | } 285 | }; 286 | struct L2_sse3_16w:public Lx<__m128,float,16> { 287 | 288 | inline float computeDist(__m128 *ptr){ 289 | __m128 sum=_mm_setzero_ps(), sub_mult; 290 | //substract, multiply and accumulate 291 | for(int i=0;i<16;i++){ 292 | sub_mult=_mm_sub_ps(feature[i],ptr[i]); 293 | sub_mult=_mm_mul_ps(sub_mult,sub_mult); 294 | sum=_mm_add_ps(sum,sub_mult); 295 | } 296 | sum=_mm_hadd_ps(sum,sum); 297 | sum=_mm_hadd_ps(sum,sum); 298 | float *sum_ptr=(float*)∑ 299 | return sum_ptr[0] ; 300 | } 301 | }; 302 | //specific for surf in avx 303 | struct L2_avx_8w:public Lx<__m256,float,32> { 304 | 305 | inline float computeDist(__m256 *ptr){ 306 | __m256 sum=_mm256_setzero_ps(), sub_mult; 307 | //substract, multiply and accumulate 308 | 309 | for(int i=0;i<8;i++){ 310 | sub_mult=_mm256_sub_ps(feature[i],ptr[i]); 311 | sub_mult=_mm256_mul_ps(sub_mult,sub_mult); 312 | sum=_mm256_add_ps(sum,sub_mult); 313 | } 314 | 315 | sum=_mm256_hadd_ps(sum,sum); 316 | sum=_mm256_hadd_ps(sum,sum); 317 | float *sum_ptr=(float*)∑ 318 | return sum_ptr[0]+sum_ptr[4]; 319 | } 320 | }; 321 | 322 | #endif 323 | 324 | //generic hamming distance calculator 325 | struct L1_x64:public Lx{ 326 | inline uint64_t computeDist(uint64_t *feat_ptr){ 327 | uint64_t result = 0; 328 | for(int i = 0; i < _nwords; ++i ) result += std::bitset<64>(feat_ptr[i] ^ feature[i]).count(); 329 | return result; 330 | } 331 | }; 332 | 333 | struct L1_x32:public Lx{ 334 | inline uint32_t computeDist(uint32_t *feat_ptr){ 335 | uint32_t result = 0; 336 | for(int i = 0; i < _nwords; ++i ) result += std::bitset<32>(feat_ptr[i] ^ feature[i]).count(); 337 | return result; 338 | } 339 | 340 | }; 341 | 342 | //for orb 343 | struct L1_32bytes:public Lx{ 344 | inline uint64_t computeDist(uint64_t *feat_ptr){ 345 | return uint64_popcnt(feat_ptr[0]^feature[0])+ uint64_popcnt(feat_ptr[1]^feature[1])+ 346 | uint64_popcnt(feat_ptr[2]^feature[2])+uint64_popcnt(feat_ptr[3]^feature[3]); 347 | } 348 | inline uint64_t uint64_popcnt(uint64_t n) { 349 | return std::bitset<64>(n).count(); 350 | } 351 | 352 | }; 353 | //for akaze 354 | struct L1_61bytes:public Lx{ 355 | inline uint64_t computeDist(uint64_t *feat_ptr){ 356 | 357 | return uint64_popcnt(feat_ptr[0]^feature[0])+ uint64_popcnt(feat_ptr[1]^feature[1])+ 358 | uint64_popcnt(feat_ptr[2]^feature[2])+uint64_popcnt(feat_ptr[3]^feature[3])+ 359 | uint64_popcnt(feat_ptr[4]^feature[4])+uint64_popcnt(feat_ptr[5]^feature[5])+ 360 | uint64_popcnt(feat_ptr[6]^feature[6])+uint64_popcnt(feat_ptr[7]^feature[7]); 361 | } 362 | inline uint64_t uint64_popcnt(uint64_t n) { 363 | return std::bitset<64>(n).count(); 364 | } 365 | }; 366 | 367 | 368 | template 369 | fBow _transform(const cv::Mat &features){ 370 | Computer comp; 371 | comp.setParams(_params._desc_size,_params._desc_size_bytes_wp); 372 | using DType=typename Computer::DType;//distance type 373 | using TData=typename Computer::TData;//data type 374 | 375 | fBow result; 376 | std::pair best_dist_idx(std::numeric_limits::max(),0);//minimum distance found 377 | block_node_info *bn_info; 378 | for(int cur_feature=0;cur_feature(cur_feature)); 380 | //ensure feature is in a 381 | Block c_block=getBlock(0); 382 | //copy to another structure and add padding with zeros 383 | do{ 384 | //given the current block, finds the node with minimum distance 385 | best_dist_idx.first=std::numeric_limits::max(); 386 | for(int cur_node=0;cur_node(cur_node)); 389 | if (disleaf()){//if the node is leaf get word id and weight 394 | result[bn_info->getId()]+=bn_info->weight; 395 | } 396 | else setBlock(bn_info->getId(),c_block);//go to its children 397 | }while( !bn_info->isleaf() && bn_info->getId()!=0); 398 | } 399 | return result; 400 | } 401 | template 402 | void _transform2(const cv::Mat &features,uint32_t storeLevel,fBow &r1,fBow2 &r2){ 403 | Computer comp; 404 | comp.setParams(_params._desc_size,_params._desc_size_bytes_wp); 405 | using DType=typename Computer::DType;//distance type 406 | using TData=typename Computer::TData;//data type 407 | 408 | r1.clear(); 409 | r2.clear(); 410 | std::pair best_dist_idx(std::numeric_limits::max(),0);//minimum distance found 411 | block_node_info *bn_info; 412 | int nbits=ceil(log2(_params._m_k)); 413 | for(int cur_feature=0;cur_feature(cur_feature)); 415 | //ensure feature is in a 416 | Block c_block=getBlock(0); 417 | uint32_t level=0;//current level of recursion 418 | uint32_t curNode=0;//id of the current node of the tree 419 | //copy to another structure and add padding with zeros 420 | do{ 421 | //given the current block, finds the node with minimum distance 422 | best_dist_idx.first=std::numeric_limits::max(); 423 | for(int cur_node=0;cur_node(cur_node)); 426 | if (disleaf()){ 434 | r1[bn_info->getId()]+=bn_info->weight; 435 | if( levelgetId(),c_block);//go to its children 440 | curNode= curNode<isleaf() && bn_info->getId()!=0); 444 | } 445 | } 446 | 447 | }; 448 | 449 | 450 | } 451 | #endif 452 | -------------------------------------------------------------------------------- /src/fbow_exports.h: -------------------------------------------------------------------------------- 1 | /***************************** 2 | Copyright 2014 Rafael Muñoz Salinas. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are 5 | permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of 8 | conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 11 | of conditions and the following disclaimer in the documentation and/or other materials 12 | provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY Rafael Muñoz Salinas ''AS IS'' AND ANY EXPRESS OR IMPLIED 15 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 16 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Rafael Muñoz Salinas OR 17 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 21 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those of the 25 | authors and should not be interpreted as representing official policies, either expressed 26 | or implied, of Rafael Muñoz Salinas. 27 | ********************************/ 28 | 29 | 30 | 31 | #ifndef __FBOW_CORE_TYPES_H__ 32 | #define __FBOW_CORE_TYPES_H__ 33 | 34 | #if !defined _CRT_SECURE_NO_DEPRECATE && _MSC_VER > 1300 35 | #define _CRT_SECURE_NO_DEPRECATE /* to avoid multiple Visual Studio 2005 warnings */ 36 | #endif 37 | 38 | #if (defined WIN32 || defined _WIN32 || defined WINCE) && defined FBOW_DSO_EXPORTS 39 | #define FBOW_API __declspec(dllexport) 40 | #pragma warning ( disable : 4251 ) //disable warning to templates with dll linkage. 41 | #pragma warning ( disable : 4290 ) //disable warning due to exception specifications. 42 | #pragma warning ( disable : 4996 ) //disable warning regarding unsafe vsprintf. 43 | #pragma warning ( disable : 4244 ) //disable warning convesions with lost of data. 44 | 45 | #else 46 | #define FBOW_API 47 | #endif 48 | 49 | 50 | #define FBOW_VERSION "${PROJECT_VERSION}" 51 | #endif 52 | -------------------------------------------------------------------------------- /src/vocabulary_creator.cpp: -------------------------------------------------------------------------------- 1 | #include "vocabulary_creator.h" 2 | #ifdef USE_OPENMP 3 | #include 4 | #else 5 | inline int omp_get_max_threads(){return 1;} 6 | inline int omp_get_thread_num(){return 0;} 7 | #endif 8 | #include 9 | using namespace std; 10 | namespace fbow{ 11 | 12 | void VocabularyCreator::create(fbow::Vocabulary &Voc, const cv::Mat &features, const std::string &desc_name, Params params) 13 | { 14 | std::vector vfeatures(1); 15 | vfeatures[0]=features; 16 | create(Voc,vfeatures,desc_name,params); 17 | } 18 | 19 | void VocabularyCreator::create(fbow::Vocabulary &Voc, const std::vector &features, const string &desc_name, Params params){ 20 | assert(features.size()>0); 21 | assert(features[0].cols>0); 22 | //select the funciton 23 | _params=params; 24 | _descCols=features[0].cols; 25 | _descType=features[0].type(); 26 | _descNBytes=features[0].cols* features[0].elemSize(); 27 | _params.nthreads=std::min(maxthreads,_params.nthreads); 28 | 29 | if(!(_descType==CV_8UC1|| _descType==CV_32FC1)) 30 | throw std::runtime_error("Descriptors must be binary CV_8UC1 or float CV_32FC1"); 31 | if (_descType==CV_8UC1){ 32 | if (_descNBytes==32) 33 | dist_func=distance_hamming_32bytes; 34 | else 35 | dist_func=distance_hamming_generic; 36 | } 37 | else dist_func=distance_float_generic; 38 | //create for later usage 39 | _features.create(features); 40 | 41 | //set all indices for the first level 42 | id_assigments.create(0,_features.size()); 43 | auto root_assign=id_assigments[0]; 44 | root_assign->resize (_features.size()); 45 | for(size_t i=0;i<_features.size();i++) root_assign->at(i)=i; 46 | 47 | if(_params.nthreads>1){ 48 | createLevel(0,0,false); 49 | //now, add threads 50 | 51 | for(auto &t:threadRunning)t=false; 52 | for(size_t i=0;i<_params.nthreads;i++) 53 | _Threads.push_back(std::thread(&VocabularyCreator::thread_consumer,this,i)); 54 | int ntimes=0; 55 | while(ntimes++<10){ 56 | for(auto &t:threadRunning) if (t){ ntimes=0;break;} 57 | std::this_thread::sleep_for(std::chrono::microseconds(600)); 58 | } 59 | 60 | //add exit info 61 | for(size_t i=0;i<_Threads.size();i++) ParentDepth_ProcesQueue.push(std::make_pair(-1,-1)); 62 | for(std::thread &th:_Threads) th.join(); 63 | } 64 | else{ 65 | createLevel(0,0,true); 66 | } 67 | // std::cout<=0) 84 | createLevel(pair.first,pair.second,false); 85 | else done=true; 86 | } 87 | threadRunning[idx]=false; 88 | } 89 | 90 | 91 | //ready to be threaded using producer consumer 92 | void VocabularyCreator::createLevel( int parent, int curL,bool recursive){ 93 | std::vector center_features; 94 | std::vector assigments_ref; 95 | assert(id_assigments.count(parent)); 96 | const auto &findices=*id_assigments[parent]; 97 | //trivial case, less features or equal than k (these are leaves) 98 | if ( findices.size()<=_params.k){ 99 | for(auto fi:findices) 100 | center_features.push_back( _features[fi] ); 101 | } 102 | else{ 103 | //create the assigment vectors and reserve memory 104 | const auto &parent_assign=id_assigments[parent]; 105 | for(size_t i=0;i<_params.k;i++){ 106 | id_assigments.create( parent*_params.k+1+i,findices.size()/_params.k); 107 | assigments_ref.push_back( id_assigments[ parent*_params.k+1+i]); 108 | } 109 | 110 | //initialize clusters 111 | auto centers=getInitialClusterCenters(findices ); 112 | center_features.resize(centers.size()); 113 | for(size_t i=0;i new_nodes; 135 | new_nodes.reserve(center_features.size()); 136 | { 137 | for(size_t c=0;c::max())); 139 | } 140 | TheTree.add(new_nodes,parent); 141 | //we can now remove the assigments of the parent 142 | id_assigments.erase(parent); 143 | // std::cout<<"parent "<0){ 147 | assert(assigments_ref.size()==new_nodes.size()); 148 | //go deeper again or add to queue 149 | if (recursive){//recursive mode(one thread only) 150 | for(size_t i=0;i VocabularyCreator::getInitialClusterCenters(const std::vector &findices ) 162 | { 163 | 164 | //set distances to zero 165 | 166 | for(auto fi:findices) _features(fi).m_Dist=0; 167 | 168 | std::vector centers; 169 | centers.reserve(_params.k); 170 | 171 | // 1.Choose one center uniformly at random from among the data points. 172 | uint32_t ifeature = findices[rand()% findices.size()]; 173 | // create first cluster 174 | centers.push_back(ifeature); 175 | do{ 176 | // add the distance to the new cluster and select the farthest one 177 | auto last_center_feat=_features[centers.back()]; 178 | std::pair farthest(0,std::numeric_limits::min()); 179 | for(auto fi:findices){ 180 | auto &feature=_features(fi); 181 | feature.m_Dist+=dist_func(last_center_feat, _features[fi]); 182 | if (feature.m_Dist>farthest.second)//found a farthest one 183 | farthest=std::make_pair(fi,feature.m_Dist); 184 | } 185 | ifeature=farthest.first; 186 | centers.push_back(ifeature); 187 | }while( centers.size() <_params.k); 188 | return centers; 189 | } 190 | 191 | std::size_t VocabularyCreator::vhash(const std::vector > & v_vec) { 192 | std::size_t seed = 0; 193 | 194 | for(size_t i=0;i> 2); 199 | 200 | return seed; 201 | } 202 | std::size_t VocabularyCreator::vhash(const std::vector &v_vec) { 203 | std::size_t seed = 0; 204 | 205 | for(size_t i=0;isize()*(i+1); 206 | 207 | for(auto& vec : v_vec) 208 | for(auto& i : *vec) 209 | seed ^= i + 0x9e3779b9 + (seed << 6) + (seed >> 2); 210 | 211 | return seed; 212 | } 213 | 214 | 215 | 216 | void VocabularyCreator::assignToClusters( const std::vector &findices, const std::vector ¢er_features,std::vector &assigments,bool omp){ 217 | for(auto &a:assigments) a->clear(); 218 | if(omp ){ 219 | std::vector > >map_assigments_omp(omp_get_max_threads()); 220 | #pragma omp parallel for 221 | for(int i=0;i< int(findices.size());i++){ 222 | auto tid=omp_get_thread_num(); 223 | auto fi=findices[i]; 224 | const auto &feature=_features[fi]; 225 | std::pair center_dist_min(0,dist_func(center_features[0],feature)); 226 | for(size_t ci=1;cipush_back(fi); 232 | } 233 | //gather all assignments in output 234 | for(const auto &mas_tid:map_assigments_omp){ 235 | for(const auto &c_assl:mas_tid){ 236 | for(const auto &id:c_assl.second) 237 | assigments[c_assl.first]->push_back(id); 238 | } 239 | } 240 | } 241 | else{ 242 | 243 | for(auto fi: findices){ 244 | const auto &feature=_features[fi]; 245 | std::pair center_dist_min(0,dist_func(center_features[0],feature)); 246 | for(size_t ci=1;cipush_back(fi); 251 | } 252 | } 253 | //check 254 | // for(int i=0;ibegin(),assigments[j]->end(),c)==assigments[j]->end()); 259 | // } 260 | // } 261 | } 262 | /** 263 | * @brief VocabularyCreator::recomputeCenters 264 | * @param findices 265 | * @param features 266 | * @param assigments 267 | * @return 268 | */ 269 | 270 | 271 | std::vector VocabularyCreator::recomputeCenters( const std::vector &assigments,bool omp){ 272 | std::vector centers; 273 | if (omp){ 274 | centers.resize(assigments.size()); 275 | #pragma omp parallel for 276 | for(int i=0;i &indices) 291 | { 292 | 293 | //determine number of bytes of the binary descriptor 294 | std::vector sum( _descNBytes * 8, 0); 295 | 296 | for(auto i:indices) 297 | { 298 | const unsigned char *p = _features[i].ptr(); 299 | for(int j = 0; j < _descCols; ++j, ++p) 300 | { 301 | if(*p & 128) ++sum[ j*8 ]; 302 | if(*p & 64) ++sum[ j*8 + 1 ]; 303 | if(*p & 32) ++sum[ j*8 + 2 ]; 304 | if(*p & 16) ++sum[ j*8 + 3 ]; 305 | if(*p & 8) ++sum[ j*8 + 4 ]; 306 | if(*p & 4) ++sum[ j*8 + 5 ]; 307 | if(*p & 2) ++sum[ j*8 + 6 ]; 308 | if(*p & 1) ++sum[ j*8 + 7 ]; 309 | } 310 | } 311 | 312 | cv::Mat mean = cv::Mat::zeros(1, _descNBytes, CV_8U); 313 | unsigned char *p = mean.ptr(); 314 | 315 | const int N2 = (int)indices.size() / 2 + indices.size() % 2; 316 | for(size_t i = 0; i < sum.size(); ++i) 317 | { 318 | // set bit 319 | if(sum[i] >= N2) *p |= 1 << (7 - (i % 8)); 320 | if(i % 8 == 7) ++p; 321 | } 322 | return mean; 323 | } 324 | 325 | cv::Mat VocabularyCreator::meanValue_float( const std::vector &indices){ 326 | cv::Mat mean(1,_descCols,_descType); 327 | mean.setTo(cv::Scalar::all(0)); 328 | for(auto i:indices) mean += _features[i] ; 329 | mean*= 1./double( indices.size()); 330 | 331 | return mean; 332 | } 333 | 334 | 335 | void VocabularyCreator::convertIntoVoc(Vocabulary &Voc, std::string desc_name){ 336 | 337 | //look for leafs and store 338 | //now, create the blocks 339 | 340 | uint32_t nLeafNodes=0; 341 | uint32_t nonLeafNodes=0; 342 | std::map nodeid_blockid; 343 | for(auto &node:TheTree.getNodes()){ 344 | if(node.second.isLeaf()) 345 | { 346 | //assing an id if not set 347 | if ( node.second.feat_idx==std::numeric_limits::max()) node.second.feat_idx=nLeafNodes; 348 | nLeafNodes++; 349 | } 350 | else nodeid_blockid.insert(std::make_pair(node.first,nonLeafNodes++)); 351 | 352 | } 353 | //determine the basic elements 354 | Voc.clear(); 355 | int aligment=8; 356 | if (_descType==CV_32F) aligment=32; 357 | Voc.setParams(aligment,_params.k,_descType,_descNBytes,nonLeafNodes,desc_name); 358 | 359 | //lets start 360 | for(auto &node:TheTree.getNodes()){ 361 | if (!node.second.isLeaf()){ 362 | auto binfo=Voc.getBlock(nodeid_blockid[node.first]); 363 | binfo.setN(node.second.children.size()); 364 | binfo.setParentId(node.first); 365 | bool areAllChildrenLeaf=true; 366 | for(size_t c=0;c< node.second.children.size();c++){ 367 | Node &child=TheTree.getNodes()[node.second.children[c]]; 368 | binfo.setFeature(c,child.feature); 369 | //go to the end and set info 370 | if (child.isLeaf()) binfo.getBlockNodeInfo(c)->setLeaf(child.feat_idx,child.weight); 371 | else { 372 | binfo.getBlockNodeInfo(c)->setNonLeaf(nodeid_blockid[child.id]); 373 | areAllChildrenLeaf=false; 374 | } 375 | } 376 | binfo.setLeaf(areAllChildrenLeaf); 377 | } 378 | } 379 | } 380 | float VocabularyCreator::distance_hamming_generic(const cv::Mat &a, const cv::Mat &b){ 381 | uint64_t ret=0; 382 | const uchar *pa = a.ptr(); // a & b are actually CV_8U 383 | const uchar *pb = b.ptr(); 384 | for(int i=0;i(); // a & b are actually CV_8U 405 | const uint64_t *pb = b.ptr(); 406 | return uint64_popcnt(pa[0]^pb[0])+ uint64_popcnt(pa[1]^pb[1])+ uint64_popcnt(pa[2]^pb[2])+uint64_popcnt(pa[3]^pb[3]); 407 | } 408 | float VocabularyCreator::distance_float_generic(const cv::Mat &a, const cv::Mat &b){ 409 | double sqd = 0.; 410 | const float *a_ptr=a.ptr(0); 411 | const float *b_ptr=b.ptr(0); 412 | for(int i = 0; i < a.cols; i ++) sqd += (a_ptr[i ] - b_ptr[i ])*(a_ptr[i ] - b_ptr[i ]); 413 | return sqd; 414 | } 415 | 416 | 417 | } 418 | 419 | -------------------------------------------------------------------------------- /src/vocabulary_creator.h: -------------------------------------------------------------------------------- 1 | #ifndef _FBOW_VOCABULARYCREATOR_H 2 | #define _FBOW_VOCABULARYCREATOR_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "fbow_exports.h" 16 | #include "fbow.h" 17 | namespace fbow{ 18 | /**This class creates the vocabulary 19 | */ 20 | class FBOW_API VocabularyCreator 21 | { 22 | public: 23 | struct Params{ 24 | Params(){} 25 | Params(uint32_t K ,int l=-1,uint32_t Nthreads=1,int MaxIters=-2):k(K),L(l),nthreads(Nthreads){ 26 | if ( MaxIters!=-2) maxIters=MaxIters; 27 | } 28 | uint32_t k=32; 29 | int L=-1; 30 | uint32_t nthreads=1; 31 | int maxIters=11; 32 | bool verbose=false; 33 | }; 34 | 35 | //create this from a set of features 36 | //Voc resulting vocabulary 37 | //features: vector of features. Each matrix represents the features of an image. 38 | //k braching factor 39 | //L maximum tree depth 40 | 41 | void create(fbow::Vocabulary &Voc, const std::vector &features, const std::string &desc_name, Params params); 42 | void create(fbow::Vocabulary &Voc, const cv::Mat &features, const std::string &desc_name, Params params); 43 | private: 44 | Params _params; 45 | struct feature_info{ 46 | feature_info(uint32_t MIDX,uint32_t FIDX):midx(MIDX),fidx(FIDX){} 47 | uint32_t midx,fidx;//matrix feature into the vector of matrices and fidx feature into the matrix 48 | float m_Dist; 49 | uint32_t parent=0; 50 | }; 51 | //struct to acces the features as a unique vector 52 | struct FeatureInfo{ 53 | void create(const std::vector &f) 54 | { 55 | features=(std::vector*)&f; 56 | uint32_t _size=0; 57 | for(auto &m:*features) _size+=m.rows; 58 | fInfo.clear(); 59 | fInfo.reserve(_size); 60 | for(size_t midx=0;midxsize();midx++){ 61 | auto nrows=features->at(midx).rows; 62 | for(int i=0;i 68 | inline T*getFeaturePtr(uint32_t i){const auto &idx=fInfo[i]; return features->at(idx.midx).ptr(idx.fidx);} 69 | inline cv::Mat operator[](uint32_t i){const auto &idx=fInfo[i]; return features->at(idx.midx).row(idx.fidx);} 70 | inline feature_info & operator()(uint32_t i){ return fInfo[i];} 71 | 72 | 73 | std::vector *features; 74 | std::vector fInfo; 75 | }; 76 | 77 | 78 | 79 | cv::Mat meanValue_binary( const std::vector &indices); 80 | cv::Mat meanValue_float( const std::vector &indices); 81 | 82 | void createLevel(const std::vector &findices, int parent=0, int curL=0); 83 | void createLevel(int parent=0, int curL=0, bool recursive=true); 84 | std::vector getInitialClusterCenters(const std::vector &findices); 85 | 86 | std::size_t vhash(const std::vector >& v_vec) ; 87 | 88 | 89 | void thread_consumer(int idx); 90 | //thread sage queue to implement producer-consumer 91 | template 92 | class Queue 93 | { 94 | public: 95 | 96 | T pop() 97 | { 98 | std::unique_lock mlock(mutex_); 99 | while (queue_.empty()) 100 | { 101 | cond_.wait(mlock); 102 | } 103 | auto item = queue_.front(); 104 | queue_.pop(); 105 | return item; 106 | } 107 | 108 | void push(const T& item) 109 | { 110 | std::unique_lock mlock(mutex_); 111 | queue_.push(item); 112 | mlock.unlock(); 113 | cond_.notify_one(); 114 | } 115 | 116 | size_t size() 117 | { 118 | std::unique_lock mlock(mutex_); 119 | size_t s=queue_.size(); 120 | return s; 121 | } 122 | private: 123 | std::queue queue_; 124 | std::mutex mutex_; 125 | std::condition_variable cond_; 126 | }; 127 | 128 | Queue > ParentDepth_ProcesQueue;//queue of parent to be processed 129 | //used to retain the distance between each pair of nodes 130 | 131 | inline uint64_t join(uint32_t a ,uint32_t b){ 132 | uint64_t a_b; 133 | uint32_t *_a_b_16=(uint32_t*)&a_b; 134 | if( a>b) { 135 | _a_b_16[0]=b;_a_b_16[1]=a; 136 | } 137 | else{ 138 | _a_b_16[1]=b;_a_b_16[0]=a; 139 | } 140 | return a_b; 141 | } 142 | inline std::pair separe(uint64_t a_b){ uint32_t *_a_b_16=(uint32_t*)&a_b;return std::make_pair(_a_b_16[1],_a_b_16[0]);} 143 | 144 | //for each pair of nodes, their distance 145 | // std::map features_distance; 146 | 147 | // 148 | struct Node{ 149 | Node(){} 150 | Node(uint32_t Id,uint32_t Parent,const cv::Mat &Feature, uint32_t Feat_idx=std::numeric_limits::max() ):id(Id),parent(Parent),feature(Feature),feat_idx(Feat_idx){ 151 | 152 | } 153 | 154 | uint32_t id=std::numeric_limits::max();//id of this node in the tree 155 | uint32_t parent=std::numeric_limits::max();//id of the parent node 156 | cv::Mat feature;//feature of this node 157 | //index of the feature this node represent(only if leaf and it stop because not enough points to create a new leave. 158 | //In case the node is a terminal point, but has many points beloging to its cluster, then, this is not set. 159 | //In other words, it is only used in nn search problems where L=-1 160 | 161 | uint32_t feat_idx=std::numeric_limits::max(); 162 | std::vector children; 163 | bool isLeaf()const{return children.size()==0;} 164 | //if leaf, its weight and the word id 165 | float weight=1; 166 | }; 167 | 168 | 169 | 170 | class Tree{ 171 | public: 172 | Tree(){ 173 | //add parent 174 | Node n;n.id=0; 175 | _nodes.insert(std::make_pair(0,n)); 176 | } 177 | void add(const std::vector &new_nodes,int parentId){ 178 | std::unique_lock mlock(mutex_); 179 | assert(_nodes.count(parentId)); 180 | Node &parent=_nodes[parentId]; 181 | parent.children.reserve(new_nodes.size()); 182 | for(auto &n:new_nodes){ 183 | _nodes.insert(std::make_pair(n.id,n)); 184 | parent.children.push_back(n.id); 185 | } 186 | } 187 | //not thread safe 188 | size_t size()const{return _nodes.size();} 189 | std::map &getNodes(){return _nodes;} 190 | private: 191 | std::map _nodes; 192 | std::mutex mutex_; 193 | 194 | }; 195 | 196 | using vector_sptr=std::shared_ptr< std::vector>; 197 | struct ThreadSafeMap{ 198 | 199 | void create(uint32_t parent,uint32_t reserved_size){ 200 | std::unique_lock mlock(mutex_); 201 | if(!parents_idx.count(parent)){ 202 | parents_idx[parent]=std::make_shared>(); 203 | parents_idx[parent]->reserve(reserved_size); 204 | } 205 | } 206 | 207 | void erase(uint32_t parent){ 208 | std::unique_lock mlock(mutex_); 209 | assert(parents_idx.count(parent)); 210 | parents_idx.erase(parent); 211 | 212 | } 213 | 214 | vector_sptr operator[](uint32_t parent){ 215 | std::unique_lock mlock(mutex_); 216 | assert(parents_idx.count(parent)); 217 | return parents_idx[parent]; 218 | } 219 | 220 | size_t count(uint32_t parent) { 221 | std::unique_lock mlock(mutex_); 222 | return parents_idx.count(parent); 223 | } 224 | std::mutex mutex_; 225 | std::map parents_idx; 226 | 227 | }; 228 | 229 | 230 | Tree TheTree; 231 | 232 | int _descCols,_descType,_descNBytes; 233 | FeatureInfo _features; 234 | void assignToClusters(const std::vector &findices, const std::vector ¢er_features, std::vector &assigments, bool omp=false); 235 | std::vector recomputeCenters(const std::vector &assigments, bool omp=false); 236 | std::size_t vhash(const std::vector& v_vec) ; 237 | 238 | //std::map > id_assigments;//for each node, its assigment vector 239 | 240 | ThreadSafeMap id_assigments; 241 | std::vector _Threads; 242 | 243 | const uint32_t maxthreads =100; 244 | std::atomic threadRunning[100];//do not know how to create dinamically :S 245 | 246 | 247 | //------------ 248 | void convertIntoVoc(Vocabulary &Voc, std::string dec_name); 249 | 250 | 251 | /** 252 | * Calculates the distance between two descriptors 253 | * @param a 254 | * @param b 255 | * @return distance 256 | */ 257 | static float distance_float_generic(const cv::Mat &a, const cv::Mat &b); 258 | static float distance_hamming_generic(const cv::Mat &a, const cv::Mat &b); 259 | static float distance_hamming_32bytes(const cv::Mat &a, const cv::Mat &b); 260 | std::function dist_func; 261 | static inline uint64_t uint64_popcnt(uint64_t v) { 262 | v = v - ((v >> 1) & (uint64_t)~(uint64_t)0/3); 263 | v = (v & (uint64_t)~(uint64_t)0/15*3) + ((v >> 2) & (uint64_t)~(uint64_t)0/15*3); 264 | v = (v + (v >> 4)) & (uint64_t)~(uint64_t)0/255*15; 265 | return (uint64_t)(v * ((uint64_t)~(uint64_t)0/255)) >> (sizeof(uint64_t) - 1) * 8; 266 | } 267 | }; 268 | } 269 | #endif 270 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMake file for sba's demo program 2 | INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src ) 3 | LINK_LIBRARIES(${PROJECT_NAME}) 4 | 5 | 6 | ADD_EXECUTABLE(fbow_test_dbow2VSfbow test_dbow2VSfbow.cpp dbow2/ScoringObject.cpp dbow2/BowVector.h dbow2/FClass.h dbow2/FeatureVector.h dbow2/FORB.h dbow2/ScoringObject.h dbow2/TemplatedVocabulary.h ) 7 | ADD_EXECUTABLE(fbow_test_cpu_x86 test_cpu_x86.cpp ) 8 | INSTALL(TARGETS fbow_test_cpu_x86 RUNTIME DESTINATION bin) 9 | 10 | -------------------------------------------------------------------------------- /tests/README_DBOW2: -------------------------------------------------------------------------------- 1 | DBOW2 version extracted from ORBSLAM2 and removed DLIB dependency 2 | -------------------------------------------------------------------------------- /tests/dbow2/BowVector.h: -------------------------------------------------------------------------------- 1 | /** 2 | * File: BowVector.h 3 | * Date: March 2011 4 | * Author: Dorian Galvez-Lopez 5 | * Description: bag of words vector 6 | * License: see the LICENSE.txt file 7 | * 8 | */ 9 | 10 | #ifndef __D_T_BOW_VECTOR__ 11 | #define __D_T_BOW_VECTOR__ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace DBoW2 { 18 | 19 | /// Id of words 20 | typedef unsigned int WordId; 21 | 22 | /// Value of a word 23 | typedef double WordValue; 24 | 25 | /// Id of nodes in the vocabulary treee 26 | typedef unsigned int NodeId; 27 | 28 | /// L-norms for normalization 29 | enum LNorm 30 | { 31 | L1, 32 | L2 33 | }; 34 | 35 | /// Weighting type 36 | enum WeightingType 37 | { 38 | TF_IDF, 39 | TF, 40 | IDF, 41 | BINARY 42 | }; 43 | 44 | /// Scoring type 45 | enum ScoringType 46 | { 47 | L1_NORM, 48 | L2_NORM, 49 | CHI_SQUARE, 50 | KL, 51 | BHATTACHARYYA, 52 | DOT_PRODUCT, 53 | }; 54 | 55 | /// Vector of words to represent images 56 | class BowVector: 57 | public std::map 58 | { 59 | public: 60 | 61 | /** 62 | * Constructor 63 | */ 64 | BowVector(void){} 65 | 66 | /** 67 | * Destructor 68 | */ 69 | ~BowVector(void){} 70 | 71 | /** 72 | * Adds a value to a word value existing in the vector, or creates a new 73 | * word with the given value 74 | * @param id word id to look for 75 | * @param v value to create the word with, or to add to existing word 76 | */ 77 | void addWeight(WordId id, WordValue v){ 78 | BowVector::iterator vit = this->lower_bound(id); 79 | 80 | if(vit != this->end() && !(this->key_comp()(id, vit->first))) 81 | { 82 | vit->second += v; 83 | } 84 | else 85 | { 86 | this->insert(vit, BowVector::value_type(id, v)); 87 | } 88 | } 89 | 90 | /** 91 | * Adds a word with a value to the vector only if this does not exist yet 92 | * @param id word id to look for 93 | * @param v value to give to the word if this does not exist 94 | */ 95 | void addIfNotExist(WordId id, WordValue v) 96 | { 97 | BowVector::iterator vit = this->lower_bound(id); 98 | 99 | if(vit == this->end() || (this->key_comp()(id, vit->first))) 100 | { 101 | this->insert(vit, BowVector::value_type(id, v)); 102 | } 103 | } 104 | 105 | /** 106 | * L1-Normalizes the values in the vector 107 | * @param norm_type norm used 108 | */ 109 | void normalize(LNorm norm_type) 110 | { 111 | double norm = 0.0; 112 | BowVector::iterator it; 113 | 114 | if(norm_type == DBoW2::L1) 115 | { 116 | for(it = begin(); it != end(); ++it) 117 | norm += fabs(it->second); 118 | } 119 | else 120 | { 121 | for(it = begin(); it != end(); ++it) 122 | norm += it->second * it->second; 123 | norm = sqrt(norm); 124 | } 125 | 126 | if(norm > 0.0) 127 | { 128 | for(it = begin(); it != end(); ++it) 129 | it->second /= norm; 130 | } 131 | } 132 | 133 | 134 | /** 135 | * Prints the content of the bow vector 136 | * @param out stream 137 | * @param v 138 | */ 139 | friend std::ostream& operator<<(std::ostream &out, const BowVector &v) 140 | { 141 | BowVector::const_iterator vit; 142 | std::vector::const_iterator iit; 143 | unsigned int i = 0; 144 | const unsigned int N = v.size(); 145 | for(vit = v.begin(); vit != v.end(); ++vit, ++i) 146 | { 147 | out << "<" << vit->first << ", " << vit->second << ">"; 148 | 149 | if(i < N-1) out << ", "; 150 | } 151 | return out; 152 | } 153 | 154 | /** 155 | * Saves the bow vector as a vector in a matlab file 156 | * @param filename 157 | * @param W number of words in the vocabulary 158 | */ 159 | void saveM(const std::string &filename, size_t W) const 160 | { 161 | std::fstream f(filename.c_str(), std::ios::out); 162 | 163 | WordId last = 0; 164 | BowVector::const_iterator bit; 165 | for(bit = this->begin(); bit != this->end(); ++bit) 166 | { 167 | for(; last < bit->first; ++last) 168 | { 169 | f << "0 "; 170 | } 171 | f << bit->second << " "; 172 | 173 | last = bit->first + 1; 174 | } 175 | for(; last < (WordId)W; ++last) 176 | f << "0 "; 177 | 178 | f.close(); 179 | } 180 | }; 181 | 182 | } // namespace DBoW2 183 | 184 | #endif 185 | -------------------------------------------------------------------------------- /tests/dbow2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | INCLUDE_DIRECTORIES(. ${OpenCV_INCLUDE_DIRS}) 2 | 3 | FILE(GLOB hdrs_base "*.h" ) 4 | FILE(GLOB srcs_base "*.cpp") 5 | 6 | 7 | FILE(GLOB hdrs ${hdrs_base} ) 8 | FILE(GLOB srcs ${srcs_base} ) 9 | 10 | 11 | SET(LIBNAME dbow2) 12 | ADD_LIBRARY(${LIBNAME} ${srcs} ${hdrs}) 13 | MESSAGE(STATUS "dbow2 :${LIBNAME}: ${srcs} ${hdrs}") 14 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ) 15 | 16 | SET_TARGET_PROPERTIES(${LIBNAME} PROPERTIES # create *nix style library versions + symbolic links 17 | VERSION ${PROJECT_VERSION} 18 | SOVERSION ${PROJECT_SOVERSION} 19 | CLEAN_DIRECT_OUTPUT 1 # allow creating static and shared libs without conflicts 20 | OUTPUT_NAME "${LIBNAME}${PROJECT_DLLVERSION}" # avoid conflicts between library and binary target names 21 | ) 22 | 23 | #TARGET_LINK_LIBRARIES(${LIBNAME} ${REQUIRED_LIBRARIES} ) 24 | 25 | INSTALL(TARGETS ${LIBNAME} 26 | RUNTIME DESTINATION bin COMPONENT main # Install the dll file in bin directory 27 | LIBRARY DESTINATION lib PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE COMPONENT main 28 | ARCHIVE DESTINATION lib COMPONENT main) # Install the dll.a file in lib directory 29 | 30 | 31 | 32 | INSTALL(FILES ${hdrs_base} 33 | DESTINATION include/${LIBNAME} 34 | COMPONENT main) 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/dbow2/FClass.h: -------------------------------------------------------------------------------- 1 | /** 2 | * File: FClass.h 3 | * Date: November 2011 4 | * Author: Dorian Galvez-Lopez 5 | * Description: generic FClass to instantiate templated classes 6 | * License: see the LICENSE.txt file 7 | * 8 | */ 9 | 10 | #ifndef __D_T_FCLASS__ 11 | #define __D_T_FCLASS__ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace DBoW2 { 18 | 19 | /// Generic class to encapsulate functions to manage descriptors. 20 | /** 21 | * This class must be inherited. Derived classes can be used as the 22 | * parameter F when creating Templated structures 23 | * (TemplatedVocabulary, TemplatedDatabase, ...) 24 | */ 25 | class FClass 26 | { 27 | class TDescriptor; 28 | typedef const TDescriptor *pDescriptor; 29 | 30 | /** 31 | * Calculates the mean value of a set of descriptors 32 | * @param descriptors 33 | * @param mean mean descriptor 34 | */ 35 | virtual void meanValue(const std::vector &descriptors, 36 | TDescriptor &mean) = 0; 37 | 38 | /** 39 | * Calculates the distance between two descriptors 40 | * @param a 41 | * @param b 42 | * @return distance 43 | */ 44 | static double distance(const TDescriptor &a, const TDescriptor &b); 45 | 46 | /** 47 | * Returns a string version of the descriptor 48 | * @param a descriptor 49 | * @return string version 50 | */ 51 | static std::string toString(const TDescriptor &a); 52 | 53 | /** 54 | * Returns a descriptor from a string 55 | * @param a descriptor 56 | * @param s string version 57 | */ 58 | static void fromString(TDescriptor &a, const std::string &s); 59 | 60 | /** 61 | * Returns a mat with the descriptors in float format 62 | * @param descriptors 63 | * @param mat (out) NxL 32F matrix 64 | */ 65 | static void toMat32F(const std::vector &descriptors, 66 | cv::Mat &mat); 67 | }; 68 | 69 | } // namespace DBoW2 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /tests/dbow2/FORB.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * File: FORB.cpp 3 | * Date: June 2012 4 | * Author: Dorian Galvez-Lopez 5 | * Description: functions for ORB descriptors 6 | * License: see the LICENSE.txt file 7 | * 8 | * Distance function has been modified 9 | * 10 | */ 11 | 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "FORB.h" 19 | 20 | using namespace std; 21 | 22 | namespace DBoW2 { 23 | 24 | // -------------------------------------------------------------------------- 25 | 26 | } // namespace DBoW2 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/dbow2/FORB.h: -------------------------------------------------------------------------------- 1 | /** 2 | * File: FORB.h 3 | * Date: June 2012 4 | * Author: Dorian Galvez-Lopez 5 | * Description: functions for ORB descriptors 6 | * License: see the LICENSE.txt file 7 | * 8 | */ 9 | 10 | #ifndef __D_T_F_ORB__ 11 | #define __D_T_F_ORB__ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "FClass.h" 18 | namespace DBoW2 { 19 | 20 | /// Functions to manipulate ORB descriptors 21 | class FORB: protected FClass 22 | { 23 | public: 24 | 25 | /// Descriptor type 26 | typedef cv::Mat TDescriptor; // CV_8U 27 | /// Pointer to a single descriptor 28 | typedef const TDescriptor *pDescriptor; 29 | /// Descriptor length (in bytes) 30 | 31 | static inline int L() {return 32;} 32 | /** 33 | * Calculates the mean value of a set of descriptors 34 | * @param descriptors 35 | * @param mean mean descriptor 36 | */ 37 | static inline void meanValue(const std::vector &descriptors, 38 | TDescriptor &mean); 39 | 40 | /** 41 | * Calculates the distance between two descriptors 42 | * @param a 43 | * @param b 44 | * @return distance 45 | */ 46 | static inline int distance(const TDescriptor &a, const TDescriptor &b); 47 | 48 | /** 49 | * Returns a string version of the descriptor 50 | * @param a descriptor 51 | * @return string version 52 | */ 53 | static inline std::string toString(const TDescriptor &a); 54 | 55 | /** 56 | * Returns a descriptor from a string 57 | * @param a descriptor 58 | * @param s string version 59 | */ 60 | static inline void fromString(TDescriptor &a, const std::string &s); 61 | 62 | /** 63 | * Returns a mat with the descriptors in float format 64 | * @param descriptors 65 | * @param mat (out) NxL 32F matrix 66 | */ 67 | static inline void toMat32F(const std::vector &descriptors, 68 | cv::Mat &mat); 69 | 70 | static inline void toMat8U(const std::vector &descriptors, 71 | cv::Mat &mat); 72 | 73 | }; 74 | 75 | 76 | // -------------------------------------------------------------------------- 77 | 78 | 79 | void FORB::meanValue(const std::vector &descriptors, 80 | FORB::TDescriptor &mean) 81 | { 82 | if(descriptors.empty()) 83 | { 84 | mean.release(); 85 | return; 86 | } 87 | else if(descriptors.size() == 1) 88 | { 89 | mean = descriptors[0]->clone(); 90 | } 91 | else 92 | { 93 | std::vector sum(FORB::L() * 8, 0); 94 | 95 | for(size_t i = 0; i < descriptors.size(); ++i) 96 | { 97 | const cv::Mat &d = *descriptors[i]; 98 | const unsigned char *p = d.ptr(); 99 | 100 | for(int j = 0; j < d.cols; ++j, ++p) 101 | { 102 | if(*p & (1 << 7)) ++sum[ j*8 ]; 103 | if(*p & (1 << 6)) ++sum[ j*8 + 1 ]; 104 | if(*p & (1 << 5)) ++sum[ j*8 + 2 ]; 105 | if(*p & (1 << 4)) ++sum[ j*8 + 3 ]; 106 | if(*p & (1 << 3)) ++sum[ j*8 + 4 ]; 107 | if(*p & (1 << 2)) ++sum[ j*8 + 5 ]; 108 | if(*p & (1 << 1)) ++sum[ j*8 + 6 ]; 109 | if(*p & (1)) ++sum[ j*8 + 7 ]; 110 | } 111 | } 112 | 113 | mean = cv::Mat::zeros(1, FORB::L(), CV_8U); 114 | unsigned char *p = mean.ptr(); 115 | 116 | const int N2 = (int)descriptors.size() / 2 + descriptors.size() % 2; 117 | for(size_t i = 0; i < sum.size(); ++i) 118 | { 119 | if(sum[i] >= N2) 120 | { 121 | // set bit 122 | *p |= 1 << (7 - (i % 8)); 123 | } 124 | 125 | if(i % 8 == 7) ++p; 126 | } 127 | } 128 | } 129 | 130 | // -------------------------------------------------------------------------- 131 | 132 | int FORB::distance(const FORB::TDescriptor &a, 133 | const FORB::TDescriptor &b) 134 | { 135 | // Bit set count operation from 136 | // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel 137 | 138 | const int *pa = a.ptr(); 139 | const int *pb = b.ptr(); 140 | 141 | int dist=0; 142 | 143 | for(int i=0; i<8; i++, pa++, pb++) 144 | { 145 | unsigned int v = *pa ^ *pb; 146 | v = v - ((v >> 1) & 0x55555555); 147 | v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 148 | dist += (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; 149 | } 150 | 151 | return dist; 152 | } 153 | 154 | // -------------------------------------------------------------------------- 155 | 156 | std::string FORB::toString(const FORB::TDescriptor &a) 157 | { 158 | std::stringstream ss; 159 | const unsigned char *p = a.ptr(); 160 | 161 | for(int i = 0; i < a.cols; ++i, ++p) 162 | { 163 | ss << (int)*p << " "; 164 | } 165 | 166 | return ss.str(); 167 | } 168 | 169 | // -------------------------------------------------------------------------- 170 | 171 | void FORB::fromString(FORB::TDescriptor &a, const std::string &s) 172 | { 173 | a.create(1, FORB::L(), CV_8U); 174 | unsigned char *p = a.ptr(); 175 | 176 | std::stringstream ss(s); 177 | for(int i = 0; i < FORB::L(); ++i, ++p) 178 | { 179 | int n; 180 | ss >> n; 181 | 182 | if(!ss.fail()) 183 | *p = (unsigned char)n; 184 | } 185 | 186 | } 187 | 188 | // -------------------------------------------------------------------------- 189 | 190 | void FORB::toMat32F(const std::vector &descriptors, 191 | cv::Mat &mat) 192 | { 193 | if(descriptors.empty()) 194 | { 195 | mat.release(); 196 | return; 197 | } 198 | 199 | const size_t N = descriptors.size(); 200 | 201 | mat.create(N, FORB::L()*8, CV_32F); 202 | float *p = mat.ptr(); 203 | 204 | for(size_t i = 0; i < N; ++i) 205 | { 206 | const int C = descriptors[i].cols; 207 | const unsigned char *desc = descriptors[i].ptr(); 208 | 209 | for(int j = 0; j < C; ++j, p += 8) 210 | { 211 | p[0] = (desc[j] & (1 << 7) ? 1 : 0); 212 | p[1] = (desc[j] & (1 << 6) ? 1 : 0); 213 | p[2] = (desc[j] & (1 << 5) ? 1 : 0); 214 | p[3] = (desc[j] & (1 << 4) ? 1 : 0); 215 | p[4] = (desc[j] & (1 << 3) ? 1 : 0); 216 | p[5] = (desc[j] & (1 << 2) ? 1 : 0); 217 | p[6] = (desc[j] & (1 << 1) ? 1 : 0); 218 | p[7] = desc[j] & (1); 219 | } 220 | } 221 | } 222 | 223 | // -------------------------------------------------------------------------- 224 | 225 | void FORB::toMat8U(const std::vector &descriptors, 226 | cv::Mat &mat) 227 | { 228 | mat.create(descriptors.size(), 32, CV_8U); 229 | 230 | unsigned char *p = mat.ptr(); 231 | 232 | for(size_t i = 0; i < descriptors.size(); ++i, p += 32) 233 | { 234 | const unsigned char *d = descriptors[i].ptr(); 235 | std::copy(d, d+32, p); 236 | } 237 | 238 | } 239 | 240 | } // namespace DBoW2 241 | 242 | #endif 243 | 244 | -------------------------------------------------------------------------------- /tests/dbow2/FeatureVector.h: -------------------------------------------------------------------------------- 1 | /** 2 | * File: FeatureVector.h 3 | * Date: November 2011 4 | * Author: Dorian Galvez-Lopez 5 | * Description: feature vector 6 | * License: see the LICENSE.txt file 7 | * 8 | */ 9 | 10 | #ifndef __D_T_FEATURE_VECTOR__ 11 | #define __D_T_FEATURE_VECTOR__ 12 | 13 | #include "BowVector.h" 14 | #include 15 | #include 16 | #include 17 | 18 | namespace DBoW2 { 19 | 20 | /// Vector of nodes with indexes of local features 21 | class FeatureVector: 22 | public std::map > 23 | { 24 | public: 25 | 26 | /** 27 | * Constructor 28 | */ 29 | FeatureVector(void); 30 | 31 | /** 32 | * Destructor 33 | */ 34 | ~FeatureVector(void); 35 | 36 | /** 37 | * Adds a feature to an existing node, or adds a new node with an initial 38 | * feature 39 | * @param id node id to add or to modify 40 | * @param i_feature index of feature to add to the given node 41 | */ 42 | void addFeature(NodeId id, unsigned int i_feature){ 43 | FeatureVector::iterator vit = this->lower_bound(id); 44 | 45 | if(vit != this->end() && vit->first == id) 46 | { 47 | vit->second.push_back(i_feature); 48 | } 49 | else 50 | { 51 | vit = this->insert(vit, FeatureVector::value_type(id, 52 | std::vector() )); 53 | vit->second.push_back(i_feature); 54 | } 55 | } 56 | 57 | /** 58 | * Sends a string versions of the feature vector through the stream 59 | * @param out stream 60 | * @param v feature vector 61 | */ 62 | friend std::ostream& operator<<(std::ostream &out, const FeatureVector &v){ 63 | if(!v.empty()) 64 | { 65 | FeatureVector::const_iterator vit = v.begin(); 66 | 67 | const std::vector* f = &vit->second; 68 | 69 | out << "<" << vit->first << ": ["; 70 | if(!f->empty()) out << (*f)[0]; 71 | for(unsigned int i = 1; i < f->size(); ++i) 72 | { 73 | out << ", " << (*f)[i]; 74 | } 75 | out << "]>"; 76 | 77 | for(++vit; vit != v.end(); ++vit) 78 | { 79 | f = &vit->second; 80 | 81 | out << ", <" << vit->first << ": ["; 82 | if(!f->empty()) out << (*f)[0]; 83 | for(unsigned int i = 1; i < f->size(); ++i) 84 | { 85 | out << ", " << (*f)[i]; 86 | } 87 | out << "]>"; 88 | } 89 | } 90 | 91 | return out; 92 | } 93 | 94 | }; 95 | 96 | } // namespace DBoW2 97 | 98 | #endif 99 | 100 | -------------------------------------------------------------------------------- /tests/dbow2/ScoringObject.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * File: ScoringObject.cpp 3 | * Date: November 2011 4 | * Author: Dorian Galvez-Lopez 5 | * Description: functions to compute bow scores 6 | * License: see the LICENSE.txt file 7 | * 8 | */ 9 | 10 | #include 11 | #include "TemplatedVocabulary.h" 12 | #include "BowVector.h" 13 | 14 | using namespace DBoW2; 15 | 16 | // If you change the type of WordValue, make sure you change also the 17 | // epsilon value (this is needed by the KL method) 18 | const double GeneralScoring::LOG_EPS = log(DBL_EPSILON); // FLT_EPSILON 19 | 20 | // --------------------------------------------------------------------------- 21 | // --------------------------------------------------------------------------- 22 | 23 | double L1Scoring::score(const BowVector &v1, const BowVector &v2) const 24 | { 25 | BowVector::const_iterator v1_it, v2_it; 26 | const BowVector::const_iterator v1_end = v1.end(); 27 | const BowVector::const_iterator v2_end = v2.end(); 28 | 29 | v1_it = v1.begin(); 30 | v2_it = v2.begin(); 31 | 32 | double score = 0; 33 | 34 | while(v1_it != v1_end && v2_it != v2_end) 35 | { 36 | const WordValue& vi = v1_it->second; 37 | const WordValue& wi = v2_it->second; 38 | 39 | if(v1_it->first == v2_it->first) 40 | { 41 | score += fabs(vi - wi) - fabs(vi) - fabs(wi); 42 | 43 | // move v1 and v2 forward 44 | ++v1_it; 45 | ++v2_it; 46 | } 47 | else if(v1_it->first < v2_it->first) 48 | { 49 | // move v1 forward 50 | v1_it = v1.lower_bound(v2_it->first); 51 | // v1_it = (first element >= v2_it.id) 52 | } 53 | else 54 | { 55 | // move v2 forward 56 | v2_it = v2.lower_bound(v1_it->first); 57 | // v2_it = (first element >= v1_it.id) 58 | } 59 | } 60 | 61 | // ||v - w||_{L1} = 2 + Sum(|v_i - w_i| - |v_i| - |w_i|) 62 | // for all i | v_i != 0 and w_i != 0 63 | // (Nister, 2006) 64 | // scaled_||v - w||_{L1} = 1 - 0.5 * ||v - w||_{L1} 65 | score = -score/2.0; 66 | 67 | return score; // [0..1] 68 | } 69 | 70 | // --------------------------------------------------------------------------- 71 | // --------------------------------------------------------------------------- 72 | 73 | double L2Scoring::score(const BowVector &v1, const BowVector &v2) const 74 | { 75 | BowVector::const_iterator v1_it, v2_it; 76 | const BowVector::const_iterator v1_end = v1.end(); 77 | const BowVector::const_iterator v2_end = v2.end(); 78 | 79 | v1_it = v1.begin(); 80 | v2_it = v2.begin(); 81 | 82 | double score = 0; 83 | 84 | while(v1_it != v1_end && v2_it != v2_end) 85 | { 86 | const WordValue& vi = v1_it->second; 87 | const WordValue& wi = v2_it->second; 88 | 89 | if(v1_it->first == v2_it->first) 90 | { 91 | score += vi * wi; 92 | 93 | // move v1 and v2 forward 94 | ++v1_it; 95 | ++v2_it; 96 | } 97 | else if(v1_it->first < v2_it->first) 98 | { 99 | // move v1 forward 100 | v1_it = v1.lower_bound(v2_it->first); 101 | // v1_it = (first element >= v2_it.id) 102 | } 103 | else 104 | { 105 | // move v2 forward 106 | v2_it = v2.lower_bound(v1_it->first); 107 | // v2_it = (first element >= v1_it.id) 108 | } 109 | } 110 | 111 | // ||v - w||_{L2} = sqrt( 2 - 2 * Sum(v_i * w_i) ) 112 | // for all i | v_i != 0 and w_i != 0 ) 113 | // (Nister, 2006) 114 | if(score >= 1) // rounding errors 115 | score = 1.0; 116 | else 117 | score = 1.0 - sqrt(1.0 - score); // [0..1] 118 | 119 | return score; 120 | } 121 | 122 | // --------------------------------------------------------------------------- 123 | // --------------------------------------------------------------------------- 124 | 125 | double ChiSquareScoring::score(const BowVector &v1, const BowVector &v2) 126 | const 127 | { 128 | BowVector::const_iterator v1_it, v2_it; 129 | const BowVector::const_iterator v1_end = v1.end(); 130 | const BowVector::const_iterator v2_end = v2.end(); 131 | 132 | v1_it = v1.begin(); 133 | v2_it = v2.begin(); 134 | 135 | double score = 0; 136 | 137 | // all the items are taken into account 138 | 139 | while(v1_it != v1_end && v2_it != v2_end) 140 | { 141 | const WordValue& vi = v1_it->second; 142 | const WordValue& wi = v2_it->second; 143 | 144 | if(v1_it->first == v2_it->first) 145 | { 146 | // (v-w)^2/(v+w) - v - w = -4 vw/(v+w) 147 | // we move the -4 out 148 | if(vi + wi != 0.0) score += vi * wi / (vi + wi); 149 | 150 | // move v1 and v2 forward 151 | ++v1_it; 152 | ++v2_it; 153 | } 154 | else if(v1_it->first < v2_it->first) 155 | { 156 | // move v1 forward 157 | v1_it = v1.lower_bound(v2_it->first); 158 | } 159 | else 160 | { 161 | // move v2 forward 162 | v2_it = v2.lower_bound(v1_it->first); 163 | } 164 | } 165 | 166 | // this takes the -4 into account 167 | score = 2. * score; // [0..1] 168 | 169 | return score; 170 | } 171 | 172 | // --------------------------------------------------------------------------- 173 | // --------------------------------------------------------------------------- 174 | 175 | double KLScoring::score(const BowVector &v1, const BowVector &v2) const 176 | { 177 | BowVector::const_iterator v1_it, v2_it; 178 | const BowVector::const_iterator v1_end = v1.end(); 179 | const BowVector::const_iterator v2_end = v2.end(); 180 | 181 | v1_it = v1.begin(); 182 | v2_it = v2.begin(); 183 | 184 | double score = 0; 185 | 186 | // all the items or v are taken into account 187 | 188 | while(v1_it != v1_end && v2_it != v2_end) 189 | { 190 | const WordValue& vi = v1_it->second; 191 | const WordValue& wi = v2_it->second; 192 | 193 | if(v1_it->first == v2_it->first) 194 | { 195 | if(vi != 0 && wi != 0) score += vi * log(vi/wi); 196 | 197 | // move v1 and v2 forward 198 | ++v1_it; 199 | ++v2_it; 200 | } 201 | else if(v1_it->first < v2_it->first) 202 | { 203 | // move v1 forward 204 | score += vi * (log(vi) - LOG_EPS); 205 | ++v1_it; 206 | } 207 | else 208 | { 209 | // move v2_it forward, do not add any score 210 | v2_it = v2.lower_bound(v1_it->first); 211 | // v2_it = (first element >= v1_it.id) 212 | } 213 | } 214 | 215 | // sum rest of items of v 216 | for(; v1_it != v1_end; ++v1_it) 217 | if(v1_it->second != 0) 218 | score += v1_it->second * (log(v1_it->second) - LOG_EPS); 219 | 220 | return score; // cannot be scaled 221 | } 222 | 223 | // --------------------------------------------------------------------------- 224 | // --------------------------------------------------------------------------- 225 | 226 | double BhattacharyyaScoring::score(const BowVector &v1, 227 | const BowVector &v2) const 228 | { 229 | BowVector::const_iterator v1_it, v2_it; 230 | const BowVector::const_iterator v1_end = v1.end(); 231 | const BowVector::const_iterator v2_end = v2.end(); 232 | 233 | v1_it = v1.begin(); 234 | v2_it = v2.begin(); 235 | 236 | double score = 0; 237 | 238 | while(v1_it != v1_end && v2_it != v2_end) 239 | { 240 | const WordValue& vi = v1_it->second; 241 | const WordValue& wi = v2_it->second; 242 | 243 | if(v1_it->first == v2_it->first) 244 | { 245 | score += sqrt(vi * wi); 246 | 247 | // move v1 and v2 forward 248 | ++v1_it; 249 | ++v2_it; 250 | } 251 | else if(v1_it->first < v2_it->first) 252 | { 253 | // move v1 forward 254 | v1_it = v1.lower_bound(v2_it->first); 255 | // v1_it = (first element >= v2_it.id) 256 | } 257 | else 258 | { 259 | // move v2 forward 260 | v2_it = v2.lower_bound(v1_it->first); 261 | // v2_it = (first element >= v1_it.id) 262 | } 263 | } 264 | 265 | return score; // already scaled 266 | } 267 | 268 | // --------------------------------------------------------------------------- 269 | // --------------------------------------------------------------------------- 270 | 271 | double DotProductScoring::score(const BowVector &v1, 272 | const BowVector &v2) const 273 | { 274 | BowVector::const_iterator v1_it, v2_it; 275 | const BowVector::const_iterator v1_end = v1.end(); 276 | const BowVector::const_iterator v2_end = v2.end(); 277 | 278 | v1_it = v1.begin(); 279 | v2_it = v2.begin(); 280 | 281 | double score = 0; 282 | 283 | while(v1_it != v1_end && v2_it != v2_end) 284 | { 285 | const WordValue& vi = v1_it->second; 286 | const WordValue& wi = v2_it->second; 287 | 288 | if(v1_it->first == v2_it->first) 289 | { 290 | score += vi * wi; 291 | 292 | // move v1 and v2 forward 293 | ++v1_it; 294 | ++v2_it; 295 | } 296 | else if(v1_it->first < v2_it->first) 297 | { 298 | // move v1 forward 299 | v1_it = v1.lower_bound(v2_it->first); 300 | // v1_it = (first element >= v2_it.id) 301 | } 302 | else 303 | { 304 | // move v2 forward 305 | v2_it = v2.lower_bound(v1_it->first); 306 | // v2_it = (first element >= v1_it.id) 307 | } 308 | } 309 | 310 | return score; // cannot scale 311 | } 312 | 313 | // --------------------------------------------------------------------------- 314 | // --------------------------------------------------------------------------- 315 | 316 | -------------------------------------------------------------------------------- /tests/dbow2/ScoringObject.h: -------------------------------------------------------------------------------- 1 | /** 2 | * File: ScoringObject.h 3 | * Date: November 2011 4 | * Author: Dorian Galvez-Lopez 5 | * Description: functions to compute bow scores 6 | * License: see the LICENSE.txt file 7 | * 8 | */ 9 | 10 | #ifndef __D_T_SCORING_OBJECT__ 11 | #define __D_T_SCORING_OBJECT__ 12 | 13 | #include "BowVector.h" 14 | 15 | namespace DBoW2 { 16 | 17 | /// Base class of scoring functions 18 | class GeneralScoring 19 | { 20 | public: 21 | /** 22 | * Computes the score between two vectors. Vectors must be sorted and 23 | * normalized if necessary 24 | * @param v (in/out) 25 | * @param w (in/out) 26 | * @return score 27 | */ 28 | virtual double score(const BowVector &v, const BowVector &w) const = 0; 29 | 30 | /** 31 | * Returns whether a vector must be normalized before scoring according 32 | * to the scoring scheme 33 | * @param norm norm to use 34 | * @return true iff must normalize 35 | */ 36 | virtual bool mustNormalize(LNorm &norm) const = 0; 37 | 38 | /// Log of epsilon 39 | static const double LOG_EPS; 40 | // If you change the type of WordValue, make sure you change also the 41 | // epsilon value (this is needed by the KL method) 42 | 43 | virtual ~GeneralScoring() {} //!< Required for virtual base classes 44 | 45 | }; 46 | 47 | /** 48 | * Macro for defining Scoring classes 49 | * @param NAME name of class 50 | * @param MUSTNORMALIZE if vectors must be normalized to compute the score 51 | * @param NORM type of norm to use when MUSTNORMALIZE 52 | */ 53 | #define __SCORING_CLASS(NAME, MUSTNORMALIZE, NORM) \ 54 | NAME: public GeneralScoring \ 55 | { public: \ 56 | /** \ 57 | * Computes score between two vectors \ 58 | * @param v \ 59 | * @param w \ 60 | * @return score between v and w \ 61 | */ \ 62 | virtual double score(const BowVector &v, const BowVector &w) const; \ 63 | \ 64 | /** \ 65 | * Says if a vector must be normalized according to the scoring function \ 66 | * @param norm (out) if true, norm to use 67 | * @return true iff vectors must be normalized \ 68 | */ \ 69 | virtual inline bool mustNormalize(LNorm &norm) const \ 70 | { norm = NORM; return MUSTNORMALIZE; } \ 71 | } 72 | 73 | /// L1 Scoring object 74 | class __SCORING_CLASS(L1Scoring, true, L1); 75 | 76 | /// L2 Scoring object 77 | class __SCORING_CLASS(L2Scoring, true, L2); 78 | 79 | /// Chi square Scoring object 80 | class __SCORING_CLASS(ChiSquareScoring, true, L1); 81 | 82 | /// KL divergence Scoring object 83 | class __SCORING_CLASS(KLScoring, true, L1); 84 | 85 | /// Bhattacharyya Scoring object 86 | class __SCORING_CLASS(BhattacharyyaScoring, true, L1); 87 | 88 | /// Dot product Scoring object 89 | class __SCORING_CLASS(DotProductScoring, false, L1); 90 | 91 | #undef __SCORING_CLASS 92 | 93 | } // namespace DBoW2 94 | 95 | #endif 96 | 97 | -------------------------------------------------------------------------------- /tests/dbow2fbow.cpp: -------------------------------------------------------------------------------- 1 | #include "dbow2/TemplatedVocabulary.h" 2 | #include "dbow2/FORB.h" 3 | #include 4 | #include "fbow.h" 5 | #include 6 | using namespace std; 7 | using ORBVocabulary=DBoW2::TemplatedVocabulary ; 8 | 9 | namespace fbow{ 10 | class VocabularyCreator{ 11 | public: 12 | struct ninfo{ 13 | ninfo(){} 14 | ninfo(uint32_t Block,ORBVocabulary::Node *Node):block(Block),node(Node){} 15 | int64_t block=-1; 16 | ORBVocabulary::Node *node=0; 17 | }; 18 | 19 | 20 | static void convert(ORBVocabulary &voc,fbow::Vocabulary &out_voc){ 21 | uint32_t nonLeafNodes=0; 22 | std::map nodeid_info; 23 | for(int i=0;idescriptor); 44 | if (child_info.node->isLeaf()) 45 | binfo.getBlockNodeInfo(c)->setLeaf(child_info.node->word_id,child_info.node->weight); 46 | else { 47 | areAllChildrenLeaf=false; 48 | binfo.getBlockNodeInfo(c)->setNonLeaf(child_info.block); 49 | } 50 | } 51 | binfo.setLeaf(areAllChildrenLeaf); 52 | } 53 | } 54 | } 55 | }; 56 | } 57 | 58 | int main(int argc,char **argv){ 59 | if (argc!=3){cerr<<"Usage voc.txt out.fbow"< 3 | #include "cpu.h" 4 | using namespace std; 5 | using namespace fbow; 6 | void print(const char* label, bool yes){ 7 | cout << label; 8 | cout << (yes ? "Yes" : "No") << endl; 9 | } 10 | void print(cpu host_info) { 11 | cout << "CPU Vendor:" << endl; 12 | print(" AMD = ", host_info.Vendor_AMD); 13 | print(" Intel = ", host_info.Vendor_Intel); 14 | cout << endl; 15 | 16 | cout << "OS Features:" << endl; 17 | #ifdef _WIN32 18 | print(" 64-bit = ", host_info.OS_x64); 19 | #endif 20 | print(" OS AVX = ", host_info.OS_AVX); 21 | print(" OS AVX512 = ", host_info.OS_AVX512); 22 | cout << endl; 23 | 24 | cout << "Hardware Features:" << endl; 25 | print(" MMX = ", host_info.HW_MMX); 26 | print(" x64 = ", host_info.HW_x64); 27 | print(" ABM = ", host_info.HW_ABM); 28 | print(" RDRAND = ", host_info.HW_RDRAND); 29 | print(" BMI1 = ", host_info.HW_BMI1); 30 | print(" BMI2 = ", host_info.HW_BMI2); 31 | print(" ADX = ", host_info.HW_ADX); 32 | print(" MPX = ", host_info.HW_MPX); 33 | print(" PREFETCHWT1 = ", host_info.HW_PREFETCHWT1); 34 | cout << endl; 35 | 36 | cout << "SIMD: 128-bit" << endl; 37 | print(" SSE = ", host_info.HW_SSE); 38 | print(" SSE2 = ", host_info.HW_SSE2); 39 | print(" SSE3 = ", host_info.HW_SSE3); 40 | print(" SSSE3 = ", host_info.HW_SSSE3); 41 | print(" SSE4a = ", host_info.HW_SSE4a); 42 | print(" SSE4.1 = ", host_info.HW_SSE41); 43 | print(" SSE4.2 = ", host_info.HW_SSE42); 44 | print(" AES-NI = ", host_info.HW_AES); 45 | print(" SHA = ", host_info.HW_SHA); 46 | cout << endl; 47 | 48 | cout << "SIMD: 256-bit" << endl; 49 | print(" AVX = ", host_info.HW_AVX); 50 | print(" XOP = ", host_info.HW_XOP); 51 | print(" FMA3 = ", host_info.HW_FMA3); 52 | print(" FMA4 = ", host_info.HW_FMA4); 53 | print(" AVX2 = ", host_info.HW_AVX2); 54 | cout << endl; 55 | 56 | cout << "SIMD: 512-bit" << endl; 57 | print(" AVX512-F = ", host_info.HW_AVX512_F); 58 | print(" AVX512-CD = ", host_info.HW_AVX512_CD); 59 | print(" AVX512-PF = ", host_info.HW_AVX512_PF); 60 | print(" AVX512-ER = ", host_info.HW_AVX512_ER); 61 | print(" AVX512-VL = ", host_info.HW_AVX512_VL); 62 | print(" AVX512-BW = ",host_info. HW_AVX512_BW); 63 | print(" AVX512-DQ = ", host_info.HW_AVX512_DQ); 64 | print(" AVX512-IFMA = ", host_info.HW_AVX512_IFMA); 65 | print(" AVX512-VBMI = ", host_info.HW_AVX512_VBMI); 66 | cout << endl; 67 | 68 | cout << "Summary:" << endl; 69 | print(" Safe to use AVX: ", host_info.HW_AVX && host_info.OS_AVX); 70 | print(" Safe to use AVX512: ",host_info. HW_AVX512_F && host_info.OS_AVX512); 71 | cout << endl; 72 | } 73 | 74 | int main(){ 75 | 76 | cout << "CPU Vendor String: " ; 77 | std::cout<< cpu::get_vendor_string() ; 78 | cpu features; 79 | features.detect_host(); 80 | print(features); 81 | 82 | #if _WIN32 83 | system("pause"); 84 | #endif 85 | } 86 | -------------------------------------------------------------------------------- /tests/test_dbow2VSfbow.cpp: -------------------------------------------------------------------------------- 1 | #include "dbow2/TemplatedVocabulary.h" 2 | #include "dbow2/FORB.h" 3 | 4 | #include "fbow.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #ifdef USE_CONTRIB 11 | #include 12 | #include 13 | #endif 14 | using ORBVocabulary=DBoW2::TemplatedVocabulary ; 15 | 16 | class CmdLineParser{int argc; char **argv; public: CmdLineParser(int _argc,char **_argv):argc(_argc),argv(_argv){} bool operator[] ( string param ) {int idx=-1; for ( int i=0; i toDescriptorVector(const cv::Mat &Descriptors) 19 | { 20 | std::vector vDesc; 21 | vDesc.reserve(Descriptors.rows); 22 | for (int j=0;j loadFeatures( std::vector path_to_images,string descriptor="") { 29 | //select detector 30 | cv::Ptr fdetector; 31 | if (descriptor=="orb") fdetector=cv::ORB::create(2000); 32 | 33 | else if (descriptor=="brisk") fdetector=cv::BRISK::create(); 34 | #ifdef OPENCV_VERSION_3 35 | else if (descriptor=="akaze") fdetector=cv::AKAZE::create(cv::AKAZE::DESCRIPTOR_MLDB, 0, 3, 1e-4); 36 | #endif 37 | #ifdef USE_CONTRIB 38 | else if(descriptor=="surf" ) fdetector=cv::xfeatures2d::SURF::create(15, 4, 2 ); 39 | #endif 40 | 41 | else throw std::runtime_error("Invalid descriptor"); 42 | assert(!descriptor.empty()); 43 | vector features; 44 | 45 | 46 | cout << "Extracting features..." << endl; 47 | for(size_t i = 0; i < path_to_images.size(); ++i) 48 | { 49 | vector keypoints; 50 | cv::Mat descriptors; 51 | cout<<"reading image: "<detectAndCompute(image, cv::Mat(), keypoints, descriptors); 56 | features.push_back(descriptors); 57 | cout<<"done detecting features"< features=loadFeatures({argv[2]}, "orb"); 69 | 70 | 71 | 72 | 73 | double dbow2_load,dbow2_transform; 74 | double fbow_load,fbow_transform; 75 | 76 | { 77 | ORBVocabulary voc; 78 | cout<<"loading dbow2 voc...."<(t_end-t_start).count()); 84 | cout<<"load time="<first<< " "<second<first<< " "<second<(t_end-t_start).count()); 95 | cout<<"DBOW2 time="<(t_end-t_start).count()); 106 | cout<<"load time="<first<< " "<second<first<< " "<second<(t_end-t_start).count()); 119 | cout<<"FBOW time="< fdetector; 20 | if (descriptor=="orb") fdetector=cv::ORB::create(2000); 21 | 22 | else if (descriptor=="brisk") fdetector=cv::BRISK::create(); 23 | #ifdef OPENCV_VERSION_3 24 | else if (descriptor=="akaze") fdetector=cv::AKAZE::create(cv::AKAZE::DESCRIPTOR_MLDB, 0, 3, 1e-4); 25 | #endif 26 | #ifdef USE_CONTRIB 27 | else if(descriptor=="surf" ) fdetector=cv::xfeatures2d::SURF::create(15, 4, 2 ); 28 | #endif 29 | 30 | else throw std::runtime_error("Invalid descriptor"); 31 | assert(!descriptor.empty()); 32 | vector features; 33 | 34 | 35 | cout << "Extracting features..." << endl; 36 | for(size_t i = 0; i < path_to_images.size(); ++i) 37 | { 38 | vector keypoints; 39 | cv::Mat descriptors; 40 | cout<<"reading image: "<detectAndCompute(image, cv::Mat(), keypoints, descriptors); 45 | features.push_back(descriptors); 46 | cout<<"done detecting features"< 53 | #include "fbow.h" 54 | #include 55 | 56 | // Namespaces 57 | using namespace cv; 58 | using namespace std; 59 | 60 | namespace fbow{ 61 | class VocabularyCreator{ 62 | public: 63 | 64 | static cv::Mat getVocabularyLeafNodes(fbow::Vocabulary &voc){ 65 | 66 | //analyze all blocks and count the leafs 67 | uint32_t nleafs=0; 68 | for(uint32_t b=0;bisleaf()) nleafs++; 73 | } 74 | //reserve memory 75 | cv::Mat features; 76 | if ( voc.getDescType()==CV_8UC1) 77 | features.create(nleafs,voc.getDescSize(),CV_8UC1); 78 | else 79 | features.create(nleafs,voc.getDescSize()/sizeof(float),CV_32FC1); 80 | //start copy data 81 | nleafs=0; 82 | for(uint32_t b=0;bisleaf()) block.getFeature(n,features.row(nleafs++)); 87 | } 88 | return features; 89 | } 90 | }; 91 | } 92 | 93 | 94 | class CmdLineParser{int argc; char **argv; public: CmdLineParser(int _argc,char **_argv):argc(_argc),argv(_argv){} bool operator[] ( string param ) {int idx=-1; for ( int i=0; i tree; 117 | cout<<"Creating tree"<(voc_words, indexParams,cvflann::FLANN_DIST_HAMMING); 121 | } 122 | else{ 123 | // cv::flann::KDTreeIndexParams indexParams; 124 | cv::flann::KMeansIndexParams indexParams(voc.getK()); 125 | cout<(voc_words, indexParams); 127 | } 128 | 129 | cout<<"done"<knnSearch(features[0], indices, dists, 1, cv::flann::SearchParams(1)); 137 | auto t_end=std::chrono::high_resolution_clock::now(); 138 | cout<<"time="<(t_end-t_start).count())<<" ms"< 4 | #include 5 | 6 | 7 | #ifdef WIN32 8 | 9 | /* 10 | * Dirent interface for Microsoft Visual Studio 11 | * 12 | * Copyright (C) 2006-2012 Toni Ronkko 13 | * This file is part of dirent. Dirent may be freely distributed 14 | * under the MIT license. For all details and documentation, see 15 | * https://github.com/tronkko/dirent 16 | */ 17 | #ifndef DIRENT_H 18 | #define DIRENT_H 19 | 20 | /* 21 | * Include windows.h without Windows Sockets 1.1 to prevent conflicts with 22 | * Windows Sockets 2.0. 23 | */ 24 | #ifndef WIN32_LEAN_AND_MEAN 25 | # define WIN32_LEAN_AND_MEAN 26 | #endif 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | /* Indicates that d_type field is available in dirent structure */ 40 | #define _DIRENT_HAVE_D_TYPE 41 | 42 | /* Indicates that d_namlen field is available in dirent structure */ 43 | #define _DIRENT_HAVE_D_NAMLEN 44 | 45 | /* Entries missing from MSVC 6.0 */ 46 | #if !defined(FILE_ATTRIBUTE_DEVICE) 47 | # define FILE_ATTRIBUTE_DEVICE 0x40 48 | #endif 49 | 50 | /* File type and permission flags for stat(), general mask */ 51 | #if !defined(S_IFMT) 52 | # define S_IFMT _S_IFMT 53 | #endif 54 | 55 | /* Directory bit */ 56 | #if !defined(S_IFDIR) 57 | # define S_IFDIR _S_IFDIR 58 | #endif 59 | 60 | /* Character device bit */ 61 | #if !defined(S_IFCHR) 62 | # define S_IFCHR _S_IFCHR 63 | #endif 64 | 65 | /* Pipe bit */ 66 | #if !defined(S_IFFIFO) 67 | # define S_IFFIFO _S_IFFIFO 68 | #endif 69 | 70 | /* Regular file bit */ 71 | #if !defined(S_IFREG) 72 | # define S_IFREG _S_IFREG 73 | #endif 74 | 75 | /* Read permission */ 76 | #if !defined(S_IREAD) 77 | # define S_IREAD _S_IREAD 78 | #endif 79 | 80 | /* Write permission */ 81 | #if !defined(S_IWRITE) 82 | # define S_IWRITE _S_IWRITE 83 | #endif 84 | 85 | /* Execute permission */ 86 | #if !defined(S_IEXEC) 87 | # define S_IEXEC _S_IEXEC 88 | #endif 89 | 90 | /* Pipe */ 91 | #if !defined(S_IFIFO) 92 | # define S_IFIFO _S_IFIFO 93 | #endif 94 | 95 | /* Block device */ 96 | #if !defined(S_IFBLK) 97 | # define S_IFBLK 0 98 | #endif 99 | 100 | /* Link */ 101 | #if !defined(S_IFLNK) 102 | # define S_IFLNK 0 103 | #endif 104 | 105 | /* Socket */ 106 | #if !defined(S_IFSOCK) 107 | # define S_IFSOCK 0 108 | #endif 109 | 110 | /* Read user permission */ 111 | #if !defined(S_IRUSR) 112 | # define S_IRUSR S_IREAD 113 | #endif 114 | 115 | /* Write user permission */ 116 | #if !defined(S_IWUSR) 117 | # define S_IWUSR S_IWRITE 118 | #endif 119 | 120 | /* Execute user permission */ 121 | #if !defined(S_IXUSR) 122 | # define S_IXUSR 0 123 | #endif 124 | 125 | /* Read group permission */ 126 | #if !defined(S_IRGRP) 127 | # define S_IRGRP 0 128 | #endif 129 | 130 | /* Write group permission */ 131 | #if !defined(S_IWGRP) 132 | # define S_IWGRP 0 133 | #endif 134 | 135 | /* Execute group permission */ 136 | #if !defined(S_IXGRP) 137 | # define S_IXGRP 0 138 | #endif 139 | 140 | /* Read others permission */ 141 | #if !defined(S_IROTH) 142 | # define S_IROTH 0 143 | #endif 144 | 145 | /* Write others permission */ 146 | #if !defined(S_IWOTH) 147 | # define S_IWOTH 0 148 | #endif 149 | 150 | /* Execute others permission */ 151 | #if !defined(S_IXOTH) 152 | # define S_IXOTH 0 153 | #endif 154 | 155 | /* Maximum length of file name */ 156 | #if !defined(PATH_MAX) 157 | # define PATH_MAX MAX_PATH 158 | #endif 159 | #if !defined(FILENAME_MAX) 160 | # define FILENAME_MAX MAX_PATH 161 | #endif 162 | #if !defined(NAME_MAX) 163 | # define NAME_MAX FILENAME_MAX 164 | #endif 165 | 166 | /* File type flags for d_type */ 167 | #define DT_ALL_DICTS 0 168 | #define DT_REG S_IFREG 169 | #define DT_DIR S_IFDIR 170 | #define DT_FIFO S_IFIFO 171 | #define DT_SOCK S_IFSOCK 172 | #define DT_CHR S_IFCHR 173 | #define DT_BLK S_IFBLK 174 | #define DT_LNK S_IFLNK 175 | 176 | /* Macros for converting between st_mode and d_type */ 177 | #define IFTODT(mode) ((mode) & S_IFMT) 178 | #define DTTOIF(type) (type) 179 | 180 | /* 181 | * File type macros. Note that block devices, sockets and links cannot be 182 | * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are 183 | * only defined for compatibility. These macros should always return false 184 | * on Windows. 185 | */ 186 | #if !defined(S_ISFIFO) 187 | # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) 188 | #endif 189 | #if !defined(S_ISDIR) 190 | # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) 191 | #endif 192 | #if !defined(S_ISREG) 193 | # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) 194 | #endif 195 | #if !defined(S_ISLNK) 196 | # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) 197 | #endif 198 | #if !defined(S_ISSOCK) 199 | # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) 200 | #endif 201 | #if !defined(S_ISCHR) 202 | # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) 203 | #endif 204 | #if !defined(S_ISBLK) 205 | # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) 206 | #endif 207 | 208 | /* Return the exact length of the file name without zero terminator */ 209 | #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) 210 | 211 | /* Return the maximum size of a file name */ 212 | #define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) 213 | 214 | 215 | #ifdef __cplusplus 216 | extern "C" { 217 | #endif 218 | 219 | 220 | /* Wide-character version */ 221 | struct _wdirent { 222 | /* Always zero */ 223 | long d_ino; 224 | 225 | /* File position within stream */ 226 | long d_off; 227 | 228 | /* Structure size */ 229 | unsigned short d_reclen; 230 | 231 | /* Length of name without \0 */ 232 | size_t d_namlen; 233 | 234 | /* File type */ 235 | int d_type; 236 | 237 | /* File name */ 238 | wchar_t d_name[PATH_MAX+1]; 239 | }; 240 | typedef struct _wdirent _wdirent; 241 | 242 | struct _WDIR { 243 | /* Current directory entry */ 244 | struct _wdirent ent; 245 | 246 | /* Private file data */ 247 | WIN32_FIND_DATAW data; 248 | 249 | /* True if data is valid */ 250 | int cached; 251 | 252 | /* Win32 search handle */ 253 | HANDLE handle; 254 | 255 | /* Initial directory name */ 256 | wchar_t *patt; 257 | }; 258 | typedef struct _WDIR _WDIR; 259 | 260 | /* Multi-byte character version */ 261 | struct dirent { 262 | /* Always zero */ 263 | long d_ino; 264 | 265 | /* File position within stream */ 266 | long d_off; 267 | 268 | /* Structure size */ 269 | unsigned short d_reclen; 270 | 271 | /* Length of name without \0 */ 272 | size_t d_namlen; 273 | 274 | /* File type */ 275 | int d_type; 276 | 277 | /* File name */ 278 | char d_name[PATH_MAX+1]; 279 | }; 280 | typedef struct dirent dirent; 281 | 282 | struct DIR { 283 | struct dirent ent; 284 | struct _WDIR *wdirp; 285 | }; 286 | typedef struct DIR DIR; 287 | 288 | 289 | /* Dirent functions */ 290 | static DIR *opendir (const char *dirname); 291 | static _WDIR *_wopendir (const wchar_t *dirname); 292 | 293 | static struct dirent *readdir (DIR *dirp); 294 | static struct _wdirent *_wreaddir (_WDIR *dirp); 295 | 296 | static int readdir_r( 297 | DIR *dirp, struct dirent *entry, struct dirent **result); 298 | static int _wreaddir_r( 299 | _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); 300 | 301 | static int closedir (DIR *dirp); 302 | static int _wclosedir (_WDIR *dirp); 303 | 304 | static void rewinddir (DIR* dirp); 305 | static void _wrewinddir (_WDIR* dirp); 306 | 307 | static int scandir (const char *dirname, struct dirent ***namelist, 308 | int (*filter)(const struct dirent*), 309 | int (*compare)(const void *, const void *)); 310 | 311 | static int alphasort (const struct dirent **a, const struct dirent **b); 312 | 313 | static int versionsort (const struct dirent **a, const struct dirent **b); 314 | 315 | 316 | /* For compatibility with Symbian */ 317 | #define wdirent _wdirent 318 | #define WDIR _WDIR 319 | #define wopendir _wopendir 320 | #define wreaddir _wreaddir 321 | #define wclosedir _wclosedir 322 | #define wrewinddir _wrewinddir 323 | 324 | 325 | /* Internal utility functions */ 326 | static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); 327 | static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); 328 | 329 | static int dirent_mbstowcs_s( 330 | size_t *pReturnValue, 331 | wchar_t *wcstr, 332 | size_t sizeInWords, 333 | const char *mbstr, 334 | size_t count); 335 | 336 | static int dirent_wcstombs_s( 337 | size_t *pReturnValue, 338 | char *mbstr, 339 | size_t sizeInBytes, 340 | const wchar_t *wcstr, 341 | size_t count); 342 | 343 | static void dirent_set_errno (int error); 344 | 345 | 346 | /* 347 | * Open directory stream DIRNAME for read and return a pointer to the 348 | * internal working area that is used to retrieve individual directory 349 | * entries. 350 | */ 351 | static _WDIR* 352 | _wopendir( 353 | const wchar_t *dirname) 354 | { 355 | _WDIR *dirp = NULL; 356 | int error; 357 | 358 | /* Must have directory name */ 359 | if (dirname == NULL || dirname[0] == '\0') { 360 | dirent_set_errno (ENOENT); 361 | return NULL; 362 | } 363 | 364 | /* Allocate new _WDIR structure */ 365 | dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); 366 | if (dirp != NULL) { 367 | DWORD n; 368 | 369 | /* Reset _WDIR structure */ 370 | dirp->handle = INVALID_HANDLE_VALUE; 371 | dirp->patt = NULL; 372 | dirp->cached = 0; 373 | 374 | /* Compute the length of full path plus zero terminator 375 | * 376 | * Note that on WinRT there's no way to convert relative paths 377 | * into absolute paths, so just assume its an absolute path. 378 | */ 379 | # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) 380 | n = wcslen(dirname); 381 | # else 382 | n = GetFullPathNameW (dirname, 0, NULL, NULL); 383 | # endif 384 | 385 | /* Allocate room for absolute directory name and search pattern */ 386 | dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); 387 | if (dirp->patt) { 388 | 389 | /* 390 | * Convert relative directory name to an absolute one. This 391 | * allows rewinddir() to function correctly even when current 392 | * working directory is changed between opendir() and rewinddir(). 393 | * 394 | * Note that on WinRT there's no way to convert relative paths 395 | * into absolute paths, so just assume its an absolute path. 396 | */ 397 | # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) 398 | wcsncpy_s(dirp->patt, n+1, dirname, n); 399 | # else 400 | n = GetFullPathNameW (dirname, n, dirp->patt, NULL); 401 | # endif 402 | if (n > 0) { 403 | wchar_t *p; 404 | 405 | /* Append search pattern \* to the directory name */ 406 | p = dirp->patt + n; 407 | if (dirp->patt < p) { 408 | switch (p[-1]) { 409 | case '\\': 410 | case '/': 411 | case ':': 412 | /* Directory ends in path separator, e.g. c:\temp\ */ 413 | /*NOP*/; 414 | break; 415 | 416 | default: 417 | /* Directory name doesn't end in path separator */ 418 | *p++ = '\\'; 419 | } 420 | } 421 | *p++ = '*'; 422 | *p = '\0'; 423 | 424 | /* Open directory stream and retrieve the first entry */ 425 | if (dirent_first (dirp)) { 426 | /* Directory stream opened successfully */ 427 | error = 0; 428 | } else { 429 | /* Cannot retrieve first entry */ 430 | error = 1; 431 | dirent_set_errno (ENOENT); 432 | } 433 | 434 | } else { 435 | /* Cannot retrieve full path name */ 436 | dirent_set_errno (ENOENT); 437 | error = 1; 438 | } 439 | 440 | } else { 441 | /* Cannot allocate memory for search pattern */ 442 | error = 1; 443 | } 444 | 445 | } else { 446 | /* Cannot allocate _WDIR structure */ 447 | error = 1; 448 | } 449 | 450 | /* Clean up in case of error */ 451 | if (error && dirp) { 452 | _wclosedir (dirp); 453 | dirp = NULL; 454 | } 455 | 456 | return dirp; 457 | } 458 | 459 | /* 460 | * Read next directory entry. 461 | * 462 | * Returns pointer to static directory entry which may be overwritted by 463 | * subsequent calls to _wreaddir(). 464 | */ 465 | static struct _wdirent* 466 | _wreaddir( 467 | _WDIR *dirp) 468 | { 469 | struct _wdirent *entry; 470 | 471 | /* 472 | * Read directory entry to buffer. We can safely ignore the return value 473 | * as entry will be set to NULL in case of error. 474 | */ 475 | (void) _wreaddir_r (dirp, &dirp->ent, &entry); 476 | 477 | /* Return pointer to statically allocated directory entry */ 478 | return entry; 479 | } 480 | 481 | /* 482 | * Read next directory entry. 483 | * 484 | * Returns zero on success. If end of directory stream is reached, then sets 485 | * result to NULL and returns zero. 486 | */ 487 | static int 488 | _wreaddir_r( 489 | _WDIR *dirp, 490 | struct _wdirent *entry, 491 | struct _wdirent **result) 492 | { 493 | WIN32_FIND_DATAW *datap; 494 | 495 | /* Read next directory entry */ 496 | datap = dirent_next (dirp); 497 | if (datap) { 498 | size_t n; 499 | DWORD attr; 500 | 501 | /* 502 | * Copy file name as wide-character string. If the file name is too 503 | * long to fit in to the destination buffer, then truncate file name 504 | * to PATH_MAX characters and zero-terminate the buffer. 505 | */ 506 | n = 0; 507 | while (n < PATH_MAX && datap->cFileName[n] != 0) { 508 | entry->d_name[n] = datap->cFileName[n]; 509 | n++; 510 | } 511 | entry->d_name[n] = 0; 512 | 513 | /* Length of file name excluding zero terminator */ 514 | entry->d_namlen = n; 515 | 516 | /* File type */ 517 | attr = datap->dwFileAttributes; 518 | if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 519 | entry->d_type = DT_CHR; 520 | } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 521 | entry->d_type = DT_DIR; 522 | } else { 523 | entry->d_type = DT_REG; 524 | } 525 | 526 | /* Reset dummy fields */ 527 | entry->d_ino = 0; 528 | entry->d_off = 0; 529 | entry->d_reclen = sizeof (struct _wdirent); 530 | 531 | /* Set result address */ 532 | *result = entry; 533 | 534 | } else { 535 | 536 | /* Return NULL to indicate end of directory */ 537 | *result = NULL; 538 | 539 | } 540 | 541 | return /*OK*/0; 542 | } 543 | 544 | /* 545 | * Close directory stream opened by opendir() function. This invalidates the 546 | * DIR structure as well as any directory entry read previously by 547 | * _wreaddir(). 548 | */ 549 | static int 550 | _wclosedir( 551 | _WDIR *dirp) 552 | { 553 | int ok; 554 | if (dirp) { 555 | 556 | /* Release search handle */ 557 | if (dirp->handle != INVALID_HANDLE_VALUE) { 558 | FindClose (dirp->handle); 559 | dirp->handle = INVALID_HANDLE_VALUE; 560 | } 561 | 562 | /* Release search pattern */ 563 | if (dirp->patt) { 564 | free (dirp->patt); 565 | dirp->patt = NULL; 566 | } 567 | 568 | /* Release directory structure */ 569 | free (dirp); 570 | ok = /*success*/0; 571 | 572 | } else { 573 | 574 | /* Invalid directory stream */ 575 | dirent_set_errno (EBADF); 576 | ok = /*failure*/-1; 577 | 578 | } 579 | return ok; 580 | } 581 | 582 | /* 583 | * Rewind directory stream such that _wreaddir() returns the very first 584 | * file name again. 585 | */ 586 | static void 587 | _wrewinddir( 588 | _WDIR* dirp) 589 | { 590 | if (dirp) { 591 | /* Release existing search handle */ 592 | if (dirp->handle != INVALID_HANDLE_VALUE) { 593 | FindClose (dirp->handle); 594 | } 595 | 596 | /* Open new search handle */ 597 | dirent_first (dirp); 598 | } 599 | } 600 | 601 | /* Get first directory entry (internal) */ 602 | static WIN32_FIND_DATAW* 603 | dirent_first( 604 | _WDIR *dirp) 605 | { 606 | WIN32_FIND_DATAW *datap; 607 | 608 | /* Open directory and retrieve the first entry */ 609 | dirp->handle = FindFirstFileExW( 610 | dirp->patt, FindExInfoStandard, &dirp->data, 611 | FindExSearchNameMatch, NULL, 0); 612 | if (dirp->handle != INVALID_HANDLE_VALUE) { 613 | 614 | /* a directory entry is now waiting in memory */ 615 | datap = &dirp->data; 616 | dirp->cached = 1; 617 | 618 | } else { 619 | 620 | /* Failed to re-open directory: no directory entry in memory */ 621 | dirp->cached = 0; 622 | datap = NULL; 623 | 624 | } 625 | return datap; 626 | } 627 | 628 | /* 629 | * Get next directory entry (internal). 630 | * 631 | * Returns 632 | */ 633 | static WIN32_FIND_DATAW* 634 | dirent_next( 635 | _WDIR *dirp) 636 | { 637 | WIN32_FIND_DATAW *p; 638 | 639 | /* Get next directory entry */ 640 | if (dirp->cached != 0) { 641 | 642 | /* A valid directory entry already in memory */ 643 | p = &dirp->data; 644 | dirp->cached = 0; 645 | 646 | } else if (dirp->handle != INVALID_HANDLE_VALUE) { 647 | 648 | /* Get the next directory entry from stream */ 649 | if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { 650 | /* Got a file */ 651 | p = &dirp->data; 652 | } else { 653 | /* The very last entry has been processed or an error occured */ 654 | FindClose (dirp->handle); 655 | dirp->handle = INVALID_HANDLE_VALUE; 656 | p = NULL; 657 | } 658 | 659 | } else { 660 | 661 | /* End of directory stream reached */ 662 | p = NULL; 663 | 664 | } 665 | 666 | return p; 667 | } 668 | 669 | /* 670 | * Open directory stream using plain old C-string. 671 | */ 672 | static DIR* 673 | opendir( 674 | const char *dirname) 675 | { 676 | struct DIR *dirp; 677 | int error; 678 | 679 | /* Must have directory name */ 680 | if (dirname == NULL || dirname[0] == '\0') { 681 | dirent_set_errno (ENOENT); 682 | return NULL; 683 | } 684 | 685 | /* Allocate memory for DIR structure */ 686 | dirp = (DIR*) malloc (sizeof (struct DIR)); 687 | if (dirp) { 688 | wchar_t wname[PATH_MAX + 1]; 689 | size_t n; 690 | 691 | /* Convert directory name to wide-character string */ 692 | error = dirent_mbstowcs_s( 693 | &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); 694 | if (!error) { 695 | 696 | /* Open directory stream using wide-character name */ 697 | dirp->wdirp = _wopendir (wname); 698 | if (dirp->wdirp) { 699 | /* Directory stream opened */ 700 | error = 0; 701 | } else { 702 | /* Failed to open directory stream */ 703 | error = 1; 704 | } 705 | 706 | } else { 707 | /* 708 | * Cannot convert file name to wide-character string. This 709 | * occurs if the string contains invalid multi-byte sequences or 710 | * the output buffer is too small to contain the resulting 711 | * string. 712 | */ 713 | error = 1; 714 | } 715 | 716 | } else { 717 | /* Cannot allocate DIR structure */ 718 | error = 1; 719 | } 720 | 721 | /* Clean up in case of error */ 722 | if (error && dirp) { 723 | free (dirp); 724 | dirp = NULL; 725 | } 726 | 727 | return dirp; 728 | } 729 | 730 | /* 731 | * Read next directory entry. 732 | */ 733 | static struct dirent* 734 | readdir( 735 | DIR *dirp) 736 | { 737 | struct dirent *entry; 738 | 739 | /* 740 | * Read directory entry to buffer. We can safely ignore the return value 741 | * as entry will be set to NULL in case of error. 742 | */ 743 | (void) readdir_r (dirp, &dirp->ent, &entry); 744 | 745 | /* Return pointer to statically allocated directory entry */ 746 | return entry; 747 | } 748 | 749 | /* 750 | * Read next directory entry into called-allocated buffer. 751 | * 752 | * Returns zero on sucess. If the end of directory stream is reached, then 753 | * sets result to NULL and returns zero. 754 | */ 755 | static int 756 | readdir_r( 757 | DIR *dirp, 758 | struct dirent *entry, 759 | struct dirent **result) 760 | { 761 | WIN32_FIND_DATAW *datap; 762 | 763 | /* Read next directory entry */ 764 | datap = dirent_next (dirp->wdirp); 765 | if (datap) { 766 | size_t n; 767 | int error; 768 | 769 | /* Attempt to convert file name to multi-byte string */ 770 | error = dirent_wcstombs_s( 771 | &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); 772 | 773 | /* 774 | * If the file name cannot be represented by a multi-byte string, 775 | * then attempt to use old 8+3 file name. This allows traditional 776 | * Unix-code to access some file names despite of unicode 777 | * characters, although file names may seem unfamiliar to the user. 778 | * 779 | * Be ware that the code below cannot come up with a short file 780 | * name unless the file system provides one. At least 781 | * VirtualBox shared folders fail to do this. 782 | */ 783 | if (error && datap->cAlternateFileName[0] != '\0') { 784 | error = dirent_wcstombs_s( 785 | &n, entry->d_name, PATH_MAX + 1, 786 | datap->cAlternateFileName, PATH_MAX + 1); 787 | } 788 | 789 | if (!error) { 790 | DWORD attr; 791 | 792 | /* Length of file name excluding zero terminator */ 793 | entry->d_namlen = n - 1; 794 | 795 | /* File attributes */ 796 | attr = datap->dwFileAttributes; 797 | if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 798 | entry->d_type = DT_CHR; 799 | } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 800 | entry->d_type = DT_DIR; 801 | } else { 802 | entry->d_type = DT_REG; 803 | } 804 | 805 | /* Reset dummy fields */ 806 | entry->d_ino = 0; 807 | entry->d_off = 0; 808 | entry->d_reclen = sizeof (struct dirent); 809 | 810 | } else { 811 | 812 | /* 813 | * Cannot convert file name to multi-byte string so construct 814 | * an errornous directory entry and return that. Note that 815 | * we cannot return NULL as that would stop the processing 816 | * of directory entries completely. 817 | */ 818 | entry->d_name[0] = '?'; 819 | entry->d_name[1] = '\0'; 820 | entry->d_namlen = 1; 821 | entry->d_type = DT_ALL_DICTS; 822 | entry->d_ino = 0; 823 | entry->d_off = -1; 824 | entry->d_reclen = 0; 825 | 826 | } 827 | 828 | /* Return pointer to directory entry */ 829 | *result = entry; 830 | 831 | } else { 832 | 833 | /* No more directory entries */ 834 | *result = NULL; 835 | 836 | } 837 | 838 | return /*OK*/0; 839 | } 840 | 841 | /* 842 | * Close directory stream. 843 | */ 844 | static int 845 | closedir( 846 | DIR *dirp) 847 | { 848 | int ok; 849 | if (dirp) { 850 | 851 | /* Close wide-character directory stream */ 852 | ok = _wclosedir (dirp->wdirp); 853 | dirp->wdirp = NULL; 854 | 855 | /* Release multi-byte character version */ 856 | free (dirp); 857 | 858 | } else { 859 | 860 | /* Invalid directory stream */ 861 | dirent_set_errno (EBADF); 862 | ok = /*failure*/-1; 863 | 864 | } 865 | return ok; 866 | } 867 | 868 | /* 869 | * Rewind directory stream to beginning. 870 | */ 871 | static void 872 | rewinddir( 873 | DIR* dirp) 874 | { 875 | /* Rewind wide-character string directory stream */ 876 | _wrewinddir (dirp->wdirp); 877 | } 878 | 879 | /* 880 | * Scan directory for entries. 881 | */ 882 | static int 883 | scandir( 884 | const char *dirname, 885 | struct dirent ***namelist, 886 | int (*filter)(const struct dirent*), 887 | int (*compare)(const void*, const void*)) 888 | { 889 | struct dirent **files = NULL; 890 | size_t size = 0; 891 | size_t allocated = 0; 892 | const size_t init_size = 1; 893 | DIR *dir = NULL; 894 | struct dirent *entry; 895 | struct dirent *tmp = NULL; 896 | size_t i; 897 | int result = 0; 898 | 899 | /* Open directory stream */ 900 | dir = opendir (dirname); 901 | if (dir) { 902 | 903 | /* Read directory entries to memory */ 904 | while (1) { 905 | 906 | /* Enlarge pointer table to make room for another pointer */ 907 | if (size >= allocated) { 908 | void *p; 909 | size_t num_entries; 910 | 911 | /* Compute number of entries in the enlarged pointer table */ 912 | if (size < init_size) { 913 | /* Allocate initial pointer table */ 914 | num_entries = init_size; 915 | } else { 916 | /* Double the size */ 917 | num_entries = size * 2; 918 | } 919 | 920 | /* Allocate first pointer table or enlarge existing table */ 921 | p = realloc (files, sizeof (void*) * num_entries); 922 | if (p != NULL) { 923 | /* Got the memory */ 924 | files = (dirent**) p; 925 | allocated = num_entries; 926 | } else { 927 | /* Out of memory */ 928 | result = -1; 929 | break; 930 | } 931 | 932 | } 933 | 934 | /* Allocate room for temporary directory entry */ 935 | if (tmp == NULL) { 936 | tmp = (struct dirent*) malloc (sizeof (struct dirent)); 937 | if (tmp == NULL) { 938 | /* Cannot allocate temporary directory entry */ 939 | result = -1; 940 | break; 941 | } 942 | } 943 | 944 | /* Read directory entry to temporary area */ 945 | if (readdir_r (dir, tmp, &entry) == /*OK*/0) { 946 | 947 | /* Did we got an entry? */ 948 | if (entry != NULL) { 949 | int pass; 950 | 951 | /* Determine whether to include the entry in result */ 952 | if (filter) { 953 | /* Let the filter function decide */ 954 | pass = filter (tmp); 955 | } else { 956 | /* No filter function, include everything */ 957 | pass = 1; 958 | } 959 | 960 | if (pass) { 961 | /* Store the temporary entry to pointer table */ 962 | files[size++] = tmp; 963 | tmp = NULL; 964 | 965 | /* Keep up with the number of files */ 966 | result++; 967 | } 968 | 969 | } else { 970 | 971 | /* 972 | * End of directory stream reached => sort entries and 973 | * exit. 974 | */ 975 | qsort (files, size, sizeof (void*), compare); 976 | break; 977 | 978 | } 979 | 980 | } else { 981 | /* Error reading directory entry */ 982 | result = /*Error*/ -1; 983 | break; 984 | } 985 | 986 | } 987 | 988 | } else { 989 | /* Cannot open directory */ 990 | result = /*Error*/ -1; 991 | } 992 | 993 | /* Release temporary directory entry */ 994 | if (tmp) { 995 | free (tmp); 996 | } 997 | 998 | /* Release allocated memory on error */ 999 | if (result < 0) { 1000 | for (i = 0; i < size; i++) { 1001 | free (files[i]); 1002 | } 1003 | free (files); 1004 | files = NULL; 1005 | } 1006 | 1007 | /* Close directory stream */ 1008 | if (dir) { 1009 | closedir (dir); 1010 | } 1011 | 1012 | /* Pass pointer table to caller */ 1013 | if (namelist) { 1014 | *namelist = files; 1015 | } 1016 | return result; 1017 | } 1018 | 1019 | /* Alphabetical sorting */ 1020 | static int 1021 | alphasort( 1022 | const struct dirent **a, const struct dirent **b) 1023 | { 1024 | return strcoll ((*a)->d_name, (*b)->d_name); 1025 | } 1026 | 1027 | /* Sort versions */ 1028 | static int 1029 | versionsort( 1030 | const struct dirent **a, const struct dirent **b) 1031 | { 1032 | /* FIXME: implement strverscmp and use that */ 1033 | return alphasort (a, b); 1034 | } 1035 | 1036 | 1037 | /* Convert multi-byte string to wide character string */ 1038 | static int 1039 | dirent_mbstowcs_s( 1040 | size_t *pReturnValue, 1041 | wchar_t *wcstr, 1042 | size_t sizeInWords, 1043 | const char *mbstr, 1044 | size_t count) 1045 | { 1046 | int error; 1047 | 1048 | #if defined(_MSC_VER) && _MSC_VER >= 1400 1049 | 1050 | /* Microsoft Visual Studio 2005 or later */ 1051 | error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); 1052 | 1053 | #else 1054 | 1055 | /* Older Visual Studio or non-Microsoft compiler */ 1056 | size_t n; 1057 | 1058 | /* Convert to wide-character string (or count characters) */ 1059 | n = mbstowcs (wcstr, mbstr, sizeInWords); 1060 | if (!wcstr || n < count) { 1061 | 1062 | /* Zero-terminate output buffer */ 1063 | if (wcstr && sizeInWords) { 1064 | if (n >= sizeInWords) { 1065 | n = sizeInWords - 1; 1066 | } 1067 | wcstr[n] = 0; 1068 | } 1069 | 1070 | /* Length of resuting multi-byte string WITH zero terminator */ 1071 | if (pReturnValue) { 1072 | *pReturnValue = n + 1; 1073 | } 1074 | 1075 | /* Success */ 1076 | error = 0; 1077 | 1078 | } else { 1079 | 1080 | /* Could not convert string */ 1081 | error = 1; 1082 | 1083 | } 1084 | 1085 | #endif 1086 | 1087 | return error; 1088 | } 1089 | 1090 | /* Convert wide-character string to multi-byte string */ 1091 | static int 1092 | dirent_wcstombs_s( 1093 | size_t *pReturnValue, 1094 | char *mbstr, 1095 | size_t sizeInBytes, /* max size of mbstr */ 1096 | const wchar_t *wcstr, 1097 | size_t count) 1098 | { 1099 | int error; 1100 | 1101 | #if defined(_MSC_VER) && _MSC_VER >= 1400 1102 | 1103 | /* Microsoft Visual Studio 2005 or later */ 1104 | error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); 1105 | 1106 | #else 1107 | 1108 | /* Older Visual Studio or non-Microsoft compiler */ 1109 | size_t n; 1110 | 1111 | /* Convert to multi-byte string (or count the number of bytes needed) */ 1112 | n = wcstombs (mbstr, wcstr, sizeInBytes); 1113 | if (!mbstr || n < count) { 1114 | 1115 | /* Zero-terminate output buffer */ 1116 | if (mbstr && sizeInBytes) { 1117 | if (n >= sizeInBytes) { 1118 | n = sizeInBytes - 1; 1119 | } 1120 | mbstr[n] = '\0'; 1121 | } 1122 | 1123 | /* Length of resulting multi-bytes string WITH zero-terminator */ 1124 | if (pReturnValue) { 1125 | *pReturnValue = n + 1; 1126 | } 1127 | 1128 | /* Success */ 1129 | error = 0; 1130 | 1131 | } else { 1132 | 1133 | /* Cannot convert string */ 1134 | error = 1; 1135 | 1136 | } 1137 | 1138 | #endif 1139 | 1140 | return error; 1141 | } 1142 | 1143 | /* Set errno variable */ 1144 | static void 1145 | dirent_set_errno( 1146 | int error) 1147 | { 1148 | #if defined(_MSC_VER) && _MSC_VER >= 1400 1149 | 1150 | /* Microsoft Visual Studio 2005 and later */ 1151 | _set_errno (error); 1152 | 1153 | #else 1154 | 1155 | /* Non-Microsoft compiler or older Microsoft compiler */ 1156 | errno = error; 1157 | 1158 | #endif 1159 | } 1160 | 1161 | 1162 | #ifdef __cplusplus 1163 | } 1164 | #endif 1165 | #endif /*DIRENT_H*/ 1166 | #else 1167 | #include 1168 | #endif 1169 | 1170 | 1171 | 1172 | 1173 | class DirReader{ 1174 | public: 1175 | static std::vector read(std::string path){ 1176 | DIR *dir; 1177 | struct dirent *ent; 1178 | std::vector res; 1179 | if ((dir = opendir (path.c_str())) != NULL) { 1180 | /* print all the files and directories within directory */ 1181 | while ((ent = readdir (dir)) != NULL) 1182 | res.push_back(path+std::string("/")+std::string(ent->d_name)); 1183 | closedir (dir); 1184 | } 1185 | //check 1186 | return res; 1187 | } 1188 | 1189 | }; 1190 | #endif 1191 | -------------------------------------------------------------------------------- /utils/fbow_create_voc_step0.cpp: -------------------------------------------------------------------------------- 1 | 2 | //First step of creating a vocabulary is extracting features from a set of images. We save them to a file for next step 3 | #include 4 | #include 5 | #include 6 | #include "fbow.h" 7 | 8 | // OpenCV 9 | #include 10 | #include 11 | #include 12 | #ifdef USE_CONTRIB 13 | #include 14 | #include 15 | #endif 16 | #include "dirreader.h" 17 | using namespace fbow; 18 | using namespace std; 19 | 20 | 21 | //command line parser 22 | class CmdLineParser{int argc; char **argv; public: CmdLineParser(int _argc,char **_argv):argc(_argc),argv(_argv){} bool operator[] ( string param ) {int idx=-1; for ( int i=0; i loadFeatures( std::vector path_to_images,string descriptor="") { 37 | //select detector 38 | cv::Ptr fdetector; 39 | if (descriptor=="orb") fdetector=cv::ORB::create(2000); 40 | else if (descriptor=="brisk") fdetector=cv::BRISK::create(); 41 | #ifdef OPENCV_VERSION_3 42 | else if (descriptor=="akaze") fdetector=cv::AKAZE::create(cv::AKAZE::DESCRIPTOR_MLDB, 0, 3, 1e-4); 43 | #endif 44 | #ifdef USE_CONTRIB 45 | else if(descriptor=="surf" ) fdetector=cv::xfeatures2d::SURF::create(15, 4, 2); 46 | #endif 47 | 48 | else throw std::runtime_error("Invalid descriptor"); 49 | assert(!descriptor.empty()); 50 | vector features; 51 | 52 | 53 | cout << "Extracting features..." << endl; 54 | for(size_t i = 0; i < path_to_images.size(); ++i) 55 | { 56 | vector keypoints; 57 | cv::Mat descriptors; 58 | cout<<"reading image: "<detectAndCompute(image, cv::Mat(), keypoints, descriptors); 65 | cout<<"extracting features: total= "< &features, std::string desc_name,bool rewrite =true){ 74 | 75 | //test it is not created 76 | if (!rewrite){ 77 | std::fstream ifile(filename, std::ios::binary); 78 | if (ifile.is_open())//read size and rewrite 79 | std::runtime_error( "ERROR::: Output File "+filename+" already exists!!!!!" ); 80 | } 81 | std::ofstream ofile(filename, std::ios::binary); 82 | if (!ofile.is_open()){cerr<<"could not open output file"<(0),f.total()*f.elemSize()); 99 | } 100 | } 101 | 102 | // ---------------------------------------------------------------------------- 103 | 104 | int main(int argc,char **argv) 105 | { 106 | 107 | try{ 108 | CmdLineParser cml(argc,argv); 109 | if (cml["-h"] || argc<4){ 110 | cerr<<"Usage: descriptor_name output dir_with_images \n\t descriptors:brisk,surf,orb(default),akaze(only if using opencv 3)"< features= loadFeatures(images,descriptor); 119 | 120 | //save features to file 121 | std::cerr<<"saving to "< 4 | #include 5 | #include 6 | #include "fbow.h" 7 | 8 | // OpenCV 9 | #include 10 | #include 11 | #include 12 | #ifdef USE_CONTRIB 13 | #include 14 | #include 15 | #endif 16 | 17 | using namespace fbow; 18 | using namespace std; 19 | 20 | 21 | //command line parser 22 | class CmdLineParser{int argc; char **argv; public: CmdLineParser(int _argc,char **_argv):argc(_argc),argv(_argv){} bool operator[] ( string param ) {int idx=-1; for ( int i=0; i readImagePathsFromFile(char * txtlist){ 36 | vector paths; 37 | // Open file 38 | std::ifstream infile(txtlist, std::ios::binary); 39 | 40 | while(!infile.eof()) 41 | { 42 | string line; 43 | getline(infile, line); 44 | 45 | if (line.length() > 0) 46 | paths.push_back(line); 47 | 48 | //std::cout << line << std::endl; 49 | } 50 | 51 | return paths; 52 | } 53 | 54 | vector< cv::Mat > loadFeatures( std::vector path_to_images,string descriptor="") { 55 | //select detector 56 | cv::Ptr fdetector; 57 | if (descriptor=="orb") fdetector=cv::ORB::create(2000); 58 | else if (descriptor=="brisk") fdetector=cv::BRISK::create(); 59 | #ifdef OPENCV_VERSION_3 60 | else if (descriptor=="akaze") fdetector=cv::AKAZE::create(cv::AKAZE::DESCRIPTOR_MLDB, 0, 3, 1e-4); 61 | #endif 62 | #ifdef USE_CONTRIB 63 | else if(descriptor=="surf" ) fdetector=cv::xfeatures2d::SURF::create(15, 4, 2); 64 | #endif 65 | 66 | else throw std::runtime_error("Invalid descriptor"); 67 | assert(!descriptor.empty()); 68 | vector features; 69 | 70 | 71 | cout << "Extracting features..." << endl; 72 | for(size_t i = 0; i < path_to_images.size(); ++i) 73 | { 74 | vector keypoints; 75 | cv::Mat descriptors; 76 | cout<<"reading image: "<detectAndCompute(image, cv::Mat(), keypoints, descriptors); 81 | features.push_back(descriptors); 82 | cout<<"done detecting features (" << i << "/" << path_to_images.size() << ")" < &features, std::string desc_name,bool rewrite =true){ 89 | 90 | //test it is not created 91 | if (!rewrite){ 92 | std::fstream ifile(filename); 93 | if (ifile.is_open())//read size and rewrite 94 | std::runtime_error( "ERROR::: Output File "+filename+" already exists!!!!!" ); 95 | } 96 | std::ofstream ofile(filename, std::ios::binary); 97 | if (!ofile.is_open()){cerr<<"could not open output file"<(0),f.total()*f.elemSize()); 114 | } 115 | } 116 | 117 | // ---------------------------------------------------------------------------- 118 | 119 | int main(int argc,char **argv) 120 | { 121 | 122 | try{ 123 | CmdLineParser cml(argc,argv); 124 | if (cml["-h"] || argc==1){ 125 | cerr<<"Usage: descriptor_name output txtlist \n\t descriptors:brisk,surf,orb(default),akaze(only if using opencv 3)"< features= loadFeatures(images,descriptor); 134 | 135 | //save features to file 136 | saveToFile(argv[2],features,descriptor); 137 | 138 | }catch(std::exception &ex){ 139 | cerr< 3 | #include 4 | #include 5 | 6 | // 7 | #include "vocabulary_creator.h" 8 | // OpenCV 9 | #include 10 | using namespace std; 11 | 12 | //command line parser 13 | class CmdLineParser{int argc; char **argv; public: CmdLineParser(int _argc,char **_argv):argc(_argc),argv(_argv){} bool operator[] ( string param ) {int idx=-1; for ( int i=0; i readFeaturesFromFile(string filename,std::string &desc_name){ 19 | vector features; 20 | //test it is not created 21 | std::ifstream ifile(filename,std::ios::binary); 22 | if (!ifile.is_open()){cerr<<"could not open input file"<(0),features[i].total()*features[i].elemSize()); 40 | } 41 | return features; 42 | } 43 | 44 | // ---------------------------------------------------------------------------- 45 | 46 | int main(int argc,char **argv) 47 | { 48 | 49 | try{ 50 | CmdLineParser cml(argc,argv); 51 | if (cml["-h"] || argc<3){ 52 | cerr<<"Usage: features output.fbow [-k k] [-l L] [-t nthreads] [-maxIters :0 default] [-v verbose on]. "< 3 | #include 4 | #include 5 | 6 | // 7 | #include "vocabulary_creator.h" 8 | // OpenCV 9 | #include 10 | 11 | // OpenCV 12 | #include 13 | #include 14 | #include 15 | #ifdef USE_CONTRIB 16 | #include 17 | #include 18 | #endif 19 | using namespace std; 20 | 21 | //command line parser 22 | class CmdLineParser{int argc; char **argv; public: CmdLineParser(int _argc,char **_argv):argc(_argc),argv(_argv){} bool operator[] ( string param ) {int idx=-1; for ( int i=0; i readFeaturesFromFile(string filename){ 28 | vector features; 29 | //test it is not created 30 | std::ifstream ifile(filename); 31 | if (!ifile.is_open()){cerr<<"could not open input file"<(0),features[i].total()*features[i].elemSize()); 43 | } 44 | return features; 45 | } 46 | 47 | // ---------------------------------------------------------------------------- 48 | 49 | int main(int argc,char **argv) 50 | { 51 | 52 | try{ 53 | CmdLineParser cml(argc,argv); 54 | if (cml["-h"] || argc<3){ 55 | cerr<<"Usage: features output.yml "< 4 | using namespace std; 5 | 6 | // OpenCV 7 | #include 8 | #include 9 | #include 10 | #ifdef USE_CONTRIB 11 | #include 12 | #include 13 | #endif 14 | 15 | 16 | #include 17 | class CmdLineParser{int argc; char **argv; public: CmdLineParser(int _argc,char **_argv):argc(_argc),argv(_argv){} bool operator[] ( string param ) {int idx=-1; for ( int i=0; i loadFeatures( std::vector path_to_images,string descriptor="") { 20 | //select detector 21 | cv::Ptr fdetector; 22 | if (descriptor=="orb") fdetector=cv::ORB::create(2000); 23 | else if (descriptor=="brisk") fdetector=cv::BRISK::create(); 24 | #ifdef OPENCV_VERSION_3 25 | else if (descriptor=="akaze") fdetector=cv::AKAZE::create(cv::AKAZE::DESCRIPTOR_MLDB, 0, 3, 1e-4); 26 | #endif 27 | #ifdef USE_CONTRIB 28 | else if(descriptor=="surf" ) fdetector=cv::xfeatures2d::SURF::create(15, 4, 2 ); 29 | #endif 30 | 31 | else throw std::runtime_error("Invalid descriptor"); 32 | assert(!descriptor.empty()); 33 | vector features; 34 | 35 | 36 | cout << "Extracting features..." << endl; 37 | for(size_t i = 0; i < path_to_images.size(); ++i) 38 | { 39 | vector keypoints; 40 | cv::Mat descriptors; 41 | cout<<"reading image: "<detectAndCompute(image, cv::Mat(), keypoints, descriptors); 46 | features.push_back(descriptors); 47 | cout<<"done detecting features"<first<<" "<second<first<<" "<second< 4 | #include 5 | using namespace std; 6 | 7 | // OpenCV 8 | #include 9 | #include 10 | #include 11 | #ifdef USE_CONTRIB 12 | #include 13 | #include 14 | #endif 15 | 16 | 17 | #include 18 | class CmdLineParser { int argc; char **argv; public: CmdLineParser(int _argc, char **_argv) :argc(_argc), argv(_argv) {} bool operator[] (string param) { int idx = -1; for (int i = 0; i loadFeatures(std::vector path_to_images, string descriptor = "") { 21 | //select detector 22 | cv::Ptr fdetector; 23 | if (descriptor == "orb") fdetector = cv::ORB::create(2000); 24 | else if (descriptor == "brisk") fdetector = cv::BRISK::create(); 25 | #ifdef OPENCV_VERSION_3 26 | else if (descriptor == "akaze") fdetector = cv::AKAZE::create(cv::AKAZE::DESCRIPTOR_MLDB, 0, 3, 1e-4); 27 | #endif 28 | #ifdef USE_CONTRIB 29 | else if (descriptor == "surf") fdetector = cv::xfeatures2d::SURF::create(15, 4, 2); 30 | #endif 31 | 32 | else throw std::runtime_error("Invalid descriptor"); 33 | assert(!descriptor.empty()); 34 | vector features; 35 | 36 | 37 | cout << "Extracting features..." << endl; 38 | for (size_t i = 0; i < path_to_images.size(); ++i) 39 | { 40 | vector keypoints; 41 | cv::Mat descriptors; 42 | cout << "reading image: " << path_to_images[i] << endl; 43 | cv::Mat image = cv::imread(path_to_images[i], 0); 44 | if (image.empty())throw std::runtime_error("Could not open image" + path_to_images[i]); 45 | cout << "extracting features" << endl; 46 | fdetector->detectAndCompute(image, cv::Mat(), keypoints, descriptors); 47 | features.push_back(descriptors); 48 | cout << "done detecting features" << endl; 49 | } 50 | return features; 51 | } 52 | 53 | int main(int argc, char **argv) { 54 | CmdLineParser cml(argc, argv); 55 | try { 56 | if (argc<3 || cml["-h"]) throw std::runtime_error("Usage: fbow image [descriptor]"); 57 | fbow::Vocabulary voc; 58 | voc.readFromFile(argv[1]); 59 | 60 | string desc_name = voc.getDescName(); 61 | cout << "voc desc name=" << desc_name << endl; 62 | vector > features(argc - 3); 63 | vector > scores; 64 | vector filenames(argc - 3); 65 | string outDir = argv[2]; 66 | for (int i = 3; i < argc; ++i) 67 | { 68 | filenames[i - 3] = { argv[i] }; 69 | } 70 | for (size_t i = 0; i score; 81 | for (size_t j = 0; j 0.01f) 88 | { 89 | score.insert(pair(score1, j)); 90 | } 91 | printf("%f, ", score1); 92 | } 93 | printf("\n"); 94 | scores.push_back(score); 95 | } 96 | auto t_end = std::chrono::high_resolution_clock::now(); 97 | avgScore += double(std::chrono::duration_cast(t_end - t_start).count()); 98 | 99 | std::string command; 100 | int j = 0; 101 | for (size_t i = 0; i < scores.size(); i++) 102 | { 103 | std::stringstream str; 104 | 105 | command = "mkdir "; 106 | str << i; 107 | command += str.str(); 108 | command += "/"; 109 | system(command.c_str()); 110 | 111 | command = "cp "; 112 | command += filenames[i]; 113 | command += " "; 114 | command += str.str(); 115 | command += "/source.JPG"; 116 | 117 | system((string("cd ") + outDir).c_str()); 118 | system(command.c_str()); 119 | j = 0; 120 | for (auto it = scores[i].begin(); it != scores[i].end(); it++) 121 | { 122 | ++j; 123 | std::stringstream str2; 124 | command = "cp "; 125 | command += filenames[it->second]; 126 | command += " "; 127 | command += str.str(); 128 | command += "/"; 129 | str2 << j << "-"; 130 | str2 << it->first; 131 | command += str2.str(); 132 | command += ".JPG"; 133 | system(command.c_str()); 134 | } 135 | 136 | } 137 | /* 138 | { 139 | cout<first<<" "<second<first<<" "<second<