編輯:關於Android編程
照相時,在預覽畫面上提示用戶人臉的位置,並完成自動對焦等,是個錯的應用; 下面是實現細節
我們知道在android的代碼中已有人臉識別的底層算法代碼,而且在framework層也封了調用的API函數
extern/neven 目錄下是實現人臉識別的算法代碼。
添加獲取照相時預覽圖片數據,可以在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布局中添加
<SurfaceView android:id="@+id/camera_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!-- Harrison add finding faces Rectangle-->
<com.android.camera.FindFaceView
android:id="@+id/faces_rectangle"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
<!-- end add 20121119 -->
我的畫提示框代碼:
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);
}
}
遇到一個問題,在預覽時頻繁的全屏普通切換,容易粗出現識別庫無效。請高手指點指點。
一般情況下,如果想要在ListView上面實現Listitem的滑動刪除效果,或者仿QQ的滑動顯示刪除效果的時候,只需要繼承ListView,自定義一個ListView就
應該說沒有幾個從事android開發的人員能敢說自己完全掌握了ListView。本篇文章比較全面的總結了lisiview中容易被忽略但有用的基礎知識點(不包括如何在異步加
本人比較懶,不說廢話,直接貼代碼,代碼後附有完整項目package test.com.opengles5_3;import android.opengl.GLES20;i
QQ或者微信出現過滑動,最近聯系人列表,可以刪去當前選中的聯系人,這個功能很棒。就是試著做了下。其實是使用了開源框架SwipeListView 。 SwipeL