diff --git a/modules/contrib/doc/facerec/facerec_api.rst b/modules/contrib/doc/facerec/facerec_api.rst index 2aa3dcfa0..3da37bdd1 100644 --- a/modules/contrib/doc/facerec/facerec_api.rst +++ b/modules/contrib/doc/facerec/facerec_api.rst @@ -46,6 +46,15 @@ a unified access to all face recongition algorithms in OpenCV. :: // Deserializes this object from a given cv::FileStorage. virtual void load(const FileStorage& fs) = 0; + + // Sets additional information as pairs label - info. + void setLabelsInfo(const std::map& labelsInfo); + + // Gets string information by label + string getLabelInfo(const int &label); + + // Gets labels by string + vector getLabelsByString(const string& str); }; @@ -70,6 +79,8 @@ Moreover every :ocv:class:`FaceRecognizer` supports the: * **Loading/Saving** the model state from/to a given XML or YAML. +* **Setting/Getting labels info**, that is storaged as a string. String labels info is useful for keeping names of the recognized people. + .. note:: When using the FaceRecognizer interface in combination with Python, please stick to Python 2. Some underlying scripts like create_csv will not work in other versions, like Python 3. Setting the Thresholds @@ -293,6 +304,30 @@ to enable loading the model state. ``FaceRecognizer::load(FileStorage& fs)`` in turn gets called by ``FaceRecognizer::load(const string& filename)``, to ease saving a model. +FaceRecognizer::setLabelsInfo +----------------------------- + +Sets string information about labels into the model. +.. ocv:function:: void FaceRecognizer::setLabelsInfo(const std::map& labelsInfo) + +Information about the label loads as a pair "label id - string info". + +FaceRecognizer::getLabelInfo +---------------------------- + +Gets string information by label. +.. ocv:function:: string FaceRecognizer::getLabelInfo(const int &label) + +If an unknown label id is provided or there is no label information assosiated with the specified label id the method returns an empty string. + +FaceRecognizer::getLabelsByString +--------------------------------- +Gets vector of labels by string. + +.. ocv:function:: vector FaceRecognizer::getLabelsByString(const string& str) + +The function searches for the labels containing the specified substring in the associated string info. + createEigenFaceRecognizer ------------------------- diff --git a/modules/contrib/include/opencv2/contrib/contrib.hpp b/modules/contrib/include/opencv2/contrib/contrib.hpp index 7d881c359..5684ee242 100644 --- a/modules/contrib/include/opencv2/contrib/contrib.hpp +++ b/modules/contrib/include/opencv2/contrib/contrib.hpp @@ -948,6 +948,14 @@ namespace cv // Deserializes this object from a given cv::FileStorage. virtual void load(const FileStorage& fs) = 0; + // Sets additional information as pairs label - info. + void setLabelsInfo(const std::map& labelsInfo); + + // Gets string information by label + string getLabelInfo(const int &label); + + // Gets labels by string + vector getLabelsByString(const string& str); }; CV_EXPORTS_W Ptr createEigenFaceRecognizer(int num_components = 0, double threshold = DBL_MAX); diff --git a/modules/contrib/src/facerec.cpp b/modules/contrib/src/facerec.cpp index a3d695ad1..3f8aafbda 100644 --- a/modules/contrib/src/facerec.cpp +++ b/modules/contrib/src/facerec.cpp @@ -98,10 +98,84 @@ inline vector<_Tp> remove_dups(const vector<_Tp>& src) { return elems; } +// The FaceRecognizer2 class is introduced to keep the FaceRecognizer binary backward compatibility in 2.4 +// In master setLabelInfo/getLabelInfo/getLabelsByString should be virtual and _labelsInfo should be moved +// to FaceRecognizer, that allows to avoid FaceRecognizer2 in master +class FaceRecognizer2 : public FaceRecognizer +{ +protected: + // Stored pairs "label id - string info" + std::map _labelsInfo; + +public: + // Sets additional information as pairs label - info. + virtual void setLabelsInfo(const std::map& labelsInfo) + { + _labelsInfo = labelsInfo; + } + + // Gets string information by label + virtual string getLabelInfo(int label) const + { + std::map::const_iterator iter(_labelsInfo.find(label)); + return iter != _labelsInfo.end() ? iter->second : ""; + } + + // Gets labels by string + virtual vector getLabelsByString(const string& str) + { + vector labels; + for(std::map::const_iterator it = _labelsInfo.begin(); it != _labelsInfo.end(); it++) + { + size_t found = (it->second).find(str); + if(found != string::npos) + labels.push_back(it->first); + } + return labels; + } + +}; + +// Utility structure to load/save face label info (a pair of int and string) via FileStorage +struct LabelInfo +{ + LabelInfo():label(-1), value("") {} + LabelInfo(int _label, const std::string &_value): label(_label), value(_value) {} + int label; + std::string value; + void write(cv::FileStorage& fs) const + { + fs << "{" << "label" << label << "value" << value << "}"; + } + void read(const cv::FileNode& node) + { + label = (int)node["label"]; + value = (std::string)node["value"]; + } + std::ostream& operator<<(std::ostream& out) + { + out << "{ label = " << label << ", " << "value = " << value << "}"; + return out; + } +}; + +static void write(cv::FileStorage& fs, const std::string&, const LabelInfo& x) +{ + x.write(fs); +} + +static void read(const cv::FileNode& node, LabelInfo& x, const LabelInfo& default_value = LabelInfo()) +{ + if(node.empty()) + x = default_value; + else + x.read(node); +} + // Turk, M., and Pentland, A. "Eigenfaces for recognition.". Journal of // Cognitive Neuroscience 3 (1991), 71–86. -class Eigenfaces : public FaceRecognizer +class Eigenfaces : public FaceRecognizer2 { private: int _num_components; @@ -154,7 +228,7 @@ public: // faces: Recognition using class specific linear projection.". IEEE // Transactions on Pattern Analysis and Machine Intelligence 19, 7 (1997), // 711–720. -class Fisherfaces: public FaceRecognizer +class Fisherfaces: public FaceRecognizer2 { private: int _num_components; @@ -211,7 +285,7 @@ public: // patterns: Application to face recognition." IEEE Transactions on Pattern // Analysis and Machine Intelligence, 28(12):2037-2041. // -class LBPH : public FaceRecognizer +class LBPH : public FaceRecognizer2 { private: int _grid_x; @@ -228,7 +302,6 @@ private: // old model data. void train(InputArrayOfArrays src, InputArray labels, bool preserveData); - public: using FaceRecognizer::save; using FaceRecognizer::load; @@ -327,6 +400,27 @@ void FaceRecognizer::load(const string& filename) { fs.release(); } +void FaceRecognizer::setLabelsInfo(const std::map& labelsInfo) +{ + FaceRecognizer2* base = dynamic_cast(this); + CV_Assert(base != 0); + base->setLabelsInfo(labelsInfo); +} + +string FaceRecognizer::getLabelInfo(const int &label) +{ + FaceRecognizer2* base = dynamic_cast(this); + CV_Assert(base != 0); + return base->getLabelInfo(label); +} + +vector FaceRecognizer::getLabelsByString(const string& str) +{ + FaceRecognizer2* base = dynamic_cast(this); + CV_Assert(base != 0); + return base->getLabelsByString(str); +} + //------------------------------------------------------------------------------ // Eigenfaces //------------------------------------------------------------------------------ @@ -423,6 +517,17 @@ void Eigenfaces::load(const FileStorage& fs) { // read sequences readFileNodeList(fs["projections"], _projections); fs["labels"] >> _labels; + const FileNode& fn = fs["labelsInfo"]; + if (fn.type() == FileNode::SEQ) + { + _labelsInfo.clear(); + for (FileNodeIterator it = fn.begin(); it != fn.end();) + { + LabelInfo item; + it >> item; + _labelsInfo.insert(std::make_pair(item.label, item.value)); + } + } } void Eigenfaces::save(FileStorage& fs) const { @@ -434,6 +539,10 @@ void Eigenfaces::save(FileStorage& fs) const { // write sequences writeFileNodeList(fs, "projections", _projections); fs << "labels" << _labels; + fs << "labelsInfo" << "["; + for (std::map::const_iterator it = _labelsInfo.begin(); it != _labelsInfo.end(); it++) + fs << LabelInfo(it->first, it->second); + fs << "]"; } //------------------------------------------------------------------------------ @@ -544,6 +653,17 @@ void Fisherfaces::load(const FileStorage& fs) { // read sequences readFileNodeList(fs["projections"], _projections); fs["labels"] >> _labels; + const FileNode& fn = fs["labelsInfo"]; + if (fn.type() == FileNode::SEQ) + { + _labelsInfo.clear(); + for (FileNodeIterator it = fn.begin(); it != fn.end();) + { + LabelInfo item; + it >> item; + _labelsInfo.insert(std::make_pair(item.label, item.value)); + } + } } // See FaceRecognizer::save. @@ -556,6 +676,10 @@ void Fisherfaces::save(FileStorage& fs) const { // write sequences writeFileNodeList(fs, "projections", _projections); fs << "labels" << _labels; + fs << "labelsInfo" << "["; + for (std::map::const_iterator it = _labelsInfo.begin(); it != _labelsInfo.end(); it++) + fs << LabelInfo(it->first, it->second); + fs << "]"; } //------------------------------------------------------------------------------ @@ -743,6 +867,17 @@ void LBPH::load(const FileStorage& fs) { //read matrices readFileNodeList(fs["histograms"], _histograms); fs["labels"] >> _labels; + const FileNode& fn = fs["labelsInfo"]; + if (fn.type() == FileNode::SEQ) + { + _labelsInfo.clear(); + for (FileNodeIterator it = fn.begin(); it != fn.end();) + { + LabelInfo item; + it >> item; + _labelsInfo.insert(std::make_pair(item.label, item.value)); + } + } } // See FaceRecognizer::save. @@ -754,6 +889,10 @@ void LBPH::save(FileStorage& fs) const { // write matrices writeFileNodeList(fs, "histograms", _histograms); fs << "labels" << _labels; + fs << "labelsInfo" << "["; + for (std::map::const_iterator it = _labelsInfo.begin(); it != _labelsInfo.end(); it++) + fs << LabelInfo(it->first, it->second); + fs << "]"; } void LBPH::train(InputArrayOfArrays _in_src, InputArray _in_labels) { @@ -849,7 +988,6 @@ int LBPH::predict(InputArray _src) const { return label; } - Ptr createEigenFaceRecognizer(int num_components, double threshold) { return new Eigenfaces(num_components, threshold); diff --git a/samples/cpp/facerec_demo.cpp b/samples/cpp/facerec_demo.cpp index 6402082e8..b92308e89 100644 --- a/samples/cpp/facerec_demo.cpp +++ b/samples/cpp/facerec_demo.cpp @@ -39,20 +39,23 @@ static Mat toGrayscale(InputArray _src) { return dst; } -static void read_csv(const string& filename, vector& images, vector& labels, char separator = ';') { +static void read_csv(const string& filename, vector& images, vector& labels, std::map& labelsInfo, char separator = ';') { std::ifstream file(filename.c_str(), ifstream::in); if (!file) { string error_message = "No valid input file was given, please check the given filename."; CV_Error(CV_StsBadArg, error_message); } - string line, path, classlabel; + string line, path, classlabel, info; while (getline(file, line)) { stringstream liness(line); getline(liness, path, separator); - getline(liness, classlabel); + getline(liness, classlabel, separator); + getline(liness, info, separator); if(!path.empty() && !classlabel.empty()) { images.push_back(imread(path, 0)); labels.push_back(atoi(classlabel.c_str())); + if(!info.empty()) + labelsInfo.insert(std::make_pair(labels.back(), info)); } } } @@ -69,15 +72,17 @@ int main(int argc, const char *argv[]) { // These vectors hold the images and corresponding labels. vector images; vector labels; + std::map labelsInfo; // Read in the data. This can fail if no valid // input filename is given. try { - read_csv(fn_csv, images, labels); + read_csv(fn_csv, images, labels, labelsInfo); } catch (cv::Exception& e) { cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl; // nothing more we can do exit(1); } + // Quit if there are not enough images for this demo. if(images.size() <= 1) { string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!"; @@ -111,7 +116,9 @@ int main(int argc, const char *argv[]) { // cv::createEigenFaceRecognizer(10, 123.0); // Ptr model = createEigenFaceRecognizer(); + model->setLabelsInfo(labelsInfo); model->train(images, labels); + // The following line predicts the label of a given // test image: int predictedLabel = model->predict(testSample); @@ -124,6 +131,8 @@ int main(int argc, const char *argv[]) { // string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel); cout << result_message << endl; + if( (predictedLabel == testLabel) && !model->getLabelInfo(predictedLabel).empty() ) + cout << format("%d-th label's info: %s", predictedLabel, model->getLabelInfo(predictedLabel).c_str()) << endl; // Sometimes you'll need to get/set internal model data, // which isn't exposed by the public cv::FaceRecognizer. // Since each cv::FaceRecognizer is derived from a