Android中的人脸检测入门
- 原文作者 :?Paul Trebilcox-Ruiz
- 譯文出自 :?開發技術前線 www.devtf.cn
- 轉載聲明: 本譯文已授權開發者頭條享有獨家轉載權,未經允許,不得轉載!
- 譯者 :?LangleyChang
- 校對者:?desmond1121
- 狀態 : 已完成
隨著Play服務8.1中引入了視覺庫,作為一個開發者,Face Detection讓你可以更容易的通過分析視頻或圖像來定位人臉(face)。一旦有了一個圖像中人臉的列表,你就能獲取到每個人臉的相關信息,比如方向,笑臉的概率,某人是睜眼還是閉眼,還有他們臉上特定的關鍵點(landmark)。
這些信息在很多應用中都有用,比如一個相機應用,它可以在每個人都在睜著眼笑的時刻拍一張照片,或者給圖片加一些搞笑的效果,比如牛角什么的。有一點很重要,注意人臉檢測(Face Detection)并不是人臉識別(facial recognition)。盡管都是在獲取一張臉的信息,(Play服務中的)視覺庫并不會用這些信息來分辨兩張臉是不是來自同一個人。
這個教程會使用一張靜止的圖片來跑Face Detection API,然后獲取有關照片中人物的信息。同時,還會在其上覆蓋圖形來展示獲取到的信息。這個教程的所有代碼都可以在GitHub上找到。
搞笑效果的例子,在人臉上添加牛角
1. 建立項目
為了把視覺庫添加到你的項目中,你需要把Play服務8.1或更高版本導入到你的項目。這個教程僅僅導入了Play服務視覺庫(Play Servcies Vision library)。打開你項目的build.gradle文件,把如下的編譯行添加到dependencies結點中
compile 'com.google.android.gms:play-services-vision:8.1.0'一旦你在你的項目中包含了Play服務,你就可以關掉build.gradle文件,然后打開AndroidManifest.xml。你需要在manifest的application結點添加一個meta-data項來定義人臉檢測的依賴。
<meta-data android:name="com.google.android.gms.vision.DEPENDENCIES" android:value="face"/>完成了AndroidManifest.xml的建立,你就可以直接關掉它了。接著,你需要創建一個叫FaceOverlayView.java的新類。這個類繼承了View,并且包含著項目中人臉檢測的邏輯,其中包括顯示分析的位圖并且在圖像上繪制用以示意的點。
現在,在類的開頭添加一個成員變量并且定義構造函數。Bitmap對象用來存儲被分析的位圖,Face對象中的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上的示例項目中包含了一張,但是你也可以拿你喜歡任何圖片和Face Detection玩玩,看看哪些好使,哪些不好使。這個教程假設你的圖片名為face.jpg。
把你的圖片放在res/raw目錄下之后,打開res/layout/activity_main.xml。這個布局包含了一個FaceOverlayView引用,這樣就可以把它顯示在MainActivity中。
<?xml version="1.0" encoding="utf-8"?> <com.tutsplus.facedetection.FaceOverlayViewxmlns: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。怎么建立呢?你先獲取一個這個view的引用,接著從raw目錄的輸入流中讀取face.jpg文件,然后把它轉換成一個位圖。一旦你拿到了這個位圖,你就可以調用FaceOverlayView的setBitmap()方法把這幅圖像傳給你自定義控件了。
public class MainActivity extends AppCompatActivity {private FaceOverlayView mFaceOverlayView;@Overrideprotected 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.Builder來創建。它允許你定義多個參數,這些參數影響著你多快能檢測出人臉和FaceDetector還能生成其他什么數據。
你選擇什么設置取決于你想在應用中做什么。如果你開啟了關鍵點(landmark)搜索,那么檢測人臉的速度就會慢一些。這和程序設計的大多數時候一樣,魚和熊掌不可兼得。想了解更多關于FaceDetector.Builder中的可用選項,你可以查看Android開發網站的官方文檔。
FaceDetector detector = new FaceDetector.Builder( getContext() ).setTrackingEnabled(false).setLandmarkType(FaceDetector.ALL_LANDMARKS).setMode(FaceDetector.FAST_MODE).build();同時,你也需要看看FaceDetector是否可用。當一個用戶第一次在他的設備上使用人臉檢測的時候,Play服務需要跳出來加載一系列小的原生庫來處理你的請求。雖然大多數情況下這都能在你的應用結束前完成,你還是需要處理加載失敗的意外情況。
如果FaceDetector是可用的,那你就可以把你的位圖轉成一個Frame對象,然后把它傳給檢測器來獲取圖像中關于人臉的數據。完成了以后,你需要釋放檢測器,防止內存泄漏。當人臉檢測完成了以后,你就可以調用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)這個方法比較有趣,它會用利用這個位置按照每張臉的寬度和高度畫一個綠色的矩形把它們都圈出來。
你需要定義你的Paint對象,然后循環遍歷SparseArray的每一個Face找到它的位置,寬度和高度,然后用這些信息在畫布上畫出矩形。
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 );} }現在,你的應用應該能跑了,并且你能看到圖像中每一個檢測出來的臉上都用矩形框了起來。注意,現在Face Detection API仍然相當稚嫩,所以它可能不會檢測出每一張臉。你可以折騰折騰FaceDetector中的其他設置,看看能不能獲得一些數據,盡管我也不能保證。
人臉被檢測了出來并用矩形框出
3.理解關鍵點
關鍵點是一個人臉中一些感興趣的點。Face Detection API不用關鍵點來檢測人臉,但是可以檢測到臉的整體輪廓之后再尋找關鍵點。這就是為什么發現關鍵點在是一個可以用FaceDetector.Builder開啟的可選選項。
你可以把這些關鍵點作為一個額外的信息源,比如對象的眼睛在哪里,這樣你就可以在你的應用中恰當的交互。可被找到的關鍵點共有十二個:
- 左右眼
- 左右耳
- 左右耳廓尖
- 鼻子基部
- 左右臉頰
- 嘴的左右角
- 嘴基部
哪些關鍵點是可用的取決于檢測到的臉的角度。舉個例子,某人的側臉中只能檢測到一個可見的眼睛,這意味著另一個眼睛是檢測不到的。下面的表格概括了基于人臉的歐拉Y角度(左右方向)哪些關鍵點是可以被檢測到的
| < -36° | 左眼,嘴的左半邊,左耳,鼻子基部,左臉頰 |
| -36° to -12° | 嘴的左半邊,鼻子基部,嘴的底部,右眼,左眼,左臉頰,左耳尖 |
| -12° to 12° | 右眼,左眼,鼻子基部,左臉頰,右臉頰,嘴的左半邊,嘴的右半邊,嘴的底部 |
| 12° to 36° | 嘴的右半邊,鼻子基部,嘴的底部,左眼,右眼,右臉頰,右耳尖 |
| > 36° | 右眼,嘴的右半邊,右耳,鼻子基部,右臉頰 |
不管你信不信,在你的應用中使用關鍵點也是非常的容易,因為你已經在人臉檢測的時候把它們包含到了你的項目中了。你只需要調用一個Face對象的getLandMarks()就能獲取到一個LandMark對象的List,這樣你就能使用它了。
你這個教程中,你將會在每個檢測出的關鍵點上繪制一個小圓圈,只需調用一個新的方法drawFaceLandmarks(Canvas canvas, double scale)。這個方法是在onDraw(Canvas canvas)調用,而不是drawFaceBox(Canvas canvas)。它會獲取每個關鍵點的位置,根據位圖的比例調整它的大小,然后繪制出示意關鍵點的圓圈。
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 );} }結論
在這個教程中,你已經學到了Play服務視覺庫的一個主要組件,Face Detection。你現在知道如何在一張靜止的圖像中檢測人臉,如何獲取其中的信息,然后從每張人臉中找到重要的關鍵點。
用你學到的東西,你應該可以在你自己的應用中添加一些很棒的特性,比如添加靜態圖像、在視頻中跟蹤人臉或者你能想到的任何創意。
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Android中的人脸检测入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CVPR 2017论文集锦
- 下一篇: PaddlePaddle文本卷积实现情感