That's actually a function a lot of people have asked me for and if you think this would be a useful feature, please open up a feature request on code.opencv.org for discussion. It's just, that I don't think every possible FaceRecognizer
has something like a distance as classifier output. So I don't think stuff like this belongs to a Generic FaceRecognizer API. The assumption of a distance array may hold for the available models, but not for all models I can think of; I'll come up with another solution as soon as I have some time.
All that said, implementing such a feature is very easy, I'll show you how to do it.
First of all you would need to extend cv::FaceRecognizer::predict
(in contrib.hpp
) like this:
// Gets a prediction from a FaceRecognizer.
virtual void predict(InputArray src, int &label, double &confidence, OutputArray distances = noArray()) const = 0;
The idea is that distances
, if given, holds all the distances for predicting the query in src
. Then you need to write the implementation (in facerec.cpp
), here is how to do it for the Eigenfaces model (exception handling stripped off for making the snippet easier to read):
void Eigenfaces::predict(InputArray _src, int &minClass, double &minDist, OutputArray distances) const {
// Get the data:
Mat src = _src.getMat();
// Project into PCA subspace:
Mat q = subspaceProject(_eigenvectors, _mean, src.reshape(1,1));
// Create the return Matrix:
if(distances.needed()) {
distances.create(_projections.size(), 1, CV_64FC1);
}
minDist = DBL_MAX;
minClass = -1;
for(size_t sampleIdx = 0; sampleIdx < _projections.size(); sampleIdx++) {
double dist = norm(_projections[sampleIdx], q, NORM_L2);
// Add to the resulting distance array:
if(distances.needed()) {
distances.getMat().at<double>(sampleIdx) = dist;
}
if((dist < minDist) && (dist < _threshold)) {
minDist = dist;
minClass = _labels.at<int>((int)sampleIdx);
}
}
}
So I simply create the matrix for the cv::OutputArray
if needed and fill it with the distance between the query and projected training samples.
Calling the function is then as easy as doing:
// Hold the return values of the predict call:
int label;
Mat distances;
// Predict the testSample. Put the threshold to DBL_MAX:
model->predict(testSample, label, DBL_MAX, distances);
// Output the distances:
cout << distances << endl;
And it will yield all the distances for the given sample:
[5319.568259578258; 5146.403381012799; 5162.0278252895; 4997.433648278493; ... ]
Please don't make a Pull Request of this to the OpenCV repository, as I have explained the reasons above.