基于opencv眨眼和表情判定

一、眨眼检测方法 (Wink Detection)

目标:给定一组图片或者视频,检测眨眼和沉默表情

​1.1 面部检测​:

使用Haar级联分类器(haarcascade_frontalface_alt.xml)检测人脸,对图像进行灰度转换和直方图均衡化预处理,提高检测效果

1.2 眼睛区域定位​:

在检测到的面部区域内,定义上半部分为眼睛区域
使用比例参数(eye_region_height_ratio)动态计算眼睛区域高度

1.3 ​眼睛检测​:

在眼睛区域内使用Haar级联分类器(haarcascade_eye.xml)检测眼睛
添加了眼睛宽高比过滤,只保留合理的眼睛检测结果(0.5 < 宽高比 < 2.0)

1.4 ​眨眼判断逻辑​:

  • 如果在眼睛区域只检测到一只眼睛,则判定为眨眼
  • 如果没有检测到面部但直接检测到一只眼睛,也判定为眨眼
  • 连续上下帧之间闭上两只眼睛也是眨眼的一种常见表现

​可视化反馈​:

  • 用紫色椭圆标记检测到的面部
  • 用青色椭圆标记检测到的眼睛
  • 当检测到眨眼时,用绿色椭圆标记面部

二、沉默表情检测方法 (Silence Detection)

2.1 ​面部检测​:

同样使用Haar级联分类器检测人脸,进行相同的预处理步骤

2.2 ​嘴部区域定位​:

在检测到的面部区域内,定义下半部分为嘴部区域,使用多个比例参数(mouth_region_width_ratio, mouth_region_height_ratio, mouth_region_y_offset_ratio)精确计算嘴部区域位置和大小

2.3 ​嘴部检测​:

在嘴部区域内使用专用的嘴部分类器(mouth.xml)检测嘴部
调整了检测参数(scaleFactor=1.2, minNeighbors=3)优化嘴部检测

2.4 ​沉默判断逻辑​:

如果在嘴部区域没有检测到任何嘴部,则判定为沉默表情
这与常见的"嘴唇紧闭"或"不张嘴"的沉默表情特征相符

2.5 ​可视化反馈​:

  • 用紫色椭圆标记检测到的面部
  • 用青色椭圆标记检测到的嘴部
  • 当检测到沉默表情时,用绿色椭圆标记面部
    mouse检测器地址
#include "opencv2/highgui.hpp"
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <memory>
#include <string>
#include "dirent.h"
#include <map>
using namespace std;
using namespace cv;

// 配置参数结构体
struct AppConfig {
   
   
    string opencv_root = "C:/Users/meetd/Downloads/opencv/";
    string faces_cascade_name = "haarcascade_frontalface_alt.xml";
    string eyes_cascade_name = "haarcascade_eye.xml";
    string mouth_cascade_name = "mouth.xml";

    // 检测参数
    float face_scale_factor = 1.1f;
    int face_min_neighbors = 3;
    Size face_min_size = Size(30, 30);

    float eye_scale_factor = 1.1f;
    int eye_min_neighbors = 4;
    Size eye_min_size = Size(20, 20);

    float mouth_scale_factor = 1.2f;
    int mouth_min_neighbors = 3;
    Size mouth_min_size = Size(30, 30);

    // 区域定义
    float mouth_region_height_ratio = 0.3f;
    float mouth_region_width_ratio = 0.6f;
    float mouth_region_y_offset_ratio = 0.65f;

    float eye_region_height_ratio = 0.5f;
};

// 资源管理类
class DirWrapper {
   
   
public:
    DirWrapper(const string& path) : dir(opendir(path.c_str())) {
   
   
        if (!dir) {
   
   
            throw runtime_error("Can't open folder " + path);
        }
    }

    ~DirWrapper() {
   
   
        if (dir) closedir(dir);
    }

    dirent* read() {
   
    return readdir(dir); }

private:
    DIR* dir;
};

// 检测模式枚举
enum class DetectionMode {
   
   
    WINK,
    SILENCE,
    WINK2,//双眼闭
    UNKNOWN
};

// 基础检测器类
class FaceFeatureDetector {
   
   
protected:
    CascadeClassifier face_cascade;
    CascadeClassifier feature_cascade;
    AppConfig config;

public:
    FaceFeatureDetector(const AppConfig& cfg) : config(cfg) {
   
   }

    virtual ~FaceFeatureDetector() = default;

    bool init() {
   
   
        string cascades_path = config.opencv_root + "build/etc/haarcascades/";
        if (!face_cascade.load(cascades_path + config.faces_cascade_name)) {
   
   
            cerr << "Error loading face cascade: " << config.faces_cascade_name << endl;
            return false;
        }
        return true;
    }

    virtual int detect(Mat& frame) = 0;

    int runOnFolder(const string& folder) {
   
   
        try {
   
   
            DirWrapper dir(folder);
            string windowName;
            int detections = 0;

            while (auto entry = dir.read()) {
   
   
                string dname = folder + "/" + entry->d_name;
                Mat img = imread(dname, IMREAD_COLOR);

                if (!img.empty()) {
   
   
                    int d = detect(img);
                    cerr << d << " detections in " << entry->d_name << endl;
                    detections += d;

                    if (!windowName.empty()) destroyWindow(windowName);
                    windowName = entry->d_name;
                    namedWindow(windowName, WINDOW_AUTOSIZE);
                    imshow(windowName, img);

                    if (waitKey(0) == 27) break; // ESC to exit
                }
            }
            return detections;
        }
        catch (const exception& e) {
   
   
            cerr << "Error processing folder: " << e.what() << endl;
            return -1;
        }
    }

    void runOnVideo() {
   
   
        VideoCapture cap(0);
        if (!cap.isOpened()) {
   
   
            throw runtime_error("Can't open default video camera");
        }

        namedWindow("Live Detection", WINDOW_AUTOSIZE);
        Mat frame;

        while (cap.read(frame)) {
   
   
            detect(frame);
            imshow("Live Detection", frame);
            if (waitKey(30) >= 0) break;
        }
    }



protected:
    void drawEllipse(Mat& frame, const Rect& rect, const Scalar& color) {
   
   
        Point center(rect.x + rect.width / 2, rect.y + rect.height / 2);
        ellipse(frame, center, Size(rect.width / 2, rect.height / 2),
            0, 0, 360, color, 2, LINE_8, 0);
    }

};

// 眨眼检测器
class WinkDetector : public FaceFeatureDetector {
   
   
private:
    std::map<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qianbo_insist

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

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

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

打赏作者

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

抵扣说明:

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

余额充值