Android如何从图片中切取人脸区域?
2016-04-12 16:30
477 查看
从一张图片中切出人脸区域是App开发中常用的场景,譬如,现在很多App用户上传头像的时候,喜欢随手自拍。自拍的图片往往在尺寸、位置上并不完美。而App需要在各种千奇百怪的UI场景下显示用户的头像。所以从原始头像图片中切取出人脸区域看起来是个刚需。这里介绍如何应用Android提供的人脸识别接口完成简单的切取人脸区域。
简单起见,输入为一个可能人脸的Bitmap,并且假定目标是识别出一个人脸而已。输出为一个以人脸为中心的原图的部分切图。如果需要自定义长宽比例,或者只需要定位人脸位置,来半侧切图,可以稍加改动实现。
Android官方提供的人脸识别api在android.media.FaceDetector,核心的api是
FaceDetector.findFaces()
主要的坑有两个:
(1)只能处理RGB_565格式的Bitmap,需要做转换。
(2)只能处理width为偶数的Bitmap,这里的解法是如果是奇数,则放弃最右侧的一列像素。
直接贴代码:
简单起见,输入为一个可能人脸的Bitmap,并且假定目标是识别出一个人脸而已。输出为一个以人脸为中心的原图的部分切图。如果需要自定义长宽比例,或者只需要定位人脸位置,来半侧切图,可以稍加改动实现。
Android官方提供的人脸识别api在android.media.FaceDetector,核心的api是
FaceDetector.findFaces()
主要的坑有两个:
(1)只能处理RGB_565格式的Bitmap,需要做转换。
(2)只能处理width为偶数的Bitmap,这里的解法是如果是奇数,则放弃最右侧的一列像素。
直接贴代码:
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; } }
相关文章推荐
- Android 代码中使用字符串方法。
- Android动画
- android(NDK+JNI)---jni开发资料
- android开发框架(五)AIDL进程间通信机制
- Android样式的开发:shape篇
- Android proguard 详解
- Android Studio多渠道打包
- Android应用中使用XmlSerializer序列化XML数据的教程
- android(NDK+JNI)---NDK编译生成so文件
- Android进程调度cgroups的简单介绍
- Android:控件Spinner实现下拉列表
- Android 中的 Service 全面总结
- Android应用市场(发布APK)及多渠道打包
- android(NDK+JNI)---NDK入门学习
- 《Android源码设计模式》读书笔记 (22) 第22章 外观模式
- 搭建android开发环境
- android:exported
- Android——SQLite数据库(一)创建数据库、创建表、初始化数据
- android6.0 adbd深入分析(一)adb驱动初始化、读取adb节点线程
- Android 源码集合