Я работаю над проектом android, где у меня есть доступ к ориентирам лица из текущего обрабатываемого кадра. Затем я должен выполнить некоторые вычисления на основе положения лица. Наконец, мне нужно сохранить текущий обрабатываемый кадр.
Для этого я использую ML Kit Vision Quickstart Sample App . Этот код выполняет большую часть моей работы. Из этого кода я использую классы LivePreviewActivity. java, FaceDetectorProcessor. java и FaceGraphi c. java. Я выполнил все вычисления внутри класса FaceGraphics. java. Но я НЕ могу получить доступ к кадру, который в настоящее время обрабатывается.
LivePreviewActivity.java
/*
* Copyright 2020 Google LLC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.mlkit.vision.demo;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback;
import androidx.core.content.ContextCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.Toast;
import android.widget.ToggleButton;
import com.google.android.gms.common.annotation.KeepName;
import com.google.mlkit.common.model.LocalModel;
import com.google.mlkit.vision.demo.automl.AutoMLImageLabelerProcessor;
import com.google.mlkit.vision.demo.barcodescanner.BarcodeScannerProcessor;
import com.google.mlkit.vision.demo.facedetector.FaceDetectorProcessor;
import com.google.mlkit.vision.demo.labeldetector.LabelDetectorProcessor;
import com.google.mlkit.vision.demo.objectdetector.ObjectDetectorProcessor;
import com.google.mlkit.vision.demo.preference.PreferenceUtils;
import com.google.mlkit.vision.demo.preference.SettingsActivity;
import com.google.mlkit.vision.demo.preference.SettingsActivity.LaunchSource;
import com.google.mlkit.vision.demo.textdetector.TextRecognitionProcessor;
import com.google.mlkit.vision.face.FaceDetectorOptions;
import com.google.mlkit.vision.label.custom.CustomImageLabelerOptions;
import com.google.mlkit.vision.label.defaults.ImageLabelerOptions;
import com.google.mlkit.vision.objects.custom.CustomObjectDetectorOptions;
import com.google.mlkit.vision.objects.defaults.ObjectDetectorOptions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Live preview demo for ML Kit APIs.
*/
@KeepName
public final class LivePreviewActivity extends AppCompatActivity
implements OnRequestPermissionsResultCallback,
OnItemSelectedListener,
CompoundButton.OnCheckedChangeListener {
private static final String FACE_DETECTION = "Face Detection";
private static final String TAG = "LivePreviewActivity";
private static final int PERMISSION_REQUESTS = 1;
private CameraSource cameraSource = null;
private CameraSourcePreview preview;
private GraphicOverlay graphicOverlay;
private String selectedModel = FACE_DETECTION;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
setContentView(R.layout.activity_vision_live_preview);
preview = findViewById(R.id.preview);
if (preview == null) {
Log.d(TAG, "Preview is null");
}
graphicOverlay = findViewById(R.id.graphic_overlay);
if (graphicOverlay == null) {
Log.d(TAG, "graphicOverlay is null");
}
Spinner spinner = findViewById(R.id.spinner);
List<String> options = new ArrayList<>();
options.add(FACE_DETECTION);
// Creating adapter for spinner
ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(this, R.layout.spinner_style, options);
// Drop down layout style - list view with radio button
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
// attaching data adapter to spinner
spinner.setAdapter(dataAdapter);
spinner.setOnItemSelectedListener(this);
ToggleButton facingSwitch = findViewById(R.id.facing_switch);
facingSwitch.setOnCheckedChangeListener(this);
ImageView settingsButton = findViewById(R.id.settings_button);
settingsButton.setOnClickListener(
v -> {
Intent intent = new Intent(getApplicationContext(), SettingsActivity.class);
intent.putExtra(SettingsActivity.EXTRA_LAUNCH_SOURCE,
SettingsActivity.LaunchSource.LIVE_PREVIEW);
startActivity(intent);
});
if (allPermissionsGranted()) {
createCameraSource(selectedModel);
} else {
getRuntimePermissions();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.live_preview_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.settings) {
Intent intent = new Intent(this, SettingsActivity.class);
intent.putExtra(SettingsActivity.EXTRA_LAUNCH_SOURCE, LaunchSource.LIVE_PREVIEW);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public synchronized void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
// An item was selected. You can retrieve the selected item using
// parent.getItemAtPosition(pos)
selectedModel = parent.getItemAtPosition(pos).toString();
Log.d(TAG, "Selected model: " + selectedModel);
preview.stop();
if (allPermissionsGranted()) {
createCameraSource(selectedModel);
startCameraSource();
} else {
getRuntimePermissions();
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// Do nothing.
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.d(TAG, "Set facing");
if (cameraSource != null) {
if (isChecked) {
cameraSource.setFacing(CameraSource.CAMERA_FACING_FRONT);
} else {
cameraSource.setFacing(CameraSource.CAMERA_FACING_BACK);
}
}
preview.stop();
startCameraSource();
}
private void createCameraSource(String model) {
// If there's no existing cameraSource, create one.
if (cameraSource == null) {
cameraSource = new CameraSource(this, graphicOverlay);
}
try {
Log.i(TAG, "Using Face Detector Processor");
FaceDetectorOptions faceDetectorOptions = new FaceDetectorOptions.Builder()
.setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL)
.build();
// PreferenceUtils.getFaceDetectorOptionsForLivePreview(this);
cameraSource.setMachineLearningFrameProcessor(
new FaceDetectorProcessor(this, faceDetectorOptions));
} catch (Exception e) {
Log.e(TAG, "Can not create image processor: " + model, e);
Toast.makeText(
getApplicationContext(),
"Can not create image processor: " + e.getMessage(),
Toast.LENGTH_LONG)
.show();
}
}
/**
* Starts or restarts the camera source, if it exists. If the camera source doesn't exist yet
* (e.g., because onResume was called before the camera source was created), this will be called
* again when the camera source is created.
*/
private void startCameraSource() {
if (cameraSource != null) {
try {
if (preview == null) {
Log.d(TAG, "resume: Preview is null");
}
if (graphicOverlay == null) {
Log.d(TAG, "resume: graphOverlay is null");
}
preview.start(cameraSource, graphicOverlay);
} catch (IOException e) {
Log.e(TAG, "Unable to start camera source.", e);
cameraSource.release();
cameraSource = null;
}
}
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
createCameraSource(selectedModel);
startCameraSource();
}
/**
* Stops the camera.
*/
@Override
protected void onPause() {
super.onPause();
preview.stop();
}
@Override
public void onDestroy() {
super.onDestroy();
if (cameraSource != null) {
cameraSource.release();
}
}
private String[] getRequiredPermissions() {
try {
PackageInfo info =
this.getPackageManager()
.getPackageInfo(this.getPackageName(), PackageManager.GET_PERMISSIONS);
String[] ps = info.requestedPermissions;
if (ps != null && ps.length > 0) {
return ps;
} else {
return new String[0];
}
} catch (Exception e) {
return new String[0];
}
}
private boolean allPermissionsGranted() {
for (String permission : getRequiredPermissions()) {
if (!isPermissionGranted(this, permission)) {
return false;
}
}
return true;
}
private void getRuntimePermissions() {
List<String> allNeededPermissions = new ArrayList<>();
for (String permission : getRequiredPermissions()) {
if (!isPermissionGranted(this, permission)) {
allNeededPermissions.add(permission);
}
}
if (!allNeededPermissions.isEmpty()) {
ActivityCompat.requestPermissions(
this, allNeededPermissions.toArray(new String[0]), PERMISSION_REQUESTS);
}
}
@Override
public void onRequestPermissionsResult(
int requestCode, String[] permissions, int[] grantResults) {
Log.i(TAG, "Permission granted!");
if (allPermissionsGranted()) {
createCameraSource(selectedModel);
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
private static boolean isPermissionGranted(Context context, String permission) {
if (ContextCompat.checkSelfPermission(context, permission)
== PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "Permission granted: " + permission);
return true;
}
Log.i(TAG, "Permission NOT granted: " + permission);
return false;
}
}
FaceDetectorProcessor.java
/*
* Copyright 2020 Google LLC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.mlkit.vision.demo.facedetector;
import android.content.Context;
import android.graphics.PointF;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.android.gms.tasks.Task;
import com.google.mlkit.vision.common.InputImage;
import com.google.mlkit.vision.demo.GraphicOverlay;
import com.google.mlkit.vision.demo.VisionProcessorBase;
import com.google.mlkit.vision.face.Face;
import com.google.mlkit.vision.face.FaceDetection;
import com.google.mlkit.vision.face.FaceDetector;
import com.google.mlkit.vision.face.FaceDetectorOptions;
import com.google.mlkit.vision.face.FaceLandmark;
import java.util.List;
import java.util.Locale;
/**
* Face Detector Demo.
*/
public class FaceDetectorProcessor extends VisionProcessorBase<List<Face>> {
private static final String TAG = "FaceDetectorProcessor";
private final FaceDetector detector;
public FaceDetectorProcessor(Context context) {
this(
context,
new FaceDetectorOptions.Builder()
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
.enableTracking()
.build());
}
public FaceDetectorProcessor(Context context, FaceDetectorOptions options) {
super(context);
Log.v(MANUAL_TESTING_LOG, "Face detector options: " + options);
detector = FaceDetection.getClient(options);
}
@Override
public void stop() {
super.stop();
detector.close();
}
@Override
protected Task<List<Face>> detectInImage(InputImage image) {
return detector.process(image);
}
@Override
protected void onSuccess(@NonNull List<Face> faces, @NonNull GraphicOverlay graphicOverlay) {
for (Face face : faces) {
graphicOverlay.add(new FaceGraphic(graphicOverlay, face));
logExtrasForTesting(face);
}
}
private static void logExtrasForTesting(Face face) {
if (face != null) {
Log.v(MANUAL_TESTING_LOG, "face bounding box: " + face.getBoundingBox().flattenToString());
Log.v(MANUAL_TESTING_LOG, "face Euler Angle X: " + face.getHeadEulerAngleX());
Log.v(MANUAL_TESTING_LOG, "face Euler Angle Y: " + face.getHeadEulerAngleY());
Log.v(MANUAL_TESTING_LOG, "face Euler Angle Z: " + face.getHeadEulerAngleZ());
// All landmarks
int[] landMarkTypes =
new int[]{
FaceLandmark.MOUTH_BOTTOM,
FaceLandmark.MOUTH_RIGHT,
FaceLandmark.MOUTH_LEFT,
FaceLandmark.RIGHT_EYE,
FaceLandmark.LEFT_EYE,
FaceLandmark.RIGHT_EAR,
FaceLandmark.LEFT_EAR,
FaceLandmark.RIGHT_CHEEK,
FaceLandmark.LEFT_CHEEK,
FaceLandmark.NOSE_BASE
};
String[] landMarkTypesStrings =
new String[]{
"MOUTH_BOTTOM",
"MOUTH_RIGHT",
"MOUTH_LEFT",
"RIGHT_EYE",
"LEFT_EYE",
"RIGHT_EAR",
"LEFT_EAR",
"RIGHT_CHEEK",
"LEFT_CHEEK",
"NOSE_BASE"
};
for (int i = 0; i < landMarkTypes.length; i++) {
FaceLandmark landmark = face.getLandmark(landMarkTypes[i]);
if (landmark == null) {
Log.v(
MANUAL_TESTING_LOG,
"No landmark of type: " + landMarkTypesStrings[i] + " has been detected");
} else {
PointF landmarkPosition = landmark.getPosition();
String landmarkPositionStr =
String.format(Locale.US, "x: %f , y: %f", landmarkPosition.x, landmarkPosition.y);
Log.v(
MANUAL_TESTING_LOG,
"Position for face landmark: "
+ landMarkTypesStrings[i]
+ " is :"
+ landmarkPositionStr);
}
}
Log.v(
MANUAL_TESTING_LOG,
"face left eye open probability: " + face.getLeftEyeOpenProbability());
Log.v(
MANUAL_TESTING_LOG,
"face right eye open probability: " + face.getRightEyeOpenProbability());
Log.v(MANUAL_TESTING_LOG, "face smiling probability: " + face.getSmilingProbability());
Log.v(MANUAL_TESTING_LOG, "face tracking id: " + face.getTrackingId());
}
}
@Override
protected void onFailure(@NonNull Exception e) {
Log.e(TAG, "Face detection failed " + e);
}
}
FaceGraphics.java
/*
* Copyright 2020 Google LLC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.mlkit.vision.demo.facedetector;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.Log;
import com.google.mlkit.vision.demo.GraphicOverlay;
import com.google.mlkit.vision.demo.GraphicOverlay.Graphic;
import com.google.mlkit.vision.face.Face;
import com.google.mlkit.vision.face.FaceContour;
import com.google.mlkit.vision.face.FaceLandmark;
import com.google.mlkit.vision.face.FaceLandmark.LandmarkType;
import java.util.Locale;
/**
* Graphic instance for rendering face position, contour, and landmarks within the associated
* graphic overlay view.
*/
public class FaceGraphic extends Graphic {
private static final float FACE_POSITION_RADIUS = 4.0f;
private static final float ID_TEXT_SIZE = 30.0f;
private static final float ID_Y_OFFSET = 40.0f;
private static final float ID_X_OFFSET = -40.0f;
private static final float BOX_STROKE_WIDTH = 5.0f;
private static final int NUM_COLORS = 10;
private static final int[][] COLORS = new int[][]{
// {Text color, background color}
{Color.BLACK, Color.WHITE},
{Color.WHITE, Color.MAGENTA},
{Color.BLACK, Color.LTGRAY},
{Color.WHITE, Color.RED},
{Color.WHITE, Color.BLUE},
{Color.WHITE, Color.DKGRAY},
{Color.BLACK, Color.CYAN},
{Color.BLACK, Color.YELLOW},
{Color.WHITE, Color.BLACK},
{Color.BLACK, Color.GREEN}
};
private final Paint facePositionPaint;
private final Paint[] idPaints;
private final Paint[] boxPaints;
private final Paint[] labelPaints;
private volatile Face face;
FaceGraphic(GraphicOverlay overlay, Face face) {
super(overlay);
this.face = face;
final int selectedColor = Color.WHITE;
facePositionPaint = new Paint();
facePositionPaint.setColor(selectedColor);
int numColors = COLORS.length;
idPaints = new Paint[numColors];
boxPaints = new Paint[numColors];
labelPaints = new Paint[numColors];
for (int i = 0; i < numColors; i++) {
idPaints[i] = new Paint();
idPaints[i].setColor(COLORS[i][0] /* text color */);
idPaints[i].setTextSize(ID_TEXT_SIZE);
boxPaints[i] = new Paint();
boxPaints[i].setColor(COLORS[i][1] /* background color */);
boxPaints[i].setStyle(Paint.Style.STROKE);
boxPaints[i].setStrokeWidth(BOX_STROKE_WIDTH);
labelPaints[i] = new Paint();
labelPaints[i].setColor(COLORS[i][1] /* background color */);
labelPaints[i].setStyle(Paint.Style.FILL);
}
}
/**
* Draws the face annotations for position on the supplied canvas.
*/
@Override
public void draw(Canvas canvas) {
Face face = this.face;
if (face == null) {
return;
}
// Draws a circle at the position of the detected face, with the face's track id below.
float x0 = translateX(face.getBoundingBox().centerX());
float y0 = translateY(face.getBoundingBox().centerY());
// canvas.drawCircle(x0, y0, FACE_POSITION_RADIUS, facePositionPaint);
// Calculate positions.
float left = x0 - scale(face.getBoundingBox().width() / 2.0f);
float top = y0 - scale(face.getBoundingBox().height() / 2.0f);
float right = x0 + scale(face.getBoundingBox().width() / 2.0f);
float bottom = y0 + scale(face.getBoundingBox().height() / 2.0f);
float lineHeight = ID_TEXT_SIZE + BOX_STROKE_WIDTH;
float yLabelOffset = -lineHeight;
// Decide color based on face ID
int colorID = (face.getTrackingId() == null)
? 0 : Math.abs(face.getTrackingId() % NUM_COLORS);
/**
// Calculate width and height of label box
float textWidth = idPaints[colorID].measureText("ID: " + face.getTrackingId());
if (face.getSmilingProbability() != null) {
yLabelOffset -= lineHeight;
textWidth = Math.max(textWidth, idPaints[colorID].measureText(
String.format(Locale.US, "Happiness: %.2f", face.getSmilingProbability())));
}
if (face.getLeftEyeOpenProbability() != null) {
yLabelOffset -= lineHeight;
textWidth = Math.max(textWidth, idPaints[colorID].measureText(
String.format(Locale.US, "Left eye: %.2f", face.getLeftEyeOpenProbability())));
}
if (face.getRightEyeOpenProbability() != null) {
yLabelOffset -= lineHeight;
textWidth = Math.max(textWidth, idPaints[colorID].measureText(
String.format(Locale.US, "Right eye: %.2f", face.getLeftEyeOpenProbability())));
}
// Draw labels
canvas.drawRect(left - BOX_STROKE_WIDTH,
top + yLabelOffset,
left + textWidth + (2 * BOX_STROKE_WIDTH),
top,
labelPaints[colorID]);
yLabelOffset += ID_TEXT_SIZE;
canvas.drawRect(left, top, right, bottom, boxPaints[colorID]);
canvas.drawText("ID: " + face.getTrackingId(), left, top + yLabelOffset,
idPaints[colorID]);
yLabelOffset += lineHeight;
**/
/**
// Draws all face contours.
for (FaceContour contour : face.getAllContours()) {
for (PointF point : contour.getPoints()) {
canvas.drawCircle(
translateX(point.x), translateY(point.y), FACE_POSITION_RADIUS, facePositionPaint);
}
}
**/
FaceContour contour = face.getContour(FaceContour.NOSE_BRIDGE);
float x1 = 0, y1 = 0;
for (PointF point : contour.getPoints()) {
// canvas.drawCircle(translateX(point.x), translateY(point.y), FACE_POSITION_RADIUS, facePositionPaint);
x1 = translateX(point.x);
y1 = translateY(point.y);
break;
}
/**
// Draws smiling and left/right eye open probabilities.
if (face.getSmilingProbability() != null) {
canvas.drawText(
"Smiling: " + String.format(Locale.US, "%.2f", face.getSmilingProbability()),
left,
top + yLabelOffset,
idPaints[colorID]);
yLabelOffset += lineHeight;
}
**/
/**
FaceLandmark leftEye = face.getLandmark(FaceLandmark.LEFT_EYE);
if (leftEye != null && face.getLeftEyeOpenProbability() != null) {
canvas.drawText(
"Left eye open: " + String.format(Locale.US, "%.2f", face.getLeftEyeOpenProbability()),
translateX(leftEye.getPosition().x) + ID_X_OFFSET,
translateY(leftEye.getPosition().y) + ID_Y_OFFSET,
idPaints[colorID]);
} else if (leftEye != null && face.getLeftEyeOpenProbability() == null) {
canvas.drawText(
"Left eye",
left,
top + yLabelOffset,
idPaints[colorID]);
yLabelOffset += lineHeight;
} else if (leftEye == null && face.getLeftEyeOpenProbability() != null) {
canvas.drawText(
"Left eye open: " + String.format(Locale.US, "%.2f", face.getLeftEyeOpenProbability()),
left,
top + yLabelOffset,
idPaints[colorID]);
yLabelOffset += lineHeight;
}
FaceLandmark rightEye = face.getLandmark(FaceLandmark.RIGHT_EYE);
if (rightEye != null && face.getRightEyeOpenProbability() != null) {
canvas.drawText(
"Right eye open: " + String.format(Locale.US, "%.2f", face.getRightEyeOpenProbability()),
translateX(rightEye.getPosition().x) + ID_X_OFFSET,
translateY(rightEye.getPosition().y) + ID_Y_OFFSET,
idPaints[colorID]);
} else if (rightEye != null && face.getRightEyeOpenProbability() == null) {
canvas.drawText(
"Right eye",
left,
top + yLabelOffset,
idPaints[colorID]);
yLabelOffset += lineHeight;
} else if (rightEye == null && face.getRightEyeOpenProbability() != null) {
canvas.drawText(
"Right eye open: " + String.format(Locale.US, "%.2f", face.getRightEyeOpenProbability()),
left,
top + yLabelOffset,
idPaints[colorID]);
}
**/
/**
// Draw facial landmarks
drawFaceLandmark(canvas, FaceLandmark.LEFT_EYE);
drawFaceLandmark(canvas, FaceLandmark.RIGHT_EYE);
drawFaceLandmark(canvas, FaceLandmark.LEFT_CHEEK);
drawFaceLandmark(canvas, FaceLandmark.RIGHT_CHEEK);
**/
}
private void drawFaceLandmark(Canvas canvas, @LandmarkType int landmarkType) {
FaceLandmark faceLandmark = face.getLandmark(landmarkType);
if (faceLandmark != null) {
canvas.drawCircle(
translateX(faceLandmark.getPosition().x),
translateY(faceLandmark.getPosition().y),
FACE_POSITION_RADIUS,
facePositionPaint);
}
}
}
В приведенном выше коде я хочу получить доступ к текущему обрабатываемому кадру. Либо в LivePreviewActivity, либо в FaceGraphics. java.
Пожалуйста, помогите мне.