diff --git a/openbr/plugins/classification/adaboost.cpp b/openbr/plugins/classification/adaboost.cpp new file mode 100644 index 0000000..04e094f --- /dev/null +++ b/openbr/plugins/classification/adaboost.cpp @@ -0,0 +1,134 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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/ebif.cpp b/openbr/plugins/classification/ebif.cpp index 718a3b9..19d79ba 100644 --- a/openbr/plugins/ebif.cpp +++ b/openbr/plugins/classification/ebif.cpp @@ -1,8 +1,24 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 "openbr_internal.h" -#include "openbr/core/common.h" -#include "openbr/core/opencvutils.h" +#include +#include +#include using namespace cv; @@ -128,4 +144,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..b717533 100644 --- a/openbr/plugins/tree.cpp +++ b/openbr/plugins/classification/forest.cpp @@ -1,7 +1,22 @@ -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 -using namespace std; using namespace cv; namespace br @@ -250,113 +265,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 dfe4ba8..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 { @@ -652,46 +652,6 @@ class SparseLDATransform : public Transform BR_REGISTER(Transform, SparseLDATransform) -/*! - * \ingroup distances - * \brief L1 distance computed using eigen. - * \author Josh Klontz \cite jklontz - */ -class L1Distance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const cv::Mat &a, const cv::Mat &b) const - { - const int size = a.rows * a.cols; - Eigen::Map aMap((float*)a.data, size); - Eigen::Map bMap((float*)b.data, size); - return (aMap-bMap).cwiseAbs().sum(); - } -}; - -BR_REGISTER(Distance, L1Distance) - -/*! - * \ingroup distances - * \brief L2 distance computed using eigen. - * \author Josh Klontz \cite jklontz - */ -class L2Distance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const cv::Mat &a, const cv::Mat &b) const - { - const int size = a.rows * a.cols; - Eigen::Map aMap((float*)a.data, size); - Eigen::Map bMap((float*)b.data, size); - return (aMap-bMap).squaredNorm(); - } -}; - -BR_REGISTER(Distance, L2Distance) - } // 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..4295004 100644 --- a/openbr/plugins/nn.cpp +++ b/openbr/plugins/classification/mlp.cpp @@ -1,51 +1,29 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 "openbr_internal.h" -#include "openbr/core/qtutils.h" -#include "openbr/core/opencvutils.h" -#include "openbr/core/eigenutils.h" -#include -#include +#include +#include -using namespace std; 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 +102,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 +115,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..79594b6 --- /dev/null +++ b/openbr/plugins/classification/turk.cpp @@ -0,0 +1,72 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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 new file mode 100644 index 0000000..a077a0a --- /dev/null +++ b/openbr/plugins/cluster/collectnn.cpp @@ -0,0 +1,58 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Collect nearest neighbors and append them to metadata. + * \author Charles Otto \cite caotto + */ +class CollectNNTransform : public UntrainableMetaTransform +{ + Q_OBJECT + + Q_PROPERTY(int keep READ get_keep WRITE set_keep RESET reset_keep STORED false) + BR_PROPERTY(int, keep, 20) + + void project(const Template &src, Template &dst) const + { + dst.file = src.file; + dst.clear(); + dst.m() = cv::Mat(); + Neighbors neighbors; + for (int i=0; i < src.m().cols;i++) { + // skip self compares + if (i == src.file.get("FrameNumber")) + continue; + neighbors.append(Neighbor(i, src.m().at(0,i))); + } + int actuallyKeep = std::min(keep, neighbors.size()); + std::partial_sort(neighbors.begin(), neighbors.begin()+actuallyKeep, neighbors.end(), compareNeighbors); + + Neighbors selected = neighbors.mid(0, actuallyKeep); + dst.file.set("neighbors", QVariant::fromValue(selected)); + } +}; + +BR_REGISTER(Transform, CollectNNTransform) + +} // namespace br + +#include "cluster/collectnn.moc" diff --git a/openbr/plugins/cluster/kmeans.cpp b/openbr/plugins/cluster/kmeans.cpp new file mode 100644 index 0000000..cea7381 --- /dev/null +++ b/openbr/plugins/cluster/kmeans.cpp @@ -0,0 +1,81 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV kmeans and flann. + * \author Josh Klontz \cite jklontz + */ +class KMeansTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(int kTrain READ get_kTrain WRITE set_kTrain RESET reset_kTrain STORED false) + Q_PROPERTY(int kSearch READ get_kSearch WRITE set_kSearch RESET reset_kSearch STORED false) + BR_PROPERTY(int, kTrain, 256) + BR_PROPERTY(int, kSearch, 1) + + Mat centers; + mutable QScopedPointer index; + mutable QMutex mutex; + + void reindex() + { + index.reset(new flann::Index(centers, flann::LinearIndexParams())); + } + + void train(const TemplateList &data) + { + Mat bestLabels; + const double compactness = kmeans(OpenCVUtils::toMatByRow(data.data()), kTrain, bestLabels, TermCriteria(TermCriteria::MAX_ITER, 10, 0), 3, KMEANS_PP_CENTERS, centers); + qDebug("KMeans compactness = %f", compactness); + reindex(); + } + + void project(const Template &src, Template &dst) const + { + QMutexLocker locker(&mutex); + Mat dists, indicies; + index->knnSearch(src, indicies, dists, kSearch); + dst = indicies.reshape(1, 1); + } + + void load(QDataStream &stream) + { + stream >> centers; + reindex(); + } + + void store(QDataStream &stream) const + { + stream << centers; + } +}; + +BR_REGISTER(Transform, KMeansTransform) + +} // namespace br + +#include "cluster/kmeans.moc" diff --git a/openbr/plugins/cluster.cpp b/openbr/plugins/cluster/knn.cpp index ab95f27..8284c78 100644 --- a/openbr/plugins/cluster.cpp +++ b/openbr/plugins/cluster/knn.cpp @@ -14,12 +14,8 @@ * limitations under the License. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include - -#include "openbr_internal.h" -#include "openbr/core/common.h" -#include "openbr/core/opencvutils.h" -#include +#include +#include using namespace cv; @@ -28,58 +24,6 @@ namespace br /*! * \ingroup transforms - * \brief Wraps OpenCV kmeans and flann. - * \author Josh Klontz \cite jklontz - */ -class KMeansTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(int kTrain READ get_kTrain WRITE set_kTrain RESET reset_kTrain STORED false) - Q_PROPERTY(int kSearch READ get_kSearch WRITE set_kSearch RESET reset_kSearch STORED false) - BR_PROPERTY(int, kTrain, 256) - BR_PROPERTY(int, kSearch, 1) - - Mat centers; - mutable QScopedPointer index; - mutable QMutex mutex; - - void reindex() - { - index.reset(new flann::Index(centers, flann::LinearIndexParams())); - } - - void train(const TemplateList &data) - { - Mat bestLabels; - const double compactness = kmeans(OpenCVUtils::toMatByRow(data.data()), kTrain, bestLabels, TermCriteria(TermCriteria::MAX_ITER, 10, 0), 3, KMEANS_PP_CENTERS, centers); - qDebug("KMeans compactness = %f", compactness); - reindex(); - } - - void project(const Template &src, Template &dst) const - { - QMutexLocker locker(&mutex); - Mat dists, indicies; - index->knnSearch(src, indicies, dists, kSearch); - dst = indicies.reshape(1, 1); - } - - void load(QDataStream &stream) - { - stream >> centers; - reindex(); - } - - void store(QDataStream &stream) const - { - stream << centers; - } -}; - -BR_REGISTER(Transform, KMeansTransform) - -/*! - * \ingroup transforms * \brief K nearest neighbors classifier. * \author Josh Klontz \cite jklontz */ @@ -151,148 +95,6 @@ class KNNTransform : public Transform BR_REGISTER(Transform, KNNTransform) -/*! - * \ingroup transforms - * \brief Chooses k random points to be centroids. - * \author Austin Blanton \cite imaus10 - * \see KMeansTransform - */ -class RandomCentroidsTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(int kTrain READ get_kTrain WRITE set_kTrain RESET reset_kTrain STORED false) - Q_PROPERTY(int kSearch READ get_kSearch WRITE set_kSearch RESET reset_kSearch STORED false) - BR_PROPERTY(int, kTrain, 256) - BR_PROPERTY(int, kSearch, 1) - - Mat centers; - mutable QScopedPointer index; - mutable QMutex mutex; - - void reindex() - { - index.reset(new flann::Index(centers, flann::LinearIndexParams())); - } - - void train(const TemplateList &data) - { - Mat flat = OpenCVUtils::toMatByRow(data.data()); - QList sample = Common::RandSample(kTrain, flat.rows, 0, true); - foreach (const int &idx, sample) - centers.push_back(flat.row(idx)); - reindex(); - } - - void project(const Template &src, Template &dst) const - { - QMutexLocker locker(&mutex); - Mat dists, indicies; - index->knnSearch(src, indicies, dists, kSearch); - dst = indicies.reshape(1, 1); - } - - void load(QDataStream &stream) - { - stream >> centers; - reindex(); - } - - void store(QDataStream &stream) const - { - stream << centers; - } -}; - -BR_REGISTER(Transform, RandomCentroidsTransform) - -class RegInitializer : public Initializer -{ - Q_OBJECT - - void initialize() const - { - qRegisterMetaType(); - } -}; -BR_REGISTER(Initializer, RegInitializer) - -class CollectNNTransform : public UntrainableMetaTransform -{ - Q_OBJECT - - Q_PROPERTY(int keep READ get_keep WRITE set_keep RESET reset_keep STORED false) - BR_PROPERTY(int, keep, 20) - - void project(const Template &src, Template &dst) const - { - dst.file = src.file; - dst.clear(); - dst.m() = cv::Mat(); - Neighbors neighbors; - for (int i=0; i < src.m().cols;i++) { - // skip self compares - if (i == src.file.get("FrameNumber")) - continue; - neighbors.append(Neighbor(i, src.m().at(0,i))); - } - int actuallyKeep = std::min(keep, neighbors.size()); - std::partial_sort(neighbors.begin(), neighbors.begin()+actuallyKeep, neighbors.end(), compareNeighbors); - - Neighbors selected = neighbors.mid(0, actuallyKeep); - dst.file.set("neighbors", QVariant::fromValue(selected)); - } -}; -BR_REGISTER(Transform, CollectNNTransform) - -class LogNNTransform : public TimeVaryingTransform -{ - Q_OBJECT - - Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) - BR_PROPERTY(QString, fileName, "") - - std::fstream fout; - - void projectUpdate(const Template &src, Template &dst) - { - dst = src; - - if (!dst.file.contains("neighbors")) { - fout << std::endl; - return; - } - - Neighbors neighbors = dst.file.get("neighbors"); - if (neighbors.isEmpty() ) { - fout << std::endl; - return; - } - - QString aLine; - aLine.append(QString::number(neighbors[0].first)+":"+QString::number(neighbors[0].second)); - for (int i=1; i < neighbors.size();i++) - aLine.append(","+QString::number(neighbors[i].first)+":"+QString::number(neighbors[i].second)); - - fout << qPrintable(aLine) << std::endl; - } - - void init() - { - if (!fileName.isEmpty()) - fout.open(qPrintable(fileName), std::ios_base::out); - } - - void finalize(TemplateList &output) - { - (void) output; - fout.close(); - } - -public: - LogNNTransform() : TimeVaryingTransform(false, false) {} -}; -BR_REGISTER(Transform, LogNNTransform) - } // namespace br -#include "cluster.moc" +#include "cluster/knn.moc" diff --git a/openbr/plugins/cluster/lognn.cpp b/openbr/plugins/cluster/lognn.cpp new file mode 100644 index 0000000..fc99f77 --- /dev/null +++ b/openbr/plugins/cluster/lognn.cpp @@ -0,0 +1,81 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Log nearest neighbors to specified file. + * \author Charles Otto \cite caotto + */ +class LogNNTransform : public TimeVaryingTransform +{ + Q_OBJECT + + Q_PROPERTY(QString fileName READ get_fileName WRITE set_fileName RESET reset_fileName STORED false) + BR_PROPERTY(QString, fileName, "") + + std::fstream fout; + + void projectUpdate(const Template &src, Template &dst) + { + dst = src; + + if (!dst.file.contains("neighbors")) { + fout << std::endl; + return; + } + + Neighbors neighbors = dst.file.get("neighbors"); + if (neighbors.isEmpty() ) { + fout << std::endl; + return; + } + + QString aLine; + aLine.append(QString::number(neighbors[0].first)+":"+QString::number(neighbors[0].second)); + for (int i=1; i < neighbors.size();i++) + aLine.append(","+QString::number(neighbors[i].first)+":"+QString::number(neighbors[i].second)); + + fout << qPrintable(aLine) << std::endl; + } + + void init() + { + if (!fileName.isEmpty()) + fout.open(qPrintable(fileName), std::ios_base::out); + } + + void finalize(TemplateList &output) + { + (void) output; + fout.close(); + } + +public: + LogNNTransform() : TimeVaryingTransform(false, false) {} +}; + +BR_REGISTER(Transform, LogNNTransform) + +} // namespace br + +#include "cluster/lognn.moc" diff --git a/openbr/plugins/cluster/randomcentroids.cpp b/openbr/plugins/cluster/randomcentroids.cpp new file mode 100644 index 0000000..b3a1a30 --- /dev/null +++ b/openbr/plugins/cluster/randomcentroids.cpp @@ -0,0 +1,84 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Chooses k random points to be centroids. + * \author Austin Blanton \cite imaus10 + * \see KMeansTransform + */ +class RandomCentroidsTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(int kTrain READ get_kTrain WRITE set_kTrain RESET reset_kTrain STORED false) + Q_PROPERTY(int kSearch READ get_kSearch WRITE set_kSearch RESET reset_kSearch STORED false) + BR_PROPERTY(int, kTrain, 256) + BR_PROPERTY(int, kSearch, 1) + + Mat centers; + mutable QScopedPointer index; + mutable QMutex mutex; + + void reindex() + { + index.reset(new flann::Index(centers, flann::LinearIndexParams())); + } + + void train(const TemplateList &data) + { + Mat flat = OpenCVUtils::toMatByRow(data.data()); + QList sample = Common::RandSample(kTrain, flat.rows, 0, true); + foreach (const int &idx, sample) + centers.push_back(flat.row(idx)); + reindex(); + } + + void project(const Template &src, Template &dst) const + { + QMutexLocker locker(&mutex); + Mat dists, indicies; + index->knnSearch(src, indicies, dists, kSearch); + dst = indicies.reshape(1, 1); + } + + void load(QDataStream &stream) + { + stream >> centers; + reindex(); + } + + void store(QDataStream &stream) const + { + stream << centers; + } +}; + +BR_REGISTER(Transform, RandomCentroidsTransform) + +} //namespace br + +#include "cluster/randomcentroids.moc" diff --git a/openbr/plugins/cmake/eigen3.cmake b/openbr/plugins/cmake/eigen3.cmake new file mode 100644 index 0000000..73646fa --- /dev/null +++ b/openbr/plugins/cmake/eigen3.cmake @@ -0,0 +1,16 @@ +set(BR_WITH_EIGEN3 ON CACHE BOOL "Build Eigen3 plugins") + +if(${BR_WITH_EIGEN3}) + find_package(Eigen3 REQUIRED) + 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/distance/L1.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/distance/L2.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/imgproc/revertaffine.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/imgproc/integralsampler.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/imgproc/recursiveintegralsampler.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/metadata/consolidatedetections.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/metadata/delaunay.cpp) + set(BR_EXCLUDED_PLUGINS ${BR_EXCLUDED_PLUGINS} plugins/metadata/procrustes.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 42c8de5..fa52d2b 100644 --- a/openbr/plugins/stasm4.cmake +++ b/openbr/plugins/cmake/stasm4.cmake @@ -1,8 +1,7 @@ -set(BR_WITH_STASM4 ON CACHE BOOL "Build with Stasm") +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/algorithms.cpp b/openbr/plugins/core/algorithms.cpp index 88ecaea..9d61dd1 100644 --- a/openbr/plugins/algorithms.cpp +++ b/openbr/plugins/core/algorithms.cpp @@ -14,7 +14,7 @@ * limitations under the License. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include "openbr_internal.h" +#include namespace br { @@ -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..05e255d --- /dev/null +++ b/openbr/plugins/core/attributealgorithms.cpp @@ -0,0 +1,153 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..ea43813 --- /dev/null +++ b/openbr/plugins/core/cache.cpp @@ -0,0 +1,95 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..018a89c --- /dev/null +++ b/openbr/plugins/core/contract.cpp @@ -0,0 +1,61 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..eb86143 100644 --- a/openbr/plugins/validate.cpp +++ b/openbr/plugins/core/crossvalidate.cpp @@ -1,8 +1,23 @@ -#include -#include -#include "openbr_internal.h" -#include "openbr/core/common.h" -#include +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#include namespace br { @@ -134,138 +149,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..553a5b2 --- /dev/null +++ b/openbr/plugins/core/discard.cpp @@ -0,0 +1,42 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..baf89da --- /dev/null +++ b/openbr/plugins/core/discardtemplates.cpp @@ -0,0 +1,42 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..6a7e5cd --- /dev/null +++ b/openbr/plugins/core/distributetemplate.cpp @@ -0,0 +1,142 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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/downsampletraining.cpp b/openbr/plugins/core/downsampletraining.cpp new file mode 100644 index 0000000..e6eecb2 --- /dev/null +++ b/openbr/plugins/core/downsampletraining.cpp @@ -0,0 +1,135 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +static TemplateList Downsample(const TemplateList &templates, int classes, int instances, float fraction, const QString &inputVariable, const QStringList &gallery, const QStringList &subjects) +{ + // Return early when no downsampling is required + if ((classes == std::numeric_limits::max()) && + (instances == std::numeric_limits::max()) && + (fraction >= 1) && + (gallery.isEmpty()) && + (subjects.isEmpty())) + return templates; + + const bool atLeast = instances < 0; + instances = abs(instances); + + QList allLabels = File::get(templates, inputVariable); + + QList uniqueLabels = allLabels.toSet().toList(); + qSort(uniqueLabels); + + QMap counts = templates.countValues(inputVariable, instances != std::numeric_limits::max()); + + if ((instances != std::numeric_limits::max()) && (classes != std::numeric_limits::max())) + foreach (const QString &label, counts.keys()) + if (counts[label] < instances) + counts.remove(label); + + uniqueLabels = counts.keys(); + if ((classes != std::numeric_limits::max()) && (uniqueLabels.size() < classes)) + qWarning("Downsample requested %d classes but only %d are available.", classes, uniqueLabels.size()); + + QList selectedLabels = uniqueLabels; + if (classes < uniqueLabels.size()) { + std::random_shuffle(selectedLabels.begin(), selectedLabels.end()); + selectedLabels = selectedLabels.mid(0, classes); + } + + TemplateList downsample; + for (int i=0; i indices; + for (int j=0; j("FTE", false)) && (!templates.value(j).file.get("PossibleFTE", false))) + indices.append(j); + + std::random_shuffle(indices.begin(), indices.end()); + const int max = atLeast ? indices.size() : std::min(indices.size(), instances); + for (int j=0; j=0; i--) + if (!gallery.contains(downsample[i].file.get("Gallery"))) + downsample.removeAt(i); + + if (!subjects.isEmpty()) + for (int i=downsample.size()-1; i>=0; i--) + if (subjects.contains(downsample[i].file.get(inputVariable))) + downsample.removeAt(i); + + return downsample; +} + +class DownsampleTrainingTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform STORED true) + Q_PROPERTY(int classes READ get_classes WRITE set_classes RESET reset_classes STORED false) + Q_PROPERTY(int instances READ get_instances WRITE set_instances RESET reset_instances STORED false) + Q_PROPERTY(float fraction READ get_fraction WRITE set_fraction RESET reset_fraction STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + Q_PROPERTY(QStringList gallery READ get_gallery WRITE set_gallery RESET reset_gallery STORED false) + Q_PROPERTY(QStringList subjects READ get_subjects WRITE set_subjects RESET reset_subjects STORED false) + BR_PROPERTY(br::Transform*, transform, NULL) + BR_PROPERTY(int, classes, std::numeric_limits::max()) + BR_PROPERTY(int, instances, std::numeric_limits::max()) + BR_PROPERTY(float, fraction, 1) + BR_PROPERTY(QString, inputVariable, "Label") + BR_PROPERTY(QStringList, gallery, QStringList()) + BR_PROPERTY(QStringList, subjects, QStringList()) + + + Transform *simplify(bool &newTForm) + { + Transform *res = transform->simplify(newTForm); + return res; + } + + void project(const Template &src, Template &dst) const + { + transform->project(src,dst); + } + + + void train(const TemplateList &data) + { + if (!transform || !transform->trainable) + return; + + TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable, gallery, subjects); + + transform->train(downsampled); + } +}; + +BR_REGISTER(Transform, DownsampleTrainingTransform) + +} // namespace br + +#include "core/downsampletraining.moc" diff --git a/openbr/plugins/core/event.cpp b/openbr/plugins/core/event.cpp new file mode 100644 index 0000000..b9f2568 --- /dev/null +++ b/openbr/plugins/core/event.cpp @@ -0,0 +1,46 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..6c19dff --- /dev/null +++ b/openbr/plugins/core/expand.cpp @@ -0,0 +1,78 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..1d82503 --- /dev/null +++ b/openbr/plugins/core/fork.cpp @@ -0,0 +1,143 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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/core/gallerycompare.cpp b/openbr/plugins/core/gallerycompare.cpp new file mode 100644 index 0000000..d8c0f64 --- /dev/null +++ b/openbr/plugins/core/gallerycompare.cpp @@ -0,0 +1,80 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Compare each template to a fixed gallery (with name = galleryName), using the specified distance. + * dst will contain a 1 by n vector of scores. + * \author Charles Otto \cite caotto + */ +class GalleryCompareTransform : public Transform +{ + Q_OBJECT + Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED true) + Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) + BR_PROPERTY(br::Distance*, distance, NULL) + BR_PROPERTY(QString, galleryName, "") + + TemplateList gallery; + + void project(const Template &src, Template &dst) const + { + dst = src; + if (gallery.isEmpty()) + return; + + QList line = distance->compare(gallery, src); + dst.m() = OpenCVUtils::toMat(line, 1); + } + + void init() + { + if (!galleryName.isEmpty()) + gallery = TemplateList::fromGallery(galleryName); + } + + void train(const TemplateList &data) + { + gallery = data; + } + + void store(QDataStream &stream) const + { + br::Object::store(stream); + stream << gallery; + } + + void load(QDataStream &stream) + { + br::Object::load(stream); + stream >> gallery; + } + +public: + GalleryCompareTransform() : Transform(false, true) {} +}; + +BR_REGISTER(Transform, GalleryCompareTransform) + +} // namespace br + +#include "core/gallerycompare.moc" diff --git a/openbr/plugins/core/identity.cpp b/openbr/plugins/core/identity.cpp new file mode 100644 index 0000000..18b4c93 --- /dev/null +++ b/openbr/plugins/core/identity.cpp @@ -0,0 +1,42 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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 new file mode 100644 index 0000000..b0c9289 --- /dev/null +++ b/openbr/plugins/core/independent.cpp @@ -0,0 +1,229 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Clones the transform so that it can be applied independently. + * \author Josh Klontz \cite jklontz + * \em Independent transforms expect single-matrix templates. + */ +class IndependentTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(br::Transform* transform READ get_transform WRITE set_transform RESET reset_transform STORED false) + BR_PROPERTY(br::Transform*, transform, NULL) + + QList transforms; + + QString description(bool expanded) const + { + return transform->description(expanded); + } + + // can't use general setPropertyRecursive because of transforms oddness + bool setPropertyRecursive(const QString &name, QVariant value) + { + if (br::Object::setExistingProperty(name, value)) + return true; + + if (!transform->setPropertyRecursive(name, value)) + return false; + + for (int i=0;i < transforms.size();i++) + transforms[i]->setPropertyRecursive(name, value); + + return true; + } + + Transform *simplify(bool &newTransform) + { + newTransform = false; + bool newChild = false; + Transform *temp = transform->simplify(newChild); + if (temp == transform) { + return this; + } + IndependentTransform* indep = new IndependentTransform(); + indep->transform = temp; + + IndependentTransform *test = dynamic_cast (temp); + if (test) { + // child was independent? this changes things... + indep->transform = test->transform; + for (int i=0; i < transforms.size(); i++) { + bool newThing = false; + IndependentTransform *probe = dynamic_cast (transforms[i]->simplify(newThing)); + indep->transforms.append(probe->transform); + if (newThing) + probe->setParent(indep); + } + indep->file = indep->transform->file; + indep->trainable = indep->transform->trainable; + indep->setObjectName(indep->transform->objectName()); + + return indep; + } + + if (newChild) + indep->transform->setParent(indep); + + for (int i=0; i < transforms.size();i++) { + bool subTform = false; + indep->transforms.append(transforms[i]->simplify(subTform)); + if (subTform) + indep->transforms[i]->setParent(indep); + } + + indep->file = indep->transform->file; + indep->trainable = indep->transform->trainable; + indep->setObjectName(indep->transform->objectName()); + + return indep; + } + + void init() + { + transforms.clear(); + if (transform == NULL) + return; + + transform->setParent(this); + transforms.append(transform); + file = transform->file; + trainable = transform->trainable; + setObjectName(transform->objectName()); + } + + Transform *clone() const + { + IndependentTransform *independentTransform = new IndependentTransform(); + independentTransform->transform = transform->clone(); + independentTransform->init(); + return independentTransform; + } + + bool timeVarying() const { return transform->timeVarying(); } + + static void _train(Transform *transform, const TemplateList *data) + { + transform->train(*data); + } + + void train(const TemplateList &data) + { + // Don't bother if the transform is untrainable + if (!trainable) return; + + QList templatesList; + foreach (const Template &t, data) { + if ((templatesList.size() != t.size()) && !templatesList.isEmpty()) + qWarning("Independent::train (%s) template %s of size %d differs from expected size %d.", qPrintable(objectName()), qPrintable(t.file.name), t.size(), templatesList.size()); + while (templatesList.size() < t.size()) + templatesList.append(TemplateList()); + for (int i=0; iclone()); + + QFutureSynchronizer futures; + for (int i=0; i mats; + for (int i=0; iproject(Template(src.file, src[i]), dst); + mats.append(dst); + dst.clear(); + } + dst.append(mats); + } + + void projectUpdate(const Template &src, Template &dst) + { + dst.file = src.file; + QList mats; + for (int i=0; iprojectUpdate(Template(src.file, src[i]), dst); + mats.append(dst); + dst.clear(); + } + dst.append(mats); + } + + void finalize(TemplateList &out) + { + if (transforms.empty()) + return; + + transforms[0]->finalize(out); + for (int i=1; i < transforms.size(); i++) { + TemplateList temp; + transforms[i]->finalize(temp); + + for (int j=0; j < out.size(); j++) + out[j].append(temp[j]); + } + } + + void projectUpdate(const TemplateList &src, TemplateList &dst) + { + dst.reserve(src.size()); + foreach (const Template &t, src) { + dst.append(Template()); + projectUpdate(t, dst.last()); + } + } + + void store(QDataStream &stream) const + { + const int size = transforms.size(); + stream << size; + for (int i=0; istore(stream); + } + + void load(QDataStream &stream) + { + int size; + stream >> size; + while (transforms.size() < size) + transforms.append(transform->clone()); + for (int i=0; iload(stream); + } +}; + +BR_REGISTER(Transform, IndependentTransform) + +} // namespace br + +#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..3240fcb --- /dev/null +++ b/openbr/plugins/core/loadstore.cpp @@ -0,0 +1,163 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..bebe445 --- /dev/null +++ b/openbr/plugins/core/pipe.cpp @@ -0,0 +1,221 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..5594fc8 100644 --- a/openbr/plugins/process.cpp +++ b/openbr/plugins/core/processwrapper.cpp @@ -1,4 +1,18 @@ - +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 @@ -9,8 +23,8 @@ #include #include -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" +#include +#include using namespace cv; @@ -658,4 +672,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..e27fbfd --- /dev/null +++ b/openbr/plugins/core/progresscounter.cpp @@ -0,0 +1,97 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#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..aaaf573 --- /dev/null +++ b/openbr/plugins/core/registrar.cpp @@ -0,0 +1,41 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..72a0fed --- /dev/null +++ b/openbr/plugins/core/remove.cpp @@ -0,0 +1,47 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..0cbcedf --- /dev/null +++ b/openbr/plugins/core/rest.cpp @@ -0,0 +1,43 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..3bb6ad6 --- /dev/null +++ b/openbr/plugins/core/schrodinger.cpp @@ -0,0 +1,61 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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 new file mode 100644 index 0000000..965750c --- /dev/null +++ b/openbr/plugins/core/singleton.cpp @@ -0,0 +1,89 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup transforms + * \brief A globally shared transform. + * \author Josh Klontz \cite jklontz + */ +class SingletonTransform : public MetaTransform +{ + Q_OBJECT + Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) + BR_PROPERTY(QString, description, "Identity") + + static QMutex mutex; + static QHash transforms; + static QHash trainingReferenceCounts; + static QHash trainingData; + + Transform *transform; + + void init() + { + QMutexLocker locker(&mutex); + if (!transforms.contains(description)) { + transforms.insert(description, make(description)); + trainingReferenceCounts.insert(description, 0); + } + + transform = transforms[description]; + trainingReferenceCounts[description]++; + } + + void train(const TemplateList &data) + { + QMutexLocker locker(&mutex); + trainingData[description].append(data); + trainingReferenceCounts[description]--; + if (trainingReferenceCounts[description] > 0) return; + transform->train(trainingData[description]); + trainingData[description].clear(); + } + + void project(const Template &src, Template &dst) const + { + transform->project(src, dst); + } + + void store(QDataStream &stream) const + { + if (transform->parent() == this) + transform->store(stream); + } + + void load(QDataStream &stream) + { + if (transform->parent() == this) + transform->load(stream); + } +}; + +QMutex SingletonTransform::mutex; +QHash SingletonTransform::transforms; +QHash SingletonTransform::trainingReferenceCounts; +QHash SingletonTransform::trainingData; + +BR_REGISTER(Transform, SingletonTransform) + +} // namespace br + +#include "core/singleton.moc" diff --git a/openbr/plugins/stream.cpp b/openbr/plugins/core/stream.cpp index c2e5019..8920d6e 100644 --- a/openbr/plugins/stream.cpp +++ b/openbr/plugins/core/stream.cpp @@ -1,3 +1,19 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 #include @@ -8,10 +24,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 +1377,5 @@ BR_REGISTER(Transform, StreamTransform) } // namespace br -#include "stream.moc" +#include "core/stream.moc" diff --git a/openbr/plugins/crop.cpp b/openbr/plugins/crop.cpp deleted file mode 100644 index 01b5167..0000000 --- a/openbr/plugins/crop.cpp +++ /dev/null @@ -1,299 +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 "openbr_internal.h" - -#include "openbr/core/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Crops about the specified region of interest. - * \author Josh Klontz \cite jklontz - */ -class CropTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int x READ get_x WRITE set_x RESET reset_x STORED false) - Q_PROPERTY(int y READ get_y WRITE set_y RESET reset_y STORED false) - Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false) - Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false) - BR_PROPERTY(int, x, 0) - BR_PROPERTY(int, y, 0) - BR_PROPERTY(int, width, -1) - BR_PROPERTY(int, height, -1) - - void project(const Template &src, Template &dst) const - { - dst = Mat(src, Rect(x, y, width < 1 ? src.m().cols-x-abs(width) : width, height < 1 ? src.m().rows-y-abs(height) : height)); - } -}; - -BR_REGISTER(Transform, CropTransform) - -/*! - * \ingroup transforms - * \brief Crops the rectangular regions of interest. - * \author Josh Klontz \cite jklontz - */ -class ROITransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(QString propName READ get_propName WRITE set_propName RESET reset_propName STORED false) - BR_PROPERTY(QString, propName, "") - - void project(const Template &src, Template &dst) const - { - if (!propName.isEmpty()) { - QRectF rect = src.file.get(propName); - dst += src.m()(OpenCVUtils::toRect(rect)); - } else if (!src.file.rects().empty()) { - foreach (const QRectF &rect, src.file.rects()) - dst += src.m()(OpenCVUtils::toRect(rect)); - } else if (src.file.contains(QStringList() << "X" << "Y" << "Width" << "Height")) { - dst += src.m()(Rect(src.file.get("X"), - src.file.get("Y"), - src.file.get("Width"), - src.file.get("Height"))); - } else { - dst = src; - if (Globals->verbose) - qWarning("No rects present in file."); - } - } -}; - -BR_REGISTER(Transform, ROITransform) - -/*! - * \ingroup transforms - * \brief Crops the rectangular regions of interest from given points and sizes. - * \author Austin Blanton \cite imaus10 - */ -class ROIFromPtsTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int width READ get_width WRITE set_width RESET reset_width STORED false) - Q_PROPERTY(int height READ get_height WRITE set_height RESET reset_height STORED false) - BR_PROPERTY(int, width, 1) - BR_PROPERTY(int, height, 1) - - void project(const Template &src, Template &dst) const - { - foreach (const QPointF &pt, src.file.points()) { - int x = pt.x() - (width/2); - int y = pt.y() - (height/2); - dst += src.m()(Rect(x, y, width, height)); - } - } -}; - -BR_REGISTER(Transform, ROIFromPtsTransform) - -/*! - * \ingroup transforms - * \brief Resize the template - * \author Josh Klontz \cite jklontz - * \note Method: Area should be used for shrinking an image, Cubic for slow but accurate enlargment, Bilin for fast enlargement. - * \param preserveAspect If true, the image will be sized per specification, but - * a border will be applied to preserve aspect ratio. - */ -class ResizeTransform : public UntrainableTransform -{ - Q_OBJECT - Q_ENUMS(Method) - -public: - /*!< */ - enum Method { Near = INTER_NEAREST, - Area = INTER_AREA, - Bilin = INTER_LINEAR, - Cubic = INTER_CUBIC, - Lanczo = INTER_LANCZOS4}; - -private: - Q_PROPERTY(int rows READ get_rows WRITE set_rows RESET reset_rows STORED false) - Q_PROPERTY(int columns READ get_columns WRITE set_columns RESET reset_columns STORED false) - Q_PROPERTY(Method method READ get_method WRITE set_method RESET reset_method STORED false) - Q_PROPERTY(bool preserveAspect READ get_preserveAspect WRITE set_preserveAspect RESET reset_preserveAspect STORED false) - BR_PROPERTY(int, rows, -1) - BR_PROPERTY(int, columns, -1) - BR_PROPERTY(Method, method, Bilin) - BR_PROPERTY(bool, preserveAspect, false) - - void project(const Template &src, Template &dst) const - { - if (!preserveAspect) - resize(src, dst, Size((columns == -1) ? src.m().cols*rows/src.m().rows : columns, rows), 0, 0, method); - else { - float inRatio = (float) src.m().rows / src.m().cols; - float outRatio = (float) rows / columns; - dst = Mat::zeros(rows, columns, src.m().type()); - if (outRatio > inRatio) { - float heightAR = src.m().rows * inRatio / outRatio; - Mat buffer; - resize(src, buffer, Size(columns, heightAR), 0, 0, method); - buffer.copyTo(dst.m()(Rect(0, (rows - heightAR) / 2, columns, heightAR))); - } else { - float widthAR = src.m().cols / inRatio * outRatio; - Mat buffer; - resize(src, buffer, Size(widthAR, rows), 0, 0, method); - buffer.copyTo(dst.m()(Rect((columns - widthAR) / 2, 0, widthAR, rows))); - } - } - } -}; - -BR_REGISTER(Transform, ResizeTransform) - -/*! - * \ingroup transforms - * \brief Limit the size of the template - * \author Josh Klontz \cite jklontz - */ -class LimitSizeTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int max READ get_max WRITE set_max RESET reset_max STORED false) - BR_PROPERTY(int, max, -1) - - void project(const Template &src, Template &dst) const - { - const Mat &m = src; - if (m.rows > m.cols) - if (m.rows > max) resize(m, dst, Size(std::max(1, m.cols * max / m.rows), max)); - else dst = m; - else - if (m.cols > max) resize(m, dst, Size(max, std::max(1, m.rows * max / m.cols))); - else dst = m; - } -}; - -BR_REGISTER(Transform, LimitSizeTransform) - -/*! - * \ingroup transforms - * \brief Enforce a multiple of \em n columns. - * \author Josh Klontz \cite jklontz - */ -class DivTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) - BR_PROPERTY(int, n, 1) - - void project(const Template &src, Template &dst) const - { - dst = Mat(src, Rect(0,0,n*(src.m().cols/n),src.m().rows)); - } -}; - -BR_REGISTER(Transform, DivTransform) - -/*! - * \ingroup transforms - * \brief Crop out black borders - * \author Josh Klontz \cite jklontz - */ -class CropBlackTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - Mat gray; - OpenCVUtils::cvtGray(src, gray); - - int xStart = 0; - while (xStart < gray.cols) { - if (mean(gray.col(xStart))[0] >= 1) break; - xStart++; - } - - int xEnd = gray.cols - 1; - while (xEnd >= 0) { - if (mean(gray.col(xEnd))[0] >= 1) break; - xEnd--; - } - - int yStart = 0; - while (yStart < gray.rows) { - if (mean(gray.col(yStart))[0] >= 1) break; - yStart++; - } - - int yEnd = gray.rows - 1; - while (yEnd >= 0) { - if (mean(gray.col(yEnd))[0] >= 1) break; - yEnd--; - } - - dst = src.m()(Rect(xStart, yStart, xEnd-xStart, yEnd-yStart)); - } -}; - -BR_REGISTER(Transform, CropBlackTransform) - -/*! - * \ingroup transforms - * \brief Divide the matrix into 4 smaller matricies of equal size. - * \author Josh Klontz \cite jklontz - */ -class SubdivideTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - const Mat &m = src; - const int subrows = m.rows/2; - const int subcolumns = m.cols/2; - dst.append(Mat(m,Rect(0, 0, subcolumns, subrows)).clone()); - dst.append(Mat(m,Rect(subcolumns, 0, subcolumns, subrows)).clone()); - dst.append(Mat(m,Rect(0, subrows, subcolumns, subrows)).clone()); - dst.append(Mat(m,Rect(subcolumns, subrows, subcolumns, subrows)).clone()); - } -}; - -BR_REGISTER(Transform, SubdivideTransform) - -/*! - * \ingroup transforms - * \brief Trim the image so the width and the height are the same size. - * \author Josh Klontz \cite jklontz - */ -class CropSquareTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - const Mat &m = src; - const int newSize = min(m.rows, m.cols); - dst = Mat(m, Rect((m.cols-newSize)/2, (m.rows-newSize)/2, newSize, newSize)); - } -}; - -BR_REGISTER(Transform, CropSquareTransform) - -} // namespace br - -#include "crop.moc" diff --git a/openbr/plugins/cvt.cpp b/openbr/plugins/cvt.cpp deleted file mode 100644 index 362ca0c..0000000 --- a/openbr/plugins/cvt.cpp +++ /dev/null @@ -1,292 +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 -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Colorspace conversion. - * \author Josh Klontz \cite jklontz - */ -class CvtTransform : public UntrainableTransform -{ - Q_OBJECT - Q_ENUMS(ColorSpace) - Q_PROPERTY(ColorSpace colorSpace READ get_colorSpace WRITE set_colorSpace RESET reset_colorSpace STORED false) - Q_PROPERTY(int channel READ get_channel WRITE set_channel RESET reset_channel STORED false) - -public: - enum ColorSpace { Gray = CV_BGR2GRAY, - RGBGray = CV_RGB2GRAY, - HLS = CV_BGR2HLS, - HSV = CV_BGR2HSV, - Lab = CV_BGR2Lab, - Luv = CV_BGR2Luv, - RGB = CV_BGR2RGB, - XYZ = CV_BGR2XYZ, - YCrCb = CV_BGR2YCrCb, - Color = CV_GRAY2BGR }; - -private: - BR_PROPERTY(ColorSpace, colorSpace, Gray) - BR_PROPERTY(int, channel, -1) - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() > 1 || colorSpace == CV_GRAY2BGR) cvtColor(src, dst, colorSpace); - else dst = src; - - if (channel != -1) { - std::vector mv; - split(dst, mv); - dst = mv[channel % (int)mv.size()]; - } - } -}; - -BR_REGISTER(Transform, CvtTransform) - -/*! - * \ingroup transforms - * \brief Convert to floating point format. - * \author Josh Klontz \cite jklontz - */ -class CvtFloatTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - src.m().convertTo(dst, CV_32F); - } -}; - -BR_REGISTER(Transform, CvtFloatTransform) - -/*! - * \ingroup transforms - * \brief Convert to uchar format - * \author Josh Klontz \cite jklontz - */ -class CvtUCharTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - OpenCVUtils::cvtUChar(src, dst); - } -}; - -BR_REGISTER(Transform, CvtUCharTransform) - -/*! - * \ingroup transforms - * \brief Scales using the given factor - * \author Scott Klum \cite sklum - */ -class ScaleTransform : public UntrainableTransform -{ - Q_OBJECT - - Q_PROPERTY(float scaleFactor READ get_scaleFactor WRITE set_scaleFactor RESET reset_scaleFactor STORED false) - BR_PROPERTY(float, scaleFactor, 1.) - - void project(const Template &src, Template &dst) const - { - resize(src, dst, Size(src.m().cols*scaleFactor,src.m().rows*scaleFactor)); - } -}; - -BR_REGISTER(Transform, ScaleTransform) - -/*! - * \ingroup transforms - * \brief Split a multi-channel matrix into several single-channel matrices. - * \author Josh Klontz \cite jklontz - */ -class SplitChannelsTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - std::vector mv; - split(src, mv); - foreach (const Mat &m, mv) - dst += m; - } -}; - -BR_REGISTER(Transform, SplitChannelsTransform) - -/*! - * \ingroup transforms - * \brief Enforce the matrix has a certain number of channels by adding or removing channels. - * \author Josh Klontz \cite jklontz - */ -class EnsureChannelsTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(int n READ get_n WRITE set_n RESET reset_n STORED false) - BR_PROPERTY(int, n, 1) - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() == n) { - dst = src; - } else { - std::vector mv; - split(src, mv); - - // Add extra channels - while ((int)mv.size() < n) { - for (int i=0; i n) - mv.pop_back(); - - merge(mv, dst); - } - } -}; - -BR_REGISTER(Transform, EnsureChannelsTransform) - -/*! - * \ingroup transforms - * \brief Drop the alpha channel (if exists). - * \author Austin Blanton \cite imaus10 - */ -class DiscardAlphaTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() > 4 || src.m().channels() == 2) { - dst.file.fte = true; - return; - } - - dst = src; - if (src.m().channels() == 4) { - std::vector mv; - split(src, mv); - mv.pop_back(); - merge(mv, dst); - } - } -}; - -BR_REGISTER(Transform, DiscardAlphaTransform) - -/*! - * \ingroup transforms - * \brief Normalized RG color space. - * \author Josh Klontz \cite jklontz - */ -class RGTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - if (src.m().type() != CV_8UC3) - qFatal("Expected CV_8UC3 images."); - - const Mat &m = src.m(); - Mat R(m.size(), CV_8UC1); // R / (R+G+B) - Mat G(m.size(), CV_8UC1); // G / (R+G+B) - - for (int i=0; i(i,j); - const int b = v[0]; - const int g = v[1]; - const int r = v[2]; - const int sum = b + g + r; - if (sum > 0) { - R.at(i, j) = saturate_cast(255.0*r/(r+g+b)); - G.at(i, j) = saturate_cast(255.0*g/(r+g+b)); - } else { - R.at(i, j) = 0; - G.at(i, j) = 0; - } - } - - dst.append(R); - dst.append(G); - } -}; - -BR_REGISTER(Transform, RGTransform) - -/*! - * \ingroup transforms - * \brief dst = a*src+b - * \author Josh Klontz \cite jklontz - */ -class MAddTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(double a READ get_a WRITE set_a RESET reset_a STORED false) - Q_PROPERTY(double b READ get_b WRITE set_b RESET reset_b STORED false) - BR_PROPERTY(double, a, 1) - BR_PROPERTY(double, b, 0) - - void project(const Template &src, Template &dst) const - { - src.m().convertTo(dst.m(), src.m().depth(), a, b); - } -}; - -BR_REGISTER(Transform, MAddTransform) - -/*! - * \ingroup transforms - * \brief Computes the absolute value of each element. - * \author Josh Klontz \cite jklontz - */ -class AbsTransform : public UntrainableTransform -{ - Q_OBJECT - - void project(const Template &src, Template &dst) const - { - dst = abs(src); - } -}; - -BR_REGISTER(Transform, AbsTransform) - -} // namespace br - -#include "cvt.moc" diff --git a/openbr/plugins/distance.cpp b/openbr/plugins/distance.cpp deleted file mode 100644 index 4fd7014..0000000 --- a/openbr/plugins/distance.cpp +++ /dev/null @@ -1,512 +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 -#include -#include -#include -#include "openbr_internal.h" - -#include "openbr/core/distance_sse.h" -#include "openbr/core/qtutils.h" -#include "openbr/core/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup distances - * \brief Standard distance metrics - * \author Josh Klontz \cite jklontz - */ -class DistDistance : public UntrainableDistance -{ - Q_OBJECT - Q_ENUMS(Metric) - Q_PROPERTY(Metric metric READ get_metric WRITE set_metric RESET reset_metric STORED false) - Q_PROPERTY(bool negLogPlusOne READ get_negLogPlusOne WRITE set_negLogPlusOne RESET reset_negLogPlusOne STORED false) - -public: - /*!< */ - enum Metric { Correlation, - ChiSquared, - Intersection, - Bhattacharyya, - INF, - L1, - L2, - Cosine, - Dot}; - -private: - BR_PROPERTY(Metric, metric, L2) - BR_PROPERTY(bool, negLogPlusOne, true) - - float compare(const Mat &a, const Mat &b) const - { - if ((a.size != b.size) || - (a.type() != b.type())) - return -std::numeric_limits::max(); - -// TODO: this max value is never returned based on the switch / default - float result = std::numeric_limits::max(); - switch (metric) { - case Correlation: - return compareHist(a, b, CV_COMP_CORREL); - case ChiSquared: - result = compareHist(a, b, CV_COMP_CHISQR); - break; - case Intersection: - result = compareHist(a, b, CV_COMP_INTERSECT); - break; - case Bhattacharyya: - result = compareHist(a, b, CV_COMP_BHATTACHARYYA); - break; - case INF: - result = norm(a, b, NORM_INF); - break; - case L1: - result = norm(a, b, NORM_L1); - break; - case L2: - result = norm(a, b, NORM_L2); - break; - case Cosine: - return cosine(a, b); - case Dot: - return a.dot(b); - default: - qFatal("Invalid metric"); - } - - if (result != result) - qFatal("NaN result."); - - return negLogPlusOne ? -log(result+1) : result; - } - - static float cosine(const Mat &a, const Mat &b) - { - float dot = 0; - float magA = 0; - float magB = 0; - - for (int row=0; row(row,col); - const float query = b.at(row,col); - dot += target * query; - magA += target * target; - magB += query * query; - } - } - - return dot / (sqrt(magA)*sqrt(magB)); - } -}; - -BR_REGISTER(Distance, DistDistance) - -/*! - * \ingroup distances - * \brief DistDistance wrapper. - * \author Josh Klontz \cite jklontz - */ -class DefaultDistance : public UntrainableDistance -{ - Q_OBJECT - Distance *distance; - - void init() - { - distance = Distance::make("Dist("+file.suffix()+")"); - } - - float compare(const cv::Mat &a, const cv::Mat &b) const - { - return distance->compare(a, b); - } -}; - -BR_REGISTER(Distance, DefaultDistance) - -/*! - * \ingroup distances - * \brief Distances in series. - * \author Josh Klontz \cite jklontz - * - * The templates are compared using each br::Distance in order. - * If the result of the comparison with any given distance is -FLOAT_MAX then this result is returned early. - * Otherwise the returned result is the value of comparing the templates using the last br::Distance. - */ -class PipeDistance : public Distance -{ - Q_OBJECT - Q_PROPERTY(QList distances READ get_distances WRITE set_distances RESET reset_distances) - BR_PROPERTY(QList, distances, QList()) - - void train(const TemplateList &data) - { - QFutureSynchronizer futures; - foreach (br::Distance *distance, distances) - futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); - futures.waitForFinished(); - } - - float compare(const Template &a, const Template &b) const - { - float result = -std::numeric_limits::max(); - foreach (br::Distance *distance, distances) { - result = distance->compare(a, b); - if (result == -std::numeric_limits::max()) - return result; - } - return result; - } -}; - -BR_REGISTER(Distance, PipeDistance) - -/*! - * \ingroup distances - * \brief Fuses similarity scores across multiple matrices of compared templates - * \author Scott Klum \cite sklum - * \note Operation: Mean, sum, min, max are supported. - */ -class FuseDistance : public Distance -{ - Q_OBJECT - Q_ENUMS(Operation) - Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) - Q_PROPERTY(Operation operation READ get_operation WRITE set_operation RESET reset_operation STORED false) - Q_PROPERTY(QList weights READ get_weights WRITE set_weights RESET reset_weights STORED false) - - QList distances; - -public: - /*!< */ - enum Operation {Mean, Sum, Max, Min}; - -private: - BR_PROPERTY(QString, description, "L2") - BR_PROPERTY(Operation, operation, Mean) - BR_PROPERTY(QList, weights, QList()) - - void train(const TemplateList &src) - { - // Partition the templates by matrix - QList split; - for (int i=0; i partitionedSrc = src.partition(split); - - while (distances.size() < partitionedSrc.size()) - distances.append(make(description)); - - // Train on each of the partitions - for (int i=0; itrain(partitionedSrc[i]); - } - - float compare(const Template &a, const Template &b) const - { - if (a.size() != b.size()) qFatal("Comparison size mismatch"); - - QList scores; - for (int i=0; icompare(Template(a.file, a[i]),Template(b.file, b[i]))); - } - - switch (operation) { - case Mean: - return std::accumulate(scores.begin(),scores.end(),0.0)/(float)scores.size(); - break; - case Sum: - return std::accumulate(scores.begin(),scores.end(),0.0); - break; - case Min: - return *std::min_element(scores.begin(),scores.end()); - break; - case Max: - return *std::max_element(scores.begin(),scores.end()); - break; - default: - qFatal("Invalid operation."); - } - return 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, FuseDistance) - -/*! - * \ingroup distances - * \brief Fast 8-bit L1 distance - * \author Josh Klontz \cite jklontz - */ -class ByteL1Distance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const unsigned char *a, const unsigned char *b, size_t size) const - { - return l1(a, b, size); - } -}; - -BR_REGISTER(Distance, ByteL1Distance) - -/*! - * \ingroup distances - * \brief Fast 4-bit L1 distance - * \author Josh Klontz \cite jklontz - */ -class HalfByteL1Distance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const Mat &a, const Mat &b) const - { - return packed_l1(a.data, b.data, a.total()); - } -}; - -BR_REGISTER(Distance, HalfByteL1Distance) - -/*! - * \ingroup distances - * \brief Returns -log(distance(a,b)+1) - * \author Josh Klontz \cite jklontz - */ -class NegativeLogPlusOneDistance : public UntrainableDistance -{ - Q_OBJECT - Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) - BR_PROPERTY(br::Distance*, distance, NULL) - - void train(const TemplateList &src) - { - distance->train(src); - } - - float compare(const Template &a, const Template &b) const - { - return -log(distance->compare(a,b)+1); - } - - void store(QDataStream &stream) const - { - distance->store(stream); - } - - void load(QDataStream &stream) - { - distance->load(stream); - } -}; - -BR_REGISTER(Distance, NegativeLogPlusOneDistance) - -/*! - * \ingroup distances - * \brief Returns \c true if the templates are identical, \c false otherwise. - * \author Josh Klontz \cite jklontz - */ -class IdenticalDistance : public UntrainableDistance -{ - Q_OBJECT - - float compare(const Mat &a, const Mat &b) const - { - const size_t size = a.total() * a.elemSize(); - if (size != b.total() * b.elemSize()) return 0; - for (size_t i=0; i scoreHash; - mutable QMutex mutex; - - float compare(const Template &target, const Template &query) const - { - float currentScore = distance->compare(target, query); - - QMutexLocker mutexLocker(&mutex); - return scoreHash[target.file.name] = (1.0- alpha) * scoreHash[target.file.name] + alpha * currentScore; - } -}; - -BR_REGISTER(Distance, OnlineDistance) - -/*! - * \ingroup distances - * \brief Attenuation function based distance from attributes - * \author Scott Klum \cite sklum - */ -class AttributeDistance : public UntrainableDistance -{ - Q_OBJECT - Q_PROPERTY(QString attribute READ get_attribute WRITE set_attribute RESET reset_attribute STORED false) - BR_PROPERTY(QString, attribute, QString()) - - float compare(const Template &target, const Template &query) const - { - float queryValue = query.file.get(attribute); - float targetValue = target.file.get(attribute); - - // TODO: Set this magic number to something meaningful - float stddev = 1; - - if (queryValue == targetValue) return 1; - else return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((targetValue-queryValue)/stddev, 2)); - } -}; - -BR_REGISTER(Distance, AttributeDistance) - -/*! - * \ingroup distances - * \brief Sum match scores across multiple distances - * \author Scott Klum \cite sklum - */ -class SumDistance : public UntrainableDistance -{ - Q_OBJECT - Q_PROPERTY(QList distances READ get_distances WRITE set_distances RESET reset_distances) - BR_PROPERTY(QList, distances, QList()) - - void train(const TemplateList &data) - { - QFutureSynchronizer futures; - foreach (br::Distance *distance, distances) - futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); - futures.waitForFinished(); - } - - float compare(const Template &target, const Template &query) const - { - float result = 0; - - foreach (br::Distance *distance, distances) { - result += distance->compare(target, query); - - if (result == -std::numeric_limits::max()) - return result; - } - - return result; - } -}; - -BR_REGISTER(Distance, SumDistance) - -/*! - * \ingroup transforms - * \brief Compare each template to a fixed gallery (with name = galleryName), using the specified distance. - * dst will contain a 1 by n vector of scores. - * \author Charles Otto \cite caotto - */ -class GalleryCompareTransform : public Transform -{ - Q_OBJECT - Q_PROPERTY(br::Distance *distance READ get_distance WRITE set_distance RESET reset_distance STORED true) - Q_PROPERTY(QString galleryName READ get_galleryName WRITE set_galleryName RESET reset_galleryName STORED false) - BR_PROPERTY(br::Distance*, distance, NULL) - BR_PROPERTY(QString, galleryName, "") - - TemplateList gallery; - - void project(const Template &src, Template &dst) const - { - dst = src; - if (gallery.isEmpty()) - return; - - QList line = distance->compare(gallery, src); - dst.m() = OpenCVUtils::toMat(line, 1); - } - - void init() - { - if (!galleryName.isEmpty()) - gallery = TemplateList::fromGallery(galleryName); - } - - void train(const TemplateList &data) - { - gallery = data; - } - - void store(QDataStream &stream) const - { - br::Object::store(stream); - stream << gallery; - } - - void load(QDataStream &stream) - { - br::Object::load(stream); - stream >> gallery; - } - -public: - GalleryCompareTransform() : Transform(false, true) {} -}; - -BR_REGISTER(Transform, GalleryCompareTransform) - - -} // namespace br -#include "distance.moc" diff --git a/openbr/plugins/distance/L2.cpp b/openbr/plugins/distance/L2.cpp new file mode 100644 index 0000000..a966c20 --- /dev/null +++ b/openbr/plugins/distance/L2.cpp @@ -0,0 +1,46 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup distances + * \brief L2 distance computed using eigen. + * \author Josh Klontz \cite jklontz + */ +class L2Distance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const cv::Mat &a, const cv::Mat &b) const + { + const int size = a.rows * a.cols; + Eigen::Map aMap((float*)a.data, size); + Eigen::Map bMap((float*)b.data, size); + return (aMap-bMap).squaredNorm(); + } +}; + +BR_REGISTER(Distance, L2Distance) + +} // namespace br + +#include "distance/L2.moc" diff --git a/openbr/plugins/distance/attribute.cpp b/openbr/plugins/distance/attribute.cpp new file mode 100644 index 0000000..29f2610 --- /dev/null +++ b/openbr/plugins/distance/attribute.cpp @@ -0,0 +1,50 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup distances + * \brief Attenuation function based distance from attributes + * \author Scott Klum \cite sklum + */ +class AttributeDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(QString attribute READ get_attribute WRITE set_attribute RESET reset_attribute STORED false) + BR_PROPERTY(QString, attribute, QString()) + + float compare(const Template &target, const Template &query) const + { + float queryValue = query.file.get(attribute); + float targetValue = target.file.get(attribute); + + // TODO: Set this magic number to something meaningful + float stddev = 1; + + if (queryValue == targetValue) return 1; + else return 1/(stddev*sqrt(2*CV_PI))*exp(-0.5*pow((targetValue-queryValue)/stddev, 2)); + } +}; + +BR_REGISTER(Distance, AttributeDistance) + +} // namespace br + +#include "distance/attribute.moc" diff --git a/openbr/plugins/distance/bayesianquantization.cpp b/openbr/plugins/distance/bayesianquantization.cpp new file mode 100644 index 0000000..ea9836c --- /dev/null +++ b/openbr/plugins/distance/bayesianquantization.cpp @@ -0,0 +1,104 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#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 new file mode 100644 index 0000000..36a5970 --- /dev/null +++ b/openbr/plugins/distance/byteL1.cpp @@ -0,0 +1,42 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup distances + * \brief Fast 8-bit L1 distance + * \author Josh Klontz \cite jklontz + */ +class ByteL1Distance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const unsigned char *a, const unsigned char *b, size_t size) const + { + return l1(a, b, size); + } +}; + +BR_REGISTER(Distance, ByteL1Distance) + +} // namespace br + +#include "distance/byteL1.moc" diff --git a/openbr/plugins/distance/crossvalidate.cpp b/openbr/plugins/distance/crossvalidate.cpp new file mode 100644 index 0000000..ef69a88 --- /dev/null +++ b/openbr/plugins/distance/crossvalidate.cpp @@ -0,0 +1,44 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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 new file mode 100644 index 0000000..e4bf121 --- /dev/null +++ b/openbr/plugins/distance/default.cpp @@ -0,0 +1,47 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup distances + * \brief DistDistance wrapper. + * \author Josh Klontz \cite jklontz + */ +class DefaultDistance : public UntrainableDistance +{ + Q_OBJECT + Distance *distance; + + void init() + { + distance = Distance::make("Dist("+file.suffix()+")"); + } + + float compare(const cv::Mat &a, const cv::Mat &b) const + { + return distance->compare(a, b); + } +}; + +BR_REGISTER(Distance, DefaultDistance) + +} // namespace br + +#include "distance/default.moc" diff --git a/openbr/plugins/distance/dist.cpp b/openbr/plugins/distance/dist.cpp new file mode 100644 index 0000000..f98196e --- /dev/null +++ b/openbr/plugins/distance/dist.cpp @@ -0,0 +1,120 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup distances + * \brief Standard distance metrics + * \author Josh Klontz \cite jklontz + */ +class DistDistance : public UntrainableDistance +{ + Q_OBJECT + Q_ENUMS(Metric) + Q_PROPERTY(Metric metric READ get_metric WRITE set_metric RESET reset_metric STORED false) + Q_PROPERTY(bool negLogPlusOne READ get_negLogPlusOne WRITE set_negLogPlusOne RESET reset_negLogPlusOne STORED false) + +public: + /*!< */ + enum Metric { Correlation, + ChiSquared, + Intersection, + Bhattacharyya, + INF, + L1, + L2, + Cosine, + Dot}; + +private: + BR_PROPERTY(Metric, metric, L2) + BR_PROPERTY(bool, negLogPlusOne, true) + + float compare(const Mat &a, const Mat &b) const + { + if ((a.size != b.size) || + (a.type() != b.type())) + return -std::numeric_limits::max(); + +// TODO: this max value is never returned based on the switch / default + float result = std::numeric_limits::max(); + switch (metric) { + case Correlation: + return compareHist(a, b, CV_COMP_CORREL); + case ChiSquared: + result = compareHist(a, b, CV_COMP_CHISQR); + break; + case Intersection: + result = compareHist(a, b, CV_COMP_INTERSECT); + break; + case Bhattacharyya: + result = compareHist(a, b, CV_COMP_BHATTACHARYYA); + break; + case INF: + result = norm(a, b, NORM_INF); + break; + case L1: + result = norm(a, b, NORM_L1); + break; + case L2: + result = norm(a, b, NORM_L2); + break; + case Cosine: + return cosine(a, b); + case Dot: + return a.dot(b); + default: + qFatal("Invalid metric"); + } + + if (result != result) + qFatal("NaN result."); + + return negLogPlusOne ? -log(result+1) : result; + } + + static float cosine(const Mat &a, const Mat &b) + { + float dot = 0; + float magA = 0; + float magB = 0; + + for (int row=0; row(row,col); + const float query = b.at(row,col); + dot += target * query; + magA += target * target; + magB += query * query; + } + } + + return dot / (sqrt(magA)*sqrt(magB)); + } +}; + +BR_REGISTER(Distance, DistDistance) + +} // namespace br + +#include "distance/dist.moc" diff --git a/openbr/plugins/distance/filter.cpp b/openbr/plugins/distance/filter.cpp new file mode 100644 index 0000000..3570289 --- /dev/null +++ b/openbr/plugins/distance/filter.cpp @@ -0,0 +1,55 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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 new file mode 100644 index 0000000..c02b411 --- /dev/null +++ b/openbr/plugins/distance/fuse.cpp @@ -0,0 +1,117 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup distances + * \brief Fuses similarity scores across multiple matrices of compared templates + * \author Scott Klum \cite sklum + * \note Operation: Mean, sum, min, max are supported. + */ +class FuseDistance : public Distance +{ + Q_OBJECT + Q_ENUMS(Operation) + Q_PROPERTY(QString description READ get_description WRITE set_description RESET reset_description STORED false) + Q_PROPERTY(Operation operation READ get_operation WRITE set_operation RESET reset_operation STORED false) + Q_PROPERTY(QList weights READ get_weights WRITE set_weights RESET reset_weights STORED false) + + QList distances; + +public: + /*!< */ + enum Operation {Mean, Sum, Max, Min}; + +private: + BR_PROPERTY(QString, description, "L2") + BR_PROPERTY(Operation, operation, Mean) + BR_PROPERTY(QList, weights, QList()) + + void train(const TemplateList &src) + { + // Partition the templates by matrix + QList split; + for (int i=0; i partitionedSrc = src.partition(split); + + while (distances.size() < partitionedSrc.size()) + distances.append(make(description)); + + // Train on each of the partitions + for (int i=0; itrain(partitionedSrc[i]); + } + + float compare(const Template &a, const Template &b) const + { + if (a.size() != b.size()) qFatal("Comparison size mismatch"); + + QList scores; + for (int i=0; icompare(Template(a.file, a[i]),Template(b.file, b[i]))); + } + + switch (operation) { + case Mean: + return std::accumulate(scores.begin(),scores.end(),0.0)/(float)scores.size(); + break; + case Sum: + return std::accumulate(scores.begin(),scores.end(),0.0); + break; + case Min: + return *std::min_element(scores.begin(),scores.end()); + break; + case Max: + return *std::max_element(scores.begin(),scores.end()); + break; + default: + qFatal("Invalid operation."); + } + return 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, FuseDistance) + +} // namespace br + +#include "distance/fuse.moc" diff --git a/openbr/plugins/distance/halfbyteL1.cpp b/openbr/plugins/distance/halfbyteL1.cpp new file mode 100644 index 0000000..d70c0cf --- /dev/null +++ b/openbr/plugins/distance/halfbyteL1.cpp @@ -0,0 +1,45 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup distances + * \brief Fast 4-bit L1 distance + * \author Josh Klontz \cite jklontz + */ +class HalfByteL1Distance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const Mat &a, const Mat &b) const + { + return packed_l1(a.data, b.data, a.total()); + } +}; + +BR_REGISTER(Distance, HalfByteL1Distance) + + +} // namespace br + +#include "distance/halfbyteL1.moc" diff --git a/openbr/plugins/distance/heatmap.cpp b/openbr/plugins/distance/heatmap.cpp new file mode 100644 index 0000000..d25cf4b --- /dev/null +++ b/openbr/plugins/distance/heatmap.cpp @@ -0,0 +1,100 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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 new file mode 100644 index 0000000..49cb36e --- /dev/null +++ b/openbr/plugins/distance/identical.cpp @@ -0,0 +1,47 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup distances + * \brief Returns \c true if the templates are identical, \c false otherwise. + * \author Josh Klontz \cite jklontz + */ +class IdenticalDistance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const Mat &a, const Mat &b) const + { + const size_t size = a.total() * a.elemSize(); + if (size != b.total() * b.elemSize()) return 0; + for (size_t i=0; i + +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup transforms + * \brief Wraps OpenCV Key Point Matcher + * \author Josh Klontz \cite jklontz + */ +class KeyPointMatcherDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(QString matcher READ get_matcher WRITE set_matcher RESET reset_matcher STORED false) + Q_PROPERTY(float maxRatio READ get_maxRatio WRITE set_maxRatio RESET reset_maxRatio STORED false) + BR_PROPERTY(QString, matcher, "BruteForce") + BR_PROPERTY(float, maxRatio, 0.8) + + Ptr descriptorMatcher; + + void init() + { + descriptorMatcher = DescriptorMatcher::create(matcher.toStdString()); + if (descriptorMatcher.empty()) + qFatal("Failed to create DescriptorMatcher: %s", qPrintable(matcher)); + } + + float compare(const Mat &a, const Mat &b) const + { + if ((a.rows < 2) || (b.rows < 2)) return 0; + + std::vector< std::vector > matches; + if (a.rows < b.rows) descriptorMatcher->knnMatch(a, b, matches, 2); + else descriptorMatcher->knnMatch(b, a, matches, 2); + + QList distances; + foreach (const std::vector &match, matches) { + if (match[0].distance / match[1].distance > maxRatio) continue; + distances.append(match[0].distance); + } + qSort(distances); + + float similarity = 0; + for (int i=0; i + +#include + +namespace br +{ + +/*! + * \ingroup distances + * \brief L1 distance computed using eigen. + * \author Josh Klontz \cite jklontz + */ +class L1Distance : public UntrainableDistance +{ + Q_OBJECT + + float compare(const cv::Mat &a, const cv::Mat &b) const + { + const int size = a.rows * a.cols; + Eigen::Map aMap((float*)a.data, size); + Eigen::Map bMap((float*)b.data, size); + return (aMap-bMap).cwiseAbs().sum(); + } +}; + +BR_REGISTER(Distance, L1Distance) + +} // namespace br + +#include "distance/L1.moc" diff --git a/openbr/plugins/distance/matchprobability.cpp b/openbr/plugins/distance/matchprobability.cpp new file mode 100644 index 0000000..e885151 --- /dev/null +++ b/openbr/plugins/distance/matchprobability.cpp @@ -0,0 +1,195 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#include + +namespace br +{ + +float KDEPointer(const QList *scores, double x, double h) +{ + return Common::KernelDensityEstimation(*scores, x, h); +} + +/* Kernel Density Estimator */ +struct KDE +{ + float min, max; + double mean, stddev; + QList bins; + + KDE() : min(0), max(1), mean(0), stddev(1) {} + + KDE(const QList &scores, bool trainKDE) + { + Common::MinMax(scores, &min, &max); + Common::MeanStdDev(scores, &mean, &stddev); + + if (!trainKDE) + return; + + double h = Common::KernelDensityBandwidth(scores); + const int size = 255; + bins.reserve(size); + + QFutureSynchronizer futures; + + for (int i=0; i < size; i++) + futures.addFuture(QtConcurrent::run(KDEPointer, &scores, min + (max-min)*i/(size-1), h)); + futures.waitForFinished(); + + foreach(const QFuture & future, futures.futures()) + bins.append(future.result()); + } + + float operator()(float score, bool gaussian = true) const + { + 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(); + const float y1 = bins[floor(x)]; + const float y2 = bins[ceil(x)]; + return y1 + (y2-y1)*(x-floor(x)); + } +}; + +QDataStream &operator<<(QDataStream &stream, const KDE &kde) +{ + return stream << kde.min << kde.max << kde.mean << kde.stddev << kde.bins; +} + +QDataStream &operator>>(QDataStream &stream, KDE &kde) +{ + return stream >> kde.min >> kde.max >> kde.mean >> kde.stddev >> kde.bins; +} + +/* Match Probability */ +struct MP +{ + KDE genuine, impostor; + MP() {} + MP(const QList &genuineScores, const QList &impostorScores, bool trainKDE) + : genuine(genuineScores, trainKDE), impostor(impostorScores, trainKDE) {} + float operator()(float score, bool gaussian = true) const + { + const float g = genuine(score, gaussian); + const float s = g / (impostor(score, gaussian) + g); + return s; + } +}; + +QDataStream &operator<<(QDataStream &stream, const MP &nmp) +{ + return stream << nmp.genuine << nmp.impostor; +} + +QDataStream &operator>>(QDataStream &stream, MP &nmp) +{ + return stream >> nmp.genuine >> nmp.impostor; +} + +/*! + * \ingroup distances + * \brief Match Probability \cite klare12 + * \author Josh Klontz \cite jklontz + */ +class MatchProbabilityDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + Q_PROPERTY(bool gaussian READ get_gaussian WRITE set_gaussian RESET reset_gaussian STORED false) + Q_PROPERTY(bool crossModality READ get_crossModality WRITE set_crossModality RESET reset_crossModality STORED false) + Q_PROPERTY(QString inputVariable READ get_inputVariable WRITE set_inputVariable RESET reset_inputVariable STORED false) + + MP mp; + + void train(const TemplateList &src) + { + distance->train(src); + + 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()); + 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; + if (labels[i] == labels[j]) genuineScores.append(score); + else impostorScores.append(score); + } + } + + mp = MP(genuineScores, impostorScores, !gaussian); + } + + float compare(const Template &target, const Template &query) const + { + return normalize(distance->compare(target, query)); + } + + float compare(const cv::Mat &target, const cv::Mat &query) const + { + return normalize(distance->compare(target, query)); + } + + float compare(const uchar *a, const uchar *b, size_t size) const + { + return normalize(distance->compare(a, b, size)); + } + + float normalize(float score) const + { + if (score == -std::numeric_limits::max()) return score; + if (!Globals->scoreNormalization) return -log(score+1); + return mp(score, gaussian); + } + + void store(QDataStream &stream) const + { + distance->store(stream); + stream << mp; + } + + void load(QDataStream &stream) + { + distance->load(stream); + stream >> mp; + } + +protected: + BR_PROPERTY(br::Distance*, distance, make("Dist(L2)")) + BR_PROPERTY(bool, gaussian, true) + BR_PROPERTY(bool, crossModality, false) + BR_PROPERTY(QString, inputVariable, "Label") +}; + +BR_REGISTER(Distance, MatchProbabilityDistance) + +} // namespace br + +#include "distance/matchprobability.moc" diff --git a/openbr/plugins/distance/metadata.cpp b/openbr/plugins/distance/metadata.cpp new file mode 100644 index 0000000..11660f7 --- /dev/null +++ b/openbr/plugins/distance/metadata.cpp @@ -0,0 +1,76 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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 new file mode 100644 index 0000000..ae92db2 --- /dev/null +++ b/openbr/plugins/distance/neglogplusone.cpp @@ -0,0 +1,58 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup distances + * \brief Returns -log(distance(a,b)+1) + * \author Josh Klontz \cite jklontz + */ +class NegativeLogPlusOneDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + BR_PROPERTY(br::Distance*, distance, NULL) + + void train(const TemplateList &src) + { + distance->train(src); + } + + float compare(const Template &a, const Template &b) const + { + return -log(distance->compare(a,b)+1); + } + + void store(QDataStream &stream) const + { + distance->store(stream); + } + + void load(QDataStream &stream) + { + distance->load(stream); + } +}; + +BR_REGISTER(Distance, NegativeLogPlusOneDistance) + +} // namespace br + +#include "distance/neglogplusone.moc" diff --git a/openbr/plugins/distance/online.cpp b/openbr/plugins/distance/online.cpp new file mode 100644 index 0000000..e683c0a --- /dev/null +++ b/openbr/plugins/distance/online.cpp @@ -0,0 +1,51 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup distances + * \brief Online distance metric to attenuate match scores across multiple frames + * \author Brendan klare \cite bklare + */ +class OnlineDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(br::Distance* distance READ get_distance WRITE set_distance RESET reset_distance STORED false) + Q_PROPERTY(float alpha READ get_alpha WRITE set_alpha RESET reset_alpha STORED false) + BR_PROPERTY(br::Distance*, distance, NULL) + BR_PROPERTY(float, alpha, 0.1f) + + mutable QHash scoreHash; + mutable QMutex mutex; + + float compare(const Template &target, const Template &query) const + { + float currentScore = distance->compare(target, query); + + QMutexLocker mutexLocker(&mutex); + return scoreHash[target.file.name] = (1.0- alpha) * scoreHash[target.file.name] + alpha * currentScore; + } +}; + +BR_REGISTER(Distance, OnlineDistance) + +} // namespace br + +#include "distance/online.moc" diff --git a/openbr/plugins/distance/pipe.cpp b/openbr/plugins/distance/pipe.cpp new file mode 100644 index 0000000..523868f --- /dev/null +++ b/openbr/plugins/distance/pipe.cpp @@ -0,0 +1,63 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup distances + * \brief Distances in series. + * \author Josh Klontz \cite jklontz + * + * The templates are compared using each br::Distance in order. + * If the result of the comparison with any given distance is -FLOAT_MAX then this result is returned early. + * Otherwise the returned result is the value of comparing the templates using the last br::Distance. + */ +class PipeDistance : public Distance +{ + Q_OBJECT + Q_PROPERTY(QList distances READ get_distances WRITE set_distances RESET reset_distances) + BR_PROPERTY(QList, distances, QList()) + + void train(const TemplateList &data) + { + QFutureSynchronizer futures; + foreach (br::Distance *distance, distances) + futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); + futures.waitForFinished(); + } + + float compare(const Template &a, const Template &b) const + { + float result = -std::numeric_limits::max(); + foreach (br::Distance *distance, distances) { + result = distance->compare(a, b); + if (result == -std::numeric_limits::max()) + return result; + } + return result; + } +}; + +BR_REGISTER(Distance, PipeDistance) + +} // namespace br + +#include "distance/pipe.moc" diff --git a/openbr/plugins/distance/reject.cpp b/openbr/plugins/distance/reject.cpp new file mode 100644 index 0000000..c183513 --- /dev/null +++ b/openbr/plugins/distance/reject.cpp @@ -0,0 +1,54 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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 new file mode 100644 index 0000000..07a9853 --- /dev/null +++ b/openbr/plugins/distance/sum.cpp @@ -0,0 +1,62 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup distances + * \brief Sum match scores across multiple distances + * \author Scott Klum \cite sklum + */ +class SumDistance : public UntrainableDistance +{ + Q_OBJECT + Q_PROPERTY(QList distances READ get_distances WRITE set_distances RESET reset_distances) + BR_PROPERTY(QList, distances, QList()) + + void train(const TemplateList &data) + { + QFutureSynchronizer futures; + foreach (br::Distance *distance, distances) + futures.addFuture(QtConcurrent::run(distance, &Distance::train, data)); + futures.waitForFinished(); + } + + float compare(const Template &target, const Template &query) const + { + float result = 0; + + foreach (br::Distance *distance, distances) { + result += distance->compare(target, query); + + if (result == -std::numeric_limits::max()) + return result; + } + + return result; + } +}; + +BR_REGISTER(Distance, SumDistance) + +} // namespace br + +#include "distance/sum.moc" diff --git a/openbr/plugins/distance/turk.cpp b/openbr/plugins/distance/turk.cpp new file mode 100644 index 0000000..def8326 --- /dev/null +++ b/openbr/plugins/distance/turk.cpp @@ -0,0 +1,65 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..f8f4b54 --- /dev/null +++ b/openbr/plugins/distance/unit.cpp @@ -0,0 +1,87 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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..ddaae48 --- /dev/null +++ b/openbr/plugins/distance/zscore.cpp @@ -0,0 +1,84 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +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/draw.cpp b/openbr/plugins/draw.cpp deleted file mode 100644 index 3dc6a9b..0000000 --- a/openbr/plugins/draw.cpp +++ /dev/null @@ -1,700 +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 -#include -#include -#include "openbr_internal.h" -#include "openbr/core/opencvutils.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Renders metadata onto the image. - * - * The inPlace argument controls whether or not the image is cloned before the metadata is drawn. - * - * \author Josh Klontz \cite jklontz - */ -class DrawTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(bool verbose READ get_verbose WRITE set_verbose RESET reset_verbose STORED false) - Q_PROPERTY(bool points READ get_points WRITE set_points RESET reset_points STORED false) - Q_PROPERTY(bool rects READ get_rects WRITE set_rects RESET reset_rects STORED false) - Q_PROPERTY(bool inPlace READ get_inPlace WRITE set_inPlace RESET reset_inPlace STORED false) - Q_PROPERTY(int lineThickness READ get_lineThickness WRITE set_lineThickness RESET reset_lineThickness STORED false) - Q_PROPERTY(bool named READ get_named WRITE set_named RESET reset_named STORED false) - Q_PROPERTY(bool location READ get_location WRITE set_location RESET reset_location STORED false) - BR_PROPERTY(bool, verbose, false) - BR_PROPERTY(bool, points, true) - BR_PROPERTY(bool, rects, true) - BR_PROPERTY(bool, inPlace, false) - BR_PROPERTY(int, lineThickness, 1) - BR_PROPERTY(bool, named, true) - BR_PROPERTY(bool, location, true) - - void project(const Template &src, Template &dst) const - { - const Scalar color(0,255,0); - const Scalar verboseColor(255, 255, 0); - dst.m() = inPlace ? src.m() : src.m().clone(); - - if (points) { - const QList pointsList = (named) ? OpenCVUtils::toPoints(src.file.points()+src.file.namedPoints()) : OpenCVUtils::toPoints(src.file.points()); - for (int i=0; i opener; - void project(const Template &src, Template &dst) const - { - dst = src; - - if (imgName.isEmpty() || targetName.isEmpty() || !dst.file.contains(imgName) || !dst.file.contains(targetName)) - return; - - QVariant temp = src.file.value(imgName); - cv::Mat im; - // is this a filename? - if (temp.canConvert()) { - QString im_name = temp.toString(); - Template temp_im; - opener->project(File(im_name), temp_im); - im = temp_im.m(); - } - // a cv::Mat ? - else if (temp.canConvert()) - im = src.file.get(imgName); - else - qDebug() << "Unrecognized property type " << imgName << "for" << src.file.name; - - // Location of detected face in source image - QRectF target_location = src.file.get(targetName); - - // match width with target region - qreal target_width = target_location.width(); - qreal current_width = im.cols; - qreal current_height = im.rows; - - qreal aspect_ratio = current_height / current_width; - qreal target_height = target_width * aspect_ratio; - - cv::resize(im, im, cv::Size(target_width, target_height)); - - // ROI used to maybe crop the matched image - cv::Rect clip_roi; - clip_roi.x = 0; - clip_roi.y = 0; - clip_roi.width = im.cols; - clip_roi.height= im.rows <= dst.m().rows ? im.rows : dst.m().rows; - - int half_width = src.m().cols / 2; - int out_x = 0; - - // place in the source image we will copy the matched image to. - cv::Rect target_roi; - bool left_side = false; - int width_adjust = 0; - // Place left - if (target_location.center().rx() > half_width) { - out_x = target_location.left() - im.cols; - if (out_x < 0) { - width_adjust = abs(out_x); - out_x = 0; - } - left_side = true; - } - // place right - else { - out_x = target_location.right(); - int high = out_x + im.cols; - if (high >= src.m().cols) { - width_adjust = abs(high - src.m().cols + 1); - } - } - - cv::Mat outIm; - if (width_adjust) - { - outIm.create(dst.m().rows, dst.m().cols + width_adjust, CV_8UC3); - memset(outIm.data, 127, outIm.rows * outIm.cols * outIm.channels()); - - Rect temp; - - if (left_side) - temp = Rect(abs(width_adjust), 0, dst.m().cols, dst.m().rows); - - else - temp = Rect(0, 0, dst.m().cols, dst.m().rows); - - dst.m().copyTo(outIm(temp)); - - } - else - outIm = dst.m(); - - if (clip_roi.height + target_location.top() >= outIm.rows) - { - clip_roi.height -= abs(outIm.rows - (clip_roi.height + target_location.top() )); - } - if (clip_roi.x + clip_roi.width >= im.cols) { - clip_roi.width -= abs(im.cols - (clip_roi.x + clip_roi.width + 1)); - if (clip_roi.width < 0) - clip_roi.width = 1; - } - - if (clip_roi.y + clip_roi.height >= im.rows) { - clip_roi.height -= abs(im.rows - (clip_roi.y + clip_roi.height + 1)); - } - if (clip_roi.x < 0) - clip_roi.x = 0; - if (clip_roi.y < 0) - clip_roi.y = 0; - - if (clip_roi.height < 0) - clip_roi.height = 0; - - if (clip_roi.width < 0) - clip_roi.width = 0; - - - if (clip_roi.y + clip_roi.height >= im.rows) - { - qDebug() << "Bad clip y" << clip_roi.y + clip_roi.height << im.rows; - } - if (clip_roi.x + clip_roi.width >= im.cols) - { - qDebug() << "Bad clip x" << clip_roi.x + clip_roi.width << im.cols; - } - - if (clip_roi.y < 0 || clip_roi.height < 0) - { - qDebug() << "bad clip y, low" << clip_roi.y << clip_roi.height; - qFatal("die"); - } - if (clip_roi.x < 0 || clip_roi.width < 0) - { - qDebug() << "bad clip x, low" << clip_roi.x << clip_roi.width; - qFatal("die"); - } - - target_roi.x = out_x; - target_roi.width = clip_roi.width; - target_roi.y = target_location.top(); - target_roi.height = clip_roi.height; - - - im = im(clip_roi); - - if (target_roi.x < 0 || target_roi.x >= outIm.cols) - { - qDebug() << "Bad xdim in targetROI!" << target_roi.x << " out im x: " << outIm.cols; - qFatal("die"); - } - - if (target_roi.x + target_roi.width < 0 || (target_roi.x + target_roi.width) >= outIm.cols) - { - qDebug() << "Bad xdim in targetROI!" << target_roi.x + target_roi.width; - qFatal("die"); - } - - if (target_roi.y < 0 || target_roi.y >= outIm.rows) - { - qDebug() << "Bad ydim in targetROI!" << target_roi.y; - qFatal("die"); - } - - if ((target_roi.y + target_roi.height) < 0 || (target_roi.y + target_roi.height) > outIm.rows) - { - qDebug() << "Bad ydim in targetROI!" << target_roi.y + target_roi.height; - qDebug() << "target_roi.y: " << target_roi.y << " height: " << target_roi.height; - qFatal("die"); - } - - - std::vector channels; - cv::split(outIm, channels); - - std::vector patch_channels; - cv::split(im, patch_channels); - - for (size_t i=0; i < channels.size(); i++) - { - cv::addWeighted(channels[i](target_roi), 0, patch_channels[i % patch_channels.size()], 1, 0,channels[i](target_roi)); - } - cv::merge(channels, outIm); - dst.m() = outIm; - - } - - void init() - { - opener = QSharedPointer(br::Transform::make("Cache(Open)", NULL)); - } - -}; - -BR_REGISTER(Transform, AdjacentOverlayTransform) - -/*! - * \ingroup transforms - * \brief Draw a line representing the direction and magnitude of optical flow at the specified points. - * \author Austin Blanton \cite imaus10 - */ -class DrawOpticalFlow : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(QString original READ get_original WRITE set_original RESET reset_original STORED false) - BR_PROPERTY(QString, original, "original") - - void project(const Template &src, Template &dst) const - { - const Scalar color(0,255,0); - Mat flow = src.m(); - dst = src; - if (!dst.file.contains(original)) qFatal("The original img must be saved in the metadata with SaveMat."); - dst.m() = dst.file.get(original); - dst.file.remove(original); - foreach (const Point2f &pt, OpenCVUtils::toPoints(dst.file.points())) { - Point2f dxy = flow.at(pt.y, pt.x); - Point2f newPt(pt.x+dxy.x, pt.y+dxy.y); - line(dst, pt, newPt, color); - } - } -}; -BR_REGISTER(Transform, DrawOpticalFlow) - -/*! - * \ingroup transforms - * \brief Fill in the segmentations or draw a line between intersecting segments. - * \author Austin Blanton \cite imaus10 - */ -class DrawSegmentation : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(bool fillSegment READ get_fillSegment WRITE set_fillSegment RESET reset_fillSegment STORED false) - BR_PROPERTY(bool, fillSegment, true) - - void project(const Template &src, Template &dst) const - { - if (!src.file.contains("SegmentsMask") || !src.file.contains("NumSegments")) qFatal("Must supply a Contours object in the metadata to drawContours."); - Mat segments = src.file.get("SegmentsMask"); - int numSegments = src.file.get("NumSegments"); - - dst.file = src.file; - Mat drawn = fillSegment ? Mat(segments.size(), CV_8UC3, Scalar::all(0)) : src.m(); - - for (int i=1; i > contours; - Scalar color(0,255,0); - findContours(mask, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE); - drawContours(drawn, contours, -1, color); - } - } - - dst.m() = drawn; - } -}; -BR_REGISTER(Transform, DrawSegmentation) - -/*! - * \ingroup transforms - * \brief Write all mats to disk as images. - * \author Brendan Klare \cite bklare - */ -class WriteImageTransform : public TimeVaryingTransform -{ - Q_OBJECT - Q_PROPERTY(QString outputDirectory READ get_outputDirectory WRITE set_outputDirectory RESET reset_outputDirectory STORED false) - Q_PROPERTY(QString imageName READ get_imageName WRITE set_imageName RESET reset_imageName STORED false) - Q_PROPERTY(QString imgExtension READ get_imgExtension WRITE set_imgExtension RESET reset_imgExtension STORED false) - BR_PROPERTY(QString, outputDirectory, "Temp") - BR_PROPERTY(QString, imageName, "image") - BR_PROPERTY(QString, imgExtension, "jpg") - - int cnt; - - void init() { - cnt = 0; - if (! QDir(outputDirectory).exists()) - QDir().mkdir(outputDirectory); - } - - void projectUpdate(const Template &src, Template &dst) - { - dst = src; - OpenCVUtils::saveImage(dst.m(), QString("%1/%2_%3.%4").arg(outputDirectory).arg(imageName).arg(cnt++, 5, 10, QChar('0')).arg(imgExtension)); - } - -}; -BR_REGISTER(Transform, WriteImageTransform) - - -/** - * @brief The MeanImageTransform class computes the average template/image - * and save the result as an encoded image. - */ -class MeanImageTransform : public TimeVaryingTransform -{ - Q_OBJECT - - Q_PROPERTY(QString imgname READ get_imgname WRITE set_imgname RESET reset_imgname STORED false) - Q_PROPERTY(QString ext READ get_ext WRITE set_ext RESET reset_ext STORED false) - - BR_PROPERTY(QString, imgname, "average") - BR_PROPERTY(QString, ext, "jpg") - - Mat average; - int cnt; - - void init() - { - cnt = 0; - } - - void projectUpdate(const Template &src, Template &dst) - { - dst = src; - if (cnt == 0) { - if (src.m().channels() == 1) - average = Mat::zeros(dst.m().size(),CV_64FC1); - else if (src.m().channels() == 3) - average = Mat::zeros(dst.m().size(),CV_64FC3); - else - qFatal("Unsupported number of channels"); - } - - Mat temp; - if (src.m().channels() == 1) { - src.m().convertTo(temp, CV_64FC1); - average += temp; - } else if (src.m().channels() == 3) { - src.m().convertTo(temp, CV_64FC3); - average += temp; - } else - qFatal("Unsupported number of channels"); - - cnt++; - } - - virtual void finalize(TemplateList &output) - { - average /= float(cnt); - imwrite(QString("%1.%2").arg(imgname).arg(ext).toStdString(), average); - output = TemplateList(); - } - - -public: - MeanImageTransform() : TimeVaryingTransform(false, false) {} -}; - -BR_REGISTER(Transform, MeanImageTransform) - - -// TODO: re-implement EditTransform using Qt -#if 0 -/*! - * \ingroup transforms - * \brief Remove landmarks. - * \author Josh Klontz \cite jklontz - */ -class EditTransform : public UntrainableTransform -{ - Q_OBJECT - - Transform *draw; - static Template currentTemplate; - static QMutex currentTemplateLock; - - void init() - { - draw = make("Draw"); - Globals->setProperty("parallelism", "0"); // Can only work in single threaded mode - } - - void project(const Template &src, Template &dst) const - { - dst = src; - - if (Globals->parallelism) { - qWarning("Edit::project() only works in single threaded mode."); - return; - } - - currentTemplateLock.lock(); - currentTemplate = src; - OpenCVUtils::showImage(src, "Edit", false); - setMouseCallback("Edit", mouseCallback, (void*)this); - mouseEvent(0, 0, 0, 0); - waitKey(-1); - dst = currentTemplate; - currentTemplateLock.unlock(); - } - - static void mouseCallback(int event, int x, int y, int flags, void *userdata) - { - ((const EditTransform*)userdata)->mouseEvent(event, x, y, flags); - } - - void mouseEvent(int event, int x, int y, int flags) const - { - (void) event; - if (flags) { - QList rects = currentTemplate.file.rects(); - for (int i=rects.size()-1; i>=0; i--) - if (rects[i].contains(x,y)) - rects.removeAt(i); - currentTemplate.file.setRects(rects); - } - - Template temp; - draw->project(currentTemplate, temp); - OpenCVUtils::showImage(temp, "Edit", false); - } -}; - -Template EditTransform::currentTemplate; -QMutex EditTransform::currentTemplateLock; - -BR_REGISTER(Transform, EditTransform) -#endif - -} // namespace br - -#include "draw.moc" diff --git a/openbr/plugins/eigen3.cmake b/openbr/plugins/eigen3.cmake deleted file mode 100644 index ed6a3e2..0000000 --- a/openbr/plugins/eigen3.cmake +++ /dev/null @@ -1,7 +0,0 @@ -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) -endif() diff --git a/openbr/plugins/filter.cpp b/openbr/plugins/filter.cpp deleted file mode 100644 index 48fbe75..0000000 --- a/openbr/plugins/filter.cpp +++ /dev/null @@ -1,258 +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 "openbr_internal.h" -#include "openbr/core/tanh_sse.h" - -using namespace cv; - -namespace br -{ - -/*! - * \ingroup transforms - * \brief Gamma correction - * \author Josh Klontz \cite jklontz - */ -class GammaTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float gamma READ get_gamma WRITE set_gamma RESET reset_gamma STORED false) - BR_PROPERTY(float, gamma, 0.2) - - Mat lut; - - void init() - { - lut.create(256, 1, CV_32FC1); - if (gamma == 0) for (int i=0; i<256; i++) lut.at(i,0) = log((float)i); - else for (int i=0; i<256; i++) lut.at(i,0) = pow(i, gamma); - } - - void project(const Template &src, Template &dst) const - { - if (src.m().depth() == CV_8U) LUT(src, lut, dst); - else pow(src, gamma, dst); - } -}; - -BR_REGISTER(Transform, GammaTransform) - -/*! - * \ingroup transforms - * \brief Gaussian blur - * \author Josh Klontz \cite jklontz - */ -class BlurTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float sigma READ get_sigma WRITE set_sigma RESET reset_sigma STORED false) - Q_PROPERTY(bool ROI READ get_ROI WRITE set_ROI RESET reset_ROI STORED false) - BR_PROPERTY(float, sigma, 1) - BR_PROPERTY(bool, ROI, false) - - void project(const Template &src, Template &dst) const - { - if (!ROI) GaussianBlur(src, dst, Size(0,0), sigma); - else { - dst.m() = src.m(); - foreach (const QRectF &rect, src.file.rects()) { - Rect region(rect.x(), rect.y(), rect.width(), rect.height()); - Mat input = dst.m(); - Mat output = input.clone(); - GaussianBlur(input(region), output(region), Size(0,0), sigma); - dst.m() = output; - } - } - } -}; - -BR_REGISTER(Transform, BlurTransform) - -/*! - * \ingroup transforms - * \brief Difference of gaussians - * \author Josh Klontz \cite jklontz - */ -class DoGTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float sigma0 READ get_sigma0 WRITE set_sigma0 RESET reset_sigma0 STORED false) - Q_PROPERTY(float sigma1 READ get_sigma1 WRITE set_sigma1 RESET reset_sigma1 STORED false) - BR_PROPERTY(float, sigma0, 1) - BR_PROPERTY(float, sigma1, 2) - - Size ksize0, ksize1; - - static Size getKernelSize(double sigma) - { - // Inverts OpenCV's conversion from kernel size to sigma: - // sigma = ((ksize-1)*0.5 - 1)*0.3 + 0.8 - // See documentation for cv::getGaussianKernel() - int ksize = ((sigma - 0.8) / 0.3 + 1) * 2 + 1; - if (ksize % 2 == 0) ksize++; - return Size(ksize, ksize); - } - - void init() - { - ksize0 = getKernelSize(sigma0); - ksize1 = getKernelSize(sigma1); - } - - void project(const Template &src, Template &dst) const - { - Mat g0, g1; - GaussianBlur(src, g0, ksize0, 0); - GaussianBlur(src, g1, ksize1, 0); - subtract(g0, g1, dst); - } -}; - -BR_REGISTER(Transform, DoGTransform) - -/*! - * \ingroup transforms - * \brief Meyers, E.; Wolf, L. - * “Using biologically inspired features for face processing,” - * Int. Journal of Computer Vision, vol. 76, no. 1, pp 93–104, 2008. - * \author Scott Klum \cite sklum - */ - -class CSDNTransform : public UntrainableTransform -{ - Q_OBJECT - - Q_PROPERTY(float s READ get_s WRITE set_s RESET reset_s STORED false) - BR_PROPERTY(int, s, 16) - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() != 1) qFatal("Expected single channel source matrix."); - - const int nRows = src.m().rows; - const int nCols = src.m().cols; - - Mat m; - src.m().convertTo(m, CV_32FC1); - - const int surround = s/2; - - for ( int i = 0; i < nRows; i++ ) { - for ( int j = 0; j < nCols; j++ ) { - int width = min( j+surround, nCols ) - max( 0, j-surround ); - int height = min( i+surround, nRows ) - max( 0, i-surround ); - - Rect_ ROI(max(0, j-surround), max(0, i-surround), width, height); - - Scalar_ avg = mean(m(ROI)); - - m.at(i,j) = m.at(i,j) - avg[0]; - } - } - - dst = m; - - } -}; - -BR_REGISTER(Transform, CSDNTransform) - -/*! - * \ingroup transforms - * \brief Xiaoyang Tan; Triggs, B.; - * "Enhanced Local Texture Feature Sets for Face Recognition Under Difficult Lighting Conditions," - * Image Processing, IEEE Transactions on , vol.19, no.6, pp.1635-1650, June 2010 - * \author Josh Klontz \cite jklontz - */ -class ContrastEqTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float a READ get_a WRITE set_a RESET reset_a STORED false) - Q_PROPERTY(float t READ get_t WRITE set_t RESET reset_t STORED false) - BR_PROPERTY(float, a, 1) - BR_PROPERTY(float, t, 0.1) - - void project(const Template &src, Template &dst) const - { - if (src.m().channels() != 1) qFatal("Expected single channel source matrix."); - - // Stage 1 - Mat stage1; - { - Mat abs_dst; - absdiff(src, Scalar(0), abs_dst); - Mat pow_dst; - pow(abs_dst, a, pow_dst); - float denominator = pow((float)mean(pow_dst)[0], 1.f/a); - src.m().convertTo(stage1, CV_32F, 1/denominator); - } - - // Stage 2 - Mat stage2; - { - Mat abs_dst; - absdiff(stage1, Scalar(0), abs_dst); - Mat min_dst; - min(abs_dst, t, min_dst); - Mat pow_dst; - pow(min_dst, a, pow_dst); - float denominator = pow((float)mean(pow_dst)[0], 1.f/a); - stage1.convertTo(stage2, CV_32F, 1/denominator); - } - - // Hyperbolic tangent - const int nRows = src.m().rows; - const int nCols = src.m().cols; - const float* p = (const float*)stage2.ptr(); - Mat m(nRows, nCols, CV_32FC1); - for (int i=0; i(i, j) = fast_tanh(p[i*nCols+j]); - // TODO: m.at(i, j) = t * fast_tanh(p[i*nCols+j] / t); - - dst = m; - } -}; - -BR_REGISTER(Transform, ContrastEqTransform) - -/*! - * \ingroup transforms - * \brief Raise each element to the specified power. - * \author Josh Klontz \cite jklontz - */ -class PowTransform : public UntrainableTransform -{ - Q_OBJECT - Q_PROPERTY(float power READ get_power WRITE set_power RESET reset_power STORED false) - Q_PROPERTY(bool preserveSign READ get_preserveSign WRITE set_preserveSign RESET reset_preserveSign STORED false) - BR_PROPERTY(float, power, 2) - BR_PROPERTY(bool, preserveSign, false) - - void project(const Template &src, Template &dst) const - { - pow(preserveSign ? abs(src) : src.m(), power, dst); - if (preserveSign) subtract(Scalar::all(0), dst, dst, src.m() < 0); - } -}; - -BR_REGISTER(Transform, PowTransform) - -} // namespace br - -#include "filter.moc" diff --git a/openbr/plugins/format.cpp b/openbr/plugins/format.cpp deleted file mode 100644 index 48eed7d..0000000 --- a/openbr/plugins/format.cpp +++ /dev/null @@ -1,975 +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 -#include -#ifndef BR_EMBEDDED -#include -#endif // BR_EMBEDDED -#include -#include - -#include -#include -#include "openbr_internal.h" - -#include "openbr/core/bee.h" -#include "openbr/core/opencvutils.h" -#include "openbr/core/qtutils.h" - -using namespace cv; - -namespace br -{ - - /*! - * \ingroup formats - * \brief Read all frames of a video using OpenCV - * \author Charles Otto \cite caotto - */ -class videoFormat : public Format -{ - Q_OBJECT - -public: - Template read() const - { - if (!file.exists() ) - return Template(); - - VideoCapture videoSource(file.name.toStdString()); - videoSource.open(file.name.toStdString() ); - - - Template frames; - if (!videoSource.isOpened()) { - qWarning("video file open failed"); - return frames; - } - - bool open = true; - while (open) { - cv::Mat frame; - open = videoSource.read(frame); - if (!open) break; - - frames.append(cv::Mat()); - frames.back() = frame.clone(); - } - - return frames; - } - - void write(const Template &t) const - { - int fourcc = OpenCVUtils::getFourcc(); - VideoWriter videoSink(file.name.toStdString(), fourcc, 30, t.begin()->size()); - - // Did we successfully open the output file? - if (!videoSink.isOpened() ) qFatal("Failed to open output file"); - - for (Template::const_iterator it = t.begin(); it!= t.end(); ++it) { - videoSink << *it; - } - } -}; - -BR_REGISTER(Format, videoFormat) - -/*! - * \ingroup formats - * \brief A simple binary matrix format. - * \author Josh Klontz \cite jklontz - * First 4 bytes indicate the number of rows. - * Second 4 bytes indicate the number of columns. - * The rest of the bytes are 32-bit floating data elements in row-major order. - */ -class binFormat : public Format -{ - Q_OBJECT - Q_PROPERTY(bool raw READ get_raw WRITE set_raw RESET reset_raw STORED false) - BR_PROPERTY(bool, raw, false) - - Template read() const - { - QByteArray data; - QtUtils::readFile(file, data); - if (raw) { - return Template(file, Mat(1, data.size(), CV_8UC1, data.data()).clone()); - } else { - return Template(file, Mat(((quint32*)data.data())[0], - ((quint32*)data.data())[1], - CV_32FC1, - data.data()+8).clone()); - } - } - - void write(const Template &t) const - { - QFile f(file); - QtUtils::touchDir(f); - if (!f.open(QFile::WriteOnly)) - qFatal("Failed to open %s for writing.", qPrintable(file)); - - Mat m; - if (!raw) { - if (t.m().type() != CV_32FC1) - t.m().convertTo(m, CV_32F); - else m = t.m(); - - if (m.channels() != 1) qFatal("Only supports single channel matrices."); - - f.write((const char *) &m.rows, 4); - f.write((const char *) &m.cols, 4); - } - else m = t.m(); - - qint64 rowSize = m.cols * sizeof(float); - for (int i=0; i < m.rows; i++) - { - f.write((const char *) m.row(i).data, rowSize); - } - f.close(); - } -}; - -BR_REGISTER(Format, binFormat) - -/*! - * \ingroup formats - * \brief Reads a comma separated value file. - * \author Josh Klontz \cite jklontz - */ -class csvFormat : public Format -{ - Q_OBJECT - - Template read() const - { - QFile f(file.name); - f.open(QFile::ReadOnly); - QStringList lines(QString(f.readAll()).split(QRegularExpression("[\n|\r\n|\r]"), QString::SkipEmptyParts)); - f.close(); - - bool isUChar = true; - QList< QList > valsList; - foreach (const QString &line, lines) { - QList vals; - foreach (const QString &word, line.split(QRegExp(" *, *"), QString::SkipEmptyParts)) { - bool ok; - const float val = word.toFloat(&ok); - vals.append(val); - isUChar = isUChar && (val == float(uchar(val))); - } - if (!vals.isEmpty()) - valsList.append(vals); - } - - Mat m(valsList.size(), valsList[0].size(), CV_32FC1); - for (int i=0; i(i,j) = valsList[i][j]; - - if (isUChar) m.convertTo(m, CV_8U); - return Template(m); - } - - void write(const Template &t) const - { - const Mat &m = t.m(); - if (t.size() != 1) qFatal("Only supports single matrix templates."); - if (m.channels() != 1) qFatal("Only supports single channel matrices."); - - QStringList lines; lines.reserve(m.rows); - for (int r=0; r::names().contains("url")) { - File urlFile = file; - urlFile.name.append(".url"); - QScopedPointer url(Factory::make(urlFile)); - t = url->read(); - } - } else { - Mat m = imread(file.resolved().toStdString()); - if (m.data) { - t.append(m); - } else { - videoFormat videoReader; - videoReader.file = file; - t = videoReader.read(); - } - } - - return t; - } - - void write(const Template &t) const - { - if (t.size() > 1) { - videoFormat videoWriter; - videoWriter.file = file; - videoWriter.write(t); - } else if (t.size() == 1) { - QtUtils::touchDir(QDir(file.path())); - imwrite(file.name.toStdString(), t); - } - } -}; - -BR_REGISTER(Format, DefaultFormat) - -/*! - * \ingroup formats - * \brief Reads a NIST LFFS file. - * \author Josh Klontz \cite jklontz - */ -class lffsFormat : public Format -{ - Q_OBJECT - - Template read() const - { - QByteArray byteArray; - QtUtils::readFile(file.name, byteArray); - return Mat(1, byteArray.size(), CV_8UC1, byteArray.data()).clone(); - } - - void write(const Template &t) const - { - QByteArray byteArray((const char*)t.m().data, t.m().total()*t.m().elemSize()); - QtUtils::writeFile(file.name, byteArray); - } -}; - -BR_REGISTER(Format, lffsFormat) - -/*! - * \ingroup formats - * \brief Reads a NIST BEE similarity matrix. - * \author Josh Klontz \cite jklontz - */ -class mtxFormat : public Format -{ - Q_OBJECT - - Template read() const - { - QString target, query; - Template result = BEE::readMatrix(file, &target, &query); - result.file.set("Target", target); - result.file.set("Query", query); - return result; - } - - void write(const Template &t) const - { - BEE::writeMatrix(t, file); - } -}; - -BR_REGISTER(Format, mtxFormat) - -/*! - * \ingroup formats - * \brief Reads a NIST BEE mask matrix. - * \author Josh Klontz \cite jklontz - */ -class maskFormat : public mtxFormat -{ - Q_OBJECT -}; - -BR_REGISTER(Format, maskFormat) - -/*! - * \ingroup formats - * \brief MATLAB .mat format. - * \author Josh Klontz \cite jklontz - * http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf - * \note matFormat is known not to work with compressed matrices - */ -class matFormat : public Format -{ - Q_OBJECT - - struct Element - { - // It is always best to cast integers to a Qt integer type, such as qint16 or quint32, when reading and writing. - // This ensures that you always know exactly what size integers you are reading and writing, no matter what the - // underlying platform and architecture the application happens to be running on. - // http://qt-project.org/doc/qt-4.8/datastreamformat.html - quint32 type, bytes; - QByteArray data; - Element() : type(0), bytes(0) {} - Element(QDataStream &stream) - : type(0), bytes(0) - { - // Read first 4 bytes into type (32 bit integer), - // specifying the type of data used - if (stream.readRawData((char*)&type, 4) != 4) - qFatal("Unexpected end of file."); - - if (type >= 1 << 16) { - // Small data format - bytes = type; - type = type & 0x0000FFFF; - bytes = bytes >> 16; - } else { - // Regular format - // Read 4 bytes into bytes (32 bit integer), - // specifying the size of the element - if (stream.readRawData((char*)&bytes, 4) != 4) - qFatal("Unexpected end of file."); - } - - // Set the size of data to bytes - data.resize(bytes); - - // Read bytes amount of data from the file into data - if (int(bytes) != stream.readRawData(data.data(), bytes)) - qFatal("Unexpected end of file."); - - // Alignment - int skipBytes = (bytes < 4) ? (4 - bytes) : (8 - bytes%8)%8; - if (skipBytes != 0) stream.skipRawData(skipBytes); - } - }; - - Template read() const - { - QByteArray byteArray; - QtUtils::readFile(file, byteArray); - QDataStream f(byteArray); - - { // Check header - QByteArray header(128, 0); - f.readRawData(header.data(), 128); - if (!header.startsWith("MATLAB 5.0 MAT-file")) - qFatal("Invalid MAT header."); - } - - Template t(file); - - while (!f.atEnd()) { - Element element(f); - - // miCOMPRESSED - if (element.type == 15) { - // Prepend the number of bytes to element.data - element.data.prepend((char*)&element.bytes, 4); // Qt zlib wrapper requires this to preallocate the buffer - QDataStream uncompressed(qUncompress(element.data)); - element = Element(uncompressed); - } - - // miMATRIX - if (element.type == 14) { - QDataStream matrix(element.data); - qint32 rows = 0, columns = 0; - int matrixType = 0; - QByteArray matrixData; - while (!matrix.atEnd()) { - Element subelement(matrix); - if (subelement.type == 5) { // Dimensions array - if (subelement.bytes == 8) { - rows = ((qint32*)subelement.data.data())[0]; - columns = ((qint32*)subelement.data.data())[1]; - } else { - qWarning("matFormat::read can only handle 2D arrays."); - } - } else if (subelement.type == 7) { //miSINGLE - matrixType = CV_32FC1; - matrixData = subelement.data; - } else if (subelement.type == 9) { //miDOUBLE - matrixType = CV_64FC1; - matrixData = subelement.data; - } - } - - if ((rows > 0) && (columns > 0) && (matrixType != 0) && !matrixData.isEmpty()) { - Mat transposed; - transpose(Mat(columns, rows, matrixType, matrixData.data()), transposed); - t.append(transposed); - } - } - } - - return t; - } - - void write(const Template &t) const - { - QByteArray data; - QDataStream stream(&data, QFile::WriteOnly); - - { // Header - QByteArray header = "MATLAB 5.0 MAT-file; Made with OpenBR | www.openbiometrics.org\n"; - QByteArray buffer(116-header.size(), 0); - stream.writeRawData(header.data(), header.size()); - stream.writeRawData(buffer.data(), buffer.size()); - quint64 subsystem = 0; - quint16 version = 0x0100; - const char *endianness = "IM"; - stream.writeRawData((const char*)&subsystem, 8); - stream.writeRawData((const char*)&version, 2); - stream.writeRawData(endianness, 2); - } - - for (int i=0; i > imageSizes; // QHash > - - Template read() const - { - QString path = file.path(); - if (!imageSizes.contains(path)) { - static QMutex mutex; - QMutexLocker locker(&mutex); - - if (!imageSizes.contains(path)) { - const QString imageSize = path+"/ImageSize.txt"; - QStringList lines; - if (QFileInfo(imageSize).exists()) { - lines = QtUtils::readLines(imageSize); - lines.removeFirst(); // Remove header - } - - QHash sizes; - QRegExp whiteSpace("\\s+"); - foreach (const QString &line, lines) { - QStringList words = line.split(whiteSpace); - if (words.size() != 3) continue; - sizes.insert(words[0], QSize(words[2].toInt(), words[1].toInt())); - } - - imageSizes.insert(path, sizes); - } - } - - QByteArray data; - QtUtils::readFile(file, data); - - QSize size = imageSizes[path][file.baseName()]; - if (!size.isValid()) size = QSize(800,768); - if (data.size() != size.width() * size.height()) - qFatal("Expected %d*%d bytes, got %d.", size.height(), size.width(), data.size()); - return Template(file, Mat(size.height(), size.width(), CV_8UC1, data.data()).clone()); - } - - void write(const Template &t) const - { - QtUtils::writeFile(file, QByteArray().setRawData((const char*)t.m().data, t.m().total() * t.m().elemSize())); - } -}; - -QHash > rawFormat::imageSizes; - -BR_REGISTER(Format, rawFormat) - -/*! - * \ingroup formats - * \brief Retrieves an image from a webcam. - * \author Josh Klontz \cite jklontz - */ -class webcamFormat : public Format -{ - Q_OBJECT - - Template read() const - { - static QScopedPointer videoCapture; - - if (videoCapture.isNull()) - videoCapture.reset(new VideoCapture(0)); - - Mat m; - videoCapture->read(m); - return Template(m); - } - - void write(const Template &t) const - { - (void) t; - qFatal("Not supported."); - } -}; - -BR_REGISTER(Format, webcamFormat) - -/*! - * \ingroup formats - * \brief Decodes images from Base64 xml - * \author Scott Klum \cite sklum - * \author Josh Klontz \cite jklontz - */ -class xmlFormat : public Format -{ - Q_OBJECT - - Template read() const - { - Template t; - -#ifndef BR_EMBEDDED - QString fileName = file.get("path") + file.name; - - QDomDocument doc(fileName); - QFile f(fileName); - - if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat())); - if (!doc.setContent(&f)) qWarning("Unable to parse %s.", qPrintable(file.flat())); - f.close(); - - QDomElement docElem = doc.documentElement(); - QDomNode subject = docElem.firstChild(); - while (!subject.isNull()) { - QDomNode fileNode = subject.firstChild(); - - while (!fileNode.isNull()) { - QDomElement e = fileNode.toElement(); - - if (e.tagName() == "FORMAL_IMG") { - QByteArray byteArray = QByteArray::fromBase64(qPrintable(e.text())); - Mat m = imdecode(Mat(3, byteArray.size(), CV_8UC3, byteArray.data()), CV_LOAD_IMAGE_COLOR); - if (!m.data) qWarning("xmlFormat::read failed to decode image data."); - t.append(m); - } else if ((e.tagName() == "RELEASE_IMG") || - (e.tagName() == "PREBOOK_IMG") || - (e.tagName() == "LPROFILE") || - (e.tagName() == "RPROFILE")) { - // Ignore these other image fields for now - } else { - t.file.set(e.tagName(), e.text()); - } - - fileNode = fileNode.nextSibling(); - } - subject = subject.nextSibling(); - } - - // Calculate age - if (t.file.contains("DOB")) { - const QDate dob = QDate::fromString(t.file.get("DOB").left(10), "yyyy-MM-dd"); - const QDate current = QDate::currentDate(); - int age = current.year() - dob.year(); - if (current.month() < dob.month()) age--; - t.file.set("Age", age); - } -#endif // BR_EMBEDDED - - return t; - } - - void write(const Template &t) const - { - QStringList lines; - lines.append(""); - lines.append(""); - lines.append("\t"); - foreach (const QString &key, t.file.localKeys()) { - if ((key == "Index") || (key == "Label")) continue; - lines.append("\t\t<"+key+">"+QtUtils::toString(t.file.value(key))+""); - } - std::vector data; - imencode(".jpg",t.m(),data); - QByteArray byteArray = QByteArray::fromRawData((const char*)data.data(), data.size()); - lines.append("\t\t"+byteArray.toBase64()+""); - lines.append("\t"); - lines.append(""); - QtUtils::writeFile(file, lines); - } -}; - -BR_REGISTER(Format, xmlFormat) - -/*! - * \ingroup formats - * \brief Reads in scores or ground truth from a text table. - * \author Josh Klontz \cite jklontz - * - * Example of the format: - * \code - * 2.2531514 FALSE 99990377 99990164 - * 2.2549822 TRUE 99990101 99990101 - * \endcode - */ -class scoresFormat : public Format -{ - Q_OBJECT - Q_PROPERTY(int column READ get_column WRITE set_column RESET reset_column STORED false) - Q_PROPERTY(bool groundTruth READ get_groundTruth WRITE set_groundTruth RESET reset_groundTruth STORED false) - Q_PROPERTY(QString delimiter READ get_delimiter WRITE set_delimiter RESET reset_delimiter STORED false) - BR_PROPERTY(int, column, 0) - BR_PROPERTY(bool, groundTruth, false) - BR_PROPERTY(QString, delimiter, "\t") - - Template read() const - { - QFile f(file.name); - if (!f.open(QFile::ReadOnly | QFile::Text)) - qFatal("Failed to open %s for reading.", qPrintable(f.fileName())); - QList values; - while (!f.atEnd()) { - const QStringList words = QString(f.readLine()).split(delimiter); - if (words.size() <= column) qFatal("Expected file to have at least %d columns.", column+1); - const QString &word = words[column]; - bool ok; - float value = word.toFloat(&ok); - if (!ok) value = (QtUtils::toBool(word) ? BEE::Match : BEE::NonMatch); - values.append(value); - } - if (values.size() == 1) - qWarning("Only one value read, double check file line endings."); - Mat result = OpenCVUtils::toMat(values); - if (groundTruth) result.convertTo(result, CV_8U); - return result; - } - - void write(const Template &t) const - { - (void) t; - qFatal("Not implemented."); - } -}; - -BR_REGISTER(Format, scoresFormat) - -/*! - * \ingroup formats - * \brief Reads FBI EBTS transactions. - * \author Scott Klum \cite sklum - * https://www.fbibiospecs.org/ebts.html - */ -class ebtsFormat : public Format -{ - Q_OBJECT - - struct Field { - int type; - QList data; - }; - - struct Record { - int type; - quint32 bytes; - int position; // Starting position of record - - QHash > fields; - }; - - quint32 recordBytes(const QByteArray &byteArray, const float recordType, int from) const - { - bool ok; - quint32 size; - - if (recordType == 4 || recordType == 7) { - // read first four bytes - ok = true; - size = qFromBigEndian((const uchar*)byteArray.mid(from,4).constData()); - } else { - int index = byteArray.indexOf(QChar(0x1D), from); - size = byteArray.mid(from, index-from).split(':').last().toInt(&ok); - } - - return ok ? size : -1; - } - - void parseRecord(const QByteArray &byteArray, Record &record) const - { - if (record.type == 4 || record.type == 7) { - // Just a binary blob - // Read everything after the first four bytes - // Not current supported - } else { - // Continue reading fields until we get all the data - unsigned int position = record.position; - while (position < record.position + record.bytes) { - int index = byteArray.indexOf(QChar(0x1D), position); - Field field = parseField(byteArray.mid(position, index-position),QChar(0x1F)); - if (field.type == 999 ) { - // Data begin after the field identifier and the colon - int dataBegin = byteArray.indexOf(':', position)+1; - field.data.clear(); - field.data.append(byteArray.mid(dataBegin, record.bytes-(dataBegin-record.position))); - - // Data fields are always last in the record - record.fields.insert(field.type,field.data); - break; - } - // Advance the position accounting for the separator - position += index-position+1; - record.fields.insert(field.type,field.data); - } - } - } - - Field parseField(const QByteArray &byteArray, const QChar &sep) const - { - bool ok; - Field f; - - QList data = byteArray.split(':'); - - f.type = data.first().split('.').last().toInt(&ok); - f.data = data.last().split(sep.toLatin1()); - - return f; - } - - Template read() const - { - QByteArray byteArray; - QtUtils::readFile(file, byteArray); - - Template t; - - Mat m; - - QList records; - - // Read the type one record (every EBTS file will have one of these) - Record r1; - r1.type = 1; - r1.position = 0; - r1.bytes = recordBytes(byteArray,r1.type,r1.position); - - // The fields in a type 1 record are strictly defined - QList data = byteArray.mid(r1.position,r1.bytes).split(QChar(0x1D).toLatin1()); - foreach (const QByteArray &datum, data) { - Field f = parseField(datum,QChar(0x1F)); - r1.fields.insert(f.type,f.data); - } - - records.append(r1); - - // Read the type two record (every EBTS file will have one of these) - Record r2; - r2.type = 2; - r2.position = r1.bytes; - r2.bytes = recordBytes(byteArray,r2.type,r2.position); - - // The fields in a type 2 record are strictly defined - data = byteArray.mid(r2.position,r2.bytes).split(QChar(0x1D).toLatin1()); - foreach (const QByteArray &datum, data) { - Field f = parseField(datum,QChar(0x1F)); - r2.fields.insert(f.type,f.data); - } - - // Demographics - if (r2.fields.contains(18)) { - QString name = r2.fields.value(18).first(); - QStringList names = name.split(','); - t.file.set("FIRSTNAME", names.at(1)); - t.file.set("LASTNAME", names.at(0)); - } - - if (r2.fields.contains(22)) t.file.set("DOB", r2.fields.value(22).first().toInt()); - if (r2.fields.contains(24)) t.file.set("GENDER", QString(r2.fields.value(24).first())); - if (r2.fields.contains(25)) t.file.set("RACE", QString(r2.fields.value(25).first())); - - if (t.file.contains("DOB")) { - const QDate dob = QDate::fromString(t.file.get("DOB"), "yyyyMMdd"); - const QDate current = QDate::currentDate(); - int age = current.year() - dob.year(); - if (current.month() < dob.month()) age--; - t.file.set("Age", age); - } - - records.append(r2); - - // The third field of the first record contains informations about all the remaining records in the transaction - // We don't care about the first two and the final items - QList recordTypes = r1.fields.value(3); - for (int i=2; i frontalIdxs; - int position = r1.bytes + r2.bytes; - for (int i=2; i +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief A simple binary matrix format. + * \author Josh Klontz \cite jklontz + * First 4 bytes indicate the number of rows. + * Second 4 bytes indicate the number of columns. + * The rest of the bytes are 32-bit floating data elements in row-major order. + */ +class binaryFormat : public Format +{ + Q_OBJECT + Q_PROPERTY(bool raw READ get_raw WRITE set_raw RESET reset_raw STORED false) + BR_PROPERTY(bool, raw, false) + + Template read() const + { + QByteArray data; + QtUtils::readFile(file, data); + if (raw) { + return Template(file, Mat(1, data.size(), CV_8UC1, data.data()).clone()); + } else { + return Template(file, Mat(((quint32*)data.data())[0], + ((quint32*)data.data())[1], + CV_32FC1, + data.data()+8).clone()); + } + } + + void write(const Template &t) const + { + QFile f(file); + QtUtils::touchDir(f); + if (!f.open(QFile::WriteOnly)) + qFatal("Failed to open %s for writing.", qPrintable(file)); + + Mat m; + if (!raw) { + if (t.m().type() != CV_32FC1) + t.m().convertTo(m, CV_32F); + else m = t.m(); + + if (m.channels() != 1) qFatal("Only supports single channel matrices."); + + f.write((const char *) &m.rows, 4); + f.write((const char *) &m.cols, 4); + } + else m = t.m(); + + qint64 rowSize = m.cols * sizeof(float); + for (int i=0; i < m.rows; i++) + { + f.write((const char *) m.row(i).data, rowSize); + } + f.close(); + } +}; + +BR_REGISTER(Format, binaryFormat) + +} // namespace br + +#include "format/binary.moc" diff --git a/openbr/plugins/format/csv.cpp b/openbr/plugins/format/csv.cpp new file mode 100644 index 0000000..6bcad6e --- /dev/null +++ b/openbr/plugins/format/csv.cpp @@ -0,0 +1,89 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads a comma separated value file. + * \author Josh Klontz \cite jklontz + */ +class csvFormat : public Format +{ + Q_OBJECT + + Template read() const + { + QFile f(file.name); + f.open(QFile::ReadOnly); + QStringList lines(QString(f.readAll()).split(QRegularExpression("[\n|\r\n|\r]"), QString::SkipEmptyParts)); + f.close(); + + bool isUChar = true; + QList< QList > valsList; + foreach (const QString &line, lines) { + QList vals; + foreach (const QString &word, line.split(QRegExp(" *, *"), QString::SkipEmptyParts)) { + bool ok; + const float val = word.toFloat(&ok); + vals.append(val); + isUChar = isUChar && (val == float(uchar(val))); + } + if (!vals.isEmpty()) + valsList.append(vals); + } + + Mat m(valsList.size(), valsList[0].size(), CV_32FC1); + for (int i=0; i(i,j) = valsList[i][j]; + + if (isUChar) m.convertTo(m, CV_8U); + return Template(m); + } + + void write(const Template &t) const + { + const Mat &m = t.m(); + if (t.size() != 1) qFatal("Only supports single matrix templates."); + if (m.channels() != 1) qFatal("Only supports single channel matrices."); + + QStringList lines; lines.reserve(m.rows); + for (int r=0; r +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads FBI EBTS transactions. + * \author Scott Klum \cite sklum + * https://www.fbibiospecs.org/ebts.html + */ +class ebtsFormat : public Format +{ + Q_OBJECT + + struct Field { + int type; + QList data; + }; + + struct Record { + int type; + quint32 bytes; + int position; // Starting position of record + + QHash > fields; + }; + + quint32 recordBytes(const QByteArray &byteArray, const float recordType, int from) const + { + bool ok; + quint32 size; + + if (recordType == 4 || recordType == 7) { + // read first four bytes + ok = true; + size = qFromBigEndian((const uchar*)byteArray.mid(from,4).constData()); + } else { + int index = byteArray.indexOf(QChar(0x1D), from); + size = byteArray.mid(from, index-from).split(':').last().toInt(&ok); + } + + return ok ? size : -1; + } + + void parseRecord(const QByteArray &byteArray, Record &record) const + { + if (record.type == 4 || record.type == 7) { + // Just a binary blob + // Read everything after the first four bytes + // Not current supported + } else { + // Continue reading fields until we get all the data + unsigned int position = record.position; + while (position < record.position + record.bytes) { + int index = byteArray.indexOf(QChar(0x1D), position); + Field field = parseField(byteArray.mid(position, index-position),QChar(0x1F)); + if (field.type == 999 ) { + // Data begin after the field identifier and the colon + int dataBegin = byteArray.indexOf(':', position)+1; + field.data.clear(); + field.data.append(byteArray.mid(dataBegin, record.bytes-(dataBegin-record.position))); + + // Data fields are always last in the record + record.fields.insert(field.type,field.data); + break; + } + // Advance the position accounting for the separator + position += index-position+1; + record.fields.insert(field.type,field.data); + } + } + } + + Field parseField(const QByteArray &byteArray, const QChar &sep) const + { + bool ok; + Field f; + + QList data = byteArray.split(':'); + + f.type = data.first().split('.').last().toInt(&ok); + f.data = data.last().split(sep.toLatin1()); + + return f; + } + + Template read() const + { + QByteArray byteArray; + QtUtils::readFile(file, byteArray); + + Template t; + + Mat m; + + QList records; + + // Read the type one record (every EBTS file will have one of these) + Record r1; + r1.type = 1; + r1.position = 0; + r1.bytes = recordBytes(byteArray,r1.type,r1.position); + + // The fields in a type 1 record are strictly defined + QList data = byteArray.mid(r1.position,r1.bytes).split(QChar(0x1D).toLatin1()); + foreach (const QByteArray &datum, data) { + Field f = parseField(datum,QChar(0x1F)); + r1.fields.insert(f.type,f.data); + } + + records.append(r1); + + // Read the type two record (every EBTS file will have one of these) + Record r2; + r2.type = 2; + r2.position = r1.bytes; + r2.bytes = recordBytes(byteArray,r2.type,r2.position); + + // The fields in a type 2 record are strictly defined + data = byteArray.mid(r2.position,r2.bytes).split(QChar(0x1D).toLatin1()); + foreach (const QByteArray &datum, data) { + Field f = parseField(datum,QChar(0x1F)); + r2.fields.insert(f.type,f.data); + } + + // Demographics + if (r2.fields.contains(18)) { + QString name = r2.fields.value(18).first(); + QStringList names = name.split(','); + t.file.set("FIRSTNAME", names.at(1)); + t.file.set("LASTNAME", names.at(0)); + } + + if (r2.fields.contains(22)) t.file.set("DOB", r2.fields.value(22).first().toInt()); + if (r2.fields.contains(24)) t.file.set("GENDER", QString(r2.fields.value(24).first())); + if (r2.fields.contains(25)) t.file.set("RACE", QString(r2.fields.value(25).first())); + + if (t.file.contains("DOB")) { + const QDate dob = QDate::fromString(t.file.get("DOB"), "yyyyMMdd"); + const QDate current = QDate::currentDate(); + int age = current.year() - dob.year(); + if (current.month() < dob.month()) age--; + t.file.set("Age", age); + } + + records.append(r2); + + // The third field of the first record contains informations about all the remaining records in the transaction + // We don't care about the first two and the final items + QList recordTypes = r1.fields.value(3); + for (int i=2; i frontalIdxs; + int position = r1.bytes + r2.bytes; + for (int i=2; i +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads a NIST LFFS file. + * \author Josh Klontz \cite jklontz + */ +class lffsFormat : public Format +{ + Q_OBJECT + + Template read() const + { + QByteArray byteArray; + QtUtils::readFile(file.name, byteArray); + return Mat(1, byteArray.size(), CV_8UC1, byteArray.data()).clone(); + } + + void write(const Template &t) const + { + QByteArray byteArray((const char*)t.m().data, t.m().total()*t.m().elemSize()); + QtUtils::writeFile(file.name, byteArray); + } +}; + +BR_REGISTER(Format, lffsFormat) + +} // namespace br + +#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 new file mode 100644 index 0000000..f118daf --- /dev/null +++ b/openbr/plugins/format/mat.cpp @@ -0,0 +1,243 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief MATLAB .mat format. + * \author Josh Klontz \cite jklontz + * http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf + * \note matFormat is known not to work with compressed matrices + */ +class matFormat : public Format +{ + Q_OBJECT + + struct Element + { + // It is always best to cast integers to a Qt integer type, such as qint16 or quint32, when reading and writing. + // This ensures that you always know exactly what size integers you are reading and writing, no matter what the + // underlying platform and architecture the application happens to be running on. + // http://qt-project.org/doc/qt-4.8/datastreamformat.html + quint32 type, bytes; + QByteArray data; + Element() : type(0), bytes(0) {} + Element(QDataStream &stream) + : type(0), bytes(0) + { + // Read first 4 bytes into type (32 bit integer), + // specifying the type of data used + if (stream.readRawData((char*)&type, 4) != 4) + qFatal("Unexpected end of file."); + + if (type >= 1 << 16) { + // Small data format + bytes = type; + type = type & 0x0000FFFF; + bytes = bytes >> 16; + } else { + // Regular format + // Read 4 bytes into bytes (32 bit integer), + // specifying the size of the element + if (stream.readRawData((char*)&bytes, 4) != 4) + qFatal("Unexpected end of file."); + } + + // Set the size of data to bytes + data.resize(bytes); + + // Read bytes amount of data from the file into data + if (int(bytes) != stream.readRawData(data.data(), bytes)) + qFatal("Unexpected end of file."); + + // Alignment + int skipBytes = (bytes < 4) ? (4 - bytes) : (8 - bytes%8)%8; + if (skipBytes != 0) stream.skipRawData(skipBytes); + } + }; + + Template read() const + { + QByteArray byteArray; + QtUtils::readFile(file, byteArray); + QDataStream f(byteArray); + + { // Check header + QByteArray header(128, 0); + f.readRawData(header.data(), 128); + if (!header.startsWith("MATLAB 5.0 MAT-file")) + qFatal("Invalid MAT header."); + } + + Template t(file); + + while (!f.atEnd()) { + Element element(f); + + // miCOMPRESSED + if (element.type == 15) { + // Prepend the number of bytes to element.data + element.data.prepend((char*)&element.bytes, 4); // Qt zlib wrapper requires this to preallocate the buffer + QDataStream uncompressed(qUncompress(element.data)); + element = Element(uncompressed); + } + + // miMATRIX + if (element.type == 14) { + QDataStream matrix(element.data); + qint32 rows = 0, columns = 0; + int matrixType = 0; + QByteArray matrixData; + while (!matrix.atEnd()) { + Element subelement(matrix); + if (subelement.type == 5) { // Dimensions array + if (subelement.bytes == 8) { + rows = ((qint32*)subelement.data.data())[0]; + columns = ((qint32*)subelement.data.data())[1]; + } else { + qWarning("matFormat::read can only handle 2D arrays."); + } + } else if (subelement.type == 7) { //miSINGLE + matrixType = CV_32FC1; + matrixData = subelement.data; + } else if (subelement.type == 9) { //miDOUBLE + matrixType = CV_64FC1; + matrixData = subelement.data; + } + } + + if ((rows > 0) && (columns > 0) && (matrixType != 0) && !matrixData.isEmpty()) { + Mat transposed; + transpose(Mat(columns, rows, matrixType, matrixData.data()), transposed); + t.append(transposed); + } + } + } + + return t; + } + + void write(const Template &t) const + { + QByteArray data; + QDataStream stream(&data, QFile::WriteOnly); + + { // Header + QByteArray header = "MATLAB 5.0 MAT-file; Made with OpenBR | www.openbiometrics.org\n"; + QByteArray buffer(116-header.size(), 0); + stream.writeRawData(header.data(), header.size()); + stream.writeRawData(buffer.data(), buffer.size()); + quint64 subsystem = 0; + quint16 version = 0x0100; + const char *endianness = "IM"; + stream.writeRawData((const char*)&subsystem, 8); + stream.writeRawData((const char*)&version, 2); + stream.writeRawData(endianness, 2); + } + + for (int i=0; i +#include + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads a NIST BEE similarity matrix. + * \author Josh Klontz \cite jklontz + */ +class mtxFormat : public Format +{ + Q_OBJECT + + Template read() const + { + QString target, query; + Template result = BEE::readMatrix(file, &target, &query); + result.file.set("Target", target); + result.file.set("Query", query); + return result; + } + + void write(const Template &t) const + { + BEE::writeMatrix(t, file); + } +}; + +BR_REGISTER(Format, mtxFormat) + +/*! + * \ingroup formats + * \brief Reads a NIST BEE mask matrix. + * \author Josh Klontz \cite jklontz + */ +class maskFormat : public mtxFormat +{ + Q_OBJECT +}; + +BR_REGISTER(Format, maskFormat) + +} // namespace br + +#include "format/mtx.moc" diff --git a/openbr/plugins/format/null.cpp b/openbr/plugins/format/null.cpp new file mode 100644 index 0000000..97caa99 --- /dev/null +++ b/openbr/plugins/format/null.cpp @@ -0,0 +1,46 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +namespace br +{ + +/*! + * \ingroup formats + * \brief Returns an empty matrix. + * \author Josh Klontz \cite jklontz + */ +class nullFormat : public Format +{ + Q_OBJECT + + Template read() const + { + return Template(file, cv::Mat()); + } + + void write(const Template &t) const + { + (void)t; + } +}; + +BR_REGISTER(Format, nullFormat) + +} // namespace br + +#include "format/null.moc" diff --git a/openbr/plugins/format/post.cpp b/openbr/plugins/format/post.cpp new file mode 100644 index 0000000..31ee504 --- /dev/null +++ b/openbr/plugins/format/post.cpp @@ -0,0 +1,106 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +#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 new file mode 100644 index 0000000..14ee263 --- /dev/null +++ b/openbr/plugins/format/raw.cpp @@ -0,0 +1,86 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief RAW format + * + * http://www.nist.gov/srd/nistsd27.cfm + * \author Josh Klontz \cite jklontz + */ +class rawFormat : public Format +{ + Q_OBJECT + static QHash > imageSizes; // QHash > + + Template read() const + { + QString path = file.path(); + if (!imageSizes.contains(path)) { + static QMutex mutex; + QMutexLocker locker(&mutex); + + if (!imageSizes.contains(path)) { + const QString imageSize = path+"/ImageSize.txt"; + QStringList lines; + if (QFileInfo(imageSize).exists()) { + lines = QtUtils::readLines(imageSize); + lines.removeFirst(); // Remove header + } + + QHash sizes; + QRegExp whiteSpace("\\s+"); + foreach (const QString &line, lines) { + QStringList words = line.split(whiteSpace); + if (words.size() != 3) continue; + sizes.insert(words[0], QSize(words[2].toInt(), words[1].toInt())); + } + + imageSizes.insert(path, sizes); + } + } + + QByteArray data; + QtUtils::readFile(file, data); + + QSize size = imageSizes[path][file.baseName()]; + if (!size.isValid()) size = QSize(800,768); + if (data.size() != size.width() * size.height()) + qFatal("Expected %d*%d bytes, got %d.", size.height(), size.width(), data.size()); + return Template(file, Mat(size.height(), size.width(), CV_8UC1, data.data()).clone()); + } + + void write(const Template &t) const + { + QtUtils::writeFile(file, QByteArray().setRawData((const char*)t.m().data, t.m().total() * t.m().elemSize())); + } +}; + +QHash > rawFormat::imageSizes; + +BR_REGISTER(Format, rawFormat) + +} // namespace br + +#include "format/raw.moc" diff --git a/openbr/plugins/format/scores.cpp b/openbr/plugins/format/scores.cpp new file mode 100644 index 0000000..c674991 --- /dev/null +++ b/openbr/plugins/format/scores.cpp @@ -0,0 +1,81 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Reads in scores or ground truth from a text table. + * \author Josh Klontz \cite jklontz + * + * Example of the format: + * \code + * 2.2531514 FALSE 99990377 99990164 + * 2.2549822 TRUE 99990101 99990101 + * \endcode + */ +class scoresFormat : public Format +{ + Q_OBJECT + Q_PROPERTY(int column READ get_column WRITE set_column RESET reset_column STORED false) + Q_PROPERTY(bool groundTruth READ get_groundTruth WRITE set_groundTruth RESET reset_groundTruth STORED false) + Q_PROPERTY(QString delimiter READ get_delimiter WRITE set_delimiter RESET reset_delimiter STORED false) + BR_PROPERTY(int, column, 0) + BR_PROPERTY(bool, groundTruth, false) + BR_PROPERTY(QString, delimiter, "\t") + + Template read() const + { + QFile f(file.name); + if (!f.open(QFile::ReadOnly | QFile::Text)) + qFatal("Failed to open %s for reading.", qPrintable(f.fileName())); + QList values; + while (!f.atEnd()) { + const QStringList words = QString(f.readLine()).split(delimiter); + if (words.size() <= column) qFatal("Expected file to have at least %d columns.", column+1); + const QString &word = words[column]; + bool ok; + float value = word.toFloat(&ok); + if (!ok) value = (QtUtils::toBool(word) ? BEE::Match : BEE::NonMatch); + values.append(value); + } + if (values.size() == 1) + qWarning("Only one value read, double check file line endings."); + Mat result = OpenCVUtils::toMat(values); + if (groundTruth) result.convertTo(result, CV_8U); + return result; + } + + void write(const Template &t) const + { + (void) t; + qFatal("Not implemented."); + } +}; + +BR_REGISTER(Format, scoresFormat) + +} // namespace br + +#include "format/scores.moc" diff --git a/openbr/plugins/format/url.cpp b/openbr/plugins/format/url.cpp new file mode 100644 index 0000000..24b1dbd --- /dev/null +++ b/openbr/plugins/format/url.cpp @@ -0,0 +1,68 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 + +#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 new file mode 100644 index 0000000..fdf659d --- /dev/null +++ b/openbr/plugins/format/video.cpp @@ -0,0 +1,163 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Read all frames of a video using OpenCV + * \author Charles Otto \cite caotto + */ +class videoFormat : public Format +{ + Q_OBJECT + +public: + Template read() const + { + if (!file.exists() ) + return Template(); + + VideoCapture videoSource(file.name.toStdString()); + videoSource.open(file.name.toStdString() ); + + + Template frames; + if (!videoSource.isOpened()) { + qWarning("video file open failed"); + return frames; + } + + bool open = true; + while (open) { + cv::Mat frame; + open = videoSource.read(frame); + if (!open) break; + + frames.append(cv::Mat()); + frames.back() = frame.clone(); + } + + return frames; + } + + void write(const Template &t) const + { + int fourcc = OpenCVUtils::getFourcc(); + VideoWriter videoSink(file.name.toStdString(), fourcc, 30, t.begin()->size()); + + // Did we successfully open the output file? + if (!videoSink.isOpened() ) qFatal("Failed to open output file"); + + for (Template::const_iterator it = t.begin(); it!= t.end(); ++it) { + videoSink << *it; + } + } +}; + +BR_REGISTER(Format, videoFormat) + +/*! + * \ingroup formats + * \brief Retrieves an image from a webcam. + * \author Josh Klontz \cite jklontz + */ +class webcamFormat : public Format +{ + Q_OBJECT + + Template read() const + { + static QScopedPointer videoCapture; + + if (videoCapture.isNull()) + videoCapture.reset(new VideoCapture(0)); + + Mat m; + videoCapture->read(m); + return Template(m); + } + + void write(const Template &t) const + { + (void) t; + qFatal("Not supported."); + } +}; + +BR_REGISTER(Format, webcamFormat) + +/*! + * \ingroup formats + * \brief Reads image files. + * \author Josh Klontz \cite jklontz + */ +class DefaultFormat : public Format +{ + Q_OBJECT + + Template read() const + { + Template t; + + if (file.name.startsWith("http://") || file.name.startsWith("https://") || file.name.startsWith("www.")) { + if (Factory::names().contains("url")) { + File urlFile = file; + urlFile.name.append(".url"); + QScopedPointer url(Factory::make(urlFile)); + t = url->read(); + } + } else { + Mat m = imread(file.resolved().toStdString()); + if (m.data) { + t.append(m); + } else { + videoFormat videoReader; + videoReader.file = file; + t = videoReader.read(); + } + } + + return t; + } + + void write(const Template &t) const + { + if (t.size() > 1) { + videoFormat videoWriter; + videoWriter.file = file; + videoWriter.write(t); + } else if (t.size() == 1) { + QtUtils::touchDir(QDir(file.path())); + imwrite(file.name.toStdString(), t); + } + } +}; + +BR_REGISTER(Format, DefaultFormat) + +} // namespace br + +#include "format/video.moc" diff --git a/openbr/plugins/format/xml.cpp b/openbr/plugins/format/xml.cpp new file mode 100644 index 0000000..9757619 --- /dev/null +++ b/openbr/plugins/format/xml.cpp @@ -0,0 +1,118 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef BR_EMBEDDED +#include +#endif // BR_EMBEDDED +#include + +#include +#include + +using namespace cv; + +namespace br +{ + +/*! + * \ingroup formats + * \brief Decodes images from Base64 xml + * \author Scott Klum \cite sklum + * \author Josh Klontz \cite jklontz + */ +class xmlFormat : public Format +{ + Q_OBJECT + + Template read() const + { + Template t; + +#ifndef BR_EMBEDDED + QString fileName = file.get("path") + file.name; + + QDomDocument doc(fileName); + QFile f(fileName); + + if (!f.open(QIODevice::ReadOnly)) qFatal("Unable to open %s for reading.", qPrintable(file.flat())); + if (!doc.setContent(&f)) qWarning("Unable to parse %s.", qPrintable(file.flat())); + f.close(); + + QDomElement docElem = doc.documentElement(); + QDomNode subject = docElem.firstChild(); + while (!subject.isNull()) { + QDomNode fileNode = subject.firstChild(); + + while (!fileNode.isNull()) { + QDomElement e = fileNode.toElement(); + + if (e.tagName() == "FORMAL_IMG") { + QByteArray byteArray = QByteArray::fromBase64(qPrintable(e.text())); + Mat m = imdecode(Mat(3, byteArray.size(), CV_8UC3, byteArray.data()), CV_LOAD_IMAGE_COLOR); + if (!m.data) qWarning("xmlFormat::read failed to decode image data."); + t.append(m); + } else if ((e.tagName() == "RELEASE_IMG") || + (e.tagName() == "PREBOOK_IMG") || + (e.tagName() == "LPROFILE") || + (e.tagName() == "RPROFILE")) { + // Ignore these other image fields for now + } else { + t.file.set(e.tagName(), e.text()); + } + + fileNode = fileNode.nextSibling(); + } + subject = subject.nextSibling(); + } + + // Calculate age + if (t.file.contains("DOB")) { + const QDate dob = QDate::fromString(t.file.get("DOB").left(10), "yyyy-MM-dd"); + const QDate current = QDate::currentDate(); + int age = current.year() - dob.year(); + if (current.month() < dob.month()) age--; + t.file.set("Age", age); + } +#endif // BR_EMBEDDED + + return t; + } + + void write(const Template &t) const + { + QStringList lines; + lines.append(""); + lines.append(""); + lines.append("\t"); + foreach (const QString &key, t.file.localKeys()) { + if ((key == "Index") || (key == "Label")) continue; + lines.append("\t\t<"+key+">"+QtUtils::toString(t.file.value(key))+""); + } + std::vector data; + imencode(".jpg",t.m(),data); + QByteArray byteArray = QByteArray::fromRawData((const char*)data.data(), data.size()); + lines.append("\t\t"+byteArray.toBase64()+""); + lines.append("\t"); + lines.append(""); + QtUtils::writeFile(file, lines); + } +}; + +BR_REGISTER(Format, xmlFormat) + +} // namespace br + +#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 -