这是帮学妹做的一个毕业设计,觉得有趣就接了。功能较为简单,更像是一个带了人脸识别功能的行车记录仪。从18年12月开始做,大部分代码在3月写出来,到现在大概花了20个工作日的样子。期间从零开始学了C++和QT,opencv也经历了从C版本到C++版本,收获不小。这算是我第一个独立的“产品”。
目前只完成了ubuntu14.04系统下的功能,还没有移植树莓派做屏幕适配等,也没有进行自己的人脸特征运算。用的还是opencv的样例代码。
详细功能有:人脸检测,循环录像,日志生成以及qt界面操作。
本文的目的是整理一下:学习经历,开发思路,经验总结,难点和坑,为了代码重构做准备。
最简单的是helloworld,最难的也是helloworld。开发环境真的是最头疼的。
说来惭愧,嵌入式专业的没用过ARM开发板,之前有过一个韦东山的,但到手就退掉了。
选择的标准就是有触屏,ARM9。最开始出于价格因素,买了周立功的easyArm m283a。可以说,忙活了一个月发现板子根本没法用,或者说压根不适合初学者。缺点如下:
所以这就很蛋疼了,不得不说周立功的开发手册写的还可以。一步步教你,但最大的问题就是,不通用。这个开发板给我的感觉更像是一片周立功自己生产的单片机。还好没让学妹直接也买了,最后这个板子亏了一百块钱卖掉了。其实都是ARM板子,为什么不能用呢,其实可以从上面的缺点总结出来。
总而言之,恰好学妹又送了个树莓派给我当礼物,解决了以上的所有问题。有问题不可怕,问题是无法解决问题。
这个没什么好说的,用UVC协议的摄像头就可以,LINUX有个啥V4L的系统驱动还是啥,脸上就能用。在公司随便拿了一个USB摄像头也能用,没啥具体问题。
唯一的问题就是学妹买同款摄像头的时候,焦距和我的不同,买成了鱼眼的。
这两个为什么放在一起呢?因为qt的版本竟然和opencv的版本有关联,QT5对应OPENCV3。是我大学同学的群里面给我解答的。
opencv自然是要编译安装,最早我都安装在了树莓派上,当时没有动态链接库之类的概念,重复装了很多次。最开始是盲目的敲命令,后来想明白了,是先通过cmake生成makefile,然后执行make安装。中间有很多配置参数,这个可以以后仔细说说。后面的就是配置动态链接库的位置,和pkg-config包。这一步学妹的电脑是种配置不好,我没搞明白究竟是我这边的问题还是她操作有误。我在自己的vbox虚拟机和两个wm虚拟机都配置成功了,树莓派也安装成功。
最先安装的事opencv2,出于喜欢用c的缘故,想用c来写。通过几个简单的C例子,入门了opencv库中的简单图像处理和人脸识别的功能。然后由于不兼容qt5的问题,重新安装opencv3.4.5,又重新做了一遍sample文件夹里面的例子。二者还是有很多不同的,比如数据结构的名称,库的更名,函数名更新。
总之,运行成功后安装qt5,自己写了几个类测试了opencv在qt程序中运行正常。qt倒是一键安装,要设置的是每一个qt项目的pro文件中的include和lib。具体位置要按照自己的安装路径来,最好所有的都直接包含进去。比如ls *.so得到所有动态链接库。
可能几百字就概括好了,但实际上将近两个多月才装好,测试好。
说实在,写这篇文章的时候,才头一次使用交叉编译,作为一个嵌入式专业的学生是挺不应该。。。
卡了大概4天,因为网上的教程都是要装32位的编译方式,想了想应该是应对老版本的raspberry系统,64位应该是今年才成熟的。直接用自带的x64交叉编译链就可以了。剩余的还没想明白,还有怎么生成不依赖动态库的打包程序。以后再说。
总之,今天写出了helloworld。
现在遇到的问题是交叉编译qt源码遇到了瓶颈。
最后没有解决这些问题,出于无奈,我只好在树莓派上装了qtcreator和opencv,直接编译源码,时间来不及了。
其实最早想做的是人流统计这种功能,脑子里没有概念,具体想法只有:摄像头+arm+触屏。后来出于能力问题,只能做到人脸检测的部分。思路就是,循环检测每一帧同屏人脸,然后进行计数,取采样周期的中位数来确定区间内的人脸数。
与其说是人脸识别系统,做到最后发现,更像个是有人脸检测功能的行车记录仪。因为预定的视频播放管理等功能,来不及实现,只实现了日志的生成和读取,以及循环录像,以及一个前端页面。
这是写完所有功能再按照代码逻辑画出来的,写的时候并没有先想好。
其实期间也重构过一次代码,最开始是按照c的思路写的,几个类并没有封装好,面向对象用的还是不熟练。
整体的框架是网上复制过来的,包含了读帧的部分,里面涉及opencv的Mat和Qt图像的格式转换等步骤。现在看看逻辑还是很简单的,几个按钮对应的信号槽。摄像头类有摄像头开启关闭,视频帧抓取,录像,以及释放各种对象的操作。日志类什么的也是一样的原理,设计文件读写。
从opencv2到opencv3,代码格式有着巨大的变化,c下的代码还是维持在结构体作为数据结构,以函数的形式编程。而3全部改为面向对象,使得很多功能可以配合C++的容器来实现。期间最大的问题不是opencv库的使用,而是opencv的编译安装和环境配置。我先写了两个测试类,分别测试图像的处理和摄像头的调用。
首先人脸识别其实是对于每个视频帧进行处理,本质上还是图像处理的一种,先把图像转化为灰度图,然后执行检测函数。
然后摄像头的调用,涉及到了录像的videowriter类,调用了就要释放,主要是内存管理的问题。期间出现了很多不好检测的段错误,大多数都是因为对迭代器和图像对象使用错误,比如迭代器越界,和录像对象没有正常关闭。代码的健壮性不是很好。
qt的部分,主要由于qtcreator的一键安装,少了很多问题。但是涉及到树莓派的交叉编译的时候遇到了很多问题,这里暂且不提。
因为用了qt自带的界面设计功能,图形化很容易就做出来了,主要的部分就是拖按钮,然后和槽函数对应,具体实现由框架来完成。每个窗体和按钮都有大量的属性,需要长时间的使用才能熟悉。不过并不复杂,没有遇到很大的问题。
值得注意的就是,qt自带的数据类型和c++,opencv的数据类型不一致,需要手动转换。如qt的date和string,再生成文件的时候要用c++的string或者char *,这个需要注意。或许也有直接生成标准类型的函数,但是没有仔细探究。
对于opencv和qt图像格式的转换,设计到一些现有数字图像类型的原理,很多参数都是知其然不知其所以然。但好在还是能找到解决方案的。
环境装好,界面设计好,功能其实很好写的,没有什么技术问题,主要的是业务问题。在设计初期并没有考虑好诸如:怎么殉难换录像,需要什么样的日志,这些没思考过。最后还是装成买家去问淘宝客服,实在惭愧。
还有个一直以来的问题,没有一个成熟的设计思想。比如我这个循环录像的类,和图像处理类耦合在一起了,我不清楚怎么把他们分开,还有关于多线程,并行运行的。比如一边播放音乐一边执行动作这种,一直以来都没有什么思路。
总之有业务逻辑,无外乎是顺序流程,判断,排序,循环这些,写这些具体功能用了不到了两周时间。
这个很头疼,qt远程部署,成功了一次,但是重新拷贝镜像到新的sd卡以后,再去远程部署就无法成功。
我也不知道为什么,可能还是重复的次数过少,没有经验,或者缺乏核心知识。
其实这个人脸识别的摄像头,并没有用到很多人脸识别的功能。一直在折腾环境,最后发现opencv官网其实有一些介绍,但是英语说的,很多对于linux系统的知识并不熟悉,导致就算看懂了也不明白如何使用。不仅是opencv,很多其他库也是一样,编译安装一定是必须要过的一道坎。
qt的ide确实很好用,如果是常规的c++程序开发,并不会遇到这么多问题。但是一旦涉及到多平台,就不像是做一个简单的界面这么容易了。细节的部分有很多,就像是前端开发一样。甚至可以当作一门语言来学了。但解决问题的过程中,反而很多还会回到linux系统的问题上。
与其说是嵌入式开发,更像是linux与c的关系。这和在windows下写代码真的是天差地别。从vc6.0到visualstdio,kail到arduino,windows平台的基本都是ide,都是认为设计好的工具。写了也有很久代码,并没有关注过所谓的顶层逻辑是怎么实现的。更多的是抽象层次的,内存,cpu,但是并没有详细的学习过。库是什么,链接有什么作用,深入了解一后,才发现教材中一笔带过的科普知识,背后可能是几十年科技演化的代价。
可能现在舆论更在乎业务,在乎逻辑,但我觉得这些底层的知识,才是区别是否属于计算机科学与编程上岗培训。今后会继续向下挖掘的,在此之前要整理一下自己的知识点和欠缺。