Commit 1f4ddbe5 authored by Roman Donchenko's avatar Roman Donchenko Committed by OpenCV Buildbot

Merge pull request #2705 from KonstantinMatskevich:face_recognition_labels_info

parents 4ef31194 02b32d86
...@@ -46,6 +46,15 @@ a unified access to all face recongition algorithms in OpenCV. :: ...@@ -46,6 +46,15 @@ a unified access to all face recongition algorithms in OpenCV. ::
// Deserializes this object from a given cv::FileStorage. // Deserializes this object from a given cv::FileStorage.
virtual void load(const FileStorage& fs) = 0; virtual void load(const FileStorage& fs) = 0;
// Sets additional information as pairs label - info.
void setLabelsInfo(const std::map<int, string>& labelsInfo);
// Gets string information by label
string getLabelInfo(const int &label);
// Gets labels by string
vector<int> getLabelsByString(const string& str);
}; };
...@@ -70,6 +79,8 @@ Moreover every :ocv:class:`FaceRecognizer` supports the: ...@@ -70,6 +79,8 @@ Moreover every :ocv:class:`FaceRecognizer` supports the:
* **Loading/Saving** the model state from/to a given XML or YAML. * **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. .. 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 Setting the Thresholds
...@@ -293,6 +304,30 @@ to enable loading the model state. ``FaceRecognizer::load(FileStorage& fs)`` in ...@@ -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 turn gets called by ``FaceRecognizer::load(const string& filename)``, to ease
saving a model. saving a model.
FaceRecognizer::setLabelsInfo
-----------------------------
Sets string information about labels into the model.
.. ocv:function:: void FaceRecognizer::setLabelsInfo(const std::map<int, string>& 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<int> FaceRecognizer::getLabelsByString(const string& str)
The function searches for the labels containing the specified substring in the associated string info.
createEigenFaceRecognizer createEigenFaceRecognizer
------------------------- -------------------------
......
...@@ -948,6 +948,14 @@ namespace cv ...@@ -948,6 +948,14 @@ namespace cv
// Deserializes this object from a given cv::FileStorage. // Deserializes this object from a given cv::FileStorage.
virtual void load(const FileStorage& fs) = 0; virtual void load(const FileStorage& fs) = 0;
// Sets additional information as pairs label - info.
void setLabelsInfo(const std::map<int, string>& labelsInfo);
// Gets string information by label
string getLabelInfo(const int &label);
// Gets labels by string
vector<int> getLabelsByString(const string& str);
}; };
CV_EXPORTS_W Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components = 0, double threshold = DBL_MAX); CV_EXPORTS_W Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components = 0, double threshold = DBL_MAX);
......
...@@ -98,10 +98,84 @@ inline vector<_Tp> remove_dups(const vector<_Tp>& src) { ...@@ -98,10 +98,84 @@ inline vector<_Tp> remove_dups(const vector<_Tp>& src) {
return elems; 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<int, string> _labelsInfo;
public:
// Sets additional information as pairs label - info.
virtual void setLabelsInfo(const std::map<int, string>& labelsInfo)
{
_labelsInfo = labelsInfo;
}
// Gets string information by label
virtual string getLabelInfo(int label) const
{
std::map<int, string>::const_iterator iter(_labelsInfo.find(label));
return iter != _labelsInfo.end() ? iter->second : "";
}
// Gets labels by string
virtual vector<int> getLabelsByString(const string& str)
{
vector<int> labels;
for(std::map<int,string>::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 // Turk, M., and Pentland, A. "Eigenfaces for recognition.". Journal of
// Cognitive Neuroscience 3 (1991), 71–86. // Cognitive Neuroscience 3 (1991), 71–86.
class Eigenfaces : public FaceRecognizer class Eigenfaces : public FaceRecognizer2
{ {
private: private:
int _num_components; int _num_components;
...@@ -154,7 +228,7 @@ public: ...@@ -154,7 +228,7 @@ public:
// faces: Recognition using class specific linear projection.". IEEE // faces: Recognition using class specific linear projection.". IEEE
// Transactions on Pattern Analysis and Machine Intelligence 19, 7 (1997), // Transactions on Pattern Analysis and Machine Intelligence 19, 7 (1997),
// 711–720. // 711–720.
class Fisherfaces: public FaceRecognizer class Fisherfaces: public FaceRecognizer2
{ {
private: private:
int _num_components; int _num_components;
...@@ -211,7 +285,7 @@ public: ...@@ -211,7 +285,7 @@ public:
// patterns: Application to face recognition." IEEE Transactions on Pattern // patterns: Application to face recognition." IEEE Transactions on Pattern
// Analysis and Machine Intelligence, 28(12):2037-2041. // Analysis and Machine Intelligence, 28(12):2037-2041.
// //
class LBPH : public FaceRecognizer class LBPH : public FaceRecognizer2
{ {
private: private:
int _grid_x; int _grid_x;
...@@ -228,7 +302,6 @@ private: ...@@ -228,7 +302,6 @@ private:
// old model data. // old model data.
void train(InputArrayOfArrays src, InputArray labels, bool preserveData); void train(InputArrayOfArrays src, InputArray labels, bool preserveData);
public: public:
using FaceRecognizer::save; using FaceRecognizer::save;
using FaceRecognizer::load; using FaceRecognizer::load;
...@@ -327,6 +400,27 @@ void FaceRecognizer::load(const string& filename) { ...@@ -327,6 +400,27 @@ void FaceRecognizer::load(const string& filename) {
fs.release(); fs.release();
} }
void FaceRecognizer::setLabelsInfo(const std::map<int, string>& labelsInfo)
{
FaceRecognizer2* base = dynamic_cast<FaceRecognizer2*>(this);
CV_Assert(base != 0);
base->setLabelsInfo(labelsInfo);
}
string FaceRecognizer::getLabelInfo(const int &label)
{
FaceRecognizer2* base = dynamic_cast<FaceRecognizer2*>(this);
CV_Assert(base != 0);
return base->getLabelInfo(label);
}
vector<int> FaceRecognizer::getLabelsByString(const string& str)
{
FaceRecognizer2* base = dynamic_cast<FaceRecognizer2*>(this);
CV_Assert(base != 0);
return base->getLabelsByString(str);
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Eigenfaces // Eigenfaces
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
...@@ -423,6 +517,17 @@ void Eigenfaces::load(const FileStorage& fs) { ...@@ -423,6 +517,17 @@ void Eigenfaces::load(const FileStorage& fs) {
// read sequences // read sequences
readFileNodeList(fs["projections"], _projections); readFileNodeList(fs["projections"], _projections);
fs["labels"] >> _labels; 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 { void Eigenfaces::save(FileStorage& fs) const {
...@@ -434,6 +539,10 @@ void Eigenfaces::save(FileStorage& fs) const { ...@@ -434,6 +539,10 @@ void Eigenfaces::save(FileStorage& fs) const {
// write sequences // write sequences
writeFileNodeList(fs, "projections", _projections); writeFileNodeList(fs, "projections", _projections);
fs << "labels" << _labels; fs << "labels" << _labels;
fs << "labelsInfo" << "[";
for (std::map<int, string>::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) { ...@@ -544,6 +653,17 @@ void Fisherfaces::load(const FileStorage& fs) {
// read sequences // read sequences
readFileNodeList(fs["projections"], _projections); readFileNodeList(fs["projections"], _projections);
fs["labels"] >> _labels; 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. // See FaceRecognizer::save.
...@@ -556,6 +676,10 @@ void Fisherfaces::save(FileStorage& fs) const { ...@@ -556,6 +676,10 @@ void Fisherfaces::save(FileStorage& fs) const {
// write sequences // write sequences
writeFileNodeList(fs, "projections", _projections); writeFileNodeList(fs, "projections", _projections);
fs << "labels" << _labels; fs << "labels" << _labels;
fs << "labelsInfo" << "[";
for (std::map<int, string>::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) { ...@@ -743,6 +867,17 @@ void LBPH::load(const FileStorage& fs) {
//read matrices //read matrices
readFileNodeList(fs["histograms"], _histograms); readFileNodeList(fs["histograms"], _histograms);
fs["labels"] >> _labels; 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. // See FaceRecognizer::save.
...@@ -754,6 +889,10 @@ void LBPH::save(FileStorage& fs) const { ...@@ -754,6 +889,10 @@ void LBPH::save(FileStorage& fs) const {
// write matrices // write matrices
writeFileNodeList(fs, "histograms", _histograms); writeFileNodeList(fs, "histograms", _histograms);
fs << "labels" << _labels; fs << "labels" << _labels;
fs << "labelsInfo" << "[";
for (std::map<int, string>::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) { void LBPH::train(InputArrayOfArrays _in_src, InputArray _in_labels) {
...@@ -849,7 +988,6 @@ int LBPH::predict(InputArray _src) const { ...@@ -849,7 +988,6 @@ int LBPH::predict(InputArray _src) const {
return label; return label;
} }
Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components, double threshold) Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components, double threshold)
{ {
return new Eigenfaces(num_components, threshold); return new Eigenfaces(num_components, threshold);
......
...@@ -39,20 +39,23 @@ static Mat toGrayscale(InputArray _src) { ...@@ -39,20 +39,23 @@ static Mat toGrayscale(InputArray _src) {
return dst; return dst;
} }
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') { static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, std::map<int, string>& labelsInfo, char separator = ';') {
std::ifstream file(filename.c_str(), ifstream::in); std::ifstream file(filename.c_str(), ifstream::in);
if (!file) { if (!file) {
string error_message = "No valid input file was given, please check the given filename."; string error_message = "No valid input file was given, please check the given filename.";
CV_Error(CV_StsBadArg, error_message); CV_Error(CV_StsBadArg, error_message);
} }
string line, path, classlabel; string line, path, classlabel, info;
while (getline(file, line)) { while (getline(file, line)) {
stringstream liness(line); stringstream liness(line);
getline(liness, path, separator); getline(liness, path, separator);
getline(liness, classlabel); getline(liness, classlabel, separator);
getline(liness, info, separator);
if(!path.empty() && !classlabel.empty()) { if(!path.empty() && !classlabel.empty()) {
images.push_back(imread(path, 0)); images.push_back(imread(path, 0));
labels.push_back(atoi(classlabel.c_str())); 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[]) { ...@@ -69,15 +72,17 @@ int main(int argc, const char *argv[]) {
// These vectors hold the images and corresponding labels. // These vectors hold the images and corresponding labels.
vector<Mat> images; vector<Mat> images;
vector<int> labels; vector<int> labels;
std::map<int, string> labelsInfo;
// Read in the data. This can fail if no valid // Read in the data. This can fail if no valid
// input filename is given. // input filename is given.
try { try {
read_csv(fn_csv, images, labels); read_csv(fn_csv, images, labels, labelsInfo);
} catch (cv::Exception& e) { } catch (cv::Exception& e) {
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl; cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
// nothing more we can do // nothing more we can do
exit(1); exit(1);
} }
// Quit if there are not enough images for this demo. // Quit if there are not enough images for this demo.
if(images.size() <= 1) { if(images.size() <= 1) {
string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!"; 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[]) { ...@@ -111,7 +116,9 @@ int main(int argc, const char *argv[]) {
// cv::createEigenFaceRecognizer(10, 123.0); // cv::createEigenFaceRecognizer(10, 123.0);
// //
Ptr<FaceRecognizer> model = createEigenFaceRecognizer(); Ptr<FaceRecognizer> model = createEigenFaceRecognizer();
model->setLabelsInfo(labelsInfo);
model->train(images, labels); model->train(images, labels);
// The following line predicts the label of a given // The following line predicts the label of a given
// test image: // test image:
int predictedLabel = model->predict(testSample); int predictedLabel = model->predict(testSample);
...@@ -124,6 +131,8 @@ int main(int argc, const char *argv[]) { ...@@ -124,6 +131,8 @@ int main(int argc, const char *argv[]) {
// //
string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel); string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
cout << result_message << endl; 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, // Sometimes you'll need to get/set internal model data,
// which isn't exposed by the public cv::FaceRecognizer. // which isn't exposed by the public cv::FaceRecognizer.
// Since each cv::FaceRecognizer is derived from a // Since each cv::FaceRecognizer is derived from a
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment