Project import generated by Copybara.
GitOrigin-RevId: 612e50bb8db2ec3dc1c30049372d87a80c3848db
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
# Basic library common across example apps.
|
||||
android_library(
|
||||
|
||||
@@ -80,7 +80,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
setContentView(getContentViewLayoutResId());
|
||||
|
||||
try {
|
||||
applicationInfo =
|
||||
@@ -112,6 +112,12 @@ public class MainActivity extends AppCompatActivity {
|
||||
PermissionHelper.checkAndRequestCameraPermissions(this);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# Copyright 2020 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"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
java_lite_proto_library(
|
||||
name = "sticker_buffer_java_proto_lite",
|
||||
deps = ["//mediapipe/graphs/instant_motion_tracking/calculators:sticker_buffer_proto"],
|
||||
)
|
||||
|
||||
android_library(
|
||||
name = "instantmotiontracking_lib",
|
||||
srcs = glob(["*.java"]),
|
||||
manifest = "//mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:AndroidManifest.xml",
|
||||
resource_files = glob([
|
||||
"res/layout/**",
|
||||
"res/drawable/**",
|
||||
]),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":sticker_buffer_java_proto_lite",
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:basic_lib",
|
||||
"//mediapipe/java/com/google/mediapipe/components:android_components",
|
||||
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
||||
"//third_party:androidx_appcompat",
|
||||
"//third_party:androidx_core",
|
||||
"//third_party:opencv",
|
||||
"@maven//:androidx_concurrent_concurrent_futures",
|
||||
"@maven//:com_github_bumptech_glide_glide",
|
||||
"@maven//:com_google_guava_guava",
|
||||
],
|
||||
)
|
||||
|
||||
# Include all calculators specific to this project defined by BUILD in graphs
|
||||
cc_binary(
|
||||
name = "libmediapipe_jni.so",
|
||||
linkshared = 1,
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
"//mediapipe/graphs/instant_motion_tracking:instant_motion_tracking_deps",
|
||||
"//mediapipe/java/com/google/mediapipe/framework/jni:mediapipe_framework_jni",
|
||||
],
|
||||
)
|
||||
|
||||
# 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,
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "asset3d",
|
||||
srcs = ["//mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/assets:robot/robot.obj.uuu.zip"],
|
||||
outs = ["robot/robot.obj.uuu"],
|
||||
cmd = "unzip -p $< > $@",
|
||||
)
|
||||
|
||||
android_binary(
|
||||
name = "instantmotiontracking",
|
||||
assets = [
|
||||
":asset3d",
|
||||
"//mediapipe/graphs/instant_motion_tracking:instant_motion_tracking.binarypb",
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/assets:gif/gif.obj.uuu",
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/assets:gif/default_gif_texture.jpg",
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/assets:robot/robot_texture.jpg",
|
||||
],
|
||||
assets_dir = "",
|
||||
manifest = "//mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:AndroidManifest.xml",
|
||||
manifest_values = {
|
||||
"applicationId": "com.google.mediapipe.apps.instantmotiontracking",
|
||||
"appName": "Instant Motion Tracking",
|
||||
"mainActivity": ".MainActivity",
|
||||
"cameraFacingFront": "False",
|
||||
"binaryGraphName": "instant_motion_tracking.binarypb",
|
||||
"inputVideoStreamName": "input_video",
|
||||
"outputVideoStreamName": "output_video",
|
||||
"flipFramesVertically": "True",
|
||||
},
|
||||
multidex = "native",
|
||||
deps = [
|
||||
":instantmotiontracking_lib",
|
||||
":mediapipe_jni_lib",
|
||||
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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.instantmotiontracking;
|
||||
|
||||
import android.content.ClipDescription;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import androidx.core.view.inputmethod.EditorInfoCompat;
|
||||
import androidx.core.view.inputmethod.InputConnectionCompat;
|
||||
import androidx.core.view.inputmethod.InputContentInfoCompat;
|
||||
|
||||
// import android.support.v13.view.inputmethod.EditorInfoCompat;
|
||||
// import android.support.v13.view.inputmethod.InputConnectionCompat;
|
||||
// import android.support.v13.view.inputmethod.InputContentInfoCompat;
|
||||
|
||||
/**
|
||||
* This custom EditText implementation uses the existing EditText framework in
|
||||
* order to develop a GIFEditText input box which is capable of accepting GIF
|
||||
* animations from the Android system keyboard and return the GIF location with
|
||||
* a content URI.
|
||||
*/
|
||||
public class GIFEditText extends AppCompatEditText {
|
||||
|
||||
private GIFCommitListener gifCommitListener;
|
||||
|
||||
public GIFEditText(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public GIFEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* onGIFCommit is called once content is pushed to the EditText via the
|
||||
* Android keyboard.
|
||||
*/
|
||||
public interface GIFCommitListener {
|
||||
void onGIFCommit(Uri contentUri, ClipDescription description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to set the gifCommitListener for this GIFEditText.
|
||||
*
|
||||
* @param gifCommitListener handles response to new content pushed to EditText
|
||||
*/
|
||||
public void setGIFCommitListener(GIFCommitListener gifCommitListener) {
|
||||
this.gifCommitListener = gifCommitListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
|
||||
final InputConnection inputConnection = super.onCreateInputConnection(editorInfo);
|
||||
EditorInfoCompat.setContentMimeTypes(editorInfo, new String[] {"image/gif"});
|
||||
return InputConnectionCompat.createWrapper(
|
||||
inputConnection,
|
||||
editorInfo,
|
||||
new InputConnectionCompat.OnCommitContentListener() {
|
||||
@Override
|
||||
public boolean onCommitContent(
|
||||
final InputContentInfoCompat inputContentInfo, int flags, Bundle opts) {
|
||||
try {
|
||||
if (gifCommitListener != null) {
|
||||
Runnable runnable =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
inputContentInfo.requestPermission();
|
||||
gifCommitListener.onGIFCommit(
|
||||
inputContentInfo.getContentUri(), inputContentInfo.getDescription());
|
||||
inputContentInfo.releasePermission();
|
||||
}
|
||||
};
|
||||
new Thread(runnable).start();
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Log.e("GIFEditText", "Input connection to GIF selection failed");
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,633 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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.instantmotiontracking;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
|
||||
import android.content.ClipDescription;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.resource.gif.GifDrawable;
|
||||
import com.bumptech.glide.request.target.CustomTarget;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.google.mediapipe.components.FrameProcessor;
|
||||
import com.google.mediapipe.framework.AndroidPacketCreator;
|
||||
import com.google.mediapipe.framework.Packet;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is the MainActivity that handles camera input, IMU sensor data acquisition
|
||||
* and sticker management for the InstantMotionTracking MediaPipe project.
|
||||
*/
|
||||
public class MainActivity extends com.google.mediapipe.apps.basic.MainActivity {
|
||||
private static final String TAG = "InstantMotionTrackingMainActivity";
|
||||
|
||||
// Allows for automated packet transmission to graph
|
||||
private MediaPipePacketManager mediaPipePacketManager;
|
||||
|
||||
private static final int TARGET_CAMERA_WIDTH = 960;
|
||||
private static final int TARGET_CAMERA_HEIGHT = 1280;
|
||||
private static final float TARGET_CAMERA_ASPECT_RATIO =
|
||||
(float) TARGET_CAMERA_WIDTH / (float) TARGET_CAMERA_HEIGHT;
|
||||
|
||||
// Bounds for a single click (sticker anchor reset)
|
||||
private static final long CLICK_DURATION = 300; // ms
|
||||
private long clickStartMillis = 0;
|
||||
private ViewGroup viewGroup;
|
||||
// Contains dynamic layout of sticker data controller
|
||||
private LinearLayout buttonLayout;
|
||||
|
||||
private ArrayList<StickerManager> stickerArrayList;
|
||||
// Current sticker being edited by user
|
||||
private StickerManager currentSticker;
|
||||
// Trip value used to determine sticker re-anchoring
|
||||
private static final String STICKER_SENTINEL_TAG = "sticker_sentinel";
|
||||
private int stickerSentinel = -1;
|
||||
|
||||
// Define parameters for 'reactivity' of object
|
||||
private static final float ROTATION_SPEED = 5.0f;
|
||||
private static final float SCALING_FACTOR = 0.025f;
|
||||
|
||||
// Parameters of device visual field for rendering system
|
||||
// (68 degrees, 4:3 for Pixel 4)
|
||||
// TODO : Make acquisition of this information automated
|
||||
private static final float VERTICAL_FOV_RADIANS = (float) Math.toRadians(68.0);
|
||||
private static final String FOV_SIDE_PACKET_TAG = "vertical_fov_radians";
|
||||
private static final String ASPECT_RATIO_SIDE_PACKET_TAG = "aspect_ratio";
|
||||
|
||||
private static final String IMU_MATRIX_TAG = "imu_rotation_matrix";
|
||||
private static final int SENSOR_SAMPLE_DELAY = SensorManager.SENSOR_DELAY_FASTEST;
|
||||
private final float[] rotationMatrix = new float[9];
|
||||
|
||||
private static final String STICKER_PROTO_TAG = "sticker_proto_string";
|
||||
// Assets for object rendering
|
||||
// All animation assets and tags for the first asset (1)
|
||||
private Bitmap asset3dTexture = null;
|
||||
private static final String ASSET_3D_TEXTURE = "robot/robot_texture.jpg";
|
||||
private static final String ASSET_3D_FILE = "robot/robot.obj.uuu";
|
||||
private static final String ASSET_3D_TEXTURE_TAG = "texture_3d";
|
||||
private static final String ASSET_3D_TAG = "asset_3d";
|
||||
// All GIF animation assets and tags
|
||||
private GIFEditText editText;
|
||||
private ArrayList<Bitmap> gifBitmaps = new ArrayList<>();
|
||||
private int gifCurrentIndex = 0;
|
||||
private Bitmap defaultGIFTexture = null; // Texture sent if no gif available
|
||||
// last time the GIF was updated
|
||||
private long gifLastFrameUpdateMS = System.currentTimeMillis();
|
||||
private static final int GIF_FRAME_RATE = 20; // 20 FPS
|
||||
private static final String GIF_ASPECT_RATIO_TAG = "gif_aspect_ratio";
|
||||
private static final String DEFAULT_GIF_TEXTURE = "gif/default_gif_texture.jpg";
|
||||
private static final String GIF_FILE = "gif/gif.obj.uuu";
|
||||
private static final String GIF_TEXTURE_TAG = "gif_texture";
|
||||
private static final String GIF_ASSET_TAG = "gif_asset_name";
|
||||
|
||||
private int cameraWidth = TARGET_CAMERA_WIDTH;
|
||||
private int cameraHeight = TARGET_CAMERA_HEIGHT;
|
||||
|
||||
@Override
|
||||
protected Size cameraTargetResolution() {
|
||||
// Camera size is in landscape, so here we have (height, width)
|
||||
return new Size(TARGET_CAMERA_HEIGHT, TARGET_CAMERA_WIDTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Size computeViewSize(int width, int height) {
|
||||
// Try to force aspect ratio of view size to match our target aspect ratio
|
||||
return new Size(height, (int) (height * TARGET_CAMERA_ASPECT_RATIO));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreviewDisplaySurfaceChanged(
|
||||
SurfaceHolder holder, int format, int width, int height) {
|
||||
super.onPreviewDisplaySurfaceChanged(holder, format, width, height);
|
||||
boolean isCameraRotated = cameraHelper.isCameraRotated();
|
||||
|
||||
// cameraImageSize computation logic duplicated from base MainActivity
|
||||
Size viewSize = computeViewSize(width, height);
|
||||
Size cameraImageSize = cameraHelper.computeDisplaySizeFromViewSize(viewSize);
|
||||
cameraWidth =
|
||||
isCameraRotated ? cameraImageSize.getHeight() : cameraImageSize.getWidth();
|
||||
cameraHeight =
|
||||
isCameraRotated ? cameraImageSize.getWidth() : cameraImageSize.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
editText = findViewById(R.id.gif_edit_text);
|
||||
editText.setGIFCommitListener(
|
||||
new GIFEditText.GIFCommitListener() {
|
||||
@Override
|
||||
public void onGIFCommit(Uri contentUri, ClipDescription description) {
|
||||
// The application must have permission to access the GIF content
|
||||
grantUriPermission(
|
||||
"com.google.mediapipe.apps.instantmotiontracking",
|
||||
contentUri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
// Set GIF frames from content URI
|
||||
setGIFBitmaps(contentUri.toString());
|
||||
// Close the keyboard upon GIF acquisition
|
||||
closeKeyboard();
|
||||
}
|
||||
});
|
||||
|
||||
// Send loaded 3d render assets as side packets to graph
|
||||
prepareDemoAssets();
|
||||
AndroidPacketCreator packetCreator = processor.getPacketCreator();
|
||||
|
||||
Map<String, Packet> inputSidePackets = new HashMap<>();
|
||||
inputSidePackets.put(ASSET_3D_TEXTURE_TAG,
|
||||
packetCreator.createRgbaImageFrame(asset3dTexture));
|
||||
inputSidePackets.put(ASSET_3D_TAG,
|
||||
packetCreator.createString(ASSET_3D_FILE));
|
||||
inputSidePackets.put(GIF_ASSET_TAG,
|
||||
packetCreator.createString(GIF_FILE));
|
||||
processor.setInputSidePackets(inputSidePackets);
|
||||
|
||||
// Add frame listener to PacketManagement system
|
||||
mediaPipePacketManager = new MediaPipePacketManager();
|
||||
processor.setOnWillAddFrameListener(mediaPipePacketManager);
|
||||
|
||||
// Send device properties to render objects via OpenGL
|
||||
Map<String, Packet> devicePropertiesSidePackets = new HashMap<>();
|
||||
// TODO: Note that if our actual camera stream resolution does not match the
|
||||
// requested aspect ratio, then we will need to update the value used for
|
||||
// this packet, or else tracking results will be off.
|
||||
devicePropertiesSidePackets.put(
|
||||
ASPECT_RATIO_SIDE_PACKET_TAG, packetCreator.createFloat32(TARGET_CAMERA_ASPECT_RATIO));
|
||||
devicePropertiesSidePackets.put(
|
||||
FOV_SIDE_PACKET_TAG, packetCreator.createFloat32(VERTICAL_FOV_RADIANS));
|
||||
processor.setInputSidePackets(devicePropertiesSidePackets);
|
||||
|
||||
// Begin with 0 stickers in dataset
|
||||
stickerArrayList = new ArrayList<>();
|
||||
currentSticker = null;
|
||||
|
||||
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
|
||||
List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ROTATION_VECTOR);
|
||||
sensorManager.registerListener(
|
||||
new SensorEventListener() {
|
||||
private final float[] rotMatFromVec = new float[9];
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
|
||||
// Update procedure on sensor adjustment (phone changes orientation)
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
// Get the Rotation Matrix from the Rotation Vector
|
||||
SensorManager.getRotationMatrixFromVector(rotMatFromVec, event.values);
|
||||
// AXIS_MINUS_X is used to remap the rotation matrix for left hand
|
||||
// rules in the MediaPipe graph
|
||||
SensorManager.remapCoordinateSystem(
|
||||
rotMatFromVec, SensorManager.AXIS_MINUS_X, SensorManager.AXIS_Y, rotationMatrix);
|
||||
}
|
||||
},
|
||||
(Sensor) sensorList.get(0),
|
||||
SENSOR_SAMPLE_DELAY);
|
||||
|
||||
// Mechanisms for zoom, pinch, rotation, tap gestures
|
||||
buttonLayout = (LinearLayout) findViewById(R.id.button_layout);
|
||||
viewGroup = findViewById(R.id.preview_display_layout);
|
||||
viewGroup.setOnTouchListener(
|
||||
new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
return manageUiTouch(event);
|
||||
}
|
||||
});
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
// Obtain our custom activity_main layout for InstantMotionTracking
|
||||
@Override
|
||||
protected int getContentViewLayoutResId() {
|
||||
return R.layout.instant_motion_tracking_activity_main;
|
||||
}
|
||||
|
||||
// Manages a touch event in order to perform placement/rotation/scaling gestures
|
||||
// on virtual sticker objects.
|
||||
private boolean manageUiTouch(MotionEvent event) {
|
||||
if (currentSticker != null) {
|
||||
switch (event.getAction()) {
|
||||
// Detecting a single click for object re-anchoring
|
||||
case (MotionEvent.ACTION_DOWN):
|
||||
clickStartMillis = System.currentTimeMillis();
|
||||
break;
|
||||
case (MotionEvent.ACTION_UP):
|
||||
if (System.currentTimeMillis() - clickStartMillis <= CLICK_DURATION) {
|
||||
recordClick(event);
|
||||
}
|
||||
break;
|
||||
case (MotionEvent.ACTION_MOVE):
|
||||
// Rotation and Scaling are independent events and can occur simulataneously
|
||||
if (event.getPointerCount() == 2) {
|
||||
if (event.getHistorySize() > 1) {
|
||||
// Calculate user scaling of sticker
|
||||
float newScaleFactor = getNewScaleFactor(event, currentSticker.getScaleFactor());
|
||||
currentSticker.setScaleFactor(newScaleFactor);
|
||||
// calculate rotation (radians) for dynamic y-axis rotations
|
||||
float rotationIncrement = calculateRotationRadians(event);
|
||||
currentSticker.setRotation(currentSticker.getRotation() + rotationIncrement);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// fall out
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns a float value that is equal to the radians of rotation from a two-finger
|
||||
// MotionEvent recorded by the OnTouchListener.
|
||||
private static float calculateRotationRadians(MotionEvent event) {
|
||||
float tangentA =
|
||||
(float) Math.atan2(event.getY(1) - event.getY(0), event.getX(1) - event.getX(0));
|
||||
float tangentB =
|
||||
(float)
|
||||
Math.atan2(
|
||||
event.getHistoricalY(1, 0) - event.getHistoricalY(0, 0),
|
||||
event.getHistoricalX(1, 0) - event.getHistoricalX(0, 0));
|
||||
float angle = ((float) Math.toDegrees(tangentA - tangentB)) % 360f;
|
||||
angle += ((angle < -180f) ? +360f : ((angle > 180f) ? -360f : 0.0f));
|
||||
float rotationIncrement = (float) (Math.PI * ((angle * ROTATION_SPEED) / 180));
|
||||
return rotationIncrement;
|
||||
}
|
||||
|
||||
// Returns a float value that is equal to the translation distance between
|
||||
// two-fingers that move in a pinch/spreading direction.
|
||||
private static float getNewScaleFactor(MotionEvent event, float currentScaleFactor) {
|
||||
double newDistance = getDistance(event.getX(0), event.getY(0), event.getX(1), event.getY(1));
|
||||
double oldDistance =
|
||||
getDistance(
|
||||
event.getHistoricalX(0, 0),
|
||||
event.getHistoricalY(0, 0),
|
||||
event.getHistoricalX(1, 0),
|
||||
event.getHistoricalY(1, 0));
|
||||
float signFloat =
|
||||
(newDistance < oldDistance)
|
||||
? -SCALING_FACTOR
|
||||
: SCALING_FACTOR; // Are they moving towards each other?
|
||||
currentScaleFactor *= (1f + signFloat);
|
||||
return currentScaleFactor;
|
||||
}
|
||||
|
||||
// Called if a single touch event is recorded on the screen and used to set the
|
||||
// new anchor position for the current sticker in focus.
|
||||
private void recordClick(MotionEvent event) {
|
||||
// First normalize our click position w.r.t. to the view display
|
||||
float x = (event.getX() / viewGroup.getWidth());
|
||||
float y = (event.getY() / viewGroup.getHeight());
|
||||
|
||||
// MediaPipe can automatically crop our camera stream when displaying it to
|
||||
// our surface, which can throw off our touch point calulations. So we need
|
||||
// to replicate that logic here. See FrameScaleMode::kFillAndCrop usage in
|
||||
// gl_quad_renderer.cc for more details.
|
||||
float widthRatio = (float) viewGroup.getWidth() / (float) cameraWidth;
|
||||
float heightRatio = (float) viewGroup.getHeight() / (float) cameraHeight;
|
||||
|
||||
float maxRatio = max(widthRatio, heightRatio);
|
||||
widthRatio /= maxRatio;
|
||||
heightRatio /= maxRatio;
|
||||
|
||||
// Now we scale by the scale factors, and then reposition (since cropping
|
||||
// is always centered)
|
||||
x *= widthRatio;
|
||||
x += 0.5f * (1.0f - widthRatio);
|
||||
y *= heightRatio;
|
||||
y += 0.5f * (1.0f - heightRatio);
|
||||
|
||||
// Finally, we can pass our adjusted x and y points to the StickerManager
|
||||
currentSticker.setAnchorCoordinate(x, y);
|
||||
stickerSentinel = currentSticker.getstickerId();
|
||||
}
|
||||
|
||||
// Provided the X and Y coordinates of two points, the distance between them
|
||||
// will be returned.
|
||||
private static double getDistance(double x1, double y1, double x2, double y2) {
|
||||
return Math.hypot((y2 - y1), (x2 - x1));
|
||||
}
|
||||
|
||||
// Called upon each button click, and used to populate the buttonLayout with the
|
||||
// current sticker data in addition to sticker controls (delete, remove, back).
|
||||
private void refreshUi() {
|
||||
if (currentSticker != null) { // No sticker in view
|
||||
buttonLayout.removeAllViews();
|
||||
ImageButton deleteSticker = new ImageButton(this);
|
||||
setControlButtonDesign(deleteSticker, R.drawable.baseline_clear_24);
|
||||
deleteSticker.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (currentSticker != null) {
|
||||
stickerArrayList.remove(currentSticker);
|
||||
currentSticker = null;
|
||||
refreshUi();
|
||||
}
|
||||
}
|
||||
});
|
||||
// Go to home sticker menu
|
||||
ImageButton goBack = new ImageButton(this);
|
||||
setControlButtonDesign(goBack, R.drawable.baseline_arrow_back_24);
|
||||
goBack.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
currentSticker = null;
|
||||
refreshUi();
|
||||
}
|
||||
});
|
||||
// Change sticker to next possible render
|
||||
ImageButton loopRender = new ImageButton(this);
|
||||
setControlButtonDesign(loopRender, R.drawable.baseline_loop_24);
|
||||
loopRender.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
currentSticker.setRender(currentSticker.getRender().iterate());
|
||||
refreshUi();
|
||||
}
|
||||
});
|
||||
buttonLayout.addView(deleteSticker);
|
||||
buttonLayout.addView(goBack);
|
||||
buttonLayout.addView(loopRender);
|
||||
|
||||
// Add the GIF search option if current sticker is GIF
|
||||
if (currentSticker.getRender() == StickerManager.Render.GIF) {
|
||||
ImageButton gifSearch = new ImageButton(this);
|
||||
setControlButtonDesign(gifSearch, R.drawable.baseline_search_24);
|
||||
gifSearch.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Clear the text field to prevent text artifacts in GIF selection
|
||||
editText.setText("");
|
||||
// Open the Keyboard to allow user input
|
||||
openKeyboard();
|
||||
}
|
||||
});
|
||||
buttonLayout.addView(gifSearch);
|
||||
}
|
||||
} else {
|
||||
buttonLayout.removeAllViews();
|
||||
// Display stickers
|
||||
for (final StickerManager sticker : stickerArrayList) {
|
||||
final ImageButton stickerButton = new ImageButton(this);
|
||||
stickerButton.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
currentSticker = sticker;
|
||||
refreshUi();
|
||||
}
|
||||
});
|
||||
if (sticker.getRender() == StickerManager.Render.GIF) {
|
||||
setControlButtonDesign(stickerButton, R.drawable.asset_gif_preview);
|
||||
} else if (sticker.getRender() == StickerManager.Render.ASSET_3D) {
|
||||
setStickerButtonDesign(stickerButton, R.drawable.asset_3d_preview);
|
||||
}
|
||||
|
||||
buttonLayout.addView(stickerButton);
|
||||
}
|
||||
ImageButton addSticker = new ImageButton(this);
|
||||
setControlButtonDesign(addSticker, R.drawable.baseline_add_24);
|
||||
addSticker.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
StickerManager newSticker = new StickerManager();
|
||||
stickerArrayList.add(newSticker);
|
||||
currentSticker = newSticker;
|
||||
refreshUi();
|
||||
}
|
||||
});
|
||||
ImageButton clearStickers = new ImageButton(this);
|
||||
setControlButtonDesign(clearStickers, R.drawable.baseline_clear_all_24);
|
||||
clearStickers.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
stickerArrayList.clear();
|
||||
refreshUi();
|
||||
}
|
||||
});
|
||||
|
||||
buttonLayout.addView(addSticker);
|
||||
buttonLayout.addView(clearStickers);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets ImageButton UI for Control Buttons.
|
||||
private void setControlButtonDesign(ImageButton btn, int imageDrawable) {
|
||||
// btn.setImageDrawable(getResources().getDrawable(imageDrawable));
|
||||
btn.setImageDrawable(getDrawable(imageDrawable));
|
||||
btn.setBackgroundColor(Color.parseColor("#00ffffff"));
|
||||
btn.setColorFilter(Color.parseColor("#0494a4"));
|
||||
btn.setLayoutParams(new LinearLayout.LayoutParams(200, 200));
|
||||
btn.setPadding(25, 25, 25, 25);
|
||||
btn.setScaleType(ImageView.ScaleType.FIT_XY);
|
||||
}
|
||||
|
||||
// Sets ImageButton UI for Sticker Buttons.
|
||||
private void setStickerButtonDesign(ImageButton btn, int imageDrawable) {
|
||||
btn.setImageDrawable(getDrawable(imageDrawable));
|
||||
btn.setBackground(getDrawable(R.drawable.circle_button));
|
||||
btn.setLayoutParams(new LinearLayout.LayoutParams(250, 250));
|
||||
btn.setPadding(25, 25, 25, 25);
|
||||
btn.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
|
||||
}
|
||||
|
||||
// Used to set ArrayList of Bitmap frames
|
||||
private void setGIFBitmaps(String gifUrl) {
|
||||
gifBitmaps = new ArrayList<>(); // Empty the bitmap array
|
||||
Glide.with(this)
|
||||
.asGif()
|
||||
.load(gifUrl)
|
||||
.into(
|
||||
new CustomTarget<GifDrawable>() {
|
||||
@Override
|
||||
public void onLoadCleared(Drawable placeholder) {}
|
||||
|
||||
@Override
|
||||
public void onResourceReady(
|
||||
GifDrawable resource, Transition<? super GifDrawable> transition) {
|
||||
try {
|
||||
Object startConstant = resource.getConstantState();
|
||||
Field frameManager = startConstant.getClass().getDeclaredField("frameLoader");
|
||||
frameManager.setAccessible(true);
|
||||
Object frameLoader = frameManager.get(startConstant);
|
||||
Field decoder = frameLoader.getClass().getDeclaredField("gifDecoder");
|
||||
decoder.setAccessible(true);
|
||||
|
||||
Object frameObject = (decoder.get(frameLoader));
|
||||
for (int i = 0; i < resource.getFrameCount(); i++) {
|
||||
frameObject.getClass().getMethod("advance").invoke(frameObject);
|
||||
Bitmap bmp =
|
||||
(Bitmap)
|
||||
frameObject.getClass().getMethod("getNextFrame").invoke(frameObject);
|
||||
gifBitmaps.add(flipHorizontal(bmp));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Bitmaps must be flipped due to native acquisition of frames from Android OS
|
||||
private static Bitmap flipHorizontal(Bitmap bmp) {
|
||||
Matrix matrix = new Matrix();
|
||||
// Flip Bitmap frames horizontally
|
||||
matrix.preScale(-1.0f, 1.0f);
|
||||
return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
|
||||
}
|
||||
|
||||
// Function that is continuously called in order to time GIF frame updates
|
||||
private void updateGIFFrame() {
|
||||
long millisPerFrame = 1000 / GIF_FRAME_RATE;
|
||||
if (System.currentTimeMillis() - gifLastFrameUpdateMS >= millisPerFrame) {
|
||||
// Update GIF timestamp
|
||||
gifLastFrameUpdateMS = System.currentTimeMillis();
|
||||
// Cycle through every possible frame and avoid a divide by 0
|
||||
gifCurrentIndex = gifBitmaps.isEmpty() ? 1 : (gifCurrentIndex + 1) % gifBitmaps.size();
|
||||
}
|
||||
}
|
||||
|
||||
// Called once to popup the Keyboard via Android OS with focus set to editText
|
||||
private void openKeyboard() {
|
||||
editText.requestFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
// Called once to close the Keyboard via Android OS
|
||||
private void closeKeyboard() {
|
||||
View view = this.getCurrentFocus();
|
||||
if (view != null) {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareDemoAssets() {
|
||||
// We render from raw data with openGL, so disable decoding preprocessing
|
||||
BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
|
||||
decodeOptions.inScaled = false;
|
||||
decodeOptions.inDither = false;
|
||||
decodeOptions.inPremultiplied = false;
|
||||
|
||||
try {
|
||||
InputStream inputStream = getAssets().open(DEFAULT_GIF_TEXTURE);
|
||||
defaultGIFTexture =
|
||||
flipHorizontal(
|
||||
BitmapFactory.decodeStream(inputStream, null /*outPadding*/, decodeOptions));
|
||||
inputStream.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error parsing object texture; error: ", e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream inputStream = getAssets().open(ASSET_3D_TEXTURE);
|
||||
asset3dTexture = BitmapFactory.decodeStream(inputStream, null /*outPadding*/, decodeOptions);
|
||||
inputStream.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error parsing object texture; error: ", e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private class MediaPipePacketManager implements FrameProcessor.OnWillAddFrameListener {
|
||||
@Override
|
||||
public void onWillAddFrame(long timestamp) {
|
||||
// set current GIF bitmap as default texture
|
||||
Bitmap currentGIFBitmap = defaultGIFTexture;
|
||||
// If current index is in bounds, display current frame
|
||||
if (gifCurrentIndex <= gifBitmaps.size() - 1) {
|
||||
currentGIFBitmap = gifBitmaps.get(gifCurrentIndex);
|
||||
}
|
||||
// Update to next GIF frame based on timing and frame rate
|
||||
updateGIFFrame();
|
||||
|
||||
// Calculate and set the aspect ratio of the GIF
|
||||
float gifAspectRatio =
|
||||
(float) currentGIFBitmap.getWidth() / (float) currentGIFBitmap.getHeight();
|
||||
|
||||
Packet stickerSentinelPacket = processor.getPacketCreator().createInt32(stickerSentinel);
|
||||
// Sticker sentinel value must be reset for next graph iteration
|
||||
stickerSentinel = -1;
|
||||
// Initialize sticker data protobufferpacket information
|
||||
Packet stickerProtoDataPacket =
|
||||
processor
|
||||
.getPacketCreator()
|
||||
.createSerializedProto(StickerManager.getMessageLiteData(stickerArrayList));
|
||||
// Define and set the IMU sensory information float array
|
||||
Packet imuDataPacket = processor.getPacketCreator().createFloat32Array(rotationMatrix);
|
||||
// Communicate GIF textures (dynamic texturing) to graph
|
||||
Packet gifTexturePacket = processor.getPacketCreator().createRgbaImageFrame(currentGIFBitmap);
|
||||
Packet gifAspectRatioPacket = processor.getPacketCreator().createFloat32(gifAspectRatio);
|
||||
processor
|
||||
.getGraph()
|
||||
.addConsumablePacketToInputStream(STICKER_SENTINEL_TAG, stickerSentinelPacket, timestamp);
|
||||
processor
|
||||
.getGraph()
|
||||
.addConsumablePacketToInputStream(STICKER_PROTO_TAG, stickerProtoDataPacket, timestamp);
|
||||
processor
|
||||
.getGraph()
|
||||
.addConsumablePacketToInputStream(IMU_MATRIX_TAG, imuDataPacket, timestamp);
|
||||
processor
|
||||
.getGraph()
|
||||
.addConsumablePacketToInputStream(GIF_TEXTURE_TAG, gifTexturePacket, timestamp);
|
||||
processor
|
||||
.getGraph()
|
||||
.addConsumablePacketToInputStream(GIF_ASPECT_RATIO_TAG, gifAspectRatioPacket, timestamp);
|
||||
stickerSentinelPacket.release();
|
||||
stickerProtoDataPacket.release();
|
||||
imuDataPacket.release();
|
||||
gifTexturePacket.release();
|
||||
gifAspectRatioPacket.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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.instantmotiontracking;
|
||||
|
||||
import com.google.mediapipe.graphs.instantmotiontracking.StickerBufferProto.Sticker;
|
||||
import com.google.mediapipe.graphs.instantmotiontracking.StickerBufferProto.StickerRoll;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This class represents a single sticker object placed in the
|
||||
* instantmotiontracking system. StickerManagers represent a unique object to render
|
||||
* and manipulate in an AR scene.
|
||||
* <p>A sticker has a sticker_id (a unique integer identifying a sticker object
|
||||
* to render), x and y normalized anchor coordinates [0.0-1.0], user inputs for
|
||||
* rotation in radians, scaling, and a renderID (another unique integer which
|
||||
* determines what object model to render for this unique sticker).
|
||||
*/
|
||||
public class StickerManager {
|
||||
|
||||
/** All types of possible objects to render for our application. */
|
||||
public enum Render {
|
||||
// Every possible render for a sticker object
|
||||
GIF,
|
||||
ASSET_3D;
|
||||
|
||||
/**
|
||||
* Once called, will set the value of the current render to the next
|
||||
* possible Render available. If all possible Renders have been iterated
|
||||
* through, the function will loop and set to the first available Render.
|
||||
*/
|
||||
public Render iterate() {
|
||||
int newEnumIdx = (this.ordinal() + 1) % Render.values().length;
|
||||
return Render.values()[newEnumIdx];
|
||||
}
|
||||
}
|
||||
|
||||
// Current render of the sticker object
|
||||
private Render currentRender;
|
||||
|
||||
// Normalized X and Y coordinates of anchor
|
||||
// (0,0) lies at top-left corner of screen
|
||||
// (1.0,1.0) lies at bottom-right corner of screen
|
||||
private float anchorX;
|
||||
private float anchorY;
|
||||
|
||||
// Rotation in radians from user
|
||||
private float userRotation = 0f;
|
||||
// Scaling factor as defined by user (defaults to 1.0)
|
||||
private float userScalingFactor = 1f;
|
||||
|
||||
// Unique sticker integer ID
|
||||
private final int stickerId;
|
||||
|
||||
// Used to determine next stickerId
|
||||
private static int globalIDLimit = 1;
|
||||
|
||||
/**
|
||||
* Used to create a StickerManager object with a newly generated stickerId and a
|
||||
* default Render of the first possible render in our Render enum.
|
||||
*/
|
||||
public StickerManager() {
|
||||
// Every sticker will have a default render of the first 3D asset
|
||||
this.currentRender = Render.values()[1];
|
||||
// StickerManager will render out of view by default
|
||||
this.setAnchorCoordinate(2.0f, 2.0f);
|
||||
// Set the global sticker ID limit for the next sticker
|
||||
stickerId = StickerManager.globalIDLimit++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to create a StickerManager object with a newly generated stickerId.
|
||||
*
|
||||
* @param render initial Render of the new StickerManager object
|
||||
*/
|
||||
public StickerManager(Render render) {
|
||||
this.currentRender = render;
|
||||
// StickerManager will render out of view by default
|
||||
this.setAnchorCoordinate(2.0f, 2.0f);
|
||||
// Set the global sticker ID limit for the next sticker
|
||||
stickerId = StickerManager.globalIDLimit++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get the sticker ID of the object.
|
||||
*
|
||||
* @return integer of the unique sticker ID
|
||||
*/
|
||||
public int getstickerId() {
|
||||
return this.stickerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to update or reset the anchor positions in normalized [0.0-1.0]
|
||||
* coordinate space for the sticker object.
|
||||
*
|
||||
* @param normalizedX normalized X coordinate for the new anchor position
|
||||
* @param normalizedY normalized Y coordinate for the new anchor position
|
||||
*/
|
||||
public void setAnchorCoordinate(float normalizedX, float normalizedY) {
|
||||
this.anchorX = normalizedX;
|
||||
this.anchorY = normalizedY;
|
||||
}
|
||||
|
||||
/** Returns the normalized X anchor coordinate of the sticker object. */
|
||||
public float getAnchorX() {
|
||||
return anchorX;
|
||||
}
|
||||
|
||||
/** Returns the normalized Y anchor coordinate of the sticker object. */
|
||||
public float getAnchorY() {
|
||||
return anchorY;
|
||||
}
|
||||
|
||||
/** Returns current asset to be rendered for this sticker object. */
|
||||
public Render getRender() {
|
||||
return currentRender;
|
||||
}
|
||||
|
||||
/** Set render for this sticker object */
|
||||
public void setRender(Render render) {
|
||||
this.currentRender = render;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets new user value of rotation radians. This rotation is not cumulative,
|
||||
* and must be set to an absolute value of rotation applied to the object.
|
||||
*
|
||||
* @param radians specified radians to rotate the sticker object by
|
||||
*/
|
||||
public void setRotation(float radians) {
|
||||
this.userRotation = radians;
|
||||
}
|
||||
|
||||
/** Returns current user radian rotation setting. */
|
||||
public float getRotation() {
|
||||
return this.userRotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets new user scale factor. This factor will be proportional to the scale
|
||||
* of the sticker object.
|
||||
*
|
||||
* @param scaling scale factor to be applied
|
||||
*/
|
||||
public void setScaleFactor(float scaling) {
|
||||
this.userScalingFactor = scaling;
|
||||
}
|
||||
|
||||
/** Returns current user scale factor setting. */
|
||||
public float getScaleFactor() {
|
||||
return this.userScalingFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts an ArrayList of stickers to a MessageLite object
|
||||
* which can be passed directly to the MediaPipe graph.
|
||||
*
|
||||
* @param stickerArrayList ArrayList of StickerManager objects to convert to data string
|
||||
* @return MessageLite protobuffer of all sticker data
|
||||
*/
|
||||
public static StickerRoll getMessageLiteData(
|
||||
ArrayList<StickerManager> stickerArrayList) {
|
||||
StickerRoll.Builder stickerRollBuilder
|
||||
= StickerRoll.newBuilder();
|
||||
for (final StickerManager sticker : stickerArrayList) {
|
||||
Sticker protoSticker =
|
||||
Sticker.newBuilder()
|
||||
.setId(sticker.getstickerId())
|
||||
.setX(sticker.getAnchorX())
|
||||
.setY(sticker.getAnchorY())
|
||||
.setRotation(sticker.getRotation())
|
||||
.setScale(sticker.getScaleFactor())
|
||||
.setRenderId(sticker.getRender().ordinal())
|
||||
.build();
|
||||
stickerRollBuilder.addSticker(protoSticker);
|
||||
}
|
||||
return stickerRollBuilder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# 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"])
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files(
|
||||
srcs = glob(["**"]),
|
||||
)
|
||||
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,16 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M11.5,9h1.5v6h-1.5z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,9H6c-0.6,0 -1,0.5 -1,1v4c0,0.5 0.4,1 1,1h3c0.6,0 1,-0.5 1,-1v-2H8.5v1.5h-2v-3H10V10C10,9.5 9.6,9 9,9z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,10.5l0,-1.5l-4.5,0l0,6l1.5,0l0,-2l2,0l0,-1.5l-2,0l0,-1z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M5,13h14v-2L5,11v2zM3,17h14v-2L3,15v2zM7,7v2h14L21,7L7,7z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
@@ -0,0 +1,62 @@
|
||||
<?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"
|
||||
android:id="@+id/constraint_layout">
|
||||
|
||||
<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>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:gravity="top"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="top"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
|
||||
<com.google.mediapipe.apps.instantmotiontracking.GIFEditText
|
||||
android:id="@+id/gif_edit_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="0dp"
|
||||
android:background="@null"
|
||||
android:gravity="center"
|
||||
android:hint=""
|
||||
android:text=""
|
||||
android:padding="0dp"
|
||||
android:cursorVisible="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
@@ -57,6 +57,7 @@ android_binary(
|
||||
deps = [
|
||||
":mediapipe_jni_lib",
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:basic_lib",
|
||||
"//mediapipe/framework/formats:landmark_java_proto_lite",
|
||||
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -15,7 +15,13 @@
|
||||
package com.google.mediapipe.apps.iristrackinggpu;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmark;
|
||||
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmarkList;
|
||||
import com.google.mediapipe.framework.Packet;
|
||||
import com.google.mediapipe.framework.PacketGetter;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -24,6 +30,7 @@ public class MainActivity extends com.google.mediapipe.apps.basic.MainActivity {
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
private static final String FOCAL_LENGTH_STREAM_NAME = "focal_length_pixel";
|
||||
private static final String OUTPUT_LANDMARKS_STREAM_NAME = "face_landmarks_with_iris";
|
||||
|
||||
@Override
|
||||
protected void onCameraStarted(SurfaceTexture surfaceTexture) {
|
||||
@@ -37,4 +44,55 @@ public class MainActivity extends com.google.mediapipe.apps.basic.MainActivity {
|
||||
processor.setInputSidePackets(inputSidePackets);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// To show verbose logging, run:
|
||||
// adb shell setprop log.tag.MainActivity VERBOSE
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
processor.addPacketCallback(
|
||||
OUTPUT_LANDMARKS_STREAM_NAME,
|
||||
(packet) -> {
|
||||
byte[] landmarksRaw = PacketGetter.getProtoBytes(packet);
|
||||
try {
|
||||
NormalizedLandmarkList landmarks = NormalizedLandmarkList.parseFrom(landmarksRaw);
|
||||
if (landmarks == null) {
|
||||
Log.v(TAG, "[TS:" + packet.getTimestamp() + "] No landmarks.");
|
||||
return;
|
||||
}
|
||||
Log.v(
|
||||
TAG,
|
||||
"[TS:"
|
||||
+ packet.getTimestamp()
|
||||
+ "] #Landmarks for face (including iris): "
|
||||
+ landmarks.getLandmarkCount());
|
||||
Log.v(TAG, getLandmarksDebugString(landmarks));
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
Log.e(TAG, "Couldn't Exception received - " + e);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static String getLandmarksDebugString(NormalizedLandmarkList landmarks) {
|
||||
int landmarkIndex = 0;
|
||||
String landmarksString = "";
|
||||
for (NormalizedLandmark landmark : landmarks.getLandmarkList()) {
|
||||
landmarksString +=
|
||||
"\t\tLandmark["
|
||||
+ landmarkIndex
|
||||
+ "]: ("
|
||||
+ landmark.getX()
|
||||
+ ", "
|
||||
+ landmark.getY()
|
||||
+ ", "
|
||||
+ landmark.getZ()
|
||||
+ ")\n";
|
||||
++landmarkIndex;
|
||||
}
|
||||
return landmarksString;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
@@ -72,11 +72,11 @@ android_binary(
|
||||
] + select({
|
||||
"//conditions:default": [
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/objectdetection3d/assets/sneaker:model.obj.uuu",
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/objectdetection3d/assets/sneaker:texture.bmp",
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/objectdetection3d/assets/sneaker:texture.jpg",
|
||||
],
|
||||
":use_chair_model": [
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/objectdetection3d/assets/chair:model.obj.uuu",
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/objectdetection3d/assets/chair:texture.bmp",
|
||||
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/objectdetection3d/assets/chair:texture.jpg",
|
||||
],
|
||||
}),
|
||||
assets_dir = "",
|
||||
|
||||
@@ -31,7 +31,7 @@ import java.util.Map;
|
||||
public class MainActivity extends com.google.mediapipe.apps.basic.MainActivity {
|
||||
private static final String TAG = "MainActivity";
|
||||
|
||||
private static final String OBJ_TEXTURE = "texture.bmp";
|
||||
private static final String OBJ_TEXTURE = "texture.jpg";
|
||||
private static final String OBJ_FILE = "model.obj.uuu";
|
||||
private static final String BOX_TEXTURE = "classic_colors.png";
|
||||
private static final String BOX_FILE = "box.obj.uuu";
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 6.9 MiB |
|
After Width: | Height: | Size: 420 KiB |
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 48 MiB |
|
After Width: | Height: | Size: 385 KiB |
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:private"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -14,7 +14,7 @@ load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library"
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library"
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = [
|
||||
"//mediapipe/examples:__subpackages__",
|
||||
|
||||
@@ -173,6 +173,7 @@ TEST(ContentZoomingCalculatorTest, PanConfig) {
|
||||
auto* options = config.mutable_options()->MutableExtension(
|
||||
ContentZoomingCalculatorOptions::ext);
|
||||
options->mutable_kinematic_options_pan()->set_min_motion_to_reframe(0.0);
|
||||
options->mutable_kinematic_options_pan()->set_update_rate_seconds(2);
|
||||
options->mutable_kinematic_options_tilt()->set_min_motion_to_reframe(5.0);
|
||||
options->mutable_kinematic_options_zoom()->set_min_motion_to_reframe(5.0);
|
||||
auto runner = ::absl::make_unique<CalculatorRunner>(config);
|
||||
@@ -191,6 +192,7 @@ TEST(ContentZoomingCalculatorTest, TiltConfig) {
|
||||
ContentZoomingCalculatorOptions::ext);
|
||||
options->mutable_kinematic_options_pan()->set_min_motion_to_reframe(5.0);
|
||||
options->mutable_kinematic_options_tilt()->set_min_motion_to_reframe(0.0);
|
||||
options->mutable_kinematic_options_tilt()->set_update_rate_seconds(2);
|
||||
options->mutable_kinematic_options_zoom()->set_min_motion_to_reframe(5.0);
|
||||
auto runner = ::absl::make_unique<CalculatorRunner>(config);
|
||||
AddDetection(cv::Rect_<float>(.4, .5, .1, .1), 0, runner.get());
|
||||
@@ -209,6 +211,7 @@ TEST(ContentZoomingCalculatorTest, ZoomConfig) {
|
||||
options->mutable_kinematic_options_pan()->set_min_motion_to_reframe(5.0);
|
||||
options->mutable_kinematic_options_tilt()->set_min_motion_to_reframe(5.0);
|
||||
options->mutable_kinematic_options_zoom()->set_min_motion_to_reframe(0.0);
|
||||
options->mutable_kinematic_options_zoom()->set_update_rate_seconds(2);
|
||||
auto runner = ::absl::make_unique<CalculatorRunner>(config);
|
||||
AddDetection(cv::Rect_<float>(.4, .5, .1, .1), 0, runner.get());
|
||||
AddDetection(cv::Rect_<float>(.45, .55, .15, .15), 1000000, runner.get());
|
||||
@@ -345,8 +348,13 @@ TEST(ContentZoomingCalculatorTest, ZoomTestPairSize) {
|
||||
}
|
||||
|
||||
TEST(ContentZoomingCalculatorTest, ZoomTestNearOutsideBorder) {
|
||||
auto runner = ::absl::make_unique<CalculatorRunner>(
|
||||
ParseTextProtoOrDie<CalculatorGraphConfig::Node>(kConfigD));
|
||||
auto config = ParseTextProtoOrDie<CalculatorGraphConfig::Node>(kConfigD);
|
||||
auto* options = config.mutable_options()->MutableExtension(
|
||||
ContentZoomingCalculatorOptions::ext);
|
||||
options->mutable_kinematic_options_pan()->set_update_rate_seconds(2);
|
||||
options->mutable_kinematic_options_tilt()->set_update_rate_seconds(2);
|
||||
options->mutable_kinematic_options_zoom()->set_update_rate_seconds(2);
|
||||
auto runner = ::absl::make_unique<CalculatorRunner>(config);
|
||||
AddDetection(cv::Rect_<float>(.95, .95, .05, .05), 0, runner.get());
|
||||
AddDetection(cv::Rect_<float>(.9, .9, .1, .1), 1000000, runner.get());
|
||||
MP_ASSERT_OK(runner->Run());
|
||||
@@ -357,8 +365,13 @@ TEST(ContentZoomingCalculatorTest, ZoomTestNearOutsideBorder) {
|
||||
}
|
||||
|
||||
TEST(ContentZoomingCalculatorTest, ZoomTestNearInsideBorder) {
|
||||
auto runner = ::absl::make_unique<CalculatorRunner>(
|
||||
ParseTextProtoOrDie<CalculatorGraphConfig::Node>(kConfigD));
|
||||
auto config = ParseTextProtoOrDie<CalculatorGraphConfig::Node>(kConfigD);
|
||||
auto* options = config.mutable_options()->MutableExtension(
|
||||
ContentZoomingCalculatorOptions::ext);
|
||||
options->mutable_kinematic_options_pan()->set_update_rate_seconds(2);
|
||||
options->mutable_kinematic_options_tilt()->set_update_rate_seconds(2);
|
||||
options->mutable_kinematic_options_zoom()->set_update_rate_seconds(2);
|
||||
auto runner = ::absl::make_unique<CalculatorRunner>(config);
|
||||
AddDetection(cv::Rect_<float>(0, 0, .05, .05), 0, runner.get());
|
||||
AddDetection(cv::Rect_<float>(0, 0, .1, .1), 1000000, runner.get());
|
||||
MP_ASSERT_OK(runner->Run());
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
filegroup(
|
||||
name = "test_images",
|
||||
|
||||
@@ -14,7 +14,7 @@ load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library"
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace autoflip {
|
||||
current_velocity_deg_per_s_ = 0;
|
||||
RET_CHECK_GT(pixels_per_degree_, 0)
|
||||
<< "pixels_per_degree must be larger than 0.";
|
||||
RET_CHECK_GE(options_.update_rate_seconds(), 0)
|
||||
<< "update_rate_seconds must be greater than 0.";
|
||||
RET_CHECK_GE(options_.min_motion_to_reframe(), options_.reframe_window())
|
||||
<< "Reframe window cannot exceed min_motion_to_reframe.";
|
||||
return ::mediapipe::OkStatus();
|
||||
@@ -41,9 +43,10 @@ namespace autoflip {
|
||||
|
||||
// Observed velocity and then weighted update of this velocity.
|
||||
double observed_velocity = delta_degs / delta_t;
|
||||
double updated_velocity =
|
||||
current_velocity_deg_per_s_ * (1 - options_.update_rate()) +
|
||||
observed_velocity * options_.update_rate();
|
||||
double update_rate = std::min(delta_t / options_.update_rate_seconds(),
|
||||
options_.max_update_rate());
|
||||
double updated_velocity = current_velocity_deg_per_s_ * (1 - update_rate) +
|
||||
observed_velocity * update_rate;
|
||||
// Limited current velocity.
|
||||
current_velocity_deg_per_s_ =
|
||||
updated_velocity > 0 ? fmin(updated_velocity, options_.max_velocity())
|
||||
|
||||
@@ -5,7 +5,7 @@ package mediapipe.autoflip;
|
||||
message KinematicOptions {
|
||||
// Weighted update of new camera velocity (measurement) vs current state
|
||||
// (prediction).
|
||||
optional double update_rate = 1 [default = 0.5];
|
||||
optional double update_rate = 1 [default = 0.5, deprecated = true];
|
||||
// Max velocity (degrees per second) that the camera can move.
|
||||
optional double max_velocity = 2 [default = 18];
|
||||
// Min motion (in degrees) to react in pixels.
|
||||
@@ -15,4 +15,9 @@ message KinematicOptions {
|
||||
// total reframe distance on average. Value cannot exceed
|
||||
// min_motion_to_reframe value.
|
||||
optional float reframe_window = 4 [default = 0];
|
||||
// Calculation of internal velocity state is:
|
||||
// min((delta_time_s / update_rate_seconds), max_update_rate)
|
||||
// where delta_time_s is the time since the last frame.
|
||||
optional double update_rate_seconds = 5 [default = 0.20];
|
||||
optional double max_update_rate = 6 [default = 0.8];
|
||||
}
|
||||
|
||||
@@ -85,7 +85,8 @@ TEST(KinematicPathSolverTest, PassEnoughMotionLargeImg) {
|
||||
KinematicOptions options;
|
||||
// Set min motion to 1deg
|
||||
options.set_min_motion_to_reframe(1.0);
|
||||
options.set_update_rate(1);
|
||||
options.set_update_rate_seconds(.0000001);
|
||||
options.set_max_update_rate(1.0);
|
||||
options.set_max_velocity(1000);
|
||||
// Set degrees / pixel to 16.6
|
||||
KinematicPathSolver solver(options, 0, 1000, 1000.0 / kWidthFieldOfView);
|
||||
@@ -102,7 +103,8 @@ TEST(KinematicPathSolverTest, PassEnoughMotionSmallImg) {
|
||||
KinematicOptions options;
|
||||
// Set min motion to 2deg
|
||||
options.set_min_motion_to_reframe(1.0);
|
||||
options.set_update_rate(1);
|
||||
options.set_update_rate_seconds(.0000001);
|
||||
options.set_max_update_rate(1.0);
|
||||
options.set_max_velocity(18);
|
||||
// Set degrees / pixel to 8.3
|
||||
KinematicPathSolver solver(options, 0, 500, 500.0 / kWidthFieldOfView);
|
||||
@@ -132,7 +134,8 @@ TEST(KinematicPathSolverTest, PassReframeWindow) {
|
||||
KinematicOptions options;
|
||||
// Set min motion to 1deg
|
||||
options.set_min_motion_to_reframe(1.0);
|
||||
options.set_update_rate(1);
|
||||
options.set_update_rate_seconds(.0000001);
|
||||
options.set_max_update_rate(1.0);
|
||||
options.set_max_velocity(1000);
|
||||
// Set reframe window size to .75 for test.
|
||||
options.set_reframe_window(0.75);
|
||||
@@ -147,10 +150,41 @@ TEST(KinematicPathSolverTest, PassReframeWindow) {
|
||||
EXPECT_EQ(state, 507);
|
||||
}
|
||||
|
||||
TEST(KinematicPathSolverTest, PassUpdateRate30FPS) {
|
||||
KinematicOptions options;
|
||||
options.set_min_motion_to_reframe(1.0);
|
||||
options.set_update_rate_seconds(.25);
|
||||
options.set_max_update_rate(0.8);
|
||||
options.set_max_velocity(18);
|
||||
KinematicPathSolver solver(options, 0, 1000, 1000.0 / kWidthFieldOfView);
|
||||
int state;
|
||||
MP_ASSERT_OK(solver.AddObservation(500, kMicroSecInSec * 0));
|
||||
MP_ASSERT_OK(solver.AddObservation(520, kMicroSecInSec * 1 / 30));
|
||||
MP_ASSERT_OK(solver.GetState(&state));
|
||||
// (0.033 / .25) * 20 =
|
||||
EXPECT_EQ(state, 503);
|
||||
}
|
||||
|
||||
TEST(KinematicPathSolverTest, PassUpdateRate10FPS) {
|
||||
KinematicOptions options;
|
||||
options.set_min_motion_to_reframe(1.0);
|
||||
options.set_update_rate_seconds(.25);
|
||||
options.set_max_update_rate(0.8);
|
||||
options.set_max_velocity(18);
|
||||
KinematicPathSolver solver(options, 0, 1000, 1000.0 / kWidthFieldOfView);
|
||||
int state;
|
||||
MP_ASSERT_OK(solver.AddObservation(500, kMicroSecInSec * 0));
|
||||
MP_ASSERT_OK(solver.AddObservation(520, kMicroSecInSec * 1 / 10));
|
||||
MP_ASSERT_OK(solver.GetState(&state));
|
||||
// (0.1 / .25) * 20 =
|
||||
EXPECT_EQ(state, 508);
|
||||
}
|
||||
|
||||
TEST(KinematicPathSolverTest, PassUpdateRate) {
|
||||
KinematicOptions options;
|
||||
options.set_min_motion_to_reframe(1.0);
|
||||
options.set_update_rate(0.25);
|
||||
options.set_update_rate_seconds(4);
|
||||
options.set_max_update_rate(1.0);
|
||||
options.set_max_velocity(18);
|
||||
KinematicPathSolver solver(options, 0, 1000, 1000.0 / kWidthFieldOfView);
|
||||
int state;
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ load("//mediapipe/framework/tool:mediapipe_graph.bzl", "mediapipe_simple_subgrap
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
cc_binary(
|
||||
name = "extract_yt8m_features",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ load(
|
||||
"example_provisioning",
|
||||
)
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
licenses(["notice"])
|
||||
|
||||
MIN_IOS_VERSION = "10.0"
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ import time
|
||||
from typing import Tuple, Union
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import mediapipe.python as mp
|
||||
import numpy as np
|
||||
# resources dependency
|
||||
from mediapipe.framework.formats import landmark_pb2
|
||||
|
||||
@@ -107,7 +107,7 @@ class UpperBodyPoseTracker:
|
||||
output_file='/tmp/output.png')
|
||||
|
||||
# Read an image and convert the BGR image to RGB.
|
||||
input_image = cv2.imread('/tmp/input.png')[:, :, ::-1]
|
||||
input_image = cv2.cvtColor(cv2.imread('/tmp/input.png'), COLOR_BGR2RGB)
|
||||
pose_landmarks, annotated_image = pose_tracker.run(input_image)
|
||||
pose_tracker.close()
|
||||
"""
|
||||
@@ -150,8 +150,11 @@ class UpperBodyPoseTracker:
|
||||
success, input_frame = cap.read()
|
||||
if not success:
|
||||
break
|
||||
_, output_frame = self._run_graph(input_frame[:, :, ::-1])
|
||||
cv2.imshow('MediaPipe upper body pose tracker', output_frame[:, :, ::-1])
|
||||
input_frame = cv2.cvtColor(input_frame, cv2.COLOR_BGR2RGB)
|
||||
input_frame.flags.writeable = False
|
||||
_, output_frame = self._run_graph(input_frame)
|
||||
cv2.imshow('MediaPipe upper body pose tracker',
|
||||
cv2.cvtColor(output_frame, cv2.COLOR_RGB2BGR))
|
||||
if cv2.waitKey(5) & 0xFF == 27:
|
||||
break
|
||||
cap.release()
|
||||
|
||||