add facefinder
diff --git a/FaceShared/src/main/java/com/libremobileos/yifan/face/scan/FaceFinder.java b/FaceShared/src/main/java/com/libremobileos/yifan/face/scan/FaceFinder.java
new file mode 100644
index 0000000..e4b1ad4
--- /dev/null
+++ b/FaceShared/src/main/java/com/libremobileos/yifan/face/scan/FaceFinder.java
@@ -0,0 +1,64 @@
+package com.libremobileos.yifan.face.scan;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.SystemClock;
+import android.util.Pair;
+
+import com.libremobileos.yifan.face.shared.FaceDetector;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** lame wrapper around FaceDetector and FaceScanner */
+public class FaceFinder {
+ private final FaceDetector faceDetector;
+ private final FaceDetector.InputImageProcessor detectorInputProc;
+ private final FaceScanner faceScanner;
+ private final int sensorOrientation;
+
+ private FaceFinder(Context ctx, int inputWidth, int inputHeight, int sensorOrientation) {
+ this.faceDetector = FaceDetector.create(ctx);
+ this.faceScanner = FaceScanner.create(ctx);
+ this.sensorOrientation = sensorOrientation;
+ this.detectorInputProc = new FaceDetector.InputImageProcessor(inputWidth, inputHeight, sensorOrientation);
+ }
+
+ public static FaceFinder create(Context ctx, int inputWidth, int inputHeight, int sensorOrientation) {
+ return new FaceFinder(ctx, inputWidth, inputHeight, sensorOrientation);
+ }
+
+ public void setUseNNAPI(boolean useNNAPI) {
+ faceScanner.setUseNNAPI(useNNAPI);
+ faceDetector.setUseNNAPI(useNNAPI);
+ }
+
+ public void setNumThreads(int numThreads) {
+ faceScanner.setNumThreads(numThreads);
+ faceDetector.setNumThreads(numThreads);
+ }
+
+ public Pair<List<Pair<FaceDetector.Face, FaceScanner.Face>> /* detected faces */, Long /* processing time */> process(Bitmap input, boolean add) {
+ FaceDetector.InputImage inputImage = detectorInputProc.process(input);
+
+ final long startTime1 = SystemClock.uptimeMillis();
+ final List<FaceDetector.Face> faces = faceDetector.detectFaces(inputImage);
+ final List<Pair<FaceDetector.Face, FaceScanner.Face>> results = new ArrayList<>();
+
+ if (faces != null && faces.size() > 0) {
+ final FaceScanner.InputImageProcessor scannerInputProc = new FaceScanner.InputImageProcessor(input, sensorOrientation);
+
+ for (FaceDetector.Face face : faces) {
+ FaceScanner.InputImage faceBmp = scannerInputProc.process(face.getLocation());
+ if (faceBmp == null) continue;
+
+ final FaceScanner.Face scanned = faceScanner.detectFace(faceBmp, add);
+ if (scanned == null) continue;
+
+ results.add(new Pair<>(face, scanned));
+ }
+ }
+
+ return new Pair<>(results, /* lastProcessingTimeMs */ SystemClock.uptimeMillis() - startTime1);
+ }
+}
diff --git a/FaceShared/src/main/java/com/libremobileos/yifan/face/scan/FaceScanner.java b/FaceShared/src/main/java/com/libremobileos/yifan/face/scan/FaceScanner.java
index 8a9cd43..0b2e146 100644
--- a/FaceShared/src/main/java/com/libremobileos/yifan/face/scan/FaceScanner.java
+++ b/FaceShared/src/main/java/com/libremobileos/yifan/face/scan/FaceScanner.java
@@ -5,10 +5,8 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
-import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
-import android.util.Pair;
import androidx.annotation.NonNull;
@@ -16,10 +14,8 @@
import com.libremobileos.yifan.face.shared.SimilarityClassifier;
import java.io.IOException;
-import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
public class FaceScanner {
private final AssetManager am;
@@ -53,12 +49,33 @@
public static class InputImageProcessor {
private final int sensorOrientation;
+ private final Bitmap portraitBmp;
+ private final Matrix transform;
- public InputImageProcessor(int sensorOrientation) {
+ public InputImageProcessor(Bitmap rawImage, int sensorOrientation) {
this.sensorOrientation = sensorOrientation;
+ //TODO replace this mess with ImageUtils transform
+ int targetW, targetH;
+ if (sensorOrientation == 90 || sensorOrientation == 270) {
+ targetH = rawImage.getWidth();
+ targetW = rawImage.getHeight();
+ } else {
+ targetW = rawImage.getWidth();
+ targetH = rawImage.getHeight();
+ }
+ Bitmap portraitBmp = Bitmap.createBitmap(targetW, targetH, Bitmap.Config.ARGB_8888);
+ transform = createTransform(
+ rawImage.getWidth(),
+ rawImage.getHeight(),
+ targetW,
+ targetH,
+ sensorOrientation);
+ final Canvas cv = new Canvas(portraitBmp);
+ cv.drawBitmap(rawImage, transform, null);
+ this.portraitBmp = portraitBmp;
}
- public InputImage process(Bitmap input) {
+ public static InputImage process(Bitmap input, int sensorOrientation) {
Matrix frameToCropTransform =
ImageUtils.getTransformationMatrix(
input.getWidth(), input.getHeight(),
@@ -70,66 +87,40 @@
return new InputImage(croppedBitmap, input);
}
- // utility because everyone is lazy :D
- public InputImage process(Bitmap fullInput, Bitmap input, RectF inputBB) {
- //TODO cleanup
- Matrix transform = createTransform(
- fullInput.getWidth(),
- fullInput.getHeight(),
- input.getWidth(),
- input.getHeight(),
- sensorOrientation);
+ public InputImage process(Bitmap input) {
+ return process(input, sensorOrientation);
+ }
+
+ public InputImage process(RectF inputBB) {
RectF faceBB = new RectF(inputBB);
transform.mapRect(faceBB);
if (faceBB.left < 0 || faceBB.top < 0 || faceBB.bottom < 0 ||
- faceBB.right < 0 || (faceBB.left + faceBB.width()) > input.getWidth()
- || (faceBB.top + faceBB.height()) > input.getHeight()) return null;
- return process(Bitmap.createBitmap(input,
+ faceBB.right < 0 || (faceBB.left + faceBB.width()) > portraitBmp.getWidth()
+ || (faceBB.top + faceBB.height()) > portraitBmp.getHeight()) return null;
+ return process(Bitmap.createBitmap(portraitBmp,
(int) faceBB.left,
(int) faceBB.top,
(int) faceBB.width(),
(int) faceBB.height()));
}
- public Bitmap transformRawImageToPortrait(Bitmap input) {
- //TODO replace this mess with ImageUtils transform
- int targetW, targetH;
- if (sensorOrientation == 90 || sensorOrientation == 270) {
- targetH = input.getWidth();
- targetW = input.getHeight();
- } else {
- targetW = input.getWidth();
- targetH = input.getHeight();
+ private static Matrix createTransform( //should be removed while reworking portraitBmp creation
+ final int srcWidth,
+ final int srcHeight,
+ final int dstWidth,
+ final int dstHeight,
+ final int applyRotation) {
+ Matrix matrix = new Matrix();
+ if (applyRotation != 0) {
+ matrix.postTranslate(-srcWidth / 2.0f, -srcHeight / 2.0f);
+ matrix.postRotate(applyRotation);
+ matrix.postTranslate(dstWidth / 2.0f, dstHeight / 2.0f);
}
- Bitmap portraitBmp = Bitmap.createBitmap(targetW, targetH, Bitmap.Config.ARGB_8888);
- Matrix transform = createTransform(
- input.getWidth(),
- input.getHeight(),
- targetW,
- targetH,
- sensorOrientation);
- final Canvas cv = new Canvas(portraitBmp);
- cv.drawBitmap(input, transform, null);
- return portraitBmp;
+ return matrix;
}
- }
- private static Matrix createTransform( //should be removed while reworking transformRawImageToPortrait
- final int srcWidth,
- final int srcHeight,
- final int dstWidth,
- final int dstHeight,
- final int applyRotation) {
- Matrix matrix = new Matrix();
- if (applyRotation != 0) {
- matrix.postTranslate(-srcWidth / 2.0f, -srcHeight / 2.0f);
- matrix.postRotate(applyRotation);
- matrix.postTranslate(dstWidth / 2.0f, dstHeight / 2.0f);
- }
- return matrix;
}
-
/** An immutable result returned by a FaceDetector describing what was recognized. */
public static class Face {
/**
@@ -288,7 +279,7 @@
SimilarityClassifier.Recognition result = results.get(0);
return new Face(result.getId(), result.getTitle(), result.getDistance(), null, add ? input.getUserDisplayableImage() : null, result.getExtra());
} catch (IOException e) {
- Log.e("FaceDetector", Log.getStackTraceString(e));
+ Log.e("FaceScanner", Log.getStackTraceString(e));
return null;
}
}
diff --git a/app/src/main/java/com/libremobileos/facedetect/MainActivity.java b/app/src/main/java/com/libremobileos/facedetect/MainActivity.java
index 6758225..6936272 100644
--- a/app/src/main/java/com/libremobileos/facedetect/MainActivity.java
+++ b/app/src/main/java/com/libremobileos/facedetect/MainActivity.java
@@ -17,7 +17,6 @@
package com.libremobileos.facedetect;
import android.app.AlertDialog;
-import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Color;
@@ -28,6 +27,7 @@
import android.media.ImageReader.OnImageAvailableListener;
import android.os.Bundle;
import android.os.SystemClock;
+import android.util.Pair;
import android.util.Size;
import android.util.TypedValue;
import android.view.LayoutInflater;
@@ -42,12 +42,13 @@
import com.libremobileos.facedetect.util.Logger;
import com.libremobileos.facedetect.util.MultiBoxTracker;
import com.libremobileos.facedetect.util.OverlayView;
+import com.libremobileos.yifan.face.scan.FaceFinder;
import com.libremobileos.yifan.face.scan.FaceScanner;
import com.libremobileos.yifan.face.shared.FaceDetector;
import com.libremobileos.yifan.face.shared.SimilarityClassifier;
+import java.util.ArrayList;
import java.util.HashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -64,8 +65,7 @@
OverlayView trackingOverlay;
private Integer sensorOrientation;
- private FaceScanner detector;
- private FaceScanner.InputImageProcessor inputImageProcessor;
+ private FaceFinder detector;
private long lastProcessingTimeMs;
private Bitmap rgbFrameBitmap = null;
@@ -77,10 +77,6 @@
private MultiBoxTracker tracker;
- // Face detector
- private FaceDetector faceDetector;
- private FaceDetector.InputImageProcessor detectorInputImageProcessor;
-
private HashMap<String, float[][]> knownFaces = new HashMap<>();
@Override
@@ -110,12 +106,7 @@
tracker = new MultiBoxTracker(this);
-
- detector /* face auth */ =
- FaceScanner.create(this);
-
- faceDetector /* find face */ =
- FaceDetector.create(this);
+ detector = FaceFinder.create(this, previewWidth, previewHeight, sensorOrientation);
previewWidth = size.getWidth();
previewHeight = size.getHeight();
@@ -135,8 +126,6 @@
}
});
- detectorInputImageProcessor = new FaceDetector.InputImageProcessor(previewWidth, previewHeight, sensorOrientation);
- inputImageProcessor = new FaceScanner.InputImageProcessor(sensorOrientation);
tracker.setFrameConfiguration(previewWidth, previewHeight, sensorOrientation);
}
@@ -160,17 +149,9 @@
readyForNextImage();
- FaceDetector.InputImage croppedBitmap = detectorInputImageProcessor.process(rgbFrameBitmap);
-
- // 匿名类
runInBackground(
() -> {
- LOGGER.i("Running detection on image " + currTimestamp);
- final long startTime = SystemClock.uptimeMillis();
- final List<FaceDetector.Face> results = faceDetector.detectFaces(croppedBitmap);
- lastProcessingTimeMs = SystemClock.uptimeMillis() - startTime;
-
- onFacesDetected(currTimestamp, results, addPending);
+ onFacesDetected(currTimestamp, rgbFrameBitmap, addPending);
addPending = false;
});
}
@@ -189,7 +170,6 @@
protected void setUseNNAPI(final boolean isChecked) {
runInBackground(() -> {
detector.setUseNNAPI(isChecked);
- faceDetector.setUseNNAPI(isChecked);
});
}
@@ -197,7 +177,6 @@
protected void setNumThreads(final int numThreads) {
runInBackground(() -> {
detector.setNumThreads(numThreads);
- faceDetector.setNumThreads(numThreads);
});
}
@@ -214,18 +193,14 @@
ivFace.setImageBitmap(rec.getCrop());
etName.setHint("Input name");
- builder.setPositiveButton("OK", new DialogInterface.OnClickListener(){
- @Override
- public void onClick(DialogInterface dlg, int i) {
-
- String name = etName.getText().toString();
- if (name.isEmpty()) {
- return;
- }
- //detector.register(name, rec);
- knownFaces.put(name, rec.getExtra());
- dlg.dismiss();
- }
+ builder.setPositiveButton("OK", (dlg, i) -> {
+ String name = etName.getText().toString();
+ if (name.isEmpty()) {
+ return;
+ }
+ //detector.register(name, rec);
+ knownFaces.put(name, rec.getExtra());
+ dlg.dismiss();
});
builder.setView(dialogLayout);
builder.show();
@@ -249,29 +224,28 @@
}
runOnUiThread(
- new Runnable() {
- @Override
- public void run() {
- showFrameInfo(previewWidth + "x" + previewHeight);
- showInference(lastProcessingTimeMs + "ms");
- }
+ () -> {
+ showFrameInfo(previewWidth + "x" + previewHeight);
+ showInference(lastProcessingTimeMs + "ms");
});
}
- private void onFacesDetected(long currTimestamp, List<FaceDetector.Face> faces, boolean add) {
+ private void onFacesDetected(long currTimestamp, Bitmap rgbFrameBitmap, boolean add) {
// TODO move this to yifan.face package to make this recycle-able code
- final List<SimilarityClassifier.Recognition> mappedRecognitions =
- new LinkedList<>();
- Bitmap portraitImage = inputImageProcessor.transformRawImageToPortrait(rgbFrameBitmap);
+ List<SimilarityClassifier.Recognition> mappedRecognitions = new ArrayList<>();
- for (FaceDetector.Face face : faces) {
+ Pair<List<Pair<FaceDetector.Face, FaceScanner.Face>>, Long> faces = detector.process(rgbFrameBitmap, add);
+ lastProcessingTimeMs = faces.second;
+
+ for (Pair<FaceDetector.Face, FaceScanner.Face> faceFacePair : faces.first) {
+
+ FaceDetector.Face face = faceFacePair.first;
+ FaceScanner.Face scanned = faceFacePair.second;
LOGGER.i("FACE" + face.toString());
LOGGER.i("Running detection on face " + currTimestamp);
- FaceScanner.InputImage faceBmp = inputImageProcessor.process(rgbFrameBitmap, portraitImage, face.getLocation());
- if (faceBmp == null) continue;
final RectF boundingBox = new RectF(face.getLocation());
@@ -281,10 +255,6 @@
float[][] extra = null;
Bitmap crop = null;
- final long startTime = SystemClock.uptimeMillis();
- final FaceScanner.Face scanned = detector.detectFace(faceBmp, add);
- lastProcessingTimeMs = SystemClock.uptimeMillis() - startTime;
-
FaceScanner.Face detected = null;
for (Map.Entry<String, float[][]> possible : knownFaces.entrySet()) {
float newdistance = scanned.compare(possible.getValue());
@@ -293,7 +263,10 @@
}
}
- if (detected != null) {
+ if (add) {
+ extra = scanned.getExtra();
+ crop = scanned.getCrop();
+ } else if (detected != null) {
extra = detected.getExtra();
distance = detected.getDistance();
label = detected.getTitle();
@@ -303,9 +276,6 @@
} else {
color = Color.RED;
}
- } else if (add) {
- extra = scanned.getExtra();
- crop = scanned.getCrop();
}
if (getCameraFacing() == CameraCharacteristics.LENS_FACING_FRONT) {
diff --git a/app/src/main/java/com/libremobileos/facedetect/util/CameraActivity.java b/app/src/main/java/com/libremobileos/facedetect/util/CameraActivity.java
index 232f858..73608fe 100644
--- a/app/src/main/java/com/libremobileos/facedetect/util/CameraActivity.java
+++ b/app/src/main/java/com/libremobileos/facedetect/util/CameraActivity.java
@@ -86,7 +86,7 @@
private LinearLayout gestureLayout;
private BottomSheetBehavior<LinearLayout> sheetBehavior;
- protected TextView frameValueTextView, cropValueTextView, inferenceTimeTextView;
+ protected TextView frameValueTextView, inferenceTimeTextView;
protected ImageView bottomSheetArrowImageView;
private ImageView plusImageView, minusImageView;
private SwitchCompat apiSwitchCompat;
@@ -184,7 +184,6 @@
});
frameValueTextView = findViewById(R.id.frame_info);
- cropValueTextView = findViewById(R.id.crop_info);
inferenceTimeTextView = findViewById(R.id.inference_info);
apiSwitchCompat.setOnCheckedChangeListener(this);
@@ -622,10 +621,6 @@
frameValueTextView.setText(frameInfo);
}
- protected void showCropInfo(String cropInfo) {
- cropValueTextView.setText(cropInfo);
- }
-
protected void showInference(String inferenceTime) {
inferenceTimeTextView.setText(inferenceTime);
}
diff --git a/app/src/main/res/layout/tfe_od_layout_bottom_sheet.xml b/app/src/main/res/layout/tfe_od_layout_bottom_sheet.xml
index 8b3a8c5..b7334f7 100644
--- a/app/src/main/res/layout/tfe_od_layout_bottom_sheet.xml
+++ b/app/src/main/res/layout/tfe_od_layout_bottom_sheet.xml
@@ -52,30 +52,6 @@
android:textColor="@android:color/black" />
</LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <TextView
- android:id="@+id/crop"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:text="Crop"
- android:textColor="@android:color/black" />
-
- <TextView
- android:id="@+id/crop_info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:gravity="right"
- android:text="640*480"
- android:textColor="@android:color/black" />
- </LinearLayout>
-
-
<LinearLayout
android:layout_width="match_parent"