added posetracking solution api - wip
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.mediapipe.apps.posetrackingsolutiongpu">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="21"
|
||||
android:targetSdkVersion="27" />
|
||||
|
||||
<!-- For using the camera -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
|
||||
<!-- For profiling -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="${appName}"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name="${mainActivity}"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- <meta-data android:name="cameraFacingFront" android:value="${cameraFacingFront}"/>-->
|
||||
<!-- <meta-data android:name="binaryGraphName" android:value="${binaryGraphName}"/>-->
|
||||
<!-- <meta-data android:name="inputVideoStreamName" android:value="${inputVideoStreamName}"/>-->
|
||||
<!-- <meta-data android:name="outputVideoStreamName" android:value="${outputVideoStreamName}"/>-->
|
||||
<!-- <meta-data android:name="flipFramesVertically" android:value="${flipFramesVertically}"/>-->
|
||||
<!-- <meta-data android:name="converterNumBuffers" android:value="${converterNumBuffers}"/>-->
|
||||
</application>
|
||||
</manifest>
|
|
@ -0,0 +1,95 @@
|
|||
# Copyright 2019 The MediaPipe Authors.
|
||||
#
|
||||
# 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.
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
# Basic library common across example apps.
|
||||
android_library(
|
||||
name = "basic_lib",
|
||||
srcs = glob(["*.java"]),
|
||||
manifest = "AndroidManifest.xml",
|
||||
resource_files = glob(["res/**"]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//mediapipe/framework/formats:detection_java_proto_lite",
|
||||
"//mediapipe/framework/formats:location_data_java_proto_lite",
|
||||
"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper",
|
||||
"//mediapipe/java/com/google/mediapipe/components:android_components",
|
||||
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
||||
"//mediapipe/java/com/google/mediapipe/glutil",
|
||||
"//mediapipe/java/com/google/mediapipe/solutioncore:camera_input",
|
||||
"//mediapipe/java/com/google/mediapipe/solutioncore:solution_rendering",
|
||||
"//mediapipe/java/com/google/mediapipe/solutions/facedetection",
|
||||
"//mediapipe/java/com/google/mediapipe/solutions/posetracking",
|
||||
"//third_party:androidx_appcompat",
|
||||
"//third_party:androidx_constraint_layout",
|
||||
"//third_party:opencv",
|
||||
"@maven//:androidx_concurrent_concurrent_futures",
|
||||
"@maven//:com_google_guava_guava",
|
||||
],
|
||||
)
|
||||
|
||||
# Manifest common across example apps.
|
||||
exports_files(
|
||||
srcs = ["AndroidManifest.xml"],
|
||||
)
|
||||
|
||||
# Native dependencies to perform edge detection in the Hello World example.
|
||||
cc_binary(
|
||||
name = "libmediapipe_jni.so",
|
||||
linkshared = 1,
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
"//mediapipe/graphs/edge_detection:mobile_calculators",
|
||||
"//mediapipe/java/com/google/mediapipe/framework/jni:mediapipe_framework_jni",
|
||||
#facedetection deps
|
||||
"//mediapipe/graphs/face_detection:face_detection_full_range_mobile_gpu_deps",
|
||||
"//mediapipe/graphs/face_detection:mobile_calculators",
|
||||
#pose tracking deps
|
||||
"//mediapipe/graphs/pose_tracking:pose_tracking_gpu_deps",
|
||||
],
|
||||
)
|
||||
|
||||
# Converts the .so cc_binary into a cc_library, to be consumed in an android_binary.
|
||||
cc_library(
|
||||
name = "mediapipe_jni_lib",
|
||||
srcs = [":libmediapipe_jni.so"],
|
||||
alwayslink = 1,
|
||||
)
|
||||
|
||||
# Hello World example app.
|
||||
android_binary(
|
||||
name = "helloworld",
|
||||
# assets = [
|
||||
# "//mediapipe/graphs/edge_detection:mobile_gpu.binarypb",
|
||||
# ],
|
||||
# assets_dir = "",
|
||||
manifest = "AndroidManifest.xml",
|
||||
manifest_values = {
|
||||
"applicationId": "com.google.mediapipe.apps.posetrackingsolutiongpu",
|
||||
"appName": "Hello World",
|
||||
"mainActivity": ".MainActivity",
|
||||
# "cameraFacingFront": "False",
|
||||
# "binaryGraphName": "mobile_gpu.binarypb",
|
||||
# "inputVideoStreamName": "input_video",
|
||||
# "outputVideoStreamName": "output_video",
|
||||
# "flipFramesVertically": "True",
|
||||
# "converterNumBuffers": "2",
|
||||
},
|
||||
multidex = "native",
|
||||
deps = [
|
||||
":basic_lib",
|
||||
":mediapipe_jni_lib",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2021 The MediaPipe Authors.
|
||||
//
|
||||
// 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.mediapipe.apps.posetrackingsolutiongpu;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import com.google.mediapipe.formats.proto.DetectionProto.Detection;
|
||||
import com.google.mediapipe.solutions.facedetection.FaceDetectionResult;
|
||||
import com.google.mediapipe.solutions.facedetection.FaceKeypoint;
|
||||
|
||||
/** An ImageView implementation for displaying {@link FaceDetectionResult}. */
|
||||
public class FaceDetectionResultImageView extends AppCompatImageView {
|
||||
private static final String TAG = "FaceDetectionResultImageView";
|
||||
|
||||
private static final int KEYPOINT_COLOR = Color.RED;
|
||||
private static final int KEYPOINT_RADIUS = 8; // Pixels
|
||||
private static final int BBOX_COLOR = Color.GREEN;
|
||||
private static final int BBOX_THICKNESS = 5; // Pixels
|
||||
private Bitmap latest;
|
||||
|
||||
public FaceDetectionResultImageView(Context context) {
|
||||
super(context);
|
||||
setScaleType(ScaleType.FIT_CENTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link FaceDetectionResult} to render.
|
||||
*
|
||||
* @param result a {@link FaceDetectionResult} object that contains the solution outputs and the
|
||||
* input {@link Bitmap}.
|
||||
*/
|
||||
public void setFaceDetectionResult(FaceDetectionResult result) {
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
Bitmap bmInput = result.inputBitmap();
|
||||
int width = bmInput.getWidth();
|
||||
int height = bmInput.getHeight();
|
||||
latest = Bitmap.createBitmap(width, height, bmInput.getConfig());
|
||||
Canvas canvas = new Canvas(latest);
|
||||
|
||||
canvas.drawBitmap(bmInput, new Matrix(), null);
|
||||
int numDetectedFaces = result.multiFaceDetections().size();
|
||||
for (int i = 0; i < numDetectedFaces; ++i) {
|
||||
drawDetectionOnCanvas(result.multiFaceDetections().get(i), canvas, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the image view with the latest {@link FaceDetectionResult}. */
|
||||
public void update() {
|
||||
postInvalidate();
|
||||
if (latest != null) {
|
||||
setImageBitmap(latest);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawDetectionOnCanvas(Detection detection, Canvas canvas, int width, int height) {
|
||||
if (!detection.hasLocationData()) {
|
||||
return;
|
||||
}
|
||||
// Draw keypoints.
|
||||
Paint keypointPaint = new Paint();
|
||||
keypointPaint.setColor(KEYPOINT_COLOR);
|
||||
for (int i = 0; i < FaceKeypoint.NUM_KEY_POINTS; ++i) {
|
||||
int xPixel =
|
||||
min(
|
||||
(int) (detection.getLocationData().getRelativeKeypoints(i).getX() * width),
|
||||
width - 1);
|
||||
int yPixel =
|
||||
min(
|
||||
(int) (detection.getLocationData().getRelativeKeypoints(i).getY() * height),
|
||||
height - 1);
|
||||
canvas.drawCircle(xPixel, yPixel, KEYPOINT_RADIUS, keypointPaint);
|
||||
}
|
||||
if (!detection.getLocationData().hasRelativeBoundingBox()) {
|
||||
return;
|
||||
}
|
||||
// Draw bounding box.
|
||||
Paint bboxPaint = new Paint();
|
||||
bboxPaint.setColor(BBOX_COLOR);
|
||||
bboxPaint.setStyle(Paint.Style.STROKE);
|
||||
bboxPaint.setStrokeWidth(BBOX_THICKNESS);
|
||||
float left = detection.getLocationData().getRelativeBoundingBox().getXmin() * width;
|
||||
float top = detection.getLocationData().getRelativeBoundingBox().getYmin() * height;
|
||||
float right = left + detection.getLocationData().getRelativeBoundingBox().getWidth() * width;
|
||||
float bottom = top + detection.getLocationData().getRelativeBoundingBox().getHeight() * height;
|
||||
canvas.drawRect(left, top, right, bottom, bboxPaint);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
// Copyright 2019 The MediaPipe Authors.
|
||||
//
|
||||
// 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.mediapipe.apps.posetrackingsolutiongpu;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.google.mediapipe.components.CameraHelper;
|
||||
import com.google.mediapipe.components.CameraXPreviewHelper;
|
||||
import com.google.mediapipe.components.ExternalTextureConverter;
|
||||
import com.google.mediapipe.components.FrameProcessor;
|
||||
import com.google.mediapipe.components.PermissionHelper;
|
||||
import com.google.mediapipe.formats.proto.LocationDataProto;
|
||||
import com.google.mediapipe.framework.AndroidAssetUtil;
|
||||
import com.google.mediapipe.glutil.EglManager;
|
||||
import com.google.mediapipe.solutioncore.CameraInput;
|
||||
import com.google.mediapipe.solutioncore.SolutionGlSurfaceView;
|
||||
import com.google.mediapipe.solutions.facedetection.FaceDetection;
|
||||
import com.google.mediapipe.solutions.facedetection.FaceDetectionOptions;
|
||||
import com.google.mediapipe.solutions.posetracking.PoseTracking;
|
||||
import com.google.mediapipe.solutions.posetracking.PoseTrackingOptions;
|
||||
import com.google.mediapipe.solutions.posetracking.PoseTrackingResult;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/** Main activity of MediaPipe basic app. */
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
// Flips the camera-preview frames vertically by default, before sending them into FrameProcessor
|
||||
// to be processed in a MediaPipe graph, and flips the processed frames back when they are
|
||||
// displayed. This maybe needed because OpenGL represents images assuming the image origin is at
|
||||
// the bottom-left corner, whereas MediaPipe in general assumes the image origin is at the
|
||||
// top-left corner.
|
||||
// NOTE: use "flipFramesVertically" in manifest metadata to override this behavior.
|
||||
private static final boolean FLIP_FRAMES_VERTICALLY = true;
|
||||
|
||||
// Number of output frames allocated in ExternalTextureConverter.
|
||||
// NOTE: use "converterNumBuffers" in manifest metadata to override number of buffers. For
|
||||
// example, when there is a FlowLimiterCalculator in the graph, number of buffers should be at
|
||||
// least `max_in_flight + max_in_queue + 1` (where max_in_flight and max_in_queue are used in
|
||||
// FlowLimiterCalculator options). That's because we need buffers for all the frames that are in
|
||||
// flight/queue plus one for the next frame from the camera.
|
||||
private static final int NUM_BUFFERS = 2;
|
||||
|
||||
static {
|
||||
// Load all native libraries needed by the app.
|
||||
System.loadLibrary("mediapipe_jni");
|
||||
try {
|
||||
System.loadLibrary("opencv_java3");
|
||||
} catch (java.lang.UnsatisfiedLinkError e) {
|
||||
// Some example apps (e.g. template matching) require OpenCV 4.
|
||||
System.loadLibrary("opencv_java4");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(getContentViewLayoutResId());
|
||||
|
||||
PoseTrackingOptions poseTrackingOptions = PoseTrackingOptions.builder()
|
||||
.setStaticImageMode(false).build();
|
||||
PoseTracking poseTracking = new PoseTracking(this,poseTrackingOptions);
|
||||
|
||||
poseTracking.setErrorListener(
|
||||
(message, e) -> Log.e(TAG, "MediaPipe Face Detection error:" + message));
|
||||
CameraInput cameraInput = new CameraInput(this);
|
||||
|
||||
|
||||
cameraInput.setNewFrameListener(
|
||||
textureFrame -> poseTracking.send(textureFrame));
|
||||
SolutionGlSurfaceView<PoseTrackingResult> glSurfaceView =
|
||||
new SolutionGlSurfaceView<>(
|
||||
this, poseTracking.getGlContext(), poseTracking.getGlMajorVersion());
|
||||
glSurfaceView.setSolutionResultRenderer(new PoseTrackingResultGlRenderer());
|
||||
glSurfaceView.setRenderInputImage(true);
|
||||
|
||||
poseTracking.setResultListener(
|
||||
faceDetectionResult -> {
|
||||
if (faceDetectionResult.multiPoseTrackings().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LocationDataProto.LocationData locationData = faceDetectionResult
|
||||
.multiPoseTrackings()
|
||||
.get(0)
|
||||
.getLocationData();
|
||||
// .getRelativeKeypoints(FaceKeypoint.NOSE_TIP);
|
||||
Log.i(
|
||||
TAG, locationData.toString());
|
||||
// String.format(
|
||||
// "MediaPipe Face Detection nose tip normalized coordinates (value range: [0, 1]): x=%f, y=%f",
|
||||
// noseTip.getX(), noseTip.getY()));
|
||||
// Request GL rendering.
|
||||
glSurfaceView.setRenderData(faceDetectionResult);
|
||||
glSurfaceView.requestRender();
|
||||
});
|
||||
// The runnable to start camera after the GLSurfaceView is attached.
|
||||
glSurfaceView.post(
|
||||
() ->
|
||||
cameraInput.start(
|
||||
this,
|
||||
poseTracking.getGlContext(),
|
||||
CameraInput.CameraFacing.FRONT,
|
||||
glSurfaceView.getWidth(),
|
||||
glSurfaceView.getHeight()));
|
||||
glSurfaceView.setVisibility(View.VISIBLE);
|
||||
FrameLayout frameLayout = findViewById(R.id.preview_display_layout);
|
||||
frameLayout.removeAllViewsInLayout();
|
||||
frameLayout.addView(glSurfaceView);
|
||||
glSurfaceView.setVisibility(View.VISIBLE);
|
||||
frameLayout.requestLayout();
|
||||
}
|
||||
|
||||
|
||||
// Used to obtain the content view for this application. If you are extending this class, and
|
||||
// have a custom layout, override this method and return the custom layout.
|
||||
protected int getContentViewLayoutResId() {
|
||||
return R.layout.activity_main;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright 2021 The MediaPipe Authors.
|
||||
//
|
||||
// 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.mediapipe.apps.posetrackingsolutiongpu;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
|
||||
import com.google.mediapipe.formats.proto.DetectionProto.Detection;
|
||||
import com.google.mediapipe.solutioncore.ResultGlRenderer;
|
||||
import com.google.mediapipe.solutions.posetracking.PoseTrackingResult;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
/** A custom implementation of {@link ResultGlRenderer} to render {@link PoseTrackingResult}. */
|
||||
public class PoseTrackingResultGlRenderer implements ResultGlRenderer<PoseTrackingResult> {
|
||||
private static final String TAG = "PoseTrackingResultGlRenderer";
|
||||
|
||||
private static final float[] KEYPOINT_COLOR = new float[] {1f, 0f, 0f, 1f};
|
||||
private static final float KEYPOINT_SIZE = 16f;
|
||||
private static final float[] BBOX_COLOR = new float[] {0f, 1f, 0f, 1f};
|
||||
private static final int BBOX_THICKNESS = 8;
|
||||
private static final String VERTEX_SHADER =
|
||||
"uniform mat4 uProjectionMatrix;\n"
|
||||
+ "uniform float uPointSize;\n"
|
||||
+ "attribute vec4 vPosition;\n"
|
||||
+ "void main() {\n"
|
||||
+ " gl_Position = uProjectionMatrix * vPosition;\n"
|
||||
+ " gl_PointSize = uPointSize;"
|
||||
+ "}";
|
||||
private static final String FRAGMENT_SHADER =
|
||||
"precision mediump float;\n"
|
||||
+ "uniform vec4 uColor;\n"
|
||||
+ "void main() {\n"
|
||||
+ " gl_FragColor = uColor;\n"
|
||||
+ "}";
|
||||
private int program;
|
||||
private int positionHandle;
|
||||
private int pointSizeHandle;
|
||||
private int projectionMatrixHandle;
|
||||
private int colorHandle;
|
||||
|
||||
private int loadShader(int type, String shaderCode) {
|
||||
int shader = GLES20.glCreateShader(type);
|
||||
GLES20.glShaderSource(shader, shaderCode);
|
||||
GLES20.glCompileShader(shader);
|
||||
return shader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupRendering() {
|
||||
program = GLES20.glCreateProgram();
|
||||
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER);
|
||||
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER);
|
||||
GLES20.glAttachShader(program, vertexShader);
|
||||
GLES20.glAttachShader(program, fragmentShader);
|
||||
GLES20.glLinkProgram(program);
|
||||
positionHandle = GLES20.glGetAttribLocation(program, "vPosition");
|
||||
pointSizeHandle = GLES20.glGetUniformLocation(program, "uPointSize");
|
||||
projectionMatrixHandle = GLES20.glGetUniformLocation(program, "uProjectionMatrix");
|
||||
colorHandle = GLES20.glGetUniformLocation(program, "uColor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderResult(PoseTrackingResult result, float[] projectionMatrix) {
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
GLES20.glUseProgram(program);
|
||||
GLES20.glUniformMatrix4fv(projectionMatrixHandle, 1, false, projectionMatrix, 0);
|
||||
GLES20.glUniform1f(pointSizeHandle, KEYPOINT_SIZE);
|
||||
int numDetectedFaces = result.multiPoseTrackings().size();
|
||||
for (int i = 0; i < numDetectedFaces; ++i) {
|
||||
drawDetection(result.multiPoseTrackings().get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the shader program.
|
||||
*
|
||||
* <p>This is only necessary if one wants to release the program while keeping the context around.
|
||||
*/
|
||||
public void release() {
|
||||
GLES20.glDeleteProgram(program);
|
||||
}
|
||||
|
||||
private void drawDetection(Detection detection) {
|
||||
if (!detection.hasLocationData()) {
|
||||
return;
|
||||
}
|
||||
// Draw keypoints.
|
||||
// float[] points = new float[FaceKeypoint.NUM_KEY_POINTS * 2];
|
||||
// for (int i = 0; i < FaceKeypoint.NUM_KEY_POINTS; ++i) {
|
||||
// points[2 * i] = detection.getLocationData().getRelativeKeypoints(i).getX();
|
||||
// points[2 * i + 1] = detection.getLocationData().getRelativeKeypoints(i).getY();
|
||||
// }
|
||||
GLES20.glUniform4fv(colorHandle, 1, KEYPOINT_COLOR, 0);
|
||||
// FloatBuffer vertexBuffer =
|
||||
// ByteBuffer.allocateDirect(points.length * 4)
|
||||
// .order(ByteOrder.nativeOrder())
|
||||
// .asFloatBuffer()
|
||||
// .put(points);
|
||||
// vertexBuffer.position(0);
|
||||
GLES20.glEnableVertexAttribArray(positionHandle);
|
||||
// GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer);
|
||||
// GLES20.glDrawArrays(GLES20.GL_POINTS, 0, FaceKeypoint.NUM_KEY_POINTS);
|
||||
if (!detection.getLocationData().hasRelativeBoundingBox()) {
|
||||
return;
|
||||
}
|
||||
// Draw bounding box.
|
||||
float left = detection.getLocationData().getRelativeBoundingBox().getXmin();
|
||||
float top = detection.getLocationData().getRelativeBoundingBox().getYmin();
|
||||
float right = left + detection.getLocationData().getRelativeBoundingBox().getWidth();
|
||||
float bottom = top + detection.getLocationData().getRelativeBoundingBox().getHeight();
|
||||
drawLine(top, left, top, right);
|
||||
drawLine(bottom, left, bottom, right);
|
||||
drawLine(top, left, bottom, left);
|
||||
drawLine(top, right, bottom, right);
|
||||
}
|
||||
|
||||
private void drawLine(float y1, float x1, float y2, float x2) {
|
||||
GLES20.glUniform4fv(colorHandle, 1, BBOX_COLOR, 0);
|
||||
GLES20.glLineWidth(BBOX_THICKNESS);
|
||||
float[] vertex = {x1, y1, x2, y2};
|
||||
FloatBuffer vertexBuffer =
|
||||
ByteBuffer.allocateDirect(vertex.length * 4)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer()
|
||||
.put(vertex);
|
||||
vertexBuffer.position(0);
|
||||
GLES20.glEnableVertexAttribArray(positionHandle);
|
||||
GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer);
|
||||
GLES20.glDrawArrays(GLES20.GL_LINES, 0, 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0" />
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1" />
|
||||
</vector>
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/preview_display_layout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1">
|
||||
<TextView
|
||||
android:id="@+id/no_camera_access_view"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="fill_parent"
|
||||
android:gravity="center"
|
||||
android:text="@string/no_camera_access" />
|
||||
</FrameLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 959 B |
After Width: | Height: | Size: 900 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
</resources>
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="no_camera_access" translatable="false">Please grant camera permissions.</string>
|
||||
</resources>
|
|
@ -0,0 +1,11 @@
|
|||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -19,6 +19,7 @@ node {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
# Throttles the images flowing downstream for flow control. It passes through
|
||||
# the very first incoming image unaltered, and waits for downstream nodes
|
||||
# (calculators and subgraphs) in the graph to finish their tasks before it
|
||||
|
@ -29,6 +30,9 @@ node {
|
|||
# real-time mobile applications. It also eliminates unnecessarily computation,
|
||||
# e.g., the output produced by a node may get dropped downstream if the
|
||||
# subsequent nodes are still busy processing previous inputs.
|
||||
|
||||
|
||||
|
||||
node {
|
||||
calculator: "FlowLimiterCalculator"
|
||||
input_stream: "input_video"
|
||||
|
@ -37,9 +41,18 @@ node {
|
|||
tag_index: "FINISHED"
|
||||
back_edge: true
|
||||
}
|
||||
output_stream: "throttled_input_video"
|
||||
output_stream: "throttled_input_video_cpu"
|
||||
}
|
||||
|
||||
# Converts Image to GpuBuffer for PoseLandmarkGPU to consume.
|
||||
node {
|
||||
calculator: "FromImageCalculator"
|
||||
input_stream: "IMAGE:throttled_input_video_cpu"
|
||||
output_stream: "IMAGE_GPU:throttled_input_video"
|
||||
output_stream: "SOURCE_ON_GPU:is_gpu_image"
|
||||
}
|
||||
|
||||
|
||||
# Subgraph that detects poses and corresponding landmarks.
|
||||
node {
|
||||
calculator: "PoseLandmarkGpu"
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.mediapipe.solutions.posetracking">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21"
|
||||
android:targetSdkVersion="27" />
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,50 @@
|
|||
# Copyright 2021 The MediaPipe Authors.
|
||||
#
|
||||
# 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.
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
android_library(
|
||||
name = "posetracking",
|
||||
srcs = [
|
||||
"PersonKeypoint.java",
|
||||
"PoseTracking.java",
|
||||
"PoseTrackingOptions.java",
|
||||
"PoseTrackingResult.java",
|
||||
],
|
||||
assets = [
|
||||
"//mediapipe/modules/face_detection:face_detection_full_range_image.binarypb",
|
||||
"//mediapipe/modules/face_detection:face_detection_full_range_sparse.tflite",
|
||||
"//mediapipe/modules/face_detection:face_detection_short_range.tflite",
|
||||
"//mediapipe/modules/face_detection:face_detection_short_range_image.binarypb",
|
||||
"//mediapipe/graphs/pose_tracking:pose_tracking_gpu.binarypb",
|
||||
"//mediapipe/modules/pose_landmark:pose_landmark_heavy.tflite",
|
||||
"//mediapipe/modules/pose_landmark:pose_landmark_full.tflite",
|
||||
"//mediapipe/modules/pose_landmark:pose_landmark_lite.tflite",
|
||||
"//mediapipe/modules/pose_detection:pose_detection.tflite",
|
||||
],
|
||||
assets_dir = "",
|
||||
javacopts = ["-Acom.google.auto.value.AutoBuilderIsUnstable"],
|
||||
manifest = ":AndroidManifest.xml",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//mediapipe/framework/formats:detection_java_proto_lite",
|
||||
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
||||
"//mediapipe/java/com/google/mediapipe/solutioncore:camera_input",
|
||||
"//mediapipe/java/com/google/mediapipe/solutioncore:solution_base",
|
||||
"//third_party:autovalue",
|
||||
"@maven//:androidx_annotation_annotation",
|
||||
"@maven//:com_google_code_findbugs_jsr305",
|
||||
"@maven//:com_google_guava_guava",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2021 The MediaPipe Authors.
|
||||
//
|
||||
// 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.mediapipe.solutions.posetracking;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
/** The 6 face keypoints. */
|
||||
public final class PersonKeypoint {
|
||||
public static final int NUM_KEY_POINTS = 6;
|
||||
|
||||
public static final int RIGHT_EYE = 0;
|
||||
public static final int LEFT_EYE = 1;
|
||||
public static final int NOSE_TIP = 2;
|
||||
public static final int MOUTH_CENTER = 3;
|
||||
public static final int RIGHT_EAR_TRAGION = 4;
|
||||
public static final int LEFT_EAR_TRAGION = 5;
|
||||
|
||||
/** Represents a face keypoint type. */
|
||||
@IntDef({
|
||||
RIGHT_EYE,
|
||||
LEFT_EYE,
|
||||
NOSE_TIP,
|
||||
MOUTH_CENTER,
|
||||
RIGHT_EAR_TRAGION,
|
||||
LEFT_EAR_TRAGION,
|
||||
})
|
||||
public @interface FaceKeypointType {}
|
||||
|
||||
private PersonKeypoint() {}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2021 The MediaPipe Authors.
|
||||
//
|
||||
// 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.mediapipe.solutions.posetracking;
|
||||
|
||||
import android.content.Context;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.mediapipe.framework.MediaPipeException;
|
||||
import com.google.mediapipe.framework.Packet;
|
||||
import com.google.mediapipe.solutioncore.ErrorListener;
|
||||
import com.google.mediapipe.solutioncore.ImageSolutionBase;
|
||||
import com.google.mediapipe.solutioncore.OutputHandler;
|
||||
import com.google.mediapipe.solutioncore.ResultListener;
|
||||
import com.google.mediapipe.solutioncore.SolutionInfo;
|
||||
import com.google.mediapipe.formats.proto.DetectionProto.Detection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* MediaPipe Face Detection Solution API.
|
||||
*
|
||||
* <p>MediaPipe Face Detection processes a {@link TextureFrame} or a {@link Bitmap} and returns the
|
||||
* {@link PoseTrackingResult} representing each detected face. Please refer to
|
||||
* https://solutions.mediapipe.dev/face_detection#android-solution-api for usage examples.
|
||||
*/
|
||||
public class PoseTracking extends ImageSolutionBase {
|
||||
private static final String TAG = "PoseTracking";
|
||||
|
||||
private static final String SHORT_RANGE_GRAPH_NAME = "pose_tracking_gpu.binarypb";
|
||||
private static final String FULL_RANGE_GRAPH_NAME = "face_detection_full_range_image.binarypb";
|
||||
private static final String IMAGE_INPUT_STREAM = "input_video";
|
||||
private static final ImmutableList<String> OUTPUT_STREAMS =
|
||||
ImmutableList.of("pose_detection", "throttled_input_video");
|
||||
private static final int DETECTIONS_INDEX = 0;
|
||||
private static final int INPUT_IMAGE_INDEX = 1;
|
||||
private final OutputHandler<PoseTrackingResult> outputHandler;
|
||||
|
||||
/**
|
||||
* Initializes MediaPipe Face Detection solution.
|
||||
*
|
||||
* @param context an Android {@link Context}.
|
||||
* @param options the configuration options defined in {@link PoseTrackingOptions}.
|
||||
*/
|
||||
public PoseTracking(Context context, PoseTrackingOptions options) {
|
||||
outputHandler = new OutputHandler<>();
|
||||
outputHandler.setOutputConverter(
|
||||
packets -> {
|
||||
PoseTrackingResult.Builder poseTrackingResultBuilder = PoseTrackingResult.builder();
|
||||
try {
|
||||
poseTrackingResultBuilder.setMultiPoseTrackings(
|
||||
getProtoVector(packets.get(DETECTIONS_INDEX), Detection.parser()));
|
||||
} catch (MediaPipeException e) {
|
||||
reportError("Error occurs while getting MediaPipe pose tracking results.", e);
|
||||
}
|
||||
return poseTrackingResultBuilder
|
||||
.setImagePacket(packets.get(INPUT_IMAGE_INDEX))
|
||||
.setTimestamp(
|
||||
staticImageMode ? Long.MIN_VALUE : packets.get(INPUT_IMAGE_INDEX).getTimestamp())
|
||||
.build();
|
||||
});
|
||||
|
||||
SolutionInfo solutionInfo =
|
||||
SolutionInfo.builder()
|
||||
.setBinaryGraphPath(
|
||||
options.modelSelection() == 0 ? SHORT_RANGE_GRAPH_NAME : FULL_RANGE_GRAPH_NAME)
|
||||
.setImageInputStreamName(IMAGE_INPUT_STREAM)
|
||||
.setOutputStreamNames(OUTPUT_STREAMS)
|
||||
.setStaticImageMode(options.staticImageMode())
|
||||
.build();
|
||||
|
||||
initialize(context, solutionInfo, outputHandler);
|
||||
Map<String, Packet> emptyInputSidePackets = new HashMap<>();
|
||||
start(emptyInputSidePackets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a callback to be invoked when a {@link PoseTrackingResult} becomes available.
|
||||
*
|
||||
* @param listener the {@link ResultListener} callback.
|
||||
*/
|
||||
public void setResultListener(ResultListener<PoseTrackingResult> listener) {
|
||||
this.outputHandler.setResultListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a callback to be invoked when the Face Detection solution throws errors.
|
||||
*
|
||||
* @param listener the {@link ErrorListener} callback.
|
||||
*/
|
||||
public void setErrorListener(@Nullable ErrorListener listener) {
|
||||
this.outputHandler.setErrorListener(listener);
|
||||
this.errorListener = listener;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2021 The MediaPipe Authors.
|
||||
//
|
||||
// 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.mediapipe.solutions.posetracking;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
|
||||
/**
|
||||
* MediaPipe Face Detection solution-specific options.
|
||||
*
|
||||
* <p>staticImageMode: Whether to treat the input images as a batch of static and possibly unrelated
|
||||
* images, or a video stream. Default to false. See details in
|
||||
* https://solutions.mediapipe.dev/face_detection#static_image_mode.
|
||||
*
|
||||
* <p>minDetectionConfidence: Minimum confidence value ([0.0, 1.0]) for face detection to be
|
||||
* considered successful. See details in
|
||||
* https://solutions.mediapipe.dev/face_detection#min_detection_confidence.
|
||||
*
|
||||
* <p>modelSelection: 0 or 1. 0 to select a short-range model that works best for faces within 2
|
||||
* meters from the camera, and 1 for a full-range model best for faces within 5 meters. See details
|
||||
* in https://solutions.mediapipe.dev/face_detection#model_selection.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class PoseTrackingOptions {
|
||||
public abstract boolean staticImageMode();
|
||||
|
||||
public abstract int modelSelection();
|
||||
|
||||
public abstract float minDetectionConfidence();
|
||||
|
||||
public static Builder builder() {
|
||||
return new AutoValue_PoseTrackingOptions.Builder().withDefaultValues();
|
||||
}
|
||||
|
||||
/** Builder for {@link PoseTrackingOptions}. */
|
||||
@AutoValue.Builder
|
||||
public abstract static class Builder {
|
||||
public Builder withDefaultValues() {
|
||||
return setStaticImageMode(false).setModelSelection(0).setMinDetectionConfidence(0.5f);
|
||||
}
|
||||
|
||||
public abstract Builder setStaticImageMode(boolean value);
|
||||
|
||||
public abstract Builder setModelSelection(int value);
|
||||
|
||||
public abstract Builder setMinDetectionConfidence(float value);
|
||||
|
||||
public abstract PoseTrackingOptions build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2021 The MediaPipe Authors.
|
||||
//
|
||||
// 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.mediapipe.solutions.posetracking;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import com.google.auto.value.AutoBuilder;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.mediapipe.framework.Packet;
|
||||
import com.google.mediapipe.framework.TextureFrame;
|
||||
import com.google.mediapipe.solutioncore.ImageSolutionResult;
|
||||
import com.google.mediapipe.formats.proto.DetectionProto.Detection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* FaceDetectionResult contains the detected faces, and the input {@link Bitmap} or {@link
|
||||
* TextureFrame}. If not in static image mode, the timestamp field will be set to the timestamp of
|
||||
* the corresponding input image.
|
||||
*/
|
||||
public class PoseTrackingResult extends ImageSolutionResult {
|
||||
private final ImmutableList<Detection> multiPoseTrackings;
|
||||
|
||||
PoseTrackingResult(
|
||||
ImmutableList<Detection> multiPoseTrackings, Packet imagePacket, long timestamp) {
|
||||
this.multiPoseTrackings = multiPoseTrackings;
|
||||
this.timestamp = timestamp;
|
||||
this.imagePacket = imagePacket;
|
||||
}
|
||||
|
||||
// Collection of detected faces, where each face is represented as a detection proto message that
|
||||
// contains a bounding box and 6 {@link FaceKeypoint}s. The bounding box is composed of xmin and
|
||||
// width (both normalized to [0.0, 1.0] by the image width) and ymin and height (both normalized
|
||||
// to [0.0, 1.0] by the image height). Each keypoint is composed of x and y, which are normalized
|
||||
// to [0.0, 1.0] by the image width and height respectively.
|
||||
public ImmutableList<Detection> multiPoseTrackings() {
|
||||
return multiPoseTrackings;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new AutoBuilder_PoseTrackingResult_Builder();
|
||||
}
|
||||
|
||||
/** Builder for {@link PoseTrackingResult}. */
|
||||
@AutoBuilder
|
||||
public abstract static class Builder {
|
||||
abstract Builder setMultiPoseTrackings(List<Detection> value);
|
||||
|
||||
abstract Builder setTimestamp(long value);
|
||||
|
||||
abstract Builder setImagePacket(Packet value);
|
||||
|
||||
abstract PoseTrackingResult build();
|
||||
}
|
||||
}
|