Android实现人脸识别技术_Android如何从图片中切取人脸区域?
2020-06-17 15:31:12  By: shinyuu

从一张图片中切出人脸区域是App开发中常用的场景,譬如,现在很多App用户上传头像的时候,喜欢随手自拍。自拍的图片往往在尺寸、位置上并不完美。而App需要在各种千奇百怪的UI场景下显示用户的头像。所以从原始头像图片中切取出人脸区域看起来是个刚需。这里介绍如何应用Android提供的人脸识别接口完成简单的切取人脸区域。


简单起见,输入为一个可能人脸的Bitmap,并且假定目标是识别出一个人脸而已。输出为一个以人脸为中心的原图的部分切图。如果需要自定义长宽比例,或者只需要定位人脸位置,来半侧切图,可以稍加改动实现。


Android官方提供的人脸识别api在android.media.FaceDetector,核心的api是FaceDetector.findFaces()


直接贴代码

package com.example.testface;
 
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.media.FaceDetector;
import android.util.Log;
 
public final class FaceHelper {
 
    private static final String LOG_TAG = "FaceHelper";
    private static final boolean DEBUG_ENABLE = false;
 
    public static Bitmap genFaceBitmap(Bitmap sourceBitmap) {
 
        if (!checkBitmap(sourceBitmap, "genFaceBitmap()")) {
            return null;
        }
 
        // default algorithm of face detecting of Android can only handle
        // RGB_565 bitmap, so copy it by RGB_565 here.
        Bitmap cacheBitmap = sourceBitmap.copy(Bitmap.Config.RGB_565, false);
 
        // gives up strong reference here. because this method may be
        // time-consuming, so maybe run in work thread usually, we give up the
        // strong reference of the source bitmap after it copied, so that it can
        // be recycled and GC in another thread.
        sourceBitmap = null;
 
        if (DEBUG_ENABLE) {
            Log.i(LOG_TAG,
                    "genFaceBitmap() : source bitmap width - "
                              cacheBitmap.getWidth()   " , height - "
                              cacheBitmap.getHeight());
        }
 
        int cacheWidth = cacheBitmap.getWidth();
        int cacheHeight = cacheBitmap.getHeight();
 
        // default algorithm of face detecting of Android can only handle the
        // bitmap that width is even, so we give up the 1 pixel from right if
        // not even.
        if (cacheWidth % 2 != 0) {
            if (0 == cacheWidth - 1) {
                if (DEBUG_ENABLE) {
                    Log.e(LOG_TAG,
                            "genFaceBitmap() : source bitmap width is only 1 , return null.");
                }
                return null;
            }
            final Bitmap localCacheBitmap = Bitmap.createBitmap(cacheBitmap, 0,
                    0, cacheWidth - 1, cacheHeight);
            cacheBitmap.recycle();
            cacheBitmap = localCacheBitmap;
            --cacheWidth;
 
            if (DEBUG_ENABLE) {
                Log.i(LOG_TAG,
                        "genFaceBitmap() : source bitmap width - "
                                  cacheBitmap.getWidth()   " , height - "
                                  cacheBitmap.getHeight());
            }
        }
 
        final FaceDetector.Face[] faces = new FaceDetector.Face[1];
        final int facefound = new FaceDetector(cacheWidth, cacheHeight, 1)
                .findFaces(cacheBitmap, faces);
        if (DEBUG_ENABLE) {
            Log.i(LOG_TAG, "genFaceBitmap() : facefound - "   facefound);
        }
        if (0 == facefound) {
            if (DEBUG_ENABLE) {
                Log.e(LOG_TAG, "genFaceBitmap() : no face found , return null.");
            }
            return null;
        }
 
        final PointF p = new PointF();
        faces[0].getMidPoint(p);
        if (DEBUG_ENABLE) {
            Log.i(LOG_TAG,
                    "getFaceBitmap() : confidence - "   faces[0].confidence());
            Log.i(LOG_TAG, "genFaceBitmap() : face center x - "   p.x
                      " , y - "   p.y);
        }
        final int faceX = (int) p.x;
        final int faceY = (int) p.y;
        if (DEBUG_ENABLE) {
            Log.i(LOG_TAG, "genFaceBitmap() : int faceX - "   faceX
                      " , int faceY - "   faceY);
        }
 
        // compute an area that face in middle of it.
        int startX, startY, width, height;
        if (faceX <= cacheWidth - faceX) {
            startX = 0;
            width = faceX * 2;
        } else {
            startX = faceX - (cacheWidth - faceX);
            width = (cacheWidth - faceX) * 2;
        }
        if (faceY <= cacheHeight - faceY) {
            startY = 0;
            height = faceY * 2;
        } else {
            startY = faceY - (cacheHeight - faceY);
            height = (cacheHeight - faceY) * 2;
        }
 
        final Bitmap result = Bitmap.createBitmap(cacheBitmap, startX, startY,
                width, height);
        cacheBitmap.recycle();
        return result;
    }
 
    private static boolean checkBitmap(final Bitmap bitmap,
            final String debugInfo) {
        if (null == bitmap || bitmap.isRecycled()) {
            if (DEBUG_ENABLE) {
                Log.e(LOG_TAG, debugInfo
                          " : check bitmap , is null or is recycled.");
            }
            return false;
        }
        if (0 == bitmap.getWidth() || 0 == bitmap.getHeight()) {
            if (DEBUG_ENABLE) {
                Log.e(LOG_TAG, debugInfo
                          " : check bitmap , width is 0 or height is 0.");
            }
            return false;
        }
        return true;
    }
 
}


主要的坑有两个:

(1)只能处理RGB_565格式的Bitmap,需要做转换。

(2)只能处理width为偶数的Bitmap,这里的解法是如果是奇数,则放弃最右侧的一列像素。

这样就可以一个一个把人脸裁剪出来,以便获取人脸的图片,从而用其获取单人脸的特征。


猜你喜欢的

快速评论


技术评论

  • 该技术还没有评论、赶快抢沙发吧...
DD记账
top
+