【ATU Book-i.MX8系列 - TFLite 进阶】 人脸侦测应用

一.   概述

边缘运算的重点技术之中,模组轻量化网路架构 是不可或缺的一环,如何高效的利用硬体资源来达到最佳目标,特别是在效能与准确度的衡量上,是个非常有趣的议题。此章节再来探讨深度学习热门的研究项目之一 人脸侦测(Face Detection) ,在过去的技术中,常使用 Harr Cascade、Adaboost 这类传统影像与机器学习技术的分类器来实现撷取人脸位置。其中最具代表性、最受广泛使用的莫过于 OpenCV 所提供的方法。但可惜的是这种分类器的运算模式,不足以匹配现今热门神经网路处理器的运算模式。对于深度学习技术中,较常见的为 RetinaFace、yolo、MobileNet  SSD、FaceNet、ArcFace 等架构皆可实现人脸识别之目的。 

若新读者欲理解更多人工智能、机器学习以及深度学习的资讯,可点选查阅下方博文
大大通精彩博文   【ATU Book-i.MX8系列】博文索引

 

TensorFlow Lite 进阶系列博文-文章架构示意图

二.  算法介绍

由于 边缘运算(edge computing) 需以轻量、快速、准确为主,故采用神经网路架构最轻量,且有一定识别能力、运行效率极佳的 MobileNet SSD 架构。
由 轻量化网路架构 MobileNet 与 物件检测算法(Single Shot MultiBox Detector, SSD) 组成之应用。

神经网路架构探讨 :

(1) MobileNet

核心概念是利用拆分的概念,将原本的卷积层拆成 深度卷积(Depthwise Convolution) 与 逐点卷积(Pointwise Convolution) 两个部分,称作 深层可分离卷积(Depthwise Separable Convolution)
以此方式进行运算,能够大幅度减少参数量,以达到加快运算速度。(用途撷取特征)

MobileNet 轻量化概念示意图, 参考 LaptrihnX 网站

 

(2) Single Shot Multi-Box Detector, SSD

核心概念是由 金字塔特征结构(Pyramidal Feature Hierarchy) 与 先验框(Prior boxes) 的概念组成。

金字塔特征结构(Pyramidal Feature Hierarchy) :

采用不同大小的特征图检测物件,比如说大特征图检测小物件、小特征图检测大物件。

先验框(Prior boxes) :

让每个特征图上设置不同尺寸长宽比的先验框,以作为预测框的基准。这能够帮助训练过程时,提供梯度一定程度的范围限制,能够降低一定程度的训练难度。 

如下图所示,将每个不同大小的输出层去加上 检测器(detector) 来进行人脸位置预测
最后再将每层结果结合至 NMS(non-maximum suppression) 非最大抑制层来找到图片中最佳的人脸位置。

SSD 架构概念示意图 , 参考 SSD-SFace 论文

 

WIDER FACE DataSets :

(此小节所介绍的核心算法与物件侦测相同,仅是将资料库更换为 Widerface 人脸资料库)
资料库连结 : http://shuoyang1213.me/WIDERFACE/

 

 

三.  算法实现

Google 官方有提供效果极佳的 mobilenet_object_detector.tflite 模组,即可直接使用,请点选下载。
同一上篇文章,这里将再次利用迁移学习方法与 TF-Slim 函示库、WIDER FACE 资料库来实现 MobileNet SSD人脸检测器(Face Detection)

实现步骤如下:

 第一步 :  开启 Colab 设定环境

%tensorflow_version 1.x
!python -c 'import matplotlib as tf; print(tf.__version__)' # Check the version of the tensorflow

 ※ 由于 colab 已暂停支援 Tensorflow 1.x 的方式,请本机方式实现。

第二步 :  TensorFlow Model Garden 下载与安装

%cd root
!git clone https://github.com/tensorflow/models.git
%cd root/models/research/
!protoc object_detection/protos/*.proto --python_out=. # gernate *.proto
!python setup.py build # 建置 TensorFlow Model Garden 档案

第三步 :  TensorFlow  Slim 下载与安装

import os
os.environ['PYTHONPATH'] += ':/root/models/research/:/root/models/research/slim/:/root/models/research/object_detection/utils/:/root/models/research/object_detection'
!pip install tf_slim # 安装 TensorFlow Slim
!python object_detection/builders/model_builder_test.py # TensorFlow Slim 模组建立是否成功测试

第四步 :  下载资料库

 ※ 使用 Widerface 人脸资料库

%cd /root/models/
!git clone https://github.com/yeephycho/widerface-to-tfrecord.git
# 建立资料夹
%cd /root/models/widerface-to-tfrecord/
!mkdir WIDER
%cd WIDER
# 下载并解压缩 Widerdafe 资料库 (训练)
%cd /root/models/widerface-to-tfrecord/WIDER/
#WIDER_train
!gdown https://drive.google.com/u/0/uc?id=0B6eKvaijfFUDQUUwd21EckhUbWs&export=download
!unzip WIDER_train.zip
!rm WIDER_train.zip

# 下载并解压缩 Widerdafe 资料库 (验证)
!gdown https://drive.google.com/u/0/uc?id=0B6eKvaijfFUDd3dIRmpvSk8tLUk&export=download
!unzip WIDER_val.zip
!rm WIDER_val.zip
# 下载并解压缩 Widerdafe 资料库 (测试)
!gdown https://drive.google.com/u/0/uc?id=0B6eKvaijfFUDbW4tdGpaYjgzZkU&export=download
!unzip WIDER_test.zip
!rm WIDER_test.zip

第五步 :  下载标签

# 下载训练时须用到物件讯息 (此处须更新 wider_face_train_annot.txt )
%cd /root/models/widerface-to-tfrecord/WIDER/
!gdown http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/support/bbx_annotation/wider_face_split.zip
!unzip wider_face_split.zip
!rm wider_face_split.zip
!mv /root/models/widerface-to-tfrecord/WIDER/wider_face_split/wider_face_train_bbx_gt.txt wider_face_train_annot.txt
!mv /root/models/widerface-to-tfrecord/WIDER/wider_face_split/wider_face_test_filelist.txt wider_face_test_annot.txt
!mv /root/models/widerface-to-tfrecord/WIDER/wider_face_split/wider_face_val_bbx_gt.txt wider_face_val_annot.txt
!rm -r wider_face_split

第六步 :  制作 TensorFlow Record

%cd /root/models/widerface-to-tfrecord/
!mkdir output
%cd /root/models/widerface-to-tfrecord/
!python3 widerface_To_TFRecord.py # 建立 TensorFlow Record

※ 执行后,将于 models/widerface-to-tfrecord/output 资料夹内产出 train.record & test.record' 档案
※ 由于 WIDER 资料库提供的图库有些已失效,故须更新 widerface_To_TFRecord.py (如连结)

第七步 :  下载训练过的 MobileNet 模组

此步骤利用之前训练过的模组资源重新训练,即 迁移学习(Transfer Learning) 的技术。

%cd ~/models
import shutil
import tarfile
from requests import get
MODEL = 'ssd_mobilenet_v1_coco_2017_11_17'
MODEL_FILE = MODEL + '.tar.gz'
DOWNLOAD_BASE = 'http://download.tensorflow.org/models/object_detection/'
DEST_DIR = 'pretrained_model'
# 下载mobilenet 模组
if not (os.path.exists(MODEL_FILE)):
with open(MODEL_FILE, "wb") as file:
response = get(DOWNLOAD_BASE + MODEL_FILE)
file.write(response.content)
# 解压缩 mobilenet 模组
tar = tarfile.open(MODEL_FILE)
tar.extractall()
tar.close()
os.remove(MODEL_FILE)
if (os.path.exists(DEST_DIR)):
shutil.rmtree(DEST_DIR)

os.rename(MODEL, DEST_DIR)
# 移动 mobilenet.config" 资讯
shutil.move( "/root/models/research/object_detection/samples/configs/ssd_mobilenet_v1_coco.config", "/root/models" )

第八步 :  修改 Config 档案

%cd /root/models/research/
# Edit Pipeline
import tensorflow as tf
from google.protobuf import text_format
from object_detection.protos import pipeline_pb2
pipeline = pipeline_pb2.TrainEvalPipelineConfig()
config_path = '/root/models/ssd_mobilenet_v1_coco.config'
with tf.gfile.GFile( config_path, "r") as f:
proto_str = f.read()
text_format.Merge(proto_str, pipeline)
pipeline.train_input_reader.tf_record_input_reader.input_path[:] = ['/root/models/widerface-to-tfrecord/output/train.tfrecord']
pipeline.train_input_reader.label_map_path = ' '/root/models/widerface-to-tfrecord/data/face-detection.pbtxt’
pipeline.eval_input_reader[0].tf_record_input_reader.input_path[:] = [' /root/models/widerface-to-tfrecord/output/test.record ']
pipeline.eval_input_reader[0].label_map_path = ' /root/models/widerface-to-tfrecord/data/face-detection.pbtxt '
pipeline.train_config.fine_tune_checkpoint = ' /root/models/pretrained_model/model.ckpt'
pipeline.train_config.num_steps = 500 # training step
pipeline.model.ssd.num_classes = 1 # classes num
pipeline.eval_config.num_examples = 5 # test image number
config_text = text_format.MessageToString(pipeline)
with tf.gfile.Open( config_path, "wb") as f:
f.write(config_text)

第九步 :  进行训练

!python /root/models/research/object_detection/legacy/train.py \
--logtostderr \
--train_dir=/root/models/trained \
--pipeline_config_path=/root/models/ssd_mobilenet_v1_coco.config

※ 训练完成后,将于 models/trained/ 资料夹内产出 model.ckpt-500 档案

第十步 :  产生 Frozen Graph

此步骤可以调整模组输出大小,比如说将原本输入大小 224x224 改成 96x96 。

!python /root/models/research/object_detection/export_tflite_ssd_graph.py \
--pipeline_config_path=/root/models/ssd_mobilenet_v1_coco.config \
--output_directory=/root/models/fine_tuned_model \
--trained_checkpoint_prefix=/root/models/trained/model.ckpt-500

※ 训练完成后,将于 models/fine_tuned_model / 资料夹内产出 tflite_graph.pb 档案

第十一步 :  TensorFlow Lite 转换

# 此处以指令方式进行转换,亦可使用上述文章所介绍代码方式。
! tflite_convert \
--output_file=/root/models/fine_tuned_model/mobilenetssd_uint8_face.tflite \
--graph_def_file=/root/models/fine_tuned_model/tflite_graph.pb \
--inference_type=QUANTIZED_UINT8 \
--input_arrays=normalized_input_image_tensor \
--input_shapes=1,300,300,3 \
--output_arrays="raw_outputs/class_predictions,raw_outputs/box_encodings" \
--default_ranges_min=0 \
--default_ranges_max=6 \
--mean_values=128 \
--std_dev_values=127 \
--allow_custom_ops

※ 训练完成后,将于 models/fine_tuned_model / 资料夹内产出 mobilenetssd_uint8.tflite档案

第十二步 :  Object Detection 范例实现 ( i.MX8M Plus 撰写运行)

import cv2
import numpy as np
from tflite_runtime.interpreter import Interpreter
# 解析 tensorflow lite 档案
interpreter = Interpreter(model_path='mobilenetssd_uint8_face.tflite') # 记得将模组移动至 i.MX8 平台
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
width = input_details[0]['shape'][2]
height = input_details[0]['shape'][1]

# 读取测试资料,并设置于解译器中
frame = cv2.imread('rana.jpg')
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame_resized = cv2.resize(frame_rgb, (width, height))
input_data = np.expand_dims(frame_resized, axis=0)
interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()# 进行推理

# 取得输出资料
detection_boxes = interpreter.get_tensor(output_details[0]['index']) # 输出位置资讯
detection_classes = interpreter.get_tensor(output_details[1]['index']) # 输出类别资讯
detection_scores = interpreter.get_tensor(output_details[2]['index']) # 输出分数资讯
num_boxes = interpreter.get_tensor(output_details[3]['index'])

# 标示物件
for i in range(10):
if detection_scores[0, i] > .5: # 预测值大于 0.5则显示
x = detection_boxes[0, i, [1, 3]] * frame_rgb.shape[1]
y = detection_boxes[0, i, [0, 2]] * frame_rgb.shape[0]
class_id = detection_classes[0, i]
cv2.rectangle(frame_rgb, (x[0], y[0]), (x[1], y[1]), (0, 255, 0), 2)

cv2.imshow('Face Detector',frame)
cv2.waitKey(0)
cv2.destroyAllWindows()

实现结果 : 

如下图所示,成功检测出人脸。
在 i.MX8M Plus 的 NPU 处理器,推理时间(Inference Time) 约 10 ms。

四.  结语

根据上一小节,物件侦测的作法,这里根据 MobileNet SSD 的模组架构,以及结合 Widerface 资料库来重新训练出一套人脸侦测的应用 !!
在 i.MX8MP 的 Vivante VIP8000 NPU 运行,其推理时间可达每秒 9-10 ms 的处理速度,约 110 张 FPS 。此训练的方式称作迁移学习,在上篇与本篇文章皆是以这种技术来学习过去的特征。在介绍此技术之前,作者将会介绍一些常见的 AI 应用像是人脸、手部身体的特征点检测、表情识别、风格转换等等。下一章节将会介绍人脸侦测的延伸应用之一的  脸部表情识别(Facial Expression Recognition)  !! 敬请期待 !! 

五.  参考文件

[1] SSD: Single Shot MultiBox Detector
[2] SSD-Tensorflow

[3] Single Shot MultiBox Detector (SSD) 论文阅读
[4] ssd-mobilenet v1 算法结构及程式码介绍

[5] Get models for TensorFlow Lite
[6] widerface-to-tfrecord
[7]
MobileNets: Efficient Convolutional Neural Networks for Mobile Vision
 

如有任何相关 TensorFlow Lite 进阶技术问题,欢迎至博文底下留言提问 !!
接下来还会分享更多 TensorFlow Lite 进阶文章 !!敬请期待 【ATU Book-i.MX8系列 – TFLite 进阶 !!

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★文明上网,请理性发言。内容一周内被举报5次,发文人进小黑屋喔~

评论