Skip to content

Commit 19358a6

Browse files
committed
multiple faces support added
1 parent 535f888 commit 19358a6

File tree

3 files changed

+169
-150
lines changed

3 files changed

+169
-150
lines changed

library/src/main/java/com/rohitarya/picasso/facedetection/transformation/CenterFaceCrop.java

Lines changed: 0 additions & 149 deletions
This file was deleted.
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package com.rohitarya.picasso.facedetection.transformation;
2+
3+
import android.content.res.Resources;
4+
import android.graphics.Bitmap;
5+
import android.graphics.Canvas;
6+
import android.graphics.PointF;
7+
import android.graphics.RectF;
8+
import android.util.SparseArray;
9+
10+
import com.google.android.gms.vision.Frame;
11+
import com.google.android.gms.vision.face.Face;
12+
import com.google.android.gms.vision.face.FaceDetector;
13+
import com.rohitarya.picasso.facedetection.transformation.core.PicassoFaceDetector;
14+
import com.squareup.picasso.Transformation;
15+
16+
/**
17+
* Created by Rohit Arya (http://rohitarya.com) on 19/7/16.
18+
*/
19+
public class FaceCenterCrop implements Transformation {
20+
21+
public static final int PIXEL = 0;
22+
public static final int DP = 1;
23+
protected int width, height;
24+
25+
public FaceCenterCrop(int width, int height) {
26+
this.width = width;
27+
this.height = height;
28+
}
29+
30+
public FaceCenterCrop(int width, int height, int unit) {
31+
if (unit == PIXEL) {
32+
this.width = width;
33+
this.height = height;
34+
} else if (unit == DP) {
35+
Resources resources = PicassoFaceDetector.getContext().getResources();
36+
this.width = resources.getDimensionPixelSize(width);
37+
this.height = resources.getDimensionPixelSize(height);
38+
} else {
39+
throw new IllegalArgumentException("unit should either be FaceCenterCrop.PIXEL, FaceCenterCrop.DP");
40+
}
41+
}
42+
43+
@Override
44+
public Bitmap transform(Bitmap original) {
45+
if (width == 0 || height == 0) {
46+
throw new IllegalArgumentException("width or height should not be zero!");
47+
}
48+
float scaleX = (float) width / original.getWidth();
49+
float scaleY = (float) height / original.getHeight();
50+
51+
if (scaleX != scaleY) {
52+
53+
Bitmap.Config config =
54+
original.getConfig() != null ? original.getConfig() : Bitmap.Config.ARGB_8888;
55+
Bitmap result = Bitmap.createBitmap(width, height, config);
56+
57+
float scale = Math.max(scaleX, scaleY);
58+
59+
float left = 0f;
60+
float top = 0f;
61+
62+
float scaledWidth = width, scaledHeight = height;
63+
64+
PointF focusPoint = new PointF();
65+
detectFace(original, focusPoint);
66+
67+
if (scaleX < scaleY) {
68+
69+
scaledWidth = scale * original.getWidth();
70+
71+
float faceCenterX = scale * focusPoint.x;
72+
left = getLeftPoint(width, scaledWidth, faceCenterX);
73+
74+
} else {
75+
76+
scaledHeight = scale * original.getHeight();
77+
78+
float faceCenterY = scale * focusPoint.y;
79+
top = getTopPoint(height, scaledHeight, faceCenterY);
80+
}
81+
82+
RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
83+
Canvas canvas = new Canvas(result);
84+
canvas.drawBitmap(original, null, targetRect, null);
85+
86+
original.recycle();
87+
88+
return result;
89+
} else {
90+
return original;
91+
}
92+
}
93+
94+
@Override
95+
public String key() {
96+
return FaceCenterCrop.class.getCanonicalName() + "-width-" + width + "height-" + height;
97+
}
98+
99+
/**
100+
* Calculates center point in bitmap, around which cropping needs to be performed.
101+
* Right now, it takes on the average of all faces (their centers).
102+
*
103+
* @param bitmap Bitmap in which faces are to be detected.
104+
* @param centerOfAllFaces To store the center point.
105+
*/
106+
private void detectFace(Bitmap bitmap, PointF centerOfAllFaces) {
107+
FaceDetector faceDetector = PicassoFaceDetector.getFaceDetector();
108+
if (!faceDetector.isOperational()) {
109+
centerOfAllFaces.set(bitmap.getWidth() / 2, bitmap.getHeight() / 2); // center crop
110+
return;
111+
}
112+
Frame frame = new Frame.Builder().setBitmap(bitmap).build();
113+
SparseArray<Face> faces = faceDetector.detect(frame);
114+
final int totalFaces = faces.size();
115+
if (totalFaces > 0) {
116+
float sumX = 0f;
117+
float sumY = 0f;
118+
for (int i = 0; i < totalFaces; i++) {
119+
PointF faceCenter = new PointF();
120+
getFaceCenter(faces.get(faces.keyAt(i)), faceCenter);
121+
sumX = sumX + faceCenter.x;
122+
sumY = sumY + faceCenter.y;
123+
}
124+
centerOfAllFaces.set(sumX / totalFaces, sumY / totalFaces);
125+
return;
126+
}
127+
centerOfAllFaces.set(bitmap.getWidth() / 2, bitmap.getHeight() / 2); // center crop
128+
}
129+
130+
/**
131+
* Calculates center of a given face
132+
*
133+
* @param face Face
134+
* @param center Center of the face
135+
*/
136+
private void getFaceCenter(Face face, PointF center) {
137+
float x = face.getPosition().x;
138+
float y = face.getPosition().y;
139+
float width = face.getWidth();
140+
float height = face.getHeight();
141+
center.set(x + (width / 2), y + (height / 2)); // face center in original bitmap
142+
}
143+
144+
private float getTopPoint(int height, float scaledHeight, float faceCenterY) {
145+
if (faceCenterY <= height / 2) { // Face is near the top edge
146+
return 0f;
147+
} else if ((scaledHeight - faceCenterY) <= height / 2) { // face is near bottom edge
148+
return height - scaledHeight;
149+
} else {
150+
return (height / 2) - faceCenterY;
151+
}
152+
}
153+
154+
private float getLeftPoint(int width, float scaledWidth, float faceCenterX) {
155+
if (faceCenterX <= width / 2) { // face is near the left edge.
156+
return 0f;
157+
} else if ((scaledWidth - faceCenterX) <= width / 2) { // face is near right edge
158+
return (width - scaledWidth);
159+
} else {
160+
return (width / 2) - faceCenterX;
161+
}
162+
}
163+
164+
}

library/src/main/java/com/rohitarya/picasso/facedetection/transformation/core/PicassoFaceDetector.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public static void initialize(Context context) {
2323
if (context == null) {
2424
throw new IllegalArgumentException("Context must not be null.");
2525
}
26-
mContext = context.getApplicationContext();
26+
mContext = context.getApplicationContext(); // To make it independent of activity lifecycle
2727
initDetector();
2828
}
2929

@@ -45,6 +45,10 @@ public static FaceDetector getFaceDetector() {
4545
return faceDetector;
4646
}
4747

48+
/**
49+
* Release the detector when you no longer need it.
50+
* Remember to call PicassoFaceDetector.initialize(context) if you have to re-use.
51+
*/
4852
public static void releaseDetector() {
4953
if (faceDetector != null) {
5054
faceDetector.release();

0 commit comments

Comments
 (0)