【问题标题】:OpenCV get faces from image and predict with modelOpenCV 从图像中获取人脸并使用模型进行预测
【发布时间】:2020-08-17 23:04:00
【问题描述】:

从灰度图像中检索人脸的代码(已经转换为 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);

这很好用:标签指的是正确的人和 10 以内的信心。 但让我们继续这个函数代码:

for (auto &faceCandidateRegion : faceCandidates) {
    cv::Mat faceResized;
    // size_ is a member and contains 1280x720 for my case, equal to model trained photos. 
    cv::resize( cv::Mat(grayscale, faceCandidateRegion), faceResized, cv::Size(size_.width(), size_.height()));

    // Recognize current face.
    m_model->predict(faceResized, label, confidence);
// ... other processing 

这段代码完全错误:即使我使用训练照片集中的识别照片,它总是产生不正确的标签,置信度约为 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;
}

【问题讨论】:

  • faceCandidateRegion 的大小/纵横比是多少?
  • 刚刚更新的帖子。实际上我已经尝试过使用 (... , 1.0, 1.0, cv::INTER_CUBIC);有同样的错误结果
  • 有趣的是model-&gt;predict(grayscale, label, confidence); 工作正常。您是否裁剪了人脸来训练分类器?
  • 在这种情况下,您可能应该使用m_cascade 来检测数据库图像中的人脸,然后使用这些人脸来训练模型。
  • 非常感谢!如果您可以回复为“答案”,我会将您的回复标记为提高评分的解决方案)

标签: c++ opencv face-recognition


【解决方案1】:

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

这一行的事实

// this line for the testing purposes only
model->predict(grayscale, label, confidence);

更有信心
// Recognize current face.
m_model->predict(faceResized, label, confidence);

发生是因为模型是用未裁剪的图像训练的,而检测器裁剪了人脸。

为了匹配输入,而不是使用整个图像进行预测,应该使用裁剪后的人脸训练模型:

  • 由于多尺度检测,分类器的执行独立于原始图像中人脸的大小;即图像中人脸的大小和位置成为不变量。
  • 背景不会干扰分类。原始输入的宽高比为 16:9,因此至少图像的两侧会在描述符中产生噪点。

【讨论】:

  • 重要的是如何调整带有裁剪面的 cv::Mats 的大小(对于火车)? “如何” - 我的意思是 cv::resize(...) 函数的插值参数?
  • 我通常对默认(双线性插值)没有问题,因为它在性能(最近邻)和视觉精度(双三次)之间取得了公平的平衡。虽然您始终可以测试这两种情况,看看您的分类器选择是否有差异。
  • 似乎没有解决我的问题。现在我在训练时从初始照片中有 5 张裁剪的人脸(只有一个人),但是预测结果(置信度)不依赖于训练集中是否存在测试人脸或者这是未知人脸,置信度非常接近彼此。另一方面 - 如果我为训练更改面部 cv::Mats 的大小,信心也会发生变化:对于 1000x1000,信心大约是 9000,对于 100x100,信心大约是 2500
  • 是的,大小会影响最终的“信心”。使用FisherFaceRecognizer,它返回描述符之间的 L2 范数(距离),这似乎是根据输入图像大小进行缩放的。所以,你不应该使用绝对值来比较,而是相对于你的系统。尝试使用 2 组不同的人脸(或更多)训练模型,使用相同的算法进行裁剪,然后检查属于该组的人脸与不属于该组人脸的距离。我们一般用来确定置信度阈值的算法略有不同,但遵循了这个思路。
  • 刚刚更改为 Fisher,使用两个人,每个人有 5 张图像(如上所述 - 在训练前使用 DetectMultiScale 裁剪。并执行三个测试:1)来自训练集的第一个人的脸 - 距离 = 0, label=2(对我来说是正确的); 2)第二个人的脸来自训练集,相同的距离=0,标签=正确;3)第二个人的脸不是来自训练集;信心=41655;标签错误! (标签是指第一人称。预测代码使用相同的功能来裁剪传入的照片。用另一张第二人称的照片仔细检查了这一点,相似的信心和错误的标签......
猜你喜欢
  • 2016-09-17
  • 2013-03-23
  • 2017-10-02
  • 2012-02-17
  • 2012-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-28
相关资源
最近更新 更多