1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-21 19:33:03 +04:00
Files
opencv/modules/objdetect/src/face_recognize.cpp
T
quantizs e1b06371ad Added buffer-based model loading to FaceRecognizerSF
- Implemented a new `create` method in `FaceRecognizerSF` to allow model and configuration loading from memory buffers (std::vector<uchar>), similar to the existing functionality in `FaceDetectorYN`.
- Updated `face_recognize.cpp` with a new constructor in `FaceRecognizerSFImpl` that supports buffer-based loading for both model weights and network configuration.
- Ensured compatibility with both file-based and buffer-based model loading by maintaining consistent backend and target settings across both constructors.
- This change improves flexibility, allowing FaceRecognizerSF to be instantiated from memory buffers, which is useful for dynamic model loading scenarios such as embedded systems or applications where models are loaded in-memory.
2024-10-09 15:13:47 +02:00

219 lines
8.1 KiB
C++

// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "precomp.hpp"
#include "opencv2/core.hpp"
#ifdef HAVE_OPENCV_DNN
#include "opencv2/dnn.hpp"
#endif
#include <algorithm>
namespace cv
{
#ifdef HAVE_OPENCV_DNN
class FaceRecognizerSFImpl : public FaceRecognizerSF
{
public:
FaceRecognizerSFImpl(const String& model, const String& config, int backend_id, int target_id)
{
net = dnn::readNet(model, config);
CV_Assert(!net.empty());
net.setPreferableBackend(backend_id);
net.setPreferableTarget(target_id);
}
FaceRecognizerSFImpl(const String& framework,
const std::vector<uchar>& bufferModel,
const std::vector<uchar>& bufferConfig,
int backend_id, int target_id)
{
net = dnn::readNet(framework, bufferModel, bufferConfig);
CV_Assert(!net.empty());
net.setPreferableBackend(backend_id);
net.setPreferableTarget(target_id);
}
void alignCrop(InputArray _src_img, InputArray _face_mat, OutputArray _aligned_img) const override
{
Mat face_mat = _face_mat.getMat();
float src_point[5][2];
for (int row = 0; row < 5; ++row)
{
for(int col = 0; col < 2; ++col)
{
src_point[row][col] = face_mat.at<float>(0, row*2+col+4);
}
}
Mat warp_mat = getSimilarityTransformMatrix(src_point);
warpAffine(_src_img, _aligned_img, warp_mat, Size(112, 112), INTER_LINEAR);
}
void feature(InputArray _aligned_img, OutputArray _face_feature) override
{
Mat inputBolb = dnn::blobFromImage(_aligned_img, 1, Size(112, 112), Scalar(0, 0, 0), true, false);
net.setInput(inputBolb);
net.forward(_face_feature);
}
double match(InputArray _face_feature1, InputArray _face_feature2, int dis_type) const override
{
Mat face_feature1 = _face_feature1.getMat(), face_feature2 = _face_feature2.getMat();
normalize(face_feature1, face_feature1);
normalize(face_feature2, face_feature2);
if(dis_type == DisType::FR_COSINE){
return sum(face_feature1.mul(face_feature2))[0];
}else if(dis_type == DisType::FR_NORM_L2){
return norm(face_feature1, face_feature2);
}else{
throw std::invalid_argument("invalid parameter " + std::to_string(dis_type));
}
}
private:
Mat getSimilarityTransformMatrix(float src[5][2]) const {
float dst[5][2] = { {38.2946f, 51.6963f}, {73.5318f, 51.5014f}, {56.0252f, 71.7366f}, {41.5493f, 92.3655f}, {70.7299f, 92.2041f} };
float avg0 = (src[0][0] + src[1][0] + src[2][0] + src[3][0] + src[4][0]) / 5;
float avg1 = (src[0][1] + src[1][1] + src[2][1] + src[3][1] + src[4][1]) / 5;
//Compute mean of src and dst.
float src_mean[2] = { avg0, avg1 };
float dst_mean[2] = { 56.0262f, 71.9008f };
//Subtract mean from src and dst.
float src_demean[5][2];
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 5; j++)
{
src_demean[j][i] = src[j][i] - src_mean[i];
}
}
float dst_demean[5][2];
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 5; j++)
{
dst_demean[j][i] = dst[j][i] - dst_mean[i];
}
}
double A00 = 0.0, A01 = 0.0, A10 = 0.0, A11 = 0.0;
for (int i = 0; i < 5; i++)
A00 += dst_demean[i][0] * src_demean[i][0];
A00 = A00 / 5;
for (int i = 0; i < 5; i++)
A01 += dst_demean[i][0] * src_demean[i][1];
A01 = A01 / 5;
for (int i = 0; i < 5; i++)
A10 += dst_demean[i][1] * src_demean[i][0];
A10 = A10 / 5;
for (int i = 0; i < 5; i++)
A11 += dst_demean[i][1] * src_demean[i][1];
A11 = A11 / 5;
Mat A = (Mat_<double>(2, 2) << A00, A01, A10, A11);
double d[2] = { 1.0, 1.0 };
double detA = A00 * A11 - A01 * A10;
if (detA < 0)
d[1] = -1;
double T[3][3] = { {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0} };
Mat s, u, vt, v;
SVD::compute(A, s, u, vt);
double smax = s.ptr<double>(0)[0]>s.ptr<double>(1)[0] ? s.ptr<double>(0)[0] : s.ptr<double>(1)[0];
double tol = smax * 2 * FLT_MIN;
int rank = 0;
if (s.ptr<double>(0)[0]>tol)
rank += 1;
if (s.ptr<double>(1)[0]>tol)
rank += 1;
double arr_u[2][2] = { {u.ptr<double>(0)[0], u.ptr<double>(0)[1]}, {u.ptr<double>(1)[0], u.ptr<double>(1)[1]} };
double arr_vt[2][2] = { {vt.ptr<double>(0)[0], vt.ptr<double>(0)[1]}, {vt.ptr<double>(1)[0], vt.ptr<double>(1)[1]} };
double det_u = arr_u[0][0] * arr_u[1][1] - arr_u[0][1] * arr_u[1][0];
double det_vt = arr_vt[0][0] * arr_vt[1][1] - arr_vt[0][1] * arr_vt[1][0];
if (rank == 1)
{
if ((det_u*det_vt) > 0)
{
Mat uvt = u*vt;
T[0][0] = uvt.ptr<double>(0)[0];
T[0][1] = uvt.ptr<double>(0)[1];
T[1][0] = uvt.ptr<double>(1)[0];
T[1][1] = uvt.ptr<double>(1)[1];
}
else
{
double temp = d[1];
d[1] = -1;
Mat D = (Mat_<double>(2, 2) << d[0], 0.0, 0.0, d[1]);
Mat Dvt = D*vt;
Mat uDvt = u*Dvt;
T[0][0] = uDvt.ptr<double>(0)[0];
T[0][1] = uDvt.ptr<double>(0)[1];
T[1][0] = uDvt.ptr<double>(1)[0];
T[1][1] = uDvt.ptr<double>(1)[1];
d[1] = temp;
}
}
else
{
Mat D = (Mat_<double>(2, 2) << d[0], 0.0, 0.0, d[1]);
Mat Dvt = D*vt;
Mat uDvt = u*Dvt;
T[0][0] = uDvt.ptr<double>(0)[0];
T[0][1] = uDvt.ptr<double>(0)[1];
T[1][0] = uDvt.ptr<double>(1)[0];
T[1][1] = uDvt.ptr<double>(1)[1];
}
double var1 = 0.0;
for (int i = 0; i < 5; i++)
var1 += src_demean[i][0] * src_demean[i][0];
var1 = var1 / 5;
double var2 = 0.0;
for (int i = 0; i < 5; i++)
var2 += src_demean[i][1] * src_demean[i][1];
var2 = var2 / 5;
double scale = 1.0 / (var1 + var2)* (s.ptr<double>(0)[0] * d[0] + s.ptr<double>(1)[0] * d[1]);
double TS[2];
TS[0] = T[0][0] * src_mean[0] + T[0][1] * src_mean[1];
TS[1] = T[1][0] * src_mean[0] + T[1][1] * src_mean[1];
T[0][2] = dst_mean[0] - scale*TS[0];
T[1][2] = dst_mean[1] - scale*TS[1];
T[0][0] *= scale;
T[0][1] *= scale;
T[1][0] *= scale;
T[1][1] *= scale;
Mat transform_mat = (Mat_<double>(2, 3) << T[0][0], T[0][1], T[0][2], T[1][0], T[1][1], T[1][2]);
return transform_mat;
}
private:
dnn::Net net;
};
#endif
Ptr<FaceRecognizerSF> FaceRecognizerSF::create(const String& model, const String& config, int backend_id, int target_id)
{
#ifdef HAVE_OPENCV_DNN
return makePtr<FaceRecognizerSFImpl>(model, config, backend_id, target_id);
#else
CV_UNUSED(model); CV_UNUSED(config); CV_UNUSED(backend_id); CV_UNUSED(target_id);
CV_Error(cv::Error::StsNotImplemented, "cv::FaceRecognizerSF requires enabled 'dnn' module");
#endif
}
Ptr<FaceRecognizerSF> FaceRecognizerSF::create(const String& framework,
const std::vector<uchar>& bufferModel,
const std::vector<uchar>& bufferConfig,
int backend_id, int target_id)
{
#ifdef HAVE_OPENCV_DNN
return makePtr<FaceRecognizerSFImpl>(framework, bufferModel, bufferConfig, backend_id, target_id);
#else
CV_UNUSED(bufferModel); CV_UNUSED(bufferConfig); CV_UNUSED(backend_id); CV_UNUSED(target_id);
CV_Error(cv::Error::StsNotImplemented, "cv::FaceRecognizerSF requires enabled 'dnn' module");
#endif
}
} // namespace cv