double FacialFeatureRecognizer::compareFaces(Mat& face1, Mat& face2) { Mat eigenVectors = recognizer->getMat("eigenvectors"); Mat mean = recognizer->getMat("mean"); Mat proj = subspaceProject(eigenVectors, mean, face1.reshape(1,1)); Mat proj2 = subspaceProject(eigenVectors, mean, face2.reshape(1,1)); return norm(proj, proj2, NORM_L2); }
void PCAKNN::train(const list<Interval> &intervals, const bool console_output) { if (intervals.size() == 0) return; projections.clear(); n = 0; width = intervals.front().start->face.cols; height = intervals.front().start->face.rows; list<Interval>::const_iterator itr; vector<Face>::const_iterator start, end; //add the number of images from all intervals for (itr = intervals.cbegin(); itr != intervals.cend(); itr++) { n += itr->Length(); } Mat pca_matrix(static_cast<int>(n), width*height, data_type); int c = 0; for (itr = intervals.cbegin(); itr != intervals.cend(); itr++) { //for each image in the current interval for (start = itr->start, end = itr->end; start != end; start++, ++c) { if (console_output) printf("Preparing samples %d/%d\n", c + 1, n); //insert current image into pca_matrix Mat image_row = start->face.clone().reshape(1, 1); Mat row_i = pca_matrix.row(c); image_row.convertTo(row_i, data_type);//CV_64FC1 ? Face f; f.name = start->name; projections.push_back(f);//save the names for later } } if (console_output) printf("TRAINING...\n"); //Perfrom principal component analysis on pca_matrix PCA pca(pca_matrix, Mat(), CV_PCA_DATA_AS_ROW, pca_matrix.rows); //extract mean/eigenvalues mean = pca.mean.reshape(1, 1); ev = pca.eigenvalues.clone(); transpose(pca.eigenvectors, w); //project each face into subspace and save them with the name above for recognition for (unsigned int i = 0; i<n; ++i) { if (console_output) printf("Projecting %d/%d\n", i + 1, n);//project so subspace projections[i].face = subspaceProject(w, mean, pca_matrix.row(i)); } }
// Generate an approximately reconstructed face by back-projecting the eigenvectors & eigenvalues of the given (preprocessed) face. Mat reconstructFace(const Ptr<FaceRecognizer> model, const Mat preprocessedFace) { // Since we can only reconstruct the face for some types of FaceRecognizer models (ie: Eigenfaces or Fisherfaces), // we should surround the OpenCV calls by a try/catch block so we don't crash for other models. try { // Get some required data from the FaceRecognizer model. 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)); //printMatInfo(projection, "projection"); // Generate the reconstructed face back from the PCA subspace. Mat reconstructionRow = subspaceReconstruct(eigenvectors, averageFaceRow, projection); //printMatInfo(reconstructionRow, "reconstructionRow"); // 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(); } }
//k should be uneven to break ties string PCAKNN::recognize(const Mat &face, const unsigned int k, const bool use_distance_weighting, const bool console_output) const { string name = "N/A"; if (n <= k) return name; //project target face to subspace Mat target = subspaceProject(w, mean, face.reshape(1, 1)); vector<unsigned int> classes(k, 0); vector<double> distances(k, DBL_MAX); double dist = DBL_MAX; //find k nearest neighbours for (unsigned int i = 0; i < projections.size(); ++i) { dist = norm(projections[i].face, target, NORM_L2);//norml2 for (unsigned int j = 0; j < k; ++j) { if (dist < distances[j]) { //discard the worst match and shift remaining down for (int l = k - 1; l > j; --l) { distances[l] = distances[l - 1]; classes[l] = classes[l - 1]; } classes[j] = i; //set new best match distances[j] = dist; break; } } } map<string, Weighting> neighbours; //count occurence of classes for (unsigned int i = 0; i<k; ++i) { Weighting &weight = neighbours[projections[classes[i]].name]; weight.count++; weight.total_dist += distances[i]; } //evaluate voting if (use_distance_weighting) { double min_weight = DBL_MAX; for (map<string, Weighting>::iterator itr = neighbours.begin(); itr != neighbours.end(); ++itr) { double weight = itr->second.total_dist / (double)itr->second.count; //concider average weight instead of number of votes if (weight < min_weight) { min_weight = weight; name = itr->first; } if (console_output)printf("%s %f\n", itr->first.c_str(), weight); } } else { unsigned int max_count = 0; //choose the class with the most votes for (map<string, Weighting>::iterator itr = neighbours.begin(); itr != neighbours.end(); ++itr) { if (itr->second.count > max_count) { max_count = itr->second.count; name = itr->first; } if (console_output)printf("%s %d\n", itr->first.c_str(), itr->second.count); } } return name; }