系统要求
在ZYNQ中实现基于opencv的人脸识别,可以简单的进行人脸检测与识别。
设计方案:
人脸检测选择opencv中自带的人脸检测器进行检测,内部自带的人脸检测器如下:haarcascade_frontalface_default.xml;之后利用opencv内部自带的识别器进行训练并得到相应的模型文件;得到模型文件后进行预测;之后交叉编译opencv以及qt并且移植到ZYNQ中,实现最终要求。
原理介绍:
人脸检测原理介绍
OpenCV在物体检测上使用的是haar特征的级联表,这个级联表中包含的是boost的分类器。首先,人们采用样本的haar特征进行分类器的训练,从而得到一个级联的boost分类器。训练的方式包含两方面:
1. 正例样本,即待检测目标样本
2. 反例样本,其他任意的图片
首先将这些图片统一成相同的尺寸,这个过程被称为归一化,然后进行统计。一旦分类器建立完成,就可以用来检测输入图片中的感兴趣区域的检测了,一般来说,输入的图片会大于样本,那样,需要移动搜索窗口,为了检索出不同大小的目标,分类器可以按比例的改变自己的尺寸,这样可能要对输入图片进行多次的扫描。
级联分类器是由若干个简单分类器级联成的一个大的分类器,被检测的窗口依次通过每一个分类器,可以通过所有分类器的窗口即可判定为目标区域。同时,为了考虑效率问题,可以将最严格的分类器放在整个级联分类器的最顶端,那样可以减少匹配次数。
基础分类器以haar特征为输入,以0/1为输出,0表示未匹配,1表示匹配。
LBPH人脸识别原理介绍
opencv内使用的圆形LBP算子。基本的LBP算子最大缺陷在于它只覆盖了一个固定半径范围内的小区域,这显然不能满足不同尺寸和频率纹理的要求。为了适应不同尺度纹理特征,Ojala等对LBP算子进行了改进,将3 * 3邻域扩展到了任意领域,并用圆形代替了正方形,改进后的LBP算子允许半径为R的圆形邻域内有任意多像素点。一个LBP算子可以产生多种二进制模式随着邻域内采样点增加,二进制模式种类急剧增加,如3 * 3邻域内有8个采样点,即有2^8=256中模式;5 * 5邻域内有20个采样点,就有2^20=1048576种二进制模式。这么多的二进制模式对纹理的提取或者识别都提出了挑战。为了解决这个问题,等价模式(Uniform Pattern)被提出用来对LBP算子的模式种类进行降维。等价模式的定义为:当某个局部二进制模式所对应的循环二进制数从0到1或从1到0最多只有2次跳变。不符合这个定义的都归为混合模式。00000000、00000111、10001111都是等价模式。等价模式大大减少了二进制模式种类同时不会丢失信息。模式的数量可以由原来的2^n减少为n*(n-1) + 2种,n表示了邻域内的采样点数目。等价模式代表了图像的边缘等关键模式,如下图所示,白点表示1,黑点表示0.等价模式类占总模式的绝大多数。旋转不变的LBP算子
LBP算子的特点
一般分析纹理的方法包括统计分析法和结构分析法。前者是基于图像灰度值的分布和相互关系,来找出反应这些关系的特征。后者是分析纹理的结构,从中获取结构特征。任何纹理都同时包含了统计特征和结构特征,因此单一的某种分析方法都不能取得满意的结果。LBP方法可以看成结合了两种方法,一方面LBP具有原始纹理和布局规则,因此具有结构特点;同时LBP又可以看出图像经过非线性滤波之后的统计。因此,LBP方法能广泛应用于各种纹理图像识别。
opencv中的LBPH人脸识别的大致过程是:
将训练集中人脸图调整为一维矩阵,并将调整后的人脸图像全部合成一个矩阵A;
选则检测窗口对矩阵A用LBP算子处理:
计算检测窗口(圆形)上每一点的像素值;
对没有完全落在像素位置点的点进行双线性插值;
若检测窗口的像素值大于中心点像素值进行对比,则标记为1,否则标记为0。进而得到全部LBP矩阵;
根据LBP矩阵计算每个检测窗口的空间直方图,最终得到一个一维矩阵,即训练集的特征向量;
当输入测试人脸图片识别时,也需要计算其LBP矩阵然后根据空间直方图获取特征向量;
然后通过比较测试人脸的特征向量和训练集的特征向量,识别结果就是训练集中与测试人脸“最近”的。
操作流程:
1.检测人脸
人脸检测选择opencv内置的haar人脸检测器;如下所示:
def detect_face(img):
#将测试图像转换为灰度图像,因为opencv人脸检测器需要灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#加载OpenCV人脸检测分类器Haar
face_cascade = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml')
#检测多尺度图像,返回值是一张脸部区域信息的列表(x,y,宽,高)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=5)
# 如果未检测到面部,则返回原始图像
if (len(faces) == 0):
return None, None
#目前假设只有一张脸,xy为左上角坐标,wh为矩形的宽高
(x, y, w, h) = faces[0]
#返回图像的正面部分
return gray[y:y + w, x:x + h], faces[0]
我们选择haarcascade_frontalface_default.xml这个人脸检测器。
2.采集人脸图像进行训练
每人采集100张图片进行训练,采集人脸使用上文提到的人脸检测器进行人脸的检测并裁剪出人脸。
3.建立人脸数据后将每一个人脸对应一个标签:
# 该函数将读取所有的训练图像,从每个图像检测人脸并将返回两个相同大小的列表,分别为脸部信息和标签
def prepare_training_data(data_folder_path):
# 获取数据文件夹中的目录(每个主题的一个目录)
dirs = os.listdir(data_folder_path)
# 两个列表分别保存所有的脸部和标签
faces = []
labels = []
# 浏览每个目录并访问其中的图像
for dir_name in dirs:
# dir_name(str类型)即标签
label = int(dir_name)
# 建立包含当前主题主题图像的目录路径
subject_dir_path = data_folder_path + "/" + dir_name
# 获取给定主题目录内的图像名称
subject_images_names = os.listdir(subject_dir_path)
# 浏览每张图片并检测脸部,然后将脸部信息添加到脸部列表faces[]
for image_name in subject_images_names:
# 建立图像路径
image_path = subject_dir_path + "/" + image_name
# 读取图像
image = cv2.imread(image_path)
# 显示图像0.1s
cv2.imshow("Training on image...", image)
cv2.waitKey(100)
# 检测脸部
face, rect = detect_face(image)
# 我们忽略未检测到的脸部
if face is not None:
#将脸添加到脸部列表并添加相应的标签
faces.append(face)
labels.append(label)
cv2.waitKey(1)
cv2.destroyAllWindows()
#最终返回值为人脸和标签列表
return faces, labels
4.利用opencv内部自带的识别器进行训练
选择opencv内部自带的LBPHFaceRecognizer_create()人脸识别器进行训练。
#调用prepare_training_data()函数
faces, labels = prepare_training_data("training_data")
#创建LBPH识别器并开始训练,当然也可以选择Eigen或者Fisher识别器
face_recognizer = cv2.face.LBPHFaceRecognizer_create()
face_recognizer.train(faces, np.array(labels))
5.训练完毕后进行预测
得到训练后的人脸识别模型后,调用并测试。
# 此函数识别传递的图像中的人物并在检测到的脸部周围绘制一个矩形及其名称
def predict(test_img):
#生成图像的副本,这样就能保留原始图像
img = test_img.copy()
#检测人脸
face, rect = detect_face(img)
#预测人脸
label = face_recognizer.predict(face)
# 获取由人脸识别器返回的相应标签的名称
label_text = subjects[label[0]]
# 在检测到的脸部周围画一个矩形
draw_rectangle(img, rect)
# 标出预测的名字
draw_text(img, label_text, rect[0], rect[1] - 5)
#返回预测的图像
return img
6.交叉编译opencv3.4
1)下载opencv-3.4.3源码以及opencv_contrib-3.4.3源码并且建立文件夹:build_3559
2)安装cmake
指令代码:apt-get-install cmake-gui
3)终端打开cmake-gui进行配置
4)打开后填写opencv源码路径以及build文件夹路径
图4 cmake配置opencv路径结果
5)配置configure
图5 configure
6)configure添加opencv_contrib
图6 添加拓展文件
7)添加install_prefix
图7 添加install_prefix、
8)configure---generate---done
9)修改CMakeCache.txt
CMAKE_EXE_LINKER_FLAGS:STRING= -lpthread -lrt -ldl
10)在common.cc中添加#define HAVE_PTHREAD
11)在CmkaeList.txt中添加以下代码:
ocv_include_directories(${OPENCV_CONFIG_FILE_INCLUDE_DIR})
ocv_include_directories(./3rdparty/zlib/)
图8 CmkaeList.txt增加代码
12)make install
13)移植到ZYNQ中
7.交叉编译qt
1)将压缩包复制到Ubuntu主机中
2)将压缩包解压
tar -zxvf alinx_qt_5.7.1.tar .gz
3)运行编译脚本文件
4)到目录"/opt/alinx/zynq_qt5.7.1"下我们可以找到bin目录,里面有我们要用到的qmake文件,和lib目录,这个目录就是我们需要的QT库。
5)运行脚本制作QT库的img文件,需要输入账号密码
./make_img.sh
6)得到可以在ZYNQ中挂载的img文件
7、配置ZYNQ版本QT
1)运行Qr Creator,找不到应用程序快捷方式,可以搜索一下。
2)点击Tools->options
3)在Build&Run 选项的at Versions 页点击Add..
4)选择前面编译生成的qmake文件
5)添加qmake以后,保持默认name,不修改
6)在Compilers 页面,点击Add..,选择GCC->C
7)Name 修改为ZYNQ_GCC,Compiler path 选择
/opt/xilinx/SDK/2017.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-gcc
8)在Compilers页面,点击Add..,选择GCC->C++,Name 修改为ZYNQC++,路径选择/opt/Xilinx/SDK/2017.4/gnu/aarch32/lin/gcc-arm-linux-gnueabi/bin/arm-linux-gnueabihf-g++