相关阅读:
吊炸天!74款APP完整源码!
[干货]2017已来,最全面试总结——这些Android面试题你一定需要
Android 名企面试题及涉及知识点整理
来源:Paul Trebilcox-Ruiz
译文:小谢
http://android.jobbole.com/82310/
自从Play Services 8.1中引入了Vision开发库,开发者可以方便地对视频或图像进行人脸定位。只要有一张包含了人脸信息的图片,你就可以收集每一张图片上的人脸信息,例如人脸的位置、是否微笑、睁眼或者闭眼和他们具体的面部特征。
这些信息对于许多应用来说是非常有用的,例如一个相机应用可以利用这些信息做到当所有人都睁眼微笑的时候拍照,或者利用它增加一些搞笑效果,例如给照片中的人头上添加一个独角兽的角。不过大家要注意的是,这只能用来做人脸检测,而不是人脸识别。我们只能利用它检测到人脸信息,但是不能通过它判断两张照片上的是否是同一个人。
这篇教程通过人脸检测API对静态图片分析,识别图片中的人物,同时对覆盖图形(overlaid graphics)进行绘制。所有教程使用的代码可以在GitHub上找到。
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库知道你将会在应用中使用它。
一旦完成了AndroidManifest.xml的配置,你就可以关闭这个文件。下一步,你需要创建一个新的类文件FaceOverlayView.java。这个类继承自View类,用来进行人脸检测逻辑、显示经过分析的图像和在图像上绘制信息来说明观点等功能。
现在,我们开始增加成员变量并实现构造函数。这个Bitmap(位图)对象用来存储将要被分析的位图数据,SparseArray数组用来存储在图像中发现的人脸信息。
public
class
FaceOverlayView
extends
View
{
private
Bitmap mBitmap;
private
SparseArray
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中显示出来。
"1.0"
encoding=
"utf-8"
?>
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