Smorodov, спасибо за развернутый и подробный ответ! Перечитал вашу статью раз 5, но очень туго до меня доходит такая сложная информация. Есть как минимум прогресс с пониманием базиса. Теперь я знаю что каждый раз его пересчитывать не нужно. Раз так туго до меня доходит теория, давайте попробуем разобрать код, если вы не против.
Ptr<FaceRecognizer> model = Algorithm::create<FaceRecognizer>("FaceRecognizer.Eigenfaces");//Модель распознавания лица
vector<Mat> preprocessedFaces;// Массив для хранения полученных с камеры уже нормализованных лиц
vector<int> faceLabels;// Массив для хранения меток для каждого нового лица
// Запускаем режим сбора лиц с камеры MODE_COLLECT_FACES
preprocessedFaces.push_back(cv::Mat уже обработанное лицо);// Предположим что мы собрали 100 снимков одного лица
faceLabels.push_back(1);//Петр Петрович Петров
preprocessedFaces.push_back(cv::Mat уже обработанное лицо);// Предположим что мы собрали 100 снимков другого лица
faceLabels.push_back(2);//Иван Иванович Иванов
// Далее мы тренируем PCA базис для этих двух человек
model->train(preprocessedFaces, faceLabels);
// Далее мы сохраняем наш натренированый базис в файл с расширением yml, чтобы после перезапуска программы больше не проходить процесс тренировки
model->save("C:\\eigen.yml");
// Далее мы переходим в режим распознавания лица MODE_RECOGNITION
reconstructedFace = reconstructFace(model, preprocessedFace);//Так как значение этой функции я не понимаю, приведу ниже ее реализацию и большая просьба может ктонибудь объяснить по строчно что означает каждая строчка этой функции
Mat reconstructFace(const Ptr<FaceRecognizer> model, const Mat preprocessedFace)
{
try {
Mat eigenvectors = model->get<Mat>("eigenvectors");// Что здесь происходит и для чего это???
Mat averageFaceRow = model->get<Mat>("mean");// Что здесь происходит и для чего это???
int faceHeight = preprocessedFace.rows;
// Project the input image onto the PCA subspace.
Mat projection = subspaceProject(eigenvectors, averageFaceRow, preprocessedFace.reshape(1,1));// Что здесь происходит и для чего это???
// Generate the reconstructed face back from the PCA subspace.
Mat reconstructionRow = subspaceReconstruct(eigenvectors, averageFaceRow, projection); // Что здесь происходит и для чего это???
// Convert the float row matrix to a regular 8-bit image. Note that we
// shouldn't use "getImageFrom1DFloatMat()" because we don't want to normalize
// the data since it is already at the perfect scale.
// Make it a rectangular shaped image instead of a single row.
Mat reconstructionMat = reconstructionRow.reshape(1, faceHeight);// Что здесь происходит и для чего это???
// Convert the floating-point pixels to regular 8-bit uchar pixels.
Mat reconstructedFace = Mat(reconstructionMat.size(), CV_8U);// Что здесь происходит и для чего это???
reconstructionMat.convertTo(reconstructedFace, CV_8U, 1, 0);// Что здесь происходит и для чего это???
//printMatInfo(reconstructedFace, "reconstructedFace");
return reconstructedFace;// Это понятно, если все операции прошли нормально возвращаем какоето измененное лицо.. Что мы должны получить на выходе?
} catch (cv::Exception e) {
//cout << "WARNING: Missing FaceRecognizer properties." << endl;
return Mat();// Здесь тоже понятно, если какая либо ошибка возврашаеи пустой Mat.
}
}
// Verify whether the reconstructed face looks like the preprocessed face, otherwise it is probably an unknown person.
double similarity = getSimilarity(preprocessedFace, reconstructedFace);// Работа этой функции тоже непонятна, ниже вставлю код реализации этой функции. Просьба объяснить как она работает.
double getSimilarity(const Mat A, const Mat B)
{
if (A.rows > 0 && A.rows == B.rows && A.cols > 0 && A.cols == B.cols) {// Что здесь происходит???
// Calculate the L2 relative error between the 2 images.
double errorL2 = norm(A, B, CV_L2);// Что здесь происходит???
// Convert to a reasonable scale, since L2 error is summed across all pixels of the image.
double similarity = errorL2 / (double)(A.rows * A.cols);// Что здесь происходит???
return similarity;
}
else {
//cout << "WARNING: Images have a different size in 'getSimilarity()'." << endl;
return 100000000.0; // Return a bad value
}
}
//Далее понятно
if (similarity < UNKNOWN_PERSON_THRESHOLD) {.// Если значение схожести изображения в пределах допустимого порога
// Identify who the person is in the preprocessed face image.
identity = model->predict(preprocessedFace);// Здесь мы получаем дистанцию до ближайшей фотографии в базисе или я не прав???
outputStr = toString(identity);
}
else {
// Since the confidence is low, assume it is an unknown person.
outputStr = "Unknown";
}
///////////// Далее допустим мы перезапускаем программу//////////////////////
//И после перезапуска мы хотим определить лица Петра Петровича Петрова и Ивана Ивановича Иванова;
// Я так понимаю что первым делом мы загружаем ранее сохраненный PCA базис
model->load("C:\\eigen.yml");
// Далее мы сразу переходим в режим распознавания лица MODE_RECOGNITION, не прозодя процесс тренировки то есть режим MODE_TRAINING нам не нужен???
reconstructedFace = reconstructFace(model, preprocessedFace);
//Выясняем на сколько совпадает реконструированное лицо с лицом в текущем кадре
double similarity = getSimilarity(preprocessedFace, reconstructedFace);
// И далее определяем известное это лицо или нет
if (similarity < UNKNOWN_PERSON_THRESHOLD) {.
// Identify who the person is in the preprocessed face image.
identity = model->predict(preprocessedFace);// Здесь мы получаем дистанцию до ближайшей фотографии в базисе или я не прав???
outputStr = toString(identity);
}
else {
// Since the confidence is low, assume it is an unknown person.
outputStr = "Unknown";
}
А теперь появляются основные вопросы:
1. После перезапуска программы теперь мне надо натренировать базу на новое лицо Сидора Сидоровича. Что мне для этого необходимо сделать? Получить 100 кадров нового лица и пройти процесс тренировки заново? Если я пройду тренировку заново для нового лица, то откуда модель будет брать Массивы лиц "preprocessedFaces" Петра Петровича и Ивана Ивановича, а также присвоенные Массив меток "faceLabeles". Получается я должен был сохранить эти массивы в файл прежде чем перезапускать программу?
2. И если лиц будет тысяча, это получается каждый раз после перезапуска программы я должен загружать в массив preprocessedFaces всю 1000 лиц и загружать в массив faceLabeles 1000 меток?
Просьба объясните на пальцах дураку. Если можно простыми словами.