照相时,在预览画面上提示用户人脸的位置,并完成自动对焦等,是个错的应用; 下面是实现细节

我们知道在android的代码中已有人脸识别的底层算法代码,而且在framework层也封了调用的API函数

  1.      extern/neven 目录下是实现人脸识别的算法代码。
  2. 添加获取照相时预览图片数据,可以在onPreviewFrame回调函数中得。在开始预览的地方,用mCameraDevice.setPreviewCallback(mPreviewCallback);设置预览回调函数。

import android.media.FaceDetector;
import android.media.FaceDetector.Face;

 

 //Harrison add
 private void DrawRectOnFace() {
  if (numberOfFaceDetected != 0) {
   Face mFace1 = mFace[0];
   PointF midPoint = new PointF();
   mFace1.getMidPoint(midPoint);
     if ((Math.abs(mPreMidPoint.x-midPoint.x) < 50) && (Math.abs(mPreMidPoint.y-midPoint.y) < 50)) {
    Log.i("Harrison", "not draw Rect .");
    return ;
   }
   mPreMidPoint.x = midPoint.x;
   mPreMidPoint.y = midPoint.y;
   mFindFaceView.setVisibility(View.VISIBLE);
  } else {
   mPreMidPoint.x = 0;
   mPreMidPoint.y = 0;
   mFindFaceView.clearDraw();
   mFindFaceView.setVisibility(View.GONE);
   return;
  }
  mFindFaceView.drawRects(mFace, numberOfFaceDetected);
 }

 

//调用API找人脸,需要import进软件包哦!

 private void FindFacesInBitmap(Bitmap myBitmap) {
  imageWidth = myBitmap.getWidth();
  imageHeight = myBitmap.getHeight();
  Log.i("Harrison", "imageWidth="+imageWidth+",  imageHeight="+imageHeight);
  mFace = new FaceDetector.Face[numberOfFace];
  mFaceDetect = new FaceDetector(imageWidth, imageHeight, numberOfFace);
  numberOfFaceDetected = mFaceDetect.findFaces(myBitmap, mFace);
  Log.i("Harrison", "numberOfFaceDetected="+numberOfFaceDetected);
 }


 private Bitmap rotateMyBitmap(Bitmap bitmap) {

  int width = bitmap.getWidth(); 
  int height = bitmap.getHeight();  

  Matrix matrix = new Matrix(); 
  matrix.postRotate(90); //椤烘椂閽熸棆杞?0搴︺€?

  Bitmap rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
 
  return rotateBitmap;
 }

 private Bitmap scaleMyBitmap(Bitmap bitmap) {

  int width = bitmap.getWidth(); 
  int height = bitmap.getHeight();  
 
  int nWidth = mFindFaceView.getFaceViewWidth();; 
  int nHeight = mFindFaceView.getFaceViewHeight();  
 // Log.i("Harrison", "nWidth="+nWidth+",  nHeight"+nHeight);
   
  float scaleWidth = ((float) nWidth)/width;  
  float scaleHeight = ((float)nHeight)/height; 
  
  Matrix matrix = new Matrix(); 
  matrix.postScale(scaleWidth, scaleHeight); 
 
  Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
  
  return resizedBitmap;
 }

//处理图片格式,一般摄像头抓到的数据为ImageFormat.NV21,不同的格式,需要调整的。

 private void decodeToBitMap(byte[] data, android.hardware.Camera _camera) {
  mCameraDevice.setPreviewCallback(null);
  Size size = mCameraDevice.getParameters().getPreviewSize();
  //FileOutputStream outStream = null;
  try {
   YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null);

   if (image != null) {
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    image.compressToJpeg(new Rect(0, 0, size.width, size.height), 80, stream);

  //  outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
  //  outStream.write(stream.toByteArray());
  //  outStream.close();
  //  Log.i("Harrison", "write file to sdcard.");

 

//在我的手机上有两种预览模式,发现全屏模式时需要调整图片的大小才能正确定位。

    Bitmap myBitmap=BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
     stream.close();
    if (mPreviewLayoutProxy.getFullScreenMode()) { // fullscreen mode 
     Bitmap tmpScaleBmp=null;
     Bitmap tmpRotateBmp=null;

//手机是竖屏横排是与其别的哦
     if (((mOrientation/90) == 0) || ((mOrientation/90) == 2)) { 
      tmpRotateBmp = rotateMyBitmap(myBitmap);
      tmpScaleBmp = scaleMyBitmap(tmpRotateBmp);
      FindFacesInBitmap(tmpScaleBmp);
      if (tmpRotateBmp != null) {
       tmpRotateBmp.recycle();
       tmpRotateBmp = null;
      }
     } else {
      FindFacesInBitmap(scaleMyBitmap(myBitmap));
     }
     if (tmpScaleBmp != null) {
      tmpScaleBmp.recycle();
      tmpScaleBmp = null;
     }
    } else { //normal mode
     FindFacesInBitmap(myBitmap);
    }
    DrawRectOnFace();
    if (myBitmap != null) {
     myBitmap.recycle();
     myBitmap = null;
    }
   }
  } catch (Exception ex) {
   Log.e("Sys", "Error:" + ex.getMessage());
  } 
  mCameraDevice.setPreviewCallback(mPreviewCallback);
 }
 
 private  final class PostPreviewCallback implements PreviewCallback {
  @Override
  public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {

     decodeToBitMap(data, camera);
   }
 }

 

我们知道,相机预览是用 SurfaceView来显示图片的;在我们画提示框时,不能直接用那个view的,会出现黑屏的状态,预览的画面也不流畅的。

添加一个一样大小的SurfaceView来提示。

在xml布局中添加

                      android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
   
        android:id="@+id/faces_rectangle" 
       android:layout_width="fill_parent" 
    android:layout_height="fill_parent" />
   

 

我的画提示框代码:

package com.android.camera;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.graphics.PixelFormat;
import android.graphics.PorterDuffXfermode;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.graphics.PointF;
import android.media.FaceDetector;
import android.media.FaceDetector.Face;

public class FindFaceView extends SurfaceView implements SurfaceHolder.Callback{

 protected SurfaceHolder sh;
 private  SurfaceHolder mCameraSh;
 private int mWidth;
 private int mHeight;
 private float mEyesDistance;

 public FindFaceView(Context context, AttributeSet attrs) {
  super(context, attrs);
  // TODO Auto-generated constructor stub
  sh = getHolder();
  sh.addCallback(this);
  sh.setFormat(PixelFormat.TRANSPARENT);
  setZOrderOnTop(true);
 }

 public void surfaceChanged(SurfaceHolder arg0, int arg1, int w, int h) {
  // TODO Auto-generated method stub
  mWidth = w;
  mHeight = h;
 }

 public void surfaceCreated(SurfaceHolder arg0) {
  // TODO Auto-generated method stub

 }

 public void surfaceDestroyed(SurfaceHolder arg0) {
  // TODO Auto-generated method stub

 }

 void setCameraPreviewSurfaceHolder(SurfaceHolder  sh) {
  mCameraSh = sh;
 }
 public int getFaceViewWidth() {
  return mWidth;
 }
 public int getFaceViewHeight() {
  return mHeight;
 }

 void clearDraw() {
  Canvas canvas = sh.lockCanvas();
  Paint clipPaint = new Paint();
  clipPaint.setAntiAlias(true);
  clipPaint.setStyle(Paint.Style.STROKE);
  clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
  canvas.drawPaint(clipPaint);
  sh.unlockCanvasAndPost(canvas);
 }

     public void drawRects(FaceDetector.Face[] mFace, int numberOfFaceDetected) {
  Canvas canvas = sh.lockCanvas();

  Paint clipPaint = new Paint();
  clipPaint.setAntiAlias(true);
  clipPaint.setStyle(Paint.Style.STROKE);
  clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
  canvas.drawPaint(clipPaint);
 
  canvas.drawColor(Color.TRANSPARENT);
  Paint p = new Paint();
  p.setAntiAlias(true);
  p.setColor(Color.RED);
  p.setStyle(Style.STROKE);

  for (int i = 0; i < numberOfFaceDetected; i++) {
 //  if (0 == i) {
 //   p.setColor(Color.WHITE);
 //  } else {
 //   p.setColor(Color.GRAY);
 //  }
   Face face = mFace[i];
   PointF myMidPoint = new PointF();
   face.getMidPoint(myMidPoint);
   mEyesDistance = face.eyesDistance();
   Log.i("Harrison", "i="+i+"("+myMidPoint.x+", "+myMidPoint.y+")");
   canvas.drawRect((int)(myMidPoint.x-mEyesDistance),
     (int)(myMidPoint.y-mEyesDistance),
     (int)(myMidPoint.x+mEyesDistance),
     (int)(myMidPoint.y+mEyesDistance),
     p);
  }
  sh.unlockCanvasAndPost(canvas);
  }

//测试两个View是否错移
 public void drawBitmap(Bitmap myBitmap) {
  Canvas canvas = sh.lockCanvas();
  canvas.drawBitmap(myBitmap, 0, 0, null);
  sh.unlockCanvasAndPost(canvas);
 // mImage.setImageBitmap(myBitmap);
 // mImage.invalidate();
 }
 public void doDraw() {
  Canvas canvas = sh.lockCanvas();
  canvas.drawColor(Color.TRANSPARENT);// 这里是绘制背景
  Paint p = new Paint(); // 笔触
  p.setAntiAlias(true); // 反锯齿
  p.setColor(Color.RED);
  p.setStyle(Style.STROKE);
  canvas.drawLine(mWidth/2 - 100, 0, mWidth/2 - 100, mHeight, p);
  canvas.drawLine(mWidth/2 + 100, 0, mWidth/2 + 100, mHeight, p);
  // ------------------------
  // 画边框---------------------
  Rect rec = canvas.getClipBounds();
  rec.bottom--;
  rec.right--;
  p.setColor(Color.GRAY);
  // 颜色
  p.setStrokeWidth(5);
  canvas.drawRect(rec, p);
  // 提交绘制
  sh.unlockCanvasAndPost(canvas);
 }
}

遇到一个问题,在预览时频繁的全屏普通切换,容易粗出现识别库无效。请高手指点指点。

 

 

 

更多相关文章

  1. C语言函数的递归(上)
  2. 如何在android平台上实现语音识别
  3. 在Ubuntu上下载、编译和安装Android源代码
  4. 拥抱Android(安卓)Studio:从ADT到Android(安卓)Studio
  5. 【iOS-cocos2d-X 游戏开发之十三】详细讲解在Xcode中利用预编译
  6. Android(安卓)demo--调用系统相机拍照并显示图片为黑白
  7. Android学习系列(15)--App列表之下拉刷新
  8. Launcher3 总结
  9. (二) RIL 层分析

随机推荐

  1. android webview ZoomButtonsController
  2. com.android.support:appcompat-v7:15.+.
  3. Android(安卓)读写XML文件(使用pull解析)
  4. Android创建和删除桌面快捷方式
  5. Android作为HTTP服务器--NanoHTTPD源码分
  6. google地图demo
  7. 如何把android设备中的固件dump出来
  8. Android(安卓)播放提示音
  9. 编程回忆之Android回忆(Android应用参数的
  10. Android中Data和String数据类型转换