You’re cropping images wrong

Learn how to use face detection based cropping in your Android app in 5 minutes

Ugly thumbnails

Google News app official screenshot on Google Play Store
Yahoo News (Hong Kong edition) screenshot on Google Play Store

The proper way

On-device vs. cloud-based face detection

Proof of concept

Google Mobile Vision

The implementation

Step 1: Add Google Mobile Vision dependency

implementation 'com.google.android.gms:play-services-vision:11.6.2'

Step 2: Initialize FaceDetector

detector = new FaceDetector.Builder(context)
.setMinFaceSize(FACE_DETECTION_RATIO_MIN)
.setTrackingEnabled(false)
.build();

Step 3: Find all faces in an image

@NonNull
private Collection<PointF> findFaceCenters(@NonNull final Bitmap bitmap) {
if (this.detector.isOperational()) {
final SparseArray<Face> faces = detector.detect(new Frame.Builder()
.setBitmap(bitmap)
.build());
final Collection<PointF> centers = new ArrayList<>(); for (int i = 0; i < faces.size(); i++) {
final Face face = faces.get(faces.keyAt(i));
centers.add(new PointF(face.getPosition().x + face.getWidth() / 2f, face.getPosition().y + face.getHeight() / 2f));
}
return centers;
}
return Collections.emptyList();
}

Step 4: Down-sample input image

private static int findDownSamplingScale(@NonNull final File file) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int width = options.outWidth;
int height = options.outHeight;
int scale = 1;
while (width * height > FACE_DETECTION_SIZE_MAX) {
width /= 2;
height /= 2;
scale *= 2;
}
return scale;
}

Step 5: Find the center of all faces

@NonNull
public
PointF findFaceCenter(@NonNull final File file) {
final int scale = findDownSamplingScale(file);
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = scale;
final Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options); final Collection<PointF> centers = findFaceCenters(bitmap);
if (centers.isEmpty()) return new PointF(bitmap.getWidth() / 2f, bitmap.getHeight() / 2f);
float sumX = 0f;
float sumY = 0f;
for (final PointF center : centers) {
sumX += center.x;
sumY += center.y;
}
return new PointF(scale * sumX / centers.size(), scale * sumY / centers.size());
}

Step 6: Translate the center of your ImageView

Step 7: Release the detector

What’s the point?

Continuously innovates and creates extraordinary impacts on digital businesses through technology excellence | https://www.linkedin.com/in/ayltai/