基于树莓派的人脸检测云台

基于树莓派的人脸检测云台

基于树莓派的人脸追踪系统是指当摄像头捕捉到人脸图像时,系统能够首先识别出人脸,并在人脸移动过程中控制摄像头不断跟随人脸移动,使得在摄像头可移动范围内,人脸一直处于摄像头正中间部位,达到人脸追踪的目的。它主要是由以下两大模块构成:

一、人脸识别模块

我们是通过一个USB摄像头与树莓派连接,用于人脸图像的采集。当摄像头采集到视频数据后,树莓派便对这些视频的每一帧进行人脸识别处理。当树莓派识别到某一帧图像里包含有人脸后,就在这一帧图像里标记出人脸的位置,最后再将人脸图像进行输出显示。其中人脸检测的过程用到的是Opencv里自带的haarcascade分类器。将图像数据输入至该分类器,便能够得到人脸的范围信息。然后我们只需按照返回的位置信息进行标记处理,便能够标记出人脸的位置。
识别出的人脸

二、舵机模块

舵机是用来实现摄像头移动的控制模块,我们在本次实验中设置了水平移动舵机和垂直移动舵机,这样便能够实现大范围的人脸追踪。其中舵机的伺服系统由可变宽度的脉冲来进行控制,控制线是用来传送脉冲的。脉冲的参数有最小值,最大值,和频率。一般而言,舵机的基准信号都是周期为20ms,宽度为1.5ms。这个基准信号定义的位置为中间位置。舵机有最大转动角度,中间位置的定义就是从这个位置到最大角度与最小角度的量完全一样。
角度是由来自控制线的持续的脉冲所产生。这种控制方法叫做脉冲调制。脉冲的长短决定舵机转动多大角度。例如:1.5毫秒脉冲会到转动到中间位置(对于180°舵机来说,就是90°位置)。当控制系统发出指令,让舵机移动到某一位置,并让他保持这个角度,这时外力的影响不会让他角度产生变化,但是这个是由上限的,上限就是他的最大扭力。除非控制系统不停的发出脉冲稳定舵机的角度,舵机的角度不会一直不变。
当舵机接收到一个小于1.5ms的脉冲,输出轴会以中间位置为标准,逆时针旋转一定角度。接收到的脉冲大于1.5ms情况相反。不同品牌,甚至同一品牌的不同舵机,都会有不同的最大值和最小值。一般而言,最小脉冲为1ms,最大脉冲为2ms。如下图:
PWM脉冲控制

实验内容

一、安装部件

本次实验主要涉及到有摄像头模块和舵机模块,其中摄像头模块的电源和数据传输可以由树莓派的USB口来完成,而舵机的电源则是由外部的一个5V、1A的直流源提供。其中水平舵机和垂直舵机的信号线接入到树莓派的GPIO27和GPIO17,用于输出PWM信号控制舵机转动角度。其中舵机是安装在一个云台套件里,云台则用来固定USB摄像头。其中要注意,外部电源、舵机和树莓派的GND要连在一起。安装示意图和实物图如下所示:
树莓派接线图
完整接线

二、舵机的校准

1、首先你要弄清楚你购买到的舵机的主要特点。在这个实验中,我使用的是Power Pro SG90。
以下是它的数据,我们可以参考一下:
范围:180°
电源:4.8V(外部可使用USB 5V DC电源)
工作频率:50Hz(周期:20 ms)
脉冲宽度:从1ms到2ms
2、理论上,舵机运转的位置
初始位置(0°):1ms脉冲到数据终端。
中间位置(90°):1.5ms脉冲到数据终端。
最终位置(180°):2 ms脉冲到数据终端。
3、使用Python编写舵机位置,了解上述位置相应的“占空比”非常重要,我们来做一些计算:
初始位置==>(0°)脉冲宽度==> 1ms >占空比= 1ms / 20ms > 2.0%
中间位置(90°)
> 1.5 ms的脉冲宽度
>占空比= 1.5ms / 20ms > 7.5%
最终位置(180°)
> 2 ms的脉冲宽度==>占空比= 2ms / 20ms ==> 10%
所以占空比应该在2%到10%的范围内变化。
4、单独测试舵机
打开树莓派终端并以“sudo”启动你的 Python 3 shell 编辑器(你可能是“超级用户”来处理GPIO):
首先在Python Shell上导入RPI.GPIO模块并作为GPIO:
import RPi.GPIO as GPIO
我用BOARD做了这个测试,所以我使用的引脚为物理引脚(GPIO 17 =引脚11和GPIO 27引脚13)。
GPIO.setmode(GPIO.BOARD)
定义你要使用的舵机引脚:
tiltPin = 11
现在,我们必须指定这个引脚将为“输出”
GPIO.setup(tiltPin, GPIO.OUT)
而且,这个引脚上产生的频率,对于我们的舵机来说应该是50Hz:
tilt = GPIO.PWM(tiltPin, 50)
现在,让我们开始在引脚上设置一个初始占空比(我们将它保持为“0”)的PWM信号:
tilt = start(0)
现在,你可以输入不同的占空比值,观察舵机的运动。让我们从2%开始,看看会发生什么(我们观察舵机从“零位”开始):
tilt.ChangeDutyCycle(2)
这样可以看到通过输出占空比对应的舵机转角度数,以便实验过程中进行调节,以达到准确转角的效果。
完成测试后,停止PWM并清理GPIO:
tilt= stop()
GPIO.cleanup()

三、平台移动机制

舵机是安装在一个云台套件里,云台则用来固定USB摄像头。“平移”舵机将“水平”移动摄像机(“方位角”),而我们的“俯仰”舵机将“垂直移动”(仰角)。以下是云台移动原理图:
舵机控制
云台和舵机

四、舵机控制程序

这一部分, 我们首先设置了程序开始运行时的舵机初始位置,这里对水平舵机和垂直舵机分别设置80、60的初始值。

# 设置舵机的初始位置
x0, y0 = 80, 30
setServoAngle(27, x0)
setServoAngle(17, y0)

setServoAngle函数时将输入的树莓派引脚号和舵机转角值转变输出为树莓派对应硬件的PWM值,来驱动舵机转动相应角度。

def setServoAngle(servo, angle):

    #param servo 控制舵机的引脚编号,这取决于你,我用的分别是17和27
    #param angle 舵机的位置,范围是:0到180,单位是度

    
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.setup(servo, GPIO.OUT)
    pwm = GPIO.PWM(servo, 50)
    pwm.start(8)
    dutyCycle = angle / 18. + 3.
    pwm.ChangeDutyCycle(dutyCycle)
    time.sleep(0.30)
    pwm.stop()
    GPIO.cleanup()

我们将通过摄像头得到人脸的位置范围,通过简单的计算便可得到人脸图像的中心坐标x、y,对横纵坐标简单处理后乘以一个比例系数得到相应的摄像头转动角度,为了防止舵机小角度抖动,我们设置了一个阈值,如果得到的转动角度大于该阈值,舵机才会动作。

五、人脸识别程序

人脸识别是整个实验的关键,只有正确得到人脸位置,舵机才能够得到正确的转动角度。本次实验中我们用到是Opencv里自带的haarcascade分类器,它能够根据输入的视频图像识别出人脸并返回人脸的范围坐标。我们将返回的人脸位置坐标进行连线处理,便能够框出图像中的人脸位置。

    for x, y, w, h in faces:

        cv2.rectangle( img, ( x, y ),( x + w, y + h ), face_color, strokeWeight)       
        cv2.putText(img, "Face No." + str( len( faces ) ), ( x, y ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )
        face_gray=gray[y:y+h,x:x+w]
    fps = fps + 1
    sfps = fps / (time.time() - t_start)
    cv2.putText(img, "FPS : " + str( int( sfps ) ), ( 10, 10 ), cv2.FONT_HERSHEY_SIMPLEX, 0.5, ( 0, 0, 255 ), 2 )

    cv2.imshow( "Frame", img )

在这个过程中我们可以加入输出图像处理帧数的内容,这样能够为后期进一步优化提供便利。
刚开始我们只是单核驱动这个程序,后来我们发现这样做视频处理的速度是很慢的,根本达不到想要的效果。于是我们考虑加入多线程处理程序。将图像处理分到树莓派的四个核,依次分别处理一帧图像,这样图像处理的速度立刻就跟上了。显示出来的视频图像也变得流畅起来。

if __name__ == '__main__':

    camera = cv2.VideoCapture(0)
    camera.set(cv2.CAP_PROP_FRAME_WIDTH,resX)  
    camera.set(cv2.CAP_PROP_FRAME_HEIGHT,resY) 

    pool = mp.Pool( processes=4 )

    read, img = camera.read()
    pr1 = pool.apply_async( get_faces, [ img ] )   
    read, img = camera.read()
    pr2 = pool.apply_async( get_faces, [ img ] )  
    read, img = camera.read() 
    pr3 = pool.apply_async( get_faces, [ img ] )   
    read, img = camera.read()
    pr4 = pool.apply_async( get_faces, [ img ] )   

    fcount = 1

将图像处理的流程放到一个循环里。

    while (True):
        read, img = camera.read()

        if   fcount == 1:
            pr1 = pool.apply_async( get_faces, [ img ] )
            faces, img, gray=pr2.get()
            draw_frame( faces, img, gray )

        elif fcount == 2:
            pr2 = pool.apply_async( get_faces, [ img ] )
            faces, img, gray=pr3.get()
            draw_frame( faces, img, gray )

        elif fcount == 3:
            pr3 = pool.apply_async( get_faces, [ img ] )
            faces, img, gray=pr4.get()
            draw_frame( faces, img, gray )

        elif fcount == 4:
            pr4 = pool.apply_async( get_faces, [ img ] )
            faces, img, gray=pr1.get()
            draw_frame( faces, img, gray )
            fcount = 0

以上贴出来的是主要程序,完成程序请到我的资源出下载

  • 3
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值