Android人脸检测介绍

简介:

自从Play Services 8.1中引入了Vision开发库,开发者可以方便地对视频或图像进行人脸定位。只要有一张包含了人脸信息的图片,你就可以收集每一张图片上的人脸信息,例如人脸的位置、是否微笑、睁眼或者闭眼和他们具体的面部特征。

这些信息对于许多应用来说是非常有用的,例如一个相机应用可以利用这些信息做到当所有人都睁眼微笑的时候拍照,或者利用它增加一些搞笑效果,例如给照片中的人头上添加一个独角兽的角。不过大家要注意的是,这只能用来做人脸检测,而不是人脸识别。我们只能利用它检测到人脸信息,但是不能通过它判断两张照片上的是否是同一个人。

这篇教程通过人脸检测API对静态图片分析,识别图片中的人物,同时对覆盖图形(overlaid graphics)进行绘制。所有教程使用的代码可以在GitHub上找到。

1、项目配置

首先,为了将Vision库添加到你的工程,你需要导入Play Services 8.1或者更高的版本进入你的工程。本教程只导入Play Services Vision库。打开你工程中的build.gradle文件然后添加以下的编译依赖节点代码。


  
  
  1. compile 'com.google.android.gms:play-services-vision:8.1.0' 

当你已经在工程中包含了Play Services,就可以关闭工程中的build.gradle文件,然后打开 AndroidManifest.xml文件。在你的manifest文件中加入下列数据定义人脸检测的依赖项。让Vision库知道你将会在应用中使用它。


  
  
  1. <meta-data android:name="com.google.android.gms.vision.DEPENDENCIES"android:value="face"/> 

一旦完成了AndroidManifest.xml的配置,你就可以关闭这个文件。下一步,你需要创建一个新的类文件FaceOverlayView.java。这个类继承自View类,用来进行人脸检测逻辑、显示经过分析的图像和在图像上绘制信息来说明观点等功能。

现在,我们开始增加成员变量并实现构造函数。这个Bitmap(位图)对象用来存储将要被分析的位图数据,SparseArray数组用来存储在图像中发现的人脸信息。


  
  
  1. public class FaceOverlayView extends View { 
  2. private Bitmap mBitmap; 
  3. private SparseArray&lt;Face&gt; mFaces; 
  4. public FaceOverlayView(Context context) { 
  5. this(context, null); 
  6. public FaceOverlayView(Context context, AttributeSet attrs) { 
  7. this(context, attrs, 0); 
  8. public FaceOverlayView(Context context, AttributeSet attrs, int defStyleAttr) { 
  9. super(context, attrs, defStyleAttr); 
  10.  

然后,我们在FaceOverlayView类中增加一个setBitmap(Bitmap bitmap)函数,现在我们只通过这个函数存储位图对象,一会将用这个方法来分析位图数据。


  
  
  1. public void setBitmap( Bitmap bitmap ) { 
  2. mBitmap = bitmap; 
  3.  

接下来,我们需要一张位图图片。我已经在GitHub上的示例工程中添加了一张,当然你可以使用任何一张你喜欢的图片,然后看看它到底可不可行。当你选好图片后,把它放到res/raw目录下。本教程假定图片的名字叫face.jpg。

当你把图片放到res/raw目录后,打开res/layout/activity_main.xml文件。在这个布局文件中引用一个FaceOverlayView对象,使它在MainActivity中显示出来。


  
  
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <com.tutsplus.facedetection.FaceOverlayView 
  3. xmlns:android="http://schemas.android.com/apk/res/android" 
  4. android:id="@+id/face_overlay" 
  5. android:layout_width="match_parent" 
  6. android:layout_height="match_parent" />  

定义完布局文件后,打开MainActivity然后在onCreate()函数中引用一个FaceOverlayView的实例。通过输入流从raw文件夹中读入face.jpg并转成位图数据。在拥有了位图数据之后,你就可以通过调用FaceOverlayView的setBitmap方法在自定义视图中设置位图了。


  
  
  1. public class MainActivity extends AppCompatActivity { 
  2. private FaceOverlayView mFaceOverlayView; 
  3. @Override 
  4. protected void onCreate(Bundle savedInstanceState) { 
  5. super.onCreate(savedInstanceState); 
  6. setContentView(R.layout.activity_main); 
  7. mFaceOverlayView = (FaceOverlayView) findViewById( R.id.face_overlay ); 
  8. InputStream stream = getResources().openRawResource( R.raw.face ); 
  9. Bitmap bitmap = BitmapFactory.decodeStream(stream); 
  10. mFaceOverlayView.setBitmap(bitmap); 
  11.  

2、检测人脸

现在你的工程已经设置好了,是时候来开始检测人脸了。在setBitmap( Bitmap bitmap )方法中定义一个FaceDetector对象。我们可以通过用FaceDetector中的构造器来实现,通过FaceDetector.Builder你可以定义多个参数来控制人脸检测的速度和FaceDetector生成的其他数据。

具体的设置取决于你的应用程序的用途。如果开启了面部特征搜索,那么人脸检测的速度回变得很慢。在大多数程序设计中,每一件事都有它的优缺点。如果想要了解关于FaceDetector.Builder的更多信息,你可以通过查找安卓开发者网站的官网文档获得。


  
  
  1. FaceDetector detector = new FaceDetector.Builder( getContext() ) 
  2. .setTrackingEnabled(false
  3. .setLandmarkType(FaceDetector.ALL_LANDMARKS) 
  4. .setMode(FaceDetector.FAST_MODE) 
  5. .build();  

你需要检查FaceDetector是否是可操作的。每当用户第一次在设备上使用人脸检测,Play Services服务需要加载一组小型本地库去处理应用程序的请求。虽然这些工作一般在应用程序启动之前就完成了,但是做好失败处理同样是必要的。

如果FaceDetector是可操作的,那么你需要将位图数据转化成Frame对象,并通过detect函数传入用来做人脸数据分析。当完成数据分析后,你需要释放探测器,防止内存泄露。最后调用invalidate()函数来触发视图刷新。


  
  
  1. if (!detector.isOperational()) { 
  2. //Handle contingency 
  3. else { 
  4. Frame frame = new Frame.Builder().setBitmap(bitmap).build(); 
  5. mFaces = detector.detect(frame); 
  6. detector.release(); 
  7. invalidate();  

现在你已经在图片中发现了人脸信息,并可以使用了。例如,你可以沿着检测出的每一张脸画一个框。在invalidate()函数调用之后,我们可以在OnDraw(Canvas canvas)函数中添加所有必要的逻辑。我们需要确保位图和人脸数据是有效的,在那之后画布上画出位图数据,然后再沿着每张脸的方位画一个框。

因为不同的设备的分辨率不同,你需要通过控制位图的缩放尺寸来保证图片总是能被正确显示出来。


  
  
  1. @Override 
  2. protected void onDraw(Canvas canvas) { 
  3. super.onDraw(canvas); 
  4. if ((mBitmap != null) &amp;&amp; (mFaces != null)) { 
  5. double scale = drawBitmap(canvas); 
  6. drawFaceBox(canvas, scale); 
  7.  

drawBitmap(Canvas canvas)方法会将图像自适应大小的画在画布上,同时返回一个正确的缩放值供你使用。


  
  
  1. private double drawBitmap( Canvas canvas ) { 
  2. double viewWidth = canvas.getWidth(); 
  3. double viewHeight = canvas.getHeight(); 
  4. double imageWidth = mBitmap.getWidth(); 
  5. double imageHeight = mBitmap.getHeight(); 
  6. double scale = Math.min( viewWidth / imageWidth, viewHeight / imageHeight ); 
  7. Rect destBounds = new Rect( 0, 0, (int) ( imageWidth * scale ), (int) ( imageHeight * scale ) ); 
  8. canvas.drawBitmap( mBitmap, null, destBounds, null ); 
  9. return scale; 
  10.  

drawFaceBox(Canvas canvas, double scale)方法会更有趣,被检测到人脸数据以位置信息的方式存储到mFaces中,这个方法将基于这些位置数据中的宽、高在检测到的人脸位置画一个绿色的矩形框。

你需要定义自己的绘画对象,然后从你的SparseArray数组中循环的找出位置、高度和宽度信息,再利用这些信息在画布上画出矩形。


  
  
  1. private void drawFaceBox(Canvas canvas, double scale) { 
  2. //paint should be defined as a member variable rather than 
  3. //being created on each onDraw request, but left here for 
  4. //emphasis. 
  5. Paint paint = new Paint(); 
  6. paint.setColor(Color.GREEN); 
  7. paint.setStyle(Paint.Style.STROKE); 
  8. paint.setStrokeWidth(5); 
  9. float left = 0; 
  10. float top = 0; 
  11. float right = 0; 
  12. float bottom = 0; 
  13. forint i = 0; i &lt; mFaces.size(); i++ ) { 
  14. Face face = mFaces.valueAt(i); 
  15. left = (float) ( face.getPosition().x * scale ); 
  16. top = (float) ( face.getPosition().y * scale ); 
  17. right = (float) scale * ( face.getPosition().x + face.getWidth() ); 
  18. bottom = (float) scale * ( face.getPosition().y + face.getHeight() ); 
  19. canvas.drawRect( lefttopright, bottom, paint ); 
  20.  

这时运行你的应用程序,你会发现每张被检测到的人脸都被矩形包围着。值得注意的是,现在我们所使用的人脸检测API版本非常新,所以它不一定能检测到所有的人脸。你可以通过修改FaceDetector.Builder中的配置,使它获得到更多的信息,但是我不能保证这一定会起作用。

3、理解面部特征

面部特征指的是脸上的一些特殊点。人脸检测API不是依靠面部特征来检测一张人脸,而是在检测到人脸之后才能检测面部特征。这就是为什么检测面部特征是一个可选的设置,我们可以通过FaceDetector.Builder开启。

你可以把这些面部特征信息做为一个附加的信息来源,例如需找模特的眼睛在哪里,这样就可以在应用中做相应的处理了。有十二种面部特征是可能被检测出来的: 左右眼 左右耳朵 左右耳垂 鼻子 左右脸颊 左右嘴角 嘴

面部特征的检测取决于检测的角度。例如,有人侧对着的话,那么只能检测到他的一个眼睛,这意味着另一只眼睛不会被检测到。下表概述了哪些面部特征应该检测到(Y是基于脸部的欧拉角(左或右))。

欧拉角 Y 可见的标志
< -36° 左眼、左嘴角、左耳朵、鼻子、左脸颊
-36° to -12° 左嘴角、鼻子、下嘴角、右眼、左眼、左脸颊、左耳垂
-12° to 12° 右眼、左眼、鼻子、左脸颊、右脸颊、左嘴角、右嘴角、下嘴角
12° to 36° 右嘴角、鼻子、下嘴角、左眼、右眼、右脸颊、右耳垂
> 36° 右眼、右嘴角、右耳朵、鼻子、右脸颊
 

如果在人脸检测中,你已经开启了面部特征检测,那么你可以很容易地使用面部特征信息。你只需要调用getLandmarks()函数获得一个面部特征列表就可以了,你可以直接使用它。

在本教程中,你可以利用一个新的函数drawFaceLandmarks(Canvas canvas, double scale)在人脸检测中检测出的每一个面部特征上画一个小圆圈,在onDraw(canvas canvas)函数中,用drawFaceLandmarks替换drawFaceBox。该方法以每个面部特征点的位置为中心,自适应位图大小,用一个圆圈把面部特征点圈起来。


  
  
  1. private void drawFaceLandmarks( Canvas canvas, double scale ) { 
  2. Paint paint = new Paint(); 
  3. paint.setColor( Color.GREEN ); 
  4. paint.setStyle( Paint.Style.STROKE ); 
  5. paint.setStrokeWidth( 5 ); 
  6. forint i = 0; i &lt; mFaces.size(); i++ ) { 
  7. Face face = mFaces.valueAt(i); 
  8. for ( Landmark landmark : face.getLandmarks() ) { 
  9. int cx = (int) ( landmark.getPosition().x * scale ); 
  10. int cy = (int) ( landmark.getPosition().y * scale ); 
  11. canvas.drawCircle( cx, cy, 10, paint ); 

调用该方法之后,您应该看到如下图所示的画面,面部特征点被绿色的小圆圈圈起来。

4、额外的面部数据

人脸的位置和面部特征信息是非常有用的,除此之外,我们在应用中还可以通过Face的内置方法获得人脸检测的更多信息。通过getIsSmilingProbability()、getIsLeftEyeOpenProbability()和getIsRightEyeOpenProbability()方法的返回值(范围从0.0到1.0)我们可以判断人的左右眼是否睁开,是否微笑。当数值越接近于1.0那么可能性也就越大。

你也可以通过人脸检测获得Y和Z轴的欧拉值,Z轴的欧拉值是一定会返回的,如果你想接收到X轴的值,那么你必须在检测时使用一个准确的模式,下面是一个如何或者这些值的例子。


  
  
  1. private void logFaceData() { 
  2. float smilingProbability; 
  3. float leftEyeOpenProbability; 
  4. float rightEyeOpenProbability; 
  5. float eulerY; 
  6. float eulerZ; 
  7. forint i = 0; i &lt; mFaces.size(); i++ ) { 
  8. Face face = mFaces.valueAt(i); 
  9. smilingProbability = face.getIsSmilingProbability(); 
  10. leftEyeOpenProbability = face.getIsLeftEyeOpenProbability(); 
  11. rightEyeOpenProbability = face.getIsRightEyeOpenProbability(); 
  12. eulerY = face.getEulerY(); 
  13. eulerZ = face.getEulerZ(); 
  14. Log.e( "Tuts+ Face Detection""Smiling: " + smilingProbability ); 
  15. Log.e( "Tuts+ Face Detection""Left eye open: " + leftEyeOpenProbability ); 
  16. Log.e( "Tuts+ Face Detection""Right eye open: " + rightEyeOpenProbability ); 
  17. Log.e( "Tuts+ Face Detection""Euler Y: " + eulerY ); 
  18. Log.e( "Tuts+ Face Detection""Euler Z: " + eulerZ ); 

结论

在本教程中,你已经学会了Play Services Vision库中的一个主要组件:人脸检测。你现在知道了如何在一张静态图片中检测到人脸、如何收集人脸的信息并找到每个人脸的重要面部特征。

用你学到的这些东西,可以给自己的图像应用增加一个有意思的特性,在视频中跟踪人脸,或者做任何你能想到的事情。





本文作者:佚名
来源:51CTO
目录
相关文章
|
3月前
|
API 开发工具 Android开发
视觉智能开放平台产品使用合集之人脸活体检测能力是否支持Android端或者iOS端直接调用
视觉智能开放平台是指提供一系列基于视觉识别技术的API和服务的平台,这些服务通常包括图像识别、人脸识别、物体检测、文字识别、场景理解等。企业或开发者可以通过调用这些API,快速将视觉智能功能集成到自己的应用或服务中,而无需从零开始研发相关算法和技术。以下是一些常见的视觉智能开放平台产品及其应用场景的概览。
|
1月前
|
设计模式 Java Android开发
安卓应用开发中的内存泄漏检测与修复
【9月更文挑战第30天】在安卓应用开发过程中,内存泄漏是一个常见而又棘手的问题。它不仅会导致应用运行缓慢,还可能引发应用崩溃,严重影响用户体验。本文将深入探讨如何检测和修复内存泄漏,以提升应用性能和稳定性。我们将通过一个具体的代码示例,展示如何使用Android Studio的Memory Profiler工具来定位内存泄漏,并介绍几种常见的内存泄漏场景及其解决方案。无论你是初学者还是有经验的开发者,这篇文章都将为你提供实用的技巧和方法,帮助你打造更优质的安卓应用。
|
6月前
|
Android开发
如何在Android真机上检测是否有Google Map add-on
如何在Android真机上检测是否有Google Map add-on
73 3
|
4月前
|
监控 Java Android开发
探究Android应用开发中的内存泄漏检测与修复
在移动应用的开发过程中,优化用户体验和提升性能是至关重要的。对于Android平台而言,内存泄漏是一个常见且棘手的问题,它可能导致应用运行缓慢甚至崩溃。本文将深入探讨如何有效识别和解决内存泄漏问题,通过具体案例分析,揭示内存泄漏的成因,并提出相应的检测工具和方法。我们还将讨论一些最佳实践,帮助开发者预防内存泄漏,确保应用稳定高效地运行。
|
6月前
|
Android开发
android检测网络连接是否存在(一)
android检测网络连接是否存在(一)
49 2
|
6月前
|
Android开发
安卓手机快速过检测完成某某学习
安卓手机快速过检测完成某某学习
53 0
|
6月前
|
XML 监控 Java
Android App开发之事件交互Event中检测软键盘和物理按键讲解及实战(附源码 演示简单易懂)
Android App开发之事件交互Event中检测软键盘和物理按键讲解及实战(附源码 演示简单易懂)
733 0
|
Android开发 UED
Android版本检测更新升级
Android版本检测更新升级
|
API Android开发
使用Android的Service实现后台定时检测并重启应用
使用Android的Service实现后台定时检测并重启应用
|
Android开发
全网最优雅安卓列表项可见性检测
全网最优雅安卓列表项可见性检测
176 0
下一篇
无影云桌面