Android API教程:人脸检测(上)

通过两个主要的API,Android提供了一个直接在位图上进行脸部检测的方法,这两个API分别是 android.media.FaceDetector和android.media.FaceDetector.Face,已经包含在Android官方API中。本教程来自Developer网站,向大家介绍了这些API,同时提供教程中实例代码下载

图片来源:Wikipedia

所谓人脸检测就是指从一副图片或者一帧视频中标定出所有人脸的位置和尺寸。人脸检测是人脸识别系统中的一个重要环节,也可以独立应用于视频监控。在数字媒体日益普及的今天,利用人脸检测技术还可以帮助我们从海量图片数据中快速筛选出包含人脸的图片。 在目前的数码相机中,人脸检测可以用来完成自动对焦,即“脸部对焦”。“脸部对焦”是在自动曝光和自动对焦发明后,二十年来最重要的一次摄影技术革新。家用数码相机,占绝大多数的照片是以人为拍摄主体的,这就要求相机的自动曝光和对焦以人物为基准。
via cdstm.cn

构建一个人脸检测的Android Activity

你可以构建一个通用的Android Activity,我们扩展了基类ImageView,成为MyImageView,而我们需要进行检测的包含人脸的位图文件必须是565格式,API才能正常工作。被检测出来的人脸需要一个置信测度(confidence measure),这个措施定义在android.media.FaceDetector.Face.CONFIDENCE_THRESHOLD。

最重要的方法实现在setFace(),它将FaceDetector对象实例化,同时调用findFaces,结果存放在faces里,人脸的中点转移到MyImageView。代码如下:

 
  1. publicclassTutorialOnFaceDetect1extendsActivity{
  2. privateMyImageViewmIV;
  3. privateBitmapmFaceBitmap;
  4. privateintmFaceWidth=200;
  5. privateintmFaceHeight=200;
  6. privatestaticfinalintMAX_FACES=1;
  7. privatestaticStringTAG="TutorialOnFaceDetect";
  8. @Override
  9. publicvoidonCreate(BundlesavedInstanceState){
  10. super.onCreate(savedInstanceState);
  11. mIV=newMyImageView(this);
  12. setContentView(mIV,newLayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
  13. //loadthephoto
  14. Bitmapb=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. //performfacedetectionandsetthefeaturepointssetFace();
  21. mIV.invalidate();
  22. }
  23. publicvoidsetFace(){
  24. FaceDetectorfd;
  25. FaceDetector.Face[]faces=newFaceDetector.Face[MAX_FACES];
  26. PointFmidpoint=newPointF();
  27. int[]fpx=null;
  28. int[]fpy=null;
  29. intcount=0;
  30. try{
  31. fd=newFaceDetector(mFaceWidth,mFaceHeight,MAX_FACES);
  32. count=fd.findFaces(mFaceBitmap,faces);
  33. }catch(Exceptione){
  34. Log.e(TAG,"setFace():"+e.toString());
  35. return;
  36. }
  37. //checkifwedetectanyfaces
  38. if(count>0){
  39. fpx=newint[count];
  40. fpy=newint[count];
  41. for(inti=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(Exceptione){
  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. //setupdetectedfacefeaturesfordisplay
  2. publicvoidsetDisplayPoints(int[]xx,int[]yy,inttotal,intstyle){
  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(inti=0;i<total;i++){
  10. mPX[i]=xx[i];
  11. mPY[i]=yy[i];
  12. }
  13. }
  14. }

图1:单一人脸检测

多人脸检测

通过FaceDetector可以设定检测到人脸数目的上限。比如设置最多只检测10张脸:

 
  1. privatestaticfinalintMAX_FACES=10;

图2展示检测到多张人脸的情况。

图2:多人人脸检测

定位眼睛中心位置

Android人脸检测返回其他有用的信息,例同时会返回如eyesDistance,pose,以及confidence。我们可以通过eyesDistance来定位眼睛的中心位置。

下面的代码中,我们将setFace()放在doLengthyCalc()中。同时图3展示了定位眼睛中心位置的效果。

 
  1. publicclassTutorialOnFaceDetectextendsActivity{
  2. privateMyImageViewmIV;
  3. privateBitmapmFaceBitmap;
  4. privateintmFaceWidth=200;
  5. privateintmFaceHeight=200;
  6. privatestaticfinalintMAX_FACES=10;
  7. privatestaticStringTAG="TutorialOnFaceDetect";
  8. privatestaticbooleanDEBUG=false;
  9. protectedstaticfinalintGUIUPDATE_SETFACE=999;
  10. protectedHandlermHandler=newHandler(){
  11. //@Override
  12. publicvoidhandleMessage(Messagemsg){
  13. mIV.invalidate();
  14. super.handleMessage(msg);
  15. }
  16. };
  17. @Override
  18. publicvoidonCreate(BundlesavedInstanceState){
  19. super.onCreate(savedInstanceState);
  20. mIV=newMyImageView(this);
  21. setContentView(mIV,newLayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT));
  22. //loadthephoto
  23. Bitmapb=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. //performfacedetectioninsetFace()inabackgroundthread
  31. doLengthyCalc();
  32. }
  33. publicvoidsetFace(){
  34. FaceDetectorfd;
  35. FaceDetector.Face[]faces=newFaceDetector.Face[MAX_FACES];
  36. PointFeyescenter=newPointF();
  37. floateyesdist=0.0f;
  38. int[]fpx=null;
  39. int[]fpy=null;
  40. intcount=0;
  41. try{
  42. fd=newFaceDetector(mFaceWidth,mFaceHeight,MAX_FACES);
  43. count=fd.findFaces(mFaceBitmap,faces);
  44. }catch(Exceptione){
  45. Log.e(TAG,"setFace():"+e.toString());
  46. return;
  47. }
  48. //checkifwedetectanyfaces
  49. if(count>0){
  50. fpx=newint[count*2];
  51. fpy=newint[count*2];
  52. for(inti=0;i<count;i++){
  53. try{
  54. faces[i].getMidPoint(eyescenter);
  55. eyesdist=faces[i].eyesDistance();
  56. //setuplefteyelocation
  57. fpx[2*i]=(int)(eyescenter.x-eyesdist/2);
  58. fpy[2*i]=(int)eyescenter.y;
  59. //setuprighteyelocation
  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. +",eyesdistance="+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. +",eyesmidpoint=("+eyescenter.x+","+eyescenter.y+")");
  69. }
  70. }catch(Exceptione){
  71. Log.e(TAG,"setFace():face"+i+":"+e.toString());
  72. }
  73. }
  74. }
  75. mIV.setDisplayPoints(fpx,fpy,count*2,1);
  76. }
  77. privatevoiddoLengthyCalc(){
  78. Threadt=newThread(){
  79. Messagem=newMessage();
  80. publicvoidrun(){
  81. try{
  82. setFace();
  83. m.what=TutorialOnFaceDetect.GUIUPDATE_SETFACE;
  84. TutorialOnFaceDetect.this.mHandler.sendMessage(m);
  85. }catch(Exceptione){
  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的表现的会有一点点差强人意。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值