PaddleDetection:人脸检测算法细节‘BlazeFace’

2021SC@SDUSC

本周分析PaddleDetection特色模型人脸识别里的算法细节BlazeFace。

BlazeFace 是一个轻量且性能出色的人脸检测器,为移动 GPU 推理量身定制。它在旗舰设备上的运行速度为200-1000+ FPS。这种超实时性能使其能够应用于任何增强现实管道,该管道需要准确的面部感兴趣区域作为任务特定模型的输入,例如2D/3D 面部关键点或几何估计,人脸特征或表情分类,以及人脸区域分割。BlazeFace 的贡献包括:

一个轻量级的特征提取网络,其灵感来自于 MobileNetV1/V2;
一种对 GPU 友好的 SSD 锚点改良方案;
一种改进的限制分辨率策略替代非极大值抑制。
由于前置和后置相机的焦距以及拍摄物体的典型尺寸不同,BlazeFace 为二者分别建立了模型。除了预测轴对齐的人脸矩形外,BlazeFace 模型还可以生成6个人脸关键点(眼睛中心、耳屏点、嘴部中心和鼻尖)坐标,以便估计面部旋转(滚动角度)。这样可以将旋转的面部矩形传递到视频处理管道的后续特定任务阶段,从而减轻后续处理步骤中显著平移和旋转不变性要求。
————————————————

转载自:https://blog.youkuaiyun.com/yiran103/article/details/100063021

模型代码:

首先对于输入来说,包含了初始化函数

def __init__(self,
                 min_subgraph_size=60,
                 use_gpu=False,
                 run_mode='fluid',
                 threshold=0.5):

        model_dir = os.path.join(self.directory, 'blazeface_keypoint')
        self.predictor = D.load_predictor(
            model_dir,
            run_mode=run_mode,
            min_subgraph_size=min_subgraph_size,
            use_gpu=use_gpu)

参数解释:

run_mode:默认采用采用 fluid 运行模式,可选模式有fluid/trt_fp32/trt_fp16

p16是指采用2字节(16位)进行编码存储的一种数据类型;同理fp32是指采用4字节(32位);

use_gpu (bool):是否使用gpu,默认不采用gpu

threshold (float):保留输出结果的阈值。

本函数初始化一些重要参数,并定义了文件路径;

面部图像处理函数定义如下:

def face_img_process(self,
                         image,
                         mean=[104., 117., 123.],
                         std=[127.502231, 127.502231, 127.502231]):
        image = np.array(image)
        # HWC to CHW
        if len(image.shape) == 3:
            image = np.swapaxes(image, 1, 2)
            image = np.swapaxes(image, 1, 0)
        # RBG to BGR
        image = image[[2, 1, 0], :, :]
        image = image.astype('float32')
        image -= np.array(mean)[:, np.newaxis, np.newaxis].astype('float32')
        image /= np.array(std)[:, np.newaxis, np.newaxis].astype('float32')
        image = [image]
        image = np.array(image)

        return image

该函数接收待处理图像,并将图像处理成可检测格式,如将HWC转化为CHW,将RBG转化为BGR

后处理函数定义如下:

def postprocess(self, boxes_list, lmks_list, im_info, threshold=0.5):
        assert len(boxes_list) == len(lmks_list)
        best_np_boxes, best_np_lmk = boxes_list[0], lmks_list[0]
        for i in range(1, len(boxes_list)):
            #judgment detection score
            if boxes_list[i][0][1] > 0.9:
                break
            face_width = boxes_list[i][0][4] - boxes_list[i][0][2]
            if boxes_list[i][0][1] - best_np_boxes[0][
                    1] > 0.01 and face_width > 0.2:
                best_np_boxes, best_np_lmk = boxes_list[i], lmks_list[i]
        # postprocess output of predictor
        results = {}
        results['landmark'] = D.lmk2out(best_np_boxes, best_np_lmk, im_info,
                                        threshold)

        w, h = im_info['origin_shape']
        best_np_boxes[:, 2] *= h
        best_np_boxes[:, 3] *= w
        best_np_boxes[:, 4] *= h
        best_np_boxes[:, 5] *= w
        expect_boxes = (best_np_boxes[:, 1] > threshold) & (
            best_np_boxes[:, 0] > -1)
        best_np_boxes = best_np_boxes[expect_boxes, :]
        for box in best_np_boxes:
            print('class_id:{:d}, confidence:{:.4f},'
                  'left_top:[{:.2f},{:.2f}],'
                  ' right_bottom:[{:.2f},{:.2f}]'.format(
                      int(box[0]), box[1], box[2], box[3], box[4], box[5]))
        results['boxes'] = best_np_boxes
        return results

 由于8×8特征图上扩增了anchor数量,大量的检测结果会重叠在一起。传统的处理方式是使用NMS。作者提出使用融合(blending)而非抑制(suppression)策略。具体的做法为将重叠边界框回归参数做加权平均计算,而NMS则是采用重叠结果中的一个。

预测函数定义如下:

def predict(self,
                image,
                threshold=0.5,
                repeats=1,
                visualization=False,
                with_lmk=True,
                save_dir='blaze_result'):
        '''
        Args:
            image (str/np.ndarray): path of image/ np.ndarray read by cv2
            threshold (float): threshold of predicted box' score
        Returns:
            results (dict): include 'boxes': np.ndarray: shape:[N,6], N: number of box,
                            matix element:[class, score, x_min, y_min, x_max, y_max]
        '''
        shrink = [960, 640, 480, 320, 180]
        boxes_list = []
        lmks_list = []
        for sh in shrink:
            inputs, im_info = self.transform(image, shrink=sh)
            np_boxes, np_lmk = None, None

            input_names = self.predictor.get_input_names()
            for i in range(len(input_names)):
                input_tensor = self.predictor.get_input_tensor(input_names[i])
                input_tensor.copy_from_cpu(inputs[input_names[i]])

            t1 = time.time()
            for i in range(repeats):
                self.predictor.zero_copy_run()
                output_names = self.predictor.get_output_names()
                boxes_tensor = self.predictor.get_output_tensor(output_names[0])
                np_boxes = boxes_tensor.copy_to_cpu()
                if with_lmk == True:
                    face_index = self.predictor.get_output_tensor(output_names[
                        1])
                    landmark = self.predictor.get_output_tensor(output_names[2])
                    prior_boxes = self.predictor.get_output_tensor(output_names[
                        3])
                    np_face_index = face_index.copy_to_cpu()
                    np_prior_boxes = prior_boxes.copy_to_cpu()
                    np_landmark = landmark.copy_to_cpu()
                    np_lmk = [np_face_index, np_landmark, np_prior_boxes]

            t2 = time.time()
            ms = (t2 - t1) * 1000.0 / repeats
            print("Inference: {} ms per batch image".format(ms))

            # do not perform postprocess in benchmark mode
            results = []
            if reduce(lambda x, y: x * y, np_boxes.shape) < 6:
                print('[WARNNING] No object detected.')
                results = {'boxes': np.array([])}
            else:
                boxes_list.append(np_boxes)
                lmks_list.append(np_lmk)

        results = self.postprocess(
            boxes_list, lmks_list, im_info, threshold=threshold)

        if visualization:
            if not os.path.exists(save_dir):
                os.makedirs(save_dir)
            output = D.visualize_box_mask(
                im=image, results=results, labels=["background", "face"])
            name = str(time.time()) + '.png'
            save_path = os.path.join(save_dir, name)
            output.save(save_path)
            img = cv2.cvtColor(np.array(output), cv2.COLOR_RGB2BGR)
            results['image'] = img

        return results

参数说明:

image (str/np.ndarray): cv2读取的image/np.ndarray的路径

threshold (float): 预测分数阈值

该函数接收输入图像,并根据训练模型返回预测结果,设置预测分数阈值可以优化模型运行效率。

并且,函数内定义多种异常处理,对图像格式等做了规定。

返回的结果包含'boxs':np.ndarray, matix element:[class, score, x_min, y_min, x_max, y_max]

### 构建基于人脸识别的门闸验证系统的实现方案 #### 1. 系统架构概述 构建一个人脸识别门闸验证系统涉及多个模块协同工作,主要包括前端设备(摄像头)、后台处理服务器以及控制单元。该系统的工作流程如下: - **人脸捕获**:通过安装在入口处的高清摄像装置实时获取进入者的面部影像; - **特征提取与对比**:利用深度学习模型如BlazeFace进行高效精准的人脸定位并裁剪出感兴趣区域(ROI),随后采用ArcFace算法计算得到高维空间内表征个体身份的独特向量表示形式[^1]; - **权限判断及响应动作触发**:一旦完成上述两步操作之后便可以将所得结果同预存于本地数据库里的注册样本集做相似度衡量进而决定是否允许通行。 #### 2. 技术选型说明 为了确保项目的顺利实施,在技术栈的选择上需考虑稳定性、易用性和扩展性等因素。以下是推荐的技术框架组合: - **编程语言**: Python因其丰富的机器学习库支持成为首选开发工具之一; - **深度学习平台**: PaddlePaddle作为国内领先的AI开放平台提供了大量预训练模型供开发者调用,特别适合本案例中的应用场景需求[^4]; - **硬件设施**: 对于实际部署而言,建议选用具备一定算力水平且易于集成至现有安防体系内的嵌入式工控板卡产品线。 #### 3. 关键功能设计要点 针对具体业务逻辑层面的设计考量主要有以下几个方面: ##### a. 用户管理子系统 负责维护所有合法访问人员的基础资料档案及其对应的照片素材集合,并提供便捷的操作界面以便管理员日常增删改查作业。 ##### b. 访问日志记录机制 每当有访客尝试进出受保护区域时都应即时创建一条详尽的日志条目保存下来,内容至少涵盖时间戳记、抓拍画面片段链接地址等必要字段信息[^2]。 ##### c. 安全策略配置选项 考虑到不同场合下可能存在的特殊要求,有必要引入灵活的安全等级设定参数让用户自行调整诸如误拒率/漏识率之间的平衡关系。 ```python import paddlehub as hub from PIL import Image def recognize_face(image_path): model = hub.Module(name='pyramidbox_lite_server') result = model.face_detection(images=[Image.open(image_path)]) if not result[0]['data']: return None face_module = hub.Module(name="arcface") embedding = face_module.keypoint_detection(np.array(result[0]['data'][0])) return embedding['face_embedding'] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值