diff --git a/modules/face/include/opencv2/face.hpp b/modules/face/include/opencv2/face.hpp index 4d65978b3..d7237bc94 100644 --- a/modules/face/include/opencv2/face.hpp +++ b/modules/face/include/opencv2/face.hpp @@ -48,6 +48,7 @@ the use of this software, even if advised of the possibility of such damage. */ #include "opencv2/core.hpp" +#include "face/predict_collector.hpp" #include namespace cv { namespace face { @@ -255,7 +256,8 @@ public: CV_WRAP virtual void update(InputArrayOfArrays src, InputArray labels); /** @overload */ - virtual int predict(InputArray src) const = 0; + CV_WRAP int predict(InputArray src) const; + /** @brief Predicts a label and associated confidence (e.g. distance) for a given input image. @@ -292,7 +294,18 @@ public: model->predict(img, predicted_label, predicted_confidence); @endcode */ - CV_WRAP virtual void predict(InputArray src, CV_OUT int &label, CV_OUT double &confidence) const = 0; + CV_WRAP void predict(InputArray src, CV_OUT int &label, CV_OUT double &confidence) const; + + + /** @brief - if implemented - send all result of prediction to collector that can be used for somehow custom result handling + @param src Sample image to get a prediction from. + @param collector User-defined collector object that accepts all results + @param state - optional user-defined state token that should be passed back from FaceRecognizer implementation + + To implement this method u just have to do same internal cycle as in predict(InputArray src, CV_OUT int &label, CV_OUT double &confidence) but + not try to get "best@ result, just resend it to caller side with given collector + */ + CV_WRAP virtual void predict(InputArray src, Ptr collector, const int state = 0) const = 0; /** @brief Saves a FaceRecognizer and its model state. @@ -345,7 +358,8 @@ public: info. */ CV_WRAP virtual std::vector getLabelsByString(const String& str) const; - + /** @brief threshhold parameter accessor - required for default BestMinDist collector */ + virtual double getThreshold() const = 0; protected: // Stored pairs "label id - string info" std::map _labelsInfo; diff --git a/modules/face/include/opencv2/face/predict_collector.hpp b/modules/face/include/opencv2/face/predict_collector.hpp new file mode 100644 index 000000000..e659d3e34 --- /dev/null +++ b/modules/face/include/opencv2/face/predict_collector.hpp @@ -0,0 +1,102 @@ +/* +By downloading, copying, installing or using the software you agree to this license. +If you do not agree to this license, do not download, install, +copy or use the software. + + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez Inc., all rights reserved. +Third party copyrights are property of their respective owners. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the names of the copyright holders nor the names of the contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders or contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused +and on any theory of liability, whether in contract, strict liability, +or tort (including negligence or otherwise) arising in any way out of +the use of this software, even if advised of the possibility of such damage. +*/ + +#ifndef __OPENCV_PREDICT_COLLECTOR_HPP__ +#define __OPENCV_PREDICT_COLLECTOR_HPP__ +#include +#include "opencv2/core/cvdef.h" +#include "opencv2/core/cvstd.hpp" +namespace cv { +namespace face { +//! @addtogroup face +//! @{ +/** @brief Abstract base class for all strategies of prediction result handling +*/ +class CV_EXPORTS_W PredictCollector { +protected: + double _threshhold; + int _size; + int _state; +public: + /** @brief creates new predict collector with given threshhold */ + PredictCollector(double threshhold = DBL_MAX) :_threshhold(threshhold) {}; + CV_WRAP virtual ~PredictCollector() {} + /** @brief called once at start of recognition + @param size total size of prediction evaluation that recognizer could perform + @param state user defined send-to-back optional value to allow multi-thread, multi-session or aggregation scenarios + */ + CV_WRAP virtual void init(const int size, const int state = 0); + /** @brief called with every recognition result + @param label current prediction label + @param dist current prediction distance (confidence) + @param state user defined send-to-back optional value to allow multi-thread, multi-session or aggregation scenarios + @return true if recognizer should proceed prediction , false - if recognizer should terminate prediction + */ + CV_WRAP virtual bool emit(const int label, const double dist, const int state = 0); //not abstract while Python generation require non-abstract class +}; + +/** @brief default predict collector that trace minimal distance with treshhold checking (that is default behavior for most predict logic) +*/ +class CV_EXPORTS_W MinDistancePredictCollector : public PredictCollector { +private: + int _label; + double _dist; +public: + /** @brief creates new MinDistancePredictCollector with given threshhold */ + CV_WRAP MinDistancePredictCollector(double threshhold = DBL_MAX) : PredictCollector(threshhold) { + _label = 0; + _dist = DBL_MAX; + }; + CV_WRAP bool emit(const int label, const double dist, const int state = 0); + /** @brief result label, 0 if not found */ + CV_WRAP int getLabel() const; + /** @brief result distance (confidence) DBL_MAX if not found */ + CV_WRAP double getDist() const; + /** @brief factory method to create cv-pointers to MinDistancePredictCollector */ + CV_WRAP static Ptr create(double threshold = DBL_MAX); +}; +//! @} +} +} +#endif \ No newline at end of file diff --git a/modules/face/src/eigen_faces.cpp b/modules/face/src/eigen_faces.cpp index 8247ad79f..2e5d58c88 100644 --- a/modules/face/src/eigen_faces.cpp +++ b/modules/face/src/eigen_faces.cpp @@ -41,11 +41,8 @@ public: // in labels. void train(InputArrayOfArrays src, InputArray labels); - // Predicts the label of a query image in src. - int predict(InputArray src) const; - - // Predicts the label and confidence for a given sample. - void predict(InputArray _src, int &label, double &dist) const; + // Send all predict results to caller side for custom result handling + void predict(InputArray src, Ptr collector, const int state) const; }; //------------------------------------------------------------------------------ @@ -102,7 +99,7 @@ void Eigenfaces::train(InputArrayOfArrays _src, InputArray _local_labels) { } } -void Eigenfaces::predict(InputArray _src, int &minClass, double &minDist) const { +void Eigenfaces::predict(InputArray _src, Ptr collector, const int state) const { // get data Mat src = _src.getMat(); // make sure the user is passing correct data @@ -116,25 +113,15 @@ void Eigenfaces::predict(InputArray _src, int &minClass, double &minDist) const CV_Error(Error::StsBadArg, error_message); } // project into PCA subspace - Mat q = LDA::subspaceProject(_eigenvectors, _mean, src.reshape(1,1)); - minDist = DBL_MAX; - minClass = -1; - for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) { + Mat q = LDA::subspaceProject(_eigenvectors, _mean, src.reshape(1, 1)); + collector->init((int)_projections.size(), state); + for (size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) { double dist = norm(_projections[sampleIdx], q, NORM_L2); - if((dist < minDist) && (dist < _threshold)) { - minDist = dist; - minClass = _labels.at((int)sampleIdx); - } + int label = _labels.at((int)sampleIdx); + if (!collector->emit(label, dist, state))return; } } -int Eigenfaces::predict(InputArray _src) const { - int label; - double dummy; - predict(_src, label, dummy); - return label; -} - Ptr createEigenFaceRecognizer(int num_components, double threshold) { return makePtr(num_components, threshold); diff --git a/modules/face/src/facerec.cpp b/modules/face/src/facerec.cpp index dac7d082b..0ea2a044d 100644 --- a/modules/face/src/facerec.cpp +++ b/modules/face/src/facerec.cpp @@ -72,6 +72,20 @@ void FaceRecognizer::save(const String &filename) const fs.release(); } +int FaceRecognizer::predict(InputArray src) const { + int _label; + double _dist; + predict(src, _label, _dist); + return _label; +} + +void FaceRecognizer::predict(InputArray src, CV_OUT int &label, CV_OUT double &confidence) const { + Ptr collector = MinDistancePredictCollector::create(getThreshold()); + predict(src, collector, 0); + label = collector->getLabel(); + confidence = collector->getDist(); +} + } } diff --git a/modules/face/src/fisher_faces.cpp b/modules/face/src/fisher_faces.cpp index d8466f996..160d72404 100644 --- a/modules/face/src/fisher_faces.cpp +++ b/modules/face/src/fisher_faces.cpp @@ -36,11 +36,8 @@ public: // in labels. void train(InputArrayOfArrays src, InputArray labels); - // Predicts the label of a query image in src. - int predict(InputArray src) const; - - // Predicts the label and confidence for a given sample. - void predict(InputArray _src, int &label, double &dist) const; + // Send all predict results to caller side for custom result handling + void predict(InputArray src, Ptr collector, const int state) const; }; // Removes duplicate elements in a given vector. @@ -123,7 +120,7 @@ void Fisherfaces::train(InputArrayOfArrays src, InputArray _lbls) { } } -void Fisherfaces::predict(InputArray _src, int &minClass, double &minDist) const { +void Fisherfaces::predict(InputArray _src, Ptr collector, const int state) const { Mat src = _src.getMat(); // check data alignment just for clearer exception messages if(_projections.empty()) { @@ -137,24 +134,14 @@ void Fisherfaces::predict(InputArray _src, int &minClass, double &minDist) const // project into LDA subspace Mat q = LDA::subspaceProject(_eigenvectors, _mean, src.reshape(1,1)); // find 1-nearest neighbor - minDist = DBL_MAX; - minClass = -1; - for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) { + collector->init((int)_projections.size(), state); + for (size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) { double dist = norm(_projections[sampleIdx], q, NORM_L2); - if((dist < minDist) && (dist < _threshold)) { - minDist = dist; - minClass = _labels.at((int)sampleIdx); - } + int label = _labels.at((int)sampleIdx); + if (!collector->emit(label, dist, state))return; } } -int Fisherfaces::predict(InputArray _src) const { - int label; - double dummy; - predict(_src, label, dummy); - return label; -} - Ptr createFisherFaceRecognizer(int num_components, double threshold) { return makePtr(num_components, threshold); diff --git a/modules/face/src/lbph_faces.cpp b/modules/face/src/lbph_faces.cpp index bc01acefb..8d8d40bc3 100644 --- a/modules/face/src/lbph_faces.cpp +++ b/modules/face/src/lbph_faces.cpp @@ -91,11 +91,8 @@ public: // corresponding labels in labels. void update(InputArrayOfArrays src, InputArray labels); - // Predicts the label of a query image in src. - int predict(InputArray src) const; - - // Predicts the label and confidence for a given sample. - void predict(InputArray _src, int &label, double &dist) const; + // Send all predict results to caller side for custom result handling + void predict(InputArray src, Ptr collector, const int state = 0) const; // See FaceRecognizer::load. void load(const FileStorage& fs); @@ -386,7 +383,7 @@ void LBPH::train(InputArrayOfArrays _in_src, InputArray _in_labels, bool preserv } } -void LBPH::predict(InputArray _src, int &minClass, double &minDist) const { +void LBPH::predict(InputArray _src, Ptr collector, const int state) const { if(_histograms.empty()) { // throw error if no data (or simply return -1?) String error_message = "This LBPH model is not computed yet. Did you call the train method?"; @@ -402,24 +399,14 @@ void LBPH::predict(InputArray _src, int &minClass, double &minDist) const { _grid_y, /* grid size y */ true /* normed histograms */); // find 1-nearest neighbor - minDist = DBL_MAX; - minClass = -1; - for(size_t sampleIdx = 0; sampleIdx < _histograms.size(); sampleIdx++) { + collector->init((int)_histograms.size(), state); + for (size_t sampleIdx = 0; sampleIdx < _histograms.size(); sampleIdx++) { double dist = compareHist(_histograms[sampleIdx], query, HISTCMP_CHISQR_ALT); - if((dist < minDist) && (dist < _threshold)) { - minDist = dist; - minClass = _labels.at((int) sampleIdx); - } + int label = _labels.at((int)sampleIdx); + if (!collector->emit(label, dist, state))return; } } -int LBPH::predict(InputArray _src) const { - int label; - double dummy; - predict(_src, label, dummy); - return label; -} - Ptr createLBPHFaceRecognizer(int radius, int neighbors, int grid_x, int grid_y, double threshold) { diff --git a/modules/face/src/predict_collector.cpp b/modules/face/src/predict_collector.cpp new file mode 100644 index 000000000..732a0688a --- /dev/null +++ b/modules/face/src/predict_collector.cpp @@ -0,0 +1,86 @@ +/* +By downloading, copying, installing or using the software you agree to this license. +If you do not agree to this license, do not download, install, +copy or use the software. + + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2015, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015, OpenCV Foundation, all rights reserved. +Copyright (C) 2015, Itseez Inc., all rights reserved. +Third party copyrights are property of their respective owners. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the names of the copyright holders nor the names of the contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders or contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused +and on any theory of liability, whether in contract, strict liability, +or tort (including negligence or otherwise) arising in any way out of +the use of this software, even if advised of the possibility of such damage. +*/ +#include "opencv2/face/predict_collector.hpp" +#include "opencv2/core/cvstd.hpp" +namespace cv { +namespace face { + +void PredictCollector::init(const int size, const int state) { + //reserve for some-how usage in descendants + _size = size; + _state = state; +} + +bool PredictCollector::emit(const int, const double, const int state) { + if (_state == state) { + return false; // if it's own session - terminate it while default PredictCollector does nothing + } + return true; +} + +bool MinDistancePredictCollector::emit(const int label, const double dist, const int state) { + if (_state != state) { + return true; // it works only in one (same) session doesn't accept values for other states + } + if (dist < _threshhold && dist < _dist) { + _label = label; + _dist = dist; + } + return true; +} + +int MinDistancePredictCollector::getLabel() const { + return _label; +} + +double MinDistancePredictCollector::getDist() const { + return _dist; +} + +Ptr MinDistancePredictCollector::create(double threshold) { + return Ptr(new MinDistancePredictCollector(threshold)); +} + +} +} \ No newline at end of file