diff --git a/.gitignore b/.gitignore
index da44912..0422061 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,4 @@ local.properties
# Signing
signing/
+build/
diff --git a/FaceCropper-library/build.gradle b/FaceCropper-library/build.gradle
index 0b1954b..1c3159b 100644
--- a/FaceCropper-library/build.gradle
+++ b/FaceCropper-library/build.gradle
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-apply plugin: 'android-library'
+apply plugin: 'com.android.library'
android {
compileSdkVersion 19
- buildToolsVersion "19.0.3"
+ buildToolsVersion "20.0.0"
defaultConfig {
minSdkVersion 8
targetSdkVersion 16
- versionCode 1
- versionName "1.0"
+ versionCode 2
+ versionName "1.1"
}
buildTypes {
release {
@@ -38,9 +38,11 @@ dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
+import com.android.builder.core.BuilderConstants
+
android.libraryVariants.all { variant ->
def name = variant.buildType.name
- if (name.equals(com.android.builder.BuilderConstants.DEBUG)) {
+ if (name.equals(BuilderConstants.DEBUG)) {
return; // Skip debug builds.
}
def task = project.tasks.create "jar${name.capitalize()}", Jar
diff --git a/FaceCropper-library/src/main/java/cat/lafosca/facecropper/FaceCropper.java b/FaceCropper-library/src/main/java/cat/lafosca/facecropper/FaceCropper.java
index 652808c..5eb6d48 100644
--- a/FaceCropper-library/src/main/java/cat/lafosca/facecropper/FaceCropper.java
+++ b/FaceCropper-library/src/main/java/cat/lafosca/facecropper/FaceCropper.java
@@ -19,7 +19,13 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.media.FaceDetector;
import android.util.Log;
@@ -41,15 +47,32 @@ public enum SizeMode { FaceMarginPx, EyeDistanceFactorMargin };
private float mEyeDistanceFactorMargin = 2f;
private int mMaxFaces = MAX_FACES;
private SizeMode mSizeMode = SizeMode.EyeDistanceFactorMargin;
+ private boolean mDebug;
+ private Paint mDebugPainter;
+ private Paint mDebugAreaPainter;
- public FaceCropper() { }
+ public FaceCropper() {
+ initPaints();
+ }
public FaceCropper(int faceMarginPx) {
setFaceMarginPx(faceMarginPx);
+ initPaints();
}
public FaceCropper(float eyesDistanceFactorMargin) {
setEyeDistanceFactorMargin(eyesDistanceFactorMargin);
+ initPaints();
+ }
+
+ private void initPaints() {
+ mDebugPainter = new Paint();
+ mDebugPainter.setColor(Color.RED);
+ mDebugPainter.setAlpha(80);
+
+ mDebugAreaPainter = new Paint();
+ mDebugAreaPainter.setColor(Color.GREEN);
+ mDebugAreaPainter.setAlpha(80);
}
public int getMaxFaces() {
@@ -90,48 +113,57 @@ public void setEyeDistanceFactorMargin(float 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;
+ public boolean isDebug() {
+ return mDebug;
+ }
- return cropFace(BitmapFactory.decodeResource(ctx.getResources(), resDrawable, bitmapOptions));
+ public void setDebug(boolean debug) {
+ mDebug = debug;
}
- public Bitmap cropFace(Bitmap original) {
+ protected CropResult cropFace(Bitmap original, boolean debug) {
Bitmap fixedBitmap = BitmapUtils.forceEvenBitmapSize(original);
fixedBitmap = BitmapUtils.forceConfig565(fixedBitmap);
+ Bitmap mutableBitmap = fixedBitmap.copy(Bitmap.Config.RGB_565, true);
+
+ if (fixedBitmap != mutableBitmap) {
+ fixedBitmap.recycle();
+ }
FaceDetector faceDetector = new FaceDetector(
- fixedBitmap.getWidth(), fixedBitmap.getHeight(),
+ mutableBitmap.getWidth(), mutableBitmap.getHeight(),
mMaxFaces);
FaceDetector.Face[] faces = new FaceDetector.Face[mMaxFaces];
// The bitmap must be in 565 format (for now).
- int faceCount = faceDetector.findFaces(fixedBitmap, faces);
+ int faceCount = faceDetector.findFaces(mutableBitmap, faces);
if (BuildConfig.DEBUG) {
Log.d(LOG_TAG, faceCount + " faces found");
}
if (faceCount == 0) {
- return fixedBitmap;
+ return new CropResult(mutableBitmap);
}
- int initX = fixedBitmap.getWidth();
- int initY = fixedBitmap.getHeight();
+ int initX = mutableBitmap.getWidth();
+ int initY = mutableBitmap.getHeight();
int endX = 0;
int endY = 0;
PointF centerFace = new PointF();
+ Canvas canvas = new Canvas(mutableBitmap);
+ canvas.drawBitmap(mutableBitmap, new Matrix(), null);
+
// 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
}
@@ -143,6 +175,11 @@ else if (SizeMode.EyeDistanceFactorMargin.equals(mSizeMode)) {
face.getMidPoint(centerFace);
+ if (debug) {
+ canvas.drawPoint(centerFace.x, centerFace.y, mDebugPainter);
+ canvas.drawCircle(centerFace.x, centerFace.y, face.eyesDistance() * 1.5f, mDebugPainter);
+ }
+
int tInitX = (int) (centerFace.x - faceSize / 2);
int tInitY = (int) (centerFace.y - faceSize / 2);
tInitX = Math.max(0, tInitX);
@@ -150,8 +187,8 @@ else if (SizeMode.EyeDistanceFactorMargin.equals(mSizeMode)) {
int tEndX = tInitX + faceSize;
int tEndY = tInitY + faceSize;
- tEndX = Math.min(tEndX, fixedBitmap.getWidth());
- tEndY = Math.min(tEndY, fixedBitmap.getHeight());
+ tEndX = Math.min(tEndX, mutableBitmap.getWidth());
+ tEndY = Math.min(tEndY, mutableBitmap.getHeight());
initX = Math.min(initX, tInitX);
initY = Math.min(initY, tInitY);
@@ -162,18 +199,101 @@ else if (SizeMode.EyeDistanceFactorMargin.equals(mSizeMode)) {
int sizeX = endX - initX;
int sizeY = endY - initY;
- if (sizeX + initX > fixedBitmap.getWidth()) {
- sizeX = fixedBitmap.getWidth() - initX;
+ if (sizeX + initX > mutableBitmap.getWidth()) {
+ sizeX = mutableBitmap.getWidth() - initX;
}
- if (sizeY + initY > fixedBitmap.getHeight()) {
- sizeY = fixedBitmap.getHeight() - initY;
+ if (sizeY + initY > mutableBitmap.getHeight()) {
+ sizeY = mutableBitmap.getHeight() - initY;
}
- Bitmap croppedBitmap = Bitmap.createBitmap(fixedBitmap, initX, initY, sizeX, sizeY);
- if (fixedBitmap != croppedBitmap) {
- fixedBitmap.recycle();
+ Point init = new Point(initX, initY);
+ Point end = new Point(initX + sizeX, initY + sizeY);
+
+ return new CropResult(mutableBitmap, init, end);
+ }
+
+ @Deprecated
+ public Bitmap cropFace(Context ctx, int resDrawable) {
+ return getCroppedImage(ctx, resDrawable);
+ }
+
+ @Deprecated
+ public Bitmap cropFace(Bitmap bitmap) {
+ return getCroppedImage(bitmap);
+ }
+
+ public Bitmap getFullDebugImage(Context ctx, int resDrawable) {
+ // Set internal configuration to RGB_565
+ BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
+ bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+
+ return getFullDebugImage(BitmapFactory.decodeResource(ctx.getResources(), resDrawable, bitmapOptions));
+ }
+
+ public Bitmap getFullDebugImage(Bitmap bitmap) {
+ CropResult result = cropFace(bitmap, true);
+ Canvas canvas = new Canvas(result.getBitmap());
+
+ canvas.drawBitmap(result.getBitmap(), new Matrix(), null);
+ canvas.drawRect(result.getInit().x,
+ result.getInit().y,
+ result.getEnd().x,
+ result.getEnd().y,
+ mDebugAreaPainter);
+
+ return result.getBitmap();
+ }
+
+ public Bitmap getCroppedImage(Context ctx, int resDrawable) {
+ // Set internal configuration to RGB_565
+ BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
+ bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+
+ return getCroppedImage(BitmapFactory.decodeResource(ctx.getResources(), resDrawable, bitmapOptions));
+ }
+
+ public Bitmap getCroppedImage(Bitmap bitmap) {
+ CropResult result = cropFace(bitmap, mDebug);
+ Bitmap croppedBitmap = Bitmap.createBitmap(result.getBitmap(),
+ result.getInit().x,
+ result.getInit().y,
+ result.getEnd().x - result.getInit().x,
+ result.getEnd().y - result.getInit().y);
+
+ if (result.getBitmap() != croppedBitmap) {
+ result.getBitmap().recycle();
}
return croppedBitmap;
}
+
+ protected class CropResult {
+ Bitmap mBitmap;
+ Point mInit;
+ Point mEnd;
+
+ public CropResult(Bitmap bitmap, Point init, Point end) {
+ mBitmap = bitmap;
+ mInit = init;
+ mEnd = end;
+ }
+
+ public CropResult(Bitmap bitmap) {
+ mBitmap = bitmap;
+ mInit = new Point(0, 0);
+ mEnd = new Point(bitmap.getWidth(), bitmap.getHeight());
+ }
+
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
+
+ public Point getInit() {
+ return mInit;
+ }
+
+ public Point getEnd() {
+ return mEnd;
+ }
+ }
}
diff --git a/FaceCropper-sample/build.gradle b/FaceCropper-sample/build.gradle
index fe0e172..dc7a835 100644
--- a/FaceCropper-sample/build.gradle
+++ b/FaceCropper-sample/build.gradle
@@ -1,8 +1,8 @@
-apply plugin: 'android'
+apply plugin: 'com.android.application'
android {
compileSdkVersion 19
- buildToolsVersion "19.0.3"
+ buildToolsVersion "20.0.0"
defaultConfig {
minSdkVersion 8
@@ -19,8 +19,8 @@ android {
}
dependencies {
- compile 'com.android.support:appcompat-v7:+'
compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.squareup.picasso:picasso:2.2.0'
compile project(':FaceCropper-library')
+ compile 'com.android.support:appcompat-v7:20.+'
+ compile 'com.squareup.picasso:picasso:2.2.0'
}
diff --git a/FaceCropper-sample/src/main/AndroidManifest.xml b/FaceCropper-sample/src/main/AndroidManifest.xml
index ff0623a..b4c0b70 100644
--- a/FaceCropper-sample/src/main/AndroidManifest.xml
+++ b/FaceCropper-sample/src/main/AndroidManifest.xml
@@ -3,11 +3,10 @@
package="cat.lafosca.facecropper.sample" >
-
+
-
diff --git a/FaceCropper-sample/src/main/java/cat/lafosca/facecropper/sample/MainActivity.java b/FaceCropper-sample/src/main/java/cat/lafosca/facecropper/sample/MainActivity.java
index db7d258..446c727 100644
--- a/FaceCropper-sample/src/main/java/cat/lafosca/facecropper/sample/MainActivity.java
+++ b/FaceCropper-sample/src/main/java/cat/lafosca/facecropper/sample/MainActivity.java
@@ -25,12 +25,12 @@ public class MainActivity extends ActionBarActivity {
@Override
public Bitmap transform(Bitmap source) {
- return mFaceCropper.cropFace(source);
+ return mFaceCropper.getCroppedImage(source);
}
@Override
public String key() {
- StringBuilder builder = new StringBuilder(48);
+ StringBuilder builder = new StringBuilder();
builder.append("faceCrop(");
builder.append("minSize=").append(mFaceCropper.getFaceMinSize());
@@ -47,6 +47,32 @@ public String key() {
}
};
+ private Transformation mDebugCropTransformation = new Transformation() {
+
+ @Override
+ public Bitmap transform(Bitmap source) {
+ return mFaceCropper.getFullDebugImage(source);
+ }
+
+ @Override
+ public String key() {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("faceDebugCrop(");
+ builder.append("minSize=").append(mFaceCropper.getFaceMinSize());
+ builder.append(",maxFaces=").append(mFaceCropper.getMaxFaces());
+
+ FaceCropper.SizeMode mode = mFaceCropper.getSizeMode();
+ if (FaceCropper.SizeMode.EyeDistanceFactorMargin.equals(mode)) {
+ builder.append(",distFactor=").append(mFaceCropper.getEyeDistanceFactorMargin());
+ } else if (FaceCropper.SizeMode.FaceMarginPx.equals(mode)) {
+ builder.append(",margin=").append(mFaceCropper.getFaceMarginPx());
+ }
+
+ return builder.append(")").toString();
+ }
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -54,6 +80,7 @@ protected void onCreate(Bundle savedInstanceState) {
mFaceCropper = new FaceCropper(1f);
mFaceCropper.setFaceMinSize(0);
+ mFaceCropper.setDebug(true);
mPicasso = Picasso.with(this);
final ImageAdapter adapter = new ImageAdapter();
@@ -135,7 +162,7 @@ public void setupView(View v, int position) {
ImageView image = (ImageView) v.findViewById(R.id.imageView);
ImageView imageCropped = (ImageView) v.findViewById(R.id.imageViewCropped);
- mPicasso.load(urls[position]).into(image);
+ mPicasso.load(urls[position]).transform(mDebugCropTransformation).into(image);
mPicasso.load(urls[position])
.config(Bitmap.Config.RGB_565)
diff --git a/FaceCropper-sample/src/main/res/values/strings.xml b/FaceCropper-sample/src/main/res/values/strings.xml
index 8ce5e79..234f1a9 100644
--- a/FaceCropper-sample/src/main/res/values/strings.xml
+++ b/FaceCropper-sample/src/main/res/values/strings.xml
@@ -4,5 +4,6 @@
FaceCropper
Hello world!
Settings
+ DebugActivity
diff --git a/build.gradle b/build.gradle
index 80eec1a..e33f142 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.9.+'
+ classpath 'com.android.tools.build:gradle:0.12.+'
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 5de946b..1e61d1f 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip