Project import generated by Copybara.

GitOrigin-RevId: 612e50bb8db2ec3dc1c30049372d87a80c3848db
This commit is contained in:
MediaPipe Team
2020-08-30 19:52:55 -04:00
committed by chuoling
parent a7225b938a
commit c0124fb83c
248 changed files with 5225 additions and 1914 deletions
@@ -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(["**"]),
)
@@ -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"])
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

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"])
Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 MiB

Binary file not shown.

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"])
+1 -1
View File
@@ -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",
+1 -1
View File
@@ -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",
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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__"])
+1 -1
View File
@@ -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__"])
+1 -1
View File
@@ -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__"])
+1 -1
View File
@@ -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",
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -22,7 +22,7 @@ load(
"example_provisioning",
)
licenses(["notice"]) # Apache 2.0
licenses(["notice"])
MIN_IOS_VERSION = "10.0"
+1 -1
View File
@@ -22,7 +22,7 @@ load(
"example_provisioning",
)
licenses(["notice"]) # Apache 2.0
licenses(["notice"])
MIN_IOS_VERSION = "10.0"
+1 -1
View File
@@ -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()