嵌入式人工智能应用-第三章 opencv操作8 图像特征之LBP特征 下

1 人脸识别

人脸识别可以基于 LBP 特征 来实现,OpenCV 实际上已经为我们封装好了一个完整的 LBP 人脸识别模型。确保你安装的是 OpenCV 3.4+ 或 OpenCV 4.x(含 opencv_contrib)。低的版本无法运行。

OpenCV 提供了三个主流的人脸识别器:

在这里插入图片描述

2 实现步骤

1.收集人脸数据(灰度图,每张图对应一个标签 ID)

  1. 使用 LBPHFaceRecognizer 训练模型

  2. 保存或加载模型

2.1 搜集图片,进行预处理

2.1.1 图片搜集和灰度转化

每个人最好准备 10 张不同角度和光照的灰度图,也可以使用开源数据库。

  1. LFW(Labeled Faces in the Wild): 包含多个名人的人脸图像,每个图像都有相应的标签。
  2. FER2013: 用于情感识别,包含多种情感的人脸图像。
  3. CelebA: 包含超过20万张名人图像,并提供多种标签(如性别、年龄、表情等)。

2.1.2 控制图片尺寸

  1. 图像统一尺寸(如 100x100),减少计算量。

2.1.3 图像预处理-直方图均衡

图像预处理:equalizeHist() 可以增强对比度。

图像的“直方图”描述的是图像中各个灰度级(0~255)像素的分布情况。例如,一个图像中灰度值为100的像素很多,那它在直方图中就对应一个高柱子。
但有些图像的直方图可能非常集中,比如都集中在暗部(灰度0~50),那么图像看起来就很“灰暗”,对比度很低。
直方图均衡的目标就是让图像的灰度分布更“均匀”,也就是让整个灰度范围都能被利用,这样图像的明暗层次感就增强了。

在这里插入图片描述

2.1.4 具体代码

#include <iostream>
#include <dirent.h>
#include <sys/stat.h>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

// 设置统一尺寸
const Size targetSize(100, 100);

// 目录路径
const string input_dir = "dataset/";
const string output_dir = "processed/";

bool isImageFile(const string& filename) {
    return filename.find(".jpg") != string::npos ||
           filename.find(".png") != string::npos ||
           filename.find(".jpeg") != string::npos;
}

int main() {
    // 创建输出目录
    mkdir(output_dir.c_str(), 0755);

    DIR* dir = opendir(input_dir.c_str());
    if (!dir) {
        cerr << "无法打开目录: " << input_dir << endl;
        return -1;
    }

    dirent* entry;
    while ((entry = readdir(dir)) != nullptr) {
        string filename = entry->d_name;
        if (filename == "." || filename == "..") continue;
        if (!isImageFile(filename)) continue;

        string input_path = input_dir + filename;
        Mat img = imread(input_path);
        if (img.empty()) {
            cerr << "无法读取图像: " << input_path << endl;
            continue;
        }

        // 转为灰度
        Mat gray;
        cvtColor(img, gray, COLOR_BGR2GRAY);

        // 缩放
        Mat resized;
        resize(gray, resized, targetSize);

        // 直方图均衡
        Mat equalized;
        equalizeHist(resized, equalized);

        // 保存处理后的图像
        string output_path = output_dir + filename;
        imwrite(output_path, equalized);

        cout << "处理并保存: " << output_path << endl;
      }

    closedir(dir);
    return 0;
}

处理结果:

./processed/
├── harry_01.jpeg
├── harry_02.jpeg
├── harry_03.jpeg
├── hermi_01.jpeg
├── hermi_02.jpeg
└── hermi_03.jpeg

在这里插入图片描述
处理后结果
在这里插入图片描述

2.2 执行训练模块(LBPH)

#include <opencv2/opencv.hpp>
#include <opencv2/face.hpp>
#include <iostream>
#include <fstream>
#include <dirent.h>
#include <map>

using namespace std;
using namespace cv;
using namespace cv::face;

const string image_dir = "processed/";
const string model_path = "lbph_model.xml";
const string label_file = "labels.txt";

bool isImageFile(const string& filename) {
    return filename.find(".jpg") != string::npos ||
           filename.find(".png") != string::npos ||
           filename.find(".jpeg") != string::npos;
}

// 从文件名中提取标签前缀(如 person1_01.jpg -> person1)
string extractNamePrefix(const string& filename) {
    return filename.substr(0, filename.find("_"));
}

int main() {
    vector<Mat> images;
    vector<int> labels;
    map<string, int> nameToLabel;
    int nextLabel = 1;

    DIR* dir = opendir(image_dir.c_str());
    if (!dir) {
        cerr << "无法打开图像目录: " << image_dir << endl;
        return -1;
    }

    dirent* entry;
    while ((entry = readdir(dir)) != nullptr) {
        string filename = entry->d_name;
        if (filename == "." || filename == ".." || !isImageFile(filename)) continue;

        string filepath = image_dir + filename;
        Mat img = imread(filepath, IMREAD_GRAYSCALE);
        if (img.empty()) continue;

        // resize 以防尺寸不一致
        resize(img, img, Size(100, 100));

        string name = extractNamePrefix(filename);
        if (nameToLabel.find(name) == nameToLabel.end()) {
            nameToLabel[name] = nextLabel++;
        }

        int label = nameToLabel[name];
        images.push_back(img);
        labels.push_back(label);
    }
    closedir(dir);

    if (images.empty()) {
        cerr << "没有找到图像用于训练。" << endl;
        return -1;
    }

    // 训练模型
    Ptr<LBPHFaceRecognizer> recognizer = LBPHFaceRecognizer::create();
    recognizer->train(images, labels);
    recognizer->save(model_path);
    cout << "✅ 模型保存至: " << model_path << endl;

    // 保存标签映射
    ofstream labelOut(label_file);
    for (const auto& pair : nameToLabel) {
        labelOut << pair.second << " " << pair.first << endl;
        cout << "标签映射: " << pair.second << " -> " << pair.first << endl;
    }
    labelOut.close();
    cout << "✅ 标签文件保存至: " << label_file << endl;

    return 0;
}


处理结果输出:

在这里插入图片描述

2.3 加载模型并预测:

#include <opencv2/opencv.hpp>
#include <opencv2/face.hpp>
#include <iostream>

using namespace std;
using namespace cv;
using namespace cv::face;

int main(int argc, char** argv) {
    if (argc != 2) {
        cout << "用法: " << argv[0] << " test_image.jpg" << endl;
        return -1;
    }

    string model_path = "lbph_model.xml";
    string test_image_path = argv[1];

    // 加载模型
    Ptr<LBPHFaceRecognizer> recognizer = LBPHFaceRecognizer::create();
    recognizer->read(model_path);

    // 读取灰度图像
    Mat img = imread(test_image_path, IMREAD_GRAYSCALE);
    if (img.empty()) {
        cerr << "无法加载图像: " << test_image_path << endl;
        return -1;
    }

    // 图像尺寸应与训练时一致(例如100x100),否则 resize
    Size expectedSize(100, 100);
    resize(img, img, expectedSize);

    // 预测标签与置信度
    int predictedLabel = -1;
    double confidence = 0.0;
    recognizer->predict(img, predictedLabel, confidence);

    cout << "识别结果: 标签 = " << predictedLabel << ", 置信度 = " << confidence << endl;

    // 显示图像并标注结果
    Mat showImg;
    cvtColor(img, showImg, COLOR_GRAY2BGR);
    putText(showImg, "Label: " + to_string(predictedLabel), Point(10, 25),
            FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 255, 0), 2);
    imshow("Test Image", showImg);
    waitKey(0);

    return 0;
}

在这里插入图片描述

3 人脸识别总结

3.1 置信度介绍

在这里插入图片描述

在 recognizer->predict() 中返回的置信度(double confidence)表示模型对这个预测的“不确定性”:

值越小,模型越确信预测是对的(越“像”训练图像)

值越大,模型越不确定(与任何训练图像都不够相似)

这是 LBPH 的内部距离度量(欧氏距离),不是概率,范围没有固定的上限。

3.2 提升置信度方法

  1. 每人训练图片数量多一些(8~10张效果最好)
  2. 表情、角度、光照变化要有覆盖
  3. 训练图像分辨率一致(100x100)
  4. 图像预处理:灰度 + 直方图均衡
  5. 避免训练图像重复或过于相似
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数贾电子科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值