Android API 人臉檢測(Face Detect)

yangxi_001發表於2016-01-25
摘要:通過兩個主要的API,Android提供了一個直接在點陣圖上進行臉部檢測的方法,這兩個API分別是 android.media.FaceDetector和android.media.FaceDetector.Face,已經包含在Android官方API中。

通過兩個主要的API,Android提供了一個直接在點陣圖上進行臉部檢測的方法,這兩個API分別是    android.media.FaceDetector和android.media.FaceDetector.Face,已經包含在Android官方API中。向大家介紹了這些API。

所謂人臉檢測就是指從一副圖片或者一幀視訊中標定出所有人臉的位置和尺寸。人臉檢測是人臉識別系統中的一個重要環節,也可以獨立應用於視訊監控。在數字媒體日益普及的今天,利用人臉檢測技術還可以幫助我們從海量圖片資料中快速篩選出包含人臉的圖片。     在目前的數碼相機中,人臉檢測可以用來完成自動對焦,即“臉部對焦”。“臉部對焦”是在自動曝光和自動對焦發明後,二十年來最重要的一次攝影技術革新。家用數碼相機,佔絕大多數的照片是以人為拍攝主體的,這就要求相機的自動曝光和對焦以人物為基準。
構建一個人臉檢測的Android Activity

你可以構建一個通用的Android Activity,我們擴充套件了基類ImageView,成為MyImageView,而我們需要進行檢測的包含人臉的點陣圖檔案必須是565格式,API才能正常工作。被檢測出來的人臉需要一個置信測度(confidence measure),這個措施定義在android.media.FaceDetector.Face.CONFIDENCE_THRESHOLD。

最重要的方法實現在setFace(),它將FaceDetector物件例項化,同時呼叫findFaces,結果存放在faces裡,人臉的中點轉移到MyImageView。程式碼如下:

  1. publicclass TutorialOnFaceDetect1 extends Activity {
  2. private MyImageView mIV;
  3. private Bitmap mFaceBitmap;
  4. privateint mFaceWidth = 200;
  5. privateint mFaceHeight = 200;
  6. privatestaticfinalint MAX_FACES = 1;
  7. privatestatic String TAG = "TutorialOnFaceDetect";
  8. @Override
  9. publicvoid onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. mIV = new MyImageView(this);
  12. setContentView(mIV, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
  13. // load the photo
  14. Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.face3);
  15. mFaceBitmap = b.copy(Bitmap.Config.RGB_565, true);
  16. b.recycle();
  17. mFaceWidth = mFaceBitmap.getWidth();
  18. mFaceHeight = mFaceBitmap.getHeight();
  19. mIV.setImageBitmap(mFaceBitmap);
  20. // perform face detection and set the feature points setFace();
  21. mIV.invalidate();
  22. }
  23. publicvoid setFace() {
  24. FaceDetector fd;
  25. FaceDetector.Face [] faces = new FaceDetector.Face[MAX_FACES];
  26. PointF midpoint = new PointF();
  27. int [] fpx = null;
  28. int [] fpy = null;
  29. int count = 0;
  30. try {
  31. fd = new FaceDetector(mFaceWidth, mFaceHeight, MAX_FACES);
  32. count = fd.findFaces(mFaceBitmap, faces);
  33. catch (Exception e) {
  34. Log.e(TAG, "setFace(): " + e.toString());
  35. return;
  36. }
  37. // check if we detect any faces
  38. if (count > 0) {
  39. fpx = newint[count];
  40. fpy = newint[count];
  41. for (int i = 0; i < count; i++) {
  42. try {
  43. faces[i].getMidPoint(midpoint);
  44. fpx[i] = (int)midpoint.x;
  45. fpy[i] = (int)midpoint.y;
  46. catch (Exception e) {
  47. Log.e(TAG, "setFace(): face " + i + ": " + e.toString());
  48. }
  49. }
  50. }
  51. mIV.setDisplayPoints(fpx, fpy, count, 0);
  52. }
  53. }

接下來的程式碼中,我們在MyImageView中新增setDisplayPoints() ,用來在被檢測出的人臉上標記渲染。圖1展示了一個標記在被檢測處的人臉上處於中心位置。

  1. // set up detected face features for display
  2. publicvoid setDisplayPoints(int [] xx, int [] yy, int total, int style) {
  3. mDisplayStyle = style;
  4. mPX = null;
  5. mPY = null;
  6. if (xx != null && yy != null && total > 0) {
  7. mPX = newint[total];
  8. mPY = newint[total];
  9. for (int i = 0; i < total; i++) {
  10. mPX[i] = xx[i];
  11. mPY[i] = yy[i];
  12. }
  13. }
  14. }

圖1:單一人臉檢測

多人臉檢測

通過FaceDetector可以設定檢測到人臉數目的上限。比如設定最多隻檢測10張臉:

  1. privatestaticfinalint MAX_FACES = 10;

圖2展示檢測到多張人臉的情況。

圖2:多人人臉檢測

定位眼睛中心位置

Android人臉檢測返回其他有用的資訊,例同時會返回如eyesDistance,pose,以及confidence。我們可以通過eyesDistance來定位眼睛的中心位置。

下面的程式碼中,我們將setFace()放在doLengthyCalc()中。同時圖3展示了定位眼睛中心位置的效果。

  1. publicclass TutorialOnFaceDetect extends Activity {
  2. private MyImageView mIV;
  3. private Bitmap mFaceBitmap;
  4. privateint mFaceWidth = 200;
  5. privateint mFaceHeight = 200;
  6. privatestaticfinalint MAX_FACES = 10;
  7. privatestatic String TAG = "TutorialOnFaceDetect";
  8. privatestaticboolean DEBUG = false;
  9. protectedstaticfinalint GUIUPDATE_SETFACE = 999;
  10. protected Handler mHandler = new Handler(){
  11. // @Override
  12. publicvoid handleMessage(Message msg) {
  13. mIV.invalidate();
  14. super.handleMessage(msg);
  15. }
  16. };
  17. @Override
  18. publicvoid onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. mIV = new MyImageView(this);
  21. setContentView(mIV, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
  22. // load the photo
  23. Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.face3);
  24. mFaceBitmap = b.copy(Bitmap.Config.RGB_565, true);
  25. b.recycle();
  26. mFaceWidth = mFaceBitmap.getWidth();
  27. mFaceHeight = mFaceBitmap.getHeight();
  28. mIV.setImageBitmap(mFaceBitmap);
  29. mIV.invalidate();
  30. // perform face detection in setFace() in a background thread
  31. doLengthyCalc();
  32. }
  33. publicvoid setFace() {
  34. FaceDetector fd;
  35. FaceDetector.Face [] faces = new FaceDetector.Face[MAX_FACES];
  36. PointF eyescenter = new PointF();
  37. float eyesdist = 0.0f;
  38. int [] fpx = null;
  39. int [] fpy = null;
  40. int count = 0;
  41. try {
  42. fd = new FaceDetector(mFaceWidth, mFaceHeight, MAX_FACES);
  43. count = fd.findFaces(mFaceBitmap, faces);
  44. catch (Exception e) {
  45. Log.e(TAG, "setFace(): " + e.toString());
  46. return;
  47. }
  48. // check if we detect any faces
  49. if (count > 0) {
  50. fpx = newint[count * 2];
  51. fpy = newint[count * 2];
  52. for (int i = 0; i < count; i++) {
  53. try {
  54. faces[i].getMidPoint(eyescenter);
  55. eyesdist = faces[i].eyesDistance();
  56. // set up left eye location
  57. fpx[2 * i] = (int)(eyescenter.x - eyesdist / 2);
  58. fpy[2 * i] = (int)eyescenter.y;
  59. // set up right eye location
  60. fpx[2 * i + 1] = (int)(eyescenter.x + eyesdist / 2);
  61. fpy[2 * i + 1] = (int)eyescenter.y;
  62. if (DEBUG) {
  63. Log.e(TAG, "setFace(): face " + i + ": confidence = " + faces[i].confidence()
  64. ", eyes distance = " + faces[i].eyesDistance()
  65. ", pose = ("+ faces[i].pose(FaceDetector.Face.EULER_X) + ","
  66. + faces[i].pose(FaceDetector.Face.EULER_Y) + ","
  67. + faces[i].pose(FaceDetector.Face.EULER_Z) + ")"
  68. ", eyes midpoint = (" + eyescenter.x + "," + eyescenter.y +")");
  69. }
  70. catch (Exception e) {
  71. Log.e(TAG, "setFace(): face " + i + ": " + e.toString());
  72. }
  73. }
  74. }
  75. mIV.setDisplayPoints(fpx, fpy, count * 21);
  76. }
  77. privatevoid doLengthyCalc() {
  78. Thread t = new Thread() {
  79. Message m = new Message();
  80. publicvoid run() {
  81. try {
  82. setFace();
  83. m.what = TutorialOnFaceDetect.GUIUPDATE_SETFACE;
  84. TutorialOnFaceDetect.this.mHandler.sendMessage(m);
  85. catch (Exception e) {
  86. Log.e(TAG, "doLengthyCalc(): " + e.toString());
  87. }
  88. }
  89. };
  90. t.start();
  91. }
  92. }

圖3:定位眼睛中心位置

色彩 vs. 灰度

通常來講,人臉檢測成功取決於搜尋人臉高對比度區域,實際效果來看色彩和灰度的差距不會太遠。不過很多學者仍在致力於證明色彩比灰度更靠譜。經過在對示例圖片的驗證,發現Android APIs返回的結果非常接近,似乎APIs意圖忽略掉不同顏色通道的因素。請看圖4(BTW,獨自一人在陰暗環境下請謹慎觀看):

圖4:灰度人臉檢測看起來會稍微有點恐怖

總結

  介紹了簡單的Android人臉檢測APIs,並通過例項進行了演示。以上的軟體包均可在官網上下載,方便大家將其import到Eclipse中。最後提供一些有益的忠告:

  1. 很多應用對人臉檢測其實都有著潛在的重要需求,例如去紅眼、計算人頭數、自動對焦人臉、新增人臉特效等等。
  2. 這個世界上存在有非常多的人臉資料庫,有意者請點選此處
  3. 在實時的人臉檢測過程中,Android的表現的會有一點點差強人意。

相關文章