diff --git a/3rdparty/NaturalStringCompare2/NaturalStringCompare.cpp b/3rdparty/NaturalStringCompare2/NaturalStringCompare.cpp new file mode 100755 index 0000000..67d3551 --- /dev/null +++ b/3rdparty/NaturalStringCompare2/NaturalStringCompare.cpp @@ -0,0 +1,167 @@ +/* + * This software was written by people from OnShore Consulting services LLC + * and placed in the public domain. + * + * We reserve no legal rights to any of this. You are free to do + * whatever you want with it. And we make no guarantee or accept + * any claims on damages as a result of this. + * + * If you change the software, please help us and others improve the + * code by sending your modifications to us. If you choose to do so, + * your changes will be included under this license, and we will add + * your name to the list of contributors. +*/ + +#include "NaturalStringCompare.h" +#include + +#define INCBUF() { buffer += curr; ++pos; curr = ( pos < string.length() ) ? string[ pos ] : QChar(); } + +void ExtractToken( QString & buffer, const QString & string, int & pos, bool & isNumber ) +{ + buffer.clear(); + if ( string.isNull() || pos >= string.length() ) + return; + + isNumber = false; + QChar curr = string[ pos ]; + if ( curr == '-' || curr == '+' || curr.isDigit() ) + { + if ( curr == '-' || curr == '+' ) + INCBUF(); + + if ( !curr.isNull() && curr.isDigit() ) + { + isNumber = true; + while ( curr.isDigit() ) + INCBUF(); + + if ( curr == '.' ) + { + INCBUF(); + while ( curr.isDigit() ) + INCBUF(); + } + + if ( !curr.isNull() && curr.toLower() == 'e' ) + { + INCBUF(); + if ( curr == '-' || curr == '+' ) + INCBUF(); + + if ( curr.isNull() || !curr.isDigit() ) + isNumber = false; + else + while ( curr.isDigit() ) + INCBUF(); + } + } + } + + if ( !isNumber ) + { + while ( curr != '-' && curr != '+' && !curr.isDigit() && pos < string.length() ) + INCBUF(); + } +} + +int NaturalStringCompare( const QString & lhs, const QString & rhs, Qt::CaseSensitivity caseSensitive ) +{ + int ii = 0; + int jj = 0; + + QString lhsBufferQStr; + QString rhsBufferQStr; + + int retVal = 0; + + // all status values are created on the stack outside the loop to make as fast as possible + bool lhsNumber = false; + bool rhsNumber = false; + + double lhsValue = 0.0; + double rhsValue = 0.0; + bool ok1; + bool ok2; + + while ( retVal == 0 && ii < lhs.length() && jj < rhs.length() ) + { + ExtractToken( lhsBufferQStr, lhs, ii, lhsNumber ); + ExtractToken( rhsBufferQStr, rhs, jj, rhsNumber ); + + if ( !lhsNumber && !rhsNumber ) + { + // both strings curr val is a simple strcmp + retVal = lhsBufferQStr.compare( rhsBufferQStr, caseSensitive ); + + int maxLen = qMin( lhsBufferQStr.length(), rhsBufferQStr.length() ); + QString tmpRight = rhsBufferQStr.left( maxLen ); + QString tmpLeft = lhsBufferQStr.left( maxLen ); + if ( tmpLeft.compare( tmpRight, caseSensitive ) == 0 ) + { + retVal = lhsBufferQStr.length() - rhsBufferQStr.length(); + if ( retVal ) + { + QChar nextChar; + if ( ii < lhs.length() ) // more on the lhs + nextChar = lhs[ ii ]; + else if ( jj < rhs.length() ) // more on the rhs + nextChar = rhs[ jj ]; + + bool nextIsNum = ( nextChar == '-' || nextChar == '+' || nextChar.isDigit() ); + + if ( nextIsNum ) + retVal = -1*retVal; + } + } + } + else if ( lhsNumber && rhsNumber ) + { + // both numbers, convert and compare + lhsValue = lhsBufferQStr.toDouble( &ok1 ); + rhsValue = rhsBufferQStr.toDouble( &ok2 ); + if ( !ok1 || !ok2 ) + retVal = lhsBufferQStr.compare( rhsBufferQStr, caseSensitive ); + else if ( lhsValue > rhsValue ) + retVal = 1; + else if ( lhsValue < rhsValue ) + retVal = -1; + } + else + { + // completely arebitrary that a number comes before a string + retVal = lhsNumber ? -1 : 1; + } + } + + if ( retVal != 0 ) + return retVal; + if ( ii < lhs.length() ) + return -1; + else if ( jj < rhs.length() ) + return 1; + else + return 0; +} + +bool NaturalStringCompareLessThan( const QString & lhs, const QString & rhs ) +{ + return NaturalStringCompare( lhs, rhs, Qt::CaseSensitive ) < 0; +} + +bool NaturalStringCaseInsensitiveCompareLessThan( const QString & lhs, const QString & rhs ) +{ + return NaturalStringCompare( lhs, rhs, Qt::CaseInsensitive ) < 0; +} + +QStringList NaturalStringSort( const QStringList & list, Qt::CaseSensitivity caseSensitive ) +{ + QStringList retVal = list; + if ( caseSensitive == Qt::CaseSensitive ) + qSort( retVal.begin(), retVal.end(), NaturalStringCompareLessThan ); + else + qSort( retVal.begin(), retVal.end(), NaturalStringCaseInsensitiveCompareLessThan ); + return retVal; +} + + diff --git a/3rdparty/NaturalStringCompare2/NaturalStringCompare.h b/3rdparty/NaturalStringCompare2/NaturalStringCompare.h new file mode 100755 index 0000000..e19c061 --- /dev/null +++ b/3rdparty/NaturalStringCompare2/NaturalStringCompare.h @@ -0,0 +1,26 @@ +#ifndef __NATURALSTRINGCOMPARE_H +#define __NATURALSTRINGCOMPARE_H + +/* + * This software was written by people from OnShore Consulting services LLC + * and placed in the public domain. + * + * We reserve no legal rights to any of this. You are free to do + * whatever you want with it. And we make no guarantee or accept + * any claims on damages as a result of this. + * + * If you change the software, please help us and others improve the + * code by sending your modifications to us. If you choose to do so, + * your changes will be included under this license, and we will add + * your name to the list of contributors. +*/ + +#include +#include + +int NaturalStringCompare( const QString & lhs, const QString & rhs, Qt::CaseSensitivity caseSensitive=Qt::CaseSensitive ); +QStringList NaturalStringSort( const QStringList & list, Qt::CaseSensitivity caseSensitive=Qt::CaseSensitive ); +bool NaturalStringCompareLessThan( const QString & lhs, const QString & rhs ); +bool NaturalStringCaseInsensitiveCompareLessThan( const QString & lhs, const QString & rhs ); + +#endif diff --git a/3rdparty/NaturalStringCompare2/NaturalStringCompare.pro b/3rdparty/NaturalStringCompare2/NaturalStringCompare.pro new file mode 100755 index 0000000..0fc7ab3 --- /dev/null +++ b/3rdparty/NaturalStringCompare2/NaturalStringCompare.pro @@ -0,0 +1,16 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Mon Feb 25 10:13:54 2008 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += NaturalStringCompare.h +SOURCES += main.cpp NaturalStringCompare.cpp + +CONFIG += console qtestlib +QT-=gui + diff --git a/3rdparty/NaturalStringCompare2/main.cpp b/3rdparty/NaturalStringCompare2/main.cpp new file mode 100755 index 0000000..7f4a14f --- /dev/null +++ b/3rdparty/NaturalStringCompare2/main.cpp @@ -0,0 +1,181 @@ +/* + * This software was written by people from OnShore Consulting services LLC + * and placed in the public domain. + * + * We reserve no legal rights to any of this. You are free to do + * whatever you want with it. And we make no guarantee or accept + * any claims on damages as a result of this. + * + * If you change the software, please help us and others improve the + * code by sending your modifications to us. If you choose to do so, + * your changes will be included under this license, and we will add + * your name to the list of contributors. +*/ + +#include +#include +#include +#include "NaturalStringCompare.h" + +class CTestNaturalStringCompare : public QObject +{ + Q_OBJECT +private slots: + void compareString() + { + QString str1 = "abcdef"; + QString str2 = "aabc"; + QVERIFY( NaturalStringCompare(str1,str2) > 0 ); + QVERIFY( NaturalStringCompare(str2,str1) < 0 ); + QVERIFY( NaturalStringCompare(str1,str1) == 0 ); + QVERIFY( NaturalStringCompare(str2,str2) == 0 ); + + QVERIFY( NaturalStringCompare(str1,str1.left(2)) > 0 ); + QVERIFY( NaturalStringCompare(str1.left(2),str1) < 0 ); + } + + void compareInteger() + { + for( int ii = -15; ii <= 15; ii++ ) + { + QString currVal = QString( "%1" ).arg( ii ); + QString nextVal = QString( "%1" ).arg( ii+1 ); + + QVERIFY( NaturalStringCompare(currVal,nextVal) < 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal, "prefix" + nextVal) < 0 ); + QVERIFY( NaturalStringCompare( currVal + "postfix", nextVal + "postfix") < 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal + "postfix", "prefix" + nextVal + "postfix") < 0 ); + + QVERIFY( NaturalStringCompare(nextVal,currVal) > 0 ); + QVERIFY( NaturalStringCompare( "prefix" + nextVal, "prefix" + currVal) > 0 ); + QVERIFY( NaturalStringCompare( nextVal + "postfix", currVal + "postfix") > 0 ); + QVERIFY( NaturalStringCompare( "prefix" + nextVal + "postfix", "prefix" + currVal + "postfix") > 0 ); + + QVERIFY( NaturalStringCompare(currVal,currVal) == 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal, "prefix" + currVal) == 0 ); + QVERIFY( NaturalStringCompare( currVal + "postfix", currVal + "postfix") == 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal + "postfix", "prefix" + currVal + "postfix") == 0 ); + + QVERIFY( NaturalStringCompare( "prefix" + currVal + "middle" + currVal, "prefix" + currVal + "middle" + nextVal ) < 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal + "middle" + nextVal, "prefix" + currVal + "middle" + currVal ) > 0 ); + } + } + + void compareDouble() + { + for( int ii = -15; ii <= 15; ii++ ) + { + QString currVal = QString( "%1" ).arg( 1.0*ii - 0.05 ); + QString nextVal = QString( "%1" ).arg( 1.0*ii + 0.05 ); + + QVERIFY( NaturalStringCompare(currVal,nextVal) < 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal, "prefix" + nextVal) < 0 ); + QVERIFY( NaturalStringCompare( currVal + "postfix", nextVal + "postfix") < 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal + "postfix", "prefix" + nextVal + "postfix") < 0 ); + + QVERIFY( NaturalStringCompare(nextVal,currVal) > 0 ); + QVERIFY( NaturalStringCompare( "prefix" + nextVal, "prefix" + currVal) > 0 ); + QVERIFY( NaturalStringCompare( nextVal + "postfix", currVal + "postfix") > 0 ); + QVERIFY( NaturalStringCompare( "prefix" + nextVal + "postfix", "prefix" + currVal + "postfix") > 0 ); + + QVERIFY( NaturalStringCompare(currVal,currVal) == 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal, "prefix" + currVal) == 0 ); + QVERIFY( NaturalStringCompare( currVal + "postfix", currVal + "postfix") == 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal + "postfix", "prefix" + currVal + "postfix") == 0 ); + + QVERIFY( NaturalStringCompare( "prefix" + currVal + "middle" + currVal, "prefix" + currVal + "middle" + nextVal ) < 0 ); + QVERIFY( NaturalStringCompare( "prefix" + currVal + "middle" + nextVal, "prefix" + currVal + "middle" + currVal ) > 0 ); + } + } + + void sortStringList() + { + QStringList orig; + for( int ii = 15; ii >= -15; ii-- ) + { + QString currVal = QString( "%1" ).arg( 1.0*ii - 0.05 ); + orig << "prefix" + currVal + "postfix"; + orig << "Prefix" + currVal + "posTfIx"; + currVal = QString( "%1" ).arg( ii ); + orig << "Prefix" + currVal; + orig << currVal + "PostFIX"; + orig << currVal + "PostFix"; + } + + QStringList toBeSorted = orig; + qSort( toBeSorted.begin(), toBeSorted.end(), NaturalStringCompareLessThan ); + + + int cnt = toBeSorted.count(); + QVERIFY( toBeSorted.count() == 31 * 5 ); + QVERIFY( toBeSorted.front() == "-15PostFIX" ); + QVERIFY( toBeSorted.back() == "prefix14.95postfix" ); + + QStringList tmp = NaturalStringSort( orig ); + QVERIFY( tmp.count() == toBeSorted.count() ); + for( int ii = 0; ii < tmp.count(); ++ii ) + { + QVERIFY( tmp[ ii ] == toBeSorted[ ii ] ); + } + + toBeSorted = orig; + qSort( toBeSorted.begin(), toBeSorted.end(), NaturalStringCaseInsensitiveCompareLessThan ); + cnt = toBeSorted.count(); + QVERIFY( toBeSorted.count() == 31 * 5 ); + // QVERIFY( toBeSorted.front() == "-15PostFix" ); for case insensitive, PostFix vs PostFIX is non-deterministic which comes firs + // QVERIFY( toBeSorted.back() == "Prefix15" ); + + tmp = NaturalStringSort( orig, Qt::CaseInsensitive ); + QVERIFY( tmp.count() == toBeSorted.count() ); + for( int ii = 0; ii < tmp.count(); ++ii ) + { + QVERIFY( tmp[ ii ] == toBeSorted[ ii ] ); + } + } + + void lingfaYangTest() + { + // this is actually not what the spec wants, however, the 8 causes the first compare to be + // ppt/slides/slide vs ppt/slides/slide.xml which means embedded numbers will come first. + + QVERIFY( NaturalStringCompare( "ppt/slides/slide8.xml", "ppt/slides/slide.xml2", Qt::CaseInsensitive ) > 0 ); + + QStringList orderedList = QStringList() // from Ligfa Ya email + << "[Content_Types].xml" + << "_rels/.rels" + << "ppt/media/image8.wmf" + << "ppt/media/image9.jpeg" + << "ppt/media/image10.png" + << "ppt/media/image11.gif" + << "ppt/slides/_rels/slide9.xml.rels" + << "ppt/slides/_rels/slide10.xml.rels" + << "ppt/slides/slide.xml" + << "ppt/slides/slide8.xml" + << "PPT/SLIDES/SLIDE9.XML" + << "ppt/slides/slide10.xml" + << "ppt/slides/slide11.xml" + << "slide.xml" + ; + + QStringList toBeSorted = QStringList(); + QStringList tmp = orderedList; + qsrand( QDateTime::currentDateTime().toTime_t() ); + while( !tmp.isEmpty() ) + { + double randVal = qrand(); + randVal /= RAND_MAX; + int val = (int)( tmp.count() - 1 ) * randVal; + toBeSorted << tmp[ val ]; + tmp.removeAt( val ); + } + tmp = NaturalStringSort( toBeSorted, Qt::CaseInsensitive ); + for( int ii = 0; ii < tmp.count(); ++ii ) + { + QVERIFY( tmp[ ii ] == orderedList[ ii ] ); + } + } + +}; + +QTEST_MAIN(CTestNaturalStringCompare) +#include "main.moc" diff --git a/CHANGELOG.md b/CHANGELOG.md index 5259d0d..f30a05a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ 0.3.0 - ??/??/?? ================ +* Added wrapper to NEC Latent SDK +* Enrolling files/folders are now sorted naturally instead of alpha numerically * YouTubeFacesDBTransform implements Dr. Wolf's experimental protocol * NEC3 refactored diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d3cdd0..ce6325f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,8 +39,6 @@ endif() if(WIN32) configure_file(${BR_SHARE_DIR}/resources.rc.in resources.rc) set(BR_RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/resources.rc) -elseif(APPLE) - set(BR_RESOURCES ${NATIVE_ICON}) endif() # Build options @@ -72,8 +70,11 @@ set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${Qt5Core_QTMAIN_LIBRARIES}) # Find OpenCV find_package(OpenCV REQUIRED) +set(OPENCV_DEPENDENCIES opencv_core opencv_features2d opencv_flann opencv_highgui opencv_imgproc opencv_ml opencv_nonfree opencv_objdetect opencv_photo) set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${OpenCV_LIBS}) -set(OPENCV_DEPENDENCIES calib3d contrib core features2d flann gpu highgui imgproc legacy ml nonfree objdetect photo stitching video videostab) + +# Find NaturalStringCompare +find_package(NaturalStringCompare REQUIRED) # Compiler flags if(UNIX) diff --git a/app/examples/CMakeLists.txt b/app/examples/CMakeLists.txt index ff7be55..1685ae7 100644 --- a/app/examples/CMakeLists.txt +++ b/app/examples/CMakeLists.txt @@ -5,5 +5,5 @@ foreach(EXAMPLE ${EXAMPLES}) qt5_use_modules(${EXAMPLE_BASENAME} ${QT_DEPENDENCIES}) target_link_libraries(${EXAMPLE_BASENAME} openbr ${BR_THIRDPARTY_LIBS}) install(TARGETS ${EXAMPLE_BASENAME} RUNTIME DESTINATION bin) - add_test("${EXAMPLE_BASENAME}_test" ${EXAMPLE_BASENAME}) + add_test(NAME ${EXAMPLE_BASENAME}_test WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${EXAMPLE_BASENAME}) endforeach() diff --git a/app/openbr-gui/CMakeLists.txt b/app/openbr-gui/CMakeLists.txt index 1d62a5d..3b8c676 100644 --- a/app/openbr-gui/CMakeLists.txt +++ b/app/openbr-gui/CMakeLists.txt @@ -13,7 +13,6 @@ if(NOT ${BR_EMBEDDED}) SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR} LINK_INTERFACE_LIBRARIES "") target_link_libraries(openbr-gui openbr ${OpenCV_LIBS}) - add_cppcheck(openbr-gui) install(FILES ${HEADERS} DESTINATION include/openbr-gui) install(TARGETS openbr-gui RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) diff --git a/sdk/CMakeLists.txt b/sdk/CMakeLists.txt index 65b44a7..a8c2574 100644 --- a/sdk/CMakeLists.txt +++ b/sdk/CMakeLists.txt @@ -7,7 +7,7 @@ aux_source_directory(. SRC) aux_source_directory(core BR_CORE) include(plugins/plugins.cmake) -add_library(openbr SHARED ${SRC} ${BR_CORE} ${BR_PLUGIN} ${BR_THIRDPARTY_SRC} ${BR_RESOURCES}) +add_library(openbr SHARED ${SRC} ${BR_CORE} ${BR_PLUGIN} ${BR_THIRDPARTY_SRC} ${BR_RESOURCES} ${NATURALSTRINGCOMPARE_SRC}) qt5_use_modules(openbr ${QT_DEPENDENCIES}) set_target_properties(openbr PROPERTIES DEFINE_SYMBOL BR_LIBRARY diff --git a/sdk/core/core.cpp b/sdk/core/core.cpp index b2868f5..acdf227 100644 --- a/sdk/core/core.cpp +++ b/sdk/core/core.cpp @@ -174,12 +174,19 @@ struct AlgorithmCore void retrieveOrEnroll(const File &file, QScopedPointer &gallery, FileList &galleryFiles) { - gallery.reset(Gallery::make(file)); - if ((file.suffix() != "gal") && (file.suffix() != "mem")) { - enroll(file); - gallery.reset(Gallery::make(getMemoryGallery(file))); + if ((file.suffix() == "gal") || (file.suffix() == "mem")) { + // Retrieve it + gallery.reset(Gallery::make(file)); galleryFiles = gallery->files(); } else { + // Was it already enrolled in memory? + gallery.reset(Gallery::make(getMemoryGallery(file))); + galleryFiles = gallery->files(); + if (!galleryFiles.isEmpty()) return; + + // Enroll it + enroll(file); + gallery.reset(Gallery::make(getMemoryGallery(file))); galleryFiles = gallery->files(); } } @@ -242,7 +249,7 @@ private: if (!file.isEmpty()) description = file; if (QFileInfo(description).exists()) { - qDebug("Loading %s", qPrintable(QFileInfo(description).fileName())); + if (Globals->verbose) qDebug("Loading %s", qPrintable(QFileInfo(description).fileName())); load(description); return; } diff --git a/sdk/core/qtutils.cpp b/sdk/core/qtutils.cpp index be837d1..8714e25 100644 --- a/sdk/core/qtutils.cpp +++ b/sdk/core/qtutils.cpp @@ -28,6 +28,7 @@ #include #include +#include "NaturalStringCompare.h" #include "qtutils.h" using namespace br; @@ -37,12 +38,12 @@ QStringList QtUtils::getFiles(QDir dir, bool recursive) dir = QDir(dir.canonicalPath()); QStringList files; - foreach (const QString &file, dir.entryList(QDir::Files)) + foreach (const QString &file, NaturalStringSort(dir.entryList(QDir::Files))) files.append(QDir::cleanPath(dir.absoluteFilePath(file))); if (!recursive) return files; - foreach (const QString &folder, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + foreach (const QString &folder, NaturalStringSort(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))) { QDir subdir(dir); bool success = subdir.cd(folder); if (!success) qFatal("cd failure."); files.append(getFiles(subdir, true)); diff --git a/sdk/openbr_export.cpp b/sdk/openbr_export.cpp index 581c02c..13c9821 100644 --- a/sdk/openbr_export.cpp +++ b/sdk/openbr_export.cpp @@ -84,19 +84,18 @@ $ br -help * -# Grab any available Visual Studio Updates. * -# Download CMake 2.8.10.2 and install. * -# During installation setup select "add CMake to PATH". - * -# Download OpenCV 2.4.3 and unarchive. + * -# Download OpenCV 2.4.4. * -# Consider the free open source program 7-Zip if you need a program to unarchive tarballs. - * -# Move the "OpenCV-2.4.3" folder to "C:\". - * -# In "C:\OpenCV-2.4.3\modules\objdetect\src\haar.cpp" line 55, change "#ifdev CV_AVX" to "#ifdef 0". + * -# Move the "OpenCV-2.4.4" folder to "C:\". * -# Open "VS2012 x64 Cross Tools Command Prompt" (from the Start Menu, select "All Programs" -> "Microsoft Visual Studio 2012" -> "Visual Studio Tools" -> "VS2012 x64 Cross Tools Command Prompt") and enter: * \code - * $ cd C:\OpenCV-2.4.3 + * $ cd C:\OpenCV-2.4.4 * $ mkdir build-msvc2012 * $ cd build-msvc2012 - * $ cmake -G "NMake Makefiles" -D BUILD_PERF_TESTS=OFF -D BUILD_TESTS=OFF -D WITH_FFMPEG=OFF -D CMAKE_BUILD_TYPE=Debug .. + * $ cmake -G "NMake Makefiles" -DBUILD_opencv_java=OFF -DBUILD_opencv_world=ON -DBUILD_PERF_TESTS=OFF -DBUILD_TESTS=OFF -DWITH_FFMPEG=OFF -DCMAKE_BUILD_TYPE=Debug .. * $ nmake * $ nmake install - * $ cmake -D CMAKE_BUILD_TYPE=Release .. + * $ cmake -DCMAKE_BUILD_TYPE=Release .. * $ nmake * $ nmake install * $ nmake clean @@ -129,7 +128,7 @@ $ br -help * $ cd C:\openbr * $ mkdir build-msvc2012 * $ cd build-msvc2012 - * $ cmake -G "CodeBlocks - NMake Makefiles" -D CMAKE_PREFIX_PATH="C:/OpenCV-2.4.3/build-msvc2012/install;C:/Qt/5.0.1/msvc2012" -D CMAKE_INSTALL_PREFIX="./install" -D BR_INSTALL_DEPENDENCIES=ON -D CMAKE_BUILD_TYPE=Release .. + * $ cmake -G "CodeBlocks - NMake Makefiles" -DCMAKE_PREFIX_PATH="C:/OpenCV-2.4.4/build-msvc2012/install;C:/Qt/5.0.1/msvc2012" -DCMAKE_INSTALL_PREFIX="./install" -DBR_INSTALL_DEPENDENCIES=ON -DCMAKE_BUILD_TYPE=Release .. * $ nmake * $ nmake install * \endcode @@ -164,18 +163,18 @@ $ br -help * -# Move "x86_64-w64-mingw32-gcc-4.7.2-release-win64_rubenvb\mingw64" to "C:\". * -# Download CMake 2.8.10.2 and install. * -# During installation setup select "add CMake to PATH". - * -# Download OpenCV 2.4.3 and unarchive. + * -# Download OpenCV 2.4.4. * -# Consider the free open source program 7-Zip if you need a program to unarchive tarballs. - * -# Move the "OpenCV-2.4.3" folder to "C:\". + * -# Move the "OpenCV-2.4.4" folder to "C:\". * -# From the MinGW-w64 Command Prompt (double-click "C:\mingw64\mingw64env.cmd"): * \code - * $ cd C:\OpenCV-2.4.3 + * $ cd C:\OpenCV-2.4.4 * $ mkdir build-mingw64 * $ cd build-mingw64 - * $ cmake -G "MinGW Makefiles" -D BUILD_PERF_TESTS=OFF -D BUILD_TESTS=OFF -D WITH_FFMPEG=OFF -D CMAKE_BUILD_TYPE=Debug .. + * $ cmake -G "MinGW Makefiles" -DBUILD_opencv_java=OFF -DBUILD_opencv_world=ON -DBUILD_PERF_TESTS=OFF -DBUILD_TESTS=OFF -DWITH_FFMPEG=OFF -DCMAKE_BUILD_TYPE=Debug .. * $ mingw32-make * $ mingw32-make install - * $ cmake -D CMAKE_BUILD_TYPE=Release + * $ cmake -DCMAKE_BUILD_TYPE=Release * $ mingw32-make * $ mingw32-make install * $ mingw32-make clean @@ -208,7 +207,7 @@ $ br -help * $ cd C:\openbr * $ mkdir build-mingw64 * $ cd build-mingw64 - * $ cmake -G "CodeBlocks - MinGW Makefiles" -D CMAKE_RC_COMPILER="C:/mingw64/bin/windres.exe" -D CMAKE_PREFIX_PATH="C:/OpenCV-2.4.3/build-mingw64/install;C:/Qt/5.0.1/mingw64" -D CMAKE_INSTALL_PREFIX="./install" -D BR_INSTALL_DEPENDENCIES=ON -D CMAKE_BUILD_TYPE=Release .. + * $ cmake -G "CodeBlocks - MinGW Makefiles" -DCMAKE_RC_COMPILER="C:/mingw64/bin/windres.exe" -DCMAKE_PREFIX_PATH="C:/OpenCV-2.4.4/build-mingw64/install;C:/Qt/5.0.1/mingw64" -DCMAKE_INSTALL_PREFIX="./install" -DBR_INSTALL_DEPENDENCIES=ON -DCMAKE_BUILD_TYPE=Release .. * $ mingw32-make * $ mingw32-make install * \endcode @@ -250,18 +249,18 @@ $ br -help * $ cd .. * $ rm -r cmake-2.8.10.2 * \endcode - * -# Download OpenCV 2.4.3. + * -# Download OpenCV 2.4.4. * \code * $ cd ~/Downloads - * $ tar -xf OpenCV-2.4.3.tar.bz2 - * $ cd OpenCV-2.4.3 + * $ tar -xf OpenCV-2.4.4.tar.bz2 + * $ cd OpenCV-2.4.4 * $ mkdir build * $ cd build - * $ cmake .. + * $ cmake -DBUILD_opencv_java=OFF -DBUILD_opencv_world=OFF -DCMAKE_BUILD_TYPE=Release .. * $ make -j4 * $ sudo make install * $ cd ../.. - * $ rm -r OpenCV-2.4.3 + * $ rm -r OpenCV-2.4.4 * \endcode * -# Download Qt 5.0.1 and install. * -# Create a GitHub account, follow their instructions for setting up Git. @@ -276,7 +275,7 @@ $ br -help * $ cd openbr * $ mkdir build * $ cd build - * $ cmake -D CMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/clang_64 -D CMAKE_BUILD_TYPE=Release .. + * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/clang_64 -DCMAKE_BUILD_TYPE=Release .. * $ make -j4 * $ make install * \endcode @@ -296,7 +295,7 @@ $ br -help * $ make package * \endcode * -# Build OpenBR documentation! - * -# Download Doxygen 1.8.3.1 and install: + * -# Download Doxygen 1.8.2 and install: * \code * $ cd ~/Downloads * $ tar -xf doxygen-1.8.2.src.tar.gz @@ -310,7 +309,7 @@ $ br -help * -# Modify build settings and recompile: * \code * $ cd openbr/build - * $ cmake -D BR_BUILD_DOCUMENTATION=ON .. + * $ cmake -DBR_BUILD_DOCUMENTATION=ON .. * $ make -j4 * $ open html/index.html * \endcode @@ -334,18 +333,18 @@ $ br -help * $ cd .. * $ rm -r cmake-2.8.10.2 * \endcode - * -# Download OpenCV 2.4.3. + * -# Download OpenCV 2.4.4. * \code * $ cd ~/Downloads - * $ tar -xf OpenCV-2.4.3.tar.bz2 - * $ cd OpenCV-2.4.3 + * $ tar -xf OpenCV-2.4.4.tar.bz2 + * $ cd OpenCV-2.4.4 * $ mkdir build * $ cd build - * $ cmake -D CMAKE_BUILD_TYPE=Release .. + * $ cmake -DBUILD_opencv_java=OFF -DBUILD_opencv_world=ON -DCMAKE_BUILD_TYPE=Release .. * $ make -j4 * $ sudo make install * $ cd ../.. - * $ rm -r OpenCV-2.4.3 + * $ rm -r OpenCV-2.4.4 * \endcode * -# Download Qt 5.0.1. * \code @@ -366,7 +365,7 @@ $ br -help * $ cd openbr * $ mkdir build * $ cd build - * $ cmake -D CMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -D CMAKE_BUILD_TYPE=Release .. + * $ cmake -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -DCMAKE_BUILD_TYPE=Release .. * $ make -j4 * $ make install * \endcode @@ -401,18 +400,18 @@ $ br -help * $ cd .. * $ rm -r cmake-2.8.10.2 * \endcode - * -# Download OpenCV 2.4.3. + * -# Download OpenCV 2.4.4. * \code * $ cd ~/Downloads - * $ tar -xf OpenCV-2.4.3.tar.bz2 - * $ cd OpenCV-2.4.3 + * $ tar -xf OpenCV-2.4.4.tar.bz2 + * $ cd OpenCV-2.4.4 * $ mkdir build * $ cd build - * $ cmake -D CMAKE_BUILD_TYPE=Release .. + * $ cmake cmake -DBUILD_opencv_java=OFF -DBUILD_opencv_world=ON -DCMAKE_BUILD_TYPE=Release .. * $ make -j4 * $ sudo make install * $ cd ../.. - * $ rm -r OpenCV-2.4.3 + * $ rm -r OpenCV-2.4.4 * \endcode * -# Download Qt 5.0.1. * \code @@ -433,7 +432,7 @@ $ br -help * $ cd openbr * $ mkdir build-icc * $ cd build-icc - * $ cmake -D CMAKE_C_COMPILER=/opt/intel/bin/icc -D CMAKE_CXX_COMPILER=/opt/intel/bin/icpc -D CMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -D CMAKE_BUILD_TYPE=Release .. + * $ cmake -DCMAKE_C_COMPILER=/opt/intel/bin/icc -DCMAKE_CXX_COMPILER=/opt/intel/bin/icpc -DCMAKE_PREFIX_PATH=~/Qt5.0.1/5.0.1/gcc_64 -DCMAKE_BUILD_TYPE=Release .. * $ make -j4 * $ make install * \endcode diff --git a/sdk/openbr_plugin.cpp b/sdk/openbr_plugin.cpp index 93422ce..9a539fc 100644 --- a/sdk/openbr_plugin.cpp +++ b/sdk/openbr_plugin.cpp @@ -24,9 +24,9 @@ #ifdef BR_DISTRIBUTED #include #endif // BR_DISTRIBUTED -#include - #include +#include +#include #include "version.h" #include "core/bee.h" @@ -450,10 +450,10 @@ QDataStream &br::operator>>(QDataStream &stream, Template &t) TemplateList TemplateList::fromGallery(const br::File &gallery) { TemplateList templates; - foreach (const br::File &file, gallery.split()) { QScopedPointer i(Gallery::make(file)); TemplateList newTemplates = i->read(); + newTemplates = newTemplates.mid(gallery.getInt("pos", 0), gallery.getInt("length", -1)); if (gallery.getBool("reduce")) newTemplates = newTemplates.reduced(); const int crossValidate = gallery.getInt("crossValidate"); if (crossValidate > 0) srand(0); @@ -940,7 +940,7 @@ void br::Context::messageHandler(QtMsgType type, const QMessageLogContext &conte break; } - fprintf(stderr, "%s", qPrintable(txt)); + std::cerr << txt.toStdString(); Globals->mostRecentMessage = txt; if (Globals->logFile.isWritable()) { diff --git a/sdk/plugins/draw.cpp b/sdk/plugins/draw.cpp index cdc4109..51e85ce 100644 --- a/sdk/plugins/draw.cpp +++ b/sdk/plugins/draw.cpp @@ -33,7 +33,13 @@ class DrawTransform : public UntrainableTransform { Q_OBJECT Q_PROPERTY(bool verbose READ get_verbose WRITE set_verbose RESET reset_verbose STORED false) + Q_PROPERTY(bool named READ get_named WRITE set_named RESET reset_named STORED false) + Q_PROPERTY(bool unnamed READ get_unnamed WRITE set_unnamed RESET reset_unnamed STORED false) + Q_PROPERTY(bool ROI READ get_ROI WRITE set_ROI RESET reset_ROI STORED false) BR_PROPERTY(bool, verbose, false) + BR_PROPERTY(bool, named, true) + BR_PROPERTY(bool, unnamed, true) + BR_PROPERTY(bool, ROI, true) void project(const Template &src, Template &dst) const { @@ -43,14 +49,20 @@ class DrawTransform : public UntrainableTransform QList landmarks = OpenCVUtils::toPoints(src.file.landmarks()); - foreach (const Point2f &landmark, landmarks) - circle(dst, landmark, 3, color, -1); - QList namedLandmarks = OpenCVUtils::toPoints(src.file.namedLandmarks()); - foreach (const Point2f &landmark, namedLandmarks) - circle(dst, landmark, 3, color); - QList ROIs = OpenCVUtils::toRects(src.file.ROIs()); - foreach (const Rect ROI, ROIs) - rectangle(dst, ROI, color); + if (unnamed) { + foreach (const Point2f &landmark, landmarks) + circle(dst, landmark, 3, color, -1); + } + if (named) { + QList namedLandmarks = OpenCVUtils::toPoints(src.file.namedLandmarks()); + foreach (const Point2f &landmark, namedLandmarks) + circle(dst, landmark, 3, color); + } + if (ROI) { + QList ROIs = OpenCVUtils::toRects(src.file.ROIs()); + foreach (const Rect ROI, ROIs) + rectangle(dst, ROI, color); + } if (verbose) for (int i=0; i +#include "NaturalStringCompare.h" #include "core/bee.h" #include "core/opencvutils.h" #include "core/qtutils.h" @@ -102,7 +103,7 @@ class EmptyGallery : public Gallery // Add immediate subfolders QDir dir(file); - foreach (const QString &folder, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) + foreach (const QString &folder, NaturalStringSort(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))) foreach (const QString &file, QtUtils::getFiles(dir.absoluteFilePath(folder), true)) templates.append(File(file, folder)); diff --git a/sdk/plugins/misc.cpp b/sdk/plugins/misc.cpp index b7aa5ff..97b30aa 100644 --- a/sdk/plugins/misc.cpp +++ b/sdk/plugins/misc.cpp @@ -40,7 +40,7 @@ class OpenTransform : public UntrainableMetaTransform foreach (const File &file, src.file.split()) { QScopedPointer format(Factory::make(file)); Template t = format->read(); - if (t.isEmpty()) qWarning("Can't open %s", qPrintable(file.flat())); + if (t.isEmpty()) qWarning("Can't open %s from %s", qPrintable(file.flat()), qPrintable(QDir::currentPath())); dst.append(t); dst.file.append(t.file.localMetadata()); } diff --git a/sdk/plugins/neclatent1.cpp b/sdk/plugins/neclatent1.cpp index 3fb877b..7b15f10 100644 --- a/sdk/plugins/neclatent1.cpp +++ b/sdk/plugins/neclatent1.cpp @@ -19,7 +19,8 @@ class NECLatent1Initialier : public Initializer void initialize() const { - Globals->abbreviations.insert("NECLatent1", "Open+Cvt(Gray)+NECLatent1Enroll:NECLatent1Compare"); + Globals->abbreviations.insert("NECTenprint1", "Open+Cvt(Gray)+NECLatent1Enroll:NECLatent1Compare"); + Globals->abbreviations.insert("NECLatent1", "Open+Cvt(Gray)+NECLatent1Enroll(true):NECLatent1Compare"); } }; @@ -29,17 +30,38 @@ BR_REGISTER(Initializer, NECLatent1Initialier) * \ingroup transforms * \brief Enroll an NEC latent fingerprint. * \author Josh Klontz \cite jklontz + * \warning Applications using this transform must have their working directory be the 'bin/win/32' folder of the NEC Latent SDK. */ class NECLatent1EnrollTransform : public UntrainableTransform { Q_OBJECT + Q_ENUMS(Algorithm) + Q_PROPERTY(bool latent READ get_latent WRITE set_latent RESET reset_latent STORED false) + Q_PROPERTY(Algorithm algorithm READ get_algorithm WRITE set_algorithm RESET reset_algorithm STORED false) + +public: + enum Algorithm { LFML, + ELFT }; + +private: + BR_PROPERTY(bool, latent, false) + BR_PROPERTY(Algorithm, algorithm, LFML) void project(const Template &src, Template &dst) const { if (src.m().type() != CV_8UC1) qFatal("Requires 8UC1 data!"); - unsigned char *data = new unsigned char[MAX_TEMPLATE_SIZE]; + unsigned char data[MAX_TEMPLATE_SIZE]; int size = 0; - int error = NEC_LFML_ExtractLatent(src.m().data, src.m().rows, src.m().cols, 500, data, &size); + int error; + + if (latent) { + if (algorithm == LFML) error = NEC_LFML_ExtractLatent(src.m().data, src.m().rows, src.m().cols, 500, data, &size); + else error = NEC_ELFT_ExtractLatent(src.m().data, src.m().rows, src.m().cols, 500, 4, data, &size); + } else { + if (algorithm == LFML) error = NEC_LFML_ExtractTenprint(src.m().data, src.m().rows, src.m().cols, 500, data, &size); + else error = NEC_ELFT_ExtractTenprint(src.m().data, src.m().rows, src.m().cols, 500, 2, data, &size); + } + if (!error) { cv::Mat n(1, size, CV_8UC1); memcpy(n.data, data, size); @@ -49,8 +71,6 @@ class NECLatent1EnrollTransform : public UntrainableTransform dst.m() = cv::Mat(); dst.file.set("FTE", true); } - - delete[] data; } }; @@ -64,12 +84,22 @@ BR_REGISTER(Transform, NECLatent1EnrollTransform) class NECLatent1CompareDistance : public Distance { Q_OBJECT + Q_ENUMS(Algorithm) + Q_PROPERTY(Algorithm algorithm READ get_algorithm WRITE set_algorithm RESET reset_algorithm STORED false) + +public: + enum Algorithm { LFML, + ELFT }; + +private: + BR_PROPERTY(Algorithm, algorithm, LFML) float compare(const Template &a, const Template &b) const { if (!a.m().data || !b.m().data) return -std::numeric_limits::max(); int score; - NEC_LFML_Verify(b.m().data, b.m().total(), a.m().data, a.m().total(), &score); + if (algorithm == LFML) NEC_LFML_Verify(b.m().data, b.m().total(), a.m().data, a.m().total(), &score); + else NEC_ELFT_Verify(b.m().data, a.m().data, &score, 2); return score; } }; diff --git a/sdk/plugins/reduce.cpp b/sdk/plugins/reduce.cpp index e052763..d63948d 100644 --- a/sdk/plugins/reduce.cpp +++ b/sdk/plugins/reduce.cpp @@ -107,11 +107,11 @@ private: if ((statistic == Min) || (statistic == Max)) { double min, max; minMaxLoc(src, &min, &max); - m.at(1, 1) = (statistic == Min ? min : max); + m.at(0,0) = (statistic == Min ? min : max); } else { Scalar mean, stddev; meanStdDev(src, mean, stddev); - m.at(1,1) = (statistic == Mean ? mean[0] : stddev[0]); + m.at(0,0) = (statistic == Mean ? mean[0] : stddev[0]); } dst = m; } @@ -122,6 +122,7 @@ BR_REGISTER(Transform, StatTransform) /*! * \ingroup transforms * \brief Downsample the rows and columns of a matrix. + * \author Lacey Best-Rowden \cite lbestrowden */ class DownsampleTransform : public UntrainableTransform { @@ -131,9 +132,15 @@ class DownsampleTransform : public UntrainableTransform void project(const Template &src, Template &dst) const { + if (src.m().channels() != 1) + qFatal("Expected 1 channel matrix."); Mat input = src.m(); - Mat output; - (void) input; // TODO: write me! + Mat output(ceil((double)input.rows/k), ceil((double)input.cols/k), CV_32FC1); + for (int r=0; r(r,c) = input.at(r*k,c*k); + } + } dst.m() = output; } }; diff --git a/share/openbr/cmake/FindNaturalStringCompare.cmake b/share/openbr/cmake/FindNaturalStringCompare.cmake new file mode 100644 index 0000000..fda7b76 --- /dev/null +++ b/share/openbr/cmake/FindNaturalStringCompare.cmake @@ -0,0 +1,4 @@ +find_path(NATURALSTRINGCOMPARE_DIR NaturalStringCompare.h ${CMAKE_SOURCE_DIR}/3rdparty/*) + +include_directories(${NATURALSTRINGCOMPARE_DIR}) +set(NATURALSTRINGCOMPARE_SRC ${NATURALSTRINGCOMPARE_DIR}/NaturalStringCompare.cpp) diff --git a/share/openbr/cmake/InstallDependencies.cmake b/share/openbr/cmake/InstallDependencies.cmake index ec03aa3..77937bd 100644 --- a/share/openbr/cmake/InstallDependencies.cmake +++ b/share/openbr/cmake/InstallDependencies.cmake @@ -10,17 +10,12 @@ function(install_opencv_library lib) if(NOT MSVC) set(BR_INSTALL_DEPENDENCIES_PREFIX "lib") endif() - install(FILES ${OpenCV_DIR}/bin/${BR_INSTALL_DEPENDENCIES_PREFIX}opencv_${lib}${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}${BR_INSTALL_DEPENDENCIES_SUFFIX}.dll DESTINATION bin) - elseif(CMAKE_HOST_APPLE) - set(OpenCV_LIB_DIR "/usr/local/lib") - install(FILES ${OpenCV_LIB_DIR}/libopencv_${lib}.${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.${OpenCV_VERSION_PATCH}.dylib DESTINATION lib) - install(FILES ${OpenCV_LIB_DIR}/libopencv_${lib}.${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.dylib DESTINATION lib) - install(FILES ${OpenCV_LIB_DIR}/libopencv_${lib}.dylib DESTINATION lib) + install(FILES ${OpenCV_DIR}/bin/${BR_INSTALL_DEPENDENCIES_PREFIX}${lib}${OpenCV_VERSION_MAJOR}${OpenCV_VERSION_MINOR}${OpenCV_VERSION_PATCH}${BR_INSTALL_DEPENDENCIES_SUFFIX}.dll DESTINATION bin) else() set(OpenCV_LIB_DIR "/usr/local/lib") - install(FILES ${OpenCV_LIB_DIR}/libopencv_${lib}.so.${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.${OpenCV_VERSION_PATCH} DESTINATION lib) - install(FILES ${OpenCV_LIB_DIR}/libopencv_${lib}.so.${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR} DESTINATION lib) - install(FILES ${OpenCV_LIB_DIR}/libopencv_${lib}.so DESTINATION lib) + install(FILES ${OpenCV_LIB_DIR}/lib${lib}.${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}.${OpenCV_VERSION_PATCH}${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION lib) + install(FILES ${OpenCV_LIB_DIR}/lib${lib}.${OpenCV_VERSION_MAJOR}.${OpenCV_VERSION_MINOR}${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION lib) + install(FILES ${OpenCV_LIB_DIR}/lib${lib}${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION lib) endif() endif() endfunction() diff --git a/share/openbr/openbr.bib b/share/openbr/openbr.bib index 71f922d..556446d 100644 --- a/share/openbr/openbr.bib +++ b/share/openbr/openbr.bib @@ -28,6 +28,11 @@ Author = {Charles A. Otto}, Howpublished = {https://github.com/caotto}, Title = {ottochar at gmail.com}} + +@misc{lbestrowden, + Author = {Lacey S. Best-Rowden}, + Howpublished = {https://github.com/lbestrowden}, + Title = {bestrow1 at msu.edu}} % Software