C++ OpenCV从图像中获取人脸,并使用模型进行预测

C++ OpenCV从图像中获取人脸,并使用模型进行预测,c++,opencv,face-recognition,C++,Opencv,Face Recognition,从灰度图像(已转换为cv::Mat)检索人脸的代码工作异常, 我做错了什么 // in initializer list model(cv::face::FisherFaceRecognizer::create()) // .... const cv::Mat grayscale = cv::imread("photo_15.jpeg",cv::IMREAD_GRAYSCALE); std::vector<cv::Rect> faceCandidates; m_cascade.d

从灰度图像(已转换为cv::Mat)检索人脸的代码工作异常, 我做错了什么

// in initializer list 
model(cv::face::FisherFaceRecognizer::create())
// ....
const cv::Mat grayscale = cv::imread("photo_15.jpeg",cv::IMREAD_GRAYSCALE);

std::vector<cv::Rect> faceCandidates;
m_cascade.detectMultiScale(grayscale, faceCandidates);

uint32 label = -1;         
double confidence = 0.0;
// this line for the testing purposes only
model->predict(grayscale, label, confidence);
这段代码的工作原理是完全错误的:它总是产生错误的标签,即使我使用培训照片集中的识别照片,置信度约为45-46K

知道我做错了什么吗? 对于测试:我尝试用fisher、eigen和lbph执行此操作,但结果相同

更新:应用程序中的每个模型都是几个用户组,每个用户都有2-6张照片,所以这就是我在模型中培训几个用户的原因

以下是培训模型的代码:

std::size_t
Recognizer::extractFacesAndConvertGrayscale(const QByteArray &rgb888, std::vector<cv::Mat> &faces)
{
    cv::Mat frame = cv::imdecode(std::vector<char>{rgb888.cbegin(), rgb888.cend()}, cv::IMREAD_GRAYSCALE);
    std::vector<cv::Rect> faceCandidates;
    m_cascade.detectMultiScale(frame, faceCandidates);
    int label = 0;
    for(const auto &face : faceCandidates) {
        cv::Mat faceResized;
        cv::resize(cv::Mat{frame, face}, faceResized,
                   cv::Size(this->m_size.width(), this->m_size.height()));

        faces.push_back(faceResized);
    }

    return faceCandidates.size();
}

bool Recognizer::train(const std::vector<qint32> &labels, const std::vector<QByteArray> &rgb888s)
{
    if (labels.empty() || rgb888s.empty() || labels.size() != rgb888s.size())
        return false;

    std::vector<cv::Mat> mats = {};
    std::vector<int32_t> processedLabels = {};
    std::size_t i = 0;
    for(const QByteArray &data : rgb888s)
    {
        std::size_t count = this->extractFacesAndConvertGrayscale(data, mats);
        if (count)
            std::fill_n(std::back_inserter(processedLabels), count, labels[i++]);
    }
    m_model->train(mats, processedLabels);

    return true;
}
std::size\u t
识别器::提取人脸和转换灰度(常量QByteArray和rgb888,标准::向量和人脸)
{
cv::Mat frame=cv::imdecode(std::vector{rgb888.cbegin(),rgb888.cend()},cv::IMREAD_GRAYSCALE);
std::矢量人脸候选;
m_级联检测多尺度(帧、面候选);
int-label=0;
用于(常量自动和面:面候选){
cv::调整垫面尺寸;
cv::resize(cv::Mat{frame,face},faceResized,
cv::Size(this->m_Size.width(),this->m_Size.height());
面。向后推(面大小调整);
}
返回faceCandidates.size();
}
bool识别器::train(const std::vector&labels,const std::vector&rgb888s)
{
如果(labels.empty()| | rgb888s.empty()| | labels.size()!=rgb888s.size())
返回false;
std::vector mats={};
std::vector processedLabels={};
标准:尺寸i=0;
用于(常数QByteArray和数据:rgb888s)
{
std::size\u t count=this->extractFacesAndConvertGrayscale(数据、垫子);
如果(计数)
std::fill_n(std::back_inserter(processedLabels),计数,标签[i++];
}
m_模型->列车(垫子、加工标签);
返回true;
}

我们在评论中解决了这个问题,但供将来参考:

事实上,这条线

//此行仅用于测试目的
模型->预测(灰度、标签、置信度);
比…更有信心

//识别当前人脸。
m_模型->预测(面部大小、标签、置信度);
发生的原因是模型使用非裁剪图像进行训练,而检测器裁剪人脸

为了匹配输入,应使用裁剪面对模型进行训练,而不是使用带有预测的整个图像:

  • 由于多尺度检测,分类器的执行与原始图像中的人脸大小无关;i、 图像中面的大小和位置成为不变量
  • 背景不影响分类。原始输入的纵横比为16:9,因此至少图像的侧面会在描述符中产生噪声

faceCandidateRegion的大小/长宽比是多少?刚刚更新的帖子。实际上,我已经尝试过使用类似(…,1.0,1.0,cv::INTER_CUBIC)的东西调整大小;同样错误的结果有趣的是
model->predict(灰度、标签、置信度)工作正常。您是否裁剪了人脸来训练分类器?在这种情况下,您可能应该使用
m_cascade
来检测数据库图像中的人脸,然后使用这些来训练模型。非常感谢!如果你可以回答为“回答”,我会将你的回答标记为一种提高你的速度的解决方案)如何调整带有裁剪面的cv::Mats的大小(对于火车来说)重要吗?“如何”-我指的是cv::resize(…)函数的插值参数?我通常对默认值(双线性插值)没有问题,因为它在性能(最近邻)和视觉精度(双三次)之间有一个公平的平衡。虽然你总是可以测试这两种情况,看看你的分类器选择是否有差异。似乎这并没有解决我的问题。现在我有5张从训练时的初始照片中剪下的脸(只有一个人),但是预测结果(置信度)并不依赖于训练集中的存在性测试脸,或者这是一张未知脸,置信度非常接近。另一方面,如果我为训练改变面部cv::Mats的大小,信心也在改变:对于1000x1000,信心约为9000,对于100x100,信心约为2500是的,大小影响最终的“信心”。使用
FisherFaceRecognizer
,它返回描述符之间的L2范数(距离),该距离似乎根据输入图像大小进行缩放。所以,您不应该使用绝对值来进行比较,而是相对于您的系统。尝试使用两组不同的面(或更多)训练模型,使用相同的算法进行裁剪,然后检查属于该组的面与不属于该组的面之间的距离。我们通常用于确定置信阈值的算法有点不同,但遵循这一思路。刚改为Fisher,使用两个人,每个人有5张图像(如所讨论的,在训练前用DetectMultiScale裁剪)。并执行三个测试:1)训练集中第一个人的脸-距离=0,标签=2(针对我的情况正确);2)训练集中第二个人的脸,相同距离=0,标签=正确;3) 第二个人的脸不来自训练集;置信度=41655;标签不正确!(标签是指第一人称。预测代码使用相同的功能裁剪传入的照片。再次检查第二人称的另一张照片,类似的置信度和不正确的标签。。。
std::size_t
Recognizer::extractFacesAndConvertGrayscale(const QByteArray &rgb888, std::vector<cv::Mat> &faces)
{
    cv::Mat frame = cv::imdecode(std::vector<char>{rgb888.cbegin(), rgb888.cend()}, cv::IMREAD_GRAYSCALE);
    std::vector<cv::Rect> faceCandidates;
    m_cascade.detectMultiScale(frame, faceCandidates);
    int label = 0;
    for(const auto &face : faceCandidates) {
        cv::Mat faceResized;
        cv::resize(cv::Mat{frame, face}, faceResized,
                   cv::Size(this->m_size.width(), this->m_size.height()));

        faces.push_back(faceResized);
    }

    return faceCandidates.size();
}

bool Recognizer::train(const std::vector<qint32> &labels, const std::vector<QByteArray> &rgb888s)
{
    if (labels.empty() || rgb888s.empty() || labels.size() != rgb888s.size())
        return false;

    std::vector<cv::Mat> mats = {};
    std::vector<int32_t> processedLabels = {};
    std::size_t i = 0;
    for(const QByteArray &data : rgb888s)
    {
        std::size_t count = this->extractFacesAndConvertGrayscale(data, mats);
        if (count)
            std::fill_n(std::back_inserter(processedLabels), count, labels[i++]);
    }
    m_model->train(mats, processedLabels);

    return true;
}