Replace Firebase ML vision with built in face detection.

fork-5.53.8
Alan Evans 2021-01-27 20:00:07 -04:00 zatwierdzone przez Greyson Parrelli
rodzic 1b448c2bdf
commit 6a45858b4a
6 zmienionych plików z 120 dodań i 127 usunięć

Wyświetl plik

@ -162,10 +162,6 @@ android {
exclude 'META-INF/proguard/androidx-annotations.pro'
}
aaptOptions {
ignoreAssetsPattern '!contours.tfl:!LMprec_600.emd:!blazeface.tfl'
}
buildTypes {
debug {
if (keystores['debug'] != null) {
@ -315,8 +311,6 @@ dependencies {
implementation "androidx.camera:camera-view:1.0.0-alpha18"
implementation "androidx.concurrent:concurrent-futures:1.0.0"
implementation "androidx.autofill:autofill:1.0.0"
implementation 'com.google.firebase:firebase-ml-vision:24.0.3'
implementation 'com.google.firebase:firebase-ml-vision-face-model:20.0.1'
implementation ('com.google.firebase:firebase-messaging:20.2.0') {
exclude group: 'com.google.firebase', module: 'firebase-core'

Wyświetl plik

@ -0,0 +1,98 @@
package org.thoughtcrime.securesms.scribbles;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.RectF;
import androidx.annotation.NonNull;
import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log;
import java.util.List;
import java.util.Locale;
/**
* Detects faces with the built in Android face detection.
*/
final class AndroidFaceDetector implements FaceDetector {
private static final String TAG = Log.tag(AndroidFaceDetector.class);
private static final int MAX_FACES = 20;
@Override
public List<Face> detect(@NonNull Bitmap source) {
long startTime = System.currentTimeMillis();
Log.d(TAG, String.format(Locale.US, "Bitmap format is %dx%d %s", source.getWidth(), source.getHeight(), source.getConfig()));
boolean createBitmap = source.getConfig() != Bitmap.Config.RGB_565 || source.getWidth() % 2 != 0;
Bitmap bitmap;
if (createBitmap) {
Log.d(TAG, "Changing colour format to 565, with even width");
bitmap = Bitmap.createBitmap(source.getWidth() & ~0x1, source.getHeight(), Bitmap.Config.RGB_565);
new Canvas(bitmap).drawBitmap(source, 0, 0, null);
} else {
bitmap = source;
}
try {
android.media.FaceDetector faceDetector = new android.media.FaceDetector(bitmap.getWidth(), bitmap.getHeight(), MAX_FACES);
android.media.FaceDetector.Face[] faces = new android.media.FaceDetector.Face[MAX_FACES];
int foundFaces = faceDetector.findFaces(bitmap, faces);
Log.d(TAG, String.format(Locale.US, "Found %d faces", foundFaces));
return Stream.of(faces)
.limit(foundFaces)
.map(AndroidFaceDetector::faceToFace)
.toList();
} finally {
if (createBitmap) {
bitmap.recycle();
}
Log.d(TAG, "Finished in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
private static Face faceToFace(@NonNull android.media.FaceDetector.Face face) {
PointF point = new PointF();
face.getMidPoint(point);
float halfWidth = face.eyesDistance() * 1.4f;
float yOffset = face.eyesDistance() * 0.4f;
RectF bounds = new RectF(point.x - halfWidth, point.y - halfWidth + yOffset, point.x + halfWidth, point.y + halfWidth + yOffset);
return new DefaultFace(bounds, face.confidence());
}
private static class DefaultFace implements Face {
private final RectF bounds;
private final float certainty;
public DefaultFace(@NonNull RectF bounds, float confidence) {
this.bounds = bounds;
this.certainty = confidence;
}
@Override
public RectF getBounds() {
return bounds;
}
@Override
public Class<? extends FaceDetector> getDetectorClass() {
return AndroidFaceDetector.class;
}
@Override
public float getConfidence() {
return certainty;
}
}
}

Wyświetl plik

@ -3,8 +3,18 @@ package org.thoughtcrime.securesms.scribbles;
import android.graphics.Bitmap;
import android.graphics.RectF;
import androidx.annotation.NonNull;
import java.util.List;
interface FaceDetector {
List<RectF> detect(Bitmap bitmap);
List<Face> detect(@NonNull Bitmap bitmap);
interface Face {
RectF getBounds();
Class<? extends FaceDetector> getDetectorClass();
float getConfidence();
}
}

Wyświetl plik

@ -1,79 +0,0 @@
package org.thoughtcrime.securesms.scribbles;
import android.graphics.Bitmap;
import android.graphics.RectF;
import android.os.Build;
import com.annimon.stream.Stream;
import com.google.firebase.ml.vision.FirebaseVision;
import com.google.firebase.ml.vision.common.FirebaseVisionImage;
import com.google.firebase.ml.vision.face.FirebaseVisionFace;
import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetector;
import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetectorOptions;
import org.signal.core.util.logging.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
class FirebaseFaceDetector implements FaceDetector {
private static final String TAG = Log.tag(FirebaseFaceDetector.class);
private static final long MAX_SIZE = 1000 * 1000;
@Override
public List<RectF> detect(Bitmap source) {
long startTime = System.currentTimeMillis();
int performanceMode = getPerformanceMode(source);
Log.d(TAG, "Using performance mode " + performanceMode + " (API " + Build.VERSION.SDK_INT + ", " + source.getWidth() + "x" + source.getHeight() + ")");
FirebaseVisionFaceDetectorOptions options = new FirebaseVisionFaceDetectorOptions.Builder()
.setPerformanceMode(performanceMode)
.setMinFaceSize(0.05f)
.setContourMode(FirebaseVisionFaceDetectorOptions.NO_CONTOURS)
.setLandmarkMode(FirebaseVisionFaceDetectorOptions.NO_LANDMARKS)
.setClassificationMode(FirebaseVisionFaceDetectorOptions.NO_CLASSIFICATIONS)
.build();
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(source);
List<RectF> output = new ArrayList<>();
try (FirebaseVisionFaceDetector detector = FirebaseVision.getInstance().getVisionFaceDetector(options)) {
CountDownLatch latch = new CountDownLatch(1);
detector.detectInImage(image)
.addOnSuccessListener(firebaseVisionFaces -> {
output.addAll(Stream.of(firebaseVisionFaces)
.map(FirebaseVisionFace::getBoundingBox)
.map(r -> new RectF(r.left, r.top, r.right, r.bottom))
.toList());
latch.countDown();
})
.addOnFailureListener(e -> latch.countDown());
latch.await(15, TimeUnit.SECONDS);
} catch (IOException e) {
Log.w(TAG, "Failed to close!", e);
} catch (InterruptedException e) {
Log.w(TAG, e);
}
Log.d(TAG, "Finished in " + (System.currentTimeMillis() - startTime) + " ms");
return output;
}
private static int getPerformanceMode(Bitmap source) {
if (Build.VERSION.SDK_INT < 28) {
return FirebaseVisionFaceDetectorOptions.FAST;
}
return source.getWidth() * source.getHeight() < MAX_SIZE ? FirebaseVisionFaceDetectorOptions.ACCURATE
: FirebaseVisionFaceDetectorOptions.FAST;
}
}

Wyświetl plik

@ -375,7 +375,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
if (mainImage.getRenderer() != null) {
Bitmap bitmap = ((UriGlideRenderer) mainImage.getRenderer()).getBitmap();
if (bitmap != null) {
FaceDetector detector = new FirebaseFaceDetector();
FaceDetector detector = new AndroidFaceDetector();
Point size = model.getOutputSizeMaxWidth(1000);
Bitmap render = model.render(ApplicationDependencies.getApplication(), size);
@ -486,7 +486,7 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
}
private void renderFaceBlurs(@NonNull FaceDetectionResult result) {
List<RectF> faces = result.rects;
List<FaceDetector.Face> faces = result.faces;
if (faces.isEmpty()) {
cachedFaceDetection = null;
@ -497,12 +497,12 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
Matrix faceMatrix = new Matrix();
for (RectF face : faces) {
FaceBlurRenderer faceBlurRenderer = new FaceBlurRenderer();
for (FaceDetector.Face face : faces) {
Renderer faceBlurRenderer = new FaceBlurRenderer();
EditorElement element = new EditorElement(faceBlurRenderer, EditorModel.Z_MASK);
Matrix localMatrix = element.getLocalMatrix();
faceMatrix.setRectToRect(Bounds.FULL_BOUNDS, face, Matrix.ScaleToFit.FILL);
faceMatrix.setRectToRect(Bounds.FULL_BOUNDS, face.getBounds(), Matrix.ScaleToFit.FILL);
localMatrix.set(result.position);
localMatrix.preConcat(faceMatrix);
@ -574,11 +574,11 @@ public final class ImageEditorFragment extends Fragment implements ImageEditorHu
}
private static class FaceDetectionResult {
private final List<RectF> rects;
private final Matrix position;
private final List<FaceDetector.Face> faces;
private final Matrix position;
private FaceDetectionResult(@NonNull List<RectF> rects, @NonNull Point imageSize, @NonNull Matrix position) {
this.rects = rects;
private FaceDetectionResult(@NonNull List<FaceDetector.Face> faces, @NonNull Point imageSize, @NonNull Matrix position) {
this.faces = faces;
this.position = new Matrix(position);
Matrix imageProjectionMatrix = new Matrix();

Wyświetl plik

@ -258,8 +258,8 @@ dependencyVerification {
['com.google.android.gms:play-services-auth-api-phone:16.0.0',
'19365818b9ceb048ef48db12b5ffadd5eb86dbeb2c7c7b823bfdd89c665f42e5'],
['com.google.android.gms:play-services-auth-base:17.0.0',
'c494d23d5cdc7e4c33721877592868d3dc16085cab535c3f589c03052524f737'],
['com.google.android.gms:play-services-auth-base:16.0.0',
'51dc02ad2f8d1d9dff7b5b52c4df2c6c12ef7df55d752e919d5cb4dd6002ecd0'],
['com.google.android.gms:play-services-auth:16.0.1',
'aec9e1c584d442cb9f59481a50b2c66dc191872607c04d97ecb82dd0eb5149ec'],
@ -270,36 +270,15 @@ dependencyVerification {
['com.google.android.gms:play-services-basement:17.0.0',
'd324a1785bbc48bfe3639fc847cfd3cf43d49e967b5caf2794240a854557a39c'],
['com.google.android.gms:play-services-clearcut:17.0.0',
'cce72073c269c2b4cff301304751f2faa2cd1b0344fef581a59da63665f9a4b4'],
['com.google.android.gms:play-services-flags:17.0.0',
'746e66b850c5d2b3a0c73871d3fe71ad1b98b62abc0625bbd5badabb73c82cf2'],
['com.google.android.gms:play-services-maps:16.1.0',
'ff50cae9e4059416202375597d99cdc8ddefd9cea3f1dc2ff53779a3a12eb480'],
['com.google.android.gms:play-services-phenotype:17.0.0',
'53d40a205e48ad4e35923a01f04d9850acbd7403b3d30fb388e586fad1540ece'],
['com.google.android.gms:play-services-stats:17.0.0',
'e8ae5b40512b71e2258bfacd8cd3da398733aa4cde3b32d056093f832b83a6fe'],
['com.google.android.gms:play-services-tasks:17.0.0',
'2e6d1738b73647f3fe7a038b9780b97717b3746eae258009197e36e7bf3112a5'],
['com.google.android.gms:play-services-vision-common:19.0.2',
'b1d93b40a8b49d63d86dfd88ddc4030ab7231d839c5ff3adeb876de94d44b970'],
['com.google.android.gms:play-services-vision-face-contour-internal:16.0.0',
'79e5be6ea321a7c10822f190c45612f1999d37c7bc846d8b01a35478eeb0f985'],
['com.google.android.gms:play-services-vision-image-label:18.0.3',
'aea181d214e170a07f13f537c165750cf81fe4522c4e3df6a845b9aa1dcaa06d'],
['com.google.android.gms:play-services-vision:20.0.0',
'0386c1c32b06c3c771dd518220d47bb5828fa3d415863ecd6859909b52cc4f6f'],
['com.google.android.material:material:1.2.1',
'd3d0cc776f2341da8e572586c7d390a5b356ce39a0deb2768071dc40b364ac80'],
@ -339,15 +318,6 @@ dependencyVerification {
['com.google.firebase:firebase-messaging:20.2.0',
'f49cfba49ab33c6fb7436fe9b790b16d3f1265a29955b48fccc1fb1f231da2d8'],
['com.google.firebase:firebase-ml-common:22.1.1',
'74ac365da2578a07b7dd5cd6ca4ae6d7279c7010153025d081afa5db0dce6d57'],
['com.google.firebase:firebase-ml-vision-face-model:20.0.1',
'e81fc985d9e680be0b18891fa8d108f546173c5da2fd923d787fd13759db3b8a'],
['com.google.firebase:firebase-ml-vision:24.0.3',
'afe0d27eebcb8c52a1e40f1e147b750456e7e02747b7e8f3b9d7f3aa58922c78'],
['com.google.guava:listenablefuture:1.0',
'e4ad7607e5c0477c6f890ef26a49cb8d1bb4dffb650bab4502afee64644e3069'],