Commit af3194b3 authored by Otmane Lahlou's avatar Otmane Lahlou
Browse files

ADD: sift fast library

parent f32394d6
......@@ -27,7 +27,7 @@ ENDIF(NOT OTB_USE_EXTERNAL_EXPAT)
#Supress libraries not used by the 2.2.0 version
#SUBDIRS(BGL otbsvm dxflib InsightJournal otbossim otb6S otbgeotiff tinyXMLlib otbgalib otbkml)
SUBDIRS(BGL otbsvm dxflib InsightJournal otbossim otbossimplugins otb6S otbgeotiff tinyXMLlib otbkml otbliblas otbedison)
SUBDIRS(BGL otbsvm dxflib InsightJournal otbossim otbossimplugins otb6S otbgeotiff tinyXMLlib otbkml otbliblas otbedison otbsiftfast)
IF(BUILD_TESTING)
SUBDIRS( Dart )
......
zerofrog(@gmail.com)
\ No newline at end of file
cmake_minimum_required (VERSION 2.4.7)
project (otbsiftfast)
set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE )
include(CheckIncludeFile)
include(CheckLibraryExists)
include(CheckCXXSourceRuns)
include(CheckCXXCompilerFlag)
# check for SSE extensions
if( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX )
set(SSE_FLAGS)
set(CMAKE_REQUIRED_FLAGS "-msse2")
check_cxx_source_runs("
#include <emmintrin.h>
int main()
{
__m128d a, b;
double vals[2] = {0};
a = _mm_loadu_pd(vals);
b = _mm_add_pd(a,a);
_mm_storeu_pd(vals,b);
return 0;
}"
HAS_SSE2_EXTENSIONS)
set(CMAKE_REQUIRED_FLAGS "-msse")
check_cxx_source_runs("
#include <xmmintrin.h>
int main()
{
__m128 a, b;
float vals[4] = {0};
a = _mm_loadu_ps(vals);
b = a;
b = _mm_add_ps(a,b);
_mm_storeu_ps(vals,b);
return 0;
}"
HAS_SSE_EXTENSIONS)
set(CMAKE_REQUIRED_FLAGS)
if(HAS_SSE2_EXTENSIONS)
message(STATUS "Using SSE2 extensions")
set(SSE_FLAGS "-msse2 -mfpmath=sse")
elseif(HAS_SSE_EXTENSIONS)
message(STATUS "Using SSE extensions")
set(SSE_FLAGS "-msse -mfpmath=sse")
endif()
add_definitions(${SSE_FLAGS})
elseif(MSVC)
check_cxx_source_runs("
#include <emmintrin.h>
int main()
{
__m128d a, b;
double vals[2] = {0};
a = _mm_loadu_pd(vals);
b = _mm_add_pd(a,a);
_mm_storeu_pd(vals,b);
return 0;
}"
HAS_SSE2_EXTENSIONS)
if( HAS_SSE2_EXTENSIONS )
message(STATUS "Using SSE2 extensions")
add_definitions( "/arch:SSE2 /fp:fast -D__SSE__ -D__SSE2__" )
endif()
endif()
add_library(otbsiftfast libsiftfast.cpp)
# check for OpenMP
if( NOT DEFINED USE_OPENMP OR USE_OPENMP )
if( WIN32 )
CHECK_INCLUDE_FILE(omp.h HAVE_OMP_H)
if( HAVE_OMP_H )
message(STATUS "Using OpenMP")
check_cxx_compiler_flag(/openmp HAVE_OPENMP)
if( HAVE_OPENMP )
add_definitions("/openmp")
endif()
endif()
elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
# check if compilers supports -fopenmp
INCLUDE(CheckCCompilerFlag)
check_cxx_compiler_flag(-fopenmp HAVE_OPENMP)
check_library_exists(gomp omp_get_num_threads "" HAS_GOMP_LIB)
if( HAVE_OPENMP AND HAS_GOMP_LIB )
add_definitions("-fopenmp")
target_link_libraries(otbsiftfast gomp)
set(OPENMP_LFLAGS "-lgomp")
endif()
endif()
endif()
#Instal TARGET & FILES for otb-lib
INSTALL(TARGETS otbsiftfast
RUNTIME DESTINATION ${OTB_INSTALL_BIN_DIR} COMPONENT RuntimeLibraries
LIBRARY DESTINATION ${OTB_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries
ARCHIVE DESTINATION ${OTB_INSTALL_LIB_DIR} COMPONENT Development)
INSTALL(FILES siftfast.h
DESTINATION ${OTB_INSTALL_INCLUDE_DIR}/Utilities/otbsiftfast
COMPONENT Development)
libsiftfast: author zerofrog(@gmail.com)
----------------------------------------
The compilation system uses a cross-platform tool called cmake.
Linux/Mac OSX Instructions:
---------------------------
Just type
> make
This will set the project to be installed in /usr/local. To change the install directory type
> make prefix=/my/new/dir
To change other environment variables that cmake uses after initially making, type "cmake build"
OpenMP:
cmake will use OpenMP if it exists in your system. Note that OpenMP is available only on gcc versions >= 4.2. CMake checks this automatically, but you can force usage or disabling of it by adding "-DUSE_OPENMP=OFF" when manually running cmake. Use ON to force enabling.
Sometimes siftfast might fail to compile with OpenMP because libgomp.so is not setup properly to be used by shared objects. Checkout this tutorial here on how to compile the correct libgomp:
http://openrave.programmingvision.com/index.php?title=Misc:MatlabOpenMP
Basically the problem is that the default libgomp might be compiled with nodlopen flag refusing it to be dynamically loaded. The only way around this is to compile your own libgomp library and make sure libsiftfast is linking to it.
Matlab:
Read this if you are interested in using siftfast.m for matlab on Linux. When compiling a matlab mex file, you might get a message saying the gcc version is too high. If so, matlab will have a hard time locating the correct libstdc++.so file. In this case, go into /usr/local/share/sys/os/glnx86
and make libgcc_s and libstdc++ point to the /usr/lib versions
sudo mv libgcc_s.so.1 libgcc_s.so.1.back
sudo ln -s /lib/libgcc_s.so.1 libgcc_s.so.1
sudo rm libstdc++.so.6 (this was already a symbolic link)
sudo ln -s /usr/lib/libstdc++.so.6.0.9 libstdc++.so.6
Windows Instructions:
---------------------
Download cmake (http://www.cmake.org/), make sure to install it in the PATH. Then run runcmake.bat, that should generate visual studio files in the build folder. Open libsiftfast.sln and compile.
all:
@mkdir -p build
@if [ $(prefix) ]; then \
cd build && cmake -DCMAKE_INSTALL_PREFIX=$(prefix) -DCMAKE_BUILD_TYPE=Release ..; \
else \
cd build && cmake -DCMAKE_VERBOSE_MAKEFILE=OFF -DCMAKE_BUILD_TYPE=Release ..; \
fi
cd build && make
install:
cd build && make install
uninstall:
cd build && make uninstall
test: all
cd build && make test
test-future: all
cd build && make test-future
clean:
-cd build && make clean
rm -rf build
libsiftfast: author zerofrog(@gmail.com)
----------------------------------------
Various utilities to compute the SIFT features of a greyscale image. Packages offered:
*libsiftfast.so - main library that contains the sift code
*siftfast - really fast SIFT implementation using SSE and OpenMP (also fixes some bugs from lowe's code, so outputs are similar, but not exact). The usage is very similar to David Lowe's sift program. To input a greyscale image test.pgm and get the SIFT keys test.key use
> siftfast < test.pgm > test.key
The format of test.key is pretty self explanatory:
#keys #key_dimension
(for #keys) [x y scale orientation (for #key_dimension)[value] ]
*siftfast.m - matlab/octave mex file that uses sift_fast, just do [frames,descr]=sift_mex(grayscale_image);
frames is a 4xN matrix of [X,Y,scale,orientation],
descr is a 128xN matrix of normalized descriptors
To use the mex files, libsift_fast.so, Octave/Matlab need to be able to load it. A way to do it is to add its path to your LD_LIBRARY_PATH in your ~/.bashrc file, or in matlab with:
setenv('LD_LIBRARY_PATH',[getenv('LD_LIBRARY_PATH') ':' libsift_directory]);
SIFT is based on
David G. Lowe, "Distinctive image features from scale-invariant keypoints,
"International Journal of Computer Vision, 60, 2 (2004), pp. 91-110.
Using the Octave/Matlab mex files
---------------------------------
the octave and matlab mex files are installed in $prefix/share/siftfast/octave and $prefix/share/siftfast/matlab where $prefix is the installation directory you've provided (via CMAKE_INSTALL_PREFIX). The default value for $prefix is /usr/local
Add the path via addpath('$prefix/share/siftfast/matlab'), and then in octave/matlab type:
> help siftfast
this should give a help file on how to use it.
Comparisons with other SIFT Code
--------------------------------
The default setting of siftfast produce the same output as Lowe's free sift program. On a quad-core Core2Duo machine with OpenMP, siftfast goes about 6x faster than lowe's sift program for 640x480 images.
IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
STRING(REGEX REPLACE "\n" ";" files "${files}")
FOREACH(file ${files})
MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
IF(EXISTS "$ENV{DESTDIR}${file}")
EXEC_PROGRAM(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
IF(NOT "${rm_retval}" STREQUAL 0)
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
ENDIF(NOT "${rm_retval}" STREQUAL 0)
ELSE(EXISTS "$ENV{DESTDIR}${file}")
MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
ENDIF(EXISTS "$ENV{DESTDIR}${file}")
ENDFOREACH(file)
This diff is collapsed.
version=1.0
rm -rf libsiftfast-$version libsiftfast-$version-src
make prefix=`pwd`/libsiftfast-$version
make install
cp README libsiftfast-$version/
tar czf libsiftfast-$version-linux-i386.tgz libsiftfast-$version
svn export . libsiftfast-$version-src/
tar czf libsiftfast-$version-src.tgz libsiftfast-$version-src
rm -rf libsiftfast-$version libsiftfast-$version-src
\ No newline at end of file
#include "profiler.h"
////////////////////
// Small profiler //
////////////////////
#include <list>
#include <string>
#include <map>
#include <sys/timeb.h> // ftime(), struct timeb
#include <sys/time.h>
using namespace std;
int g_bWriteProfile=1;
static u64 luPerfFreq = 1000000;
inline u64 GET_PROFILE_TIME()
{
struct timeval t;
gettimeofday(&t, NULL);
return (u64)t.tv_sec*1000000+t.tv_usec;
}
struct DVPROFSTRUCT;
struct DVPROFSTRUCT
{
struct DATA
{
DATA(u64 time, u32 user = 0) : dwTime(time), dwUserData(user) {}
DATA() : dwTime(0), dwUserData(0) {}
u64 dwTime;
u32 dwUserData;
};
~DVPROFSTRUCT() {
list<DVPROFSTRUCT*>::iterator it = listpChild.begin();
while(it != listpChild.end() ) {
delete *it; *it = NULL;
++it;
}
}
list<DATA> listTimes; // before DVProfEnd is called, contains the global time it started
// after DVProfEnd is called, contains the time it lasted
// the list contains all the tracked times
char pname[256];
list<DVPROFSTRUCT*> listpChild; // other profilers called during this profiler period
};
struct DVPROFTRACK
{
u32 dwUserData;
DVPROFSTRUCT::DATA* pdwTime;
DVPROFSTRUCT* pprof;
};
list<DVPROFTRACK> g_listCurTracking; // the current profiling functions, the back element is the
// one that will first get popped off the list when DVProfEnd is called
// the pointer is an element in DVPROFSTRUCT::listTimes
list<DVPROFSTRUCT> g_listProfilers; // the current profilers, note that these are the parents
// any profiler started during the time of another is held in
// DVPROFSTRUCT::listpChild
list<DVPROFSTRUCT*> g_listAllProfilers; // ignores the hierarchy, pointer to elements in g_listProfilers
void DVProfRegister(const char* pname)
{
if( !g_bWriteProfile )
return;
list<DVPROFSTRUCT*>::iterator it = g_listAllProfilers.begin();
// while(it != g_listAllProfilers.end() ) {
//
// if( _tcscmp(pname, (*it)->pname) == 0 ) {
// (*it)->listTimes.push_back(timeGetTime());
// DVPROFTRACK dvtrack;
// dvtrack.pdwTime = &(*it)->listTimes.back();
// dvtrack.pprof = *it;
// g_listCurTracking.push_back(dvtrack);
// return;
// }
//
// ++it;
// }
// else add in a new profiler to the appropriate parent profiler
DVPROFSTRUCT* pprof = NULL;
if( g_listCurTracking.size() > 0 ) {
assert( g_listCurTracking.back().pprof != NULL );
g_listCurTracking.back().pprof->listpChild.push_back(new DVPROFSTRUCT());
pprof = g_listCurTracking.back().pprof->listpChild.back();
}
else {
g_listProfilers.push_back(DVPROFSTRUCT());
pprof = &g_listProfilers.back();
}
strncpy(pprof->pname, pname, 256);
// setup the profiler for tracking
pprof->listTimes.push_back(DVPROFSTRUCT::DATA(GET_PROFILE_TIME()));
DVPROFTRACK dvtrack;
dvtrack.pdwTime = &pprof->listTimes.back();
dvtrack.pprof = pprof;
dvtrack.dwUserData = 0;
g_listCurTracking.push_back(dvtrack);
// add to all profiler list
g_listAllProfilers.push_back(pprof);
}
void DVProfEnd(u32 dwUserData)
{
if( !g_bWriteProfile )
return;
if( g_listCurTracking.size() == 0 )
return;
DVPROFTRACK dvtrack = g_listCurTracking.back();
assert( dvtrack.pdwTime != NULL && dvtrack.pprof != NULL );
dvtrack.pdwTime->dwTime = GET_PROFILE_TIME()- dvtrack.pdwTime->dwTime;
dvtrack.pdwTime->dwUserData= dwUserData;
g_listCurTracking.pop_back();
}
struct DVTIMEINFO
{
DVTIMEINFO() : uInclusive(0), uExclusive(0) {}
u64 uInclusive, uExclusive;
};
map<string, DVTIMEINFO> mapAggregateTimes;
u64 DVProfWriteStruct(FILE* f, DVPROFSTRUCT* p, int ident)
{
fprintf(f, "%*s%s - ", ident, "", p->pname);
list<DVPROFSTRUCT::DATA>::iterator ittime = p->listTimes.begin();
u64 utime = 0;
while(ittime != p->listTimes.end() ) {
// if( ittime->dwTime > luPerfFreq*10 ) {
// ++ittime;
// continue;
// }
utime += ittime->dwTime;
if( ittime->dwUserData )
fprintf(f, "time: %d, user: 0x%8.8x", (u32)ittime->dwTime, ittime->dwUserData);
else
fprintf(f, "time: %d", (u32)ittime->dwTime);
++ittime;
}
// yes this is necessary, maps have problems with constructors on their type
map<string, DVTIMEINFO>::iterator ittimes = mapAggregateTimes.find(p->pname);
if( ittimes == mapAggregateTimes.end() ) {
ittimes = mapAggregateTimes.insert(map<string, DVTIMEINFO>::value_type(p->pname, DVTIMEINFO())).first;
ittimes->second.uExclusive = 0;
ittimes->second.uInclusive = 0;
}
ittimes->second.uInclusive += utime;
fprintf(f, "\n");
list<DVPROFSTRUCT*>::iterator itprof = p->listpChild.begin();
u64 uex = utime;
while(itprof != p->listpChild.end() ) {
uex -= DVProfWriteStruct(f, *itprof, ident+4);
++itprof;
}
if( uex > utime ) {
uex = 0;
//RAVEPRINT(L"profiling precision error!\n");
}
ittimes->second.uExclusive += uex;
return utime;
}
void DVProfWrite(const char* pfilename, u32 frames)
{
assert( pfilename != NULL );
FILE* f = fopen(pfilename, "w");
// pop back any unused
mapAggregateTimes.clear();
list<DVPROFSTRUCT>::iterator it = g_listProfilers.begin();
while(it != g_listProfilers.end() ) {
DVProfWriteStruct(f, &(*it), 0);
++it;
}
{
map<string, DVTIMEINFO>::iterator it;
fprintf(f, "\n\n--------------------------------------------------------------\n\n");
u64 uTotal[2]; uTotal[0] = uTotal[1] = 0;
double fiTotalTime[2]; fiTotalTime[0] = fiTotalTime[1] = 0;
for(it = mapAggregateTimes.begin(); it != mapAggregateTimes.end(); ++it) {
uTotal[0] += it->second.uExclusive;
uTotal[1] += it->second.uInclusive;
}
fprintf(f, "total times (%d): ex: %Lu ", frames, 1000000*uTotal[0]/(luPerfFreq*(u64)frames));
fprintf(f, "inc: %Lu\n", 1000000 * uTotal[1]/(luPerfFreq*(u64)frames));
if( uTotal[0] > 0 )
fiTotalTime[0] = 1.0 / (double)uTotal[0];
if( uTotal[1] > 0 )
fiTotalTime[1] = 1.0 / (double)uTotal[1];
// output the combined times
for(it = mapAggregateTimes.begin(); it != mapAggregateTimes.end(); ++it) {
fprintf(f, "%s - ex: %f%% (%fs) inc: %f%%\n", it->first.c_str(),
100.0f*(float)((double)it->second.uExclusive * fiTotalTime[0]),
(double)it->second.uExclusive/(double)luPerfFreq,
100.0f*(float)((double)it->second.uInclusive * fiTotalTime[1]));
}
}
fclose(f);
}
void DVProfClear()
{
g_listCurTracking.clear();
g_listProfilers.clear();
g_listAllProfilers.clear();
}
#ifndef SMALL_PROFILER_H
#define SMALL_PROFILER_H
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed long long s64;
#include <assert.h>
////
// profiling
///
extern int g_bWriteProfile; // global variable to enable/disable profiling (if DVPROFILE is defined)
// IMPORTANT: For every Reigster there must be an End
void DVProfRegister(const char* pname); // first checks if this profiler exists in g_listProfilers
void DVProfEnd(u32 dwUserData);
void DVProfWrite(const char* pfilename, u32 frames = 1);
void DVProfClear(); // clears all the profilers
#if defined(DVPROFILE)
#ifdef _MSC_VER
#ifndef __PRETTY_FUNCTION__
#define __PRETTY_FUNCTION__ __FUNCTION__
#endif
#endif
#define DVSTARTPROFILE() DVProfileFunc _pf(__FUNCTION__);
class DVProfileFunc
{
public:
u32 dwUserData;
DVProfileFunc(const char* pname) { DVProfRegister(pname); dwUserData = 0; }
DVProfileFunc(const char* pname, u32 dwUserData) : dwUserData(dwUserData) { DVProfRegister(pname); }
~DVProfileFunc() { DVProfEnd(dwUserData); }
};
#else
#define DVSTARTPROFILE()
class DVProfileFunc
{
public:
u32 dwUserData;
inline DVProfileFunc(const char* pname) {}
inline DVProfileFunc(const char* pname, u32 dwUserData) { }
~DVProfileFunc() {}
};
#endif
#endif
mkdir build
cd build
cmake ..
// exact C++ implementation of lowe's sift program
// author: zerofrog(@gmail.com), Sep 2008
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//Lesser GNU General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public License
//along with this program. If not, see <http://www.gnu.org/licenses/>.
// This source code was carefully calibrated to match David Lowe's SIFT features program
#include "siftfast.h"
#include <iostream>
#include <sys/timeb.h> // ftime(), struct timeb
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#endif
#include <cstdlib>
#include <assert.h>
using namespace std;