2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "FaceRecognitionModel.h"
19 #include "mv_private.h"
20 #include "mv_common.h"
28 namespace MediaVision {
32 unsigned int DefaultUnisizeWidth = 200;
33 unsigned int DefaultUnisizeHeight = 200;
35 bool isEmptyAlgorithmParam(const std::string& path)
40 ifs.open(path.c_str());
43 LOGE("[%s] Can't open file.", path.c_str());
47 ifs.getline(valid, 256);
50 LOGD("Validation string: %s", valid);
51 if (strlen(valid) <= 0) {
52 LOGE("algorithm params is empty.");
59 int CopyOpenCVAlgorithmParameters(const cv::Ptr<cv::face::FaceRecognizer>& srcAlg,
60 cv::Ptr<cv::face::FaceRecognizer>& dstAlg)
62 char tempPath[1024] = "";
64 snprintf(tempPath, 1024, "/tmp/alg_copy_%p_%p", srcAlg.get(), dstAlg.get());
66 srcAlg->write(tempPath);
68 if (!isEmptyAlgorithmParam(tempPath))
69 dstAlg->read(tempPath);
71 if (0 != remove(tempPath))
72 LOGW("Error removing serialized FaceRecognizer in %s", tempPath);
74 /* todo: consider to uncomment this lines if OpenCV will support deep
75 copy of AlgorithmInfo objects: */
77 /*std::vector<std::string> paramNames;
78 srcAlg->getParams(paramNames);
79 size_t paramSize = paramNames.size();
80 for (size_t i = 0; i < paramSize; ++i) {
81 int pType = srcAlg->paramType(paramNames[i]);
85 case cv::Param::UNSIGNED_INT:
86 case cv::Param::UINT64:
87 case cv::Param::SHORT:
88 case cv::Param::UCHAR:
89 dstAlg->set(paramNames[i], srcAlg->getInt(paramNames[i]));
91 case cv::Param::BOOLEAN:
92 dstAlg->set(paramNames[i], srcAlg->getBool(paramNames[i]));
95 case cv::Param::FLOAT:
96 dstAlg->set(paramNames[i], srcAlg->getDouble(paramNames[i]));
98 case cv::Param::STRING:
99 dstAlg->set(paramNames[i], srcAlg->getString(paramNames[i]));
102 dstAlg->set(paramNames[i], srcAlg->getMat(paramNames[i]));
104 case cv::Param::MAT_VECTOR:
106 //std::vector<cv::Mat> value = srcAlg->getMatVector(paramNames[i]);
107 //dstAlg->info()->addParam(*(dstAlg.obj), paramNames[i].c_str(), value);
108 dstAlg->set(paramNames[i], srcAlg->getMatVector(paramNames[i]));
111 case cv::Param::ALGORITHM:
112 dstAlg->set(paramNames[i], srcAlg->getAlgorithm(paramNames[i]));
115 LOGE("While copying algorothm parameters unsupported parameter "
116 "%s was found.", paramNames[i].c_str());
118 return MEDIA_VISION_ERROR_NOT_SUPPORTED;
122 return MEDIA_VISION_ERROR_NONE;
125 void ParseOpenCVLabels(
126 const cv::Mat labels,
127 std::set<int>& outLabels)
129 if (!labels.empty()) {
130 for (int i = 0; i < labels.rows; ++i)
131 outLabels.insert(labels.at<int>(i, 0));
135 } /* anonymous namespace */
137 FaceRecognitionModelConfig::FaceRecognitionModelConfig() :
138 mModelType(MEDIA_VISION_FACE_MODEL_TYPE_UNKNOWN),
145 mImgWidth(DefaultUnisizeWidth),
146 mImgHeight(DefaultUnisizeHeight)
151 FaceRecognitionResults::FaceRecognitionResults() :
152 mIsRecognized(false),
159 bool FaceRecognitionModelConfig::operator!=(
160 const FaceRecognitionModelConfig& other) const
162 return mModelType != other.mModelType ||
163 mNumComponents != other.mNumComponents ||
164 mThreshold != other.mThreshold ||
165 mRadius != other.mRadius ||
166 mNeighbors != other.mNeighbors ||
167 mGridX != other.mGridX ||
168 mGridY != other.mGridY ||
169 mImgWidth != other.mImgWidth ||
170 mImgHeight != other.mImgHeight;
173 FaceRecognitionModel::FaceRecognitionModel() :
174 m_canRecognize(false),
175 m_recognizer() // The default constructor creates a null Ptr
180 FaceRecognitionModel::FaceRecognitionModel(const FaceRecognitionModel& origin) :
181 m_canRecognize(origin.m_canRecognize),
182 m_faceSamples(origin.m_faceSamples),
183 m_learnAlgorithmConfig(origin.m_learnAlgorithmConfig),
184 m_recognizer(CreateRecognitionAlgorithm(origin.m_learnAlgorithmConfig)),
185 m_learnedLabels(origin.m_learnedLabels)
187 if (!m_recognizer.empty())
188 CopyOpenCVAlgorithmParameters(origin.m_recognizer, m_recognizer);
191 FaceRecognitionModel& FaceRecognitionModel::operator=(
192 const FaceRecognitionModel& copy)
195 m_canRecognize = copy.m_canRecognize;
196 m_faceSamples = copy.m_faceSamples;
197 m_learnAlgorithmConfig = copy.m_learnAlgorithmConfig;
198 m_recognizer = CreateRecognitionAlgorithm(m_learnAlgorithmConfig);
199 m_learnedLabels = copy.m_learnedLabels;
201 if (!m_recognizer.empty())
202 CopyOpenCVAlgorithmParameters(copy.m_recognizer, m_recognizer);
208 FaceRecognitionModel::~FaceRecognitionModel()
213 int FaceRecognitionModel::save(const std::string& fileName)
215 if (!m_recognizer.empty()) {
216 std::string filePath;
220 std::string prefixPath = filePath.substr(0, filePath.find_last_of('/'));
221 LOGD("prefixPath: %s", prefixPath.c_str());
223 /* check the directory is available */
224 if (access(prefixPath.c_str(), F_OK)) {
225 LOGE("Can't save recognition model. Path[%s] doesn't existed.", prefixPath.c_str());
227 return MEDIA_VISION_ERROR_INVALID_PATH;
230 cv::FileStorage storage(filePath, cv::FileStorage::WRITE);
231 if (!storage.isOpened()) {
232 LOGE("Can't save recognition model. Write to file permission denied.");
233 return MEDIA_VISION_ERROR_PERMISSION_DENIED;
236 switch (m_learnAlgorithmConfig.mModelType) {
237 case MEDIA_VISION_FACE_MODEL_TYPE_EIGENFACES:
238 storage << "algorithm" << "Eigenfaces";
239 storage << "resizeW" << m_learnAlgorithmConfig.mImgWidth;
240 storage << "resizeH" << m_learnAlgorithmConfig.mImgHeight;
242 case MEDIA_VISION_FACE_MODEL_TYPE_FISHERFACES:
243 storage << "algorithm" << "Fisherfaces";
244 storage << "resizeW" << m_learnAlgorithmConfig.mImgWidth;
245 storage << "resizeH" << m_learnAlgorithmConfig.mImgHeight;
247 case MEDIA_VISION_FACE_MODEL_TYPE_LBPH:
248 storage << "algorithm" << "LBPH";
252 return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT;
255 storage << "can_recognize" << m_canRecognize;
256 m_recognizer->write(storage);
260 LOGE("Attempt to save recognition model before learn");
261 return MEDIA_VISION_ERROR_INVALID_OPERATION;
264 return MEDIA_VISION_ERROR_NONE;
267 int FaceRecognitionModel::load(const std::string& fileName)
269 std::string filePath;
273 if (access(filePath.c_str(), F_OK)) {
274 LOGE("Can't load face recognition model. File[%s] doesn't existed.", filePath.c_str());
276 return MEDIA_VISION_ERROR_INVALID_PATH;
279 cv::FileStorage storage(filePath, cv::FileStorage::READ);
280 if (!storage.isOpened()) {
281 LOGE("Can't load recognition model. Read from file permission denied.");
283 return MEDIA_VISION_ERROR_PERMISSION_DENIED;
286 LOGD("Loading recognition model from file.");
289 int canRecognize = 0;
290 storage["algorithm"] >> algName;
291 storage["can_recognize"] >> canRecognize;
293 cv::Ptr<cv::face::FaceRecognizer> tempRecognizer;
294 FaceRecognitionModelConfig tempConfig;
295 std::set<int> tempLearnedLabels;
298 if (algName == "Eigenfaces") {
299 tempRecognizer = cv::face::EigenFaceRecognizer::create();
300 storage["resizeW"] >> tempConfig.mImgWidth;
301 storage["resizeH"] >> tempConfig.mImgHeight;
302 tempRecognizer->read(storage.root());
303 tempConfig.mModelType =
304 MEDIA_VISION_FACE_MODEL_TYPE_EIGENFACES;
305 cv::face::EigenFaceRecognizer* recognizer = dynamic_cast<cv::face::EigenFaceRecognizer*>(tempRecognizer.get());
306 if (recognizer != NULL) {
307 tempConfig.mNumComponents = recognizer->getNumComponents();
308 labels = recognizer->getLabels();
310 return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT;
312 } else if (algName == "Fisherfaces") {
313 tempRecognizer = cv::face::FisherFaceRecognizer::create();
314 storage["resizeW"] >> tempConfig.mImgWidth;
315 storage["resizeH"] >> tempConfig.mImgHeight;
316 tempRecognizer->read(storage.root());
317 tempConfig.mModelType =
318 MEDIA_VISION_FACE_MODEL_TYPE_FISHERFACES;
319 cv::face::FisherFaceRecognizer* recognizer = dynamic_cast<cv::face::FisherFaceRecognizer*>(tempRecognizer.get());
320 if (recognizer != NULL) {
321 tempConfig.mNumComponents = recognizer->getNumComponents();
322 labels = recognizer->getLabels();
324 return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT;
326 } else if (algName == "LBPH") {
327 tempRecognizer = cv::face::LBPHFaceRecognizer::create();
328 tempRecognizer->read(storage.root());
329 cv::face::LBPHFaceRecognizer* recognizer = dynamic_cast<cv::face::LBPHFaceRecognizer*>(tempRecognizer.get());
330 if (recognizer != NULL) {
331 tempConfig.mModelType =
332 MEDIA_VISION_FACE_MODEL_TYPE_LBPH;
333 tempConfig.mGridX = recognizer->getGridX();
334 tempConfig.mGridY = recognizer->getGridY();
335 tempConfig.mNeighbors = recognizer->getNeighbors();
336 tempConfig.mRadius = recognizer->getRadius();
337 labels = recognizer->getLabels();
339 return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT;
342 tempConfig = FaceRecognitionModelConfig();
343 LOGE("Failed to load face recognition model from file. File is in "
344 "unsupported format");
348 return MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT;
350 ParseOpenCVLabels(labels, tempLearnedLabels);
351 tempConfig.mThreshold = tempRecognizer->getThreshold();
353 LOGD("Recognition model of [%s] type has been loaded from file",
358 m_recognizer = tempRecognizer;
359 m_learnAlgorithmConfig = tempConfig;
360 m_canRecognize = (canRecognize == 1);
361 m_learnedLabels.clear();
362 m_learnedLabels = tempLearnedLabels;
364 return MEDIA_VISION_ERROR_NONE;
367 int FaceRecognitionModel::addFaceExample(
368 const cv::Mat& faceImage,
371 m_faceSamples[faceLabel].push_back(faceImage);
373 LOGD("Added face image example for label %i for recognition model",
376 return MEDIA_VISION_ERROR_NONE;
379 int FaceRecognitionModel::resetFaceExamples(void)
381 m_faceSamples.clear();
383 LOGD("All face image examples have been removed from recognition model");
385 return MEDIA_VISION_ERROR_NONE;
388 int FaceRecognitionModel::resetFaceExamples(int faceLabel)
390 if (1 > m_faceSamples.erase(faceLabel)) {
391 LOGD("Failed to remove face image examples for label %i. "
392 "No such examples", faceLabel);
394 return MEDIA_VISION_ERROR_KEY_NOT_AVAILABLE;
397 LOGD("Face image examples for label %i have been removed from "
398 "recognition model", faceLabel);
400 return MEDIA_VISION_ERROR_NONE;
403 const std::set<int>& FaceRecognitionModel::getFaceLabels(void) const
405 return m_learnedLabels;
408 int FaceRecognitionModel::learn(const FaceRecognitionModelConfig& config)
410 /* Check number of classes collected for learning, some algorithms
411 * require specific class number constraints. For example, Fisherfaces
412 * requires more that 1 class in training set */
413 if (MEDIA_VISION_FACE_MODEL_TYPE_FISHERFACES == config.mModelType &&
414 m_faceSamples.size() < 2) {
415 LOGE("Can't apply Fisherfaces learning algorithm. It requires at "
416 "least two classes (face labes) to learn on.");
418 return MEDIA_VISION_ERROR_INVALID_OPERATION;
421 bool isIncremental = false;
422 bool isUnisize = false;
424 if (MEDIA_VISION_FACE_MODEL_TYPE_LBPH == config.mModelType)
425 isIncremental = true;
427 if (MEDIA_VISION_FACE_MODEL_TYPE_EIGENFACES == config.mModelType ||
428 MEDIA_VISION_FACE_MODEL_TYPE_FISHERFACES == config.mModelType)
431 std::vector<cv::Mat> samples;
432 std::vector<int> labels;
433 std::set<int> learnedLabels;
436 learnedLabels.insert(m_learnedLabels.begin(), m_learnedLabels.end());
438 std::map<int, std::vector<cv::Mat> >::const_iterator it = m_faceSamples.begin();
440 for (; it != m_faceSamples.end(); ++it) {
441 const size_t faceClassSamplesSize = it->second.size();
442 labels.insert(labels.end(), faceClassSamplesSize, it->first);
443 learnedLabels.insert(it->first);
446 LOGD("%zu examples has been added with label %i",
447 it->second.size(), it->first);
448 samples.insert(samples.end(), it->second.begin(), it->second.end());
450 for (size_t sampleInd = 0; sampleInd < faceClassSamplesSize; ++sampleInd) {
451 cv::Mat resizedSample;
452 cv::resize(it->second[sampleInd],
454 cv::Size(config.mImgWidth, config.mImgHeight),
455 0.0, 0.0, cv::INTER_CUBIC);
456 samples.push_back(resizedSample);
461 const size_t samplesSize = samples.size();
462 const size_t labelsSize = labels.size();
464 if (0 != samplesSize && samplesSize == labelsSize) {
465 LOGD("Start to learn the model for %zu samples and %zu labels",
466 samplesSize, labelsSize);
468 if (m_learnAlgorithmConfig != config || m_recognizer.empty())
469 m_recognizer = CreateRecognitionAlgorithm(config);
471 if (m_recognizer.empty()) {
472 LOGE("Can't create recognition algorithm for recognition model. "
473 "Configuration is not supported by any of known algorithms.");
475 return MEDIA_VISION_ERROR_NOT_SUPPORTED;
478 isIncremental ? m_recognizer->update(samples, labels) :
479 m_recognizer->train(samples, labels);
480 m_canRecognize = true;
481 m_learnedLabels.clear();
482 m_learnedLabels = learnedLabels;
484 LOGE("Can't create recognition algorithm for no examples. Try to add "
485 "some face examples before learning");
487 return MEDIA_VISION_ERROR_NO_DATA;
490 m_learnAlgorithmConfig = config;
492 LOGD("Recognition model has been learned");
494 return MEDIA_VISION_ERROR_NONE;
497 int FaceRecognitionModel::recognize(const cv::Mat& image, FaceRecognitionResults& results)
499 if (!m_recognizer.empty() && m_canRecognize) {
500 double absConf = 0.0;
501 cv::Mat predictionImg(m_learnAlgorithmConfig.mImgWidth,
502 m_learnAlgorithmConfig.mImgHeight, CV_8UC1);
504 if ((MEDIA_VISION_FACE_MODEL_TYPE_EIGENFACES == m_learnAlgorithmConfig.mModelType ||
505 MEDIA_VISION_FACE_MODEL_TYPE_FISHERFACES == m_learnAlgorithmConfig.mModelType) &&
506 (image.cols != m_learnAlgorithmConfig.mImgWidth ||
507 image.rows != m_learnAlgorithmConfig.mImgHeight))
508 cv::resize(image, predictionImg, predictionImg.size());
510 predictionImg = image;
512 m_recognizer->predict(predictionImg, results.mFaceLabel, absConf);
514 if (-1 != results.mFaceLabel) {
515 double normShift = 7.5;
516 double normSmooth = 0.05;
518 if (MEDIA_VISION_FACE_MODEL_TYPE_EIGENFACES == m_learnAlgorithmConfig.mModelType) {
521 } else if (MEDIA_VISION_FACE_MODEL_TYPE_FISHERFACES == m_learnAlgorithmConfig.mModelType) {
526 /* Normalize the absolute value of the confidence */
527 absConf = exp(normShift - (normSmooth * absConf));
528 results.mConfidence = absConf / (1 + absConf);
529 results.mIsRecognized = true;
531 results.mConfidence = 0.0;
532 results.mIsRecognized = false;
535 results.mFaceLocation = cv::Rect(0, 0, image.cols, image.rows);
537 LOGE("Attempt to recognize faces with untrained model");
538 return MEDIA_VISION_ERROR_INVALID_OPERATION;
541 return MEDIA_VISION_ERROR_NONE;
544 cv::Ptr<cv::face::FaceRecognizer> FaceRecognitionModel::CreateRecognitionAlgorithm(
545 const FaceRecognitionModelConfig& config)
547 cv::Ptr<cv::face::FaceRecognizer> tempRecognizer;
548 switch (config.mModelType) {
549 case MEDIA_VISION_FACE_MODEL_TYPE_EIGENFACES:
550 tempRecognizer = cv::face::EigenFaceRecognizer::create(
551 config.mNumComponents,
554 case MEDIA_VISION_FACE_MODEL_TYPE_FISHERFACES:
555 tempRecognizer = cv::face::FisherFaceRecognizer::create(
556 config.mNumComponents,
559 case MEDIA_VISION_FACE_MODEL_TYPE_LBPH:
560 tempRecognizer = cv::face::LBPHFaceRecognizer::create(
568 LOGE("Unknown FaceRecognition model");
571 return tempRecognizer;