lzhan 发表于 2024-10-28 12:36

【2024 DigiKey 创意大赛】 键鼠统一管家(1) 软件算法及实现

# 【2024 DigiKey 创意大赛】 键鼠统一管家(1) 软件算法及实现
## 1.引言
我此次使用OPENMV H7开发板来实现人脸检测,并根据人脸中心位置与预设标定位置的比较来判断人脸朝向。因此在本文将介绍人脸识别算法原理、算法实现流程以及测试结果。

## 2.算法原理
Haar级联分类器是一种用于对象检测的机器学习算法,由Paul Viola和Michael Jones在2001年提出。它的核心思想是使用大量的正样本(包含人脸的图像)和负样本(不包含人脸的图像)来训练一个级联函数,这个函数能够高效地检测出图像中的人脸。
算法的工作原理可以概括为以下几个步骤:
1. 特征提取:算法首先需要从训练样本中提取特征。这些特征是基于Haar特征的,它们可以被看作是一种卷积核。每个特征的值是通过计算黑色矩形区域的像素值之和减去白色矩形区域的像素值之和得到的。
2. 积分图像:为了提高特征计算的速度,Viola和Jones引入了积分图像的概念。积分图像允许算法在常数时间内计算出任意矩形区域内的像素和,从而大大加快了特征提取的速度。
3. 级联分类器:通过训练,算法会构建一个级联的分类器,这个分类器由多个阶段组成,每个阶段都包含一系列弱分类器。这些弱分类器通常是基于单个或多个Haar特征的。级联分类器的工作方式是,首先通过第一阶段的所有弱分类器,然后是第二阶段,依此类推,直到最后一个阶段。如果一个候选区域通过了所有阶段的检测,那么它就被认为是一个有效的人脸。
4. 多尺度检测:由于人脸在图像中的大小可能不同,Haar级联分类器需要能够在不同的尺度上检测人脸。这是通过在不同的尺度上滑动窗口并应用分类器来实现的。
5. 参数调整:在实际应用中,可以通过调整参数如scaleFactor(尺度因子)和minNeighbors(最小邻居数)来控制检测的精度和速度。scaleFactor决定了在每次图像尺度变化时窗口的大小,而minNeighbors则决定了在当前窗口中需要检测到的最小人脸数量。

Haar级联分类器的优点在于其速度较快,适合实时应用。但它的缺点是检测精度相对较低,容易受到光照、表情和遮挡等因素的影响

## 3.算法实现流程及源码
本算法在实现时使用到了OPENMV H7的摄像头、LED和GPIO共三种外设。
1. import摄像头所需要的sensor库、延时所需要的time库、图像处理所需要的image库、GPIO所需要的pyb库和LED所需要的machine库。
2. 声明一个全局变量calib,用于处理标定
3. 对Sensor进行复位、设置Sensor对比度、设置Sensor增益、设置Sensor捕捉图像大小、设置Sensor输出格式
4. 初始化变量calib为0;初始化标定中点midpoint为0;初始化方向状态变量stat为1,1标志头向左偏,2标志头向右偏;初始化现在中点nowpoint为0。
5. 初始化蓝色LED灯作为头部转向为左(蓝的拼音为Lan,取L,近似为Left)的指示;初始化红色LED灯作为头部转向为右(红的英文为Red,取R,近似为Right)的指示;初始化绿色LED灯作为标定成功的指示。
6. 初始化GPIO P0,设置其为输入;并为其启动中断,触发条件为下降沿触发;绑定中断回调函数,触发时将calib置为1。
7. 初始化GPIO P1和P2,设置其为输出,并设置其电平为低电平。
8. 加载Haar人脸检测模型,然后进行人脸检测
9. 当检测到人脸时,如果calib为1时,说明需要标定,则根据检测到人脸的左坐标和宽度计算得到中点坐标,然后将calib置为0,避免重复标定,然后打开绿色LED灯,用于提示标定成功,延迟5s后关闭绿色LED灯,标定完成。
10. 根据人脸的左坐标和宽度计算得到从此时脸的中点坐标。若此时脸的中点坐标小于等于标定中点坐标且stat等于1,说明脸向右偏,则需要通知处理模块,因此先将LED1关闭,然后打开LED2,将stat置为2,拉低GPIO P1,拉高GPIO P2;若此时脸的中点坐标大于标定中点坐标且stat等于2,说明脸向左偏,则需要通知处理模块,因此先将LED2关闭,然后打开LED1,将stat置为1,拉低GPIO P2,拉高GPIO P2。


具体代码如下所示:
```python
import sensor
import time
import image
import pyb
from machine import LED


global calib

def callback_p0(line):
    calib = 1


def main():
    sensor.reset()
    sensor.set_contrast(8)
    sensor.set_gainceiling(16)
    sensor.set_framesize(sensor.HQVGA)
    sensor.set_pixformat(sensor.GRAYSCALE)

    calib = 0
    midpoint = 0
    stat = 1
    nowpoint = 0

    led1 = LED("LED_BLUE")
    led2 = LED("LED_RED")
    led3 = LED("LED_GREEN")

    p0_h = pyb.Pin("P0", pyb.Pin.IN)
    p1_h = pyb.Pin("P1", pyb.Pin.OUT_PP)
    p2_h = pyb.Pin("P2", pyb.Pin.OUT_PP)

    p1_h.low()
    p2_h.low()

    p0_ext = pyb.ExtInt(p0_h, pyb.ExtInt.IRQ_FALLING,
                        pyb.Pin.PULL_UP, callback_p0)

    face_cascade = image.HaarCascade("frontalface", stages=25)

    while True:
      img = sensor.snapshot()
      objects = img.find_features(face_cascade, threshold=1, scale_factor=1.5)

      for face in objects:
             if(calib):
                nowpoint = (face+face)/2
                calib = 0
                led3.on()
                pyb.delay(5000)
                led3.off()

            nowpoint = (face+face)/2

            if((nowpoint <= midpoint) and (stat == 1)):
                led1.off()
                led2.on()
                stat = 2
                p1_h.low()
                p2_h.high()

            elif(nowpoint>midpoint) and (stat == 2):
                led2.off()
                led1.on()
                stat = 1
                p2_h.low()
                p1_h.high()


if __name__ == "__main__":
    main()
```
## 4.实现效果
向左看,如下图

向右看,如下图

## 5.其他
OPENMV支持将使用OPENCV进行训练的HaarCascade的xml文件转换为所支持的Cascade文件,具体转换脚本代码详见附件。
注意并不是所有的xml文件都能够转换成功,该脚本存在一定的兼容性问题。

## 6.不足及待改进之处
该种方法是通过比较脸部的中点位置与标定中点位置的大小来判断脸部的朝向的。该种办法虽然简便、快捷,但是在进行完一次标定后,只允许脑袋进行转向,若身子发生较大的移动,则相对位置关系被破坏,因此就无法获得正确的判别结果,存在一定限制,需要再次匹配。因此可以考虑使用人脸立体模型来进行匹配,从而解决身子不可移动的问题。

## 7.参考
1. http://www.micropython.com.cn/en/latet/library/pyb.ExtInt.html
2. https://book.openmv.cc/image/sensor.html
3. https://docs.openmv.io/openmvcam/tutorial/gpio_control.html

wangerxian 发表于 2024-10-28 14:29

<p>我觉得可以把摄像头放在一个屏幕上方,这样如果看着其中一个屏幕就是对某个屏幕进行控制。而不是放在两个屏幕中间。</p>

cruelfox 发表于 2024-10-29 09:43

wangerxian 发表于 2024-10-28 14:29
我觉得可以把摄像头放在一个屏幕上方,这样如果看着其中一个屏幕就是对某个屏幕进行控制。而不是放在两个屏 ...

<p>那样两个屏幕就要两个摄像头。</p>

wangerxian 发表于 2024-10-29 15:01

cruelfox 发表于 2024-10-29 09:43
那样两个屏幕就要两个摄像头。

<p>可以只在一个屏幕上弄摄像头,摄像头未识别到正对屏幕就默认控制另一个屏幕。</p>
页: [1]
查看完整版本: 【2024 DigiKey 创意大赛】 键鼠统一管家(1) 软件算法及实现