单例模式是一个常用的设计模式,从理解和实现的角度来看该模式都比较简单。即便如此,滥用单例模式的情况也屡见不鲜。对于单例模式的一些基础性介绍,比如懒汉模式(注意多线程同步问题)、恶汉模式、DoubleCheck方法(该方法下要注意对类型变量使用volatile关键字,保证多线程之间的可见性及避免指令重排)、内部类实现(实现懒加载,通过使用final关键字来实现安全发布)、枚举类实现,这些话题在菜鸟教程里单例模式部分有详细阐述。
实现单例模式的众多方法中都有如下几个概念:构造函数被声明为private,这样可以阻止其他类实例化该对象;私有的static变量是该类的唯一实例;被声明为public的static方法返回唯一的静态变量,该静态方法是获取该单例类的唯一访问点。(简称两私一公开)
注:可能JAVA通过枚举实现的时候和上述描述有点出入。
总结下来满足如下两点可以使用单例模式:一是确定该对象在系统中只有唯一实例;二是该类基本不存在扩展的需求(单例模式默认构造函数被声明为private,在子类中不可用)。所以通常在日志系统、驱动对象、缓冲及线程池等场景使用。
在这么多单例模式在实现方法中,有些很细小在知识点,比如用JAVA语言实现,就存在反射、克隆等手段绕过单例。还有在《程序员在自我修养》一书中的1.62节的过度优化中提到在指令执行顺序问题,也有可能导致单例模式崩溃。所以个人建议在严格遵守单例使用原则前提下:JAVA使用枚举方式实现;C++语言使用饿汉模式实现。
下面来谈谈最近有一个需求(2017年6月),对图像进行人脸检测,不仅要检测到人脸区域,还要检测出人脸的姿态(Pitch、Roll、Yaw见图)。
Pitch-Roll-Yaw参数标准
通过相关咨询,最后决定使用通甲优博公布的VOOME-SDK1.0。这里我们来解释下人脸认证,整个过程其实可以分为三步:
人脸检测:将图像中的人脸区域检测出来。
检测出人脸区域
人脸对齐:在第一步的输出的基础上,在人脸区域上检测出脸部的特征点(例如:眼角、鼻尖、嘴角等……见图),并根据这些特征点的位置经过仿射变换得到一个标准人脸,即归一化。通甲优博的VOOME-SDK1.0一共检测出了68个特征点。人脸姿态根据68个特征点的位置是可以计算得到,该SDK在人脸对齐模块中是输出了姿态信息的,即Pitch、Roll、Yaw的值。
特征点检测模块检测的68个特征点
人脸识别:将归一化后的人脸区域作为输入计算出特征模板,在模板库里面进行比对。根据预先设定的阈值来判断是否为同一个人。
在特征模板库里比对
调用VOOME-SDK,现有的应用层是基于JAVA语言开发的,这里就涉及到如何在JAVA语言调用C\C++的使用,实现步骤如下:
- 定义好JAVA语言的接口;
- 生成C\C++的头文件;
- 编写C\C++代码,生成动态链接库;
- JAVA语言载入动态链接库,使用步骤一写好的JAVA语言接口;
上述的步骤在本文中有详细讲解,我们着重谈下第三步C\C++代码的编写等问题。
第一个问题:每次人脸检测都需要初始化VOOMESDK。
起初实现该方法的时候,就是将JAVA摄像头API获取到的图片,传递给本地函数,本地函数检测图片,返回相关信息。