/* * Copyright (C) 2014 lafosca Studio, SL * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.marshalchen.common.uimodule.facecropper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.PointF; import android.media.FaceDetector; import com.marshalchen.common.commonUtils.logUtils.Logs; /** * An utility that crops faces from bitmaps. * It support multiple faces (max 8 by default) and crop them all, fitted in the same image. */ public class FaceCropper { private static final String LOG_TAG = FaceCropper.class.getSimpleName(); public enum SizeMode {FaceMarginPx, EyeDistanceFactorMargin} ; private static final int MAX_FACES = 8; private static final int MIN_FACE_SIZE = 200; private int mFaceMinSize = MIN_FACE_SIZE; private int mFaceMarginPx = 100; private float mEyeDistanceFactorMargin = 2f; private int mMaxFaces = MAX_FACES; private SizeMode mSizeMode = SizeMode.EyeDistanceFactorMargin; public FaceCropper() { } public FaceCropper(int faceMarginPx) { setFaceMarginPx(faceMarginPx); } public FaceCropper(float eyesDistanceFactorMargin) { setEyeDistanceFactorMargin(eyesDistanceFactorMargin); } public int getMaxFaces() { return mMaxFaces; } public void setMaxFaces(int maxFaces) { this.mMaxFaces = maxFaces; } public int getFaceMinSize() { return mFaceMinSize; } public void setFaceMinSize(int faceMinSize) { mFaceMinSize = faceMinSize; } public int getFaceMarginPx() { return mFaceMarginPx; } public void setFaceMarginPx(int faceMarginPx) { mFaceMarginPx = faceMarginPx; mSizeMode = SizeMode.FaceMarginPx; } public SizeMode getSizeMode() { return mSizeMode; } public float getEyeDistanceFactorMargin() { return mEyeDistanceFactorMargin; } public void setEyeDistanceFactorMargin(float eyeDistanceFactorMargin) { mEyeDistanceFactorMargin = eyeDistanceFactorMargin; mSizeMode = SizeMode.EyeDistanceFactorMargin; } public Bitmap cropFace(Context ctx, int resDrawable) { // Set internal configuration to RGB_565 BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565; return cropFace(BitmapFactory.decodeResource(ctx.getResources(), resDrawable, bitmapOptions)); } public Bitmap cropFace(Bitmap original) { Bitmap fixedBitmap = BitmapUtils.forceEvenBitmapSize(original); fixedBitmap = BitmapUtils.forceConfig565(fixedBitmap); FaceDetector faceDetector = new FaceDetector( fixedBitmap.getWidth(), fixedBitmap.getHeight(), mMaxFaces); FaceDetector.Face[] faces = new FaceDetector.Face[mMaxFaces]; // The bitmap must be in 565 format (for now). int faceCount = faceDetector.findFaces(fixedBitmap, faces); Logs.d("face crop"); if (faceCount == 0) { return fixedBitmap; } int initX = fixedBitmap.getWidth(); int initY = fixedBitmap.getHeight(); int endX = 0; int endY = 0; PointF centerFace = new PointF(); // Calculates minimum box to fit all detected faces for (int i = 0; i < faceCount; i++) { FaceDetector.Face face = faces[i]; // Eyes distance * 3 usually fits an entire face int faceSize = (int) (face.eyesDistance() * 3); if (SizeMode.FaceMarginPx.equals(mSizeMode)) { faceSize += mFaceMarginPx * 2; // *2 for top and down/right and left effect } else if (SizeMode.EyeDistanceFactorMargin.equals(mSizeMode)) { faceSize += face.eyesDistance() * mEyeDistanceFactorMargin; } faceSize = Math.max(faceSize, mFaceMinSize); face.getMidPoint(centerFace); int tInitX = (int) (centerFace.x - faceSize / 2); int tInitY = (int) (centerFace.y - faceSize / 2); tInitX = Math.max(0, tInitX); tInitY = Math.max(0, tInitY); int tEndX = tInitX + faceSize; int tEndY = tInitY + faceSize; tEndX = Math.min(tEndX, fixedBitmap.getWidth()); tEndY = Math.min(tEndY, fixedBitmap.getHeight()); initX = Math.min(initX, tInitX); initY = Math.min(initY, tInitY); endX = Math.max(endX, tEndX); endY = Math.max(endY, tEndY); } int sizeX = endX - initX; int sizeY = endY - initY; if (sizeX + initX > fixedBitmap.getWidth()) { sizeX = fixedBitmap.getWidth() - initX; } if (sizeY + initY > fixedBitmap.getHeight()) { sizeY = fixedBitmap.getHeight() - initY; } Bitmap croppedBitmap = Bitmap.createBitmap(fixedBitmap, initX, initY, sizeX, sizeY); if (fixedBitmap != croppedBitmap) { fixedBitmap.recycle(); } return croppedBitmap; } }