diff --git a/openbr/plugins/classification/adaboost.cpp b/openbr/plugins/classification/adaboost.cpp new file mode 100644 index 0000000..35173e9 --- /dev/null +++ b/openbr/plugins/classification/adaboost.cpp @@ -0,0 +1,118 @@ +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV's Ada Boost framework + * \author Scott Klum \cite sklum + * \brief http://docs.opencv.org/modules/ml/doc/boosting.html + */ +class AdaBoostTransform : public Transform +{ + Q_OBJECT + Q_ENUMS(Type) + Q_ENUMS(SplitCriteria) + + Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false) + Q_PROPERTY(SplitCriteria splitCriteria READ get_splitCriteria WRITE set_splitCriteria RESET reset_splitCriteria STORED false) + Q_PROPERTY(int weakCount READ get_weakCount WRITE set_weakCount RESET reset_weakCount STORED false) + Q_PROPERTY(float trimRate READ get_trimRate WRITE set_trimRate RESET reset_trimRate STORED false) + Q_PROPERTY(int folds READ get_folds WRITE set_folds RESET reset_folds STORED false) + Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false) + Q_PROPERTY(bool returnConfidence READ get_returnConfidence WRITE set_returnConfidence RESET reset_returnConfidence STORED false) + Q_PROPERTY(bool overwriteMat READ get_overwriteMat WRITE set_overwriteMat RESET reset_overwriteMat STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false) + +public: + enum Type { Discrete = CvBoost::DISCRETE, + Real = CvBoost::REAL, + Logit = CvBoost::LOGIT, + Gentle = CvBoost::GENTLE}; + + enum SplitCriteria { Default = CvBoost::DEFAULT, + Gini = CvBoost::GINI, + Misclass = CvBoost::MISCLASS, + Sqerr = CvBoost::SQERR}; + +private: + BR_PROPERTY(Type, type, Real) + BR_PROPERTY(SplitCriteria, splitCriteria, Default) + BR_PROPERTY(int, weakCount, 100) + BR_PROPERTY(float, trimRate, .95) + BR_PROPERTY(int, folds, 0) + BR_PROPERTY(int, maxDepth, 1) + BR_PROPERTY(bool, returnConfidence, true) + BR_PROPERTY(bool, overwriteMat, true) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(QString, outputVariable, "") + + CvBoost boost; + + void train(const TemplateList &data) + { + Mat samples = OpenCVUtils::toMat(data.data()); + Mat labels = OpenCVUtils::toMat(File::get(data, inputVariable)); + + Mat types = Mat(samples.cols + 1, 1, CV_8U); + types.setTo(Scalar(CV_VAR_NUMERICAL)); + types.at(samples.cols, 0) = CV_VAR_CATEGORICAL; + + CvBoostParams params; + params.boost_type = type; + params.split_criteria = splitCriteria; + params.weak_count = weakCount; + params.weight_trim_rate = trimRate; + params.cv_folds = folds; + params.max_depth = maxDepth; + + boost.train( samples, CV_ROW_SAMPLE, labels, Mat(), Mat(), types, Mat(), + params); + } + + void project(const Template &src, Template &dst) const + { + dst = src; + float response; + if (returnConfidence) { + response = boost.predict(src.m().reshape(1,1),Mat(),Range::all(),false,true)/weakCount; + } else { + response = boost.predict(src.m().reshape(1,1)); + } + + if (overwriteMat) { + dst.m() = Mat(1, 1, CV_32F); + dst.m().at(0, 0) = response; + } else { + dst.file.set(outputVariable, response); + } + } + + void load(QDataStream &stream) + { + OpenCVUtils::loadModel(boost,stream); + } + + void store(QDataStream &stream) const + { + OpenCVUtils::storeModel(boost,stream); + } + + + void init() + { + if (outputVariable.isEmpty()) + outputVariable = inputVariable; + } +}; + +BR_REGISTER(Transform, AdaBoostTransform) + +} // namespace br + +#include "classification/adaboost.moc" diff --git a/openbr/plugins/classification/ebif.cpp b/openbr/plugins/classification/ebif.cpp index ca9f184..765c9f1 100644 --- a/openbr/plugins/classification/ebif.cpp +++ b/openbr/plugins/classification/ebif.cpp @@ -128,4 +128,4 @@ BR_REGISTER(Transform, EBIFTransform) } // namespace br -#include "ebif.moc" +#include "classification/ebif.moc" diff --git a/openbr/plugins/tree.cpp b/openbr/plugins/classification/forest.cpp index 8ecc39f..cd50743 100644 --- a/openbr/plugins/tree.cpp +++ b/openbr/plugins/classification/forest.cpp @@ -1,7 +1,6 @@ -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" +#include +#include -using namespace std; using namespace cv; namespace br @@ -250,113 +249,6 @@ class ForestInductionTransform : public ForestTransform BR_REGISTER(Transform, ForestInductionTransform) -/*! - * \ingroup transforms - * \brief Wraps OpenCV's Ada Boost framework - * \author Scott Klum \cite sklum - * \brief http://docs.opencv.org/modules/ml/doc/boosting.html - */ -class AdaBoostTransform : public Transform -{ - Q_OBJECT - Q_ENUMS(Type) - Q_ENUMS(SplitCriteria) - - Q_PROPERTY(Type type READ get_type WRITE set_type RESET reset_type STORED false) - Q_PROPERTY(SplitCriteria splitCriteria READ get_splitCriteria WRITE set_splitCriteria RESET reset_splitCriteria STORED false) - Q_PROPERTY(int weakCount READ get_weakCount WRITE set_weakCount RESET reset_weakCount STORED false) - Q_PROPERTY(float trimRate READ get_trimRate WRITE set_trimRate RESET reset_trimRate STORED false) - Q_PROPERTY(int folds READ get_folds WRITE set_folds RESET reset_folds STORED false) - Q_PROPERTY(int maxDepth READ get_maxDepth WRITE set_maxDepth RESET reset_maxDepth STORED false) - Q_PROPERTY(bool returnConfidence READ get_returnConfidence WRITE set_returnConfidence RESET reset_returnConfidence STORED false) - Q_PROPERTY(bool overwriteMat READ get_overwriteMat WRITE set_overwriteMat RESET reset_overwriteMat STORED false) - Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) - Q_PROPERTY(QString outputVariable READ get_outputVariable WRITE set_outputVariable RESET reset_outputVariable STORED false) - -public: - enum Type { Discrete = CvBoost::DISCRETE, - Real = CvBoost::REAL, - Logit = CvBoost::LOGIT, - Gentle = CvBoost::GENTLE}; - - enum SplitCriteria { Default = CvBoost::DEFAULT, - Gini = CvBoost::GINI, - Misclass = CvBoost::MISCLASS, - Sqerr = CvBoost::SQERR}; - -private: - BR_PROPERTY(Type, type, Real) - BR_PROPERTY(SplitCriteria, splitCriteria, Default) - BR_PROPERTY(int, weakCount, 100) - BR_PROPERTY(float, trimRate, .95) - BR_PROPERTY(int, folds, 0) - BR_PROPERTY(int, maxDepth, 1) - BR_PROPERTY(bool, returnConfidence, true) - BR_PROPERTY(bool, overwriteMat, true) - BR_PROPERTY(QString, inputVariable, "Label") - BR_PROPERTY(QString, outputVariable, "") - - CvBoost boost; - - void train(const TemplateList &data) - { - Mat samples = OpenCVUtils::toMat(data.data()); - Mat labels = OpenCVUtils::toMat(File::get(data, inputVariable)); - - Mat types = Mat(samples.cols + 1, 1, CV_8U); - types.setTo(Scalar(CV_VAR_NUMERICAL)); - types.at(samples.cols, 0) = CV_VAR_CATEGORICAL; - - CvBoostParams params; - params.boost_type = type; - params.split_criteria = splitCriteria; - params.weak_count = weakCount; - params.weight_trim_rate = trimRate; - params.cv_folds = folds; - params.max_depth = maxDepth; - - boost.train( samples, CV_ROW_SAMPLE, labels, Mat(), Mat(), types, Mat(), - params); - } - - void project(const Template &src, Template &dst) const - { - dst = src; - float response; - if (returnConfidence) { - response = boost.predict(src.m().reshape(1,1),Mat(),Range::all(),false,true)/weakCount; - } else { - response = boost.predict(src.m().reshape(1,1)); - } - - if (overwriteMat) { - dst.m() = Mat(1, 1, CV_32F); - dst.m().at(0, 0) = response; - } else { - dst.file.set(outputVariable, response); - } - } - - void load(QDataStream &stream) - { - OpenCVUtils::loadModel(boost,stream); - } - - void store(QDataStream &stream) const - { - OpenCVUtils::storeModel(boost,stream); - } - - - void init() - { - if (outputVariable.isEmpty()) - outputVariable = inputVariable; - } -}; - -BR_REGISTER(Transform, AdaBoostTransform) - } // namespace br -#include "tree.moc" +#include "classification/forest.moc" diff --git a/openbr/plugins/ipc2013.cpp b/openbr/plugins/classification/ipc2013.cpp index 4f5ce59..d16958c 100644 --- a/openbr/plugins/ipc2013.cpp +++ b/openbr/plugins/classification/ipc2013.cpp @@ -1,9 +1,10 @@ -#include "openbr_internal.h" #include #include #include #include +#include + using namespace br; static PXCSession *pxcSession = NULL; diff --git a/openbr/plugins/eigen3.cpp b/openbr/plugins/classification/lda.cpp index 97bd90f..390f266 100644 --- a/openbr/plugins/eigen3.cpp +++ b/openbr/plugins/classification/lda.cpp @@ -16,11 +16,11 @@ #include -#include "openbr_internal.h" +#include -#include "openbr/core/common.h" -#include "openbr/core/eigenutils.h" -#include "openbr/core/opencvutils.h" +#include +#include +#include namespace br { @@ -654,4 +654,4 @@ BR_REGISTER(Transform, SparseLDATransform) } // namespace br -#include "eigen3.moc" +#include "classification/lda.moc" diff --git a/openbr/plugins/liblinear.cpp b/openbr/plugins/classification/liblinear.cpp index 1b9649e..31b2137 100644 --- a/openbr/plugins/liblinear.cpp +++ b/openbr/plugins/classification/liblinear.cpp @@ -2,8 +2,8 @@ #include #include -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" +#include +#include #include diff --git a/openbr/plugins/nn.cpp b/openbr/plugins/classification/mlp.cpp index 83fd93f..5cf4ce4 100644 --- a/openbr/plugins/nn.cpp +++ b/openbr/plugins/classification/mlp.cpp @@ -1,51 +1,13 @@ #include -#include "openbr_internal.h" -#include "openbr/core/qtutils.h" -#include "openbr/core/opencvutils.h" -#include "openbr/core/eigenutils.h" -#include -#include - -using namespace std; +#include +#include + using namespace cv; namespace br { -static void storeMLP(const CvANN_MLP &mlp, QDataStream &stream) -{ - // Create local file - QTemporaryFile tempFile; - tempFile.open(); - tempFile.close(); - - // Save MLP to local file - mlp.save(qPrintable(tempFile.fileName())); - - // Copy local file contents to stream - tempFile.open(); - QByteArray data = tempFile.readAll(); - tempFile.close(); - stream << data; -} - -static void loadMLP(CvANN_MLP &mlp, QDataStream &stream) -{ - // Copy local file contents from stream - QByteArray data; - stream >> data; - - // Create local file - QTemporaryFile tempFile(QDir::tempPath()+"/MLP"); - tempFile.open(); - tempFile.write(data); - tempFile.close(); - - // Load MLP from local file - mlp.load(qPrintable(tempFile.fileName())); -} - /*! * \ingroup transforms * \brief Wraps OpenCV's multi-layer perceptron framework @@ -124,12 +86,12 @@ private: void load(QDataStream &stream) { - loadMLP(mlp,stream); + OpenCVUtils::loadModel(mlp, stream); } void store(QDataStream &stream) const { - storeMLP(mlp,stream); + OpenCVUtils::storeModel(mlp, stream); } }; @@ -137,4 +99,4 @@ BR_REGISTER(Transform, MLPTransform) } // namespace br -#include "nn.moc" +#include "classification/mlp.moc" diff --git a/openbr/plugins/nt4.cpp b/openbr/plugins/classification/nt4.cpp index a3ded95..c0b1cb7 100644 --- a/openbr/plugins/nt4.cpp +++ b/openbr/plugins/classification/nt4.cpp @@ -457,4 +457,4 @@ class NT4Compare : public Distance BR_REGISTER(Distance, NT4Compare) -#include "nt4.moc" +#include "classification/nt4.moc" diff --git a/openbr/plugins/pp4.cpp b/openbr/plugins/classification/pp4.cpp index 69bac4b..69bac4b 100644 --- a/openbr/plugins/pp4.cpp +++ b/openbr/plugins/classification/pp4.cpp diff --git a/openbr/plugins/pp5.cpp b/openbr/plugins/classification/pp5.cpp index 6c795a5..5627e8d 100644 --- a/openbr/plugins/pp5.cpp +++ b/openbr/plugins/classification/pp5.cpp @@ -593,4 +593,4 @@ class PP5GalleryTransform: public UntrainableMetaTransform BR_REGISTER(Transform, PP5GalleryTransform) -#include "plugins/pp5.moc" +#include "classification/pp5.moc" diff --git a/openbr/plugins/svm.cpp b/openbr/plugins/classification/svm.cpp index 8acd3a7..e6f42d1 100644 --- a/openbr/plugins/svm.cpp +++ b/openbr/plugins/classification/svm.cpp @@ -18,8 +18,8 @@ #include #include -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" +#include +#include using namespace cv; @@ -261,4 +261,4 @@ BR_REGISTER(Distance, SVMDistance) } // namespace br -#include "svm.moc" +#include "classification/svm.moc" diff --git a/openbr/plugins/classification/turk.cpp b/openbr/plugins/classification/turk.cpp new file mode 100644 index 0000000..ecaace7 --- /dev/null +++ b/openbr/plugins/classification/turk.cpp @@ -0,0 +1,56 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Convenience class for training turk attribute regressors + * \author Josh Klontz \cite jklontz + */ +class TurkClassifierTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key STORED false) + Q_PROPERTY(QStringList values READ get_values WRITE set_values RESET reset_values STORED false) + Q_PROPERTY(bool isMeta READ get_isMeta WRITE set_isMeta RESET reset_isMeta STORED false) + BR_PROPERTY(QString, key, QString()) + BR_PROPERTY(QStringList, values, QStringList()) + BR_PROPERTY(bool, isMeta, false) + + Transform *child; + + void init() + { + QStringList classifiers; + foreach (const QString &value, values) + classifiers.append(QString("(SVM(RBF,EPS_SVR,returnDFVal=true,inputVariable=%1,outputVariable=predicted_%1)%2)").arg(key + "_" + value, isMeta ? QString("+Average+SaveMat(predicted_%1)").arg(value) : QString())); + child = Transform::make(classifiers.join("/") + (classifiers.size() > 1 ? "+Cat" : "")); + } + + void train(const QList &data) + { + child->train(data); + } + + void project(const Template &src, Template &dst) const + { + child->project(src, dst); + } + + void store(QDataStream &stream) const + { + child->store(stream); + } + + void load(QDataStream &stream) + { + child->load(stream); + } +}; + +BR_REGISTER(Transform, TurkClassifierTransform) + +} // namespace br + +#include "classification/turk.moc" diff --git a/openbr/plugins/cluster/collectnn.cpp b/openbr/plugins/cluster/collectnn.cpp index 74f2eff..00d1764 100644 --- a/openbr/plugins/cluster/collectnn.cpp +++ b/openbr/plugins/cluster/collectnn.cpp @@ -39,4 +39,4 @@ BR_REGISTER(Transform, CollectNNTransform) } // namespace br -#include "collectnn.moc" +#include "cluster/collectnn.moc" diff --git a/openbr/plugins/cluster/kmeans.cpp b/openbr/plugins/cluster/kmeans.cpp index 4f30c31..475a30d 100644 --- a/openbr/plugins/cluster/kmeans.cpp +++ b/openbr/plugins/cluster/kmeans.cpp @@ -62,4 +62,4 @@ BR_REGISTER(Transform, KMeansTransform) } // namespace br -#include "kmeans.moc" +#include "cluster/kmeans.moc" diff --git a/openbr/plugins/cluster/knn.cpp b/openbr/plugins/cluster/knn.cpp index 60fed0f..c1030e5 100644 --- a/openbr/plugins/cluster/knn.cpp +++ b/openbr/plugins/cluster/knn.cpp @@ -81,4 +81,4 @@ BR_REGISTER(Transform, KNNTransform) } // namespace br -#include "knn.moc" +#include "cluster/knn.moc" diff --git a/openbr/plugins/cluster/lognn.cpp b/openbr/plugins/cluster/lognn.cpp index 0cdab6e..db2ba40 100644 --- a/openbr/plugins/cluster/lognn.cpp +++ b/openbr/plugins/cluster/lognn.cpp @@ -62,4 +62,4 @@ BR_REGISTER(Transform, LogNNTransform) } // namespace br -#include "lognn.moc" +#include "cluster/lognn.moc" diff --git a/openbr/plugins/cluster/randomcentroids.cpp b/openbr/plugins/cluster/randomcentroids.cpp index d9b8c9d..c7a7016 100644 --- a/openbr/plugins/cluster/randomcentroids.cpp +++ b/openbr/plugins/cluster/randomcentroids.cpp @@ -65,4 +65,4 @@ BR_REGISTER(Transform, RandomCentroidsTransform) } //namespace br -#include "randomcentroids.moc" +#include "cluster/randomcentroids.moc" diff --git a/openbr/plugins/eigen3.cmake b/openbr/plugins/cmake/eigen3.cmake index ed6a3e2..95c018c 100644 --- a/openbr/plugins/eigen3.cmake +++ b/openbr/plugins/cmake/eigen3.cmake @@ -2,6 +2,8 @@ set(BR_WITH_EIGEN3 ON CACHE BOOL "Build Eigen3 plugins") if(${BR_WITH_EIGEN3}) find_package(Eigen3 REQUIRED) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/eigen3.cpp) install(FILES ${EIGEN3_LICENSE} RENAME Eigen3 DESTINATION share/openbr/licenses) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/lda.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/imgproc/revertaffine.cpp) endif() diff --git a/openbr/plugins/ipc2013.cmake b/openbr/plugins/cmake/ipc2013.cmake index d764890..617ac4d 100644 --- a/openbr/plugins/ipc2013.cmake +++ b/openbr/plugins/cmake/ipc2013.cmake @@ -2,7 +2,8 @@ set(BR_WITH_IPC2013 OFF CACHE BOOL "Build with Intel Perceptual Computing SDK 20 if(${BR_WITH_IPC2013}) find_package(IPC2013 REQUIRED) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/ipc2013.cpp) set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${IPC2013_LIBS}) install(DIRECTORY ${IPC2013_DIR}/bin/x64/ DESTINATION bin) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/ipc2013.cpp) endif() diff --git a/openbr/plugins/jni.cmake b/openbr/plugins/cmake/jni.cmake index 0fc3f77..41cc5ba 100644 --- a/openbr/plugins/jni.cmake +++ b/openbr/plugins/cmake/jni.cmake @@ -3,10 +3,10 @@ set(BR_WITH_JAVA OFF CACHE BOOL "Use Java Code") if (${BR_WITH_JAVA}) find_package(JNI REQUIRED) find_package(JAVA REQUIRED) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/jni.cpp) set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${JNI_LIBRARIES}) include_directories(${JAVA_INCLUDE_PATH}) include_directories(${JAVA_INCLUDE_PATH2}) - +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/core/jni.cpp) endif() diff --git a/openbr/plugins/liblinear.cmake b/openbr/plugins/cmake/liblinear.cmake index 3845f08..431a64c 100644 --- a/openbr/plugins/liblinear.cmake +++ b/openbr/plugins/cmake/liblinear.cmake @@ -3,5 +3,6 @@ set(BR_WITH_LIBLINEAR OFF CACHE BOOL "Build with LibLinear") if(${BR_WITH_LIBLINEAR}) find_package(LibLinear REQUIRED) set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} ${LibLinear_SRC}) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/liblinear.cpp) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/liblinear.cpp) endif() diff --git a/openbr/plugins/likely.cmake b/openbr/plugins/cmake/likely.cmake index 92ab602..ba894d6 100644 --- a/openbr/plugins/likely.cmake +++ b/openbr/plugins/cmake/likely.cmake @@ -2,6 +2,9 @@ set(BR_WITH_LIKELY OFF CACHE BOOL "Build with Likely") if(${BR_WITH_LIKELY}) find_package(Likely REQUIRED) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/likely.cpp) set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${Likely_LIBS}) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/core/likely.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/format/lmat.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/gallery/lmat.cpp) endif() diff --git a/openbr/plugins/mongoose.cmake b/openbr/plugins/cmake/mongoose.cmake index a32ea7d..729365a 100644 --- a/openbr/plugins/mongoose.cmake +++ b/openbr/plugins/cmake/mongoose.cmake @@ -1,6 +1,8 @@ set(BR_WITH_MONGOOSE OFF CACHE BOOL "Build with Mongoose") if(${BR_WITH_MONGOOSE}) find_package(Mongoose) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/mongoose.cpp ${MONGOOSE_SRC}) + set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} ${MONGOOSE_SRC}) install(FILES ${MONGOOSE_LICENSE} RENAME mongoose DESTINATION share/openbr/licenses) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/metadata/mongoose.cpp) endif() diff --git a/openbr/plugins/qtnetwork.cmake b/openbr/plugins/cmake/network.cmake index 49e02e9..46899ee 100644 --- a/openbr/plugins/qtnetwork.cmake +++ b/openbr/plugins/cmake/network.cmake @@ -3,6 +3,10 @@ if(${BR_WITH_QTNETWORK}) find_package(Qt5Network) find_package(HttpParser) set(QT_DEPENDENCIES ${QT_DEPENDENCIES} Network) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/qtnetwork.cpp ${HTTPPARSER_SRC}) + set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} ${HTTPPARSER_SRC}) install(FILES ${HTTPPARSER_LICENSE} RENAME http-parser DESTINATION share/openbr/licenses) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/format/urlformat.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/format/postformat.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/gallery/postgallery.cpp) endif() diff --git a/openbr/plugins/nt4.cmake b/openbr/plugins/cmake/nt4.cmake index 41fa42e..bdba899 100644 --- a/openbr/plugins/nt4.cmake +++ b/openbr/plugins/cmake/nt4.cmake @@ -2,7 +2,8 @@ set(BR_WITH_NT4 OFF CACHE BOOL "Build with Neurotec Biometric 4") if(${BR_WITH_NT4}) find_package(NT4 REQUIRED) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/nt4.cpp) set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${NT4_LIBS}) install(DIRECTORY ${NT4_DIR_LIB}/ DESTINATION lib) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/nt4.cpp) endif() diff --git a/openbr/plugins/pp4.cmake b/openbr/plugins/cmake/pp4.cmake index fd79240..95743a7 100644 --- a/openbr/plugins/pp4.cmake +++ b/openbr/plugins/cmake/pp4.cmake @@ -2,8 +2,9 @@ set(BR_WITH_PP4 OFF CACHE BOOL "Build with PittPatt 4") if(${BR_WITH_PP4}) find_package(PP4 REQUIRED) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/pp4.cpp) set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${PP4_LIBS}) install(DIRECTORY ${PP4_DIR}/lib/ DESTINATION lib) install(DIRECTORY ${PP4_DIR}/models/ DESTINATION models/pp4) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/pp4.cpp) endif() diff --git a/openbr/plugins/pp5.cmake b/openbr/plugins/cmake/pp5.cmake index 7fcebd9..2df45b6 100644 --- a/openbr/plugins/pp5.cmake +++ b/openbr/plugins/cmake/pp5.cmake @@ -2,7 +2,6 @@ set(BR_WITH_PP5 OFF CACHE BOOL "Build with PittPatt 5") if(${BR_WITH_PP5}) find_package(PP5 REQUIRED) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/pp5.cpp) set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${PP5_LIBS}) if(WIN32) @@ -12,4 +11,6 @@ if(${BR_WITH_PP5}) endif() install(DIRECTORY ${PP5_DIR}/models/ DESTINATION share/openbr/models/pp5) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/classification/pp5.cpp) endif() diff --git a/openbr/plugins/cmake/show.cmake b/openbr/plugins/cmake/show.cmake new file mode 100644 index 0000000..54e924a --- /dev/null +++ b/openbr/plugins/cmake/show.cmake @@ -0,0 +1,3 @@ +if(${BR_EMBEDDED}) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/gui/show.cpp) +endif() diff --git a/openbr/plugins/stasm4.cmake b/openbr/plugins/cmake/stasm4.cmake index 8d7e543..fa52d2b 100644 --- a/openbr/plugins/stasm4.cmake +++ b/openbr/plugins/cmake/stasm4.cmake @@ -2,7 +2,6 @@ set(BR_WITH_STASM4 OFF CACHE BOOL "Build with Stasm") if(${BR_WITH_STASM4}) find_package(Stasm4 REQUIRED) - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/stasm4.cpp) set(BR_THIRDPARTY_LIBS ${BR_THIRDPARTY_LIBS} ${Stasm4_LIBS}) if(WIN32) @@ -12,4 +11,7 @@ if(${BR_WITH_STASM4}) endif() install(DIRECTORY ${Stasm_DIR}/data/ DESTINATION share/openbr/models/stasm) +else() + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/metadata/stasm4.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/imgproc/revertaffine.cpp) endif() diff --git a/openbr/plugins/core/algorithms.cpp b/openbr/plugins/core/algorithms.cpp index 2293e65..9d61dd1 100644 --- a/openbr/plugins/core/algorithms.cpp +++ b/openbr/plugins/core/algorithms.cpp @@ -108,4 +108,4 @@ BR_REGISTER(Initializer, AlgorithmsInitializer) } // namespace br -#include "algorithms.moc" +#include "core/algorithms.moc" diff --git a/openbr/plugins/core/attributealgorithms.cpp b/openbr/plugins/core/attributealgorithms.cpp new file mode 100644 index 0000000..7915e31 --- /dev/null +++ b/openbr/plugins/core/attributealgorithms.cpp @@ -0,0 +1,137 @@ +#include + +namespace br +{ + +/*! + * \ingroup initializers + * \brief Initializes global abbreviations with implemented algorithms for attributes + * \author Babatunde Ogunfemi \cite baba1472 + */ +class AttributeAlgorithmsInitializer : public Initializer +{ + Q_OBJECT + + void initialize() const + { + // Constants + QString BASE="Open+PP5Register+Rename(PP5_Landmark0_Right_Eye,Affine_0)+Rename(PP5_Landmark1_Left_Eye,Affine_1)+Affine(192,240,.345,.475)+Cvt(Gray)+Stasm(false,true,[(66.24,114),(125.76,114)])"; + QString SUBSPACE ="Blur(1.1)+Gamma(0.2)+DoG(1,2)+ContrastEq(0.1,10)+LBP(1,2)+RectRegions(8,8,4,4)+Hist(59)+Cat+PCA(0.95)"; + + QString NOSE="RectFromStasmNoseWithBridge+ROI+Resize(36,24)+" + SUBSPACE; + QString MOUTH="RectFromStasmMouth+ROI+Resize(24,36)+" + SUBSPACE; + QString EYES="RectFromStasmEyes+ROI+Resize(24,36)+" + SUBSPACE; + QString HAIR="RectFromStasmHair+ROI+Resize(24,36)+" + SUBSPACE; + QString BROW="RectFromStasmBrow+ROI+Resize(24,36)+" + SUBSPACE; + QString JAW="RectFromStasmJaw+ROI+Resize(36,36)+" + SUBSPACE; + QString FACE = "Crop(24,30,144,190)+Resize(36,36)+" + SUBSPACE; + + // All Attributes + Globals->abbreviations.insert("AllAttributes", "AttributeBrow/AttributeMouth/AttributeEyes/AttributeFace/AttributeHair/AttributeNose/AttributeJaw"); + Globals->abbreviations.insert("AllAttributesMatching", "(AttributeBrow)/(AttributeMouth)/(AttributeEyes)/(AttributeFace)/(AttributeHair)/(AttributeNose)/(AttributeJaw):AttributeMatch"); + + //Individual Attributes + Globals->abbreviations.insert("AttributeBrow", "(" + BASE+ "+" + BROW + "+" + "TurkClassifier(eyebrowposition,[closebrows,highbrows],3)/" + "TurkClassifier(unibrow,[unibrow],3)/" + "TurkClassifier(eyebroworientation,[eyebrowsdown,eyebrowsuptodown],3)/" + "TurkClassifier(thickeyebrows,[thickeyebrows,lighteyebrows],3))"); + Globals->abbreviations.insert("AttributeMouth", "(" + BASE + "+" + MOUTH + "+" + "TurkClassifier(smiling,[smiling],3)/" + "TurkClassifier(lipthickness,[cherry,big,small],3)/" + "TurkClassifier(mouthbite,[underbite,overbite],3)/" + "TurkClassifier(mouthopen,[closed,noteeth,halfteeth,allteeth],3)/" + "TurkClassifier(mouthwidth,[small,wide],3)/" + "TurkClassifier(mustache,[nomustache,linemustache,lightmustache,normalmustache,down],3)/" + "TurkClassifier(mouthasymmetry,[asymmetrical],3))"); + Globals->abbreviations.insert("AttributeEyes", "(" + BASE + "+" + EYES + "+ " + "TurkClassifier(eyeseparation,[close,wide],3)/" + "TurkClassifier(eyeslant,[slant2,slant1,wild],3)/" + "TurkClassifier(benteyes,[bent])/" + "TurkClassifier(eyecolor,[darkeyes,lighteyes],3)/" + "TurkClassifier(baggyeyes,[baggy],3)/" + "TurkClassifier(almondeyes,[almond],3)/" + "TurkClassifier(buriedeyes,[buriedeyes],3)/" + "TurkClassifier(sleepyeyes,[sleepy],3)/" + "TurkClassifier(lineeyes,[line],3)/" + "TurkClassifier(roundeyes,[round],3)/" + "TurkClassifier(sharpeyes,[sharp],3)/" + "TurkClassifier(smalleyes,[smalleyes],3)/" + "TurkClassifier(glasses,[glasses],3)/" + "TurkClassifier(eyelashvisibility,[feweyelashes],3))"); + Globals->abbreviations.insert("AttributeFace", "(" + BASE + "+" + FACE + "+" + "TurkClassifier(gender,[male],3)/" + "TurkClassifier(faceshape,[round,triangular,rectangular],3)/" + "TurkClassifier(cheekdensity,[puffy,in,normal],3)/" + "TurkClassifier(facemarks,[scars,moles,normal],3)/" + "TurkClassifier(facelength,[long],3)/" + "TurkClassifier(nosetoeyedist,[short,long],3)/" + "TurkClassifier(nosetomouthdist,[long,small],3))"); + Globals->abbreviations.insert("AttributeHair", "(" + BASE + "+" + HAIR + "+" + "TurkClassifier(foreheadwrinkles,[wrinkled],3)/" + "TurkClassifier(foreheadsize,[smallforehead,largeforehead],3)/" + "TurkClassifier(haircolor,[darkhair,lighthair,greyhair],3)/" + "TurkClassifier(hairdensity,[thick,bald,thin,halfbald],3)/" + "TurkClassifier(widowspeak,[widowspeak],3)/" + "TurkClassifier(hairstyle,[curlyhair],3))"); + Globals->abbreviations.insert("AttributeNose", "(" + BASE + "+" + NOSE + "+" + "TurkClassifier(noseorientation,[upnose,downnose],3)/" + "TurkClassifier(nosewidth,[small,thick],3)/" + "TurkClassifier(nosesize,[smallnose,bignose],3)/" + "TurkClassifier(brokennose,[broken],3))"); + Globals->abbreviations.insert("AttributeJaw", "(" + BASE + "+" + JAW + "+" + "TurkClassifier(beard,[nobeard,bigbeard,lightbeard,goatee,linebeard,normalbeard,lincolnbeard],3)/" + "TurkClassifier(chinsize,[shortchin,longchin],3))"); + Globals->abbreviations.insert("AttributeMatch", "Fuse([" + "Turk(eyebrowposition,[closebrows,highbrows],3)," + "Turk(unibrow,[unibrow],3)," + "Turk(eyebroworientation,[eyebrowsdown,eyebrowsuptodown],3)," + "Turk(thickeyebrows,[thickeyebrows,lighteyebrows],3)," + "Turk(smiling,[smiling],3)," + "Turk(lipthickness,[cherry,big,small],3)," + "Turk(mouthbite,[underbite,overbite],3)," + "Turk(mouthopen,[closed,noteeth,halfteeth,allteeth],3)," + "Turk(mouthwidth,[small,wide],3)," + "Turk(mustache,[nomustache,linemustache,lightmustache,normalmustache,down],3)," + "Turk(mouthasymmetry,[asymmetrical],3)," + "Turk(eyeseparation,[close,wide],3)," + "Turk(eyeslant,[slant2,slant1,wild],3)," + "Turk(benteyes,[bent],3)," + "Turk(eyecolor,[darkeyes,lighteyes],3)," + "Turk(baggyeyes,[baggy],3)," + "Turk(almondeyes,[almond],3)," + "Turk(buriedeyes,[buriedeyes],3)," + "Turk(sleepyeyes,[sleepy],3)," + "Turk(lineeyes,[line],3)," + "Turk(roundeyes,[round],3)," + "Turk(sharpeyes,[sharp],3)," + "Turk(smalleyes,[smalleyes],3)," + "Turk(glasses,[glasses],3)," + "Turk(eyelashvisibility,[feweyelashes],3)," + "Turk(gender,[male],3)," + "Turk(faceshape,[round,triangular,rectangular],3)," + "Turk(cheekdensity,[puffy,in,normal],3)," + "Turk(facemarks,[scars,moles,normal],3)," + "Turk(facelength,[long],3)," + "Turk(nosetoeyedist,[short,long],3)," + "Turk(nosetomouthdist,[long,small],3)," + "Turk(foreheadwrinkles,[wrinkled],3)," + "Turk(foreheadsize,[smallforehead,largeforehead],3)," + "Turk(haircolor,[darkhair,lighthair,greyhair],3)," + "Turk(hairdensity,[thick,bald,thin,halfbald],3)," + "Turk(widowspeak,[widowspeak],3)," + "Turk(hairstyle,[curlyhair],3)," + "Turk(noseorientation,[upnose,downnose],3)," + "Turk(nosewidth,[small,thick],3)," + "Turk(nosesize,[smallnose,bignose],3)," + "Turk(brokennose,[broken],3)," + "Turk(beard,[nobeard,bigbeard,lightbeard,goatee,linebeard,normalbeard,lincolnbeard],3)," + "Turk(chinsize,[shortchin,longchin],3)])"); + } +}; + +BR_REGISTER(Initializer, AttributeAlgorithmsInitializer) + +} // namespace br + +#include "core/attributealgorithms.moc" diff --git a/openbr/plugins/core/cache.cpp b/openbr/plugins/core/cache.cpp new file mode 100644 index 0000000..fa07aad --- /dev/null +++ b/openbr/plugins/core/cache.cpp @@ -0,0 +1,79 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Caches br::Transform::project() results. + * \author Josh Klontz \cite jklontz + */ +class CacheTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform) + BR_PROPERTY(br::Transform*, transform, NULL) + + static QHash cache; + static QMutex cacheLock; + +public: + ~CacheTransform() + { + if (cache.isEmpty()) return; + + // Write to cache + QFile file("Cache"); + if (!file.open(QFile::WriteOnly)) + qFatal("Unable to open %s for writing.", qPrintable(file.fileName())); + QDataStream stream(&file); + stream << cache; + file.close(); + } + +private: + void init() + { + if (!transform) return; + + trainable = transform->trainable; + if (!cache.isEmpty()) return; + + // Read from cache + QFile file("Cache"); + if (file.exists()) { + if (!file.open(QFile::ReadOnly)) + qFatal("Unable to open %s for reading.", qPrintable(file.fileName())); + QDataStream stream(&file); + stream >> cache; + file.close(); + } + } + + void train(const QList &data) + { + transform->train(data); + } + + void project(const Template &src, Template &dst) const + { + const QString &file = src.file; + if (cache.contains(file)) { + dst = cache[file]; + } else { + transform->project(src, dst); + cacheLock.lock(); + cache[file] = dst; + cacheLock.unlock(); + } + } +}; + +QHash CacheTransform::cache; +QMutex CacheTransform::cacheLock; + +BR_REGISTER(Transform, CacheTransform) + +} // namespace br + +#include "core/cache.moc" diff --git a/openbr/plugins/core/contract.cpp b/openbr/plugins/core/contract.cpp new file mode 100644 index 0000000..d23fe57 --- /dev/null +++ b/openbr/plugins/core/contract.cpp @@ -0,0 +1,45 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief It's like the opposite of ExpandTransform, but not really + * \author Charles Otto \cite caotto + * + * Given a set of templatelists as input, concatenate them onto a single Template + */ +class ContractTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + virtual void project(const TemplateList &src, TemplateList &dst) const + { + if (src.empty()) return; + Template out; + + foreach (const Template &t, src) { + out.merge(t); + } + out.file.clearRects(); + foreach (const Template &t, src) { + if (!t.file.rects().empty()) + out.file.appendRects(t.file.rects()); + } + dst.clear(); + dst.append(out); + } + + virtual void project(const Template &src, Template &dst) const + { + qFatal("this has gone bad"); + (void) src; (void) dst; + } +}; + +BR_REGISTER(Transform, ContractTransform) + +} // namespace br + +#include "core/contract.moc" diff --git a/openbr/plugins/validate.cpp b/openbr/plugins/core/crossvalidate.cpp index cb0e651..d0d9066 100644 --- a/openbr/plugins/validate.cpp +++ b/openbr/plugins/core/crossvalidate.cpp @@ -1,8 +1,7 @@ -#include -#include -#include "openbr_internal.h" -#include "openbr/core/common.h" -#include +#include + +#include +#include namespace br { @@ -134,138 +133,6 @@ class CrossValidateTransform : public MetaTransform BR_REGISTER(Transform, CrossValidateTransform) -/*! - * \ingroup distances - * \brief Cross validate a distance metric. - * \author Josh Klontz \cite jklontz - */ -class CrossValidateDistance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const Template &a, const Template &b) const - { - static const QString key("Partition"); // More efficient to preallocate this - const int partitionA = a.file.get(key, 0); - const int partitionB = b.file.get(key, 0); - return (partitionA != partitionB) ? -std::numeric_limits::max() : 0; - } -}; - -BR_REGISTER(Distance, CrossValidateDistance) - -/*! - * \ingroup distances - * \brief Checks target metadata against filters. - * \author Josh Klontz \cite jklontz - */ -class FilterDistance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const Template &a, const Template &b) const - { - (void) b; // Query template isn't checked - foreach (const QString &key, Globals->filters.keys()) { - bool keep = false; - const QString metadata = a.file.get(key, ""); - if (Globals->filters[key].isEmpty()) continue; - if (metadata.isEmpty()) return -std::numeric_limits::max(); - foreach (const QString &value, Globals->filters[key]) { - if (metadata == value) { - keep = true; - break; - } - } - if (!keep) return -std::numeric_limits::max(); - } - return 0; - } -}; - -BR_REGISTER(Distance, FilterDistance) - -/*! - * \ingroup distances - * \brief Checks target metadata against query metadata. - * \author Scott Klum \cite sklum - */ -class MetadataDistance : public UntrainableDistance -{ - Q_OBJECT - - Q_PROPERTY(QStringList filters READ get_filters WRITE set_filters RESET reset_filters STORED false) - BR_PROPERTY(QStringList, filters, QStringList()) - - float compare(const Template &a, const Template &b) const - { - foreach (const QString &key, filters) { - QString aValue = a.file.get(key, QString()); - QString bValue = b.file.get(key, QString()); - - // The query value may be a range. Let's check. - if (bValue.isEmpty()) bValue = QtUtils::toString(b.file.get(key, QPointF())); - - if (aValue.isEmpty() || bValue.isEmpty()) continue; - - bool keep = false; - bool ok; - - QPointF range = QtUtils::toPoint(bValue,&ok); - - if (ok) /* Range */ { - int value = range.x(); - int upperBound = range.y(); - - while (value <= upperBound) { - if (aValue == QString::number(value)) { - keep = true; - break; - } - value++; - } - } - else if (aValue == bValue) keep = true; - - if (!keep) return -std::numeric_limits::max(); - } - return 0; - } -}; - - -BR_REGISTER(Distance, MetadataDistance) - -/*! - * \ingroup distances - * \brief Sets distance to -FLOAT_MAX if a target template has/doesn't have a key. - * \author Scott Klum \cite sklum - */ -class RejectDistance : public UntrainableDistance -{ - Q_OBJECT - - Q_PROPERTY(QStringList keys READ get_keys WRITE set_keys RESET reset_keys STORED false) - BR_PROPERTY(QStringList, keys, QStringList()) - Q_PROPERTY(bool rejectIfContains READ get_rejectIfContains WRITE set_rejectIfContains RESET reset_rejectIfContains STORED false) - BR_PROPERTY(bool, rejectIfContains, false) - - float compare(const Template &a, const Template &b) const - { - // We don't look at the query - (void) b; - - foreach (const QString &key, keys) - if ((rejectIfContains && a.file.contains(key)) || (!rejectIfContains && !a.file.contains(key))) - return -std::numeric_limits::max(); - - return 0; - } -}; - - -BR_REGISTER(Distance, RejectDistance) - } // namespace br -#include "validate.moc" +#include "core/crossvalidate.moc" diff --git a/openbr/plugins/core/discard.cpp b/openbr/plugins/core/discard.cpp new file mode 100644 index 0000000..56c62a8 --- /dev/null +++ b/openbr/plugins/core/discard.cpp @@ -0,0 +1,26 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Removes all template's matrices. + * \see IdentityTransform FirstTransform RestTransform RemoveTransform + * \author Josh Klontz \cite jklontz + */ +class DiscardTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + dst.file = src.file; + } +}; + +BR_REGISTER(Transform, DiscardTransform) + +} // namespace br + +#include "core/discard.moc" diff --git a/openbr/plugins/core/discardtemplates.cpp b/openbr/plugins/core/discardtemplates.cpp new file mode 100644 index 0000000..cf3d7bc --- /dev/null +++ b/openbr/plugins/core/discardtemplates.cpp @@ -0,0 +1,26 @@ +#include + +namespace br +{ + +class DiscardTemplatesTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + (void) src; (void) dst; + qFatal("Incorrect project called on DiscardTemplatesTransform"); + } + void project(const TemplateList &src, TemplateList &dst) const + { + (void) src; + dst.clear(); + } +}; + +BR_REGISTER(Transform, DiscardTemplatesTransform) + +} // namespace br + +#include "core/discardtemplates.moc" diff --git a/openbr/plugins/core/distributetemplate.cpp b/openbr/plugins/core/distributetemplate.cpp new file mode 100644 index 0000000..53298f1 --- /dev/null +++ b/openbr/plugins/core/distributetemplate.cpp @@ -0,0 +1,126 @@ +#include + +#include + +namespace br +{ + +static void _projectList(const Transform *transform, const TemplateList *src, TemplateList *dst) +{ + transform->project(*src, *dst); +} + +class DistributeTemplateTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform) + BR_PROPERTY(br::Transform*, transform, NULL) + +public: + + Transform *smartCopy(bool &newTransform) + { + if (!transform->timeVarying()) { + newTransform = false; + return this; + } + newTransform = true; + + DistributeTemplateTransform *output = new DistributeTemplateTransform; + bool newChild = false; + output->transform = transform->smartCopy(newChild); + if (newChild) + output->transform->setParent(output); + + return output; + } + + void train(const QList &data) + { + if (!transform->trainable) { + qWarning("Attempted to train untrainable transform, nothing will happen."); + return; + } + + QList separated; + foreach (const TemplateList &list, data) { + foreach (const Template &t, list) { + separated.append(TemplateList()); + separated.last().append(t); + } + } + + transform->train(separated); + } + + void project(const Template &src, Template &dst) const + { + TemplateList input; + input.append(src); + TemplateList output; + project(input, output); + + if (output.size() != 1) qFatal("output contains more than 1 template"); + else dst = output[0]; + } + + // For each input template, form a single element TemplateList, push all those + // lists through transform, and form dst by concatenating the results. + // Process the single elemnt templates in parallel if parallelism is enabled. + void project(const TemplateList &src, TemplateList &dst) const + { + // Pre-allocate output for each template + QList output_buffer; + output_buffer.reserve(src.size()); + + // Can't declare this local to the loop because it would go out of scope + QList input_buffer; + input_buffer.reserve(src.size()); + + QFutureSynchronizer futures; + + for (int i =0; i < src.size();i++) { + input_buffer.append(TemplateList()); + output_buffer.append(TemplateList()); + } + QList > temp; + temp.reserve(src.size()); + for (int i=0; iparallelism > 1) temp.append(QtConcurrent::run(_projectList, transform, &input_buffer[i], &output_buffer[i])); + else _projectList(transform, &input_buffer[i], &output_buffer[i]); + } + // We add the futures in reverse order, since in Qt 5.1 at least the + // waiting thread will wait on them in the order added (which for uniform priority + // threads is the order of execution), and we want the waiting thread to go in the opposite order + // so that it can steal runnables and do something besides wait. + for (int i = temp.size() - 1; i >= 0; i--) { + futures.addFuture(temp[i]); + } + + futures.waitForFinished(); + + for (int i=0; iproject(src, dst); + return; + } + + void init() + { + if (!transform) + return; + + trainable = transform->trainable; + } + +}; +BR_REGISTER(Transform, DistributeTemplateTransform) + +} // namespace br + +#include "core/distributetemplate.moc" diff --git a/openbr/plugins/core/downsample.cpp b/openbr/plugins/core/downsampletraining.cpp index b1cc62f..8f3d38c 100644 --- a/openbr/plugins/core/downsample.cpp +++ b/openbr/plugins/core/downsampletraining.cpp @@ -116,4 +116,4 @@ BR_REGISTER(Transform, DownsampleTrainingTransform) } // namespace br -#include "downsample.moc" +#include "core/downsampletraining.moc" diff --git a/openbr/plugins/core/event.cpp b/openbr/plugins/core/event.cpp new file mode 100644 index 0000000..b17209d --- /dev/null +++ b/openbr/plugins/core/event.cpp @@ -0,0 +1,30 @@ +#include + +namespace br +{ + +class EventTransform : public UntrainableMetaTransform +{ + Q_OBJECT + Q_PROPERTY(QString eventName READ get_eventName WRITE set_eventName RESET reset_eventName STORED false) + BR_PROPERTY(QString, eventName, "") + + TemplateEvent event; + + void project(const Template &src, Template &dst) const + { + dst = src; + event.pulseSignal(dst); + } + + TemplateEvent *getEvent(const QString &name) + { + return name == eventName ? &event : NULL; + } +}; + +BR_REGISTER(Transform, EventTransform) + +} // namespace br + +#include "core/event.moc" diff --git a/openbr/plugins/core/expand.cpp b/openbr/plugins/core/expand.cpp new file mode 100644 index 0000000..6fdd8af --- /dev/null +++ b/openbr/plugins/core/expand.cpp @@ -0,0 +1,62 @@ +#include + +namespace br +{ + +static TemplateList Expanded(const TemplateList &templates) +{ + TemplateList expanded; + foreach (const Template &t, templates) { + const bool enrollAll = t.file.get("enrollAll"); + if (t.isEmpty()) { + if (!enrollAll) + expanded.append(t); + continue; + } + + const QList points = t.file.points(); + const QList rects = t.file.rects(); + if (points.size() % t.size() != 0) qFatal("Uneven point count."); + if (rects.size() % t.size() != 0) qFatal("Uneven rect count."); + const int pointStep = points.size() / t.size(); + const int rectStep = rects.size() / t.size(); + + for (int i=0; i + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Removes all but the first matrix from the template. + * \see IdentityTransform DiscardTransform RestTransform RemoveTransform + * \author Josh Klontz \cite jklontz + */ +class FirstTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + // AggregateFrames will leave the Template empty + // if it hasn't filled up the buffer + // so we gotta anticipate an empty Template + if (src.empty()) return; + dst.file = src.file; + dst = src.m(); + } +}; + +BR_REGISTER(Transform, FirstTransform) + +} // namespace br + +#include "core/first.moc" diff --git a/openbr/plugins/core/fork.cpp b/openbr/plugins/core/fork.cpp new file mode 100644 index 0000000..867cb7c --- /dev/null +++ b/openbr/plugins/core/fork.cpp @@ -0,0 +1,127 @@ +#include + +#include + +namespace br +{ + +static void _train(Transform *transform, const QList *data) +{ + transform->train(*data); +} + +/*! + * \ingroup transforms + * \brief Transforms in parallel. + * \author Josh Klontz \cite jklontz + * + * The source br::Template is seperately given to each transform and the results are appended together. + * + * \see PipeTransform + */ +class ForkTransform : public CompositeTransform +{ + Q_OBJECT + + void train(const QList &data) + { + if (!trainable) return; + QFutureSynchronizer futures; + for (int i=0; iprojectUpdate(src, res); + dst.merge(res); + } catch (...) { + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); + dst = Template(src.file); + dst.file.fte = true; + break; + } + } + } + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + dst.reserve(src.size()); + for (int i=0; iprojectUpdate(src, m); + if (m.size() != dst.size()) qFatal("TemplateList is of an unexpected size."); + for (int i=0; ifinalize(last_set); + if (last_set.empty()) + continue; + + if (output.empty()) output = last_set; + else + { + // is the number of templates received from this transform consistent with the number + // received previously? If not we can't do anything coherent here. + if (last_set.size() != output.size()) + qFatal("mismatched template list sizes in ForkTransform"); + for (int j = 0; j < output.size(); j++) { + output[j].append(last_set[j]); + } + } + } + } + +protected: + + // Apply each transform to src, concatenate the results + void _project(const Template &src, Template &dst) const + { + foreach (const Transform *f, transforms) { + try { + dst.merge((*f)(src)); + } catch (...) { + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); + dst = Template(src.file); + dst.file.fte = true; + break; + } + } + } + + void _project(const TemplateList &src, TemplateList &dst) const + { + dst.reserve(src.size()); + for (int i=0; iproject(src, m); + if (m.size() != dst.size()) qFatal("TemplateList is of an unexpected size."); + for (int i=0; i +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Flags images that failed to enroll based on the specified transform. + * \author Josh Klontz \cite jklontz + */ +class FTETransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform) + Q_PROPERTY(float min READ get_min WRITE set_min RESET reset_min) + Q_PROPERTY(float max READ get_max WRITE set_max RESET reset_max) + BR_PROPERTY(br::Transform*, transform, NULL) + BR_PROPERTY(float, min, -std::numeric_limits::max()) + BR_PROPERTY(float, max, std::numeric_limits::max()) + + void train(const TemplateList &data) + { + transform->train(data); + + TemplateList projectedData; + transform->project(data, projectedData); + + QList vals; + foreach (const Template &t, projectedData) { + if (!t.file.contains(transform->objectName())) + qFatal("Matrix metadata missing key %s.", qPrintable(transform->objectName())); + vals.append(t.file.get(transform->objectName())); + } + float q1, q3; + Common::Median(vals, &q1, &q3); + min = q1 - 1.5 * (q3 - q1); + max = q3 + 1.5 * (q3 - q1); + } + + void project(const Template &src, Template &dst) const + { + Template projectedSrc; + transform->project(src, projectedSrc); + const float val = projectedSrc.file.get(transform->objectName()); + + dst = src; + dst.file.set(transform->objectName(), val); + dst.file.set("PossibleFTE", (val < min) || (val > max)); + } +}; + +BR_REGISTER(Transform, FTETransform) + +} // namespace br + +#include "core/fte.moc" diff --git a/openbr/plugins/distance/gallerycompare.cpp b/openbr/plugins/core/gallerycompare.cpp index d4aba79..76a76da 100644 --- a/openbr/plugins/distance/gallerycompare.cpp +++ b/openbr/plugins/core/gallerycompare.cpp @@ -61,4 +61,4 @@ BR_REGISTER(Transform, GalleryCompareTransform) } // namespace br -#include "gallerycompare.moc" +#include "core/gallerycompare.moc" diff --git a/openbr/plugins/core/identity.cpp b/openbr/plugins/core/identity.cpp new file mode 100644 index 0000000..dccca5c --- /dev/null +++ b/openbr/plugins/core/identity.cpp @@ -0,0 +1,26 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief A no-op transform. + * \see DiscardTransform FirstTransform RestTransform RemoveTransform + * \author Josh Klontz \cite jklontz + */ +class IdentityTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + dst = src; + } +}; + +BR_REGISTER(Transform, IdentityTransform) + +} // namespace br + +#include "core/identity.moc" diff --git a/openbr/plugins/core/independent.cpp b/openbr/plugins/core/independent.cpp index 41a0054..59136a2 100644 --- a/openbr/plugins/core/independent.cpp +++ b/openbr/plugins/core/independent.cpp @@ -210,4 +210,4 @@ BR_REGISTER(Transform, IndependentTransform) } // namespace br -#include "independent.moc" +#include "core/independent.moc" diff --git a/openbr/plugins/jni.cpp b/openbr/plugins/core/jni.cpp index 9aa6870..1b84bb9 100644 --- a/openbr/plugins/jni.cpp +++ b/openbr/plugins/core/jni.cpp @@ -1,8 +1,8 @@ //Need to include location of jvm.dll (jdk version) and its parent directory in the environment variables #include -#include "openbr_internal.h" -#include "openbr/core/resource.h" +#include +#include #include namespace br diff --git a/openbr/plugins/likely.cpp b/openbr/plugins/core/likely.cpp index f15f4b4..400b7e7 100644 --- a/openbr/plugins/likely.cpp +++ b/openbr/plugins/core/likely.cpp @@ -1,8 +1,4 @@ -#include -#include - -#include "openbr/core/opencvutils.h" -#include "openbr_internal.h" +#include namespace br { @@ -57,68 +53,6 @@ public: BR_REGISTER(Transform, LikelyTransform) -/*! - * \ingroup formats - * \brief Likely matrix format - * - * www.liblikely.org - * \author Josh Klontz \cite jklontz - */ -class lmatFormat : public Format -{ - Q_OBJECT - - Template read() const - { - const likely_const_mat m = likely_read(qPrintable(file.name), likely_file_guess); - const Template result(likelyToOpenCVMat(m)); - likely_release_mat(m); - return result; - } - - void write(const Template &t) const - { - const likely_const_mat m = likelyFromOpenCVMat(t); - likely_write(m, qPrintable(file.name)); - likely_release_mat(m); - } -}; - -BR_REGISTER(Format, lmatFormat) - -/*! - * \ingroup formats - * \brief Likely matrix format - * - * www.liblikely.org - * \author Josh Klontz \cite jklontz - */ -class lmatGallery : public Gallery -{ - Q_OBJECT - QList mats; - - ~lmatGallery() - { - const likely_const_mat m = likelyFromOpenCVMat(OpenCVUtils::toMatByRow(mats)); - likely_write(m, qPrintable(file.name)); - likely_release_mat(m); - } - - TemplateList readBlock(bool *done) - { - *done = true; - qFatal("Not supported."); - } - - void write(const Template &t) - { - mats.append(t); - } -}; - -BR_REGISTER(Gallery, lmatGallery) - } // namespace br -#include "likely.moc" +#include "core/likely.moc" diff --git a/openbr/plugins/core/loadstore.cpp b/openbr/plugins/core/loadstore.cpp new file mode 100644 index 0000000..08f34fa --- /dev/null +++ b/openbr/plugins/core/loadstore.cpp @@ -0,0 +1,147 @@ +#include +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Caches transform training. + * \author Josh Klontz \cite jklontz + */ +class LoadStoreTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(QString transformString READ get_transformString WRITE set_transformString RESET reset_transformString STORED false) + Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) + BR_PROPERTY(QString, transformString, "Identity") + BR_PROPERTY(QString, fileName, QString()) + +public: + Transform *transform; + + LoadStoreTransform() : transform(NULL) {} + + QString description(bool expanded = false) const + { + if (expanded) { + QString res = transform->description(expanded); + return res; + } + return br::Object::description(expanded); + } + + Transform *simplify(bool &newTForm) + { + Transform *res = transform->simplify(newTForm); + return res; + } + + QList getChildren() const + { + QList rval; + rval.append(transform); + return rval; + } +private: + + void init() + { + if (transform != NULL) return; + if (fileName.isEmpty()) fileName = QRegExp("^[_a-zA-Z0-9]+$").exactMatch(transformString) ? transformString : QtUtils::shortTextHash(transformString); + + if (!tryLoad()) + transform = make(transformString); + else + trainable = false; + } + + bool timeVarying() const + { + return transform->timeVarying(); + } + + void train(const QList &data) + { + if (QFileInfo(getFileName()).exists()) + return; + + transform->train(data); + + qDebug("Storing %s", qPrintable(fileName)); + QtUtils::BlockCompression compressedOut; + QFile fout(fileName); + QtUtils::touchDir(fout); + compressedOut.setBasis(&fout); + + QDataStream stream(&compressedOut); + QString desc = transform->description(); + + if (!compressedOut.open(QFile::WriteOnly)) + qFatal("Failed to open %s for writing.", qPrintable(file)); + + stream << desc; + transform->store(stream); + compressedOut.close(); + } + + void project(const Template &src, Template &dst) const + { + transform->project(src, dst); + } + + void project(const TemplateList &src, TemplateList &dst) const + { + transform->project(src, dst); + } + + void projectUpdate(const Template &src, Template &dst) + { + transform->projectUpdate(src, dst); + } + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + transform->projectUpdate(src, dst); + } + + void finalize(TemplateList &output) + { + transform->finalize(output); + } + + QString getFileName() const + { + if (QFileInfo(fileName).exists()) return fileName; + const QString file = Globals->sdkPath + "/share/openbr/models/transforms/" + fileName; + return QFileInfo(file).exists() ? file : QString(); + } + + bool tryLoad() + { + const QString file = getFileName(); + if (file.isEmpty()) return false; + + qDebug("Loading %s", qPrintable(file)); + QFile fin(file); + QtUtils::BlockCompression reader(&fin); + if (!reader.open(QIODevice::ReadOnly)) { + if (QFileInfo(file).exists()) qFatal("Unable to open %s for reading. Check file permissions.", qPrintable(file)); + else qFatal("Unable to open %s for reading. File does not exist.", qPrintable(file)); + } + + QDataStream stream(&reader); + stream >> transformString; + + transform = Transform::make(transformString); + transform->load(stream); + + return true; + } +}; + +BR_REGISTER(Transform, LoadStoreTransform) + +} // namespace br + +#include "core/loadstore.moc" diff --git a/openbr/plugins/core/pipe.cpp b/openbr/plugins/core/pipe.cpp new file mode 100644 index 0000000..77938ac --- /dev/null +++ b/openbr/plugins/core/pipe.cpp @@ -0,0 +1,205 @@ +#include + +#include + +namespace br +{ + +/*! + * \ingroup Transforms + * \brief Transforms in series. + * \author Josh Klontz \cite jklontz + * + * The source br::Template is given to the first transform and the resulting br::Template is passed to the next transform, etc. + * + * \see ExpandTransform + * \see ForkTransform + */ +class PipeTransform : public CompositeTransform +{ + Q_OBJECT + + void _projectPartial(TemplateList *srcdst, int startIndex, int stopIndex) + { + TemplateList ftes; + for (int i=startIndex; iproject(*srcdst, res); + + splitFTEs(res, ftes); + *srcdst = res; + } + } + + void train(const QList &data) + { + if (!trainable) return; + + QList dataLines(data); + + int i = 0; + while (i < transforms.size()) { + // Conditional statement covers likely case that first transform is untrainable + if (transforms[i]->trainable) { + qDebug() << "Training " << transforms[i]->description() << "\n..."; + transforms[i]->train(dataLines); + } + + // if the transform is time varying, we can't project it in parallel + if (transforms[i]->timeVarying()) { + qDebug() << "Projecting " << transforms[i]->description() << "\n..."; + for (int j=0; j < dataLines.size();j++) { + TemplateList junk; + splitFTEs(dataLines[j], junk); + + transforms[i]->projectUpdate(dataLines[j], dataLines[j]); + } + + // advance i since we already projected for this stage. + i++; + + // the next stage might be trainable, so continue to evaluate it. + continue; + } + + // We project through any subsequent untrainable transforms at once + // as a memory optimization in case any of these intermediate + // transforms allocate a lot of memory (like OpenTransform) + // then we don't want all the training templates to be processed + // by that transform at once if we can avoid it. + int nextTrainableTransform = i+1; + while ((nextTrainableTransform < transforms.size()) && + !transforms[nextTrainableTransform]->trainable && + !transforms[nextTrainableTransform]->timeVarying()) + nextTrainableTransform++; + + // No more trainable transforms? Don't need any more projects then + if (nextTrainableTransform == transforms.size()) + break; + + fprintf(stderr, "Projecting %s", qPrintable(transforms[i]->description())); + for (int j=i+1; j < nextTrainableTransform; j++) + fprintf(stderr,"+%s", qPrintable(transforms[j]->description())); + fprintf(stderr, "\n...\n"); + fflush(stderr); + + QFutureSynchronizer futures; + for (int j=0; j < dataLines.size(); j++) + futures.addFuture(QtConcurrent::run(this, &PipeTransform::_projectPartial, &dataLines[j], i, nextTrainableTransform)); + futures.waitForFinished(); + + i = nextTrainableTransform; + } + } + + void projectUpdate(const Template &src, Template &dst) + { + dst = src; + foreach (Transform *f, transforms) { + try { + f->projectUpdate(dst); + if (dst.file.fte) + break; + } catch (...) { + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); + dst = Template(src.file); + dst.file.fte = true; + break; + } + } + } + + // For time varying transforms, parallel execution over individual templates + // won't work. + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + TemplateList ftes; + dst = src; + foreach (Transform *f, transforms) { + TemplateList res; + f->projectUpdate(dst, res); + splitFTEs(res, ftes); + dst = res; + } + dst.append(ftes); + } + + virtual void finalize(TemplateList &output) + { + output.clear(); + // For each transform, + for (int i = 0; i < transforms.size(); i++) + { + + // Collect any final templates + TemplateList last_set; + transforms[i]->finalize(last_set); + if (last_set.empty()) + continue; + // Push any templates received through the remaining transforms in the sequence + for (int j = (i+1); j < transforms.size();j++) + { + transforms[j]->projectUpdate(last_set); + } + // append the result to the output set + output.append(last_set); + } + } + + void init() + { + QList flattened; + for (int i=0;i < transforms.size(); i++) + { + PipeTransform *probe = dynamic_cast (transforms[i]); + if (!probe) { + flattened.append(transforms[i]); + continue; + } + for (int j=0; j < probe->transforms.size(); j++) + flattened.append(probe->transforms[j]); + } + transforms = flattened; + + CompositeTransform::init(); + } + +protected: + // Template list project -- process templates in parallel through Transform::project + // or if parallelism is disabled, handle them sequentially + void _project(const TemplateList &src, TemplateList &dst) const + { + TemplateList ftes; + dst = src; + foreach (const Transform *f, transforms) { + TemplateList res; + f->project(dst, res); + splitFTEs(res, ftes); + dst = res; + } + dst.append(ftes); + } + + // Single template const project, pass the template through each sub-transform, one after the other + virtual void _project(const Template &src, Template &dst) const + { + dst = src; + foreach (const Transform *f, transforms) { + try { + dst >> *f; + if (dst.file.fte) + break; + } catch (...) { + qWarning("Exception triggered when processing %s with transform %s", qPrintable(src.file.flat()), qPrintable(f->objectName())); + dst = Template(src.file); + dst.file.fte = true; + } + } + } +}; + +BR_REGISTER(Transform, PipeTransform) + +} // namespace br + +#include "core/pipe.moc" diff --git a/openbr/plugins/process.cpp b/openbr/plugins/core/processwrapper.cpp index ba3559b..70a674b 100644 --- a/openbr/plugins/process.cpp +++ b/openbr/plugins/core/processwrapper.cpp @@ -9,8 +9,8 @@ #include #include -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" +#include +#include using namespace cv; @@ -658,4 +658,4 @@ BR_REGISTER(Transform, ProcessWrapperTransform) } -#include "process.moc" +#include "core/processwrapper.moc" diff --git a/openbr/plugins/core/progresscounter.cpp b/openbr/plugins/core/progresscounter.cpp new file mode 100644 index 0000000..fadbcde --- /dev/null +++ b/openbr/plugins/core/progresscounter.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include + +namespace br +{ + +class ProgressCounterTransform : public TimeVaryingTransform +{ + Q_OBJECT + + Q_PROPERTY(qint64 totalProgress READ get_totalProgress WRITE set_totalProgress RESET reset_totalProgress STORED false) + BR_PROPERTY(qint64, totalProgress, 1) + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + dst = src; + + qint64 elapsed = timer.elapsed(); + int last_frame = -2; + if (!dst.empty()) { + for (int i=0;i < dst.size();i++) { + int frame = dst[i].file.get("FrameNumber", -1); + if (frame == last_frame && frame != -1) + continue; + + // Use 1 as the starting index for progress output + Globals->currentProgress = dst[i].file.get("progress",0)+1; + dst[i].file.remove("progress"); + last_frame = frame; + + Globals->currentStep++; + } + } + + // updated every second + if (elapsed > 1000) { + Globals->printStatus(); + timer.start(); + } + + return; + } + + void train(const TemplateList& data) + { + (void) data; + } + + void finalize(TemplateList &data) + { + (void) data; + float p = br_progress(); + qDebug("\r%05.2f%% ELAPSED=%s REMAINING=%s COUNT=%g", p*100, QtUtils::toTime(Globals->startTime.elapsed()/1000.0f).toStdString().c_str(), QtUtils::toTime(0).toStdString().c_str(), Globals->currentStep); + timer.start(); + Globals->startTime.start(); + Globals->currentStep = 0; + Globals->currentProgress = 0; + Globals->totalSteps = totalProgress; + } + + void init() + { + timer.start(); + Globals->startTime.start(); + Globals->currentProgress = 0; + Globals->currentStep = 0; + Globals->totalSteps = totalProgress; + } + +public: + ProgressCounterTransform() : TimeVaryingTransform(false,false) {} + QElapsedTimer timer; +}; + +BR_REGISTER(Transform, ProgressCounterTransform) + +} // namespace br + +#include "core/progresscounter.moc" diff --git a/openbr/plugins/core/registrar.cpp b/openbr/plugins/core/registrar.cpp new file mode 100644 index 0000000..8879c8b --- /dev/null +++ b/openbr/plugins/core/registrar.cpp @@ -0,0 +1,25 @@ +#include + +namespace br +{ + +/*! + * \ingroup initializers + * \brief Register custom objects with Qt meta object system. + * \author Charles Otto \cite caotto + */ +class Registrar : public Initializer +{ + Q_OBJECT + + void initialize() const + { + qRegisterMetaType(); + } +}; + +BR_REGISTER(Initializer, Registrar) + +} // namespace br + +#include "core/registrar.moc" diff --git a/openbr/plugins/core/remove.cpp b/openbr/plugins/core/remove.cpp new file mode 100644 index 0000000..7105d4d --- /dev/null +++ b/openbr/plugins/core/remove.cpp @@ -0,0 +1,31 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Removes the matrix from the template at the specified index. + * \author Josh Klontz \cite jklontz + * \see IdentityTransform DiscardTransform FirstTransform RestTransform + */ +//! [example_transform] +class RemoveTransform : public UntrainableMetaTransform +{ + Q_OBJECT + Q_PROPERTY(int index READ get_index WRITE set_index RESET reset_index STORED false) + BR_PROPERTY(int, index, 0) + + void project(const Template &src, Template &dst) const + { + dst = src; + dst.removeAt(index); + } +}; + +BR_REGISTER(Transform, RemoveTransform) +//! [example_transform] + +} // namespace br + +#include "core/remove.moc" diff --git a/openbr/plugins/core/rest.cpp b/openbr/plugins/core/rest.cpp new file mode 100644 index 0000000..49c87aa --- /dev/null +++ b/openbr/plugins/core/rest.cpp @@ -0,0 +1,27 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Removes the first matrix from the template. + * \see IdentityTransform DiscardTransform FirstTransform RemoveTransform + * \author Josh Klontz \cite jklontz + */ +class RestTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + void project(const Template &src, Template &dst) const + { + dst = src; + dst.removeFirst(); + } +}; + +BR_REGISTER(Transform, RestTransform) + +} // namespace br + +#include "core/rest.moc" diff --git a/openbr/plugins/core/schrodinger.cpp b/openbr/plugins/core/schrodinger.cpp new file mode 100644 index 0000000..1fbcfd6 --- /dev/null +++ b/openbr/plugins/core/schrodinger.cpp @@ -0,0 +1,45 @@ +#include + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Generates two templates, one of which is passed through a transform and the other + * is not. No cats were harmed in the making of this transform. + * \author Scott Klum \cite sklum + */ +class SchrodingerTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform) + BR_PROPERTY(br::Transform*, transform, NULL) + +public: + void train(const TemplateList &data) + { + transform->train(data); + } + + void project(const TemplateList &src, TemplateList &dst) const + { + foreach(const Template &t, src) { + dst.append(t); + Template u; + transform->project(t,u); + dst.append(u); + } + } + + void project(const Template &src, Template &dst) const { + TemplateList temp; + project(TemplateList() << src, temp); + if (!temp.isEmpty()) dst = temp.first(); + } + +}; +BR_REGISTER(Transform, SchrodingerTransform) + +} // namespace br + +#include "core/schrodinger.moc" diff --git a/openbr/plugins/core/singleton.cpp b/openbr/plugins/core/singleton.cpp index df64659..1a4ede8 100644 --- a/openbr/plugins/core/singleton.cpp +++ b/openbr/plugins/core/singleton.cpp @@ -70,4 +70,4 @@ BR_REGISTER(Transform, SingletonTransform) } // namespace br -#include "singleton.moc" +#include "core/singleton.moc" diff --git a/openbr/plugins/stream.cpp b/openbr/plugins/core/stream.cpp index c2e5019..dda116b 100644 --- a/openbr/plugins/stream.cpp +++ b/openbr/plugins/core/stream.cpp @@ -8,10 +8,11 @@ #include #include #include -#include "openbr_internal.h" -#include "openbr/core/common.h" -#include "openbr/core/opencvutils.h" -#include "openbr/core/qtutils.h" + +#include +#include +#include +#include using namespace cv; using namespace std; @@ -1360,5 +1361,5 @@ BR_REGISTER(Transform, StreamTransform) } // namespace br -#include "stream.moc" +#include "core/stream.moc" diff --git a/openbr/plugins/distance/L2.cpp b/openbr/plugins/distance/L2.cpp index 3543894..aca49a0 100644 --- a/openbr/plugins/distance/L2.cpp +++ b/openbr/plugins/distance/L2.cpp @@ -27,4 +27,4 @@ BR_REGISTER(Distance, L2Distance) } // namespace br -#include "L2.moc" +#include "distance/L2.moc" diff --git a/openbr/plugins/distance/attribute.cpp b/openbr/plugins/distance/attribute.cpp index 17446b2..cdf4568 100644 --- a/openbr/plugins/distance/attribute.cpp +++ b/openbr/plugins/distance/attribute.cpp @@ -31,4 +31,4 @@ BR_REGISTER(Distance, AttributeDistance) } // namespace br -#include "attribute.moc" +#include "distance/attribute.moc" diff --git a/openbr/plugins/distance/bayesianquantization.cpp b/openbr/plugins/distance/bayesianquantization.cpp new file mode 100644 index 0000000..0fe58e4 --- /dev/null +++ b/openbr/plugins/distance/bayesianquantization.cpp @@ -0,0 +1,88 @@ +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup distances + * \brief Bayesian quantization distance + * \author Josh Klontz \cite jklontz + */ +class BayesianQuantizationDistance : public Distance +{ + Q_OBJECT + + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + BR_PROPERTY(QString, inputVariable, "Label") + + QVector loglikelihoods; + + static void computeLogLikelihood(const Mat &data, const QList &labels, float *loglikelihood) + { + const QList vals = OpenCVUtils::matrixToVector(data); + if (vals.size() != labels.size()) + qFatal("Logic error."); + + QVector genuines(256, 0), impostors(256,0); + for (int i=0; i 1) || (src.first().m().type() != CV_8UC1)) + qFatal("Expected sigle matrix templates of type CV_8UC1!"); + + const Mat data = OpenCVUtils::toMat(src.data()); + const QList templateLabels = src.indexProperty(inputVariable); + loglikelihoods = QVector(data.cols*256, 0); + + QFutureSynchronizer futures; + for (int i=0; i> loglikelihoods; + } +}; + +BR_REGISTER(Distance, BayesianQuantizationDistance) + +} // namespace br + +#include "distance/bayesianquantization.moc" diff --git a/openbr/plugins/distance/byteL1.cpp b/openbr/plugins/distance/byteL1.cpp index 4457999..ea377ac 100644 --- a/openbr/plugins/distance/byteL1.cpp +++ b/openbr/plugins/distance/byteL1.cpp @@ -23,4 +23,4 @@ BR_REGISTER(Distance, ByteL1Distance) } // namespace br -#include "byteL1.moc" +#include "distance/byteL1.moc" diff --git a/openbr/plugins/distance/crossvalidate.cpp b/openbr/plugins/distance/crossvalidate.cpp new file mode 100644 index 0000000..54a8f17 --- /dev/null +++ b/openbr/plugins/distance/crossvalidate.cpp @@ -0,0 +1,28 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Cross validate a distance metric. + * \author Josh Klontz \cite jklontz + */ +class CrossValidateDistance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const Template &a, const Template &b) const + { + static const QString key("Partition"); // More efficient to preallocate this + const int partitionA = a.file.get(key, 0); + const int partitionB = b.file.get(key, 0); + return (partitionA != partitionB) ? -std::numeric_limits::max() : 0; + } +}; + +BR_REGISTER(Distance, CrossValidateDistance) + +} // namespace br + +#include "distance/crossvalidate.moc" diff --git a/openbr/plugins/distance/default.cpp b/openbr/plugins/distance/default.cpp index 1409145..7c9c4d5 100644 --- a/openbr/plugins/distance/default.cpp +++ b/openbr/plugins/distance/default.cpp @@ -28,4 +28,4 @@ BR_REGISTER(Distance, DefaultDistance) } // namespace br -#include "default.moc" +#include "distance/default.moc" diff --git a/openbr/plugins/distance/dist.cpp b/openbr/plugins/distance/dist.cpp index 1f36afc..2fb8887 100644 --- a/openbr/plugins/distance/dist.cpp +++ b/openbr/plugins/distance/dist.cpp @@ -101,4 +101,4 @@ BR_REGISTER(Distance, DistDistance) } // namespace br -#include "dist.moc" +#include "distance/dist.moc" diff --git a/openbr/plugins/distance/filter.cpp b/openbr/plugins/distance/filter.cpp new file mode 100644 index 0000000..b752493 --- /dev/null +++ b/openbr/plugins/distance/filter.cpp @@ -0,0 +1,39 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Checks target metadata against filters. + * \author Josh Klontz \cite jklontz + */ +class FilterDistance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const Template &a, const Template &b) const + { + (void) b; // Query template isn't checked + foreach (const QString &key, Globals->filters.keys()) { + bool keep = false; + const QString metadata = a.file.get(key, ""); + if (Globals->filters[key].isEmpty()) continue; + if (metadata.isEmpty()) return -std::numeric_limits::max(); + foreach (const QString &value, Globals->filters[key]) { + if (metadata == value) { + keep = true; + break; + } + } + if (!keep) return -std::numeric_limits::max(); + } + return 0; + } +}; + +BR_REGISTER(Distance, FilterDistance) + +} // namespace br + +#include "distance/filter.moc" diff --git a/openbr/plugins/distance/fuse.cpp b/openbr/plugins/distance/fuse.cpp index 782fe58..d517db3 100644 --- a/openbr/plugins/distance/fuse.cpp +++ b/openbr/plugins/distance/fuse.cpp @@ -98,4 +98,4 @@ BR_REGISTER(Distance, FuseDistance) } // namespace br -#include "fuse.moc" +#include "distance/fuse.moc" diff --git a/openbr/plugins/distance/halfbyteL1.cpp b/openbr/plugins/distance/halfbyteL1.cpp index 6f89ebb..70ef1a4 100644 --- a/openbr/plugins/distance/halfbyteL1.cpp +++ b/openbr/plugins/distance/halfbyteL1.cpp @@ -26,4 +26,4 @@ BR_REGISTER(Distance, HalfByteL1Distance) } // namespace br -#include "halfbyteL1.moc" +#include "distance/halfbyteL1.moc" diff --git a/openbr/plugins/distance/heatmap.cpp b/openbr/plugins/distance/heatmap.cpp new file mode 100644 index 0000000..351c04a --- /dev/null +++ b/openbr/plugins/distance/heatmap.cpp @@ -0,0 +1,84 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief 1v1 heat map comparison + * \author Scott Klum \cite sklum + */ +class HeatMapDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) + Q_PROPERTY(int step READ get_step WRITE set_step RESET reset_step STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + BR_PROPERTY(QString, description, "IdenticalDistance") + BR_PROPERTY(int, step, 1) + BR_PROPERTY(QString, inputVariable, "Label") + + QList distances; + + void train(const TemplateList &src) + { + QList patches; + + // Split src into list of TemplateLists of corresponding patches across all Templates + for (int i=0; itrain(patches[i]); + } + + float compare(const cv::Mat &target, const cv::Mat &query) const + { + (void) target; + (void) query; + qFatal("Heatmap Distance not compatible with Template to Template comparison."); + + return 0; + } + + void compare(const TemplateList &target, const TemplateList &query, Output *output) const + { + for (int i=0; isetRelative(distances[j]->compare(target[i][j],query[i][j]), j, 0); + } + } + + void store(QDataStream &stream) const + { + stream << distances.size(); + foreach (Distance *distance, distances) + distance->store(stream); + } + + void load(QDataStream &stream) + { + int numDistances; + stream >> numDistances; + while (distances.size() < numDistances) + distances.append(make(description)); + foreach (Distance *distance, distances) + distance->load(stream); + } +}; + +BR_REGISTER(Distance, HeatMapDistance) + +} // namespace br + +#include "distance/heatmap.moc" diff --git a/openbr/plugins/distance/identical.cpp b/openbr/plugins/distance/identical.cpp index ec36f24..aae6cd6 100644 --- a/openbr/plugins/distance/identical.cpp +++ b/openbr/plugins/distance/identical.cpp @@ -28,4 +28,4 @@ BR_REGISTER(Distance, IdenticalDistance) } // namespace br -#include "identical.moc" +#include "distance/identical.moc" diff --git a/openbr/plugins/distance/keypointmatcher.cpp b/openbr/plugins/distance/keypointmatcher.cpp index f6a720e..bceb45b 100644 --- a/openbr/plugins/distance/keypointmatcher.cpp +++ b/openbr/plugins/distance/keypointmatcher.cpp @@ -55,4 +55,4 @@ BR_REGISTER(Distance, KeyPointMatcherDistance) } // namespace br -#include "keypointmatcher.moc" +#include "distance/keypointmatcher.moc" diff --git a/openbr/plugins/distance/l1.cpp b/openbr/plugins/distance/l1.cpp index 1fa2183..dae988f 100644 --- a/openbr/plugins/distance/l1.cpp +++ b/openbr/plugins/distance/l1.cpp @@ -27,4 +27,4 @@ BR_REGISTER(Distance, L1Distance) } // namespace br -#include "L1.moc" +#include "distance/L1.moc" diff --git a/openbr/plugins/quality.cpp b/openbr/plugins/distance/matchprobability.cpp index 11d4bbd..8bc6f63 100644 --- a/openbr/plugins/quality.cpp +++ b/openbr/plugins/distance/matchprobability.cpp @@ -1,83 +1,11 @@ -#include #include -#include "openbr_internal.h" -#include "openbr/core/common.h" -#include "openbr/core/opencvutils.h" +#include +#include namespace br { -/*! - * \ingroup transforms - * \brief Impostor Uniqueness Measure \cite klare12 - * \author Josh Klontz \cite jklontz - */ -class ImpostorUniquenessMeasureTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) - Q_PROPERTY(double mean READ get_mean WRITE set_mean RESET reset_mean) - Q_PROPERTY(double stddev READ get_stddev WRITE set_stddev RESET reset_stddev) - Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) - BR_PROPERTY(br::Distance*, distance, Distance::make("Dist(L2)", this)) - BR_PROPERTY(double, mean, 0) - BR_PROPERTY(double, stddev, 1) - BR_PROPERTY(QString, inputVariable, "Label") - - TemplateList impostors; - - float calculateIUM(const Template &probe, const TemplateList &gallery) const - { - const QString probeLabel = probe.file.get(inputVariable); - TemplateList subset = gallery; - for (int j=subset.size()-1; j>=0; j--) - if (subset[j].file.get(inputVariable) == probeLabel) - subset.removeAt(j); - - QList scores = distance->compare(subset, probe); - float min, max; - Common::MinMax(scores, &min, &max); - double mean = Common::Mean(scores); - return (max-mean)/(max-min); - } - - void train(const TemplateList &data) - { - distance->train(data); - impostors = data; - - QList iums; iums.reserve(impostors.size()); - for (int i=0; istore(stream); - stream << mean << stddev << impostors; - } - - void load(QDataStream &stream) - { - distance->load(stream); - stream >> mean >> stddev >> impostors; - } -}; - -BR_REGISTER(Transform, ImpostorUniquenessMeasureTransform) - - float KDEPointer(const QList *scores, double x, double h) { return Common::KernelDensityEstimation(*scores, x, h); @@ -119,7 +47,7 @@ struct KDE if (gaussian) return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((score-mean)/stddev, 2)); if (bins.empty()) return -std::numeric_limits::max(); - + if (score <= min) return bins.first(); if (score >= max) return bins.last(); const float x = (score-min)/(max-min)*bins.size(); @@ -186,7 +114,7 @@ class MatchProbabilityDistance : public Distance const QList labels = src.indexProperty(inputVariable); QScopedPointer matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size()))); distance->compare(src, src, matrixOutput.data()); - + QList genuineScores, impostorScores; genuineScores.reserve(labels.size()); impostorScores.reserve(labels.size()*labels.size()); @@ -199,7 +127,7 @@ class MatchProbabilityDistance : public Distance else impostorScores.append(score); } } - + mp = MP(genuineScores, impostorScores, !gaussian); } @@ -246,204 +174,6 @@ protected: BR_REGISTER(Distance, MatchProbabilityDistance) -class ZScoreDistance : public Distance -{ - Q_OBJECT - Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) - Q_PROPERTY(bool crossModality READ get_crossModality WRITE set_crossModality RESET reset_crossModality STORED false) - BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) - BR_PROPERTY(bool, crossModality, false) - - float min, max; - double mean, stddev; - - void train(const TemplateList &src) - { - distance->train(src); - - QScopedPointer matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size()))); - distance->compare(src, src, matrixOutput.data()); - - QList scores; - scores.reserve(src.size()*src.size()); - for (int i=0; idata.at(i, j); - if (score == -std::numeric_limits::max()) continue; - if (crossModality && src[i].file.get("MODALITY") == src[j].file.get("MODALITY")) continue; - scores.append(score); - } - } - - Common::MinMax(scores, &min, &max); - Common::MeanStdDev(scores, &mean, &stddev); - - if (stddev == 0) qFatal("Stddev is 0."); - } - - float compare(const Template &target, const Template &query) const - { - float score = distance->compare(target,query); - if (score == -std::numeric_limits::max()) score = (min - mean) / stddev; - else if (score == std::numeric_limits::max()) score = (max - mean) / stddev; - else score = (score - mean) / stddev; - return score; - } - - void store(QDataStream &stream) const - { - distance->store(stream); - stream << min << max << mean << stddev; - } - - void load(QDataStream &stream) - { - distance->load(stream); - stream >> min >> max >> mean >> stddev; - } -}; - -BR_REGISTER(Distance, ZScoreDistance) - -/*! - * \ingroup distances - * \brief 1v1 heat map comparison - * \author Scott Klum \cite sklum - */ -class HeatMapDistance : public Distance -{ - Q_OBJECT - Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) - Q_PROPERTY(int step READ get_step WRITE set_step RESET reset_step STORED false) - Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) - BR_PROPERTY(QString, description, "IdenticalDistance") - BR_PROPERTY(int, step, 1) - BR_PROPERTY(QString, inputVariable, "Label") - - QList distances; - - void train(const TemplateList &src) - { - QList patches; - - // Split src into list of TemplateLists of corresponding patches across all Templates - for (int i=0; itrain(patches[i]); - } - - float compare(const cv::Mat &target, const cv::Mat &query) const - { - (void) target; - (void) query; - qFatal("Heatmap Distance not compatible with Template to Template comparison."); - - return 0; - } - - void compare(const TemplateList &target, const TemplateList &query, Output *output) const - { - for (int i=0; isetRelative(distances[j]->compare(target[i][j],query[i][j]), j, 0); - } - } - - void store(QDataStream &stream) const - { - stream << distances.size(); - foreach (Distance *distance, distances) - distance->store(stream); - } - - void load(QDataStream &stream) - { - int numDistances; - stream >> numDistances; - while (distances.size() < numDistances) - distances.append(make(description)); - foreach (Distance *distance, distances) - distance->load(stream); - } -}; - -BR_REGISTER(Distance, HeatMapDistance) - -/*! - * \ingroup distances - * \brief Linear normalizes of a distance so the mean impostor score is 0 and the mean genuine score is 1. - * \author Josh Klontz \cite jklontz - */ -class UnitDistance : public Distance -{ - Q_OBJECT - Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance) - Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a) - Q_PROPERTY(float b READ get_b WRITE set_b RESET reset_b) - Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) - BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) - BR_PROPERTY(float, a, 1) - BR_PROPERTY(float, b, 0) - BR_PROPERTY(QString, inputVariable, "Label") - - void train(const TemplateList &templates) - { - const TemplateList samples = templates.mid(0, 2000); - const QList sampleLabels = samples.indexProperty(inputVariable); - QScopedPointer matrixOutput(MatrixOutput::make(FileList(samples.size()), FileList(samples.size()))); - Distance::compare(samples, samples, matrixOutput.data()); - - double genuineAccumulator, impostorAccumulator; - int genuineCount, impostorCount; - genuineAccumulator = impostorAccumulator = genuineCount = impostorCount = 0; - - for (int i=0; idata.at(i, j); - if (sampleLabels[i] == sampleLabels[j]) { - genuineAccumulator += val; - genuineCount++; - } else { - impostorAccumulator += val; - impostorCount++; - } - } - } - - if (genuineCount == 0) { qWarning("No genuine matches."); return; } - if (impostorCount == 0) { qWarning("No impostor matches."); return; } - - double genuineMean = genuineAccumulator / genuineCount; - double impostorMean = impostorAccumulator / impostorCount; - - if (genuineMean == impostorMean) { qWarning("Genuines and impostors are indistinguishable."); return; } - - a = 1.0/(genuineMean-impostorMean); - b = impostorMean; - - qDebug("a = %f, b = %f", a, b); - } - - float compare(const Template &target, const Template &query) const - { - return a * (distance->compare(target, query) - b); - } -}; - -BR_REGISTER(Distance, UnitDistance) - } // namespace br -#include "quality.moc" +#include "distance/matchprobability.moc" diff --git a/openbr/plugins/distance/metadata.cpp b/openbr/plugins/distance/metadata.cpp new file mode 100644 index 0000000..2a49568 --- /dev/null +++ b/openbr/plugins/distance/metadata.cpp @@ -0,0 +1,60 @@ +#include +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Checks target metadata against query metadata. + * \author Scott Klum \cite sklum + */ +class MetadataDistance : public UntrainableDistance +{ + Q_OBJECT + + Q_PROPERTY(QStringList filters READ get_filters WRITE set_filters RESET reset_filters STORED false) + BR_PROPERTY(QStringList, filters, QStringList()) + + float compare(const Template &a, const Template &b) const + { + foreach (const QString &key, filters) { + QString aValue = a.file.get(key, QString()); + QString bValue = b.file.get(key, QString()); + + // The query value may be a range. Let's check. + if (bValue.isEmpty()) bValue = QtUtils::toString(b.file.get(key, QPointF())); + + if (aValue.isEmpty() || bValue.isEmpty()) continue; + + bool keep = false; + bool ok; + + QPointF range = QtUtils::toPoint(bValue,&ok); + + if (ok) /* Range */ { + int value = range.x(); + int upperBound = range.y(); + + while (value <= upperBound) { + if (aValue == QString::number(value)) { + keep = true; + break; + } + value++; + } + } + else if (aValue == bValue) keep = true; + + if (!keep) return -std::numeric_limits::max(); + } + return 0; + } +}; + + +BR_REGISTER(Distance, MetadataDistance) + +} // namespace br + +#include "distance/metadata.moc" diff --git a/openbr/plugins/distance/neglogplusone.cpp b/openbr/plugins/distance/neglogplusone.cpp index ded2dfc..3c180eb 100644 --- a/openbr/plugins/distance/neglogplusone.cpp +++ b/openbr/plugins/distance/neglogplusone.cpp @@ -39,4 +39,4 @@ BR_REGISTER(Distance, NegativeLogPlusOneDistance) } // namespace br -#include "neglogplusone.moc" +#include "distance/neglogplusone.moc" diff --git a/openbr/plugins/distance/online.cpp b/openbr/plugins/distance/online.cpp index 1880e68..19a6ab7 100644 --- a/openbr/plugins/distance/online.cpp +++ b/openbr/plugins/distance/online.cpp @@ -32,4 +32,4 @@ BR_REGISTER(Distance, OnlineDistance) } // namespace br -#include "online.moc" +#include "distance/online.moc" diff --git a/openbr/plugins/distance/pipe.cpp b/openbr/plugins/distance/pipe.cpp index a6cc9c0..de55d05 100644 --- a/openbr/plugins/distance/pipe.cpp +++ b/openbr/plugins/distance/pipe.cpp @@ -44,4 +44,4 @@ BR_REGISTER(Distance, PipeDistance) } // namespace br -#include "pipe.moc" +#include "distance/pipe.moc" diff --git a/openbr/plugins/distance/reject.cpp b/openbr/plugins/distance/reject.cpp new file mode 100644 index 0000000..e1aec36 --- /dev/null +++ b/openbr/plugins/distance/reject.cpp @@ -0,0 +1,38 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Sets distance to -FLOAT_MAX if a target template has/doesn't have a key. + * \author Scott Klum \cite sklum + */ +class RejectDistance : public UntrainableDistance +{ + Q_OBJECT + + Q_PROPERTY(QStringList keys READ get_keys WRITE set_keys RESET reset_keys STORED false) + BR_PROPERTY(QStringList, keys, QStringList()) + Q_PROPERTY(bool rejectIfContains READ get_rejectIfContains WRITE set_rejectIfContains RESET reset_rejectIfContains STORED false) + BR_PROPERTY(bool, rejectIfContains, false) + + float compare(const Template &a, const Template &b) const + { + // We don't look at the query + (void) b; + + foreach (const QString &key, keys) + if ((rejectIfContains && a.file.contains(key)) || (!rejectIfContains && !a.file.contains(key))) + return -std::numeric_limits::max(); + + return 0; + } +}; + + +BR_REGISTER(Distance, RejectDistance) + +} // namespace br + +#include "distance/reject.moc" diff --git a/openbr/plugins/distance/sum.cpp b/openbr/plugins/distance/sum.cpp index 6ab6b38..4e219c4 100644 --- a/openbr/plugins/distance/sum.cpp +++ b/openbr/plugins/distance/sum.cpp @@ -43,4 +43,4 @@ BR_REGISTER(Distance, SumDistance) } // namespace br -#include "sum.moc" +#include "distance/sum.moc" diff --git a/openbr/plugins/distance/turk.cpp b/openbr/plugins/distance/turk.cpp new file mode 100644 index 0000000..43063cb --- /dev/null +++ b/openbr/plugins/distance/turk.cpp @@ -0,0 +1,49 @@ +#include +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Unmaps Turk HITs to be compared against query mats + * \author Scott Klum \cite sklum + */ +class TurkDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(QString key READ get_key WRITE set_key RESET reset_key) + Q_PROPERTY(QStringList values READ get_values WRITE set_values RESET reset_values STORED false) + BR_PROPERTY(QString, key, QString()) + BR_PROPERTY(QStringList, values, QStringList()) + + bool targetHuman; + bool queryMachine; + + void init() + { + targetHuman = Globals->property("TurkTargetHuman").toBool(); + queryMachine = Globals->property("TurkQueryMachine").toBool(); + } + + cv::Mat getValues(const Template &t) const + { + QList result; + foreach (const QString &value, values) + result.append(t.file.get(key + "_" + value)); + return OpenCVUtils::toMat(result, 1); + } + + float compare(const Template &target, const Template &query) const + { + const cv::Mat a = targetHuman ? getValues(target) : target.m(); + const cv::Mat b = queryMachine ? query.m() : getValues(query); + return -norm(a, b, cv::NORM_L1); + } +}; + +BR_REGISTER(Distance, TurkDistance) + +} // namespace br + +#include "distance/turk.moc" diff --git a/openbr/plugins/distance/unit.cpp b/openbr/plugins/distance/unit.cpp new file mode 100644 index 0000000..7919841 --- /dev/null +++ b/openbr/plugins/distance/unit.cpp @@ -0,0 +1,71 @@ +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief Linear normalizes of a distance so the mean impostor score is 0 and the mean genuine score is 1. + * \author Josh Klontz \cite jklontz + */ +class UnitDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance) + Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a) + Q_PROPERTY(float b READ get_b WRITE set_b RESET reset_b) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) + BR_PROPERTY(float, a, 1) + BR_PROPERTY(float, b, 0) + BR_PROPERTY(QString, inputVariable, "Label") + + void train(const TemplateList &templates) + { + const TemplateList samples = templates.mid(0, 2000); + const QList sampleLabels = samples.indexProperty(inputVariable); + QScopedPointer matrixOutput(MatrixOutput::make(FileList(samples.size()), FileList(samples.size()))); + Distance::compare(samples, samples, matrixOutput.data()); + + double genuineAccumulator, impostorAccumulator; + int genuineCount, impostorCount; + genuineAccumulator = impostorAccumulator = genuineCount = impostorCount = 0; + + for (int i=0; idata.at(i, j); + if (sampleLabels[i] == sampleLabels[j]) { + genuineAccumulator += val; + genuineCount++; + } else { + impostorAccumulator += val; + impostorCount++; + } + } + } + + if (genuineCount == 0) { qWarning("No genuine matches."); return; } + if (impostorCount == 0) { qWarning("No impostor matches."); return; } + + double genuineMean = genuineAccumulator / genuineCount; + double impostorMean = impostorAccumulator / impostorCount; + + if (genuineMean == impostorMean) { qWarning("Genuines and impostors are indistinguishable."); return; } + + a = 1.0/(genuineMean-impostorMean); + b = impostorMean; + + qDebug("a = %f, b = %f", a, b); + } + + float compare(const Template &target, const Template &query) const + { + return a * (distance->compare(target, query) - b); + } +}; + +BR_REGISTER(Distance, UnitDistance) + +} // namespace br + +#include "distance/unit.moc" diff --git a/openbr/plugins/distance/zscore.cpp b/openbr/plugins/distance/zscore.cpp new file mode 100644 index 0000000..bbb52e5 --- /dev/null +++ b/openbr/plugins/distance/zscore.cpp @@ -0,0 +1,68 @@ +#include +#include + +namespace br +{ + +class ZScoreDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + Q_PROPERTY(bool crossModality READ get_crossModality WRITE set_crossModality RESET reset_crossModality STORED false) + BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) + BR_PROPERTY(bool, crossModality, false) + + float min, max; + double mean, stddev; + + void train(const TemplateList &src) + { + distance->train(src); + + QScopedPointer matrixOutput(MatrixOutput::make(FileList(src.size()), FileList(src.size()))); + distance->compare(src, src, matrixOutput.data()); + + QList scores; + scores.reserve(src.size()*src.size()); + for (int i=0; idata.at(i, j); + if (score == -std::numeric_limits::max()) continue; + if (crossModality && src[i].file.get("MODALITY") == src[j].file.get("MODALITY")) continue; + scores.append(score); + } + } + + Common::MinMax(scores, &min, &max); + Common::MeanStdDev(scores, &mean, &stddev); + + if (stddev == 0) qFatal("Stddev is 0."); + } + + float compare(const Template &target, const Template &query) const + { + float score = distance->compare(target,query); + if (score == -std::numeric_limits::max()) score = (min - mean) / stddev; + else if (score == std::numeric_limits::max()) score = (max - mean) / stddev; + else score = (score - mean) / stddev; + return score; + } + + void store(QDataStream &stream) const + { + distance->store(stream); + stream << min << max << mean << stddev; + } + + void load(QDataStream &stream) + { + distance->load(stream); + stream >> min >> max >> mean >> stddev; + } +}; + +BR_REGISTER(Distance, ZScoreDistance) + +} // namespace br + +#include "distance/zscore.moc" diff --git a/openbr/plugins/format/binary.cpp b/openbr/plugins/format/binary.cpp index 75e8fe0..1a82d57 100644 --- a/openbr/plugins/format/binary.cpp +++ b/openbr/plugins/format/binary.cpp @@ -67,4 +67,4 @@ BR_REGISTER(Format, binaryFormat) } // namespace br -#include "binary.moc" +#include "format/binary.moc" diff --git a/openbr/plugins/format/csv.cpp b/openbr/plugins/format/csv.cpp index ccede30..51d0e2f 100644 --- a/openbr/plugins/format/csv.cpp +++ b/openbr/plugins/format/csv.cpp @@ -70,4 +70,4 @@ BR_REGISTER(Format, csvFormat) } // namespace br -#include "csv.moc" +#include "format/csv.moc" diff --git a/openbr/plugins/format/ebts.cpp b/openbr/plugins/format/ebts.cpp index 97046f6..4a28fca 100644 --- a/openbr/plugins/format/ebts.cpp +++ b/openbr/plugins/format/ebts.cpp @@ -196,4 +196,4 @@ BR_REGISTER(Format, ebtsFormat) } // namespace br -#include "ebts.moc" +#include "format/ebts.moc" diff --git a/openbr/plugins/format/lffs.cpp b/openbr/plugins/format/lffs.cpp index b1da86e..2fc880c 100644 --- a/openbr/plugins/format/lffs.cpp +++ b/openbr/plugins/format/lffs.cpp @@ -33,4 +33,4 @@ BR_REGISTER(Format, lffsFormat) } // namespace br -#include "lffs.moc" +#include "format/lffs.moc" diff --git a/openbr/plugins/format/lmat.cpp b/openbr/plugins/format/lmat.cpp new file mode 100644 index 0000000..7bb4e62 --- /dev/null +++ b/openbr/plugins/format/lmat.cpp @@ -0,0 +1,37 @@ +#include + +namespace br +{ + +/*! + * \ingroup formats + * \brief Likely matrix format + * + * www.liblikely.org + * \author Josh Klontz \cite jklontz + */ +class lmatFormat : public Format +{ + Q_OBJECT + + Template read() const + { + const likely_const_mat m = likely_read(qPrintable(file.name), likely_file_guess); + const Template result(likelyToOpenCVMat(m)); + likely_release_mat(m); + return result; + } + + void write(const Template &t) const + { + const likely_const_mat m = likelyFromOpenCVMat(t); + likely_write(m, qPrintable(file.name)); + likely_release_mat(m); + } +}; + +BR_REGISTER(Format, lmatFormat) + +} // namespace br + +#include "format/lmat.moc" diff --git a/openbr/plugins/format/mat.cpp b/openbr/plugins/format/mat.cpp index 6e6e411..17a821f 100644 --- a/openbr/plugins/format/mat.cpp +++ b/openbr/plugins/format/mat.cpp @@ -224,4 +224,4 @@ BR_REGISTER(Format, matFormat) } // namespace br -#include "mat.moc" +#include "format/mat.moc" diff --git a/openbr/plugins/format/mtx.cpp b/openbr/plugins/format/mtx.cpp index 0ae94af..1aa25cf 100644 --- a/openbr/plugins/format/mtx.cpp +++ b/openbr/plugins/format/mtx.cpp @@ -44,4 +44,4 @@ BR_REGISTER(Format, maskFormat) } // namespace br -#include "mtx.moc" +#include "format/mtx.moc" diff --git a/openbr/plugins/format/null.cpp b/openbr/plugins/format/null.cpp index b5cbd38..a86382c 100644 --- a/openbr/plugins/format/null.cpp +++ b/openbr/plugins/format/null.cpp @@ -27,4 +27,4 @@ BR_REGISTER(Format, nullFormat) } // namespace br -#include "null.moc" +#include "format/null.moc" diff --git a/openbr/plugins/format/post.cpp b/openbr/plugins/format/post.cpp new file mode 100644 index 0000000..92d8cd1 --- /dev/null +++ b/openbr/plugins/format/post.cpp @@ -0,0 +1,90 @@ +#include +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Handle POST requests + * \author Josh Klontz \cite jklontz + */ +class postFormat : public Format +{ + Q_OBJECT + + Template read() const + { + Template t(file); + + // Read from socket + QTcpSocket *socket = new QTcpSocket(); + socket->setSocketDescriptor(file.get("socketDescriptor")); + socket->write("HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + "Hello World!\r\n"); + socket->waitForBytesWritten(); + socket->waitForReadyRead(); + QByteArray data = socket->readAll(); + socket->close(); + delete socket; + + qDebug() << data; + + // Parse data + http_parser_settings settings; + settings.on_body = bodyCallback; + settings.on_headers_complete = NULL; + settings.on_header_field = NULL; + settings.on_header_value = NULL; + settings.on_message_begin = NULL; + settings.on_message_complete = NULL; + settings.on_status_complete = NULL; + settings.on_url = NULL; + + { + QByteArray body; + http_parser parser; + http_parser_init(&parser, HTTP_REQUEST); + parser.data = &body; + http_parser_execute(&parser, &settings, data.data(), data.size()); + data = body; + } + + data.prepend("HTTP/1.1 200 OK"); + QByteArray body; + { // Image data is two layers deep + http_parser parser; + http_parser_init(&parser, HTTP_BOTH); + parser.data = &body; + http_parser_execute(&parser, &settings, data.data(), data.size()); + } + + t.append(imdecode(Mat(1, body.size(), CV_8UC1, body.data()), 1)); + return t; + } + + void write(const Template &t) const + { + (void) t; + qFatal("Not supported!"); + } + + static int bodyCallback(http_parser *parser, const char *at, size_t length) + { + QByteArray *byteArray = (QByteArray*)parser->data; + *byteArray = QByteArray(at, length); + return 0; + } +}; + +BR_REGISTER(Format, postFormat) + +} // namespace br + +#include "format/post.moc" diff --git a/openbr/plugins/format/raw.cpp b/openbr/plugins/format/raw.cpp index 3cf868f..431c6c1 100644 --- a/openbr/plugins/format/raw.cpp +++ b/openbr/plugins/format/raw.cpp @@ -67,4 +67,4 @@ BR_REGISTER(Format, rawFormat) } // namespace br -#include "raw.moc" +#include "format/raw.moc" diff --git a/openbr/plugins/format/scores.cpp b/openbr/plugins/format/scores.cpp index 453dffa..495ad2b 100644 --- a/openbr/plugins/format/scores.cpp +++ b/openbr/plugins/format/scores.cpp @@ -62,4 +62,4 @@ BR_REGISTER(Format, scoresFormat) } // namespace br -#include "scores.moc" +#include "format/scores.moc" diff --git a/openbr/plugins/format/url.cpp b/openbr/plugins/format/url.cpp new file mode 100644 index 0000000..54f60a0 --- /dev/null +++ b/openbr/plugins/format/url.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads image files from the web. + * \author Josh Klontz \cite jklontz + */ +class urlFormat : public Format +{ + Q_OBJECT + + Template read() const + { + Template t; + + QNetworkAccessManager networkAccessManager; + QNetworkRequest request(QString(file.name).remove(".url")); + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + QNetworkReply *reply = networkAccessManager.get(request); + + while (!reply->isFinished()) QCoreApplication::processEvents(); + if (reply->error()) qWarning("%s (%s)", qPrintable(reply->errorString()), qPrintable(QString::number(reply->error()))); + + QByteArray data = reply->readAll(); + delete reply; + + Mat m = imdecode(Mat(1, data.size(), CV_8UC1, data.data()), 1); + if (m.data) t.append(m); + + return t; + } + + void write(const Template &t) const + { + (void) t; + qFatal("Not supported."); + } +}; + +BR_REGISTER(Format, urlFormat) + +} // namespace br + +#include "format/url.moc" diff --git a/openbr/plugins/format/video.cpp b/openbr/plugins/format/video.cpp index 0a1eaf8..78ed5c8 100644 --- a/openbr/plugins/format/video.cpp +++ b/openbr/plugins/format/video.cpp @@ -144,4 +144,4 @@ BR_REGISTER(Format, DefaultFormat) } // namespace br -#include "video.moc" +#include "format/video.moc" diff --git a/openbr/plugins/format/xml.cpp b/openbr/plugins/format/xml.cpp index 8cf3dc2..c9ca39f 100644 --- a/openbr/plugins/format/xml.cpp +++ b/openbr/plugins/format/xml.cpp @@ -99,4 +99,4 @@ BR_REGISTER(Format, xmlFormat) } // namespace br -#include "xml.moc" +#include "format/xml.moc" diff --git a/openbr/plugins/gallery.cpp b/openbr/plugins/gallery.cpp deleted file mode 100644 index 5036aec..0000000 --- a/openbr/plugins/gallery.cpp +++ /dev/null @@ -1,2075 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright 2012 The MITRE Corporation * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include -#include -#ifndef BR_EMBEDDED -#include -#include -#include -#include -#include -#include -#include -#endif // BR_EMBEDDED -#include -#include "openbr_internal.h" - -#include "openbr/universal_template.h" -#include "openbr/core/bee.h" -#include "openbr/core/common.h" -#include "openbr/core/opencvutils.h" -#include "openbr/core/qtutils.h" - -#include - -#ifdef CVMATIO -#include "MatlabIO.hpp" -#include "MatlabIOContainer.hpp" -#endif - -#ifdef _WIN32 -#include -#include -#endif // _WIN32 - -namespace br -{ - -/*! - * \ingroup galleries - * \brief Weka ARFF file format. - * \author Josh Klontz \cite jklontz - * http://weka.wikispaces.com/ARFF+%28stable+version%29 - */ -class arffGallery : public Gallery -{ - Q_OBJECT - QFile arffFile; - - TemplateList readBlock(bool *done) - { - (void) done; - qFatal("Not implemented."); - return TemplateList(); - } - - void write(const Template &t) - { - if (!arffFile.isOpen()) { - arffFile.setFileName(file.name); - arffFile.open(QFile::WriteOnly); - arffFile.write("% OpenBR templates\n" - "@RELATION OpenBR\n" - "\n"); - - const int dimensions = t.m().rows * t.m().cols; - for (int i=0; i("Label") + "'\n")); - } - - void init() - { - // - } -}; - -BR_REGISTER(Gallery, arffGallery) - -class BinaryGallery : public Gallery -{ - Q_OBJECT - - void init() - { - const QString baseName = file.baseName(); - - if (baseName == "stdin") { -#ifdef _WIN32 - if(_setmode(_fileno(stdin), _O_BINARY) == -1) - qFatal("Failed to set stdin to binary mode!"); -#endif // _WIN32 - - gallery.open(stdin, QFile::ReadOnly); - } else if (baseName == "stdout") { -#ifdef _WIN32 - if(_setmode(_fileno(stdout), _O_BINARY) == -1) - qFatal("Failed to set stdout to binary mode!"); -#endif // _WIN32 - - gallery.open(stdout, QFile::WriteOnly); - } else if (baseName == "stderr") { -#ifdef _WIN32 - if(_setmode(_fileno(stderr), _O_BINARY) == -1) - qFatal("Failed to set stderr to binary mode!"); -#endif // _WIN32 - - gallery.open(stderr, QFile::WriteOnly); - } else { - // Defer opening the file, in the general case we don't know if we - // need read or write mode yet - return; - } - stream.setDevice(&gallery); - } - - void readOpen() - { - if (!gallery.isOpen()) { - gallery.setFileName(file); - if (!gallery.exists()) - qFatal("File %s does not exist", qPrintable(gallery.fileName())); - - QFile::OpenMode mode = QFile::ReadOnly; - if (!gallery.open(mode)) - qFatal("Can't open gallery: %s for reading", qPrintable(gallery.fileName())); - stream.setDevice(&gallery); - } - } - - void writeOpen() - { - if (!gallery.isOpen()) { - gallery.setFileName(file); - - // Do we remove the pre-existing gallery? - if (file.get("remove")) - gallery.remove(); - QtUtils::touchDir(gallery); - QFile::OpenMode mode = QFile::WriteOnly; - - // Do we append? - if (file.get("append")) - mode |= QFile::Append; - - if (!gallery.open(mode)) - qFatal("Can't open gallery: %s for writing", qPrintable(gallery.fileName())); - stream.setDevice(&gallery); - } - } - - TemplateList readBlock(bool *done) - { - readOpen(); - if (gallery.atEnd()) - gallery.seek(0); - - TemplateList templates; - while ((templates.size() < readBlockSize) && !gallery.atEnd()) { - const Template t = readTemplate(); - if (!t.isEmpty() || !t.file.isNull()) { - templates.append(t); - templates.last().file.set("progress", position()); - } - - // Special case for pipes where we want to process data as soon as it is available - if (gallery.isSequential()) - break; - } - - *done = gallery.atEnd(); - return templates; - } - - void write(const Template &t) - { - writeOpen(); - writeTemplate(t); - if (gallery.isSequential()) - gallery.flush(); - } - -protected: - QFile gallery; - QDataStream stream; - - qint64 totalSize() - { - readOpen(); - return gallery.size(); - } - - qint64 position() - { - return gallery.pos(); - } - - virtual Template readTemplate() = 0; - virtual void writeTemplate(const Template &t) = 0; -}; - -/*! - * \ingroup galleries - * \brief A binary gallery. - * - * Designed to be a literal translation of templates to disk. - * Compatible with TemplateList::fromBuffer. - * \author Josh Klontz \cite jklontz - */ -class galGallery : public BinaryGallery -{ - Q_OBJECT - - Template readTemplate() - { - Template t; - stream >> t; - return t; - } - - void writeTemplate(const Template &t) - { - if (t.isEmpty() && t.file.isNull()) - return; - else if (t.file.fte) - stream << Template(t.file); // only write metadata for failure to enroll - else - stream << t; - } -}; - -BR_REGISTER(Gallery, galGallery) - -/*! - * \ingroup galleries - * \brief A contiguous array of br_universal_template. - * \author Josh Klontz \cite jklontz - */ -class utGallery : public BinaryGallery -{ - Q_OBJECT - - Template readTemplate() - { - Template t; - br_universal_template ut; - if (gallery.read((char*)&ut, sizeof(br_universal_template)) == sizeof(br_universal_template)) { - QByteArray data(ut.urlSize + ut.fvSize, Qt::Uninitialized); - char *dst = data.data(); - qint64 bytesNeeded = ut.urlSize + ut.fvSize; - while (bytesNeeded > 0) { - qint64 bytesRead = gallery.read(dst, bytesNeeded); - if (bytesRead <= 0) { - qDebug() << gallery.errorString(); - qFatal("Unexepected EOF while reading universal template data, needed: %d more of: %d bytes.", int(bytesNeeded), int(ut.urlSize + ut.fvSize)); - } - bytesNeeded -= bytesRead; - dst += bytesRead; - } - - t.file.set("ImageID", QVariant(QByteArray((const char*)ut.imageID, 16).toHex())); - t.file.set("AlgorithmID", ut.algorithmID); - t.file.set("URL", QString(data.data())); - char *dataStart = data.data() + ut.urlSize; - uint32_t dataSize = ut.fvSize; - if ((ut.algorithmID <= -1) && (ut.algorithmID >= -3)) { - t.file.set("FrontalFace", QRectF(ut.x, ut.y, ut.width, ut.height)); - uint32_t *rightEyeX = reinterpret_cast(dataStart); - dataStart += sizeof(uint32_t); - uint32_t *rightEyeY = reinterpret_cast(dataStart); - dataStart += sizeof(uint32_t); - uint32_t *leftEyeX = reinterpret_cast(dataStart); - dataStart += sizeof(uint32_t); - uint32_t *leftEyeY = reinterpret_cast(dataStart); - dataStart += sizeof(uint32_t); - dataSize -= sizeof(uint32_t)*4; - t.file.set("First_Eye", QPointF(*rightEyeX, *rightEyeY)); - t.file.set("Second_Eye", QPointF(*leftEyeX, *leftEyeY)); - } else { - t.file.set("X", ut.x); - t.file.set("Y", ut.y); - t.file.set("Width", ut.width); - t.file.set("Height", ut.height); - } - t.file.set("Label", ut.label); - t.append(cv::Mat(1, dataSize, CV_8UC1, dataStart).clone() /* We don't want a shallow copy! */); - } else { - if (!gallery.atEnd()) - qFatal("Failed to read universal template header!"); - } - return t; - } - - void writeTemplate(const Template &t) - { - const QByteArray imageID = QByteArray::fromHex(t.file.get("ImageID", QByteArray(32, '0'))); - if (imageID.size() != 16) - qFatal("Expected 16-byte ImageID, got: %d bytes.", imageID.size()); - - const int32_t algorithmID = (t.isEmpty() || t.file.fte) ? 0 : t.file.get("AlgorithmID"); - const QByteArray url = t.file.get("URL", t.file.name).toLatin1(); - - uint32_t x = 0, y = 0, width = 0, height = 0; - QByteArray header; - if ((algorithmID <= -1) && (algorithmID >= -3)) { - const QRectF frontalFace = t.file.get("FrontalFace"); - x = frontalFace.x(); - y = frontalFace.y(); - width = frontalFace.width(); - height = frontalFace.height(); - - const QPointF firstEye = t.file.get("First_Eye"); - const QPointF secondEye = t.file.get("Second_Eye"); - const uint32_t rightEyeX = firstEye.x(); - const uint32_t rightEyeY = firstEye.y(); - const uint32_t leftEyeX = secondEye.x(); - const uint32_t leftEyeY = secondEye.y(); - - header.append((const char*)&rightEyeX, sizeof(uint32_t)); - header.append((const char*)&rightEyeY, sizeof(uint32_t)); - header.append((const char*)&leftEyeX , sizeof(uint32_t)); - header.append((const char*)&leftEyeY , sizeof(uint32_t)); - } else { - x = t.file.get("X", 0); - y = t.file.get("Y", 0); - width = t.file.get("Width", 0); - height = t.file.get("Height", 0); - } - const uint32_t label = t.file.get("Label", 0); - - gallery.write(imageID); - gallery.write((const char*) &algorithmID, sizeof(int32_t)); - gallery.write((const char*) &x , sizeof(uint32_t)); - gallery.write((const char*) &y , sizeof(uint32_t)); - gallery.write((const char*) &width , sizeof(uint32_t)); - gallery.write((const char*) &height , sizeof(uint32_t)); - gallery.write((const char*) &label , sizeof(uint32_t)); - - const uint32_t urlSize = url.size() + 1; - gallery.write((const char*) &urlSize, sizeof(uint32_t)); - - const uint32_t signatureSize = (algorithmID == 0) ? 0 : t.m().rows * t.m().cols * t.m().elemSize(); - const uint32_t fvSize = header.size() + signatureSize; - gallery.write((const char*) &fvSize, sizeof(uint32_t)); - - gallery.write((const char*) url.data(), urlSize); - if (algorithmID != 0) { - gallery.write(header); - gallery.write((const char*) t.m().data, signatureSize); - } - } -}; - -BR_REGISTER(Gallery, utGallery) - -/*! - * \ingroup galleries - * \brief Newline-separated URLs. - * \author Josh Klontz \cite jklontz - */ -class urlGallery : public BinaryGallery -{ - Q_OBJECT - - Template readTemplate() - { - Template t; - const QString url = QString::fromLocal8Bit(gallery.readLine()).simplified(); - if (!url.isEmpty()) - t.file.set("URL", url); - return t; - } - - void writeTemplate(const Template &t) - { - const QString url = t.file.get("URL", t.file.name); - if (!url.isEmpty()) { - gallery.write(qPrintable(url)); - gallery.write("\n"); - } - } -}; - -BR_REGISTER(Gallery, urlGallery) - -/*! - * \ingroup galleries - * \brief Newline-separated JSON objects. - * \author Josh Klontz \cite jklontz - */ -class jsonGallery : public BinaryGallery -{ - Q_OBJECT - - Template readTemplate() - { - QJsonParseError error; - const QByteArray line = gallery.readLine().simplified(); - if (line.isEmpty()) - return Template(); - File file = QJsonDocument::fromJson(line, &error).object().toVariantMap(); - if (error.error != QJsonParseError::NoError) { - qWarning("Couldn't parse: %s\n", line.data()); - qFatal("%s\n", qPrintable(error.errorString())); - } - return file; - } - - void writeTemplate(const Template &t) - { - const QByteArray json = QJsonDocument(QJsonObject::fromVariantMap(t.file.localMetadata())).toJson().replace('\n', ""); - if (!json.isEmpty()) { - gallery.write(json); - gallery.write("\n"); - } - } -}; - -BR_REGISTER(Gallery, jsonGallery) - -/*! - * \ingroup galleries - * \brief Reads/writes templates to/from folders. - * \author Josh Klontz \cite jklontz - * \param regexp An optional regular expression to match against the files extension. - */ -class EmptyGallery : public Gallery -{ - Q_OBJECT - Q_PROPERTY(QString regexp READ get_regexp WRITE set_regexp RESET reset_regexp STORED false) - BR_PROPERTY(QString, regexp, QString()) - - qint64 gallerySize; - - void init() - { - QDir dir(file.name); - QtUtils::touchDir(dir); - gallerySize = dir.count(); - } - - TemplateList readBlock(bool *done) - { - TemplateList templates; - *done = true; - - // Enrolling a null file is used as an idiom to initialize an algorithm - if (file.isNull()) return templates; - - // Add immediate subfolders - QDir dir(file); - QList< QFuture > futures; - foreach (const QString &folder, QtUtils::naturalSort(dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))) { - const QDir subdir = dir.absoluteFilePath(folder); - futures.append(QtConcurrent::run(&EmptyGallery::getTemplates, subdir)); - } - foreach (const QFuture &future, futures) - templates.append(future.result()); - - // Add root folder - foreach (const QString &fileName, QtUtils::getFiles(file.name, false)) - templates.append(File(fileName, dir.dirName())); - - if (!regexp.isEmpty()) { - QRegExp re(regexp); - re.setPatternSyntax(QRegExp::Wildcard); - for (int i=templates.size()-1; i>=0; i--) { - if (!re.exactMatch(templates[i].file.fileName())) { - templates.removeAt(i); - } - } - } - - for (int i = 0; i < templates.size(); i++) templates[i].file.set("progress", i); - - return templates; - } - - void write(const Template &t) - { - static QMutex diskLock; - - // Enrolling a null file is used as an idiom to initialize an algorithm - if (file.name.isEmpty()) return; - - const QString newFormat = file.get("newFormat",QString()); - QString destination = file.name + "/" + (file.getBool("preservePath") ? t.file.path()+"/" : QString()); - destination += (newFormat.isEmpty() ? t.file.fileName() : t.file.baseName()+newFormat); - - QMutexLocker diskLocker(&diskLock); // Windows prefers to crash when writing to disk in parallel - if (t.isNull()) { - QtUtils::copyFile(t.file.resolved(), destination); - } else { - QScopedPointer format(Factory::make(destination)); - format->write(t); - } - } - - qint64 totalSize() - { - return gallerySize; - } - - static TemplateList getTemplates(const QDir &dir) - { - const QStringList files = QtUtils::getFiles(dir, true); - TemplateList templates; templates.reserve(files.size()); - foreach (const QString &file, files) - templates.append(File(file, dir.dirName())); - return templates; - } -}; - -BR_REGISTER(Gallery, EmptyGallery) - -/*! - * \ingroup galleries - * \brief Crawl a root location for image files. - * \author Josh Klontz \cite jklontz - */ -class crawlGallery : public Gallery -{ - Q_OBJECT - Q_PROPERTY(bool autoRoot READ get_autoRoot WRITE set_autoRoot RESET reset_autoRoot STORED false) - Q_PROPERTY(int depth READ get_depth WRITE set_depth RESET reset_depth STORED false) - Q_PROPERTY(bool depthFirst READ get_depthFirst WRITE set_depthFirst RESET reset_depthFirst STORED false) - Q_PROPERTY(int images READ get_images WRITE set_images RESET reset_images STORED false) - Q_PROPERTY(bool json READ get_json WRITE set_json RESET reset_json STORED false) - Q_PROPERTY(int timeLimit READ get_timeLimit WRITE set_timeLimit RESET reset_timeLimit STORED false) - BR_PROPERTY(bool, autoRoot, false) - BR_PROPERTY(int, depth, INT_MAX) - BR_PROPERTY(bool, depthFirst, false) - BR_PROPERTY(int, images, INT_MAX) - BR_PROPERTY(bool, json, false) - BR_PROPERTY(int, timeLimit, INT_MAX) - - QTime elapsed; - TemplateList templates; - - void crawl(QFileInfo url, int currentDepth = 0) - { - if ((templates.size() >= images) || (currentDepth >= depth) || (elapsed.elapsed()/1000 >= timeLimit)) - return; - - if (url.filePath().startsWith("file://")) - url = QFileInfo(url.filePath().mid(7)); - - if (url.isDir()) { - const QDir dir(url.absoluteFilePath()); - const QFileInfoList files = dir.entryInfoList(QDir::Files); - const QFileInfoList subdirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QFileInfo &first, depthFirst ? subdirs : files) - crawl(first, currentDepth + 1); - foreach (const QFileInfo &second, depthFirst ? files : subdirs) - crawl(second, currentDepth + 1); - } else if (url.isFile()) { - const QString suffix = url.suffix(); - if ((suffix == "bmp") || (suffix == "jpg") || (suffix == "jpeg") || (suffix == "png") || (suffix == "tiff")) { - File f; - if (json) f.set("URL", "file://"+url.canonicalFilePath()); - else f.name = "file://"+url.canonicalFilePath(); - templates.append(f); - } - } - } - - void init() - { - elapsed.start(); - const QString root = file.name.mid(0, file.name.size()-6); // Remove .crawl suffix"; - if (!root.isEmpty()) { - crawl(root); - } else { - if (autoRoot) { - foreach (const QString &path, QStandardPaths::standardLocations(QStandardPaths::HomeLocation)) - crawl(path); - } else { - QFile file; - file.open(stdin, QFile::ReadOnly); - while (!file.atEnd()) { - const QString url = QString::fromLocal8Bit(file.readLine()).simplified(); - if (!url.isEmpty()) - crawl(url); - } - } - } - } - - TemplateList readBlock(bool *done) - { - *done = true; - return templates; - } - - void write(const Template &) - { - qFatal("Not supported"); - } -}; - -BR_REGISTER(Gallery, crawlGallery) - -/*! - * \ingroup galleries - * \brief Treats the gallery as a br::Format. - * \author Josh Klontz \cite jklontz - */ -class DefaultGallery : public Gallery -{ - Q_OBJECT - - TemplateList readBlock(bool *done) - { - *done = true; - return TemplateList() << file; - } - - void write(const Template &t) - { - QScopedPointer format(Factory::make(file)); - format->write(t); - } - - qint64 totalSize() - { - return 1; - } -}; - -BR_REGISTER(Gallery, DefaultGallery) - -/*! - * \ingroup galleries - * \brief Combine all templates into one large matrix and process it as a br::Format - * \author Josh Klontz \cite jklontz - */ -class matrixGallery : public Gallery -{ - Q_OBJECT - Q_PROPERTY(const QString extension READ get_extension WRITE set_extension RESET reset_extension STORED false) - BR_PROPERTY(QString, extension, "mtx") - - TemplateList templates; - - ~matrixGallery() - { - if (templates.isEmpty()) - return; - - QScopedPointer format(Factory::make(getFormat())); - format->write(Template(file, OpenCVUtils::toMat(templates.data()))); - } - - File getFormat() const - { - return file.name.left(file.name.size() - file.suffix().size()) + extension; - } - - TemplateList readBlock(bool *done) - { - *done = true; - return TemplateList() << getFormat(); - } - - void write(const Template &t) - { - templates.append(t); - } -}; - -BR_REGISTER(Gallery, matrixGallery) - -/*! - * \ingroup initializers - * \brief Initialization support for memGallery. - * \author Josh Klontz \cite jklontz - */ -class MemoryGalleries : public Initializer -{ - Q_OBJECT - - void initialize() const {} - - void finalize() const - { - galleries.clear(); - } - -public: - static QHash galleries; /*!< TODO */ -}; - -QHash MemoryGalleries::galleries; - -BR_REGISTER(Initializer, MemoryGalleries) - -/*! - * \ingroup galleries - * \brief A gallery held in memory. - * \author Josh Klontz \cite jklontz - */ -class memGallery : public Gallery -{ - Q_OBJECT - int block; - qint64 gallerySize; - - void init() - { - block = 0; - File galleryFile = file.name.mid(0, file.name.size()-4); - if ((galleryFile.suffix() == "gal") && galleryFile.exists() && !MemoryGalleries::galleries.contains(file)) { - QSharedPointer gallery(Factory::make(galleryFile)); - MemoryGalleries::galleries[file] = gallery->read(); - gallerySize = MemoryGalleries::galleries[file].size(); - } - } - - TemplateList readBlock(bool *done) - { - TemplateList templates = MemoryGalleries::galleries[file].mid(block*readBlockSize, readBlockSize); - for (qint64 i = 0; i < templates.size();i++) { - templates[i].file.set("progress", i + block * readBlockSize); - } - - *done = (templates.size() < readBlockSize); - block = *done ? 0 : block+1; - return templates; - } - - void write(const Template &t) - { - MemoryGalleries::galleries[file].append(t); - } - - qint64 totalSize() - { - return gallerySize; - } - - qint64 position() - { - return block * readBlockSize; - } - -}; - -BR_REGISTER(Gallery, memGallery) - -FileList FileList::fromGallery(const File &rFile, bool cache) -{ - File file = rFile; - file.remove("append"); - - File targetMeta = file; - targetMeta.name = targetMeta.path() + targetMeta.baseName() + "_meta" + targetMeta.hash() + ".mem"; - - FileList fileData; - - // Did we already read the data? - if (MemoryGalleries::galleries.contains(targetMeta)) - { - return MemoryGalleries::galleries[targetMeta].files(); - } - - TemplateList templates; - // OK we read the data in some form, does the gallery type containing matrices? - if ((QStringList() << "gal" << "mem" << "template" << "ut").contains(file.suffix())) { - // Retrieve it block by block, dropping matrices from read templates. - QScopedPointer gallery(Gallery::make(file)); - gallery->set_readBlockSize(10); - bool done = false; - while (!done) - { - TemplateList tList = gallery->readBlock(&done); - for (int i=0; i < tList.size();i++) - { - tList[i].clear(); - templates.append(tList[i].file); - } - } - } - else { - // this is a gallery format that doesn't include matrices, so we can just read it - QScopedPointer gallery(Gallery::make(file)); - templates= gallery->read(); - } - - if (cache) - { - QScopedPointer memOutput(Gallery::make(targetMeta)); - memOutput->writeBlock(templates); - } - fileData = templates.files(); - return fileData; -} - -/*! - * \ingroup galleries - * \brief Treats each line as a file. - * \author Josh Klontz \cite jklontz - * - * Columns should be comma separated with first row containing headers. - * The first column in the file should be the path to the file to enroll. - * Other columns will be treated as file metadata. - * - * \see txtGallery - */ -class csvGallery : public FileGallery -{ - Q_OBJECT - Q_PROPERTY(int fileIndex READ get_fileIndex WRITE set_fileIndex RESET reset_fileIndex) - BR_PROPERTY(int, fileIndex, 0) - - FileList files; - QStringList headers; - - ~csvGallery() - { - f.close(); - - if (files.isEmpty()) return; - - QMap samples; - foreach (const File &file, files) - foreach (const QString &key, file.localKeys()) - if (!samples.contains(key)) - samples.insert(key, file.value(key)); - - // Don't create columns in the CSV for these special fields - samples.remove("Points"); - samples.remove("Rects"); - - QStringList lines; - lines.reserve(files.size()+1); - - QMap columnCounts; - - { // Make header - QStringList words; - words.append("File"); - foreach (const QString &key, samples.keys()) { - int count = 0; - words.append(getCSVElement(key, samples[key], true, count)); - columnCounts.insert(key, count); - } - lines.append(words.join(",")); - } - - // Make table - foreach (const File &file, files) { - QStringList words; - words.append(file.name); - foreach (const QString &key, samples.keys()) { - int count = columnCounts[key]; - words.append(getCSVElement(key, file.value(key), false, count)); - } - lines.append(words.join(",")); - } - - QtUtils::writeFile(file, lines); - } - - TemplateList readBlock(bool *done) - { - readOpen(); - *done = false; - TemplateList templates; - if (!file.exists()) { - *done = true; - return templates; - } - QRegExp regexp("\\s*,\\s*"); - - if (f.pos() == 0) - { - // read a line - QByteArray lineBytes = f.readLine(); - QString line = QString::fromLocal8Bit(lineBytes).trimmed(); - headers = line.split(regexp); - } - - for (qint64 i = 0; i < this->readBlockSize && !f.atEnd(); i++){ - QByteArray lineBytes = f.readLine(); - QString line = QString::fromLocal8Bit(lineBytes).trimmed(); - - QStringList words = line.split(regexp); - if (words.size() != headers.size()) continue; - File fi; - for (int j=0; j()) { - if (header) return key; - else { - if (columnCount != 1) - qFatal("Inconsistent datatype for key %s, csv file cannot be generated", qPrintable(key)); - return value.value(); - } - } else if (value.canConvert()) { - const QPointF point = value.value(); - if (header) { - columnCount = 2; - return key+"_X,"+key+"_Y"; - } - else { - if (columnCount != 2) - qFatal("Inconsistent datatype for key %s, csv file cannot be generated", qPrintable(key)); - - return QString::number(point.x())+","+QString::number(point.y()); - } - } else if (value.canConvert()) { - const QRectF rect = value.value(); - if (header) { - columnCount = 4; - return key+"_X,"+key+"_Y,"+key+"_Width,"+key+"_Height"; - } - else { - if (columnCount != 4) - qFatal("Inconsistent datatype for key %s, csv file cannot be generated", qPrintable(key)); - - return QString::number(rect.x())+","+QString::number(rect.y())+","+QString::number(rect.width())+","+QString::number(rect.height()); - } - } else { - if (header) return key; - else { - QString output = QString::number(std::numeric_limits::quiet_NaN()); - for (int i = 1; i < columnCount; i++) - output += "," + QString::number(std::numeric_limits::quiet_NaN()); - return output; - } - } - } -}; - -BR_REGISTER(Gallery, csvGallery) - -/*! - * \ingroup galleries - * \brief Treats each line as a file. - * \author Josh Klontz \cite jklontz - * - * The entire line is treated as the file path. An optional label may be specified using a space ' ' separator: - * -\verbatim - - -... - -\endverbatim - * or -\verbatim -