这篇文章主要介绍“Android中如何实现人脸检测功能”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Android中如何实现人脸检测功能”文章能帮助大家解决问题。
1、项目配置
首先,为了将Vision库添加到你的工程,你需要导入Play Services 8.1或者更高的版本进入你的工程。本教程只导入Play Services Vision库。打开你工程中的build.gradle文件然后添加以下的编译依赖节点代码。
compile 'com.google.android.gms:play-services-vision:8.1.0'
当你已经在工程中包含了Play Services,就可以关闭工程中的build.gradle文件,然后打开 AndroidManifest.xml文件。在你的manifest文件中加入下列数据定义人脸检测的依赖项。让Vision库知道你将会在应用中使用它。
<meta-data android:name="com.google.android.gms.vision.DEPENDENCIES"android:value="face"/>
一旦完成了AndroidManifest.xml的配置,你就可以关闭这个文件。下一步,你需要创建一个新的类文件FaceOverlayView.java。这个类继承自View类,用来进行人脸检测逻辑、显示经过分析的图像和在图像上绘制信息来说明观点等功能。
现在,我们开始增加成员变量并实现构造函数。这个Bitmap(位图)对象用来存储将要被分析的位图数据,SparseArray数组用来存储在图像中发现的人脸信息。
public class FaceOverlayView extends View { private Bitmap mBitmap; private SparseArray<Face> mFaces; public FaceOverlayView(Context context) { this(context, null); } public FaceOverlayView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FaceOverlayView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
然后,我们在FaceOverlayView类中增加一个setBitmap(Bitmap bitmap)函数,现在我们只通过这个函数存储位图对象,一会将用这个方法来分析位图数据。
public void setBitmap( Bitmap bitmap ) { mBitmap = bitmap; }
接下来,我们需要一张位图图片。我已经在GitHub上的示例工程中添加了一张,当然你可以使用任何一张你喜欢的图片,然后看看它到底可不可行。当你选好图片后,把它放到res/raw目录下。本教程假定图片的名字叫face.jpg。
当你把图片放到res/raw目录后,打开res/layout/activity_main.xml文件。在这个布局文件中引用一个FaceOverlayView对象,使它在MainActivity中显示出来。
<?xml version="1.0" encoding="utf-8"?> <com.tutsplus.facedetection.FaceOverlayView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/face_overlay" android:layout_width="match_parent" android:layout_height="match_parent" />
定义完布局文件后,打开MainActivity然后在onCreate()函数中引用一个FaceOverlayView的实例。通过输入流从raw文件夹中读入face.jpg并转成位图数据。在拥有了位图数据之后,你就可以通过调用FaceOverlayView的setBitmap方法在自定义视图中设置位图了。
public class MainActivity extends AppCompatActivity { private FaceOverlayView mFaceOverlayView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mFaceOverlayView = (FaceOverlayView) findViewById( R.id.face_overlay ); InputStream stream = getResources().openRawResource( R.raw.face ); Bitmap bitmap = BitmapFactory.decodeStream(stream); mFaceOverlayView.setBitmap(bitmap); } }
2、检测人脸
现在你的工程已经设置好了,是时候来开始检测人脸了。在setBitmap( Bitmap bitmap )方法中定义一个FaceDetector对象。我们可以通过用FaceDetector中的构造器来实现,通过FaceDetector.Builder你可以定义多个参数来控制人脸检测的速度和FaceDetector生成的其他数据。
具体的设置取决于你的应用程序的用途。如果开启了面部特征搜索,那么人脸检测的速度回变得很慢。在大多数程序设计中,每一件事都有它的优缺点。如果想要了解关于FaceDetector.Builder的更多信息,你可以通过查找安卓开发者网站的官网文档获得。
FaceDetector detector = new FaceDetector.Builder( getContext() ) .setTrackingEnabled(false) .setLandmarkType(FaceDetector.ALL_LANDMARKS) .setMode(FaceDetector.FAST_MODE) .build();
你需要检查FaceDetector是否是可操作的。每当用户***次在设备上使用人脸检测,Play Services服务需要加载一组小型本地库去处理应用程序的请求。虽然这些工作一般在应用程序启动之前就完成了,但是做好失败处理同样是必要的。
如果FaceDetector是可操作的,那么你需要将位图数据转化成Frame对象,并通过detect函数传入用来做人脸数据分析。当完成数据分析后,你需要释放探测器,防止内存泄露。***调用invalidate()函数来触发视图刷新。
if (!detector.isOperational()) { //Handle contingency } else { Frame frame = new Frame.Builder().setBitmap(bitmap).build(); mFaces = detector.detect(frame); detector.release(); } invalidate();
现在你已经在图片中发现了人脸信息,并可以使用了。例如,你可以沿着检测出的每一张脸画一个框。在invalidate()函数调用之后,我们可以在OnDraw(Canvas canvas)函数中添加所有必要的逻辑。我们需要确保位图和人脸数据是有效的,在那之后画布上画出位图数据,然后再沿着每张脸的方位画一个框。
因为不同的设备的分辨率不同,你需要通过控制位图的缩放尺寸来保证图片总是能被正确显示出来。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if ((mBitmap != null) && (mFaces != null)) { double scale = drawBitmap(canvas); drawFaceBox(canvas, scale); } }
drawBitmap(Canvas canvas)方法会将图像自适应大小的画在画布上,同时返回一个正确的缩放值供你使用。
private double drawBitmap( Canvas canvas ) { double viewWidth = canvas.getWidth(); double viewHeight = canvas.getHeight(); double imageWidth = mBitmap.getWidth(); double imageHeight = mBitmap.getHeight(); double scale = Math.min( viewWidth / imageWidth, viewHeight / imageHeight ); Rect destBounds = new Rect( 0, 0, (int) ( imageWidth * scale ), (int) ( imageHeight * scale ) ); canvas.drawBitmap( mBitmap, null, destBounds, null ); return scale; }
drawFaceBox(Canvas canvas, double scale)方法会更有趣,被检测到人脸数据以位置信息的方式存储到mFaces中,这个方法将基于这些位置数据中的宽、高在检测到的人脸位置画一个绿色的矩形框。
你需要定义自己的绘画对象,然后从你的SparseArray数组中循环的找出位置、高度和宽度信息,再利用这些信息在画布上画出矩形。
private void drawFaceBox(Canvas canvas, double scale) { //paint should be defined as a member variable rather than //being created on each onDraw request, but left here for //emphasis. Paint paint = new Paint(); paint.setColor(Color.GREEN); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(5); float left = 0; float top = 0; float right = 0; float bottom = 0; for( int i = 0; i < mFaces.size(); i++ ) { Face face = mFaces.valueAt(i); left = (float) ( face.getPosition().x * scale ); top = (float) ( face.getPosition().y * scale ); right = (float) scale * ( face.getPosition().x + face.getWidth() ); bottom = (float) scale * ( face.getPosition().y + face.getHeight() ); canvas.drawRect( left, top, right, bottom, paint ); } }
这时运行你的应用程序,你会发现每张被检测到的人脸都被矩形包围着。值得注意的是,现在我们所使用的人脸检测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。该方法以每个面部特征点的位置为中心,自适应位图大小,用一个圆圈把面部特征点圈起来。
private void drawFaceLandmarks( Canvas canvas, double scale ) { Paint paint = new Paint(); paint.setColor( Color.GREEN ); paint.setStyle( Paint.Style.STROKE ); paint.setStrokeWidth( 5 ); for( int i = 0; i < mFaces.size(); i++ ) { Face face = mFaces.valueAt(i); for ( Landmark landmark : face.getLandmarks() ) { int cx = (int) ( landmark.getPosition().x * scale ); int cy = (int) ( landmark.getPosition().y * scale ); canvas.drawCircle( cx, cy, 10, paint ); } } }
调用该方法之后,您应该看到如下图所示的画面,面部特征点被绿色的小圆圈圈起来。
4、额外的面部数据
人脸的位置和面部特征信息是非常有用的,除此之外,我们在应用中还可以通过Face的内置方法获得人脸检测的更多信息。通过getIsSmilingProbability()、getIsLeftEyeOpenProbability()和getIsRightEyeOpenProbability()方法的返回值(范围从0.0到1.0)我们可以判断人的左右眼是否睁开,是否微笑。当数值越接近于1.0那么可能性也就越大。
你也可以通过人脸检测获得Y和Z轴的欧拉值,Z轴的欧拉值是一定会返回的,如果你想接收到X轴的值,那么你必须在检测时使用一个准确的模式,下面是一个如何或者这些值的例子。
private void logFaceData() { float smilingProbability; float leftEyeOpenProbability; float rightEyeOpenProbability; float eulerY; float eulerZ; for( int i = 0; i < mFaces.size(); i++ ) { Face face = mFaces.valueAt(i); smilingProbability = face.getIsSmilingProbability(); leftEyeOpenProbability = face.getIsLeftEyeOpenProbability(); rightEyeOpenProbability = face.getIsRightEyeOpenProbability(); eulerY = face.getEulerY(); eulerZ = face.getEulerZ(); Log.e( "Tuts+ Face Detection", "Smiling: " + smilingProbability ); Log.e( "Tuts+ Face Detection", "Left eye open: " + leftEyeOpenProbability ); Log.e( "Tuts+ Face Detection", "Right eye open: " + rightEyeOpenProbability ); Log.e( "Tuts+ Face Detection", "Euler Y: " + eulerY ); Log.e( "Tuts+ Face Detection", "Euler Z: " + eulerZ ); } }
关于“Android中如何实现人脸检测功能”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注亿速云行业资讯频道,小编每天都会为大家更新不同的知识点。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。