Android API教程:人臉檢測(cè)(上)
圖片來源:Wikipedia
所謂人臉檢測(cè)就是指從一副圖片或者一幀視頻中標(biāo)定出所有人臉的位置和尺寸。人臉檢測(cè)是人臉識(shí)別系統(tǒng)中的一個(gè)重要環(huán)節(jié),也可以獨(dú)立應(yīng)用于視頻監(jiān)控。在數(shù)字媒體日益普及的今天,利用人臉檢測(cè)技術(shù)還可以幫助我們從海量圖片數(shù)據(jù)中快速篩選出包含人臉的圖片。 在目前的數(shù)碼相機(jī)中,人臉檢測(cè)可以用來完成自動(dòng)對(duì)焦,即“臉部對(duì)焦”。“臉部對(duì)焦”是在自動(dòng)曝光和自動(dòng)對(duì)焦發(fā)明后,二十年來最重要的一次攝影技術(shù)革新。家用數(shù)碼相機(jī),占絕大多數(shù)的照片是以人為拍攝主體的,這就要求相機(jī)的自動(dòng)曝光和對(duì)焦以人物為基準(zhǔn)。
via cdstm.cn
構(gòu)建一個(gè)人臉檢測(cè)的Android Activity
你可以構(gòu)建一個(gè)通用的Android Activity,我們擴(kuò)展了基類ImageView,成為MyImageView,而我們需要進(jìn)行檢測(cè)的包含人臉的位圖文件必須是565格式,API才能正常工作。被檢測(cè)出來的人臉需要一個(gè)置信測(cè)度(confidence measure),這個(gè)措施定義在android.media.FaceDetector.Face.CONFIDENCE_THRESHOLD。
最重要的方法實(shí)現(xiàn)在setFace(),它將FaceDetector對(duì)象實(shí)例化,同時(shí)調(diào)用findFaces,結(jié)果存放在faces里,人臉的中點(diǎn)轉(zhuǎn)移到MyImageView。代碼如下:
- public class TutorialOnFaceDetect1 extends Activity {
- private MyImageView mIV;
- private Bitmap mFaceBitmap;
- private int mFaceWidth = 200;
- private int mFaceHeight = 200;
- private static final int MAX_FACES = 1;
- private static String TAG = "TutorialOnFaceDetect";
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mIV = new MyImageView(this);
- setContentView(mIV, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
- // load the photo
- Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.face3);
- mFaceBitmap = b.copy(Bitmap.Config.RGB_565, true);
- b.recycle();
- mFaceWidth = mFaceBitmap.getWidth();
- mFaceHeight = mFaceBitmap.getHeight();
- mIV.setImageBitmap(mFaceBitmap);
- // perform face detection and set the feature points setFace();
- mIV.invalidate();
- }
- public void setFace() {
- FaceDetector fd;
- FaceDetector.Face [] faces = new FaceDetector.Face[MAX_FACES];
- PointF midpoint = new PointF();
- int [] fpx = null;
- int [] fpy = null;
- int count = 0;
- try {
- fd = new FaceDetector(mFaceWidth, mFaceHeight, MAX_FACES);
- count = fd.findFaces(mFaceBitmap, faces);
- } catch (Exception e) {
- Log.e(TAG, "setFace(): " + e.toString());
- return;
- }
- // check if we detect any faces
- if (count > 0) {
- fpx = new int[count];
- fpy = new int[count];
- for (int i = 0; i < count; i++) {
- try {
- faces[i].getMidPoint(midpoint);
- fpx[i] = (int)midpoint.x;
- fpy[i] = (int)midpoint.y;
- } catch (Exception e) {
- Log.e(TAG, "setFace(): face " + i + ": " + e.toString());
- }
- }
- }
- mIV.setDisplayPoints(fpx, fpy, count, 0);
- }
- }
接下來的代碼中,我們?cè)贛yImageView中添加setDisplayPoints() ,用來在被檢測(cè)出的人臉上標(biāo)記渲染。圖1展示了一個(gè)標(biāo)記在被檢測(cè)處的人臉上處于中心位置。
- // set up detected face features for display
- public void setDisplayPoints(int [] xx, int [] yy, int total, int style) {
- mDisplayStyle = style;
- mPX = null;
- mPY = null;
- if (xx != null && yy != null && total > 0) {
- mPX = new int[total];
- mPY = new int[total];
- for (int i = 0; i < total; i++) {
- mPX[i] = xx[i];
- mPY[i] = yy[i];
- }
- }
- }

圖1:?jiǎn)我蝗四槞z測(cè)
多人臉檢測(cè)
通過FaceDetector可以設(shè)定檢測(cè)到人臉數(shù)目的上限。比如設(shè)置最多只檢測(cè)10張臉:
- private static final int MAX_FACES = 10;
圖2展示檢測(cè)到多張人臉的情況。

圖2:多人人臉檢測(cè)
定位眼睛中心位置
Android人臉檢測(cè)返回其他有用的信息,例同時(shí)會(huì)返回如eyesDistance,pose,以及confidence。我們可以通過eyesDistance來定位眼睛的中心位置。
下面的代碼中,我們將setFace()放在doLengthyCalc()中。同時(shí)圖3展示了定位眼睛中心位置的效果。
- public class TutorialOnFaceDetect extends Activity {
- private MyImageView mIV;
- private Bitmap mFaceBitmap;
- private int mFaceWidth = 200;
- private int mFaceHeight = 200;
- private static final int MAX_FACES = 10;
- private static String TAG = "TutorialOnFaceDetect";
- private static boolean DEBUG = false;
- protected static final int GUIUPDATE_SETFACE = 999;
- protected Handler mHandler = new Handler(){
- // @Override
- public void handleMessage(Message msg) {
- mIV.invalidate();
- super.handleMessage(msg);
- }
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mIV = new MyImageView(this);
- setContentView(mIV, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
- // load the photo
- Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.face3);
- mFaceBitmap = b.copy(Bitmap.Config.RGB_565, true);
- b.recycle();
- mFaceWidth = mFaceBitmap.getWidth();
- mFaceHeight = mFaceBitmap.getHeight();
- mIV.setImageBitmap(mFaceBitmap);
- mIV.invalidate();
- // perform face detection in setFace() in a background thread
- doLengthyCalc();
- }
- public void setFace() {
- FaceDetector fd;
- FaceDetector.Face [] faces = new FaceDetector.Face[MAX_FACES];
- PointF eyescenter = new PointF();
- float eyesdist = 0.0f;
- int [] fpx = null;
- int [] fpy = null;
- int count = 0;
- try {
- fd = new FaceDetector(mFaceWidth, mFaceHeight, MAX_FACES);
- count = fd.findFaces(mFaceBitmap, faces);
- } catch (Exception e) {
- Log.e(TAG, "setFace(): " + e.toString());
- return;
- }
- // check if we detect any faces
- if (count > 0) {
- fpx = new int[count * 2];
- fpy = new int[count * 2];
- for (int i = 0; i < count; i++) {
- try {
- faces[i].getMidPoint(eyescenter);
- eyesdist = faces[i].eyesDistance();
- // set up left eye location
- fpx[2 * i] = (int)(eyescenter.x - eyesdist / 2);
- fpy[2 * i] = (int)eyescenter.y;
- // set up right eye location
- fpx[2 * i + 1] = (int)(eyescenter.x + eyesdist / 2);
- fpy[2 * i + 1] = (int)eyescenter.y;
- if (DEBUG) {
- Log.e(TAG, "setFace(): face " + i + ": confidence = " + faces[i].confidence()
- + ", eyes distance = " + faces[i].eyesDistance()
- + ", pose = ("+ faces[i].pose(FaceDetector.Face.EULER_X) + ","
- + faces[i].pose(FaceDetector.Face.EULER_Y) + ","
- + faces[i].pose(FaceDetector.Face.EULER_Z) + ")"
- + ", eyes midpoint = (" + eyescenter.x + "," + eyescenter.y +")");
- }
- } catch (Exception e) {
- Log.e(TAG, "setFace(): face " + i + ": " + e.toString());
- }
- }
- }
- mIV.setDisplayPoints(fpx, fpy, count * 2, 1);
- }
- private void doLengthyCalc() {
- Thread t = new Thread() {
- Message m = new Message();
- public void run() {
- try {
- setFace();
- m.what = TutorialOnFaceDetect.GUIUPDATE_SETFACE;
- TutorialOnFaceDetect.this.mHandler.sendMessage(m);
- } catch (Exception e) {
- Log.e(TAG, "doLengthyCalc(): " + e.toString());
- }
- }
- };
- t.start();
- }
- }

圖3:定位眼睛中心位置
【編輯推薦】