Fork me on GitHub

震惊!一男子逃课两天只为了这件事......

功能要求:人脸检测和识别,口罩检测

时间期限两天

由于之前一直以为嵌入式类的比赛是不会允许使用树莓派这种平台,但是之后得知可以用,便毫不犹豫的选择了树莓派。本以为在有强大开源算法的加持下,做一个小项目应该问题不大,但遇到的几个问题还是严重爆破了我的心态。主要是下面几个

  1. 树莓派显示图像帧率低

  2. 开机启动脚本

  3. opencv-contrib配置

  4. 人脸识别算法选择

一 树莓派显示图像帧率低

首先是第一个问题。确切一点说,树莓派单独读取图像的耗时还是比较短的。问题主要出现在以下两点。一是opencv读取图像的缓冲机制,导致图像无法实时更新,树莓派显示图像总会有滞后。二是算法执行效率低,某些算法未经优化直接再树莓派上跑还是有些费劲。其实还有一点就可能涉及到树莓派显示图像本身的耗时,但其影响较小,暂时不考虑。接下来说一下我的解决方案。

首先是图像缓冲导致的延时。这篇博客给出了两种解决方案。一是每次读取图像之前先重启相机,从而保证获取的每一帧图像都是当前图像。这的确是一个投机取巧的方法,但是在我用的时候却出现了问题。当我尝试关闭相机再重新启动的时候,发现要么相机打开失败,要么读取的图像为None,始终无法顺利获取当前图像。于是我采用了第二种方法,即单开一个线程采集图像。这里贴一下核心的代码部分。

 
class Camera:

    def __init__(self, camera):
        self.frame = []
        self.ret = False
        self.cap = object
        self.camera = camera
        self.openflag = False

    def open(self):
        # if self.cap == object:
        self.cap = cv2.VideoCapture(self.camera)
        self.ret = self.cap.set(3, 320)
        self.ret = self.cap.set(4, 240)
        self.ret = False
        self.openflag = True
        threading.Thread(target=self.queryframe, args=()).start()

    def queryframe(self):
        # self.openflag = True
        # while True:
        while self.openflag:
            self.ret, self.frame = self.cap.read()
            # pass

    def getframe(self):
        return self.ret, self.frame

    def close(self):
        self.openflag = False
        self.cap.release()


def cameratest():
    camera = Camera(0)
    camera.open()
    while True:
        ret,image = camera.getframe()
        if ret:
            print(type(image))
            cv2.imshow('img',image)
            cv2.waitKey(10)
        else:
            print("read faild")

    camera.close()
    cv2.destroyAllWindows()

 

通过这种方法能看到当前采集图像的延迟确实减小了,但是效果可能不是很明显。这里我的理解是毕竟是多线程,获取图像和处理,显示任务同一时间只能完成一个,只是效率提高了(之前是每次代码执行到的时候从缓冲区读取,现在是一直从缓冲区读取,可见缓冲区还是存在的)。因此这种方法在降低延迟的同时势必会造成图像的卡顿,因为当前显示的图片和从缓冲区读取的图片并不是连续的。但这种卡顿不影响实际效果。

其次是算法的问题。这里我拿常用的opencv人脸级联分类器举例。目前来看,在离线的人脸识别方式中,opencv的级联分类器是相对速度比较快的,在一般的cpu上跑20帧/s左右应该没有问题。但一旦移植到树莓派,其速度便大幅降低,毕竟树莓派的算力有限。这也是为什么很多人都开始倾向于旋转K210这种有AI计算加成的芯片而不是选择树莓派。总而言之,如果你想用树莓派实现实时的人脸识别(神经棒加速除外),基本上不太可能实现。如果你可以接受10帧/s以下的速度,树莓派也许可以。

二 开机启动脚本

其次是第二个问题,开机启动的脚本。树莓派开机启动方式和linux系统基本一致,具体不再赘述,请参考这篇博客。这里我选择的方式为开机后自动打开终端Terminal并执行python脚本。原因是我需要将imshow的整个画面全屏显示在lcd上,我需要判断我的程序是否运行,或者有没有出现错误。具体方法如下:

  创建文件加入autostart 文件夹(如果不存在就创建一个)

mkdir ~/.config/autostart
sudo vim ~/.config/autostart/myprogram.desktop

  然后输入以下内容

[Desktop Entry]
Encoding=UTF-8
Type=Application
Name=myprogram
Exec=lxterminal -e bash -c 'python3 /home/pi/Desktop/test.py;$SHELL'
Terminal=true

  其中python3 /home/pi/Desktop/test.py为需要执行的代码,使用时替换成自己需要的执行的命令即可,注意不要删掉前面的'和后面的;$SHELL是为了保证打开的终端会一直显示,如果删掉的话执行该句代码后就会kill掉这个进程,等于程序没有执行。  

  最后设置权限:

sudo chmod a+r ~/.config/autostart/myprogram.desktop

注意事项:

  1. 这个代码执行的速度过快,如果需要连网发送数据的话,会出现网络错误的提示,因此我在python3的代码起始部分添加了time.sleep(30)命令,让程序等待30s再执行,确保可以连接到网络。
  2. 执行自己输入的代码时,系统处于根目录,相当于新打开了一个终端,没有进行cd操作,所以代码里面使用相对路径的话可能会出错,比如打开Desktop/test.png的图片,这个时候最好使用绝对路径/home/pi/Desktop/test.png

三 opencv-contrib配置

然后就是最重要的一步了,需要安装opencv-python以及opencv-contrib-python.这里是我遇到坑最多的地方,以下内容如果各位也遇到了相同的问题,那恭喜你可以顺利解决。若未曾遇到过这些问题,请在评论区打我的脸。接下来步入正题。

可能是我conda用习惯了,只要是需要用pip install我都会习惯性的选择换源。于是用树莓派我也是这么做的。我这里使用的是树莓派官方的最新镜像,一般默认的python版本是2.7,python3版本是3.7,然后我就用pip开始安装。(如果你想获得较好的体验,这边建议您直接使用源码进行编译,这样c++和python都可以用,但是过程嘛,懂得都懂,我当时是不想在这上面浪费太多时间)。输入:

pip install opencv-python

问题出现了,报错

Could not find a version that satisfies the requirement opencv (from versions: ) No matching distribution found for opencv

意思就是找不到当前python版本对应的opencv版本,查阅了几篇网上的资料,都说是要安装python3,但是众所周知,目前树莓派的镜像都是默认安装python3.7的。没办法,于是我尝试用pip3 install安装opencv-python,但还是同样的错误。最后折腾了好久,都没有成功。这时我忽然想起来,当时安装树莓派安装ros的时候,有一个问题是因为国内的源没有源码包或者没有更新,必须使用pip的默认源,于是我又把pip的源改了回去,即重新修改 ~/.pip/pip.conf文件为以下内容:

[global]
index-url = https://www.piwheels.org/simple/

这里告诉我们,换源之前千万记得要备份默认源,千万不要直接删掉!!

换源之后我重新尝试pip install opencv-python,结果还真没有报错,而且已经开始下载。但速度是极其慢,于是我参考网上的方法,直接从终端中找到源码包的地址,手动下载,例如下面这样

下载地址: https://www.piwheels.org/simple/opencv-python/opencv_python-3.4.3.18-cp37-cp37m-linux_armv7l.whl

opencv_python-3.4.3.18-cp37-cp37m-linux_armv7l.whl (7.5MB)

下载完成后,在树莓派上离线安装:

sudo pip3/pip install opencv_python-3.4.3.18-cp37-cp37m-linux_armv7l.whl

到这里基本上就可以安装成功了。接下来是opencv-contrib-python包,和上面采用一样的方式,不再重复了。

分别安装完成之后,我开始进行验证。首先是python2

>>import cv2
>>

没有任何问题,但当我在python3中测试时,却出现了下面这样的错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.7/dist-packages/cv2/__init__.py", line 3, in <module>
    from .cv2 import *
ImportError: /usr/local/lib/python3.7/dist-packages/cv2/cv2.cpython-37m-arm-linux-gnueabihf.so: undefined symbol: __atomic_fetch_add_8`

当时我还很奇怪,又反反复复重新装了好多次,但一直报这个错误,直到我看到了这篇博客。他说这是opencv的一个小bug。(但是后来我仔细想了一下,应该还是我当时安装的opencv-python版本和opencv-contrib-python版本不对应的问题,建议大家可以根据官网提供的对应关系,直接安装对应版本的包。)于是我就按照他的方法试了一下,果然解决了,于是我就以为已经大功告成了,但是事实证明我太天真了。问题出在了自启动上,根据上面的步骤,自启动的终端是在bash执行之前的,换言之,开机启动的Terminal中是没有过export LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1 

即当前终端下python3无法运行opencv。虽然每次开机之后手动打开终端都可以运行成功,但那时bash已经执行过了。于是我只能继续找资料,此时我意识到,百度解决不了问题,或许google可以,于是我直接将我的报错信息复制到google进行搜索,第一个结果就是这篇文章。凭借我的高中英语水平,我最后还是艰难的找到了问题的答案。修改如下:

[Desktop Entry]
Encoding=UTF-8
Type=Application
Name=myprogram
Exec=lxterminal -e bash -c 'export LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1 python3 /home/pi/Desktop/test.py;$SHELL'
Terminal=true

没错,就是在执行之前export 一下,就这一句话浪费了我好长的时间。然后重启,等待终端自动打开,运行,成功。

四 人脸识别算法选择

最后一个就是人脸识别算法的选择问题。解决了上面的问题之后,剩下的时间不多了。于是我也不考虑任何其他神经网络的算法,就直接拿了opencv的识别分类算法来用。opencv支持3种人脸识别的算法,分别是:
1.    Eigen Faces    PCA(特征脸方法)
2.    Fisher Faces    LDA(线性判别分析)
3.    Local Binary Pattern Histograms(LBP 局部二值模式直方图)

当时首先考虑到算法的速度,最后选择了第三种方法,即LBP方法。刚开始觉得准确率还可以,但是后来测试的时候发现,当检测时光照和训练时光照不一样时,准确率大大降低。误识别率大大上升。总之效果很差,但是最后还是硬着头皮上了,最后发现评委都不太懂,就糊弄过去了。比赛结束之后,又试了另外两种方法,发现效果最好的还是PCA特征脸检测,其实速度上来说也还可以接受,并且相对于LBP其准确率也大大上升了,虽然还是会有误识别,但是结果也在接受范围之内。至此基本的功能都已实现,待我整理一下,过两天把代码开源出来供各位参考。效果图如下:

 效果图

附录

[1]基于Caffe的口罩佩戴识别网络

写在最后

鉴于这是一个比赛而不是一个完整的项目。每当深夜自己一个人敲代码屏幕一片暴红的时候,那种孤独和绝望,估计只有经历过的人才懂吧。还是那句话,怕什么真理无穷,进一步有进一步的欢喜。

END

posted @ 2020-10-26 22:16  PatrickSDUST  阅读(348)  评论(0)    收藏  举报