commit
66cd04ce42
|
@ -30,6 +30,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
unzip \
|
unzip \
|
||||||
python \
|
python \
|
||||||
python-pip \
|
python-pip \
|
||||||
|
python3-pip \
|
||||||
libopencv-core-dev \
|
libopencv-core-dev \
|
||||||
libopencv-highgui-dev \
|
libopencv-highgui-dev \
|
||||||
libopencv-imgproc-dev \
|
libopencv-imgproc-dev \
|
||||||
|
@ -42,9 +43,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
|
||||||
RUN pip install --upgrade setuptools
|
RUN pip install --upgrade setuptools
|
||||||
RUN pip install future
|
RUN pip install future
|
||||||
|
RUN pip3 install six
|
||||||
|
|
||||||
# Install bazel
|
# Install bazel
|
||||||
ARG BAZEL_VERSION=0.26.1
|
ARG BAZEL_VERSION=1.1.0
|
||||||
RUN mkdir /bazel && \
|
RUN mkdir /bazel && \
|
||||||
wget --no-check-certificate -O /bazel/installer.sh "https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/b\
|
wget --no-check-certificate -O /bazel/installer.sh "https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/b\
|
||||||
azel-${BAZEL_VERSION}-installer-linux-x86_64.sh" && \
|
azel-${BAZEL_VERSION}-installer-linux-x86_64.sh" && \
|
||||||
|
|
|
@ -10,11 +10,13 @@
|
||||||
## ML Solutions in MediaPipe
|
## ML Solutions in MediaPipe
|
||||||
|
|
||||||
* [Hand Tracking](mediapipe/docs/hand_tracking_mobile_gpu.md)
|
* [Hand Tracking](mediapipe/docs/hand_tracking_mobile_gpu.md)
|
||||||
|
* [Multi-hand Tracking](mediapipe/docs/multi_hand_tracking_mobile_gpu.md)
|
||||||
* [Face Detection](mediapipe/docs/face_detection_mobile_gpu.md)
|
* [Face Detection](mediapipe/docs/face_detection_mobile_gpu.md)
|
||||||
* [Hair Segmentation](mediapipe/docs/hair_segmentation_mobile_gpu.md)
|
* [Hair Segmentation](mediapipe/docs/hair_segmentation_mobile_gpu.md)
|
||||||
* [Object Detection](mediapipe/docs/object_detection_mobile_gpu.md)
|
* [Object Detection](mediapipe/docs/object_detection_mobile_gpu.md)
|
||||||
|
|
||||||

|

|
||||||
|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
@ -23,7 +25,7 @@
|
||||||
Follow these [instructions](mediapipe/docs/install.md).
|
Follow these [instructions](mediapipe/docs/install.md).
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
See mobile and desktop [examples](mediapipe/docs/examples.md).
|
See mobile, desktop and Google Coral [examples](mediapipe/docs/examples.md).
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
[MediaPipe Read-the-Docs](https://mediapipe.readthedocs.io/) or [docs.mediapipe.dev](https://docs.mediapipe.dev)
|
[MediaPipe Read-the-Docs](https://mediapipe.readthedocs.io/) or [docs.mediapipe.dev](https://docs.mediapipe.dev)
|
||||||
|
@ -41,6 +43,7 @@ A web-based visualizer is hosted on [viz.mediapipe.dev](https://viz.mediapipe.de
|
||||||
* [MediaPipe: A Framework for Building Perception Pipelines](https://arxiv.org/abs/1906.08172)
|
* [MediaPipe: A Framework for Building Perception Pipelines](https://arxiv.org/abs/1906.08172)
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
|
* [AI Nextcon 2020, 12-16 Feb 2020, Seattle](http://aisea20.xnextcon.com/)
|
||||||
* [MediaPipe Madrid Meetup, 16 Dec 2019](https://www.meetup.com/Madrid-AI-Developers-Group/events/266329088/)
|
* [MediaPipe Madrid Meetup, 16 Dec 2019](https://www.meetup.com/Madrid-AI-Developers-Group/events/266329088/)
|
||||||
* [MediaPipe London Meetup, Google 123 Building, 12 Dec 2019](https://www.meetup.com/London-AI-Tech-Talk/events/266329038)
|
* [MediaPipe London Meetup, Google 123 Building, 12 Dec 2019](https://www.meetup.com/London-AI-Tech-Talk/events/266329038)
|
||||||
* [ML Conference, Berlin, 11 Dec 2019](https://mlconference.ai/machine-learning-advanced-development/mediapipe-building-real-time-cross-platform-mobile-web-edge-desktop-video-audio-ml-pipelines/)
|
* [ML Conference, Berlin, 11 Dec 2019](https://mlconference.ai/machine-learning-advanced-development/mediapipe-building-real-time-cross-platform-mobile-web-edge-desktop-video-audio-ml-pipelines/)
|
||||||
|
|
42
WORKSPACE
42
WORKSPACE
|
@ -12,17 +12,21 @@ http_archive(
|
||||||
load("@bazel_skylib//lib:versions.bzl", "versions")
|
load("@bazel_skylib//lib:versions.bzl", "versions")
|
||||||
versions.check(minimum_bazel_version = "0.24.1")
|
versions.check(minimum_bazel_version = "0.24.1")
|
||||||
|
|
||||||
# ABSL cpp library.
|
# ABSL cpp library lts_2019_08_08.
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "com_google_absl",
|
name = "com_google_absl",
|
||||||
# Head commit on 2019-04-12.
|
|
||||||
# TODO: Switch to the latest absl version when the problem gets
|
|
||||||
# fixed.
|
|
||||||
urls = [
|
urls = [
|
||||||
"https://github.com/abseil/abseil-cpp/archive/a02f62f456f2c4a7ecf2be3104fe0c6e16fbad9a.tar.gz",
|
"https://github.com/abseil/abseil-cpp/archive/20190808.tar.gz",
|
||||||
],
|
],
|
||||||
sha256 = "d437920d1434c766d22e85773b899c77c672b8b4865d5dc2cd61a29fdff3cf03",
|
# Remove after https://github.com/abseil/abseil-cpp/issues/326 is solved.
|
||||||
strip_prefix = "abseil-cpp-a02f62f456f2c4a7ecf2be3104fe0c6e16fbad9a",
|
patches = [
|
||||||
|
"@//third_party:com_google_absl_f863b622fe13612433fdf43f76547d5edda0c93001.diff"
|
||||||
|
],
|
||||||
|
patch_args = [
|
||||||
|
"-p1",
|
||||||
|
],
|
||||||
|
strip_prefix = "abseil-cpp-20190808",
|
||||||
|
sha256 = "8100085dada279bf3ee00cd064d43b5f55e5d913be0dfe2906f06f8f28d5b37e"
|
||||||
)
|
)
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
|
@ -103,9 +107,9 @@ http_archive(
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2019-11-12
|
# 2019-11-21
|
||||||
_TENSORFLOW_GIT_COMMIT = "a5f9bcd64453ff3d1f64cb4da4786db3d2da7f82"
|
_TENSORFLOW_GIT_COMMIT = "f482488b481a799ca07e7e2d153cf47b8e91a60c"
|
||||||
_TENSORFLOW_SHA256= "f2b6f2ab2ffe63e86eccd3ce4bea6b7197383d726638dfeeebcdc1e7de73f075"
|
_TENSORFLOW_SHA256= "8d9118c2ce186c7e1403f04b96982fe72c184060c7f7a93e30a28dca358694f0"
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "org_tensorflow",
|
name = "org_tensorflow",
|
||||||
urls = [
|
urls = [
|
||||||
|
@ -149,11 +153,10 @@ new_local_repository(
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "android_opencv",
|
name = "android_opencv",
|
||||||
sha256 = "056b849842e4fa8751d09edbb64530cfa7a63c84ccd232d0ace330e27ba55d0b",
|
|
||||||
build_file = "@//third_party:opencv_android.BUILD",
|
build_file = "@//third_party:opencv_android.BUILD",
|
||||||
strip_prefix = "OpenCV-android-sdk",
|
strip_prefix = "OpenCV-android-sdk",
|
||||||
type = "zip",
|
type = "zip",
|
||||||
url = "https://github.com/opencv/opencv/releases/download/4.1.0/opencv-4.1.0-android-sdk.zip",
|
url = "https://github.com/opencv/opencv/releases/download/3.4.3/opencv-3.4.3-android-sdk.zip",
|
||||||
)
|
)
|
||||||
|
|
||||||
# After OpenCV 3.2.0, the pre-compiled opencv2.framework has google protobuf symbols, which will
|
# After OpenCV 3.2.0, the pre-compiled opencv2.framework has google protobuf symbols, which will
|
||||||
|
@ -184,13 +187,18 @@ maven_install(
|
||||||
artifacts = [
|
artifacts = [
|
||||||
"androidx.annotation:annotation:aar:1.1.0",
|
"androidx.annotation:annotation:aar:1.1.0",
|
||||||
"androidx.appcompat:appcompat:aar:1.1.0-rc01",
|
"androidx.appcompat:appcompat:aar:1.1.0-rc01",
|
||||||
|
"androidx.camera:camera-core:aar:1.0.0-alpha06",
|
||||||
|
"androidx.camera:camera-camera2:aar:1.0.0-alpha06",
|
||||||
"androidx.constraintlayout:constraintlayout:aar:1.1.3",
|
"androidx.constraintlayout:constraintlayout:aar:1.1.3",
|
||||||
"androidx.core:core:aar:1.1.0-rc03",
|
"androidx.core:core:aar:1.1.0-rc03",
|
||||||
"androidx.legacy:legacy-support-v4:aar:1.0.0",
|
"androidx.legacy:legacy-support-v4:aar:1.0.0",
|
||||||
"androidx.recyclerview:recyclerview:aar:1.1.0-beta02",
|
"androidx.recyclerview:recyclerview:aar:1.1.0-beta02",
|
||||||
"com.google.android.material:material:aar:1.0.0-rc01",
|
"com.google.android.material:material:aar:1.0.0-rc01",
|
||||||
],
|
],
|
||||||
repositories = ["https://dl.google.com/dl/android/maven2"],
|
repositories = [
|
||||||
|
"https://dl.google.com/dl/android/maven2",
|
||||||
|
"https://repo1.maven.org/maven2",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
maven_server(
|
maven_server(
|
||||||
|
@ -206,10 +214,10 @@ maven_jar(
|
||||||
)
|
)
|
||||||
|
|
||||||
maven_jar(
|
maven_jar(
|
||||||
name = "androidx_concurrent_futures",
|
name = "androidx_concurrent_futures",
|
||||||
artifact = "androidx.concurrent:concurrent-futures:1.0.0-alpha03",
|
artifact = "androidx.concurrent:concurrent-futures:1.0.0-alpha03",
|
||||||
sha1 = "b528df95c7e2fefa2210c0c742bf3e491c1818ae",
|
sha1 = "b528df95c7e2fefa2210c0c742bf3e491c1818ae",
|
||||||
server = "google_server",
|
server = "google_server",
|
||||||
)
|
)
|
||||||
|
|
||||||
maven_jar(
|
maven_jar(
|
||||||
|
|
|
@ -691,6 +691,7 @@ cc_library(
|
||||||
":split_vector_calculator_cc_proto",
|
":split_vector_calculator_cc_proto",
|
||||||
"//mediapipe/framework:calculator_framework",
|
"//mediapipe/framework:calculator_framework",
|
||||||
"//mediapipe/framework/formats:landmark_cc_proto",
|
"//mediapipe/framework/formats:landmark_cc_proto",
|
||||||
|
"//mediapipe/framework/formats:rect_cc_proto",
|
||||||
"//mediapipe/framework/port:ret_check",
|
"//mediapipe/framework/port:ret_check",
|
||||||
"//mediapipe/framework/port:status",
|
"//mediapipe/framework/port:status",
|
||||||
"//mediapipe/util:resource_util",
|
"//mediapipe/util:resource_util",
|
||||||
|
|
|
@ -21,16 +21,10 @@
|
||||||
|
|
||||||
namespace mediapipe {
|
namespace mediapipe {
|
||||||
|
|
||||||
// A calculator to process std::vector<NormalizedLandmark>.
|
// A calculator to process std::vector<NormalizedLandmarkList>.
|
||||||
typedef BeginLoopCalculator<std::vector<::mediapipe::NormalizedLandmark>>
|
typedef BeginLoopCalculator<std::vector<::mediapipe::NormalizedLandmarkList>>
|
||||||
BeginLoopNormalizedLandmarkCalculator;
|
BeginLoopNormalizedLandmarkListVectorCalculator;
|
||||||
REGISTER_CALCULATOR(BeginLoopNormalizedLandmarkCalculator);
|
REGISTER_CALCULATOR(BeginLoopNormalizedLandmarkListVectorCalculator);
|
||||||
|
|
||||||
// A calculator to process std::vector<std::vector<NormalizedLandmark>>.
|
|
||||||
typedef BeginLoopCalculator<
|
|
||||||
std::vector<std::vector<::mediapipe::NormalizedLandmark>>>
|
|
||||||
BeginLoopNormalizedLandmarksVectorCalculator;
|
|
||||||
REGISTER_CALCULATOR(BeginLoopNormalizedLandmarksVectorCalculator);
|
|
||||||
|
|
||||||
// A calculator to process std::vector<NormalizedRect>.
|
// A calculator to process std::vector<NormalizedRect>.
|
||||||
typedef BeginLoopCalculator<std::vector<::mediapipe::NormalizedRect>>
|
typedef BeginLoopCalculator<std::vector<::mediapipe::NormalizedRect>>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "mediapipe/framework/formats/landmark.pb.h"
|
#include "mediapipe/framework/formats/landmark.pb.h"
|
||||||
#include "tensorflow/lite/interpreter.h"
|
#include "tensorflow/lite/interpreter.h"
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__APPLE__)
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
#include "tensorflow/lite/delegates/gpu/gl/gl_buffer.h"
|
#include "tensorflow/lite/delegates/gpu/gl/gl_buffer.h"
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif // !MEDIAPIPE_DISABLE_GPU
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ typedef ConcatenateVectorCalculator<::mediapipe::NormalizedLandmark>
|
||||||
ConcatenateLandmarkVectorCalculator;
|
ConcatenateLandmarkVectorCalculator;
|
||||||
REGISTER_CALCULATOR(ConcatenateLandmarkVectorCalculator);
|
REGISTER_CALCULATOR(ConcatenateLandmarkVectorCalculator);
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__APPLE__)
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
typedef ConcatenateVectorCalculator<::tflite::gpu::gl::GlBuffer>
|
typedef ConcatenateVectorCalculator<::tflite::gpu::gl::GlBuffer>
|
||||||
ConcatenateGlBufferVectorCalculator;
|
ConcatenateGlBufferVectorCalculator;
|
||||||
REGISTER_CALCULATOR(ConcatenateGlBufferVectorCalculator);
|
REGISTER_CALCULATOR(ConcatenateGlBufferVectorCalculator);
|
||||||
|
|
|
@ -26,14 +26,9 @@ typedef EndLoopCalculator<std::vector<::mediapipe::NormalizedRect>>
|
||||||
EndLoopNormalizedRectCalculator;
|
EndLoopNormalizedRectCalculator;
|
||||||
REGISTER_CALCULATOR(EndLoopNormalizedRectCalculator);
|
REGISTER_CALCULATOR(EndLoopNormalizedRectCalculator);
|
||||||
|
|
||||||
typedef EndLoopCalculator<std::vector<::mediapipe::NormalizedLandmark>>
|
typedef EndLoopCalculator<std::vector<::mediapipe::NormalizedLandmarkList>>
|
||||||
EndLoopNormalizedLandmarkCalculator;
|
EndLoopNormalizedLandmarkListVectorCalculator;
|
||||||
REGISTER_CALCULATOR(EndLoopNormalizedLandmarkCalculator);
|
REGISTER_CALCULATOR(EndLoopNormalizedLandmarkListVectorCalculator);
|
||||||
|
|
||||||
typedef EndLoopCalculator<
|
|
||||||
std::vector<std::vector<::mediapipe::NormalizedLandmark>>>
|
|
||||||
EndLoopNormalizedLandmarksVectorCalculator;
|
|
||||||
REGISTER_CALCULATOR(EndLoopNormalizedLandmarksVectorCalculator);
|
|
||||||
|
|
||||||
typedef EndLoopCalculator<std::vector<bool>> EndLoopBooleanCalculator;
|
typedef EndLoopCalculator<std::vector<bool>> EndLoopBooleanCalculator;
|
||||||
REGISTER_CALCULATOR(EndLoopBooleanCalculator);
|
REGISTER_CALCULATOR(EndLoopBooleanCalculator);
|
||||||
|
|
|
@ -93,14 +93,19 @@ TEST(PreviousLoopbackCalculator, CorrectTimestamps) {
|
||||||
EXPECT_EQ(TimestampValues(in_prev), (std::vector<int64>{1}));
|
EXPECT_EQ(TimestampValues(in_prev), (std::vector<int64>{1}));
|
||||||
EXPECT_EQ(pair_values(in_prev.back()), std::make_pair(1, -1));
|
EXPECT_EQ(pair_values(in_prev.back()), std::make_pair(1, -1));
|
||||||
|
|
||||||
|
send_packet("in", 2);
|
||||||
|
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
||||||
|
EXPECT_EQ(TimestampValues(in_prev), (std::vector<int64>{1, 2}));
|
||||||
|
EXPECT_EQ(pair_values(in_prev.back()), std::make_pair(2, 1));
|
||||||
|
|
||||||
send_packet("in", 5);
|
send_packet("in", 5);
|
||||||
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
||||||
EXPECT_EQ(TimestampValues(in_prev), (std::vector<int64>{1, 5}));
|
EXPECT_EQ(TimestampValues(in_prev), (std::vector<int64>{1, 2, 5}));
|
||||||
EXPECT_EQ(pair_values(in_prev.back()), std::make_pair(5, 1));
|
EXPECT_EQ(pair_values(in_prev.back()), std::make_pair(5, 2));
|
||||||
|
|
||||||
send_packet("in", 15);
|
send_packet("in", 15);
|
||||||
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
||||||
EXPECT_EQ(TimestampValues(in_prev), (std::vector<int64>{1, 5, 15}));
|
EXPECT_EQ(TimestampValues(in_prev), (std::vector<int64>{1, 2, 5, 15}));
|
||||||
EXPECT_EQ(pair_values(in_prev.back()), std::make_pair(15, 5));
|
EXPECT_EQ(pair_values(in_prev.back()), std::make_pair(15, 5));
|
||||||
|
|
||||||
MP_EXPECT_OK(graph_.CloseAllInputStreams());
|
MP_EXPECT_OK(graph_.CloseAllInputStreams());
|
||||||
|
@ -182,18 +187,22 @@ TEST(PreviousLoopbackCalculator, ClosesCorrectly) {
|
||||||
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
||||||
EXPECT_EQ(TimestampValues(outputs), (std::vector<int64>{1}));
|
EXPECT_EQ(TimestampValues(outputs), (std::vector<int64>{1}));
|
||||||
|
|
||||||
|
send_packet("in", 2);
|
||||||
|
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
||||||
|
EXPECT_EQ(TimestampValues(outputs), (std::vector<int64>{1, 2}));
|
||||||
|
|
||||||
send_packet("in", 5);
|
send_packet("in", 5);
|
||||||
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
||||||
EXPECT_EQ(TimestampValues(outputs), (std::vector<int64>{1, 5}));
|
EXPECT_EQ(TimestampValues(outputs), (std::vector<int64>{1, 2, 5}));
|
||||||
|
|
||||||
send_packet("in", 15);
|
send_packet("in", 15);
|
||||||
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
||||||
EXPECT_EQ(TimestampValues(outputs), (std::vector<int64>{1, 5, 15}));
|
EXPECT_EQ(TimestampValues(outputs), (std::vector<int64>{1, 2, 5, 15}));
|
||||||
|
|
||||||
MP_EXPECT_OK(graph_.CloseAllInputStreams());
|
MP_EXPECT_OK(graph_.CloseAllInputStreams());
|
||||||
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
MP_EXPECT_OK(graph_.WaitUntilIdle());
|
||||||
EXPECT_EQ(TimestampValues(outputs),
|
EXPECT_EQ(TimestampValues(outputs),
|
||||||
(std::vector<int64>{1, 5, 15, Timestamp::Max().Value()}));
|
(std::vector<int64>{1, 2, 5, 15, Timestamp::Max().Value()}));
|
||||||
|
|
||||||
MP_EXPECT_OK(graph_.WaitUntilDone());
|
MP_EXPECT_OK(graph_.WaitUntilDone());
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "mediapipe/framework/formats/landmark.pb.h"
|
#include "mediapipe/framework/formats/landmark.pb.h"
|
||||||
|
#include "mediapipe/framework/formats/rect.pb.h"
|
||||||
#include "tensorflow/lite/interpreter.h"
|
#include "tensorflow/lite/interpreter.h"
|
||||||
|
|
||||||
namespace mediapipe {
|
namespace mediapipe {
|
||||||
|
@ -41,4 +42,8 @@ REGISTER_CALCULATOR(SplitTfLiteTensorVectorCalculator);
|
||||||
typedef SplitVectorCalculator<::mediapipe::NormalizedLandmark>
|
typedef SplitVectorCalculator<::mediapipe::NormalizedLandmark>
|
||||||
SplitLandmarkVectorCalculator;
|
SplitLandmarkVectorCalculator;
|
||||||
REGISTER_CALCULATOR(SplitLandmarkVectorCalculator);
|
REGISTER_CALCULATOR(SplitLandmarkVectorCalculator);
|
||||||
|
|
||||||
|
typedef SplitVectorCalculator<::mediapipe::NormalizedRect>
|
||||||
|
SplitNormalizedRectVectorCalculator;
|
||||||
|
REGISTER_CALCULATOR(SplitNormalizedRectVectorCalculator);
|
||||||
} // namespace mediapipe
|
} // namespace mediapipe
|
||||||
|
|
|
@ -356,13 +356,13 @@ cc_library(
|
||||||
"//mediapipe/framework/port:opencv_imgproc",
|
"//mediapipe/framework/port:opencv_imgproc",
|
||||||
"//mediapipe/framework/port:ret_check",
|
"//mediapipe/framework/port:ret_check",
|
||||||
"//mediapipe/framework/port:status",
|
"//mediapipe/framework/port:status",
|
||||||
"//mediapipe/gpu:gpu_buffer",
|
|
||||||
] + select({
|
] + select({
|
||||||
"//mediapipe/gpu:disable_gpu": [],
|
"//mediapipe/gpu:disable_gpu": [],
|
||||||
"//conditions:default": [
|
"//conditions:default": [
|
||||||
"//mediapipe/gpu:gl_calculator_helper",
|
"//mediapipe/gpu:gl_calculator_helper",
|
||||||
"//mediapipe/gpu:gl_simple_shaders",
|
"//mediapipe/gpu:gl_simple_shaders",
|
||||||
"//mediapipe/gpu:gl_quad_renderer",
|
"//mediapipe/gpu:gl_quad_renderer",
|
||||||
|
"//mediapipe/gpu:gpu_buffer",
|
||||||
"//mediapipe/gpu:shader_util",
|
"//mediapipe/gpu:shader_util",
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -400,7 +400,7 @@ REGISTER_CALCULATOR(ImageTransformationCalculator);
|
||||||
QuadRenderer* renderer = nullptr;
|
QuadRenderer* renderer = nullptr;
|
||||||
GlTexture src1;
|
GlTexture src1;
|
||||||
|
|
||||||
#if defined(__APPLE__) && !TARGET_OS_OSX
|
#if defined(MEDIAPIPE_IOS)
|
||||||
if (input.format() == GpuBufferFormat::kBiPlanar420YpCbCr8VideoRange ||
|
if (input.format() == GpuBufferFormat::kBiPlanar420YpCbCr8VideoRange ||
|
||||||
input.format() == GpuBufferFormat::kBiPlanar420YpCbCr8FullRange) {
|
input.format() == GpuBufferFormat::kBiPlanar420YpCbCr8FullRange) {
|
||||||
if (!yuv_renderer_) {
|
if (!yuv_renderer_) {
|
||||||
|
|
|
@ -36,7 +36,8 @@ message ScaleImageCalculatorOptions {
|
||||||
|
|
||||||
// If ratio is positive, crop the image to this minimum and maximum
|
// If ratio is positive, crop the image to this minimum and maximum
|
||||||
// aspect ratio (preserving the center of the frame). This is done
|
// aspect ratio (preserving the center of the frame). This is done
|
||||||
// before scaling.
|
// before scaling. The string must contain "/", so to disable cropping,
|
||||||
|
// set both to "0/1".
|
||||||
// For example, for a min_aspect_ratio of "9/16" and max of "16/9" the
|
// For example, for a min_aspect_ratio of "9/16" and max of "16/9" the
|
||||||
// following cropping will occur:
|
// following cropping will occur:
|
||||||
// 1920x1080 (which is 16:9) is not cropped
|
// 1920x1080 (which is 16:9) is not cropped
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#include "tensorflow/core/framework/tensor_shape.h"
|
#include "tensorflow/core/framework/tensor_shape.h"
|
||||||
#include "tensorflow/core/framework/tensor_util.h"
|
#include "tensorflow/core/framework/tensor_util.h"
|
||||||
|
|
||||||
#if !defined(__ANDROID__) && !defined(__APPLE__)
|
#if !defined(MEDIAPIPE_MOBILE) && !defined(__APPLE__)
|
||||||
#include "tensorflow/core/profiler/lib/traceme.h"
|
#include "tensorflow/core/profiler/lib/traceme.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -441,7 +441,7 @@ class TensorFlowInferenceCalculator : public CalculatorBase {
|
||||||
const int64 run_start_time = absl::ToUnixMicros(clock_->TimeNow());
|
const int64 run_start_time = absl::ToUnixMicros(clock_->TimeNow());
|
||||||
tf::Status tf_status;
|
tf::Status tf_status;
|
||||||
{
|
{
|
||||||
#if !defined(__ANDROID__) && !defined(__APPLE__)
|
#if !defined(MEDIAPIPE_MOBILE) && !defined(__APPLE__)
|
||||||
tensorflow::profiler::TraceMe trace(absl::string_view(cc->NodeName()));
|
tensorflow::profiler::TraceMe trace(absl::string_view(cc->NodeName()));
|
||||||
#endif
|
#endif
|
||||||
tf_status = session_->Run(input_tensors, output_tensor_names,
|
tf_status = session_->Run(input_tensors, output_tensor_names,
|
||||||
|
|
|
@ -31,8 +31,7 @@
|
||||||
#include "mediapipe/framework/tool/status_util.h"
|
#include "mediapipe/framework/tool/status_util.h"
|
||||||
#include "tensorflow/core/public/session_options.h"
|
#include "tensorflow/core/public/session_options.h"
|
||||||
|
|
||||||
#if defined(MEDIAPIPE_LITE) || defined(__ANDROID__) || \
|
#if defined(MEDIAPIPE_MOBILE)
|
||||||
defined(__APPLE__) && !TARGET_OS_OSX
|
|
||||||
#include "mediapipe/util/android/file/base/helpers.h"
|
#include "mediapipe/util/android/file/base/helpers.h"
|
||||||
#else
|
#else
|
||||||
#include "mediapipe/framework/port/file_helpers.h"
|
#include "mediapipe/framework/port/file_helpers.h"
|
||||||
|
|
|
@ -85,7 +85,7 @@ class TFRecordReaderCalculator : public CalculatorBase {
|
||||||
tensorflow::io::RecordReader reader(file.get(),
|
tensorflow::io::RecordReader reader(file.get(),
|
||||||
tensorflow::io::RecordReaderOptions());
|
tensorflow::io::RecordReaderOptions());
|
||||||
tensorflow::uint64 offset = 0;
|
tensorflow::uint64 offset = 0;
|
||||||
std::string example_str;
|
tensorflow::tstring example_str;
|
||||||
const int target_idx =
|
const int target_idx =
|
||||||
cc->InputSidePackets().HasTag(kRecordIndex)
|
cc->InputSidePackets().HasTag(kRecordIndex)
|
||||||
? cc->InputSidePackets().Tag(kRecordIndex).Get<int>()
|
? cc->InputSidePackets().Tag(kRecordIndex).Get<int>()
|
||||||
|
@ -98,7 +98,7 @@ class TFRecordReaderCalculator : public CalculatorBase {
|
||||||
if (current_idx == target_idx) {
|
if (current_idx == target_idx) {
|
||||||
if (cc->OutputSidePackets().HasTag(kExampleTag)) {
|
if (cc->OutputSidePackets().HasTag(kExampleTag)) {
|
||||||
tensorflow::Example tf_example;
|
tensorflow::Example tf_example;
|
||||||
tf_example.ParseFromString(example_str);
|
tf_example.ParseFromArray(example_str.data(), example_str.size());
|
||||||
cc->OutputSidePackets()
|
cc->OutputSidePackets()
|
||||||
.Tag(kExampleTag)
|
.Tag(kExampleTag)
|
||||||
.Set(MakePacket<tensorflow::Example>(std::move(tf_example)));
|
.Set(MakePacket<tensorflow::Example>(std::move(tf_example)));
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library")
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
package(default_visibility = ["//visibility:private"])
|
package(default_visibility = ["//visibility:private"])
|
||||||
|
|
||||||
load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library")
|
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "ssd_anchors_calculator_proto",
|
name = "ssd_anchors_calculator_proto",
|
||||||
srcs = ["ssd_anchors_calculator.proto"],
|
srcs = ["ssd_anchors_calculator.proto"],
|
||||||
|
@ -249,6 +249,11 @@ cc_library(
|
||||||
"@org_tensorflow//tensorflow/lite/delegates/gpu/gl:gl_program",
|
"@org_tensorflow//tensorflow/lite/delegates/gpu/gl:gl_program",
|
||||||
"@org_tensorflow//tensorflow/lite/delegates/gpu/gl:gl_shader",
|
"@org_tensorflow//tensorflow/lite/delegates/gpu/gl:gl_shader",
|
||||||
],
|
],
|
||||||
|
}) + select({
|
||||||
|
"//conditions:default": [],
|
||||||
|
"//mediapipe:android": [
|
||||||
|
"@org_tensorflow//tensorflow/lite/delegates/nnapi:nnapi_delegate",
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
alwayslink = 1,
|
alwayslink = 1,
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
#include "tensorflow/lite/error_reporter.h"
|
#include "tensorflow/lite/error_reporter.h"
|
||||||
#include "tensorflow/lite/interpreter.h"
|
#include "tensorflow/lite/interpreter.h"
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
#include "mediapipe/gpu/gl_calculator_helper.h"
|
#include "mediapipe/gpu/gl_calculator_helper.h"
|
||||||
#include "mediapipe/gpu/gpu_buffer.h"
|
#include "mediapipe/gpu/gpu_buffer.h"
|
||||||
#include "tensorflow/lite/delegates/gpu/gl/gl_buffer.h"
|
#include "tensorflow/lite/delegates/gpu/gl/gl_buffer.h"
|
||||||
|
@ -35,7 +34,7 @@
|
||||||
#include "tensorflow/lite/delegates/gpu/gl_delegate.h"
|
#include "tensorflow/lite/delegates/gpu/gl_delegate.h"
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif // !MEDIAPIPE_DISABLE_GPU
|
||||||
|
|
||||||
#if defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#if defined(MEDIAPIPE_IOS)
|
||||||
#import <CoreVideo/CoreVideo.h>
|
#import <CoreVideo/CoreVideo.h>
|
||||||
#import <Metal/Metal.h>
|
#import <Metal/Metal.h>
|
||||||
#import <MetalKit/MetalKit.h>
|
#import <MetalKit/MetalKit.h>
|
||||||
|
@ -46,10 +45,9 @@
|
||||||
#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
|
#include "tensorflow/lite/delegates/gpu/metal_delegate.h"
|
||||||
#endif // iOS
|
#endif // iOS
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
typedef ::tflite::gpu::gl::GlBuffer GpuTensor;
|
typedef ::tflite::gpu::gl::GlBuffer GpuTensor;
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
typedef id<MTLBuffer> GpuTensor;
|
typedef id<MTLBuffer> GpuTensor;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -69,8 +67,7 @@ typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>
|
||||||
|
|
||||||
namespace mediapipe {
|
namespace mediapipe {
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
using ::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer;
|
using ::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer;
|
||||||
using ::tflite::gpu::gl::GlProgram;
|
using ::tflite::gpu::gl::GlProgram;
|
||||||
using ::tflite::gpu::gl::GlShader;
|
using ::tflite::gpu::gl::GlShader;
|
||||||
|
@ -80,7 +77,7 @@ struct GPUData {
|
||||||
GlShader shader;
|
GlShader shader;
|
||||||
GlProgram program;
|
GlProgram program;
|
||||||
};
|
};
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
struct GPUData {
|
struct GPUData {
|
||||||
int elements = 1;
|
int elements = 1;
|
||||||
GpuTensor buffer;
|
GpuTensor buffer;
|
||||||
|
@ -149,11 +146,10 @@ class TfLiteConverterCalculator : public CalculatorBase {
|
||||||
|
|
||||||
std::unique_ptr<tflite::Interpreter> interpreter_ = nullptr;
|
std::unique_ptr<tflite::Interpreter> interpreter_ = nullptr;
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
mediapipe::GlCalculatorHelper gpu_helper_;
|
mediapipe::GlCalculatorHelper gpu_helper_;
|
||||||
std::unique_ptr<GPUData> gpu_data_out_;
|
std::unique_ptr<GPUData> gpu_data_out_;
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
MPPMetalHelper* gpu_helper_ = nullptr;
|
MPPMetalHelper* gpu_helper_ = nullptr;
|
||||||
std::unique_ptr<GPUData> gpu_data_out_;
|
std::unique_ptr<GPUData> gpu_data_out_;
|
||||||
#endif
|
#endif
|
||||||
|
@ -202,10 +198,9 @@ REGISTER_CALCULATOR(TfLiteConverterCalculator);
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif // !MEDIAPIPE_DISABLE_GPU
|
||||||
|
|
||||||
if (use_gpu) {
|
if (use_gpu) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(mediapipe::GlCalculatorHelper::UpdateContract(cc));
|
MP_RETURN_IF_ERROR(mediapipe::GlCalculatorHelper::UpdateContract(cc));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
MP_RETURN_IF_ERROR([MPPMetalHelper updateContract:cc]);
|
MP_RETURN_IF_ERROR([MPPMetalHelper updateContract:cc]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -236,10 +231,9 @@ REGISTER_CALCULATOR(TfLiteConverterCalculator);
|
||||||
cc->Outputs().HasTag("TENSORS_GPU"));
|
cc->Outputs().HasTag("TENSORS_GPU"));
|
||||||
// Cannot use quantization.
|
// Cannot use quantization.
|
||||||
use_quantized_tensors_ = false;
|
use_quantized_tensors_ = false;
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.Open(cc));
|
MP_RETURN_IF_ERROR(gpu_helper_.Open(cc));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
gpu_helper_ = [[MPPMetalHelper alloc] initWithCalculatorContext:cc];
|
gpu_helper_ = [[MPPMetalHelper alloc] initWithCalculatorContext:cc];
|
||||||
RET_CHECK(gpu_helper_);
|
RET_CHECK(gpu_helper_);
|
||||||
#endif
|
#endif
|
||||||
|
@ -270,11 +264,10 @@ REGISTER_CALCULATOR(TfLiteConverterCalculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
::mediapipe::Status TfLiteConverterCalculator::Close(CalculatorContext* cc) {
|
::mediapipe::Status TfLiteConverterCalculator::Close(CalculatorContext* cc) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
gpu_helper_.RunInGlContext([this] { gpu_data_out_.reset(); });
|
gpu_helper_.RunInGlContext([this] { gpu_data_out_.reset(); });
|
||||||
#endif
|
#endif
|
||||||
#if defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#if defined(MEDIAPIPE_IOS)
|
||||||
gpu_data_out_.reset();
|
gpu_data_out_.reset();
|
||||||
#endif
|
#endif
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
|
@ -390,8 +383,7 @@ REGISTER_CALCULATOR(TfLiteConverterCalculator);
|
||||||
|
|
||||||
::mediapipe::Status TfLiteConverterCalculator::ProcessGPU(
|
::mediapipe::Status TfLiteConverterCalculator::ProcessGPU(
|
||||||
CalculatorContext* cc) {
|
CalculatorContext* cc) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
// GpuBuffer to tflite::gpu::GlBuffer conversion.
|
// GpuBuffer to tflite::gpu::GlBuffer conversion.
|
||||||
const auto& input = cc->Inputs().Tag("IMAGE_GPU").Get<mediapipe::GpuBuffer>();
|
const auto& input = cc->Inputs().Tag("IMAGE_GPU").Get<mediapipe::GpuBuffer>();
|
||||||
MP_RETURN_IF_ERROR(
|
MP_RETURN_IF_ERROR(
|
||||||
|
@ -427,7 +419,7 @@ REGISTER_CALCULATOR(TfLiteConverterCalculator);
|
||||||
cc->Outputs()
|
cc->Outputs()
|
||||||
.Tag("TENSORS_GPU")
|
.Tag("TENSORS_GPU")
|
||||||
.Add(output_tensors.release(), cc->InputTimestamp());
|
.Add(output_tensors.release(), cc->InputTimestamp());
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
// GpuBuffer to id<MTLBuffer> conversion.
|
// GpuBuffer to id<MTLBuffer> conversion.
|
||||||
const auto& input = cc->Inputs().Tag("IMAGE_GPU").Get<mediapipe::GpuBuffer>();
|
const auto& input = cc->Inputs().Tag("IMAGE_GPU").Get<mediapipe::GpuBuffer>();
|
||||||
{
|
{
|
||||||
|
@ -493,8 +485,7 @@ REGISTER_CALCULATOR(TfLiteConverterCalculator);
|
||||||
RET_CHECK_FAIL() << "Num input channels is less than desired output.";
|
RET_CHECK_FAIL() << "Num input channels is less than desired output.";
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif // !MEDIAPIPE_DISABLE_GPU
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext(
|
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext(
|
||||||
[this, &include_alpha, &input, &single_channel]() -> ::mediapipe::Status {
|
[this, &include_alpha, &input, &single_channel]() -> ::mediapipe::Status {
|
||||||
// Device memory.
|
// Device memory.
|
||||||
|
@ -538,7 +529,9 @@ REGISTER_CALCULATOR(TfLiteConverterCalculator);
|
||||||
&gpu_data_out_->program));
|
&gpu_data_out_->program));
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}));
|
}));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
|
||||||
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
|
|
||||||
RET_CHECK(include_alpha)
|
RET_CHECK(include_alpha)
|
||||||
<< "iOS GPU inference currently accepts only RGBA input.";
|
<< "iOS GPU inference currently accepts only RGBA input.";
|
||||||
|
|
||||||
|
@ -619,7 +612,7 @@ REGISTER_CALCULATOR(TfLiteConverterCalculator);
|
||||||
CHECK_GE(max_num_channels_, 1);
|
CHECK_GE(max_num_channels_, 1);
|
||||||
CHECK_LE(max_num_channels_, 4);
|
CHECK_LE(max_num_channels_, 4);
|
||||||
CHECK_NE(max_num_channels_, 2);
|
CHECK_NE(max_num_channels_, 2);
|
||||||
#if defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#if defined(MEDIAPIPE_IOS)
|
||||||
if (cc->Inputs().HasTag("IMAGE_GPU"))
|
if (cc->Inputs().HasTag("IMAGE_GPU"))
|
||||||
// Currently on iOS, tflite gpu input tensor must be 4 channels,
|
// Currently on iOS, tflite gpu input tensor must be 4 channels,
|
||||||
// so input image must be 4 channels also (checked in InitGpu).
|
// so input image must be 4 channels also (checked in InitGpu).
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include "tensorflow/lite/kernels/register.h"
|
#include "tensorflow/lite/kernels/register.h"
|
||||||
#include "tensorflow/lite/model.h"
|
#include "tensorflow/lite/model.h"
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
#include "mediapipe/gpu/gl_calculator_helper.h"
|
#include "mediapipe/gpu/gl_calculator_helper.h"
|
||||||
#include "mediapipe/gpu/gpu_buffer.h"
|
#include "mediapipe/gpu/gpu_buffer.h"
|
||||||
#include "tensorflow/lite/delegates/gpu/common/shape.h"
|
#include "tensorflow/lite/delegates/gpu/common/shape.h"
|
||||||
|
@ -35,9 +35,9 @@
|
||||||
#include "tensorflow/lite/delegates/gpu/gl/gl_program.h"
|
#include "tensorflow/lite/delegates/gpu/gl/gl_program.h"
|
||||||
#include "tensorflow/lite/delegates/gpu/gl/gl_shader.h"
|
#include "tensorflow/lite/delegates/gpu/gl/gl_shader.h"
|
||||||
#include "tensorflow/lite/delegates/gpu/gl_delegate.h"
|
#include "tensorflow/lite/delegates/gpu/gl_delegate.h"
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif // !MEDIAPIPE_DISABLE_GL_COMPUTE
|
||||||
|
|
||||||
#if defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#if defined(MEDIAPIPE_IOS)
|
||||||
#import <CoreVideo/CoreVideo.h>
|
#import <CoreVideo/CoreVideo.h>
|
||||||
#import <Metal/Metal.h>
|
#import <Metal/Metal.h>
|
||||||
#import <MetalKit/MetalKit.h>
|
#import <MetalKit/MetalKit.h>
|
||||||
|
@ -51,12 +51,15 @@
|
||||||
#include "tensorflow/lite/delegates/gpu/metal_delegate_internal.h"
|
#include "tensorflow/lite/delegates/gpu/metal_delegate_internal.h"
|
||||||
#endif // iOS
|
#endif // iOS
|
||||||
|
|
||||||
|
#if defined(MEDIAPIPE_ANDROID)
|
||||||
|
#include "tensorflow/lite/delegates/nnapi/nnapi_delegate.h"
|
||||||
|
#endif // ANDROID
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
typedef ::tflite::gpu::gl::GlBuffer GpuTensor;
|
typedef ::tflite::gpu::gl::GlBuffer GpuTensor;
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
typedef id<MTLBuffer> GpuTensor;
|
typedef id<MTLBuffer> GpuTensor;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -64,14 +67,35 @@ typedef id<MTLBuffer> GpuTensor;
|
||||||
size_t RoundUp(size_t n, size_t m) { return ((n + m - 1) / m) * m; } // NOLINT
|
size_t RoundUp(size_t n, size_t m) { return ((n + m - 1) / m) * m; } // NOLINT
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
#if defined(MEDIAPIPE_EDGE_TPU)
|
||||||
|
#include "edgetpu.h"
|
||||||
|
|
||||||
|
// Creates and returns an Edge TPU interpreter to run the given edgetpu model.
|
||||||
|
std::unique_ptr<tflite::Interpreter> BuildEdgeTpuInterpreter(
|
||||||
|
const tflite::FlatBufferModel& model,
|
||||||
|
tflite::ops::builtin::BuiltinOpResolver* resolver,
|
||||||
|
edgetpu::EdgeTpuContext* edgetpu_context) {
|
||||||
|
resolver->AddCustom(edgetpu::kCustomOp, edgetpu::RegisterCustomOp());
|
||||||
|
std::unique_ptr<tflite::Interpreter> interpreter;
|
||||||
|
if (tflite::InterpreterBuilder(model, *resolver)(&interpreter) != kTfLiteOk) {
|
||||||
|
std::cerr << "Failed to build edge TPU interpreter." << std::endl;
|
||||||
|
}
|
||||||
|
interpreter->SetExternalContext(kTfLiteEdgeTpuContext, edgetpu_context);
|
||||||
|
interpreter->SetNumThreads(1);
|
||||||
|
if (interpreter->AllocateTensors() != kTfLiteOk) {
|
||||||
|
std::cerr << "Failed to allocate edge TPU tensors." << std::endl;
|
||||||
|
}
|
||||||
|
return interpreter;
|
||||||
|
}
|
||||||
|
#endif // MEDIAPIPE_EDGE_TPU
|
||||||
|
|
||||||
// TfLiteInferenceCalculator File Layout:
|
// TfLiteInferenceCalculator File Layout:
|
||||||
// * Header
|
// * Header
|
||||||
// * Core
|
// * Core
|
||||||
// * Aux
|
// * Aux
|
||||||
namespace mediapipe {
|
namespace mediapipe {
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
using ::tflite::gpu::gl::CopyBuffer;
|
using ::tflite::gpu::gl::CopyBuffer;
|
||||||
using ::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer;
|
using ::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer;
|
||||||
using ::tflite::gpu::gl::GlBuffer;
|
using ::tflite::gpu::gl::GlBuffer;
|
||||||
|
@ -150,18 +174,22 @@ class TfLiteInferenceCalculator : public CalculatorBase {
|
||||||
std::unique_ptr<tflite::FlatBufferModel> model_;
|
std::unique_ptr<tflite::FlatBufferModel> model_;
|
||||||
TfLiteDelegate* delegate_ = nullptr;
|
TfLiteDelegate* delegate_ = nullptr;
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
mediapipe::GlCalculatorHelper gpu_helper_;
|
mediapipe::GlCalculatorHelper gpu_helper_;
|
||||||
std::unique_ptr<GPUData> gpu_data_in_;
|
std::unique_ptr<GPUData> gpu_data_in_;
|
||||||
std::vector<std::unique_ptr<GPUData>> gpu_data_out_;
|
std::vector<std::unique_ptr<GPUData>> gpu_data_out_;
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
MPPMetalHelper* gpu_helper_ = nullptr;
|
MPPMetalHelper* gpu_helper_ = nullptr;
|
||||||
std::unique_ptr<GPUData> gpu_data_in_;
|
std::unique_ptr<GPUData> gpu_data_in_;
|
||||||
std::vector<std::unique_ptr<GPUData>> gpu_data_out_;
|
std::vector<std::unique_ptr<GPUData>> gpu_data_out_;
|
||||||
TFLBufferConvert* converter_from_BPHWC4_ = nil;
|
TFLBufferConvert* converter_from_BPHWC4_ = nil;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(MEDIAPIPE_EDGE_TPU)
|
||||||
|
std::shared_ptr<edgetpu::EdgeTpuContext> edgetpu_context_ =
|
||||||
|
edgetpu::EdgeTpuManager::GetSingleton()->OpenDevice();
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string model_path_ = "";
|
std::string model_path_ = "";
|
||||||
bool gpu_inference_ = false;
|
bool gpu_inference_ = false;
|
||||||
bool gpu_input_ = false;
|
bool gpu_input_ = false;
|
||||||
|
@ -210,10 +238,9 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
use_gpu |= options.use_gpu();
|
use_gpu |= options.use_gpu();
|
||||||
|
|
||||||
if (use_gpu) {
|
if (use_gpu) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(mediapipe::GlCalculatorHelper::UpdateContract(cc));
|
MP_RETURN_IF_ERROR(mediapipe::GlCalculatorHelper::UpdateContract(cc));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
MP_RETURN_IF_ERROR([MPPMetalHelper updateContract:cc]);
|
MP_RETURN_IF_ERROR([MPPMetalHelper updateContract:cc]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -253,26 +280,24 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
MP_RETURN_IF_ERROR(LoadModel(cc));
|
MP_RETURN_IF_ERROR(LoadModel(cc));
|
||||||
|
|
||||||
if (gpu_inference_) {
|
if (gpu_inference_) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.Open(cc));
|
MP_RETURN_IF_ERROR(gpu_helper_.Open(cc));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
gpu_helper_ = [[MPPMetalHelper alloc] initWithCalculatorContext:cc];
|
gpu_helper_ = [[MPPMetalHelper alloc] initWithCalculatorContext:cc];
|
||||||
RET_CHECK(gpu_helper_);
|
RET_CHECK(gpu_helper_);
|
||||||
#endif
|
#endif
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
|
||||||
!defined(__APPLE__)
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext(
|
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext(
|
||||||
[this, &cc]() -> ::mediapipe::Status { return LoadDelegate(cc); }));
|
[this, &cc]() -> ::mediapipe::Status { return LoadDelegate(cc); }));
|
||||||
#else
|
#else
|
||||||
MP_RETURN_IF_ERROR(LoadDelegate(cc));
|
MP_RETURN_IF_ERROR(LoadDelegate(cc));
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
#if defined(__EMSCRIPTEN__) || defined(MEDIAPIPE_ANDROID)
|
||||||
|
MP_RETURN_IF_ERROR(LoadDelegate(cc));
|
||||||
|
#endif // __EMSCRIPTEN__ || ANDROID
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__EMSCRIPTEN__)
|
|
||||||
MP_RETURN_IF_ERROR(LoadDelegate(cc));
|
|
||||||
#endif // __EMSCRIPTEN__
|
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,8 +305,7 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
// 1. Receive pre-processed tensor inputs.
|
// 1. Receive pre-processed tensor inputs.
|
||||||
if (gpu_input_) {
|
if (gpu_input_) {
|
||||||
// Read GPU input into SSBO.
|
// Read GPU input into SSBO.
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
const auto& input_tensors =
|
const auto& input_tensors =
|
||||||
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GpuTensor>>();
|
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GpuTensor>>();
|
||||||
RET_CHECK_EQ(input_tensors.size(), 1);
|
RET_CHECK_EQ(input_tensors.size(), 1);
|
||||||
|
@ -291,7 +315,7 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
RET_CHECK_CALL(CopyBuffer(input_tensors[0], gpu_data_in_->buffer));
|
RET_CHECK_CALL(CopyBuffer(input_tensors[0], gpu_data_in_->buffer));
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}));
|
}));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
const auto& input_tensors =
|
const auto& input_tensors =
|
||||||
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GpuTensor>>();
|
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GpuTensor>>();
|
||||||
RET_CHECK_EQ(input_tensors.size(), 1);
|
RET_CHECK_EQ(input_tensors.size(), 1);
|
||||||
|
@ -327,14 +351,13 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
|
|
||||||
// 2. Run inference.
|
// 2. Run inference.
|
||||||
if (gpu_inference_) {
|
if (gpu_inference_) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(
|
MP_RETURN_IF_ERROR(
|
||||||
gpu_helper_.RunInGlContext([this]() -> ::mediapipe::Status {
|
gpu_helper_.RunInGlContext([this]() -> ::mediapipe::Status {
|
||||||
RET_CHECK_EQ(interpreter_->Invoke(), kTfLiteOk);
|
RET_CHECK_EQ(interpreter_->Invoke(), kTfLiteOk);
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}));
|
}));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
RET_CHECK_EQ(interpreter_->Invoke(), kTfLiteOk);
|
RET_CHECK_EQ(interpreter_->Invoke(), kTfLiteOk);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
|
@ -343,8 +366,7 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
|
|
||||||
// 3. Output processed tensors.
|
// 3. Output processed tensors.
|
||||||
if (gpu_output_) {
|
if (gpu_output_) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
// Output result tensors (GPU).
|
// Output result tensors (GPU).
|
||||||
auto output_tensors = absl::make_unique<std::vector<GpuTensor>>();
|
auto output_tensors = absl::make_unique<std::vector<GpuTensor>>();
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext(
|
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext(
|
||||||
|
@ -361,7 +383,7 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
cc->Outputs()
|
cc->Outputs()
|
||||||
.Tag("TENSORS_GPU")
|
.Tag("TENSORS_GPU")
|
||||||
.Add(output_tensors.release(), cc->InputTimestamp());
|
.Add(output_tensors.release(), cc->InputTimestamp());
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
// Output result tensors (GPU).
|
// Output result tensors (GPU).
|
||||||
auto output_tensors = absl::make_unique<std::vector<GpuTensor>>();
|
auto output_tensors = absl::make_unique<std::vector<GpuTensor>>();
|
||||||
output_tensors->resize(gpu_data_out_.size());
|
output_tensors->resize(gpu_data_out_.size());
|
||||||
|
@ -406,25 +428,29 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
|
|
||||||
::mediapipe::Status TfLiteInferenceCalculator::Close(CalculatorContext* cc) {
|
::mediapipe::Status TfLiteInferenceCalculator::Close(CalculatorContext* cc) {
|
||||||
if (delegate_) {
|
if (delegate_) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
if (gpu_inference_) {
|
||||||
!defined(__APPLE__)
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext([this]() -> Status {
|
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext([this]() -> Status {
|
||||||
TfLiteGpuDelegateDelete(delegate_);
|
TfLiteGpuDelegateDelete(delegate_);
|
||||||
|
gpu_data_in_.reset();
|
||||||
|
for (int i = 0; i < gpu_data_out_.size(); ++i) {
|
||||||
|
gpu_data_out_[i].reset();
|
||||||
|
}
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}));
|
||||||
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
|
TFLGpuDelegateDelete(delegate_);
|
||||||
gpu_data_in_.reset();
|
gpu_data_in_.reset();
|
||||||
for (int i = 0; i < gpu_data_out_.size(); ++i) {
|
for (int i = 0; i < gpu_data_out_.size(); ++i) {
|
||||||
gpu_data_out_[i].reset();
|
gpu_data_out_[i].reset();
|
||||||
}
|
}
|
||||||
return ::mediapipe::OkStatus();
|
|
||||||
}));
|
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
|
||||||
TFLGpuDelegateDelete(delegate_);
|
|
||||||
gpu_data_in_.reset();
|
|
||||||
for (int i = 0; i < gpu_data_out_.size(); ++i) {
|
|
||||||
gpu_data_out_[i].reset();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
delegate_ = nullptr;
|
delegate_ = nullptr;
|
||||||
}
|
}
|
||||||
|
#if defined(MEDIAPIPE_EDGE_TPU)
|
||||||
|
edgetpu_context_.reset();
|
||||||
|
#endif
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,16 +484,18 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
model_ = tflite::FlatBufferModel::BuildFromFile(model_path_.c_str());
|
model_ = tflite::FlatBufferModel::BuildFromFile(model_path_.c_str());
|
||||||
RET_CHECK(model_);
|
RET_CHECK(model_);
|
||||||
|
|
||||||
|
tflite::ops::builtin::BuiltinOpResolver op_resolver;
|
||||||
if (cc->InputSidePackets().HasTag("CUSTOM_OP_RESOLVER")) {
|
if (cc->InputSidePackets().HasTag("CUSTOM_OP_RESOLVER")) {
|
||||||
const auto& op_resolver =
|
op_resolver = cc->InputSidePackets()
|
||||||
cc->InputSidePackets()
|
.Tag("CUSTOM_OP_RESOLVER")
|
||||||
.Tag("CUSTOM_OP_RESOLVER")
|
.Get<tflite::ops::builtin::BuiltinOpResolver>();
|
||||||
.Get<tflite::ops::builtin::BuiltinOpResolver>();
|
|
||||||
tflite::InterpreterBuilder(*model_, op_resolver)(&interpreter_);
|
|
||||||
} else {
|
|
||||||
const tflite::ops::builtin::BuiltinOpResolver op_resolver;
|
|
||||||
tflite::InterpreterBuilder(*model_, op_resolver)(&interpreter_);
|
|
||||||
}
|
}
|
||||||
|
#if defined(MEDIAPIPE_EDGE_TPU)
|
||||||
|
interpreter_ =
|
||||||
|
BuildEdgeTpuInterpreter(*model_, &op_resolver, edgetpu_context_.get());
|
||||||
|
#else
|
||||||
|
tflite::InterpreterBuilder(*model_, op_resolver)(&interpreter_);
|
||||||
|
#endif // MEDIAPIPE_EDGE_TPU
|
||||||
|
|
||||||
RET_CHECK(interpreter_);
|
RET_CHECK(interpreter_);
|
||||||
|
|
||||||
|
@ -490,8 +518,22 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
|
|
||||||
::mediapipe::Status TfLiteInferenceCalculator::LoadDelegate(
|
::mediapipe::Status TfLiteInferenceCalculator::LoadDelegate(
|
||||||
CalculatorContext* cc) {
|
CalculatorContext* cc) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if defined(MEDIAPIPE_ANDROID)
|
||||||
!defined(__APPLE__)
|
if (!gpu_inference_) {
|
||||||
|
if (cc->Options<mediapipe::TfLiteInferenceCalculatorOptions>()
|
||||||
|
.use_nnapi()) {
|
||||||
|
// Attempt to use NNAPI.
|
||||||
|
// If not supported, the default CPU delegate will be created and used.
|
||||||
|
interpreter_->SetAllowFp16PrecisionForFp32(1);
|
||||||
|
delegate_ = tflite::NnApiDelegate();
|
||||||
|
RET_CHECK_EQ(interpreter_->ModifyGraphWithDelegate(delegate_), kTfLiteOk);
|
||||||
|
}
|
||||||
|
// Return, no need for GPU delegate below.
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
#endif // ANDROID
|
||||||
|
|
||||||
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
// Configure and create the delegate.
|
// Configure and create the delegate.
|
||||||
TfLiteGpuDelegateOptions options = TfLiteGpuDelegateOptionsDefault();
|
TfLiteGpuDelegateOptions options = TfLiteGpuDelegateOptionsDefault();
|
||||||
options.compile_options.precision_loss_allowed = 1;
|
options.compile_options.precision_loss_allowed = 1;
|
||||||
|
@ -551,7 +593,7 @@ REGISTER_CALCULATOR(TfLiteInferenceCalculator);
|
||||||
RET_CHECK_EQ(interpreter_->ModifyGraphWithDelegate(delegate_), kTfLiteOk);
|
RET_CHECK_EQ(interpreter_->ModifyGraphWithDelegate(delegate_), kTfLiteOk);
|
||||||
#endif // OpenGL
|
#endif // OpenGL
|
||||||
|
|
||||||
#if defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#if defined(MEDIAPIPE_IOS)
|
||||||
// Configure and create the delegate.
|
// Configure and create the delegate.
|
||||||
TFLGpuDelegateOptions options;
|
TFLGpuDelegateOptions options;
|
||||||
options.allow_precision_loss = false; // Must match converter, F=float/T=half
|
options.allow_precision_loss = false; // Must match converter, F=float/T=half
|
||||||
|
|
|
@ -45,4 +45,9 @@ message TfLiteInferenceCalculatorOptions {
|
||||||
// input tensors are on CPU. For input tensors on GPU, GPU backend is always
|
// input tensors are on CPU. For input tensors on GPU, GPU backend is always
|
||||||
// used.
|
// used.
|
||||||
optional bool use_gpu = 2 [default = false];
|
optional bool use_gpu = 2 [default = false];
|
||||||
|
|
||||||
|
// Android only. When true, an NNAPI delegate will be used for inference.
|
||||||
|
// If NNAPI is not available, then the default CPU delegate will be used
|
||||||
|
// automatically.
|
||||||
|
optional bool use_nnapi = 3 [default = false];
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,7 @@
|
||||||
#include "mediapipe/framework/port/ret_check.h"
|
#include "mediapipe/framework/port/ret_check.h"
|
||||||
#include "mediapipe/util/resource_util.h"
|
#include "mediapipe/util/resource_util.h"
|
||||||
#include "tensorflow/lite/interpreter.h"
|
#include "tensorflow/lite/interpreter.h"
|
||||||
#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || \
|
#if defined(MEDIAPIPE_MOBILE)
|
||||||
(defined(__APPLE__) && !TARGET_OS_OSX)
|
|
||||||
#include "mediapipe/util/android/file/base/file.h"
|
#include "mediapipe/util/android/file/base/file.h"
|
||||||
#include "mediapipe/util/android/file/base/helpers.h"
|
#include "mediapipe/util/android/file/base/helpers.h"
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -27,8 +27,7 @@
|
||||||
#include "mediapipe/framework/port/ret_check.h"
|
#include "mediapipe/framework/port/ret_check.h"
|
||||||
#include "tensorflow/lite/interpreter.h"
|
#include "tensorflow/lite/interpreter.h"
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
#include "mediapipe/gpu/gl_calculator_helper.h"
|
#include "mediapipe/gpu/gl_calculator_helper.h"
|
||||||
#include "tensorflow/lite/delegates/gpu/gl/gl_buffer.h"
|
#include "tensorflow/lite/delegates/gpu/gl/gl_buffer.h"
|
||||||
#include "tensorflow/lite/delegates/gpu/gl/gl_program.h"
|
#include "tensorflow/lite/delegates/gpu/gl/gl_program.h"
|
||||||
|
@ -36,7 +35,7 @@
|
||||||
#include "tensorflow/lite/delegates/gpu/gl_delegate.h"
|
#include "tensorflow/lite/delegates/gpu/gl_delegate.h"
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif // !MEDIAPIPE_DISABLE_GPU
|
||||||
|
|
||||||
#if defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#if defined(MEDIAPIPE_IOS)
|
||||||
#import <CoreVideo/CoreVideo.h>
|
#import <CoreVideo/CoreVideo.h>
|
||||||
#import <Metal/Metal.h>
|
#import <Metal/Metal.h>
|
||||||
#import <MetalKit/MetalKit.h>
|
#import <MetalKit/MetalKit.h>
|
||||||
|
@ -56,17 +55,15 @@ constexpr int kNumCoordsPerBox = 4;
|
||||||
|
|
||||||
namespace mediapipe {
|
namespace mediapipe {
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
using ::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer;
|
using ::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer;
|
||||||
using ::tflite::gpu::gl::GlShader;
|
using ::tflite::gpu::gl::GlShader;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
typedef ::tflite::gpu::gl::GlBuffer GpuTensor;
|
typedef ::tflite::gpu::gl::GlBuffer GpuTensor;
|
||||||
typedef ::tflite::gpu::gl::GlProgram GpuProgram;
|
typedef ::tflite::gpu::gl::GlProgram GpuProgram;
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
typedef id<MTLBuffer> GpuTensor;
|
typedef id<MTLBuffer> GpuTensor;
|
||||||
typedef id<MTLComputePipelineState> GpuProgram;
|
typedef id<MTLComputePipelineState> GpuProgram;
|
||||||
#endif
|
#endif
|
||||||
|
@ -183,11 +180,10 @@ class TfLiteTensorsToDetectionsCalculator : public CalculatorBase {
|
||||||
std::vector<Anchor> anchors_;
|
std::vector<Anchor> anchors_;
|
||||||
bool side_packet_anchors_{};
|
bool side_packet_anchors_{};
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
mediapipe::GlCalculatorHelper gpu_helper_;
|
mediapipe::GlCalculatorHelper gpu_helper_;
|
||||||
std::unique_ptr<GPUData> gpu_data_;
|
std::unique_ptr<GPUData> gpu_data_;
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
MPPMetalHelper* gpu_helper_ = nullptr;
|
MPPMetalHelper* gpu_helper_ = nullptr;
|
||||||
std::unique_ptr<GPUData> gpu_data_;
|
std::unique_ptr<GPUData> gpu_data_;
|
||||||
#endif
|
#endif
|
||||||
|
@ -226,10 +222,9 @@ REGISTER_CALCULATOR(TfLiteTensorsToDetectionsCalculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_gpu) {
|
if (use_gpu) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(mediapipe::GlCalculatorHelper::UpdateContract(cc));
|
MP_RETURN_IF_ERROR(mediapipe::GlCalculatorHelper::UpdateContract(cc));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
MP_RETURN_IF_ERROR([MPPMetalHelper updateContract:cc]);
|
MP_RETURN_IF_ERROR([MPPMetalHelper updateContract:cc]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -243,10 +238,9 @@ REGISTER_CALCULATOR(TfLiteTensorsToDetectionsCalculator);
|
||||||
|
|
||||||
if (cc->Inputs().HasTag("TENSORS_GPU")) {
|
if (cc->Inputs().HasTag("TENSORS_GPU")) {
|
||||||
gpu_input_ = true;
|
gpu_input_ = true;
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.Open(cc));
|
MP_RETURN_IF_ERROR(gpu_helper_.Open(cc));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
gpu_helper_ = [[MPPMetalHelper alloc] initWithCalculatorContext:cc];
|
gpu_helper_ = [[MPPMetalHelper alloc] initWithCalculatorContext:cc];
|
||||||
RET_CHECK(gpu_helper_);
|
RET_CHECK(gpu_helper_);
|
||||||
#endif
|
#endif
|
||||||
|
@ -406,8 +400,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToDetectionsCalculator);
|
||||||
}
|
}
|
||||||
::mediapipe::Status TfLiteTensorsToDetectionsCalculator::ProcessGPU(
|
::mediapipe::Status TfLiteTensorsToDetectionsCalculator::ProcessGPU(
|
||||||
CalculatorContext* cc, std::vector<Detection>* output_detections) {
|
CalculatorContext* cc, std::vector<Detection>* output_detections) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
const auto& input_tensors =
|
const auto& input_tensors =
|
||||||
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GpuTensor>>();
|
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GpuTensor>>();
|
||||||
RET_CHECK_GE(input_tensors.size(), 2);
|
RET_CHECK_GE(input_tensors.size(), 2);
|
||||||
|
@ -470,7 +463,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToDetectionsCalculator);
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}));
|
}));
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
|
|
||||||
const auto& input_tensors =
|
const auto& input_tensors =
|
||||||
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GpuTensor>>();
|
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GpuTensor>>();
|
||||||
|
@ -569,12 +562,11 @@ REGISTER_CALCULATOR(TfLiteTensorsToDetectionsCalculator);
|
||||||
|
|
||||||
::mediapipe::Status TfLiteTensorsToDetectionsCalculator::Close(
|
::mediapipe::Status TfLiteTensorsToDetectionsCalculator::Close(
|
||||||
CalculatorContext* cc) {
|
CalculatorContext* cc) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
gpu_helper_.RunInGlContext([this] { gpu_data_.reset(); });
|
gpu_helper_.RunInGlContext([this] { gpu_data_.reset(); });
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
gpu_data_.reset();
|
gpu_data_.reset();
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
@ -723,8 +715,7 @@ Detection TfLiteTensorsToDetectionsCalculator::ConvertToDetection(
|
||||||
|
|
||||||
::mediapipe::Status TfLiteTensorsToDetectionsCalculator::GpuInit(
|
::mediapipe::Status TfLiteTensorsToDetectionsCalculator::GpuInit(
|
||||||
CalculatorContext* cc) {
|
CalculatorContext* cc) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext([this]()
|
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext([this]()
|
||||||
-> ::mediapipe::Status {
|
-> ::mediapipe::Status {
|
||||||
gpu_data_ = absl::make_unique<GPUData>();
|
gpu_data_ = absl::make_unique<GPUData>();
|
||||||
|
@ -937,8 +928,7 @@ void main() {
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
#elif defined(__APPLE__) && !TARGET_OS_OSX // iOS
|
#elif defined(MEDIAPIPE_IOS)
|
||||||
// TODO consolidate Metal and OpenGL shaders via vulkan.
|
|
||||||
|
|
||||||
gpu_data_ = absl::make_unique<GPUData>();
|
gpu_data_ = absl::make_unique<GPUData>();
|
||||||
id<MTLDevice> device = gpu_helper_.mtlDevice;
|
id<MTLDevice> device = gpu_helper_.mtlDevice;
|
||||||
|
@ -1168,7 +1158,7 @@ kernel void scoreKernel(
|
||||||
CHECK_LT(num_classes_, max_wg_size) << "# classes must be <" << max_wg_size;
|
CHECK_LT(num_classes_, max_wg_size) << "# classes must be <" << max_wg_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __ANDROID__ or iOS
|
#endif // !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,11 +76,11 @@ REGISTER_CALCULATOR(TfLiteTensorsToLandmarksCalculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cc->Outputs().HasTag("LANDMARKS")) {
|
if (cc->Outputs().HasTag("LANDMARKS")) {
|
||||||
cc->Outputs().Tag("LANDMARKS").Set<std::vector<Landmark>>();
|
cc->Outputs().Tag("LANDMARKS").Set<LandmarkList>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cc->Outputs().HasTag("NORM_LANDMARKS")) {
|
if (cc->Outputs().HasTag("NORM_LANDMARKS")) {
|
||||||
cc->Outputs().Tag("NORM_LANDMARKS").Set<std::vector<NormalizedLandmark>>();
|
cc->Outputs().Tag("NORM_LANDMARKS").Set<NormalizedLandmarkList>();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
|
@ -127,54 +127,55 @@ REGISTER_CALCULATOR(TfLiteTensorsToLandmarksCalculator);
|
||||||
|
|
||||||
const float* raw_landmarks = raw_tensor->data.f;
|
const float* raw_landmarks = raw_tensor->data.f;
|
||||||
|
|
||||||
auto output_landmarks = absl::make_unique<std::vector<Landmark>>();
|
LandmarkList output_landmarks;
|
||||||
|
|
||||||
for (int ld = 0; ld < num_landmarks_; ++ld) {
|
for (int ld = 0; ld < num_landmarks_; ++ld) {
|
||||||
const int offset = ld * num_dimensions;
|
const int offset = ld * num_dimensions;
|
||||||
Landmark landmark;
|
Landmark* landmark = output_landmarks.add_landmark();
|
||||||
|
|
||||||
if (options_.flip_horizontally()) {
|
if (options_.flip_horizontally()) {
|
||||||
landmark.set_x(options_.input_image_width() - raw_landmarks[offset]);
|
landmark->set_x(options_.input_image_width() - raw_landmarks[offset]);
|
||||||
} else {
|
} else {
|
||||||
landmark.set_x(raw_landmarks[offset]);
|
landmark->set_x(raw_landmarks[offset]);
|
||||||
}
|
}
|
||||||
if (num_dimensions > 1) {
|
if (num_dimensions > 1) {
|
||||||
if (options_.flip_vertically()) {
|
if (options_.flip_vertically()) {
|
||||||
landmark.set_y(options_.input_image_height() -
|
landmark->set_y(options_.input_image_height() -
|
||||||
raw_landmarks[offset + 1]);
|
raw_landmarks[offset + 1]);
|
||||||
} else {
|
} else {
|
||||||
landmark.set_y(raw_landmarks[offset + 1]);
|
landmark->set_y(raw_landmarks[offset + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (num_dimensions > 2) {
|
if (num_dimensions > 2) {
|
||||||
landmark.set_z(raw_landmarks[offset + 2]);
|
landmark->set_z(raw_landmarks[offset + 2]);
|
||||||
}
|
}
|
||||||
output_landmarks->push_back(landmark);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output normalized landmarks if required.
|
// Output normalized landmarks if required.
|
||||||
if (cc->Outputs().HasTag("NORM_LANDMARKS")) {
|
if (cc->Outputs().HasTag("NORM_LANDMARKS")) {
|
||||||
auto output_norm_landmarks =
|
NormalizedLandmarkList output_norm_landmarks;
|
||||||
absl::make_unique<std::vector<NormalizedLandmark>>();
|
// for (const auto& landmark : output_landmarks) {
|
||||||
for (const auto& landmark : *output_landmarks) {
|
for (int i = 0; i < output_landmarks.landmark_size(); ++i) {
|
||||||
NormalizedLandmark norm_landmark;
|
const Landmark& landmark = output_landmarks.landmark(i);
|
||||||
norm_landmark.set_x(static_cast<float>(landmark.x()) /
|
NormalizedLandmark* norm_landmark = output_norm_landmarks.add_landmark();
|
||||||
options_.input_image_width());
|
norm_landmark->set_x(static_cast<float>(landmark.x()) /
|
||||||
norm_landmark.set_y(static_cast<float>(landmark.y()) /
|
options_.input_image_width());
|
||||||
options_.input_image_height());
|
norm_landmark->set_y(static_cast<float>(landmark.y()) /
|
||||||
norm_landmark.set_z(landmark.z() / options_.normalize_z());
|
options_.input_image_height());
|
||||||
|
norm_landmark->set_z(landmark.z() / options_.normalize_z());
|
||||||
output_norm_landmarks->push_back(norm_landmark);
|
|
||||||
}
|
}
|
||||||
cc->Outputs()
|
cc->Outputs()
|
||||||
.Tag("NORM_LANDMARKS")
|
.Tag("NORM_LANDMARKS")
|
||||||
.Add(output_norm_landmarks.release(), cc->InputTimestamp());
|
.AddPacket(MakePacket<NormalizedLandmarkList>(output_norm_landmarks)
|
||||||
|
.At(cc->InputTimestamp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output absolute landmarks.
|
// Output absolute landmarks.
|
||||||
if (cc->Outputs().HasTag("LANDMARKS")) {
|
if (cc->Outputs().HasTag("LANDMARKS")) {
|
||||||
cc->Outputs()
|
cc->Outputs()
|
||||||
.Tag("LANDMARKS")
|
.Tag("LANDMARKS")
|
||||||
.Add(output_landmarks.release(), cc->InputTimestamp());
|
.AddPacket(MakePacket<LandmarkList>(output_landmarks)
|
||||||
|
.At(cc->InputTimestamp()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
|
|
|
@ -28,8 +28,7 @@
|
||||||
#include "mediapipe/util/resource_util.h"
|
#include "mediapipe/util/resource_util.h"
|
||||||
#include "tensorflow/lite/interpreter.h"
|
#include "tensorflow/lite/interpreter.h"
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
#include "mediapipe/gpu/gl_calculator_helper.h"
|
#include "mediapipe/gpu/gl_calculator_helper.h"
|
||||||
#include "mediapipe/gpu/gl_simple_shaders.h"
|
#include "mediapipe/gpu/gl_simple_shaders.h"
|
||||||
#include "mediapipe/gpu/shader_util.h"
|
#include "mediapipe/gpu/shader_util.h"
|
||||||
|
@ -54,8 +53,7 @@ float Clamp(float val, float min, float max) {
|
||||||
|
|
||||||
namespace mediapipe {
|
namespace mediapipe {
|
||||||
|
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
using ::tflite::gpu::gl::CopyBuffer;
|
using ::tflite::gpu::gl::CopyBuffer;
|
||||||
using ::tflite::gpu::gl::CreateReadWriteRgbaImageTexture;
|
using ::tflite::gpu::gl::CreateReadWriteRgbaImageTexture;
|
||||||
using ::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer;
|
using ::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer;
|
||||||
|
@ -131,8 +129,7 @@ class TfLiteTensorsToSegmentationCalculator : public CalculatorBase {
|
||||||
int tensor_channels_ = 0;
|
int tensor_channels_ = 0;
|
||||||
|
|
||||||
bool use_gpu_ = false;
|
bool use_gpu_ = false;
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
mediapipe::GlCalculatorHelper gpu_helper_;
|
mediapipe::GlCalculatorHelper gpu_helper_;
|
||||||
std::unique_ptr<GlProgram> mask_program_with_prev_;
|
std::unique_ptr<GlProgram> mask_program_with_prev_;
|
||||||
std::unique_ptr<GlProgram> mask_program_no_prev_;
|
std::unique_ptr<GlProgram> mask_program_no_prev_;
|
||||||
|
@ -162,8 +159,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToSegmentationCalculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inputs GPU.
|
// Inputs GPU.
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
if (cc->Inputs().HasTag("TENSORS_GPU")) {
|
if (cc->Inputs().HasTag("TENSORS_GPU")) {
|
||||||
cc->Inputs().Tag("TENSORS_GPU").Set<std::vector<GlBuffer>>();
|
cc->Inputs().Tag("TENSORS_GPU").Set<std::vector<GlBuffer>>();
|
||||||
use_gpu |= true;
|
use_gpu |= true;
|
||||||
|
@ -182,8 +178,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToSegmentationCalculator);
|
||||||
if (cc->Outputs().HasTag("MASK")) {
|
if (cc->Outputs().HasTag("MASK")) {
|
||||||
cc->Outputs().Tag("MASK").Set<ImageFrame>();
|
cc->Outputs().Tag("MASK").Set<ImageFrame>();
|
||||||
}
|
}
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
if (cc->Outputs().HasTag("MASK_GPU")) {
|
if (cc->Outputs().HasTag("MASK_GPU")) {
|
||||||
cc->Outputs().Tag("MASK_GPU").Set<mediapipe::GpuBuffer>();
|
cc->Outputs().Tag("MASK_GPU").Set<mediapipe::GpuBuffer>();
|
||||||
use_gpu |= true;
|
use_gpu |= true;
|
||||||
|
@ -191,8 +186,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToSegmentationCalculator);
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif // !MEDIAPIPE_DISABLE_GPU
|
||||||
|
|
||||||
if (use_gpu) {
|
if (use_gpu) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(mediapipe::GlCalculatorHelper::UpdateContract(cc));
|
MP_RETURN_IF_ERROR(mediapipe::GlCalculatorHelper::UpdateContract(cc));
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif // !MEDIAPIPE_DISABLE_GPU
|
||||||
}
|
}
|
||||||
|
@ -205,8 +199,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToSegmentationCalculator);
|
||||||
|
|
||||||
if (cc->Inputs().HasTag("TENSORS_GPU")) {
|
if (cc->Inputs().HasTag("TENSORS_GPU")) {
|
||||||
use_gpu_ = true;
|
use_gpu_ = true;
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.Open(cc));
|
MP_RETURN_IF_ERROR(gpu_helper_.Open(cc));
|
||||||
#endif // !MEDIAPIPE_DISABLE_GPU
|
#endif // !MEDIAPIPE_DISABLE_GPU
|
||||||
}
|
}
|
||||||
|
@ -214,8 +207,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToSegmentationCalculator);
|
||||||
MP_RETURN_IF_ERROR(LoadOptions(cc));
|
MP_RETURN_IF_ERROR(LoadOptions(cc));
|
||||||
|
|
||||||
if (use_gpu_) {
|
if (use_gpu_) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(
|
MP_RETURN_IF_ERROR(
|
||||||
gpu_helper_.RunInGlContext([this, cc]() -> ::mediapipe::Status {
|
gpu_helper_.RunInGlContext([this, cc]() -> ::mediapipe::Status {
|
||||||
MP_RETURN_IF_ERROR(InitGpu(cc));
|
MP_RETURN_IF_ERROR(InitGpu(cc));
|
||||||
|
@ -232,8 +224,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToSegmentationCalculator);
|
||||||
::mediapipe::Status TfLiteTensorsToSegmentationCalculator::Process(
|
::mediapipe::Status TfLiteTensorsToSegmentationCalculator::Process(
|
||||||
CalculatorContext* cc) {
|
CalculatorContext* cc) {
|
||||||
if (use_gpu_) {
|
if (use_gpu_) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(
|
MP_RETURN_IF_ERROR(
|
||||||
gpu_helper_.RunInGlContext([this, cc]() -> ::mediapipe::Status {
|
gpu_helper_.RunInGlContext([this, cc]() -> ::mediapipe::Status {
|
||||||
MP_RETURN_IF_ERROR(ProcessGpu(cc));
|
MP_RETURN_IF_ERROR(ProcessGpu(cc));
|
||||||
|
@ -249,8 +240,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToSegmentationCalculator);
|
||||||
|
|
||||||
::mediapipe::Status TfLiteTensorsToSegmentationCalculator::Close(
|
::mediapipe::Status TfLiteTensorsToSegmentationCalculator::Close(
|
||||||
CalculatorContext* cc) {
|
CalculatorContext* cc) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
gpu_helper_.RunInGlContext([this] {
|
gpu_helper_.RunInGlContext([this] {
|
||||||
if (upsample_program_) glDeleteProgram(upsample_program_);
|
if (upsample_program_) glDeleteProgram(upsample_program_);
|
||||||
upsample_program_ = 0;
|
upsample_program_ = 0;
|
||||||
|
@ -377,8 +367,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToSegmentationCalculator);
|
||||||
if (cc->Inputs().Tag("TENSORS_GPU").IsEmpty()) {
|
if (cc->Inputs().Tag("TENSORS_GPU").IsEmpty()) {
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
// Get input streams.
|
// Get input streams.
|
||||||
const auto& input_tensors =
|
const auto& input_tensors =
|
||||||
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GlBuffer>>();
|
cc->Inputs().Tag("TENSORS_GPU").Get<std::vector<GlBuffer>>();
|
||||||
|
@ -464,8 +453,7 @@ REGISTER_CALCULATOR(TfLiteTensorsToSegmentationCalculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TfLiteTensorsToSegmentationCalculator::GlRender() {
|
void TfLiteTensorsToSegmentationCalculator::GlRender() {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
static const GLfloat square_vertices[] = {
|
static const GLfloat square_vertices[] = {
|
||||||
-1.0f, -1.0f, // bottom left
|
-1.0f, -1.0f, // bottom left
|
||||||
1.0f, -1.0f, // bottom right
|
1.0f, -1.0f, // bottom right
|
||||||
|
@ -537,8 +525,7 @@ void TfLiteTensorsToSegmentationCalculator::GlRender() {
|
||||||
|
|
||||||
::mediapipe::Status TfLiteTensorsToSegmentationCalculator::InitGpu(
|
::mediapipe::Status TfLiteTensorsToSegmentationCalculator::InitGpu(
|
||||||
CalculatorContext* cc) {
|
CalculatorContext* cc) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU) && !defined(__EMSCRIPTEN__) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE)
|
||||||
!defined(__APPLE__)
|
|
||||||
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext([this]()
|
MP_RETURN_IF_ERROR(gpu_helper_.RunInGlContext([this]()
|
||||||
-> ::mediapipe::Status {
|
-> ::mediapipe::Status {
|
||||||
// A shader to process a segmentation tensor into an output mask,
|
// A shader to process a segmentation tensor into an output mask,
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
#include "mediapipe/framework/port/status.h"
|
#include "mediapipe/framework/port/status.h"
|
||||||
#include "mediapipe/util/resource_util.h"
|
#include "mediapipe/util/resource_util.h"
|
||||||
|
|
||||||
#if defined(MEDIAPIPE_LITE) || defined(__EMSCRIPTEN__) || \
|
#if defined(MEDIAPIPE_MOBILE)
|
||||||
defined(__ANDROID__) || (defined(__APPLE__) && !TARGET_OS_OSX)
|
|
||||||
#include "mediapipe/util/android/file/base/file.h"
|
#include "mediapipe/util/android/file/base/file.h"
|
||||||
#include "mediapipe/util/android/file/base/helpers.h"
|
#include "mediapipe/util/android/file/base/helpers.h"
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -27,8 +27,8 @@ typedef FilterCollectionCalculator<std::vector<::mediapipe::NormalizedRect>>
|
||||||
REGISTER_CALCULATOR(FilterNormalizedRectCollectionCalculator);
|
REGISTER_CALCULATOR(FilterNormalizedRectCollectionCalculator);
|
||||||
|
|
||||||
typedef FilterCollectionCalculator<
|
typedef FilterCollectionCalculator<
|
||||||
std::vector<std::vector<::mediapipe::NormalizedLandmark>>>
|
std::vector<::mediapipe::NormalizedLandmarkList>>
|
||||||
FilterLandmarksCollectionCalculator;
|
FilterLandmarkListCollectionCalculator;
|
||||||
REGISTER_CALCULATOR(FilterLandmarksCollectionCalculator);
|
REGISTER_CALCULATOR(FilterLandmarkListCollectionCalculator);
|
||||||
|
|
||||||
} // namespace mediapipe
|
} // namespace mediapipe
|
||||||
|
|
|
@ -93,6 +93,7 @@ REGISTER_CALCULATOR(LabelsToRenderDataCalculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
::mediapipe::Status LabelsToRenderDataCalculator::Open(CalculatorContext* cc) {
|
::mediapipe::Status LabelsToRenderDataCalculator::Open(CalculatorContext* cc) {
|
||||||
|
cc->SetOffset(TimestampDiff(0));
|
||||||
options_ = cc->Options<LabelsToRenderDataCalculatorOptions>();
|
options_ = cc->Options<LabelsToRenderDataCalculatorOptions>();
|
||||||
num_colors_ = options_.color_size();
|
num_colors_ = options_.color_size();
|
||||||
label_height_px_ = std::ceil(options_.font_height_px() * kFontHeightScale);
|
label_height_px_ = std::ceil(options_.font_height_px() * kFontHeightScale);
|
||||||
|
|
|
@ -49,7 +49,7 @@ constexpr char kLetterboxPaddingTag[] = "LETTERBOX_PADDING";
|
||||||
// corresponding input image before letterboxing.
|
// corresponding input image before letterboxing.
|
||||||
//
|
//
|
||||||
// Input:
|
// Input:
|
||||||
// LANDMARKS: An std::vector<NormalizedLandmark> representing landmarks on an
|
// LANDMARKS: A NormalizedLandmarkList representing landmarks on an
|
||||||
// letterboxed image.
|
// letterboxed image.
|
||||||
//
|
//
|
||||||
// LETTERBOX_PADDING: An std::array<float, 4> representing the letterbox
|
// LETTERBOX_PADDING: An std::array<float, 4> representing the letterbox
|
||||||
|
@ -57,7 +57,7 @@ constexpr char kLetterboxPaddingTag[] = "LETTERBOX_PADDING";
|
||||||
// image, normalized to [0.f, 1.f] by the letterboxed image dimensions.
|
// image, normalized to [0.f, 1.f] by the letterboxed image dimensions.
|
||||||
//
|
//
|
||||||
// Output:
|
// Output:
|
||||||
// LANDMARKS: An std::vector<NormalizedLandmark> representing landmarks with
|
// LANDMARKS: An NormalizedLandmarkList proto representing landmarks with
|
||||||
// their locations adjusted to the letterbox-removed (non-padded) image.
|
// their locations adjusted to the letterbox-removed (non-padded) image.
|
||||||
//
|
//
|
||||||
// Usage example:
|
// Usage example:
|
||||||
|
@ -74,10 +74,10 @@ class LandmarkLetterboxRemovalCalculator : public CalculatorBase {
|
||||||
cc->Inputs().HasTag(kLetterboxPaddingTag))
|
cc->Inputs().HasTag(kLetterboxPaddingTag))
|
||||||
<< "Missing one or more input streams.";
|
<< "Missing one or more input streams.";
|
||||||
|
|
||||||
cc->Inputs().Tag(kLandmarksTag).Set<std::vector<NormalizedLandmark>>();
|
cc->Inputs().Tag(kLandmarksTag).Set<NormalizedLandmarkList>();
|
||||||
cc->Inputs().Tag(kLetterboxPaddingTag).Set<std::array<float, 4>>();
|
cc->Inputs().Tag(kLetterboxPaddingTag).Set<std::array<float, 4>>();
|
||||||
|
|
||||||
cc->Outputs().Tag(kLandmarksTag).Set<std::vector<NormalizedLandmark>>();
|
cc->Outputs().Tag(kLandmarksTag).Set<NormalizedLandmarkList>();
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
@ -94,8 +94,8 @@ class LandmarkLetterboxRemovalCalculator : public CalculatorBase {
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& input_landmarks =
|
const NormalizedLandmarkList& input_landmarks =
|
||||||
cc->Inputs().Tag(kLandmarksTag).Get<std::vector<NormalizedLandmark>>();
|
cc->Inputs().Tag(kLandmarksTag).Get<NormalizedLandmarkList>();
|
||||||
const auto& letterbox_padding =
|
const auto& letterbox_padding =
|
||||||
cc->Inputs().Tag(kLetterboxPaddingTag).Get<std::array<float, 4>>();
|
cc->Inputs().Tag(kLetterboxPaddingTag).Get<std::array<float, 4>>();
|
||||||
|
|
||||||
|
@ -104,24 +104,23 @@ class LandmarkLetterboxRemovalCalculator : public CalculatorBase {
|
||||||
const float left_and_right = letterbox_padding[0] + letterbox_padding[2];
|
const float left_and_right = letterbox_padding[0] + letterbox_padding[2];
|
||||||
const float top_and_bottom = letterbox_padding[1] + letterbox_padding[3];
|
const float top_and_bottom = letterbox_padding[1] + letterbox_padding[3];
|
||||||
|
|
||||||
auto output_landmarks =
|
NormalizedLandmarkList output_landmarks;
|
||||||
absl::make_unique<std::vector<NormalizedLandmark>>();
|
for (int i = 0; i < input_landmarks.landmark_size(); ++i) {
|
||||||
for (const auto& landmark : input_landmarks) {
|
const NormalizedLandmark& landmark = input_landmarks.landmark(i);
|
||||||
NormalizedLandmark new_landmark;
|
NormalizedLandmark* new_landmark = output_landmarks.add_landmark();
|
||||||
const float new_x = (landmark.x() - left) / (1.0f - left_and_right);
|
const float new_x = (landmark.x() - left) / (1.0f - left_and_right);
|
||||||
const float new_y = (landmark.y() - top) / (1.0f - top_and_bottom);
|
const float new_y = (landmark.y() - top) / (1.0f - top_and_bottom);
|
||||||
|
|
||||||
new_landmark.set_x(new_x);
|
new_landmark->set_x(new_x);
|
||||||
new_landmark.set_y(new_y);
|
new_landmark->set_y(new_y);
|
||||||
// Keep z-coord as is.
|
// Keep z-coord as is.
|
||||||
new_landmark.set_z(landmark.z());
|
new_landmark->set_z(landmark.z());
|
||||||
|
|
||||||
output_landmarks->emplace_back(new_landmark);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cc->Outputs()
|
cc->Outputs()
|
||||||
.Tag(kLandmarksTag)
|
.Tag(kLandmarksTag)
|
||||||
.Add(output_landmarks.release(), cc->InputTimestamp());
|
.AddPacket(MakePacket<NormalizedLandmarkList>(output_landmarks)
|
||||||
|
.At(cc->InputTimestamp()));
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,10 +43,10 @@ CalculatorGraphConfig::Node GetDefaultNode() {
|
||||||
TEST(LandmarkLetterboxRemovalCalculatorTest, PaddingLeftRight) {
|
TEST(LandmarkLetterboxRemovalCalculatorTest, PaddingLeftRight) {
|
||||||
CalculatorRunner runner(GetDefaultNode());
|
CalculatorRunner runner(GetDefaultNode());
|
||||||
|
|
||||||
auto landmarks = absl::make_unique<std::vector<NormalizedLandmark>>();
|
auto landmarks = absl::make_unique<NormalizedLandmarkList>();
|
||||||
landmarks->push_back(CreateLandmark(0.5f, 0.5f));
|
*landmarks->add_landmark() = CreateLandmark(0.5f, 0.5f);
|
||||||
landmarks->push_back(CreateLandmark(0.2f, 0.2f));
|
*landmarks->add_landmark() = CreateLandmark(0.2f, 0.2f);
|
||||||
landmarks->push_back(CreateLandmark(0.7f, 0.7f));
|
*landmarks->add_landmark() = CreateLandmark(0.7f, 0.7f);
|
||||||
runner.MutableInputs()
|
runner.MutableInputs()
|
||||||
->Tag("LANDMARKS")
|
->Tag("LANDMARKS")
|
||||||
.packets.push_back(
|
.packets.push_back(
|
||||||
|
@ -61,26 +61,28 @@ TEST(LandmarkLetterboxRemovalCalculatorTest, PaddingLeftRight) {
|
||||||
MP_ASSERT_OK(runner.Run()) << "Calculator execution failed.";
|
MP_ASSERT_OK(runner.Run()) << "Calculator execution failed.";
|
||||||
const std::vector<Packet>& output = runner.Outputs().Tag("LANDMARKS").packets;
|
const std::vector<Packet>& output = runner.Outputs().Tag("LANDMARKS").packets;
|
||||||
ASSERT_EQ(1, output.size());
|
ASSERT_EQ(1, output.size());
|
||||||
const auto& output_landmarks =
|
const auto& output_landmarks = output[0].Get<NormalizedLandmarkList>();
|
||||||
output[0].Get<std::vector<NormalizedLandmark>>();
|
|
||||||
|
|
||||||
EXPECT_EQ(output_landmarks.size(), 3);
|
EXPECT_EQ(output_landmarks.landmark_size(), 3);
|
||||||
|
|
||||||
EXPECT_THAT(output_landmarks[0].x(), testing::FloatNear(0.6f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(0).x(), testing::FloatNear(0.6f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[0].y(), testing::FloatNear(0.5f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(0).y(), testing::FloatNear(0.5f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[1].x(), testing::FloatNear(0.0f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(1).x(), testing::FloatNear(0.0f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[1].y(), testing::FloatNear(0.2f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(1).y(), testing::FloatNear(0.2f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[2].x(), testing::FloatNear(1.0f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(2).x(), testing::FloatNear(1.0f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[2].y(), testing::FloatNear(0.7f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(2).y(), testing::FloatNear(0.7f, 1e-5));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LandmarkLetterboxRemovalCalculatorTest, PaddingTopBottom) {
|
TEST(LandmarkLetterboxRemovalCalculatorTest, PaddingTopBottom) {
|
||||||
CalculatorRunner runner(GetDefaultNode());
|
CalculatorRunner runner(GetDefaultNode());
|
||||||
|
|
||||||
auto landmarks = absl::make_unique<std::vector<NormalizedLandmark>>();
|
auto landmarks = absl::make_unique<NormalizedLandmarkList>();
|
||||||
landmarks->push_back(CreateLandmark(0.5f, 0.5f));
|
NormalizedLandmark* landmark = landmarks->add_landmark();
|
||||||
landmarks->push_back(CreateLandmark(0.2f, 0.2f));
|
*landmark = CreateLandmark(0.5f, 0.5f);
|
||||||
landmarks->push_back(CreateLandmark(0.7f, 0.7f));
|
landmark = landmarks->add_landmark();
|
||||||
|
*landmark = CreateLandmark(0.2f, 0.2f);
|
||||||
|
landmark = landmarks->add_landmark();
|
||||||
|
*landmark = CreateLandmark(0.7f, 0.7f);
|
||||||
runner.MutableInputs()
|
runner.MutableInputs()
|
||||||
->Tag("LANDMARKS")
|
->Tag("LANDMARKS")
|
||||||
.packets.push_back(
|
.packets.push_back(
|
||||||
|
@ -95,17 +97,16 @@ TEST(LandmarkLetterboxRemovalCalculatorTest, PaddingTopBottom) {
|
||||||
MP_ASSERT_OK(runner.Run()) << "Calculator execution failed.";
|
MP_ASSERT_OK(runner.Run()) << "Calculator execution failed.";
|
||||||
const std::vector<Packet>& output = runner.Outputs().Tag("LANDMARKS").packets;
|
const std::vector<Packet>& output = runner.Outputs().Tag("LANDMARKS").packets;
|
||||||
ASSERT_EQ(1, output.size());
|
ASSERT_EQ(1, output.size());
|
||||||
const auto& output_landmarks =
|
const auto& output_landmarks = output[0].Get<NormalizedLandmarkList>();
|
||||||
output[0].Get<std::vector<NormalizedLandmark>>();
|
|
||||||
|
|
||||||
EXPECT_EQ(output_landmarks.size(), 3);
|
EXPECT_EQ(output_landmarks.landmark_size(), 3);
|
||||||
|
|
||||||
EXPECT_THAT(output_landmarks[0].x(), testing::FloatNear(0.5f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(0).x(), testing::FloatNear(0.5f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[0].y(), testing::FloatNear(0.6f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(0).y(), testing::FloatNear(0.6f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[1].x(), testing::FloatNear(0.2f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(1).x(), testing::FloatNear(0.2f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[1].y(), testing::FloatNear(0.0f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(1).y(), testing::FloatNear(0.0f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[2].x(), testing::FloatNear(0.7f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(2).x(), testing::FloatNear(0.7f, 1e-5));
|
||||||
EXPECT_THAT(output_landmarks[2].y(), testing::FloatNear(1.0f, 1e-5));
|
EXPECT_THAT(output_landmarks.landmark(2).y(), testing::FloatNear(1.0f, 1e-5));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mediapipe
|
} // namespace mediapipe
|
||||||
|
|
|
@ -47,13 +47,13 @@ constexpr char kRectTag[] = "NORM_RECT";
|
||||||
// Projects normalized landmarks in a rectangle to its original coordinates. The
|
// Projects normalized landmarks in a rectangle to its original coordinates. The
|
||||||
// rectangle must also be in normalized coordinates.
|
// rectangle must also be in normalized coordinates.
|
||||||
// Input:
|
// Input:
|
||||||
// NORM_LANDMARKS: An std::vector<NormalizedLandmark> representing landmarks
|
// NORM_LANDMARKS: A NormalizedLandmarkList representing landmarks
|
||||||
// in a normalized rectangle.
|
// in a normalized rectangle.
|
||||||
// NORM_RECT: An NormalizedRect representing a normalized rectangle in image
|
// NORM_RECT: An NormalizedRect representing a normalized rectangle in image
|
||||||
// coordinates.
|
// coordinates.
|
||||||
//
|
//
|
||||||
// Output:
|
// Output:
|
||||||
// NORM_LANDMARKS: An std::vector<NormalizedLandmark> representing landmarks
|
// NORM_LANDMARKS: A NormalizedLandmarkList representing landmarks
|
||||||
// with their locations adjusted to the image.
|
// with their locations adjusted to the image.
|
||||||
//
|
//
|
||||||
// Usage example:
|
// Usage example:
|
||||||
|
@ -70,10 +70,10 @@ class LandmarkProjectionCalculator : public CalculatorBase {
|
||||||
cc->Inputs().HasTag(kRectTag))
|
cc->Inputs().HasTag(kRectTag))
|
||||||
<< "Missing one or more input streams.";
|
<< "Missing one or more input streams.";
|
||||||
|
|
||||||
cc->Inputs().Tag(kLandmarksTag).Set<std::vector<NormalizedLandmark>>();
|
cc->Inputs().Tag(kLandmarksTag).Set<NormalizedLandmarkList>();
|
||||||
cc->Inputs().Tag(kRectTag).Set<NormalizedRect>();
|
cc->Inputs().Tag(kRectTag).Set<NormalizedRect>();
|
||||||
|
|
||||||
cc->Outputs().Tag(kLandmarksTag).Set<std::vector<NormalizedLandmark>>();
|
cc->Outputs().Tag(kLandmarksTag).Set<NormalizedLandmarkList>();
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
@ -92,14 +92,14 @@ class LandmarkProjectionCalculator : public CalculatorBase {
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& input_landmarks =
|
const NormalizedLandmarkList& input_landmarks =
|
||||||
cc->Inputs().Tag(kLandmarksTag).Get<std::vector<NormalizedLandmark>>();
|
cc->Inputs().Tag(kLandmarksTag).Get<NormalizedLandmarkList>();
|
||||||
const auto& input_rect = cc->Inputs().Tag(kRectTag).Get<NormalizedRect>();
|
const auto& input_rect = cc->Inputs().Tag(kRectTag).Get<NormalizedRect>();
|
||||||
|
|
||||||
auto output_landmarks =
|
NormalizedLandmarkList output_landmarks;
|
||||||
absl::make_unique<std::vector<NormalizedLandmark>>();
|
for (int i = 0; i < input_landmarks.landmark_size(); ++i) {
|
||||||
for (const auto& landmark : input_landmarks) {
|
const NormalizedLandmark& landmark = input_landmarks.landmark(i);
|
||||||
NormalizedLandmark new_landmark;
|
NormalizedLandmark* new_landmark = output_landmarks.add_landmark();
|
||||||
|
|
||||||
const float x = landmark.x() - 0.5f;
|
const float x = landmark.x() - 0.5f;
|
||||||
const float y = landmark.y() - 0.5f;
|
const float y = landmark.y() - 0.5f;
|
||||||
|
@ -110,17 +110,16 @@ class LandmarkProjectionCalculator : public CalculatorBase {
|
||||||
new_x = new_x * input_rect.width() + input_rect.x_center();
|
new_x = new_x * input_rect.width() + input_rect.x_center();
|
||||||
new_y = new_y * input_rect.height() + input_rect.y_center();
|
new_y = new_y * input_rect.height() + input_rect.y_center();
|
||||||
|
|
||||||
new_landmark.set_x(new_x);
|
new_landmark->set_x(new_x);
|
||||||
new_landmark.set_y(new_y);
|
new_landmark->set_y(new_y);
|
||||||
// Keep z-coord as is.
|
// Keep z-coord as is.
|
||||||
new_landmark.set_z(landmark.z());
|
new_landmark->set_z(landmark.z());
|
||||||
|
|
||||||
output_landmarks->emplace_back(new_landmark);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cc->Outputs()
|
cc->Outputs()
|
||||||
.Tag(kLandmarksTag)
|
.Tag(kLandmarksTag)
|
||||||
.Add(output_landmarks.release(), cc->InputTimestamp());
|
.AddPacket(MakePacket<NormalizedLandmarkList>(output_landmarks)
|
||||||
|
.At(cc->InputTimestamp()));
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,8 +28,7 @@ namespace {
|
||||||
constexpr char kDetectionTag[] = "DETECTION";
|
constexpr char kDetectionTag[] = "DETECTION";
|
||||||
constexpr char kNormalizedLandmarksTag[] = "NORM_LANDMARKS";
|
constexpr char kNormalizedLandmarksTag[] = "NORM_LANDMARKS";
|
||||||
|
|
||||||
Detection ConvertLandmarksToDetection(
|
Detection ConvertLandmarksToDetection(const NormalizedLandmarkList& landmarks) {
|
||||||
const std::vector<NormalizedLandmark>& landmarks) {
|
|
||||||
Detection detection;
|
Detection detection;
|
||||||
LocationData* location_data = detection.mutable_location_data();
|
LocationData* location_data = detection.mutable_location_data();
|
||||||
|
|
||||||
|
@ -37,7 +36,8 @@ Detection ConvertLandmarksToDetection(
|
||||||
float x_max = std::numeric_limits<float>::min();
|
float x_max = std::numeric_limits<float>::min();
|
||||||
float y_min = std::numeric_limits<float>::max();
|
float y_min = std::numeric_limits<float>::max();
|
||||||
float y_max = std::numeric_limits<float>::min();
|
float y_max = std::numeric_limits<float>::min();
|
||||||
for (const auto& landmark : landmarks) {
|
for (int i = 0; i < landmarks.landmark_size(); ++i) {
|
||||||
|
const NormalizedLandmark& landmark = landmarks.landmark(i);
|
||||||
x_min = std::min(x_min, landmark.x());
|
x_min = std::min(x_min, landmark.x());
|
||||||
x_max = std::max(x_max, landmark.x());
|
x_max = std::max(x_max, landmark.x());
|
||||||
y_min = std::min(y_min, landmark.y());
|
y_min = std::min(y_min, landmark.y());
|
||||||
|
@ -67,7 +67,7 @@ Detection ConvertLandmarksToDetection(
|
||||||
// to specify a subset of landmarks for creating the detection.
|
// to specify a subset of landmarks for creating the detection.
|
||||||
//
|
//
|
||||||
// Input:
|
// Input:
|
||||||
// NOMR_LANDMARKS: A vector of NormalizedLandmark.
|
// NOMR_LANDMARKS: A NormalizedLandmarkList proto.
|
||||||
//
|
//
|
||||||
// Output:
|
// Output:
|
||||||
// DETECTION: A Detection proto.
|
// DETECTION: A Detection proto.
|
||||||
|
@ -95,9 +95,7 @@ REGISTER_CALCULATOR(LandmarksToDetectionCalculator);
|
||||||
RET_CHECK(cc->Inputs().HasTag(kNormalizedLandmarksTag));
|
RET_CHECK(cc->Inputs().HasTag(kNormalizedLandmarksTag));
|
||||||
RET_CHECK(cc->Outputs().HasTag(kDetectionTag));
|
RET_CHECK(cc->Outputs().HasTag(kDetectionTag));
|
||||||
// TODO: Also support converting Landmark to Detection.
|
// TODO: Also support converting Landmark to Detection.
|
||||||
cc->Inputs()
|
cc->Inputs().Tag(kNormalizedLandmarksTag).Set<NormalizedLandmarkList>();
|
||||||
.Tag(kNormalizedLandmarksTag)
|
|
||||||
.Set<std::vector<NormalizedLandmark>>();
|
|
||||||
cc->Outputs().Tag(kDetectionTag).Set<Detection>();
|
cc->Outputs().Tag(kDetectionTag).Set<Detection>();
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
|
@ -113,19 +111,20 @@ REGISTER_CALCULATOR(LandmarksToDetectionCalculator);
|
||||||
|
|
||||||
::mediapipe::Status LandmarksToDetectionCalculator::Process(
|
::mediapipe::Status LandmarksToDetectionCalculator::Process(
|
||||||
CalculatorContext* cc) {
|
CalculatorContext* cc) {
|
||||||
const auto& landmarks = cc->Inputs()
|
const auto& landmarks =
|
||||||
.Tag(kNormalizedLandmarksTag)
|
cc->Inputs().Tag(kNormalizedLandmarksTag).Get<NormalizedLandmarkList>();
|
||||||
.Get<std::vector<NormalizedLandmark>>();
|
RET_CHECK_GT(landmarks.landmark_size(), 0)
|
||||||
RET_CHECK_GT(landmarks.size(), 0) << "Input landmark vector is empty.";
|
<< "Input landmark vector is empty.";
|
||||||
|
|
||||||
auto detection = absl::make_unique<Detection>();
|
auto detection = absl::make_unique<Detection>();
|
||||||
if (options_.selected_landmark_indices_size()) {
|
if (options_.selected_landmark_indices_size()) {
|
||||||
std::vector<NormalizedLandmark> subset_landmarks(
|
NormalizedLandmarkList subset_landmarks;
|
||||||
options_.selected_landmark_indices_size());
|
for (int i = 0; i < options_.selected_landmark_indices_size(); ++i) {
|
||||||
for (int i = 0; i < subset_landmarks.size(); ++i) {
|
RET_CHECK_LT(options_.selected_landmark_indices(i),
|
||||||
RET_CHECK_LT(options_.selected_landmark_indices(i), landmarks.size())
|
landmarks.landmark_size())
|
||||||
<< "Index of landmark subset is out of range.";
|
<< "Index of landmark subset is out of range.";
|
||||||
subset_landmarks[i] = landmarks[options_.selected_landmark_indices(i)];
|
*subset_landmarks.add_landmark() =
|
||||||
|
landmarks.landmark(options_.selected_landmark_indices(i));
|
||||||
}
|
}
|
||||||
*detection = ConvertLandmarksToDetection(subset_landmarks);
|
*detection = ConvertLandmarksToDetection(subset_landmarks);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -48,7 +48,7 @@ constexpr char kMatrixTag[] = "MATRIX";
|
||||||
|
|
||||||
// Converts a vector of landmarks to a vector of floats or a matrix.
|
// Converts a vector of landmarks to a vector of floats or a matrix.
|
||||||
// Input:
|
// Input:
|
||||||
// NORM_LANDMARKS: An std::vector<NormalizedLandmark>.
|
// NORM_LANDMARKS: A NormalizedLandmarkList proto.
|
||||||
//
|
//
|
||||||
// Output:
|
// Output:
|
||||||
// FLOATS(optional): A vector of floats from flattened landmarks.
|
// FLOATS(optional): A vector of floats from flattened landmarks.
|
||||||
|
@ -63,7 +63,7 @@ constexpr char kMatrixTag[] = "MATRIX";
|
||||||
class LandmarksToFloatsCalculator : public CalculatorBase {
|
class LandmarksToFloatsCalculator : public CalculatorBase {
|
||||||
public:
|
public:
|
||||||
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
||||||
cc->Inputs().Tag(kLandmarksTag).Set<std::vector<NormalizedLandmark>>();
|
cc->Inputs().Tag(kLandmarksTag).Set<NormalizedLandmarkList>();
|
||||||
RET_CHECK(cc->Outputs().HasTag(kFloatsTag) ||
|
RET_CHECK(cc->Outputs().HasTag(kFloatsTag) ||
|
||||||
cc->Outputs().HasTag(kMatrixTag));
|
cc->Outputs().HasTag(kMatrixTag));
|
||||||
if (cc->Outputs().HasTag(kFloatsTag)) {
|
if (cc->Outputs().HasTag(kFloatsTag)) {
|
||||||
|
@ -94,11 +94,12 @@ class LandmarksToFloatsCalculator : public CalculatorBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& input_landmarks =
|
const auto& input_landmarks =
|
||||||
cc->Inputs().Tag(kLandmarksTag).Get<std::vector<NormalizedLandmark>>();
|
cc->Inputs().Tag(kLandmarksTag).Get<NormalizedLandmarkList>();
|
||||||
|
|
||||||
if (cc->Outputs().HasTag(kFloatsTag)) {
|
if (cc->Outputs().HasTag(kFloatsTag)) {
|
||||||
auto output_floats = absl::make_unique<std::vector<float>>();
|
auto output_floats = absl::make_unique<std::vector<float>>();
|
||||||
for (const auto& landmark : input_landmarks) {
|
for (int i = 0; i < input_landmarks.landmark_size(); ++i) {
|
||||||
|
const NormalizedLandmark& landmark = input_landmarks.landmark(i);
|
||||||
output_floats->emplace_back(landmark.x());
|
output_floats->emplace_back(landmark.x());
|
||||||
if (num_dimensions_ > 1) {
|
if (num_dimensions_ > 1) {
|
||||||
output_floats->emplace_back(landmark.y());
|
output_floats->emplace_back(landmark.y());
|
||||||
|
@ -113,14 +114,14 @@ class LandmarksToFloatsCalculator : public CalculatorBase {
|
||||||
.Add(output_floats.release(), cc->InputTimestamp());
|
.Add(output_floats.release(), cc->InputTimestamp());
|
||||||
} else {
|
} else {
|
||||||
auto output_matrix = absl::make_unique<Matrix>();
|
auto output_matrix = absl::make_unique<Matrix>();
|
||||||
output_matrix->setZero(num_dimensions_, input_landmarks.size());
|
output_matrix->setZero(num_dimensions_, input_landmarks.landmark_size());
|
||||||
for (int i = 0; i < input_landmarks.size(); ++i) {
|
for (int i = 0; i < input_landmarks.landmark_size(); ++i) {
|
||||||
(*output_matrix)(0, i) = input_landmarks[i].x();
|
(*output_matrix)(0, i) = input_landmarks.landmark(i).x();
|
||||||
if (num_dimensions_ > 1) {
|
if (num_dimensions_ > 1) {
|
||||||
(*output_matrix)(1, i) = input_landmarks[i].y();
|
(*output_matrix)(1, i) = input_landmarks.landmark(i).y();
|
||||||
}
|
}
|
||||||
if (num_dimensions_ > 2) {
|
if (num_dimensions_ > 2) {
|
||||||
(*output_matrix)(2, i) = input_landmarks[i].z();
|
(*output_matrix)(2, i) = input_landmarks.landmark(i).z();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cc->Outputs()
|
cc->Outputs()
|
||||||
|
|
|
@ -46,12 +46,13 @@ inline float Remap(float x, float lo, float hi, float scale) {
|
||||||
return (x - lo) / (hi - lo + 1e-6) * scale;
|
return (x - lo) / (hi - lo + 1e-6) * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class LandmarkType>
|
template <class LandmarkListType, class LandmarkType>
|
||||||
inline void GetMinMaxZ(const std::vector<LandmarkType>& landmarks, float* z_min,
|
inline void GetMinMaxZ(const LandmarkListType& landmarks, float* z_min,
|
||||||
float* z_max) {
|
float* z_max) {
|
||||||
*z_min = std::numeric_limits<float>::max();
|
*z_min = std::numeric_limits<float>::max();
|
||||||
*z_max = std::numeric_limits<float>::min();
|
*z_max = std::numeric_limits<float>::min();
|
||||||
for (const auto& landmark : landmarks) {
|
for (int i = 0; i < landmarks.landmark_size(); ++i) {
|
||||||
|
const LandmarkType& landmark = landmarks.landmark(i);
|
||||||
*z_min = std::min(landmark.z(), *z_min);
|
*z_min = std::min(landmark.z(), *z_min);
|
||||||
*z_max = std::max(landmark.z(), *z_max);
|
*z_max = std::max(landmark.z(), *z_max);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +74,7 @@ void SetColorSizeValueFromZ(float z, float z_min, float z_max,
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// A calculator that converts Landmark proto to RenderData proto for
|
// A calculator that converts Landmark proto to RenderData proto for
|
||||||
// visualization. The input should be std::vector<Landmark>. It is also possible
|
// visualization. The input should be LandmarkList proto. It is also possible
|
||||||
// to specify the connections between landmarks.
|
// to specify the connections between landmarks.
|
||||||
//
|
//
|
||||||
// Example config:
|
// Example config:
|
||||||
|
@ -121,11 +122,11 @@ class LandmarksToRenderDataCalculator : public CalculatorBase {
|
||||||
const LandmarksToRenderDataCalculatorOptions& options, bool normalized,
|
const LandmarksToRenderDataCalculatorOptions& options, bool normalized,
|
||||||
int gray_val1, int gray_val2, RenderData* render_data);
|
int gray_val1, int gray_val2, RenderData* render_data);
|
||||||
|
|
||||||
template <class LandmarkType>
|
template <class LandmarkListType>
|
||||||
void AddConnections(const std::vector<LandmarkType>& landmarks,
|
void AddConnections(const LandmarkListType& landmarks, bool normalized,
|
||||||
bool normalized, RenderData* render_data);
|
RenderData* render_data);
|
||||||
template <class LandmarkType>
|
template <class LandmarkListType>
|
||||||
void AddConnectionsWithDepth(const std::vector<LandmarkType>& landmarks,
|
void AddConnectionsWithDepth(const LandmarkListType& landmarks,
|
||||||
bool normalized, float min_z, float max_z,
|
bool normalized, float min_z, float max_z,
|
||||||
RenderData* render_data);
|
RenderData* render_data);
|
||||||
|
|
||||||
|
@ -144,10 +145,10 @@ REGISTER_CALCULATOR(LandmarksToRenderDataCalculator);
|
||||||
"normalized landmarks.";
|
"normalized landmarks.";
|
||||||
|
|
||||||
if (cc->Inputs().HasTag(kLandmarksTag)) {
|
if (cc->Inputs().HasTag(kLandmarksTag)) {
|
||||||
cc->Inputs().Tag(kLandmarksTag).Set<std::vector<Landmark>>();
|
cc->Inputs().Tag(kLandmarksTag).Set<LandmarkList>();
|
||||||
}
|
}
|
||||||
if (cc->Inputs().HasTag(kNormLandmarksTag)) {
|
if (cc->Inputs().HasTag(kNormLandmarksTag)) {
|
||||||
cc->Inputs().Tag(kNormLandmarksTag).Set<std::vector<NormalizedLandmark>>();
|
cc->Inputs().Tag(kNormLandmarksTag).Set<NormalizedLandmarkList>();
|
||||||
}
|
}
|
||||||
cc->Outputs().Tag(kRenderDataTag).Set<RenderData>();
|
cc->Outputs().Tag(kRenderDataTag).Set<RenderData>();
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
|
@ -169,16 +170,17 @@ REGISTER_CALCULATOR(LandmarksToRenderDataCalculator);
|
||||||
float z_max = 0.f;
|
float z_max = 0.f;
|
||||||
|
|
||||||
if (cc->Inputs().HasTag(kLandmarksTag)) {
|
if (cc->Inputs().HasTag(kLandmarksTag)) {
|
||||||
const auto& landmarks =
|
const LandmarkList& landmarks =
|
||||||
cc->Inputs().Tag(kLandmarksTag).Get<std::vector<Landmark>>();
|
cc->Inputs().Tag(kLandmarksTag).Get<LandmarkList>();
|
||||||
RET_CHECK_EQ(options_.landmark_connections_size() % 2, 0)
|
RET_CHECK_EQ(options_.landmark_connections_size() % 2, 0)
|
||||||
<< "Number of entries in landmark connections must be a multiple of 2";
|
<< "Number of entries in landmark connections must be a multiple of 2";
|
||||||
if (visualize_depth) {
|
if (visualize_depth) {
|
||||||
GetMinMaxZ(landmarks, &z_min, &z_max);
|
GetMinMaxZ<LandmarkList, Landmark>(landmarks, &z_min, &z_max);
|
||||||
}
|
}
|
||||||
// Only change rendering if there are actually z values other than 0.
|
// Only change rendering if there are actually z values other than 0.
|
||||||
visualize_depth &= ((z_max - z_min) > 1e-3);
|
visualize_depth &= ((z_max - z_min) > 1e-3);
|
||||||
for (const auto& landmark : landmarks) {
|
for (int i = 0; i < landmarks.landmark_size(); ++i) {
|
||||||
|
const Landmark& landmark = landmarks.landmark(i);
|
||||||
auto* landmark_data_render =
|
auto* landmark_data_render =
|
||||||
AddPointRenderData(options_, render_data.get());
|
AddPointRenderData(options_, render_data.get());
|
||||||
if (visualize_depth) {
|
if (visualize_depth) {
|
||||||
|
@ -191,25 +193,27 @@ REGISTER_CALCULATOR(LandmarksToRenderDataCalculator);
|
||||||
landmark_data->set_y(landmark.y());
|
landmark_data->set_y(landmark.y());
|
||||||
}
|
}
|
||||||
if (visualize_depth) {
|
if (visualize_depth) {
|
||||||
AddConnectionsWithDepth(landmarks, /*normalized=*/false, z_min, z_max,
|
AddConnectionsWithDepth<LandmarkList>(landmarks, /*normalized=*/false,
|
||||||
render_data.get());
|
z_min, z_max, render_data.get());
|
||||||
} else {
|
} else {
|
||||||
AddConnections(landmarks, /*normalized=*/false, render_data.get());
|
AddConnections<LandmarkList>(landmarks, /*normalized=*/false,
|
||||||
|
render_data.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cc->Inputs().HasTag(kNormLandmarksTag)) {
|
if (cc->Inputs().HasTag(kNormLandmarksTag)) {
|
||||||
const auto& landmarks = cc->Inputs()
|
const NormalizedLandmarkList& landmarks =
|
||||||
.Tag(kNormLandmarksTag)
|
cc->Inputs().Tag(kNormLandmarksTag).Get<NormalizedLandmarkList>();
|
||||||
.Get<std::vector<NormalizedLandmark>>();
|
|
||||||
RET_CHECK_EQ(options_.landmark_connections_size() % 2, 0)
|
RET_CHECK_EQ(options_.landmark_connections_size() % 2, 0)
|
||||||
<< "Number of entries in landmark connections must be a multiple of 2";
|
<< "Number of entries in landmark connections must be a multiple of 2";
|
||||||
if (visualize_depth) {
|
if (visualize_depth) {
|
||||||
GetMinMaxZ(landmarks, &z_min, &z_max);
|
GetMinMaxZ<NormalizedLandmarkList, NormalizedLandmark>(landmarks, &z_min,
|
||||||
|
&z_max);
|
||||||
}
|
}
|
||||||
// Only change rendering if there are actually z values other than 0.
|
// Only change rendering if there are actually z values other than 0.
|
||||||
visualize_depth &= ((z_max - z_min) > 1e-3);
|
visualize_depth &= ((z_max - z_min) > 1e-3);
|
||||||
for (const auto& landmark : landmarks) {
|
for (int i = 0; i < landmarks.landmark_size(); ++i) {
|
||||||
|
const NormalizedLandmark& landmark = landmarks.landmark(i);
|
||||||
auto* landmark_data_render =
|
auto* landmark_data_render =
|
||||||
AddPointRenderData(options_, render_data.get());
|
AddPointRenderData(options_, render_data.get());
|
||||||
if (visualize_depth) {
|
if (visualize_depth) {
|
||||||
|
@ -222,10 +226,11 @@ REGISTER_CALCULATOR(LandmarksToRenderDataCalculator);
|
||||||
landmark_data->set_y(landmark.y());
|
landmark_data->set_y(landmark.y());
|
||||||
}
|
}
|
||||||
if (visualize_depth) {
|
if (visualize_depth) {
|
||||||
AddConnectionsWithDepth(landmarks, /*normalized=*/true, z_min, z_max,
|
AddConnectionsWithDepth<NormalizedLandmarkList>(
|
||||||
render_data.get());
|
landmarks, /*normalized=*/true, z_min, z_max, render_data.get());
|
||||||
} else {
|
} else {
|
||||||
AddConnections(landmarks, /*normalized=*/true, render_data.get());
|
AddConnections<NormalizedLandmarkList>(landmarks, /*normalized=*/true,
|
||||||
|
render_data.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,13 +240,13 @@ REGISTER_CALCULATOR(LandmarksToRenderDataCalculator);
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class LandmarkType>
|
template <class LandmarkListType>
|
||||||
void LandmarksToRenderDataCalculator::AddConnectionsWithDepth(
|
void LandmarksToRenderDataCalculator::AddConnectionsWithDepth(
|
||||||
const std::vector<LandmarkType>& landmarks, bool normalized, float min_z,
|
const LandmarkListType& landmarks, bool normalized, float min_z,
|
||||||
float max_z, RenderData* render_data) {
|
float max_z, RenderData* render_data) {
|
||||||
for (int i = 0; i < options_.landmark_connections_size(); i += 2) {
|
for (int i = 0; i < options_.landmark_connections_size(); i += 2) {
|
||||||
const auto& ld0 = landmarks[options_.landmark_connections(i)];
|
const auto& ld0 = landmarks.landmark(options_.landmark_connections(i));
|
||||||
const auto& ld1 = landmarks[options_.landmark_connections(i + 1)];
|
const auto& ld1 = landmarks.landmark(options_.landmark_connections(i + 1));
|
||||||
const int gray_val1 =
|
const int gray_val1 =
|
||||||
255 - static_cast<int>(Remap(ld0.z(), min_z, max_z, 255));
|
255 - static_cast<int>(Remap(ld0.z(), min_z, max_z, 255));
|
||||||
const int gray_val2 =
|
const int gray_val2 =
|
||||||
|
@ -272,13 +277,13 @@ void LandmarksToRenderDataCalculator::AddConnectionToRenderData(
|
||||||
connection_annotation->set_thickness(options.thickness());
|
connection_annotation->set_thickness(options.thickness());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class LandmarkType>
|
template <class LandmarkListType>
|
||||||
void LandmarksToRenderDataCalculator::AddConnections(
|
void LandmarksToRenderDataCalculator::AddConnections(
|
||||||
const std::vector<LandmarkType>& landmarks, bool normalized,
|
const LandmarkListType& landmarks, bool normalized,
|
||||||
RenderData* render_data) {
|
RenderData* render_data) {
|
||||||
for (int i = 0; i < options_.landmark_connections_size(); i += 2) {
|
for (int i = 0; i < options_.landmark_connections_size(); i += 2) {
|
||||||
const auto& ld0 = landmarks[options_.landmark_connections(i)];
|
const auto& ld0 = landmarks.landmark(options_.landmark_connections(i));
|
||||||
const auto& ld1 = landmarks[options_.landmark_connections(i + 1)];
|
const auto& ld1 = landmarks.landmark(options_.landmark_connections(i + 1));
|
||||||
AddConnectionToRenderData(ld0.x(), ld0.y(), ld1.x(), ld1.y(), options_,
|
AddConnectionToRenderData(ld0.x(), ld0.y(), ld1.x(), ld1.y(), options_,
|
||||||
normalized, render_data);
|
normalized, render_data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,7 @@
|
||||||
#include "mediapipe/framework/port/statusor.h"
|
#include "mediapipe/framework/port/statusor.h"
|
||||||
#include "mediapipe/util/resource_util.h"
|
#include "mediapipe/util/resource_util.h"
|
||||||
|
|
||||||
#if defined(MEDIAPIPE_LITE) || defined(__EMSCRIPTEN__) || \
|
#if defined(MEDIAPIPE_MOBILE)
|
||||||
defined(__ANDROID__) || (defined(__APPLE__) && !TARGET_OS_OSX)
|
|
||||||
#include "mediapipe/util/android/file/base/file.h"
|
#include "mediapipe/util/android/file/base/file.h"
|
||||||
#include "mediapipe/util/android/file/base/helpers.h"
|
#include "mediapipe/util/android/file/base/helpers.h"
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "mediapipe/framework/calculator_framework.h"
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
#include "mediapipe/framework/formats/image_format.pb.h"
|
#include "mediapipe/framework/formats/image_format.pb.h"
|
||||||
#include "mediapipe/framework/formats/image_frame.h"
|
#include "mediapipe/framework/formats/image_frame.h"
|
||||||
|
@ -66,6 +68,20 @@ ImageFormat::Format GetImageFormat(int num_channels) {
|
||||||
// output_stream: "VIDEO:video_frames"
|
// output_stream: "VIDEO:video_frames"
|
||||||
// output_stream: "VIDEO_PRESTREAM:video_header"
|
// output_stream: "VIDEO_PRESTREAM:video_header"
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
|
// OpenCV's VideoCapture doesn't decode audio tracks. If the audio tracks need
|
||||||
|
// to be saved, specify an output side packet with tag "SAVED_AUDIO_PATH".
|
||||||
|
// The calculator will call FFmpeg binary to save audio tracks as an aac file.
|
||||||
|
//
|
||||||
|
// Example config:
|
||||||
|
// node {
|
||||||
|
// calculator: "OpenCvVideoDecoderCalculator"
|
||||||
|
// input_side_packet: "INPUT_FILE_PATH:input_file_path"
|
||||||
|
// output_side_packet: "SAVED_AUDIO_PATH:audio_path"
|
||||||
|
// output_stream: "VIDEO:video_frames"
|
||||||
|
// output_stream: "VIDEO_PRESTREAM:video_header"
|
||||||
|
// }
|
||||||
|
//
|
||||||
class OpenCvVideoDecoderCalculator : public CalculatorBase {
|
class OpenCvVideoDecoderCalculator : public CalculatorBase {
|
||||||
public:
|
public:
|
||||||
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
||||||
|
@ -74,6 +90,9 @@ class OpenCvVideoDecoderCalculator : public CalculatorBase {
|
||||||
if (cc->Outputs().HasTag("VIDEO_PRESTREAM")) {
|
if (cc->Outputs().HasTag("VIDEO_PRESTREAM")) {
|
||||||
cc->Outputs().Tag("VIDEO_PRESTREAM").Set<VideoHeader>();
|
cc->Outputs().Tag("VIDEO_PRESTREAM").Set<VideoHeader>();
|
||||||
}
|
}
|
||||||
|
if (cc->OutputSidePackets().HasTag("SAVED_AUDIO_PATH")) {
|
||||||
|
cc->OutputSidePackets().Tag("SAVED_AUDIO_PATH").Set<std::string>();
|
||||||
|
}
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +146,25 @@ class OpenCvVideoDecoderCalculator : public CalculatorBase {
|
||||||
}
|
}
|
||||||
// Rewind to the very first frame.
|
// Rewind to the very first frame.
|
||||||
cap_->set(cv::CAP_PROP_POS_AVI_RATIO, 0);
|
cap_->set(cv::CAP_PROP_POS_AVI_RATIO, 0);
|
||||||
|
|
||||||
|
if (cc->OutputSidePackets().HasTag("SAVED_AUDIO_PATH")) {
|
||||||
|
#ifdef HAVE_FFMPEG
|
||||||
|
std::string saved_audio_path = std::tmpnam(nullptr);
|
||||||
|
system(absl::StrCat("ffmpeg -nostats -loglevel 0 -i ", input_file_path,
|
||||||
|
" -vn -f adts ", saved_audio_path)
|
||||||
|
.c_str());
|
||||||
|
cc->OutputSidePackets()
|
||||||
|
.Tag("SAVED_AUDIO_PATH")
|
||||||
|
.Set(MakePacket<std::string>(saved_audio_path));
|
||||||
|
|
||||||
|
#else
|
||||||
|
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
|
||||||
|
<< "OpenCVVideoDecoderCalculator can't save the audio file "
|
||||||
|
"because FFmpeg is not installed. Please remove "
|
||||||
|
"output_side_packet: \"SAVED_AUDIO_PATH\" from the node "
|
||||||
|
"config.";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -39,8 +41,7 @@ namespace mediapipe {
|
||||||
// packet. Currently, the calculator only supports one video stream (in
|
// packet. Currently, the calculator only supports one video stream (in
|
||||||
// mediapipe::ImageFrame).
|
// mediapipe::ImageFrame).
|
||||||
//
|
//
|
||||||
// Example config to generate the output video file:
|
// Example config:
|
||||||
//
|
|
||||||
// node {
|
// node {
|
||||||
// calculator: "OpenCvVideoEncoderCalculator"
|
// calculator: "OpenCvVideoEncoderCalculator"
|
||||||
// input_stream: "VIDEO:video"
|
// input_stream: "VIDEO:video"
|
||||||
|
@ -53,6 +54,26 @@ namespace mediapipe {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
|
// OpenCV's VideoWriter doesn't encode audio. If an input side packet with tag
|
||||||
|
// "AUDIO_FILE_PATH" is specified, the calculator will call FFmpeg binary to
|
||||||
|
// attach the audio file to the video as the last step in Close().
|
||||||
|
//
|
||||||
|
// Example config:
|
||||||
|
// node {
|
||||||
|
// calculator: "OpenCvVideoEncoderCalculator"
|
||||||
|
// input_stream: "VIDEO:video"
|
||||||
|
// input_stream: "VIDEO_PRESTREAM:video_header"
|
||||||
|
// input_side_packet: "OUTPUT_FILE_PATH:output_file_path"
|
||||||
|
// input_side_packet: "AUDIO_FILE_PATH:audio_path"
|
||||||
|
// node_options {
|
||||||
|
// [type.googleapis.com/mediapipe.OpenCvVideoEncoderCalculatorOptions]: {
|
||||||
|
// codec: "avc1"
|
||||||
|
// video_format: "mp4"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
class OpenCvVideoEncoderCalculator : public CalculatorBase {
|
class OpenCvVideoEncoderCalculator : public CalculatorBase {
|
||||||
public:
|
public:
|
||||||
static ::mediapipe::Status GetContract(CalculatorContract* cc);
|
static ::mediapipe::Status GetContract(CalculatorContract* cc);
|
||||||
|
@ -77,6 +98,9 @@ class OpenCvVideoEncoderCalculator : public CalculatorBase {
|
||||||
}
|
}
|
||||||
RET_CHECK(cc->InputSidePackets().HasTag("OUTPUT_FILE_PATH"));
|
RET_CHECK(cc->InputSidePackets().HasTag("OUTPUT_FILE_PATH"));
|
||||||
cc->InputSidePackets().Tag("OUTPUT_FILE_PATH").Set<std::string>();
|
cc->InputSidePackets().Tag("OUTPUT_FILE_PATH").Set<std::string>();
|
||||||
|
if (cc->InputSidePackets().HasTag("AUDIO_FILE_PATH")) {
|
||||||
|
cc->InputSidePackets().Tag("AUDIO_FILE_PATH").Set<std::string>();
|
||||||
|
}
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,6 +179,27 @@ class OpenCvVideoEncoderCalculator : public CalculatorBase {
|
||||||
if (writer_ && writer_->isOpened()) {
|
if (writer_ && writer_->isOpened()) {
|
||||||
writer_->release();
|
writer_->release();
|
||||||
}
|
}
|
||||||
|
if (cc->InputSidePackets().HasTag("AUDIO_FILE_PATH")) {
|
||||||
|
#ifdef HAVE_FFMPEG
|
||||||
|
const std::string& audio_file_path =
|
||||||
|
cc->InputSidePackets().Tag("AUDIO_FILE_PATH").Get<std::string>();
|
||||||
|
// A temp output file is needed because FFmpeg can't do in-place editing.
|
||||||
|
const std::string temp_file_path = std::tmpnam(nullptr);
|
||||||
|
system(absl::StrCat("mv ", output_file_path_, " ", temp_file_path,
|
||||||
|
"&& ffmpeg -nostats -loglevel 0 -i ", temp_file_path,
|
||||||
|
" -i ", audio_file_path,
|
||||||
|
" -c copy -map 0:v:0 -map 1:a:0 ", output_file_path_,
|
||||||
|
"&& rm ", temp_file_path)
|
||||||
|
.c_str());
|
||||||
|
|
||||||
|
#else
|
||||||
|
return ::mediapipe::InvalidArgumentErrorBuilder(MEDIAPIPE_LOC)
|
||||||
|
<< "OpenCVVideoEncoderCalculator can't attach the audio tracks to "
|
||||||
|
"the video because FFmpeg is not installed. Please remove "
|
||||||
|
"input_side_packet: \"AUDIO_FILE_PATH\" from the node "
|
||||||
|
"config.";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ project.
|
||||||
MediaPipe depends on OpenCV, you will need to copy the precompiled OpenCV so
|
MediaPipe depends on OpenCV, you will need to copy the precompiled OpenCV so
|
||||||
files into app/src/main/jniLibs. You can download the official OpenCV
|
files into app/src/main/jniLibs. You can download the official OpenCV
|
||||||
Android SDK from
|
Android SDK from
|
||||||
[here](https://github.com/opencv/opencv/releases/download/4.1.0/opencv-4.1.0-android-sdk.zip)
|
[here](https://github.com/opencv/opencv/releases/download/3.4.3/opencv-3.4.3-android-sdk.zip)
|
||||||
and run:
|
and run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -126,6 +126,6 @@ project.
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Follow our Android app examples to use MediaPipe in Android Studio for your
|
6. Follow our Android app examples to use MediaPipe in Android Studio for your
|
||||||
use case. If you are looking for an example, a working face detection
|
use case. If you are looking for an example, a face detection
|
||||||
example can be found
|
example can be found
|
||||||
[here](https://github.com/jiuqiant/mediapipe_aar_example).
|
[here](https://github.com/jiuqiant/mediapipe_face_detection_aar_example) and a multi-hand tracking example can be found [here](https://github.com/jiuqiant/mediapipe_multi_hands_tracking_aar_example).
|
||||||
|
|
|
@ -157,3 +157,20 @@ how to use MediaPipe with a TFLite model for hair segmentation on desktop using
|
||||||
GPU with live video from a webcam.
|
GPU with live video from a webcam.
|
||||||
|
|
||||||
* [Desktop GPU](./hair_segmentation_desktop.md)
|
* [Desktop GPU](./hair_segmentation_desktop.md)
|
||||||
|
|
||||||
|
## Google Coral (machine learning acceleration with Google EdgeTPU)
|
||||||
|
|
||||||
|
Below are code samples on how to run MediaPipe on Google Coral Dev Board.
|
||||||
|
|
||||||
|
### Object Detection on Coral
|
||||||
|
|
||||||
|
[Object Detection on Coral with Webcam](./object_detection_coral_devboard.md)
|
||||||
|
shows how to run quantized object detection TFlite model accelerated with
|
||||||
|
EdgeTPU on
|
||||||
|
[Google Coral Dev Board](https://coral.withgoogle.com/products/dev-board).
|
||||||
|
|
||||||
|
### Face Detection on Coral
|
||||||
|
|
||||||
|
[Face Detection on Coral with Webcam](./face_detection_coral_devboard.md) shows
|
||||||
|
how to use quantized face detection TFlite model accelerated with EdgeTPU on
|
||||||
|
[Google Coral Dev Board](https://coral.withgoogle.com/products/dev-board).
|
||||||
|
|
20
mediapipe/docs/face_detection_coral_devboard.md
Normal file
20
mediapipe/docs/face_detection_coral_devboard.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
## Face Detection on Coral with Webcam
|
||||||
|
|
||||||
|
MediaPipe is able to run cross platform across device types like desktop, mobile
|
||||||
|
and edge devices. Here is an example of running MediaPipe
|
||||||
|
[face detection pipeline](./face_detection_desktop.md) on edge device like
|
||||||
|
[Google Coral dev board](https://coral.withgoogle.com/products/dev-board) with
|
||||||
|
[Edge TPU](https://cloud.google.com/edge-tpu/). This MediaPipe Coral face
|
||||||
|
detection pipeline is running [coral specific quantized version](https://github.com/google/mediapipe/blob/master/mediapipe/examples/coral/models/face-detector-quantized_edgetpu.tflite)
|
||||||
|
of the [MediaPipe face detection TFLite model](https://github.com/google/mediapipe/blob/master/mediapipe/models/face_detection_front.tflite)
|
||||||
|
accelerated on Edge TPU.
|
||||||
|
|
||||||
|
### Cross compilation of MediaPipe Coral binaries in Docker
|
||||||
|
|
||||||
|
We recommend building the MediaPipe binaries not on the edge device due to
|
||||||
|
limited compute resulting in long build times. Instead, we will build MediaPipe
|
||||||
|
binaries using Docker containers on a more powerful host machine. For step by
|
||||||
|
step details of cross compiling and running MediaPipe binaries on Coral dev
|
||||||
|
board, please refer to [README.md in MediaPipe Coral example folder](https://github.com/google/mediapipe/blob/master/mediapipe/examples/coral/README.md).
|
||||||
|
|
||||||
|

|
|
@ -1,10 +1,10 @@
|
||||||
## Running on GPUs
|
## Running on GPUs
|
||||||
|
|
||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
- [OpenGL Support](#graphconfig)
|
- [OpenGL Support](#opengl-support)
|
||||||
- [Life of a GPU calculator](#life-of-a-gpu-calculator)
|
- [Life of a GPU calculator](#life-of-a-gpu-calculator)
|
||||||
- [GpuBuffer to ImageFrame converters](#gpubuffer-to-imageframe-converters)
|
- [GpuBuffer to ImageFrame converters](#gpubuffer-to-imageframe-converters)
|
||||||
|
- [Disable GPU support](#disable-gpu-support)
|
||||||
|
|
||||||
### Overview
|
### Overview
|
||||||
MediaPipe supports calculator nodes for GPU compute and rendering, and allows combining multiple GPU nodes, as well as mixing them with CPU based calculator nodes. There exist several GPU APIs on mobile platforms (eg, OpenGL ES, Metal and Vulkan). MediaPipe does not attempt to offer a single cross-API GPU abstraction. Individual nodes can be written using different APIs, allowing them to take advantage of platform specific features when needed.
|
MediaPipe supports calculator nodes for GPU compute and rendering, and allows combining multiple GPU nodes, as well as mixing them with CPU based calculator nodes. There exist several GPU APIs on mobile platforms (eg, OpenGL ES, Metal and Vulkan). MediaPipe does not attempt to offer a single cross-API GPU abstraction. Individual nodes can be written using different APIs, allowing them to take advantage of platform specific features when needed.
|
||||||
|
@ -23,6 +23,7 @@ Below are the design principles for GPU support in MediaPipe
|
||||||
* A calculator should be allowed maximum flexibility in using the GPU for all or part of its operation, combining it with the CPU if necessary.
|
* A calculator should be allowed maximum flexibility in using the GPU for all or part of its operation, combining it with the CPU if necessary.
|
||||||
|
|
||||||
### OpenGL support
|
### OpenGL support
|
||||||
|
|
||||||
MediaPipe supports OpenGL ES up to version 3.2 on Android and up to ES 3.0 on iOS. In addition, MediaPipe also supports Metal on iOS.
|
MediaPipe supports OpenGL ES up to version 3.2 on Android and up to ES 3.0 on iOS. In addition, MediaPipe also supports Metal on iOS.
|
||||||
|
|
||||||
* MediaPipe allows graphs to run OpenGL in multiple GL contexts. For example, this can be very useful in graphs that combine a slower GPU inference path (eg, at 10 FPS) with a faster GPU rendering path (eg, at 30 FPS): since one GL context corresponds to one sequential command queue, using the same context for both tasks would reduce the rendering frame rate. One challenge MediaPipe's use of multiple contexts solves is the ability to communicate across them. An example scenario is one with an input video that is sent to both the rendering and inferences paths, and rendering needs to have access to the latest output from inference.
|
* MediaPipe allows graphs to run OpenGL in multiple GL contexts. For example, this can be very useful in graphs that combine a slower GPU inference path (eg, at 10 FPS) with a faster GPU rendering path (eg, at 30 FPS): since one GL context corresponds to one sequential command queue, using the same context for both tasks would reduce the rendering frame rate. One challenge MediaPipe's use of multiple contexts solves is the ability to communicate across them. An example scenario is one with an input video that is sent to both the rendering and inferences paths, and rendering needs to have access to the latest output from inference.
|
||||||
|
@ -128,3 +129,26 @@ The below diagram shows the data flow in a mobile application that captures vide
|
||||||
|:--:|
|
|:--:|
|
||||||
| *Video frames from the camera are fed into the graph as `GpuBuffer` packets. The input stream is accessed by two calculators in parallel. `GpuBufferToImageFrameCalculator` converts the buffer into an `ImageFrame`, which is then sent through a grayscale converter and a canny filter (both based on OpenCV and running on the CPU), whose output is then converted into a `GpuBuffer` again. A multi-input GPU calculator, GlOverlayCalculator, takes as input both the original `GpuBuffer` and the one coming out of the edge detector, and overlays them using a shader. The output is then sent back to the application using a callback calculator, and the application renders the image to the screen using OpenGL.* |
|
| *Video frames from the camera are fed into the graph as `GpuBuffer` packets. The input stream is accessed by two calculators in parallel. `GpuBufferToImageFrameCalculator` converts the buffer into an `ImageFrame`, which is then sent through a grayscale converter and a canny filter (both based on OpenCV and running on the CPU), whose output is then converted into a `GpuBuffer` again. A multi-input GPU calculator, GlOverlayCalculator, takes as input both the original `GpuBuffer` and the one coming out of the edge detector, and overlays them using a shader. The output is then sent back to the application using a callback calculator, and the application renders the image to the screen using OpenGL.* |
|
||||||
|
|
||||||
|
### Disable GPU Support
|
||||||
|
|
||||||
|
By default, building MediaPipe (with no special bazel flags) attempts to compile
|
||||||
|
and link against OpenGL/Metal libraries.
|
||||||
|
|
||||||
|
There are some command line build flags available to disable/enable GPU support
|
||||||
|
within the MediaPipe framework:
|
||||||
|
|
||||||
|
```
|
||||||
|
# To disable *all* gpu support
|
||||||
|
bazel build --define MEDIAPIPE_DISABLE_GPU=1 <my-target>
|
||||||
|
|
||||||
|
# to enable full GPU support (OpenGL ES 3.1+ & Metal)
|
||||||
|
bazel build --copt -DMESA_EGL_NO_X11_HEADERS <my-target>
|
||||||
|
|
||||||
|
# to enable only OpenGL ES 3.0 and below (no GLES 3.1+ features)
|
||||||
|
bazel build --copt -DMESA_EGL_NO_X11_HEADERS --copt -DMEDIAPIPE_DISABLE_GL_COMPUTE <my-target>
|
||||||
|
```
|
||||||
|
|
||||||
|
Note *MEDIAPIPE_DISABLE_GL_COMPUTE* is automatically defined on all Apple
|
||||||
|
systems (Apple doesn't support OpenGL ES 3.1+).
|
||||||
|
|
||||||
|
Note on iOS and Android, it is assumed that GPU support will be enabled.
|
||||||
|
|
|
@ -629,7 +629,7 @@ to load both dependencies:
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
BIN
mediapipe/docs/images/face_detection_demo_coral.jpg
Normal file
BIN
mediapipe/docs/images/face_detection_demo_coral.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 MiB |
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
BIN
mediapipe/docs/images/multi_hand_tracking_android_gpu.gif
Normal file
BIN
mediapipe/docs/images/multi_hand_tracking_android_gpu.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 MiB |
BIN
mediapipe/docs/images/multi_hand_tracking_android_gpu_small.gif
Normal file
BIN
mediapipe/docs/images/multi_hand_tracking_android_gpu_small.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 MiB |
BIN
mediapipe/docs/images/object_detection_demo_coral.jpg
Normal file
BIN
mediapipe/docs/images/object_detection_demo_coral.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 MiB |
|
@ -245,19 +245,23 @@ To build and run iOS apps:
|
||||||
$ cd mediapipe
|
$ cd mediapipe
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Install Bazel (0.24.1 and above required).
|
3. Install Bazel (version between 0.24.1 and 1.1.0).
|
||||||
|
|
||||||
Option 1. Use package manager tool to install the latest version of Bazel.
|
Option 1. Use package manager tool to install Bazel 1.1.0
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ brew install bazel
|
# If Bazel 1.1.0+ was installed.
|
||||||
|
$ brew uninstall bazel
|
||||||
# Run 'bazel version' to check version of bazel installed
|
# Install Bazel 1.1.0
|
||||||
|
$ brew install https://raw.githubusercontent.com/bazelbuild/homebrew-tap/f8a0fa981bcb1784a0d0823e14867b844e94fb3d/Formula/bazel.rb
|
||||||
|
$ brew link bazel
|
||||||
|
# Run 'bazel version' to check version of bazel
|
||||||
```
|
```
|
||||||
|
|
||||||
Option 2. Follow the official
|
Option 2. Follow the official
|
||||||
[Bazel documentation](https://docs.bazel.build/versions/master/install-os-x.html#install-with-installer-mac-os-x)
|
[Bazel documentation](https://docs.bazel.build/versions/master/install-os-x.html#install-with-installer-mac-os-x)
|
||||||
to install any version of Bazel manually.
|
to install any version of Bazel manually. Note that MediaPipe doesn't
|
||||||
|
support Bazel 1.1.0+ on macOS yet.
|
||||||
|
|
||||||
4. Install OpenCV and FFmpeg.
|
4. Install OpenCV and FFmpeg.
|
||||||
|
|
||||||
|
@ -526,7 +530,7 @@ This will use a Docker image that will isolate mediapipe's installation from the
|
||||||
```bash
|
```bash
|
||||||
$ docker run -it --name mediapipe mediapipe:latest
|
$ docker run -it --name mediapipe mediapipe:latest
|
||||||
|
|
||||||
root@bca08b91ff63:/mediapipe# bash ./setup_android_sdk_and_ndk
|
root@bca08b91ff63:/mediapipe# bash ./setup_android_sdk_and_ndk.sh
|
||||||
|
|
||||||
# Should print:
|
# Should print:
|
||||||
# Android NDK is now installed. Consider setting $ANDROID_NDK_HOME environment variable to be /root/Android/Sdk/ndk-bundle/android-ndk-r18b
|
# Android NDK is now installed. Consider setting $ANDROID_NDK_HOME environment variable to be /root/Android/Sdk/ndk-bundle/android-ndk-r18b
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
2. Install [Bazel](https://bazel.build/).
|
2. Install [Bazel](https://bazel.build/).
|
||||||
|
|
||||||
See their [instructions](https://docs.bazel.build/versions/master/install-os-x.html).
|
See their
|
||||||
|
[instructions](https://docs.bazel.build/versions/master/install-os-x.html).
|
||||||
We recommend using [Homebrew](https://brew.sh/):
|
We recommend using [Homebrew](https://brew.sh/):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@ -15,13 +16,23 @@
|
||||||
brew install bazelbuild/tap/bazel
|
brew install bazelbuild/tap/bazel
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Clone the MediaPipe repository.
|
3. Install python "future" and "six".
|
||||||
|
|
||||||
|
To make Mediapipe work with TensorFlow, please install the python "future"
|
||||||
|
library and the python "six" library:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install --user future six
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Clone the MediaPipe repository.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/google/mediapipe.git
|
git clone https://github.com/google/mediapipe.git
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Symlink or copy your provisioning profile to `mediapipe/mediapipe/provisioning_profile.mobileprovision`.
|
5. Symlink or copy your provisioning profile to
|
||||||
|
`mediapipe/mediapipe/provisioning_profile.mobileprovision`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd mediapipe
|
cd mediapipe
|
||||||
|
|
|
@ -156,7 +156,7 @@ node {
|
||||||
output_stream: "multi_hand_rects"
|
output_stream: "multi_hand_rects"
|
||||||
node_options: {
|
node_options: {
|
||||||
[type.googleapis.com/mediapipe.AssociationCalculatorOptions] {
|
[type.googleapis.com/mediapipe.AssociationCalculatorOptions] {
|
||||||
min_similarity_threshold: 0.1
|
min_similarity_threshold: 0.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,7 @@ node {
|
||||||
output_stream: "multi_hand_rects"
|
output_stream: "multi_hand_rects"
|
||||||
node_options: {
|
node_options: {
|
||||||
[type.googleapis.com/mediapipe.AssociationCalculatorOptions] {
|
[type.googleapis.com/mediapipe.AssociationCalculatorOptions] {
|
||||||
min_similarity_threshold: 0.1
|
min_similarity_threshold: 0.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -560,7 +560,7 @@ node {
|
||||||
# BATCH_END timestamp, outputs the vector of landmarks at the BATCH_END
|
# BATCH_END timestamp, outputs the vector of landmarks at the BATCH_END
|
||||||
# timestamp.
|
# timestamp.
|
||||||
node {
|
node {
|
||||||
calculator: "EndLoopNormalizedLandmarksVectorCalculator"
|
calculator: "EndLoopNormalizedLandmarkListVectorCalculator"
|
||||||
input_stream: "ITEM:single_hand_landmarks"
|
input_stream: "ITEM:single_hand_landmarks"
|
||||||
input_stream: "BATCH_END:single_hand_rect_timestamp"
|
input_stream: "BATCH_END:single_hand_rect_timestamp"
|
||||||
output_stream: "ITERABLE:multi_hand_landmarks"
|
output_stream: "ITERABLE:multi_hand_landmarks"
|
||||||
|
@ -580,7 +580,7 @@ node {
|
||||||
# hand. If the hand presence for hand #i is false, the set of landmarks
|
# hand. If the hand presence for hand #i is false, the set of landmarks
|
||||||
# corresponding to that hand are dropped from the vector.
|
# corresponding to that hand are dropped from the vector.
|
||||||
node {
|
node {
|
||||||
calculator: "FilterLandmarksCollectionCalculator"
|
calculator: "FilterLandmarkListCollectionCalculator"
|
||||||
input_stream: "ITERABLE:multi_hand_landmarks"
|
input_stream: "ITERABLE:multi_hand_landmarks"
|
||||||
input_stream: "CONDITION:multi_hand_presence"
|
input_stream: "CONDITION:multi_hand_presence"
|
||||||
output_stream: "ITERABLE:filtered_multi_hand_landmarks"
|
output_stream: "ITERABLE:filtered_multi_hand_landmarks"
|
||||||
|
@ -669,7 +669,7 @@ node {
|
||||||
# timestamp for downstream calculators to inform them that all elements in the
|
# timestamp for downstream calculators to inform them that all elements in the
|
||||||
# vector have been processed.
|
# vector have been processed.
|
||||||
node {
|
node {
|
||||||
calculator: "BeginLoopNormalizedLandmarksVectorCalculator"
|
calculator: "BeginLoopNormalizedLandmarkListVectorCalculator"
|
||||||
input_stream: "ITERABLE:multi_hand_landmarks"
|
input_stream: "ITERABLE:multi_hand_landmarks"
|
||||||
output_stream: "ITEM:single_hand_landmarks"
|
output_stream: "ITEM:single_hand_landmarks"
|
||||||
output_stream: "BATCH_END:landmark_timestamp"
|
output_stream: "BATCH_END:landmark_timestamp"
|
||||||
|
|
20
mediapipe/docs/object_detection_coral_devboard.md
Normal file
20
mediapipe/docs/object_detection_coral_devboard.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
## Object Detection on Coral with Webcam
|
||||||
|
|
||||||
|
MediaPipe is able to run cross platform across device types like desktop, mobile
|
||||||
|
and edge devices. Here is an example of running MediaPipe
|
||||||
|
[object detection pipeline](./object_detection_desktop.md) on edge device like
|
||||||
|
[Google Coral dev board](https://coral.withgoogle.com/products/dev-board) with
|
||||||
|
[Edge TPU](https://cloud.google.com/edge-tpu/). This MediaPipe Coral object
|
||||||
|
detection pipeline is running [coral specific quantized version](https://github.com/google/mediapipe/blob/master/mediapipe/examples/coral/models/object-detector-quantized_edgetpu.tflite)
|
||||||
|
of the [MediaPipe object detection TFLite model](https://github.com/google/mediapipe/blob/master/mediapipe/models/object_detection_front.tflite)
|
||||||
|
accelerated on Edge TPU.
|
||||||
|
|
||||||
|
### Cross compilation of MediaPipe Coral binaries in Docker
|
||||||
|
|
||||||
|
We recommend building the MediaPipe binaries not on the edge device due to
|
||||||
|
limited compute resulting in long build times. Instead, we will build MediaPipe
|
||||||
|
binaries using Docker containers on a more powerful host machine. For step by
|
||||||
|
step details of cross compiling and running MediaPipe binaries on Coral dev
|
||||||
|
board, please refer to [README.md in MediaPipe Coral example folder](https://github.com/google/mediapipe/blob/master/mediapipe/examples/coral/README.md).
|
||||||
|
|
||||||
|

|
|
@ -47,7 +47,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
||||||
|
|
|
@ -75,6 +75,7 @@ android_library(
|
||||||
resource_files = glob(["res/**"]),
|
resource_files = glob(["res/**"]),
|
||||||
deps = [
|
deps = [
|
||||||
":mediapipe_jni_lib",
|
":mediapipe_jni_lib",
|
||||||
|
"//mediapipe/framework/formats:landmark_java_proto_lite",
|
||||||
"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper",
|
"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper",
|
||||||
"//mediapipe/java/com/google/mediapipe/components:android_components",
|
"//mediapipe/java/com/google/mediapipe/components:android_components",
|
||||||
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
||||||
|
|
|
@ -17,18 +17,23 @@ package com.google.mediapipe.apps.handtrackinggpu;
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmark;
|
||||||
|
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmarkList;
|
||||||
import com.google.mediapipe.components.CameraHelper;
|
import com.google.mediapipe.components.CameraHelper;
|
||||||
import com.google.mediapipe.components.CameraXPreviewHelper;
|
import com.google.mediapipe.components.CameraXPreviewHelper;
|
||||||
import com.google.mediapipe.components.ExternalTextureConverter;
|
import com.google.mediapipe.components.ExternalTextureConverter;
|
||||||
import com.google.mediapipe.components.FrameProcessor;
|
import com.google.mediapipe.components.FrameProcessor;
|
||||||
import com.google.mediapipe.components.PermissionHelper;
|
import com.google.mediapipe.components.PermissionHelper;
|
||||||
import com.google.mediapipe.framework.AndroidAssetUtil;
|
import com.google.mediapipe.framework.AndroidAssetUtil;
|
||||||
|
import com.google.mediapipe.framework.PacketGetter;
|
||||||
import com.google.mediapipe.glutil.EglManager;
|
import com.google.mediapipe.glutil.EglManager;
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
/** Main activity of MediaPipe example apps. */
|
/** Main activity of MediaPipe example apps. */
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
@ -37,6 +42,8 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private static final String BINARY_GRAPH_NAME = "handtrackinggpu.binarypb";
|
private static final String BINARY_GRAPH_NAME = "handtrackinggpu.binarypb";
|
||||||
private static final String INPUT_VIDEO_STREAM_NAME = "input_video";
|
private static final String INPUT_VIDEO_STREAM_NAME = "input_video";
|
||||||
private static final String OUTPUT_VIDEO_STREAM_NAME = "output_video";
|
private static final String OUTPUT_VIDEO_STREAM_NAME = "output_video";
|
||||||
|
private static final String OUTPUT_HAND_PRESENCE_STREAM_NAME = "hand_presence";
|
||||||
|
private static final String OUTPUT_LANDMARKS_STREAM_NAME = "hand_landmarks";
|
||||||
private static final CameraHelper.CameraFacing CAMERA_FACING = CameraHelper.CameraFacing.FRONT;
|
private static final CameraHelper.CameraFacing CAMERA_FACING = CameraHelper.CameraFacing.FRONT;
|
||||||
|
|
||||||
// Flips the camera-preview frames vertically before sending them into FrameProcessor to be
|
// Flips the camera-preview frames vertically before sending them into FrameProcessor to be
|
||||||
|
@ -48,7 +55,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
||||||
|
@ -90,6 +97,41 @@ public class MainActivity extends AppCompatActivity {
|
||||||
OUTPUT_VIDEO_STREAM_NAME);
|
OUTPUT_VIDEO_STREAM_NAME);
|
||||||
processor.getVideoSurfaceOutput().setFlipY(FLIP_FRAMES_VERTICALLY);
|
processor.getVideoSurfaceOutput().setFlipY(FLIP_FRAMES_VERTICALLY);
|
||||||
|
|
||||||
|
processor.addPacketCallback(
|
||||||
|
OUTPUT_HAND_PRESENCE_STREAM_NAME,
|
||||||
|
(packet) -> {
|
||||||
|
Boolean handPresence = PacketGetter.getBool(packet);
|
||||||
|
if (!handPresence) {
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"[TS:" + packet.getTimestamp() + "] Hand presence is false, no hands detected.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
processor.addPacketCallback(
|
||||||
|
OUTPUT_LANDMARKS_STREAM_NAME,
|
||||||
|
(packet) -> {
|
||||||
|
byte[] landmarksRaw = PacketGetter.getProtoBytes(packet);
|
||||||
|
try {
|
||||||
|
NormalizedLandmarkList landmarks = NormalizedLandmarkList.parseFrom(landmarksRaw);
|
||||||
|
if (landmarks == null) {
|
||||||
|
Log.d(TAG, "[TS:" + packet.getTimestamp() + "] No hand landmarks.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Note: If hand_presence is false, these landmarks are useless.
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"[TS:"
|
||||||
|
+ packet.getTimestamp()
|
||||||
|
+ "] #Landmarks for hand: "
|
||||||
|
+ landmarks.getLandmarkCount());
|
||||||
|
Log.d(TAG, getLandmarksDebugString(landmarks));
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
Log.e(TAG, "Couldn't Exception received - " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
PermissionHelper.checkAndRequestCameraPermissions(this);
|
PermissionHelper.checkAndRequestCameraPermissions(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,4 +206,23 @@ public class MainActivity extends AppCompatActivity {
|
||||||
});
|
});
|
||||||
cameraHelper.startCamera(this, CAMERA_FACING, /*surfaceTexture=*/ null);
|
cameraHelper.startCamera(this, CAMERA_FACING, /*surfaceTexture=*/ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ android_library(
|
||||||
resource_files = glob(["res/**"]),
|
resource_files = glob(["res/**"]),
|
||||||
deps = [
|
deps = [
|
||||||
":mediapipe_jni_lib",
|
":mediapipe_jni_lib",
|
||||||
|
"//mediapipe/framework/formats:landmark_java_proto_lite",
|
||||||
"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper",
|
"//mediapipe/java/com/google/mediapipe/components:android_camerax_helper",
|
||||||
"//mediapipe/java/com/google/mediapipe/components:android_components",
|
"//mediapipe/java/com/google/mediapipe/components:android_components",
|
||||||
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
||||||
|
|
|
@ -17,18 +17,23 @@ package com.google.mediapipe.apps.multihandtrackinggpu;
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmark;
|
||||||
|
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmarkList;
|
||||||
import com.google.mediapipe.components.CameraHelper;
|
import com.google.mediapipe.components.CameraHelper;
|
||||||
import com.google.mediapipe.components.CameraXPreviewHelper;
|
import com.google.mediapipe.components.CameraXPreviewHelper;
|
||||||
import com.google.mediapipe.components.ExternalTextureConverter;
|
import com.google.mediapipe.components.ExternalTextureConverter;
|
||||||
import com.google.mediapipe.components.FrameProcessor;
|
import com.google.mediapipe.components.FrameProcessor;
|
||||||
import com.google.mediapipe.components.PermissionHelper;
|
import com.google.mediapipe.components.PermissionHelper;
|
||||||
import com.google.mediapipe.framework.AndroidAssetUtil;
|
import com.google.mediapipe.framework.AndroidAssetUtil;
|
||||||
|
import com.google.mediapipe.framework.PacketGetter;
|
||||||
import com.google.mediapipe.glutil.EglManager;
|
import com.google.mediapipe.glutil.EglManager;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/** Main activity of MediaPipe example apps. */
|
/** Main activity of MediaPipe example apps. */
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
@ -37,6 +42,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private static final String BINARY_GRAPH_NAME = "multihandtrackinggpu.binarypb";
|
private static final String BINARY_GRAPH_NAME = "multihandtrackinggpu.binarypb";
|
||||||
private static final String INPUT_VIDEO_STREAM_NAME = "input_video";
|
private static final String INPUT_VIDEO_STREAM_NAME = "input_video";
|
||||||
private static final String OUTPUT_VIDEO_STREAM_NAME = "output_video";
|
private static final String OUTPUT_VIDEO_STREAM_NAME = "output_video";
|
||||||
|
private static final String OUTPUT_LANDMARKS_STREAM_NAME = "multi_hand_landmarks";
|
||||||
private static final CameraHelper.CameraFacing CAMERA_FACING = CameraHelper.CameraFacing.FRONT;
|
private static final CameraHelper.CameraFacing CAMERA_FACING = CameraHelper.CameraFacing.FRONT;
|
||||||
|
|
||||||
// Flips the camera-preview frames vertically before sending them into FrameProcessor to be
|
// Flips the camera-preview frames vertically before sending them into FrameProcessor to be
|
||||||
|
@ -48,7 +54,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
||||||
|
@ -90,6 +96,20 @@ public class MainActivity extends AppCompatActivity {
|
||||||
OUTPUT_VIDEO_STREAM_NAME);
|
OUTPUT_VIDEO_STREAM_NAME);
|
||||||
processor.getVideoSurfaceOutput().setFlipY(FLIP_FRAMES_VERTICALLY);
|
processor.getVideoSurfaceOutput().setFlipY(FLIP_FRAMES_VERTICALLY);
|
||||||
|
|
||||||
|
processor.addPacketCallback(
|
||||||
|
OUTPUT_LANDMARKS_STREAM_NAME,
|
||||||
|
(packet) -> {
|
||||||
|
Log.d(TAG, "Received multi-hand landmarks packet.");
|
||||||
|
List<NormalizedLandmarkList> multiHandLandmarks =
|
||||||
|
PacketGetter.getProtoVector(packet, NormalizedLandmarkList.parser());
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
"[TS:"
|
||||||
|
+ packet.getTimestamp()
|
||||||
|
+ "] "
|
||||||
|
+ getMultiHandLandmarksDebugString(multiHandLandmarks));
|
||||||
|
});
|
||||||
|
|
||||||
PermissionHelper.checkAndRequestCameraPermissions(this);
|
PermissionHelper.checkAndRequestCameraPermissions(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,4 +184,32 @@ public class MainActivity extends AppCompatActivity {
|
||||||
});
|
});
|
||||||
cameraHelper.startCamera(this, CAMERA_FACING, /*surfaceTexture=*/ null);
|
cameraHelper.startCamera(this, CAMERA_FACING, /*surfaceTexture=*/ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getMultiHandLandmarksDebugString(List<NormalizedLandmarkList> multiHandLandmarks) {
|
||||||
|
if (multiHandLandmarks.isEmpty()) {
|
||||||
|
return "No hand landmarks";
|
||||||
|
}
|
||||||
|
String multiHandLandmarksStr = "Number of hands detected: " + multiHandLandmarks.size() + "\n";
|
||||||
|
int handIndex = 0;
|
||||||
|
for (NormalizedLandmarkList landmarks : multiHandLandmarks) {
|
||||||
|
multiHandLandmarksStr +=
|
||||||
|
"\t#Hand landmarks for hand[" + handIndex + "]: " + landmarks.getLandmarkCount() + "\n";
|
||||||
|
int landmarkIndex = 0;
|
||||||
|
for (NormalizedLandmark landmark : landmarks.getLandmarkList()) {
|
||||||
|
multiHandLandmarksStr +=
|
||||||
|
"\t\tLandmark ["
|
||||||
|
+ landmarkIndex
|
||||||
|
+ "]: ("
|
||||||
|
+ landmark.getX()
|
||||||
|
+ ", "
|
||||||
|
+ landmark.getY()
|
||||||
|
+ ", "
|
||||||
|
+ landmark.getZ()
|
||||||
|
+ ")\n";
|
||||||
|
++landmarkIndex;
|
||||||
|
}
|
||||||
|
++handIndex;
|
||||||
|
}
|
||||||
|
return multiHandLandmarksStr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
static {
|
static {
|
||||||
// Load all native libraries needed by the app.
|
// Load all native libraries needed by the app.
|
||||||
System.loadLibrary("mediapipe_jni");
|
System.loadLibrary("mediapipe_jni");
|
||||||
System.loadLibrary("opencv_java4");
|
System.loadLibrary("opencv_java3");
|
||||||
}
|
}
|
||||||
|
|
||||||
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
// {@link SurfaceTexture} where the camera-preview frames can be accessed.
|
||||||
|
|
56
mediapipe/examples/coral/BUILD
Normal file
56
mediapipe/examples/coral/BUILD
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# Copyright 2019 The MediaPipe Authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
|
package(default_visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
])
|
||||||
|
|
||||||
|
# Graph Runner
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "demo_run_graph_main",
|
||||||
|
srcs = ["demo_run_graph_main.cc"],
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework/formats:image_frame",
|
||||||
|
"//mediapipe/framework/formats:image_frame_opencv",
|
||||||
|
"//mediapipe/framework/port:commandlineflags",
|
||||||
|
"//mediapipe/framework/port:file_helpers",
|
||||||
|
"//mediapipe/framework/port:opencv_highgui",
|
||||||
|
"//mediapipe/framework/port:opencv_imgproc",
|
||||||
|
"//mediapipe/framework/port:opencv_video",
|
||||||
|
"//mediapipe/framework/port:parse_text_proto",
|
||||||
|
"//mediapipe/framework/port:status",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Demos
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "object_detection_cpu",
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/examples/coral:demo_run_graph_main",
|
||||||
|
"//mediapipe/graphs/object_detection:desktop_tflite_calculators",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "face_detection_cpu",
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/examples/coral:demo_run_graph_main",
|
||||||
|
"//mediapipe/graphs/face_detection:desktop_tflite_calculators",
|
||||||
|
],
|
||||||
|
)
|
87
mediapipe/examples/coral/Dockerfile
Normal file
87
mediapipe/examples/coral/Dockerfile
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
# Copyright 2019 The MediaPipe Authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
#==== ! Prerequisite ! ====
|
||||||
|
# $ sh mediapipe/examples/coral/setup.sh
|
||||||
|
#====
|
||||||
|
|
||||||
|
# for opencv 3.2 default
|
||||||
|
FROM ubuntu:18.04
|
||||||
|
|
||||||
|
MAINTAINER <mediapipe@google.com>
|
||||||
|
|
||||||
|
WORKDIR /mediapipe
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# Install MediaPipe & Coral deps
|
||||||
|
|
||||||
|
COPY update_sources.sh /
|
||||||
|
RUN /update_sources.sh
|
||||||
|
|
||||||
|
RUN dpkg --add-architecture armhf
|
||||||
|
RUN dpkg --add-architecture arm64
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
crossbuild-essential-arm64 \
|
||||||
|
libusb-1.0-0-dev \
|
||||||
|
libusb-1.0-0-dev:arm64 \
|
||||||
|
zlib1g-dev \
|
||||||
|
zlib1g-dev:arm64 \
|
||||||
|
pkg-config \
|
||||||
|
zip \
|
||||||
|
unzip \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
git \
|
||||||
|
python \
|
||||||
|
python-pip \
|
||||||
|
python3-pip \
|
||||||
|
vim-common \
|
||||||
|
ca-certificates \
|
||||||
|
emacs \
|
||||||
|
software-properties-common && \
|
||||||
|
add-apt-repository -y ppa:openjdk-r/ppa && \
|
||||||
|
apt-get update && apt-get install -y openjdk-8-jdk
|
||||||
|
|
||||||
|
RUN pip install --upgrade setuptools
|
||||||
|
RUN pip install future
|
||||||
|
RUN pip3 install six
|
||||||
|
|
||||||
|
COPY . /mediapipe/
|
||||||
|
|
||||||
|
# Install bazel
|
||||||
|
|
||||||
|
ARG BAZEL_VERSION=0.29.1
|
||||||
|
RUN mkdir /bazel && \
|
||||||
|
wget --no-check-certificate -O /bazel/installer.sh "https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh" && \
|
||||||
|
wget --no-check-certificate -O /bazel/LICENSE.txt "https://raw.githubusercontent.com/bazelbuild/bazel/master/LICENSE" && \
|
||||||
|
chmod +x /bazel/installer.sh && \
|
||||||
|
/bazel/installer.sh && \
|
||||||
|
rm -f /bazel/installer.sh
|
||||||
|
|
||||||
|
# OpenCV (3.2 default in 18.04)
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y libopencv-dev
|
||||||
|
|
||||||
|
# Opencv libs copied from coral device into opencv32_arm64_libs
|
||||||
|
|
||||||
|
RUN cp opencv32_arm64_libs/* /usr/lib/aarch64-linux-gnu/.
|
||||||
|
|
||||||
|
# Edge tpu header and lib
|
||||||
|
|
||||||
|
RUN git clone https://github.com/google-coral/edgetpu.git /edgetpu
|
||||||
|
RUN cp /edgetpu/libedgetpu/direct/aarch64/libedgetpu.so.1.0 /usr/lib/aarch64-linux-gnu/libedgetpu.so
|
||||||
|
|
||||||
|
# See mediapipe/examples/coral/README.md to finish setup
|
137
mediapipe/examples/coral/README.md
Normal file
137
mediapipe/examples/coral/README.md
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
# Coral Dev Board Setup (experimental)
|
||||||
|
|
||||||
|
**Dislaimer**: Running MediaPipe on Coral is experimental, and this process may not be exact and is subject to change. These instructions have only been tested on the coral dev board with OS version _mendel day_, and may vary for different devices and workstations.
|
||||||
|
|
||||||
|
This file describes how to prepare a Google Coral Dev Board and setup a linux Docker container for building MediaPipe applications that run on Edge TPU.
|
||||||
|
|
||||||
|
## Before creating the Docker
|
||||||
|
|
||||||
|
* (on host machine) run _setup.sh_ from MediaPipe root directory
|
||||||
|
|
||||||
|
sh mediapipe/examples/coral/setup.sh
|
||||||
|
|
||||||
|
* Setup the coral device via [here](https://coral.withgoogle.com/docs/dev-board/get-started/), and ensure the _mdt_ command works
|
||||||
|
|
||||||
|
* (on coral device) prepare MediaPipe
|
||||||
|
|
||||||
|
cd ~
|
||||||
|
sudo apt-get install git
|
||||||
|
git clone https://github.com/google/mediapipe.git
|
||||||
|
mkdir mediapipe/bazel-bin
|
||||||
|
|
||||||
|
* (on coral device) install opencv 3.2
|
||||||
|
|
||||||
|
sudo apt-get update && apt-get install -y libopencv-dev
|
||||||
|
|
||||||
|
* (on coral device) find all opencv libs
|
||||||
|
|
||||||
|
find /usr/lib/aarch64-linux-gnu/ -name 'libopencv*so'
|
||||||
|
|
||||||
|
* (on host machine) copy core opencv libs from coral device to a local folder inside MediaPipe checkout:
|
||||||
|
|
||||||
|
# in root level mediapipe folder #
|
||||||
|
mdt pull /usr/lib/aarch64-linux-gnu/libopencv_core.so opencv32_arm64_libs
|
||||||
|
mdt pull /usr/lib/aarch64-linux-gnu/libopencv_calib3d.so opencv32_arm64_libs
|
||||||
|
mdt pull /usr/lib/aarch64-linux-gnu/libopencv_features2d.so opencv32_arm64_libs
|
||||||
|
mdt pull /usr/lib/aarch64-linux-gnu/libopencv_highgui.so opencv32_arm64_libs
|
||||||
|
mdt pull /usr/lib/aarch64-linux-gnu/libopencv_imgcodecs.so opencv32_arm64_libs
|
||||||
|
mdt pull /usr/lib/aarch64-linux-gnu/libopencv_imgproc.so opencv32_arm64_libs
|
||||||
|
mdt pull /usr/lib/aarch64-linux-gnu/libopencv_video.so opencv32_arm64_libs
|
||||||
|
mdt pull /usr/lib/aarch64-linux-gnu/libopencv_videoio.so opencv32_arm64_libs
|
||||||
|
|
||||||
|
* (on host machine) Create and start the docker environment
|
||||||
|
|
||||||
|
# from mediapipe root level directory #
|
||||||
|
docker build -t coral .
|
||||||
|
docker run -it --name coral coral:latest
|
||||||
|
|
||||||
|
## Inside the Docker environment
|
||||||
|
|
||||||
|
* Update library paths in /mediapipe/third_party/opencv_linux.BUILD
|
||||||
|
|
||||||
|
(replace 'x86_64-linux-gnu' with 'aarch64-linux-gnu')
|
||||||
|
|
||||||
|
"lib/aarch64-linux-gnu/libopencv_core.so",
|
||||||
|
"lib/aarch64-linux-gnu/libopencv_calib3d.so",
|
||||||
|
"lib/aarch64-linux-gnu/libopencv_features2d.so",
|
||||||
|
"lib/aarch64-linux-gnu/libopencv_highgui.so",
|
||||||
|
"lib/aarch64-linux-gnu/libopencv_imgcodecs.so",
|
||||||
|
"lib/aarch64-linux-gnu/libopencv_imgproc.so",
|
||||||
|
"lib/aarch64-linux-gnu/libopencv_video.so",
|
||||||
|
"lib/aarch64-linux-gnu/libopencv_videoio.so",
|
||||||
|
|
||||||
|
* Attempt to build hello world (to download external deps)
|
||||||
|
|
||||||
|
bazel build -c opt --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/examples/desktop/hello_world:hello_world
|
||||||
|
|
||||||
|
* Edit /mediapipe/bazel-mediapipe/external/com_github_glog_glog/src/signalhandler.cc
|
||||||
|
|
||||||
|
on line 78, replace
|
||||||
|
|
||||||
|
return (void*)context->PC_FROM_UCONTEXT;
|
||||||
|
|
||||||
|
with
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
* Edit /edgetpu/libedgetpu/BUILD
|
||||||
|
|
||||||
|
to add this build target
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "lib",
|
||||||
|
srcs = [
|
||||||
|
"libedgetpu.so",
|
||||||
|
],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
||||||
|
|
||||||
|
* Edit *tflite_inference_calculator.cc* BUILD rules:
|
||||||
|
|
||||||
|
sed -i 's/\":tflite_inference_calculator_cc_proto\",/\":tflite_inference_calculator_cc_proto\",\n\t\"@edgetpu\/\/:header\",\n\t\"@libedgetpu\/\/:lib\",/g' mediapipe/calculators/tflite/BUILD
|
||||||
|
|
||||||
|
The above command should add
|
||||||
|
|
||||||
|
"@edgetpu//:header",
|
||||||
|
"@libedgetpu//:lib",
|
||||||
|
|
||||||
|
to the _deps_ of tflite_inference_calculator.cc
|
||||||
|
|
||||||
|
#### Now try cross-compiling for device
|
||||||
|
|
||||||
|
* Object detection demo
|
||||||
|
|
||||||
|
bazel build -c opt --crosstool_top=@crosstool//:toolchains --compiler=gcc --cpu=aarch64 --define MEDIAPIPE_DISABLE_GPU=1 --copt -DMEDIAPIPE_EDGE_TPU --copt=-flax-vector-conversions mediapipe/examples/coral:object_detection_cpu
|
||||||
|
|
||||||
|
Copy object_detection_cpu binary to the MediaPipe checkout on the coral device
|
||||||
|
|
||||||
|
# outside docker env, open new terminal on host machine #
|
||||||
|
docker ps
|
||||||
|
docker cp <container-id>:/mediapipe/bazel-bin/mediapipe/examples/coral/object_detection_cpu /tmp/.
|
||||||
|
mdt push /tmp/object_detection_cpu /home/mendel/mediapipe/bazel-bin/.
|
||||||
|
|
||||||
|
* Face detection demo
|
||||||
|
|
||||||
|
bazel build -c opt --crosstool_top=@crosstool//:toolchains --compiler=gcc --cpu=aarch64 --define MEDIAPIPE_DISABLE_GPU=1 --copt -DMEDIAPIPE_EDGE_TPU --copt=-flax-vector-conversions mediapipe/examples/coral:face_detection_cpu
|
||||||
|
|
||||||
|
Copy face_detection_cpu binary to the MediaPipe checkout on the coral device
|
||||||
|
|
||||||
|
# outside docker env, open new terminal on host machine #
|
||||||
|
docker ps
|
||||||
|
docker cp <container-id>:/mediapipe/bazel-bin/mediapipe/examples/coral/face_detection_cpu /tmp/.
|
||||||
|
mdt push /tmp/face_detection_cpu /home/mendel/mediapipe/bazel-bin/.
|
||||||
|
|
||||||
|
## On the coral device (with display)
|
||||||
|
|
||||||
|
# Object detection
|
||||||
|
cd ~/mediapipe
|
||||||
|
chmod +x bazel-bin/object_detection_cpu
|
||||||
|
export GLOG_logtostderr=1
|
||||||
|
bazel-bin/object_detection_cpu --calculator_graph_config_file=mediapipe/examples/coral/graphs/object_detection_desktop_live.pbtxt
|
||||||
|
|
||||||
|
# Face detection
|
||||||
|
cd ~/mediapipe
|
||||||
|
chmod +x bazel-bin/face_detection_cpu
|
||||||
|
export GLOG_logtostderr=1
|
||||||
|
bazel-bin/face_detection_cpu --calculator_graph_config_file=mediapipe/examples/coral/graphs/face_detection_desktop_live.pbtxt
|
||||||
|
|
313
mediapipe/examples/coral/WORKSPACE
Normal file
313
mediapipe/examples/coral/WORKSPACE
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
workspace(name = "mediapipe")
|
||||||
|
|
||||||
|
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||||
|
|
||||||
|
skylib_version = "0.8.0"
|
||||||
|
http_archive(
|
||||||
|
name = "bazel_skylib",
|
||||||
|
type = "tar.gz",
|
||||||
|
url = "https://github.com/bazelbuild/bazel-skylib/releases/download/{}/bazel-skylib.{}.tar.gz".format (skylib_version, skylib_version),
|
||||||
|
sha256 = "2ef429f5d7ce7111263289644d233707dba35e39696377ebab8b0bc701f7818e",
|
||||||
|
)
|
||||||
|
load("@bazel_skylib//lib:versions.bzl", "versions")
|
||||||
|
versions.check(minimum_bazel_version = "0.24.1")
|
||||||
|
|
||||||
|
# ABSL cpp library.
|
||||||
|
http_archive(
|
||||||
|
name = "com_google_absl",
|
||||||
|
# Head commit on 2019-04-12.
|
||||||
|
# TODO: Switch to the latest absl version when the problem gets
|
||||||
|
# fixed.
|
||||||
|
urls = [
|
||||||
|
"https://github.com/abseil/abseil-cpp/archive/a02f62f456f2c4a7ecf2be3104fe0c6e16fbad9a.tar.gz",
|
||||||
|
],
|
||||||
|
sha256 = "d437920d1434c766d22e85773b899c77c672b8b4865d5dc2cd61a29fdff3cf03",
|
||||||
|
strip_prefix = "abseil-cpp-a02f62f456f2c4a7ecf2be3104fe0c6e16fbad9a",
|
||||||
|
)
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "rules_cc",
|
||||||
|
strip_prefix = "rules_cc-master",
|
||||||
|
urls = ["https://github.com/bazelbuild/rules_cc/archive/master.zip"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# GoogleTest/GoogleMock framework. Used by most unit-tests.
|
||||||
|
http_archive(
|
||||||
|
name = "com_google_googletest",
|
||||||
|
urls = ["https://github.com/google/googletest/archive/master.zip"],
|
||||||
|
strip_prefix = "googletest-master",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Google Benchmark library.
|
||||||
|
http_archive(
|
||||||
|
name = "com_google_benchmark",
|
||||||
|
urls = ["https://github.com/google/benchmark/archive/master.zip"],
|
||||||
|
strip_prefix = "benchmark-master",
|
||||||
|
build_file = "@//third_party:benchmark.BUILD",
|
||||||
|
)
|
||||||
|
|
||||||
|
# gflags needed by glog
|
||||||
|
http_archive(
|
||||||
|
name = "com_github_gflags_gflags",
|
||||||
|
sha256 = "6e16c8bc91b1310a44f3965e616383dbda48f83e8c1eaa2370a215057b00cabe",
|
||||||
|
strip_prefix = "gflags-77592648e3f3be87d6c7123eb81cbad75f9aef5a",
|
||||||
|
urls = [
|
||||||
|
"https://mirror.bazel.build/github.com/gflags/gflags/archive/77592648e3f3be87d6c7123eb81cbad75f9aef5a.tar.gz",
|
||||||
|
"https://github.com/gflags/gflags/archive/77592648e3f3be87d6c7123eb81cbad75f9aef5a.tar.gz",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# glog
|
||||||
|
http_archive(
|
||||||
|
name = "com_github_glog_glog",
|
||||||
|
url = "https://github.com/google/glog/archive/v0.3.5.zip",
|
||||||
|
sha256 = "267103f8a1e9578978aa1dc256001e6529ef593e5aea38193d31c2872ee025e8",
|
||||||
|
strip_prefix = "glog-0.3.5",
|
||||||
|
build_file = "@//third_party:glog.BUILD",
|
||||||
|
patches = [
|
||||||
|
"@//third_party:com_github_glog_glog_9779e5ea6ef59562b030248947f787d1256132ae.diff"
|
||||||
|
],
|
||||||
|
patch_args = [
|
||||||
|
"-p1",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# libyuv
|
||||||
|
http_archive(
|
||||||
|
name = "libyuv",
|
||||||
|
urls = ["https://chromium.googlesource.com/libyuv/libyuv/+archive/refs/heads/master.tar.gz"],
|
||||||
|
build_file = "@//third_party:libyuv.BUILD",
|
||||||
|
)
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "com_google_protobuf_javalite",
|
||||||
|
sha256 = "79d102c61e2a479a0b7e5fc167bcfaa4832a0c6aad4a75fa7da0480564931bcc",
|
||||||
|
strip_prefix = "protobuf-384989534b2246d413dbcd750744faab2607b516",
|
||||||
|
urls = ["https://github.com/google/protobuf/archive/384989534b2246d413dbcd750744faab2607b516.zip"],
|
||||||
|
)
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "com_google_audio_tools",
|
||||||
|
strip_prefix = "multichannel-audio-tools-master",
|
||||||
|
urls = ["https://github.com/google/multichannel-audio-tools/archive/master.zip"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Needed by TensorFlow
|
||||||
|
http_archive(
|
||||||
|
name = "io_bazel_rules_closure",
|
||||||
|
sha256 = "e0a111000aeed2051f29fcc7a3f83be3ad8c6c93c186e64beb1ad313f0c7f9f9",
|
||||||
|
strip_prefix = "rules_closure-cf1e44edb908e9616030cc83d085989b8e6cd6df",
|
||||||
|
urls = [
|
||||||
|
"http://mirror.tensorflow.org/github.com/bazelbuild/rules_closure/archive/cf1e44edb908e9616030cc83d085989b8e6cd6df.tar.gz",
|
||||||
|
"https://github.com/bazelbuild/rules_closure/archive/cf1e44edb908e9616030cc83d085989b8e6cd6df.tar.gz", # 2019-04-04
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2019-11-12
|
||||||
|
_TENSORFLOW_GIT_COMMIT = "a5f9bcd64453ff3d1f64cb4da4786db3d2da7f82"
|
||||||
|
_TENSORFLOW_SHA256= "f2b6f2ab2ffe63e86eccd3ce4bea6b7197383d726638dfeeebcdc1e7de73f075"
|
||||||
|
http_archive(
|
||||||
|
name = "org_tensorflow",
|
||||||
|
urls = [
|
||||||
|
"https://mirror.bazel.build/github.com/tensorflow/tensorflow/archive/%s.tar.gz" % _TENSORFLOW_GIT_COMMIT,
|
||||||
|
"https://github.com/tensorflow/tensorflow/archive/%s.tar.gz" % _TENSORFLOW_GIT_COMMIT,
|
||||||
|
],
|
||||||
|
strip_prefix = "tensorflow-%s" % _TENSORFLOW_GIT_COMMIT,
|
||||||
|
sha256 = _TENSORFLOW_SHA256,
|
||||||
|
)
|
||||||
|
|
||||||
|
load("@org_tensorflow//tensorflow:workspace.bzl", "tf_workspace")
|
||||||
|
tf_workspace(tf_repo_name = "org_tensorflow")
|
||||||
|
|
||||||
|
# Please run
|
||||||
|
# $ sudo apt-get install libopencv-core-dev libopencv-highgui-dev \
|
||||||
|
# libopencv-imgproc-dev libopencv-video-dev
|
||||||
|
new_local_repository(
|
||||||
|
name = "linux_opencv",
|
||||||
|
build_file = "@//third_party:opencv_linux.BUILD",
|
||||||
|
path = "/usr",
|
||||||
|
)
|
||||||
|
|
||||||
|
new_local_repository(
|
||||||
|
name = "linux_ffmpeg",
|
||||||
|
build_file = "@//third_party:ffmpeg_linux.BUILD",
|
||||||
|
path = "/usr"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Please run $ brew install opencv@3
|
||||||
|
new_local_repository(
|
||||||
|
name = "macos_opencv",
|
||||||
|
build_file = "@//third_party:opencv_macos.BUILD",
|
||||||
|
path = "/usr",
|
||||||
|
)
|
||||||
|
|
||||||
|
new_local_repository(
|
||||||
|
name = "macos_ffmpeg",
|
||||||
|
build_file = "@//third_party:ffmpeg_macos.BUILD",
|
||||||
|
path = "/usr",
|
||||||
|
)
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "android_opencv",
|
||||||
|
sha256 = "056b849842e4fa8751d09edbb64530cfa7a63c84ccd232d0ace330e27ba55d0b",
|
||||||
|
build_file = "@//third_party:opencv_android.BUILD",
|
||||||
|
strip_prefix = "OpenCV-android-sdk",
|
||||||
|
type = "zip",
|
||||||
|
url = "https://github.com/opencv/opencv/releases/download/4.1.0/opencv-4.1.0-android-sdk.zip",
|
||||||
|
)
|
||||||
|
|
||||||
|
# After OpenCV 3.2.0, the pre-compiled opencv2.framework has google protobuf symbols, which will
|
||||||
|
# trigger duplicate symbol errors in the linking stage of building a mediapipe ios app.
|
||||||
|
# To get a higher version of OpenCV for iOS, opencv2.framework needs to be built from source with
|
||||||
|
# '-DBUILD_PROTOBUF=OFF -DBUILD_opencv_dnn=OFF'.
|
||||||
|
http_archive(
|
||||||
|
name = "ios_opencv",
|
||||||
|
sha256 = "7dd536d06f59e6e1156b546bd581523d8df92ce83440002885ec5abc06558de2",
|
||||||
|
build_file = "@//third_party:opencv_ios.BUILD",
|
||||||
|
type = "zip",
|
||||||
|
url = "https://github.com/opencv/opencv/releases/download/3.2.0/opencv-3.2.0-ios-framework.zip",
|
||||||
|
)
|
||||||
|
|
||||||
|
RULES_JVM_EXTERNAL_TAG = "2.2"
|
||||||
|
RULES_JVM_EXTERNAL_SHA = "f1203ce04e232ab6fdd81897cf0ff76f2c04c0741424d192f28e65ae752ce2d6"
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "rules_jvm_external",
|
||||||
|
strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
|
||||||
|
sha256 = RULES_JVM_EXTERNAL_SHA,
|
||||||
|
url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
|
||||||
|
)
|
||||||
|
|
||||||
|
load("@rules_jvm_external//:defs.bzl", "maven_install")
|
||||||
|
|
||||||
|
maven_install(
|
||||||
|
artifacts = [
|
||||||
|
"androidx.annotation:annotation:aar:1.1.0",
|
||||||
|
"androidx.appcompat:appcompat:aar:1.1.0-rc01",
|
||||||
|
"androidx.constraintlayout:constraintlayout:aar:1.1.3",
|
||||||
|
"androidx.core:core:aar:1.1.0-rc03",
|
||||||
|
"androidx.legacy:legacy-support-v4:aar:1.0.0",
|
||||||
|
"androidx.recyclerview:recyclerview:aar:1.1.0-beta02",
|
||||||
|
"com.google.android.material:material:aar:1.0.0-rc01",
|
||||||
|
],
|
||||||
|
repositories = ["https://dl.google.com/dl/android/maven2"],
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_server(
|
||||||
|
name = "google_server",
|
||||||
|
url = "https://dl.google.com/dl/android/maven2",
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = "androidx_lifecycle",
|
||||||
|
artifact = "androidx.lifecycle:lifecycle-common:2.0.0",
|
||||||
|
sha1 = "e070ffae07452331bc5684734fce6831d531785c",
|
||||||
|
server = "google_server",
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = "androidx_concurrent_futures",
|
||||||
|
artifact = "androidx.concurrent:concurrent-futures:1.0.0-alpha03",
|
||||||
|
sha1 = "b528df95c7e2fefa2210c0c742bf3e491c1818ae",
|
||||||
|
server = "google_server",
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = "com_google_guava_android",
|
||||||
|
artifact = "com.google.guava:guava:27.0.1-android",
|
||||||
|
sha1 = "b7e1c37f66ef193796ccd7ea6e80c2b05426182d",
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = "com_google_common_flogger",
|
||||||
|
artifact = "com.google.flogger:flogger:0.3.1",
|
||||||
|
sha1 = "585030fe1ec709760cbef997a459729fb965df0e",
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = "com_google_common_flogger_system_backend",
|
||||||
|
artifact = "com.google.flogger:flogger-system-backend:0.3.1",
|
||||||
|
sha1 = "287b569d76abcd82f9de87fe41829fbc7ebd8ac9",
|
||||||
|
)
|
||||||
|
|
||||||
|
maven_jar(
|
||||||
|
name = "com_google_code_findbugs",
|
||||||
|
artifact = "com.google.code.findbugs:jsr305:3.0.2",
|
||||||
|
sha1 = "25ea2e8b0c338a877313bd4672d3fe056ea78f0d",
|
||||||
|
)
|
||||||
|
|
||||||
|
# You may run setup_android.sh to install Android SDK and NDK.
|
||||||
|
android_ndk_repository(
|
||||||
|
name = "androidndk",
|
||||||
|
)
|
||||||
|
|
||||||
|
android_sdk_repository(
|
||||||
|
name = "androidsdk",
|
||||||
|
)
|
||||||
|
|
||||||
|
# iOS basic build deps.
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "build_bazel_rules_apple",
|
||||||
|
sha256 = "bdc8e66e70b8a75da23b79f1f8c6207356df07d041d96d2189add7ee0780cf4e",
|
||||||
|
strip_prefix = "rules_apple-b869b0d3868d78a1d4ffd866ccb304fb68aa12c3",
|
||||||
|
url = "https://github.com/bazelbuild/rules_apple/archive/b869b0d3868d78a1d4ffd866ccb304fb68aa12c3.tar.gz",
|
||||||
|
)
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@build_bazel_rules_apple//apple:repositories.bzl",
|
||||||
|
"apple_rules_dependencies",
|
||||||
|
)
|
||||||
|
|
||||||
|
apple_rules_dependencies()
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@build_bazel_rules_swift//swift:repositories.bzl",
|
||||||
|
"swift_rules_dependencies",
|
||||||
|
)
|
||||||
|
|
||||||
|
swift_rules_dependencies()
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@build_bazel_apple_support//lib:repositories.bzl",
|
||||||
|
"apple_support_dependencies",
|
||||||
|
)
|
||||||
|
|
||||||
|
apple_support_dependencies()
|
||||||
|
|
||||||
|
# More iOS deps.
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "google_toolbox_for_mac",
|
||||||
|
url = "https://github.com/google/google-toolbox-for-mac/archive/v2.2.1.zip",
|
||||||
|
sha256 = "e3ac053813c989a88703556df4dc4466e424e30d32108433ed6beaec76ba4fdc",
|
||||||
|
strip_prefix = "google-toolbox-for-mac-2.2.1",
|
||||||
|
build_file = "@//third_party:google_toolbox_for_mac.BUILD",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Coral
|
||||||
|
#COMMIT=$(git ls-remote https://github.com/google-coral/crosstool master | awk '{print $1}')
|
||||||
|
#SHA256=$(curl -L "https://github.com/google-coral/crosstool/archive/${COMMIT}.tar.gz" | sha256sum | awk '{print $1}')
|
||||||
|
http_archive(
|
||||||
|
name = "coral_crosstool",
|
||||||
|
sha256 = "cb31b1417ccdcf7dd9fca5ec63e1571672372c30427730255997a547569d2feb",
|
||||||
|
strip_prefix = "crosstool-9e00d5be43bf001f883b5700f5d04882fea00229",
|
||||||
|
urls = [
|
||||||
|
"https://github.com/google-coral/crosstool/archive/9e00d5be43bf001f883b5700f5d04882fea00229.tar.gz",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
load("@coral_crosstool//:configure.bzl", "cc_crosstool")
|
||||||
|
cc_crosstool(name = "crosstool")
|
||||||
|
|
||||||
|
# EdgeTPU
|
||||||
|
new_local_repository(
|
||||||
|
name = "edgetpu",
|
||||||
|
path = "/edgetpu/libedgetpu",
|
||||||
|
build_file = "/edgetpu/libedgetpu/BUILD"
|
||||||
|
)
|
||||||
|
new_local_repository(
|
||||||
|
name = "libedgetpu",
|
||||||
|
path = "/usr/lib/aarch64-linux-gnu",
|
||||||
|
build_file = "/edgetpu/libedgetpu/BUILD"
|
||||||
|
)
|
151
mediapipe/examples/coral/demo_run_graph_main.cc
Normal file
151
mediapipe/examples/coral/demo_run_graph_main.cc
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
// Copyright 2019 The MediaPipe Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// An example of sending OpenCV webcam frames into a MediaPipe graph.
|
||||||
|
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/formats/image_frame.h"
|
||||||
|
#include "mediapipe/framework/formats/image_frame_opencv.h"
|
||||||
|
#include "mediapipe/framework/port/commandlineflags.h"
|
||||||
|
#include "mediapipe/framework/port/file_helpers.h"
|
||||||
|
#include "mediapipe/framework/port/opencv_highgui_inc.h"
|
||||||
|
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
|
||||||
|
#include "mediapipe/framework/port/opencv_video_inc.h"
|
||||||
|
#include "mediapipe/framework/port/parse_text_proto.h"
|
||||||
|
#include "mediapipe/framework/port/status.h"
|
||||||
|
|
||||||
|
constexpr char kInputStream[] = "input_video";
|
||||||
|
constexpr char kOutputStream[] = "output_video";
|
||||||
|
constexpr char kWindowName[] = "MediaPipe";
|
||||||
|
|
||||||
|
DEFINE_string(
|
||||||
|
calculator_graph_config_file, "",
|
||||||
|
"Name of file containing text format CalculatorGraphConfig proto.");
|
||||||
|
DEFINE_string(input_video_path, "",
|
||||||
|
"Full path of video to load. "
|
||||||
|
"If not provided, attempt to use a webcam.");
|
||||||
|
DEFINE_string(output_video_path, "",
|
||||||
|
"Full path of where to save result (.mp4 only). "
|
||||||
|
"If not provided, show result in a window.");
|
||||||
|
|
||||||
|
::mediapipe::Status RunMPPGraph() {
|
||||||
|
std::string calculator_graph_config_contents;
|
||||||
|
MP_RETURN_IF_ERROR(mediapipe::file::GetContents(
|
||||||
|
FLAGS_calculator_graph_config_file, &calculator_graph_config_contents));
|
||||||
|
LOG(INFO) << "Get calculator graph config contents: "
|
||||||
|
<< calculator_graph_config_contents;
|
||||||
|
mediapipe::CalculatorGraphConfig config =
|
||||||
|
mediapipe::ParseTextProtoOrDie<mediapipe::CalculatorGraphConfig>(
|
||||||
|
calculator_graph_config_contents);
|
||||||
|
|
||||||
|
LOG(INFO) << "Initialize the calculator graph.";
|
||||||
|
mediapipe::CalculatorGraph graph;
|
||||||
|
MP_RETURN_IF_ERROR(graph.Initialize(config));
|
||||||
|
|
||||||
|
LOG(INFO) << "Initialize the camera or load the video.";
|
||||||
|
cv::VideoCapture capture;
|
||||||
|
const bool load_video = !FLAGS_input_video_path.empty();
|
||||||
|
if (load_video) {
|
||||||
|
capture.open(FLAGS_input_video_path);
|
||||||
|
} else {
|
||||||
|
capture.open(0);
|
||||||
|
}
|
||||||
|
RET_CHECK(capture.isOpened());
|
||||||
|
|
||||||
|
cv::VideoWriter writer;
|
||||||
|
const bool save_video = !FLAGS_output_video_path.empty();
|
||||||
|
if (save_video) {
|
||||||
|
LOG(INFO) << "Prepare video writer.";
|
||||||
|
cv::Mat test_frame;
|
||||||
|
capture.read(test_frame); // Consume first frame.
|
||||||
|
capture.set(cv::CAP_PROP_POS_AVI_RATIO, 0); // Rewind to beginning.
|
||||||
|
writer.open(FLAGS_output_video_path,
|
||||||
|
mediapipe::fourcc('a', 'v', 'c', '1'), // .mp4
|
||||||
|
capture.get(cv::CAP_PROP_FPS), test_frame.size());
|
||||||
|
RET_CHECK(writer.isOpened());
|
||||||
|
} else {
|
||||||
|
cv::namedWindow(kWindowName, /*flags=WINDOW_AUTOSIZE*/ 1);
|
||||||
|
capture.set(cv::CAP_PROP_FRAME_WIDTH, 640);
|
||||||
|
capture.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
|
||||||
|
capture.set(cv::CAP_PROP_AUTOFOCUS, 0);
|
||||||
|
capture.set(cv::CAP_PROP_FOCUS, 1);
|
||||||
|
capture.set(cv::CAP_PROP_FPS, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Start running the calculator graph.";
|
||||||
|
ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller poller,
|
||||||
|
graph.AddOutputStreamPoller(kOutputStream));
|
||||||
|
MP_RETURN_IF_ERROR(graph.StartRun({}));
|
||||||
|
|
||||||
|
LOG(INFO) << "Start grabbing and processing frames.";
|
||||||
|
size_t frame_timestamp = 0;
|
||||||
|
bool grab_frames = true;
|
||||||
|
while (grab_frames) {
|
||||||
|
// Capture opencv camera or video frame.
|
||||||
|
cv::Mat camera_frame_raw;
|
||||||
|
capture >> camera_frame_raw;
|
||||||
|
if (camera_frame_raw.empty()) break; // End of video.
|
||||||
|
cv::Mat camera_frame;
|
||||||
|
cv::cvtColor(camera_frame_raw, camera_frame, cv::COLOR_BGR2RGB);
|
||||||
|
if (!load_video) {
|
||||||
|
cv::flip(camera_frame, camera_frame, /*flipcode=HORIZONTAL*/ 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap Mat into an ImageFrame.
|
||||||
|
auto input_frame = absl::make_unique<mediapipe::ImageFrame>(
|
||||||
|
mediapipe::ImageFormat::SRGB, camera_frame.cols, camera_frame.rows,
|
||||||
|
mediapipe::ImageFrame::kDefaultAlignmentBoundary);
|
||||||
|
cv::Mat input_frame_mat = mediapipe::formats::MatView(input_frame.get());
|
||||||
|
camera_frame.copyTo(input_frame_mat);
|
||||||
|
|
||||||
|
// Send image packet into the graph.
|
||||||
|
MP_RETURN_IF_ERROR(graph.AddPacketToInputStream(
|
||||||
|
kInputStream, mediapipe::Adopt(input_frame.release())
|
||||||
|
.At(mediapipe::Timestamp(frame_timestamp++))));
|
||||||
|
|
||||||
|
// Get the graph result packet, or stop if that fails.
|
||||||
|
mediapipe::Packet packet;
|
||||||
|
if (!poller.Next(&packet)) break;
|
||||||
|
auto& output_frame = packet.Get<mediapipe::ImageFrame>();
|
||||||
|
|
||||||
|
// Convert back to opencv for display or saving.
|
||||||
|
cv::Mat output_frame_mat = mediapipe::formats::MatView(&output_frame);
|
||||||
|
cv::cvtColor(output_frame_mat, output_frame_mat, cv::COLOR_RGB2BGR);
|
||||||
|
if (save_video) {
|
||||||
|
writer.write(output_frame_mat);
|
||||||
|
} else {
|
||||||
|
cv::imshow(kWindowName, output_frame_mat);
|
||||||
|
// Press any key to exit.
|
||||||
|
const int pressed_key = cv::waitKey(5);
|
||||||
|
if (pressed_key >= 0 && pressed_key != 255) grab_frames = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Shutting down.";
|
||||||
|
if (writer.isOpened()) writer.release();
|
||||||
|
MP_RETURN_IF_ERROR(graph.CloseInputStream(kInputStream));
|
||||||
|
return graph.WaitUntilDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||||
|
::mediapipe::Status run_status = RunMPPGraph();
|
||||||
|
if (!run_status.ok()) {
|
||||||
|
LOG(ERROR) << "Failed to run the graph: " << run_status.message();
|
||||||
|
} else {
|
||||||
|
LOG(INFO) << "Success!";
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
# MediaPipe graph that performs face detection with TensorFlow Lite on CPU.
|
||||||
|
# Used in the examples in
|
||||||
|
# mediapipe/examples/coral:face_detection_cpu.
|
||||||
|
|
||||||
|
# Images on GPU coming into and out of the graph.
|
||||||
|
input_stream: "input_video"
|
||||||
|
output_stream: "output_video"
|
||||||
|
|
||||||
|
# Throttles the images flowing downstream for flow control. It passes through
|
||||||
|
# the very first incoming image unaltered, and waits for
|
||||||
|
# TfLiteTensorsToDetectionsCalculator downstream in the graph to finish
|
||||||
|
# generating the corresponding detections before it passes through another
|
||||||
|
# image. All images that come in while waiting are dropped, limiting the number
|
||||||
|
# of in-flight images between this calculator and
|
||||||
|
# TfLiteTensorsToDetectionsCalculator to 1. This prevents the nodes in between
|
||||||
|
# from queuing up incoming images and data excessively, which leads to increased
|
||||||
|
# latency and memory usage, unwanted in real-time mobile applications. It also
|
||||||
|
# eliminates unnecessarily computation, e.g., a transformed image produced by
|
||||||
|
# ImageTransformationCalculator may get dropped downstream if the subsequent
|
||||||
|
# TfLiteConverterCalculator or TfLiteInferenceCalculator is still busy
|
||||||
|
# processing previous inputs.
|
||||||
|
node {
|
||||||
|
calculator: "FlowLimiterCalculator"
|
||||||
|
input_stream: "input_video"
|
||||||
|
input_stream: "FINISHED:detections"
|
||||||
|
input_stream_info: {
|
||||||
|
tag_index: "FINISHED"
|
||||||
|
back_edge: true
|
||||||
|
}
|
||||||
|
output_stream: "throttled_input_video"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Transforms the input image on CPU to a 128x128 image. To scale the input
|
||||||
|
# image, the scale_mode option is set to FIT to preserve the aspect ratio,
|
||||||
|
# resulting in potential letterboxing in the transformed image.
|
||||||
|
node: {
|
||||||
|
calculator: "ImageTransformationCalculator"
|
||||||
|
input_stream: "IMAGE:throttled_input_video"
|
||||||
|
output_stream: "IMAGE:transformed_input_video_cpu"
|
||||||
|
output_stream: "LETTERBOX_PADDING:letterbox_padding"
|
||||||
|
options: {
|
||||||
|
[mediapipe.ImageTransformationCalculatorOptions.ext] {
|
||||||
|
output_width: 128
|
||||||
|
output_height: 128
|
||||||
|
scale_mode: FIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Converts the transformed input image on CPU into an image tensor stored as a
|
||||||
|
# TfLiteTensor.
|
||||||
|
node {
|
||||||
|
calculator: "TfLiteConverterCalculator"
|
||||||
|
input_stream: "IMAGE:transformed_input_video_cpu"
|
||||||
|
output_stream: "TENSORS:image_tensor"
|
||||||
|
options: {
|
||||||
|
[mediapipe.TfLiteConverterCalculatorOptions.ext] {
|
||||||
|
use_quantized_tensors: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Runs a TensorFlow Lite model on CPU that takes an image tensor and outputs a
|
||||||
|
# vector of tensors representing, for instance, detection boxes/keypoints and
|
||||||
|
# scores.
|
||||||
|
node {
|
||||||
|
calculator: "TfLiteInferenceCalculator"
|
||||||
|
input_stream: "TENSORS:image_tensor"
|
||||||
|
output_stream: "TENSORS:detection_tensors"
|
||||||
|
options: {
|
||||||
|
[mediapipe.TfLiteInferenceCalculatorOptions.ext] {
|
||||||
|
model_path: "mediapipe/examples/coral/models/face-detector-quantized_edgetpu.tflite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generates a single side packet containing a vector of SSD anchors based on
|
||||||
|
# the specification in the options.
|
||||||
|
node {
|
||||||
|
calculator: "SsdAnchorsCalculator"
|
||||||
|
output_side_packet: "anchors"
|
||||||
|
options: {
|
||||||
|
[mediapipe.SsdAnchorsCalculatorOptions.ext] {
|
||||||
|
num_layers: 4
|
||||||
|
min_scale: 0.1484375
|
||||||
|
max_scale: 0.75
|
||||||
|
input_size_height: 128
|
||||||
|
input_size_width: 128
|
||||||
|
anchor_offset_x: 0.5
|
||||||
|
anchor_offset_y: 0.5
|
||||||
|
strides: 8
|
||||||
|
strides: 16
|
||||||
|
strides: 16
|
||||||
|
strides: 16
|
||||||
|
aspect_ratios: 1.0
|
||||||
|
fixed_anchor_size: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Decodes the detection tensors generated by the TensorFlow Lite model, based on
|
||||||
|
# the SSD anchors and the specification in the options, into a vector of
|
||||||
|
# detections. Each detection describes a detected object.
|
||||||
|
node {
|
||||||
|
calculator: "TfLiteTensorsToDetectionsCalculator"
|
||||||
|
input_stream: "TENSORS:detection_tensors"
|
||||||
|
input_side_packet: "ANCHORS:anchors"
|
||||||
|
output_stream: "DETECTIONS:detections"
|
||||||
|
options: {
|
||||||
|
[mediapipe.TfLiteTensorsToDetectionsCalculatorOptions.ext] {
|
||||||
|
num_classes: 1
|
||||||
|
num_boxes: 896
|
||||||
|
num_coords: 16
|
||||||
|
box_coord_offset: 0
|
||||||
|
keypoint_coord_offset: 4
|
||||||
|
num_keypoints: 6
|
||||||
|
num_values_per_keypoint: 2
|
||||||
|
sigmoid_score: true
|
||||||
|
score_clipping_thresh: 100.0
|
||||||
|
reverse_output_order: true
|
||||||
|
x_scale: 128.0
|
||||||
|
y_scale: 128.0
|
||||||
|
h_scale: 128.0
|
||||||
|
w_scale: 128.0
|
||||||
|
min_score_thresh: 0.75
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Performs non-max suppression to remove excessive detections.
|
||||||
|
node {
|
||||||
|
calculator: "NonMaxSuppressionCalculator"
|
||||||
|
input_stream: "detections"
|
||||||
|
output_stream: "filtered_detections"
|
||||||
|
options: {
|
||||||
|
[mediapipe.NonMaxSuppressionCalculatorOptions.ext] {
|
||||||
|
min_suppression_threshold: 0.3
|
||||||
|
overlap_type: INTERSECTION_OVER_UNION
|
||||||
|
algorithm: WEIGHTED
|
||||||
|
return_empty_detections: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Maps detection label IDs to the corresponding label text ("Face"). The label
|
||||||
|
# map is provided in the label_map_path option.
|
||||||
|
node {
|
||||||
|
calculator: "DetectionLabelIdToTextCalculator"
|
||||||
|
input_stream: "filtered_detections"
|
||||||
|
output_stream: "labeled_detections"
|
||||||
|
options: {
|
||||||
|
[mediapipe.DetectionLabelIdToTextCalculatorOptions.ext] {
|
||||||
|
label_map_path: "mediapipe/models/face_detection_front_labelmap.txt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Adjusts detection locations (already normalized to [0.f, 1.f]) on the
|
||||||
|
# letterboxed image (after image transformation with the FIT scale mode) to the
|
||||||
|
# corresponding locations on the same image with the letterbox removed (the
|
||||||
|
# input image to the graph before image transformation).
|
||||||
|
node {
|
||||||
|
calculator: "DetectionLetterboxRemovalCalculator"
|
||||||
|
input_stream: "DETECTIONS:labeled_detections"
|
||||||
|
input_stream: "LETTERBOX_PADDING:letterbox_padding"
|
||||||
|
output_stream: "DETECTIONS:output_detections"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Converts the detections to drawing primitives for annotation overlay.
|
||||||
|
node {
|
||||||
|
calculator: "DetectionsToRenderDataCalculator"
|
||||||
|
input_stream: "DETECTIONS:output_detections"
|
||||||
|
output_stream: "RENDER_DATA:render_data"
|
||||||
|
options: {
|
||||||
|
[mediapipe.DetectionsToRenderDataCalculatorOptions.ext] {
|
||||||
|
thickness: 4.0
|
||||||
|
color { r: 255 g: 0 b: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Draws annotations and overlays them on top of the input images.
|
||||||
|
node {
|
||||||
|
calculator: "AnnotationOverlayCalculator"
|
||||||
|
input_stream: "INPUT_FRAME:throttled_input_video"
|
||||||
|
input_stream: "render_data"
|
||||||
|
output_stream: "OUTPUT_FRAME:output_video"
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
# MediaPipe graph that performs object detection with TensorFlow Lite on CPU.
|
||||||
|
# Used in the examples in
|
||||||
|
# mediapipie/examples/coral:object_detection_cpu.
|
||||||
|
|
||||||
|
# Images on CPU coming into and out of the graph.
|
||||||
|
input_stream: "input_video"
|
||||||
|
output_stream: "output_video"
|
||||||
|
|
||||||
|
# Throttles the images flowing downstream for flow control. It passes through
|
||||||
|
# the very first incoming image unaltered, and waits for
|
||||||
|
# TfLiteTensorsToDetectionsCalculator downstream in the graph to finish
|
||||||
|
# generating the corresponding detections before it passes through another
|
||||||
|
# image. All images that come in while waiting are dropped, limiting the number
|
||||||
|
# of in-flight images between this calculator and
|
||||||
|
# TfLiteTensorsToDetectionsCalculator to 1. This prevents the nodes in between
|
||||||
|
# from queuing up incoming images and data excessively, which leads to increased
|
||||||
|
# latency and memory usage, unwanted in real-time mobile applications. It also
|
||||||
|
# eliminates unnecessarily computation, e.g., a transformed image produced by
|
||||||
|
# ImageTransformationCalculator may get dropped downstream if the subsequent
|
||||||
|
# TfLiteConverterCalculator or TfLiteInferenceCalculator is still busy
|
||||||
|
# processing previous inputs.
|
||||||
|
node {
|
||||||
|
calculator: "FlowLimiterCalculator"
|
||||||
|
input_stream: "input_video"
|
||||||
|
input_stream: "FINISHED:detections"
|
||||||
|
input_stream_info: {
|
||||||
|
tag_index: "FINISHED"
|
||||||
|
back_edge: true
|
||||||
|
}
|
||||||
|
output_stream: "throttled_input_video"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Transforms the input image on CPU to a 320x320 image. To scale the image, by
|
||||||
|
# default it uses the STRETCH scale mode that maps the entire input image to the
|
||||||
|
# entire transformed image. As a result, image aspect ratio may be changed and
|
||||||
|
# objects in the image may be deformed (stretched or squeezed), but the object
|
||||||
|
# detection model used in this graph is agnostic to that deformation.
|
||||||
|
node: {
|
||||||
|
calculator: "ImageTransformationCalculator"
|
||||||
|
input_stream: "IMAGE:throttled_input_video"
|
||||||
|
output_stream: "IMAGE:transformed_input_video"
|
||||||
|
options: {
|
||||||
|
[mediapipe.ImageTransformationCalculatorOptions.ext] {
|
||||||
|
output_width: 300
|
||||||
|
output_height: 300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Converts the transformed input image on CPU into an image tensor stored as a
|
||||||
|
# TfLiteTensor.
|
||||||
|
node {
|
||||||
|
calculator: "TfLiteConverterCalculator"
|
||||||
|
input_stream: "IMAGE:transformed_input_video"
|
||||||
|
output_stream: "TENSORS:image_tensor"
|
||||||
|
options: {
|
||||||
|
[mediapipe.TfLiteConverterCalculatorOptions.ext] {
|
||||||
|
use_quantized_tensors: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Runs a TensorFlow Lite model on CPU that takes an image tensor and outputs a
|
||||||
|
# vector of tensors representing, for instance, detection boxes/keypoints and
|
||||||
|
# scores.
|
||||||
|
node {
|
||||||
|
calculator: "TfLiteInferenceCalculator"
|
||||||
|
input_stream: "TENSORS:image_tensor"
|
||||||
|
output_stream: "TENSORS:detection_tensors"
|
||||||
|
options: {
|
||||||
|
[mediapipe.TfLiteInferenceCalculatorOptions.ext] {
|
||||||
|
model_path: "mediapipe/examples/coral/models/object-detector-quantized_edgetpu.tflite"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generates a single side packet containing a vector of SSD anchors based on
|
||||||
|
# the specification in the options.
|
||||||
|
node {
|
||||||
|
calculator: "SsdAnchorsCalculator"
|
||||||
|
output_side_packet: "anchors"
|
||||||
|
options: {
|
||||||
|
[mediapipe.SsdAnchorsCalculatorOptions.ext] {
|
||||||
|
num_layers: 6
|
||||||
|
min_scale: 0.2
|
||||||
|
max_scale: 0.95
|
||||||
|
input_size_height: 300
|
||||||
|
input_size_width: 300
|
||||||
|
anchor_offset_x: 0.5
|
||||||
|
anchor_offset_y: 0.5
|
||||||
|
strides: 16
|
||||||
|
strides: 32
|
||||||
|
strides: 64
|
||||||
|
strides: 128
|
||||||
|
strides: 256
|
||||||
|
strides: 512
|
||||||
|
aspect_ratios: 1.0
|
||||||
|
aspect_ratios: 2.0
|
||||||
|
aspect_ratios: 0.5
|
||||||
|
aspect_ratios: 3.0
|
||||||
|
aspect_ratios: 0.3333
|
||||||
|
reduce_boxes_in_lowest_layer: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Decodes the detection tensors generated by the TensorFlow Lite model, based on
|
||||||
|
# the SSD anchors and the specification in the options, into a vector of
|
||||||
|
# detections. Each detection describes a detected object.
|
||||||
|
node {
|
||||||
|
calculator: "TfLiteTensorsToDetectionsCalculator"
|
||||||
|
input_stream: "TENSORS:detection_tensors"
|
||||||
|
input_side_packet: "ANCHORS:anchors"
|
||||||
|
output_stream: "DETECTIONS:detections"
|
||||||
|
options: {
|
||||||
|
[mediapipe.TfLiteTensorsToDetectionsCalculatorOptions.ext] {
|
||||||
|
num_classes: 91
|
||||||
|
num_boxes: 2034
|
||||||
|
num_coords: 4
|
||||||
|
ignore_classes: 0
|
||||||
|
sigmoid_score: true
|
||||||
|
apply_exponential_on_box_size: true
|
||||||
|
x_scale: 10.0
|
||||||
|
y_scale: 10.0
|
||||||
|
h_scale: 5.0
|
||||||
|
w_scale: 5.0
|
||||||
|
min_score_thresh: 0.6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Performs non-max suppression to remove excessive detections.
|
||||||
|
node {
|
||||||
|
calculator: "NonMaxSuppressionCalculator"
|
||||||
|
input_stream: "detections"
|
||||||
|
output_stream: "filtered_detections"
|
||||||
|
options: {
|
||||||
|
[mediapipe.NonMaxSuppressionCalculatorOptions.ext] {
|
||||||
|
min_suppression_threshold: 0.4
|
||||||
|
max_num_detections: 3
|
||||||
|
overlap_type: INTERSECTION_OVER_UNION
|
||||||
|
return_empty_detections: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Maps detection label IDs to the corresponding label text. The label map is
|
||||||
|
# provided in the label_map_path option.
|
||||||
|
node {
|
||||||
|
calculator: "DetectionLabelIdToTextCalculator"
|
||||||
|
input_stream: "filtered_detections"
|
||||||
|
output_stream: "output_detections"
|
||||||
|
options: {
|
||||||
|
[mediapipe.DetectionLabelIdToTextCalculatorOptions.ext] {
|
||||||
|
label_map_path: "mediapipe/examples/coral/models/object_detection_labelmap.txt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Converts the detections to drawing primitives for annotation overlay.
|
||||||
|
node {
|
||||||
|
calculator: "DetectionsToRenderDataCalculator"
|
||||||
|
input_stream: "DETECTIONS:output_detections"
|
||||||
|
output_stream: "RENDER_DATA:render_data"
|
||||||
|
options: {
|
||||||
|
[mediapipe.DetectionsToRenderDataCalculatorOptions.ext] {
|
||||||
|
thickness: 4.0
|
||||||
|
color { r: 255 g: 0 b: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Draws annotations and overlays them on top of the input images.
|
||||||
|
node {
|
||||||
|
calculator: "AnnotationOverlayCalculator"
|
||||||
|
input_stream: "INPUT_FRAME:throttled_input_video"
|
||||||
|
input_stream: "render_data"
|
||||||
|
output_stream: "OUTPUT_FRAME:output_video"
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,90 @@
|
||||||
|
person
|
||||||
|
bicycle
|
||||||
|
car
|
||||||
|
motorcycle
|
||||||
|
airplane
|
||||||
|
bus
|
||||||
|
train
|
||||||
|
truck
|
||||||
|
boat
|
||||||
|
traffic light
|
||||||
|
fire hydrant
|
||||||
|
???
|
||||||
|
stop sign
|
||||||
|
parking meter
|
||||||
|
bench
|
||||||
|
bird
|
||||||
|
cat
|
||||||
|
dog
|
||||||
|
horse
|
||||||
|
sheep
|
||||||
|
cow
|
||||||
|
elephant
|
||||||
|
bear
|
||||||
|
zebra
|
||||||
|
giraffe
|
||||||
|
???
|
||||||
|
backpack
|
||||||
|
umbrella
|
||||||
|
???
|
||||||
|
???
|
||||||
|
handbag
|
||||||
|
tie
|
||||||
|
suitcase
|
||||||
|
frisbee
|
||||||
|
skis
|
||||||
|
snowboard
|
||||||
|
sports ball
|
||||||
|
kite
|
||||||
|
baseball bat
|
||||||
|
baseball glove
|
||||||
|
skateboard
|
||||||
|
surfboard
|
||||||
|
tennis racket
|
||||||
|
bottle
|
||||||
|
???
|
||||||
|
wine glass
|
||||||
|
cup
|
||||||
|
fork
|
||||||
|
knife
|
||||||
|
spoon
|
||||||
|
bowl
|
||||||
|
banana
|
||||||
|
apple
|
||||||
|
sandwich
|
||||||
|
orange
|
||||||
|
broccoli
|
||||||
|
carrot
|
||||||
|
hot dog
|
||||||
|
pizza
|
||||||
|
donut
|
||||||
|
cake
|
||||||
|
chair
|
||||||
|
couch
|
||||||
|
potted plant
|
||||||
|
bed
|
||||||
|
???
|
||||||
|
dining table
|
||||||
|
???
|
||||||
|
???
|
||||||
|
toilet
|
||||||
|
???
|
||||||
|
tv
|
||||||
|
laptop
|
||||||
|
mouse
|
||||||
|
remote
|
||||||
|
keyboard
|
||||||
|
cell phone
|
||||||
|
microwave
|
||||||
|
oven
|
||||||
|
toaster
|
||||||
|
sink
|
||||||
|
refrigerator
|
||||||
|
???
|
||||||
|
book
|
||||||
|
clock
|
||||||
|
vase
|
||||||
|
scissors
|
||||||
|
teddy bear
|
||||||
|
hair drier
|
||||||
|
toothbrush
|
21
mediapipe/examples/coral/setup.sh
Executable file
21
mediapipe/examples/coral/setup.sh
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -v
|
||||||
|
|
||||||
|
echo 'Please run this from root level mediapipe directory! \n Ex:'
|
||||||
|
echo ' sh mediapipe/examples/coral/setup.sh '
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
mkdir opencv32_arm64_libs
|
||||||
|
|
||||||
|
cp mediapipe/examples/coral/update_sources.sh update_sources.sh
|
||||||
|
chmod +x update_sources.sh
|
||||||
|
|
||||||
|
mv Dockerfile Dockerfile.orig
|
||||||
|
cp mediapipe/examples/coral/Dockerfile Dockerfile
|
||||||
|
|
||||||
|
cp WORKSPACE WORKSPACE.orig
|
||||||
|
cp mediapipe/examples/coral/WORKSPACE WORKSPACE
|
||||||
|
|
11
mediapipe/examples/coral/update_sources.sh
Executable file
11
mediapipe/examples/coral/update_sources.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# To run in the Coral Docker environment.
|
||||||
|
|
||||||
|
. /etc/os-release
|
||||||
|
|
||||||
|
sed -i "s/deb\ /deb \[arch=amd64\]\ /g" /etc/apt/sources.list
|
||||||
|
|
||||||
|
echo "deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports ${UBUNTU_CODENAME} main universe" >> /etc/apt/sources.list
|
||||||
|
echo "deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports ${UBUNTU_CODENAME}-updates main universe" >> /etc/apt/sources.list
|
||||||
|
echo "deb [arch=arm64,armhf] http://ports.ubuntu.com/ubuntu-ports ${UBUNTU_CODENAME}-security main universe" >> /etc/apt/sources.list
|
|
@ -9,7 +9,9 @@ bazel build -c opt mediapipe/examples/desktop/hello_world:hello_world
|
||||||
and then run it using:
|
and then run it using:
|
||||||
|
|
||||||
```
|
```
|
||||||
bazel-bin/mediapipe/examples/desktop/hello_world/hello_world --logtostderr
|
export GLOG_logtostderr=1
|
||||||
|
|
||||||
|
bazel-bin/mediapipe/examples/desktop/hello_world/hello_world
|
||||||
```
|
```
|
||||||
|
|
||||||
**TFlite Object Detection**
|
**TFlite Object Detection**
|
||||||
|
@ -23,10 +25,11 @@ bazel build -c opt mediapipe/examples/desktop/object_detection:object_detection_
|
||||||
and run it using:
|
and run it using:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
export GLOG_logtostderr=1
|
||||||
|
|
||||||
bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tflite \
|
bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tflite \
|
||||||
--calculator_graph_config_file=mediapipe/graphs/object_detection/object_detection_desktop_tflite_graph.pbtxt \
|
--calculator_graph_config_file=mediapipe/graphs/object_detection/object_detection_desktop_tflite_graph.pbtxt \
|
||||||
--input_side_packets=input_video_path=/path/to/input/file,output_video_path=/path/to/output/file \
|
--input_side_packets=input_video_path=/path/to/input/file,output_video_path=/path/to/output/file
|
||||||
--alsologtostderr
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**TensorFlow Object Detection**
|
**TensorFlow Object Detection**
|
||||||
|
@ -34,6 +37,8 @@ bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tflite \
|
||||||
To build the object detection demo using a TensorFlow model on desktop, use:
|
To build the object detection demo using a TensorFlow model on desktop, use:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
export GLOG_logtostderr=1
|
||||||
|
|
||||||
bazel build -c opt mediapipe/examples/desktop/object_detection:object_detection_tensorflow \
|
bazel build -c opt mediapipe/examples/desktop/object_detection:object_detection_tensorflow \
|
||||||
--define MEDIAPIPE_DISABLE_GPU=1
|
--define MEDIAPIPE_DISABLE_GPU=1
|
||||||
```
|
```
|
||||||
|
@ -41,8 +46,68 @@ bazel build -c opt mediapipe/examples/desktop/object_detection:object_detection_
|
||||||
and run it using:
|
and run it using:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
export GLOG_logtostderr=1
|
||||||
|
|
||||||
bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tensorflow \
|
bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tensorflow \
|
||||||
--calculator_graph_config_file=mediapipe/graphs/object_detection/object_detection_desktop_tensorflow_graph.pbtxt \
|
--calculator_graph_config_file=mediapipe/graphs/object_detection/object_detection_desktop_tensorflow_graph.pbtxt \
|
||||||
--input_side_packets=input_video_path=/path/to/input/file,output_video_path=/path/to/output/file
|
--input_side_packets=input_video_path=/path/to/input/file,output_video_path=/path/to/output/file
|
||||||
--alsologtostderr
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**TFlite Hand Detection**
|
||||||
|
|
||||||
|
To build the hand detection demo using a TFLite model on desktop, use:
|
||||||
|
|
||||||
|
```
|
||||||
|
bazel build -c opt mediapipe/examples/desktop/hand_tracking:hand_tracking_tflite --define MEDIAPIPE_DISABLE_GPU=1
|
||||||
|
```
|
||||||
|
|
||||||
|
and run it using:
|
||||||
|
|
||||||
|
```
|
||||||
|
export GLOG_logtostderr=1
|
||||||
|
|
||||||
|
bazel-bin/mediapipe/examples/desktop/hand_tracking/hand_tracking_tflite \
|
||||||
|
--calculator_graph_config_file=mediapipe/graphs/hand_tracking/hand_detection_desktop.pbtxt \
|
||||||
|
--input_side_packets=input_video_path=/path/to/input/file,output_video_path=/path/to/output/file
|
||||||
|
```
|
||||||
|
|
||||||
|
**TFlite Hand Tracking**
|
||||||
|
|
||||||
|
To build the hand tracking demo using a TFLite model on desktop, use:
|
||||||
|
|
||||||
|
```
|
||||||
|
bazel build -c opt mediapipe/examples/desktop/hand_tracking:hand_tracking_tflite --define MEDIAPIPE_DISABLE_GPU=1
|
||||||
|
```
|
||||||
|
|
||||||
|
and run it using:
|
||||||
|
|
||||||
|
```
|
||||||
|
export GLOG_logtostderr=1
|
||||||
|
|
||||||
|
bazel-bin/mediapipe/examples/desktop/hand_tracking/hand_tracking_tflite \
|
||||||
|
--calculator_graph_config_file=mediapipe/graphs/hand_tracking/hand_tracking_desktop.pbtxt \
|
||||||
|
--input_side_packets=input_video_path=/path/to/input/file,output_video_path=/path/to/output/file
|
||||||
|
```
|
||||||
|
|
||||||
|
**TFlite Multi-Hand Tracking**
|
||||||
|
|
||||||
|
To build the multi-hand tracking demo using a TFLite model on desktop, use:
|
||||||
|
|
||||||
|
```
|
||||||
|
bazel build -c opt mediapipe/examples/desktop/multi_hand_tracking:multi_hand_tracking_tflite --define MEDIAPIPE_DISABLE_GPU=1
|
||||||
|
```
|
||||||
|
|
||||||
|
and run it using:
|
||||||
|
|
||||||
|
```
|
||||||
|
export GLOG_logtostderr=1
|
||||||
|
|
||||||
|
bazel-bin/mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_tflite \
|
||||||
|
--calculator_graph_config_file=mediapipe/graphs/hand_tracking/multi_hand_tracking_desktop.pbtxt \
|
||||||
|
--input_side_packets=input_video_path=/path/to/input/file,output_video_path=/path/to/output/file
|
||||||
|
```
|
||||||
|
|
||||||
|
To change the number of hands to `x` in this application, change:
|
||||||
|
|
||||||
|
1. `min_size:x` in `CollectionHasMinSizeCalculatorOptions` in `mediapipe/graphs/hand_tracking/multi_hand_tracking_desktop.pbtxt`.
|
||||||
|
2. `max_vec_size:x` in `ClipVectorSizeCalculatorOptions` in `mediapipe/examples/dekstop/hand_tracking/subgraphs/multi_hand_detection_cpu.pbtxt`.
|
||||||
|
|
|
@ -76,6 +76,11 @@ DEFINE_string(output_video_path, "",
|
||||||
RET_CHECK(writer.isOpened());
|
RET_CHECK(writer.isOpened());
|
||||||
} else {
|
} else {
|
||||||
cv::namedWindow(kWindowName, /*flags=WINDOW_AUTOSIZE*/ 1);
|
cv::namedWindow(kWindowName, /*flags=WINDOW_AUTOSIZE*/ 1);
|
||||||
|
#if (CV_MAJOR_VERSION >= 3) && (CV_MINOR_VERSION >= 2)
|
||||||
|
capture.set(cv::CAP_PROP_FRAME_WIDTH, 640);
|
||||||
|
capture.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
|
||||||
|
capture.set(cv::CAP_PROP_FPS, 30);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(INFO) << "Start running the calculator graph.";
|
LOG(INFO) << "Start running the calculator graph.";
|
||||||
|
|
|
@ -86,6 +86,11 @@ DEFINE_string(output_video_path, "",
|
||||||
RET_CHECK(writer.isOpened());
|
RET_CHECK(writer.isOpened());
|
||||||
} else {
|
} else {
|
||||||
cv::namedWindow(kWindowName, /*flags=WINDOW_AUTOSIZE*/ 1);
|
cv::namedWindow(kWindowName, /*flags=WINDOW_AUTOSIZE*/ 1);
|
||||||
|
#if (CV_MAJOR_VERSION >= 3) && (CV_MINOR_VERSION >= 2)
|
||||||
|
capture.set(cv::CAP_PROP_FRAME_WIDTH, 640);
|
||||||
|
capture.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
|
||||||
|
capture.set(cv::CAP_PROP_FPS, 30);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(INFO) << "Start running the calculator graph.";
|
LOG(INFO) << "Start running the calculator graph.";
|
||||||
|
|
|
@ -93,15 +93,15 @@ FILEPATTERN = "kinetics_700_%s_25fps_rgb_flow"
|
||||||
SPLITS = {
|
SPLITS = {
|
||||||
"train": {
|
"train": {
|
||||||
"shards": 1000,
|
"shards": 1000,
|
||||||
"examples": 541632
|
"examples": 541490
|
||||||
},
|
},
|
||||||
"validate": {
|
"validate": {
|
||||||
"shards": 100,
|
"shards": 100,
|
||||||
"examples": 34727
|
"examples": 34715
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"shards": 100,
|
"shards": 100,
|
||||||
"examples": 69347
|
"examples": 69321
|
||||||
},
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
"csv": None, # Add a CSV for your own data here.
|
"csv": None, # Add a CSV for your own data here.
|
||||||
|
|
|
@ -12,15 +12,15 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
|
||||||
|
|
||||||
MIN_IOS_VERSION = "10.0"
|
|
||||||
|
|
||||||
load(
|
load(
|
||||||
"@build_bazel_rules_apple//apple:ios.bzl",
|
"@build_bazel_rules_apple//apple:ios.bzl",
|
||||||
"ios_application",
|
"ios_application",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
|
MIN_IOS_VERSION = "10.0"
|
||||||
|
|
||||||
# To use the 3D model instead of the default 2D model, add "--define 3D=true" to the
|
# To use the 3D model instead of the default 2D model, add "--define 3D=true" to the
|
||||||
# bazel build command.
|
# bazel build command.
|
||||||
config_setting(
|
config_setting(
|
||||||
|
@ -90,6 +90,7 @@ objc_library(
|
||||||
"//mediapipe:ios_x86_64": [],
|
"//mediapipe:ios_x86_64": [],
|
||||||
"//conditions:default": [
|
"//conditions:default": [
|
||||||
"//mediapipe/graphs/hand_tracking:mobile_calculators",
|
"//mediapipe/graphs/hand_tracking:mobile_calculators",
|
||||||
|
"//mediapipe/framework/formats:landmark_cc_proto",
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,10 +18,13 @@
|
||||||
#import "mediapipe/objc/MPPCameraInputSource.h"
|
#import "mediapipe/objc/MPPCameraInputSource.h"
|
||||||
#import "mediapipe/objc/MPPLayerRenderer.h"
|
#import "mediapipe/objc/MPPLayerRenderer.h"
|
||||||
|
|
||||||
|
#include "mediapipe/framework/formats/landmark.pb.h"
|
||||||
|
|
||||||
static NSString* const kGraphName = @"hand_tracking_mobile_gpu";
|
static NSString* const kGraphName = @"hand_tracking_mobile_gpu";
|
||||||
|
|
||||||
static const char* kInputStream = "input_video";
|
static const char* kInputStream = "input_video";
|
||||||
static const char* kOutputStream = "output_video";
|
static const char* kOutputStream = "output_video";
|
||||||
|
static const char* kLandmarksOutputStream = "hand_landmarks";
|
||||||
static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
||||||
|
|
||||||
@interface ViewController () <MPPGraphDelegate, MPPInputSourceDelegate>
|
@interface ViewController () <MPPGraphDelegate, MPPInputSourceDelegate>
|
||||||
|
@ -80,6 +83,7 @@ static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
||||||
// Create MediaPipe graph with mediapipe::CalculatorGraphConfig proto object.
|
// Create MediaPipe graph with mediapipe::CalculatorGraphConfig proto object.
|
||||||
MPPGraph* newGraph = [[MPPGraph alloc] initWithGraphConfig:config];
|
MPPGraph* newGraph = [[MPPGraph alloc] initWithGraphConfig:config];
|
||||||
[newGraph addFrameOutputStream:kOutputStream outputPacketType:MPPPacketTypePixelBuffer];
|
[newGraph addFrameOutputStream:kOutputStream outputPacketType:MPPPacketTypePixelBuffer];
|
||||||
|
[newGraph addFrameOutputStream:kLandmarksOutputStream outputPacketType:MPPPacketTypeRaw];
|
||||||
return newGraph;
|
return newGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +164,25 @@ static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Receives a raw packet from the MediaPipe graph. Invoked on a MediaPipe worker thread.
|
||||||
|
- (void)mediapipeGraph:(MPPGraph*)graph
|
||||||
|
didOutputPacket:(const ::mediapipe::Packet&)packet
|
||||||
|
fromStream:(const std::string&)streamName {
|
||||||
|
if (streamName == kLandmarksOutputStream) {
|
||||||
|
if (packet.IsEmpty()) {
|
||||||
|
NSLog(@"[TS:%lld] No hand landmarks", packet.Timestamp().Value());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& landmarks = packet.Get<::mediapipe::NormalizedLandmarkList>();
|
||||||
|
NSLog(@"[TS:%lld] Number of landmarks on hand: %d", packet.Timestamp().Value(),
|
||||||
|
landmarks.landmark_size());
|
||||||
|
for (int i = 0; i < landmarks.landmark_size(); ++i) {
|
||||||
|
NSLog(@"\tLandmark[%d]: (%f, %f, %f)", i, landmarks.landmark(i).x(),
|
||||||
|
landmarks.landmark(i).y(), landmarks.landmark(i).z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - MPPInputSourceDelegate methods
|
#pragma mark - MPPInputSourceDelegate methods
|
||||||
|
|
||||||
// Must be invoked on _videoQueue.
|
// Must be invoked on _videoQueue.
|
||||||
|
|
|
@ -12,15 +12,15 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
|
||||||
|
|
||||||
MIN_IOS_VERSION = "10.0"
|
|
||||||
|
|
||||||
load(
|
load(
|
||||||
"@build_bazel_rules_apple//apple:ios.bzl",
|
"@build_bazel_rules_apple//apple:ios.bzl",
|
||||||
"ios_application",
|
"ios_application",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
|
MIN_IOS_VERSION = "10.0"
|
||||||
|
|
||||||
# To use the 3D model instead of the default 2D model, add "--define 3D=true" to the
|
# To use the 3D model instead of the default 2D model, add "--define 3D=true" to the
|
||||||
# bazel build command.
|
# bazel build command.
|
||||||
config_setting(
|
config_setting(
|
||||||
|
@ -90,6 +90,7 @@ objc_library(
|
||||||
"//mediapipe:ios_x86_64": [],
|
"//mediapipe:ios_x86_64": [],
|
||||||
"//conditions:default": [
|
"//conditions:default": [
|
||||||
"//mediapipe/graphs/hand_tracking:multi_hand_mobile_calculators",
|
"//mediapipe/graphs/hand_tracking:multi_hand_mobile_calculators",
|
||||||
|
"//mediapipe/framework/formats:landmark_cc_proto",
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,10 +18,13 @@
|
||||||
#import "mediapipe/objc/MPPCameraInputSource.h"
|
#import "mediapipe/objc/MPPCameraInputSource.h"
|
||||||
#import "mediapipe/objc/MPPLayerRenderer.h"
|
#import "mediapipe/objc/MPPLayerRenderer.h"
|
||||||
|
|
||||||
|
#include "mediapipe/framework/formats/landmark.pb.h"
|
||||||
|
|
||||||
static NSString* const kGraphName = @"multi_hand_tracking_mobile_gpu";
|
static NSString* const kGraphName = @"multi_hand_tracking_mobile_gpu";
|
||||||
|
|
||||||
static const char* kInputStream = "input_video";
|
static const char* kInputStream = "input_video";
|
||||||
static const char* kOutputStream = "output_video";
|
static const char* kOutputStream = "output_video";
|
||||||
|
static const char* kLandmarksOutputStream = "multi_hand_landmarks";
|
||||||
static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
||||||
|
|
||||||
@interface ViewController () <MPPGraphDelegate, MPPInputSourceDelegate>
|
@interface ViewController () <MPPGraphDelegate, MPPInputSourceDelegate>
|
||||||
|
@ -80,6 +83,7 @@ static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
||||||
// Create MediaPipe graph with mediapipe::CalculatorGraphConfig proto object.
|
// Create MediaPipe graph with mediapipe::CalculatorGraphConfig proto object.
|
||||||
MPPGraph* newGraph = [[MPPGraph alloc] initWithGraphConfig:config];
|
MPPGraph* newGraph = [[MPPGraph alloc] initWithGraphConfig:config];
|
||||||
[newGraph addFrameOutputStream:kOutputStream outputPacketType:MPPPacketTypePixelBuffer];
|
[newGraph addFrameOutputStream:kOutputStream outputPacketType:MPPPacketTypePixelBuffer];
|
||||||
|
[newGraph addFrameOutputStream:kLandmarksOutputStream outputPacketType:MPPPacketTypeRaw];
|
||||||
return newGraph;
|
return newGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +164,29 @@ static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Receives a raw packet from the MediaPipe graph. Invoked on a MediaPipe worker thread.
|
||||||
|
- (void)mediapipeGraph:(MPPGraph*)graph
|
||||||
|
didOutputPacket:(const ::mediapipe::Packet&)packet
|
||||||
|
fromStream:(const std::string&)streamName {
|
||||||
|
if (streamName == kLandmarksOutputStream) {
|
||||||
|
if (packet.IsEmpty()) {
|
||||||
|
NSLog(@"[TS:%lld] No hand landmarks", packet.Timestamp().Value());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& multi_hand_landmarks = packet.Get<std::vector<::mediapipe::NormalizedLandmarkList>>();
|
||||||
|
NSLog(@"[TS:%lld] Number of hand instances with landmarks: %lu", packet.Timestamp().Value(),
|
||||||
|
multi_hand_landmarks.size());
|
||||||
|
for (int hand_index = 0; hand_index < multi_hand_landmarks.size(); ++hand_index) {
|
||||||
|
const auto& landmarks = multi_hand_landmarks[hand_index];
|
||||||
|
NSLog(@"\tNumber of landmarks for hand[%d]: %d", hand_index, landmarks.landmark_size());
|
||||||
|
for (int i = 0; i < landmarks.landmark_size(); ++i) {
|
||||||
|
NSLog(@"\t\tLandmark[%d]: (%f, %f, %f)", i, landmarks.landmark(i).x(),
|
||||||
|
landmarks.landmark(i).y(), landmarks.landmark(i).z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - MPPInputSourceDelegate methods
|
#pragma mark - MPPInputSourceDelegate methods
|
||||||
|
|
||||||
// Must be invoked on _videoQueue.
|
// Must be invoked on _videoQueue.
|
||||||
|
|
|
@ -1524,7 +1524,9 @@ cc_test(
|
||||||
"//mediapipe/framework/stream_handler:fixed_size_input_stream_handler",
|
"//mediapipe/framework/stream_handler:fixed_size_input_stream_handler",
|
||||||
"//mediapipe/framework/stream_handler:immediate_input_stream_handler",
|
"//mediapipe/framework/stream_handler:immediate_input_stream_handler",
|
||||||
"//mediapipe/framework/stream_handler:mux_input_stream_handler",
|
"//mediapipe/framework/stream_handler:mux_input_stream_handler",
|
||||||
|
"//mediapipe/framework/stream_handler:sync_set_input_stream_handler",
|
||||||
"//mediapipe/framework/tool:sink",
|
"//mediapipe/framework/tool:sink",
|
||||||
|
"@com_google_absl//absl/strings",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1094,6 +1094,19 @@ bool CalculatorGraph::IsNodeThrottled(int node_id) {
|
||||||
return max_queue_size_ != -1 && !full_input_streams_[node_id].empty();
|
return max_queue_size_ != -1 && !full_input_streams_[node_id].empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if an input stream serves as a graph-output-stream.
|
||||||
|
bool IsGraphOutputStream(
|
||||||
|
InputStreamManager* stream,
|
||||||
|
const std::vector<std::shared_ptr<internal::GraphOutputStream>>&
|
||||||
|
graph_output_streams) {
|
||||||
|
for (auto& graph_output_stream : graph_output_streams) {
|
||||||
|
if (stream == graph_output_stream->input_stream()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CalculatorGraph::UnthrottleSources() {
|
bool CalculatorGraph::UnthrottleSources() {
|
||||||
// NOTE: We can be sure that this function will grow input streams enough
|
// NOTE: We can be sure that this function will grow input streams enough
|
||||||
// to unthrottle at least one source node. The current stream queue sizes
|
// to unthrottle at least one source node. The current stream queue sizes
|
||||||
|
@ -1105,25 +1118,17 @@ bool CalculatorGraph::UnthrottleSources() {
|
||||||
{
|
{
|
||||||
absl::MutexLock lock(&full_input_streams_mutex_);
|
absl::MutexLock lock(&full_input_streams_mutex_);
|
||||||
for (absl::flat_hash_set<InputStreamManager*>& s : full_input_streams_) {
|
for (absl::flat_hash_set<InputStreamManager*>& s : full_input_streams_) {
|
||||||
if (!s.empty()) {
|
for (auto& stream : s) {
|
||||||
full_streams.insert(s.begin(), s.end());
|
// The queue size of a graph output stream shouldn't change. Throttling
|
||||||
|
// should continue until the caller of the graph output stream consumes
|
||||||
|
// enough packets.
|
||||||
|
if (!IsGraphOutputStream(stream, graph_output_streams_)) {
|
||||||
|
full_streams.insert(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (InputStreamManager* stream : full_streams) {
|
for (InputStreamManager* stream : full_streams) {
|
||||||
// The queue size of a graph output stream shouldn't change. Throttling
|
|
||||||
// should continue until the caller of the graph output stream consumes
|
|
||||||
// enough packets.
|
|
||||||
bool is_graph_output_stream = false;
|
|
||||||
for (auto& graph_output_stream : graph_output_streams_) {
|
|
||||||
if (stream == graph_output_stream->input_stream()) {
|
|
||||||
is_graph_output_stream = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (is_graph_output_stream) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (Config().report_deadlock()) {
|
if (Config().report_deadlock()) {
|
||||||
RecordError(::mediapipe::UnavailableError(absl::StrCat(
|
RecordError(::mediapipe::UnavailableError(absl::StrCat(
|
||||||
"Detected a deadlock due to input throttling for: \"", stream->Name(),
|
"Detected a deadlock due to input throttling for: \"", stream->Name(),
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "absl/strings/str_replace.h"
|
||||||
#include "mediapipe/framework/calculator_context.h"
|
#include "mediapipe/framework/calculator_context.h"
|
||||||
#include "mediapipe/framework/calculator_framework.h"
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
#include "mediapipe/framework/port/canonical_errors.h"
|
#include "mediapipe/framework/port/canonical_errors.h"
|
||||||
|
@ -642,6 +643,36 @@ REGISTER_CALCULATOR(OffsetBoundCalculator);
|
||||||
|
|
||||||
// A Calculator that produces a packet for each call to Process.
|
// A Calculator that produces a packet for each call to Process.
|
||||||
class BoundToPacketCalculator : public CalculatorBase {
|
class BoundToPacketCalculator : public CalculatorBase {
|
||||||
|
public:
|
||||||
|
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
||||||
|
for (int i = 0; i < cc->Inputs().NumEntries(); ++i) {
|
||||||
|
cc->Inputs().Index(i).SetAny();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < cc->Outputs().NumEntries(); ++i) {
|
||||||
|
cc->Outputs().Index(i).Set<Timestamp>();
|
||||||
|
}
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status Open(CalculatorContext* cc) final {
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status Process(CalculatorContext* cc) final {
|
||||||
|
for (int i = 0; i < cc->Outputs().NumEntries(); ++i) {
|
||||||
|
Timestamp t = cc->Inputs().Index(i).Value().Timestamp();
|
||||||
|
cc->Outputs().Index(i).AddPacket(
|
||||||
|
mediapipe::MakePacket<Timestamp>(t).At(cc->InputTimestamp()));
|
||||||
|
}
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
REGISTER_CALCULATOR(BoundToPacketCalculator);
|
||||||
|
|
||||||
|
// A Calculator that produces packets at timestamps beyond the input timestamp.
|
||||||
|
class FuturePacketCalculator : public CalculatorBase {
|
||||||
|
static constexpr int64 kOutputFutureMicros = 3;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
||||||
cc->Inputs().Index(0).Set<int>();
|
cc->Inputs().Index(0).Set<int>();
|
||||||
|
@ -654,11 +685,14 @@ class BoundToPacketCalculator : public CalculatorBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
::mediapipe::Status Process(CalculatorContext* cc) final {
|
::mediapipe::Status Process(CalculatorContext* cc) final {
|
||||||
cc->Outputs().Index(0).AddPacket(Adopt(new int(33)));
|
const Packet& packet = cc->Inputs().Index(0).Value();
|
||||||
|
Timestamp timestamp =
|
||||||
|
Timestamp(packet.Timestamp().Value() + kOutputFutureMicros);
|
||||||
|
cc->Outputs().Index(0).AddPacket(packet.At(timestamp));
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
REGISTER_CALCULATOR(BoundToPacketCalculator);
|
REGISTER_CALCULATOR(FuturePacketCalculator);
|
||||||
|
|
||||||
// Verifies that SetOffset still propagates when Process is called and
|
// Verifies that SetOffset still propagates when Process is called and
|
||||||
// produces no output packets.
|
// produces no output packets.
|
||||||
|
@ -964,5 +998,111 @@ TEST(CalculatorGraphBoundsTest, LastPacketCheck) {
|
||||||
MP_ASSERT_OK(graph.WaitUntilDone());
|
MP_ASSERT_OK(graph.WaitUntilDone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shows that bounds are indicated for input streams without input packets.
|
||||||
|
void TestBoundsForEmptyInputs(std::string input_stream_handler) {
|
||||||
|
// FuturePacketCalculator and OffsetBoundCalculator produce future ts bounds.
|
||||||
|
// BoundToPacketCalculator reports all of its bounds, including empty inputs.
|
||||||
|
std::string config_str = R"(
|
||||||
|
input_stream: 'input'
|
||||||
|
node {
|
||||||
|
calculator: 'FuturePacketCalculator'
|
||||||
|
input_stream: 'input'
|
||||||
|
output_stream: 'futures'
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
calculator: 'OffsetBoundCalculator'
|
||||||
|
input_stream: 'futures'
|
||||||
|
output_stream: 'bounds'
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
calculator: 'BoundToPacketCalculator'
|
||||||
|
input_stream: 'input'
|
||||||
|
input_stream: 'bounds'
|
||||||
|
output_stream: 'input_ts'
|
||||||
|
output_stream: 'bounds_ts'
|
||||||
|
input_stream_handler { $input_stream_handler }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
absl::StrReplaceAll({{"$input_stream_handler", input_stream_handler}},
|
||||||
|
&config_str);
|
||||||
|
CalculatorGraphConfig config =
|
||||||
|
::mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(config_str);
|
||||||
|
CalculatorGraph graph;
|
||||||
|
std::vector<Packet> input_ts_packets;
|
||||||
|
std::vector<Packet> bounds_ts_packets;
|
||||||
|
MP_ASSERT_OK(graph.Initialize(config));
|
||||||
|
MP_ASSERT_OK(graph.ObserveOutputStream("input_ts", [&](const Packet& p) {
|
||||||
|
input_ts_packets.push_back(p);
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}));
|
||||||
|
MP_ASSERT_OK(graph.ObserveOutputStream("bounds_ts", [&](const Packet& p) {
|
||||||
|
bounds_ts_packets.push_back(p);
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}));
|
||||||
|
MP_ASSERT_OK(graph.StartRun({}));
|
||||||
|
MP_ASSERT_OK(graph.WaitUntilIdle());
|
||||||
|
|
||||||
|
// Add four packets into the graph, with timedtamps 0, 10, 20, 30.
|
||||||
|
constexpr int kNumInputs = 4;
|
||||||
|
for (int i = 0; i < kNumInputs; ++i) {
|
||||||
|
Packet p = MakePacket<int>(33).At(Timestamp(i * 10));
|
||||||
|
MP_ASSERT_OK(graph.AddPacketToInputStream("input", p));
|
||||||
|
MP_ASSERT_OK(graph.WaitUntilIdle());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Packets arrive. The input packet timestamps are: 0, 10, 20, 30.
|
||||||
|
// The corresponding empty packet timestamps are: 3, 13, 23, 33.
|
||||||
|
MP_ASSERT_OK(graph.WaitUntilIdle());
|
||||||
|
EXPECT_EQ(input_ts_packets.size(), 4);
|
||||||
|
EXPECT_EQ(bounds_ts_packets.size(), 4);
|
||||||
|
|
||||||
|
// The timestamp bounds from OffsetBoundCalculator are: 3, 13, 23, 33.
|
||||||
|
// Because the process call waits for the input packets and not for
|
||||||
|
// the empty packets, the first empty packet timestamp can be
|
||||||
|
// either Timestamp::Unstarted() or Timestamp(3).
|
||||||
|
std::vector<Timestamp> expected = {Timestamp::Unstarted(), Timestamp(3),
|
||||||
|
Timestamp(13), Timestamp(23),
|
||||||
|
Timestamp(33)};
|
||||||
|
for (int i = 0; i < bounds_ts_packets.size(); ++i) {
|
||||||
|
Timestamp ts = bounds_ts_packets[i].Get<Timestamp>();
|
||||||
|
EXPECT_GE(ts, expected[i]);
|
||||||
|
EXPECT_LE(ts, expected[i + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown the graph.
|
||||||
|
MP_ASSERT_OK(graph.CloseAllPacketSources());
|
||||||
|
MP_ASSERT_OK(graph.WaitUntilDone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows that bounds are indicated for input streams without input packets.
|
||||||
|
TEST(CalculatorGraphBoundsTest, BoundsForEmptyInputs_Immediate) {
|
||||||
|
TestBoundsForEmptyInputs(R"(
|
||||||
|
input_stream_handler: "ImmediateInputStreamHandler")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows that bounds are indicated for input streams without input packets.
|
||||||
|
TEST(CalculatorGraphBoundsTest, BoundsForEmptyInputs_Default) {
|
||||||
|
TestBoundsForEmptyInputs(R"(
|
||||||
|
input_stream_handler: "DefaultInputStreamHandler")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows that bounds are indicated for input streams without input packets.
|
||||||
|
TEST(CalculatorGraphBoundsTest, BoundsForEmptyInputs_SyncSet) {
|
||||||
|
TestBoundsForEmptyInputs(R"(
|
||||||
|
input_stream_handler: "SyncSetInputStreamHandler")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows that bounds are indicated for input streams without input packets.
|
||||||
|
TEST(CalculatorGraphBoundsTest, BoundsForEmptyInputs_SyncSets) {
|
||||||
|
TestBoundsForEmptyInputs(R"(
|
||||||
|
input_stream_handler: "SyncSetInputStreamHandler"
|
||||||
|
options {
|
||||||
|
[mediapipe.SyncSetInputStreamHandlerOptions.ext] {
|
||||||
|
sync_set { tag_index: ":0" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace mediapipe
|
} // namespace mediapipe
|
||||||
|
|
|
@ -13,10 +13,6 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
licenses(["notice"]) # Apache 2.0
|
|
||||||
|
|
||||||
exports_files(["LICENSE"])
|
|
||||||
|
|
||||||
load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library")
|
load("//mediapipe/framework/port:build_config.bzl", "mediapipe_cc_proto_library")
|
||||||
|
|
||||||
package(
|
package(
|
||||||
|
@ -24,75 +20,74 @@ package(
|
||||||
features = ["-layering_check"],
|
features = ["-layering_check"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
|
exports_files(["LICENSE"])
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "detection_proto",
|
name = "detection_proto",
|
||||||
srcs = ["detection.proto"],
|
srcs = ["detection.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
deps = ["//mediapipe/framework/formats:location_data_proto"],
|
deps = ["//mediapipe/framework/formats:location_data_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "classification_proto",
|
name = "classification_proto",
|
||||||
srcs = ["classification.proto"],
|
srcs = ["classification.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "image_format_proto",
|
name = "image_format_proto",
|
||||||
srcs = ["image_format.proto"],
|
srcs = ["image_format.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "matrix_data_proto",
|
name = "matrix_data_proto",
|
||||||
srcs = ["matrix_data.proto"],
|
srcs = ["matrix_data.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "location_data_proto",
|
name = "location_data_proto",
|
||||||
srcs = ["location_data.proto"],
|
srcs = ["location_data.proto"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
deps = ["//mediapipe/framework/formats/annotation:rasterization_proto"],
|
deps = ["//mediapipe/framework/formats/annotation:rasterization_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "time_series_header_proto",
|
name = "time_series_header_proto",
|
||||||
srcs = ["time_series_header.proto"],
|
srcs = ["time_series_header.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "detection_cc_proto",
|
name = "detection_cc_proto",
|
||||||
srcs = ["detection.proto"],
|
srcs = ["detection.proto"],
|
||||||
cc_deps = [":location_data_cc_proto"],
|
cc_deps = [":location_data_cc_proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [":detection_proto"],
|
deps = [":detection_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "classification_cc_proto",
|
name = "classification_cc_proto",
|
||||||
srcs = ["classification.proto"],
|
srcs = ["classification.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [":classification_proto"],
|
deps = [":classification_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "image_format_cc_proto",
|
name = "image_format_cc_proto",
|
||||||
srcs = ["image_format.proto"],
|
srcs = ["image_format.proto"],
|
||||||
visibility = [
|
visibility = ["//visibility:public"],
|
||||||
"//mediapipe:__subpackages__",
|
|
||||||
"//mediapipe/java/com/google/mediapipe/framework:__subpackages__",
|
|
||||||
],
|
|
||||||
deps = [":image_format_proto"],
|
deps = [":image_format_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "matrix_data_cc_proto",
|
name = "matrix_data_cc_proto",
|
||||||
srcs = ["matrix_data.proto"],
|
srcs = ["matrix_data.proto"],
|
||||||
visibility = [
|
visibility = ["//visibility:public"],
|
||||||
"//mediapipe:__subpackages__",
|
|
||||||
"//mediapipe/java/com/google/mediapipe/framework:__subpackages__",
|
|
||||||
],
|
|
||||||
deps = [":matrix_data_proto"],
|
deps = [":matrix_data_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -100,17 +95,14 @@ mediapipe_cc_proto_library(
|
||||||
name = "location_data_cc_proto",
|
name = "location_data_cc_proto",
|
||||||
srcs = ["location_data.proto"],
|
srcs = ["location_data.proto"],
|
||||||
cc_deps = ["//mediapipe/framework/formats/annotation:rasterization_cc_proto"],
|
cc_deps = ["//mediapipe/framework/formats/annotation:rasterization_cc_proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [":location_data_proto"],
|
deps = [":location_data_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "time_series_header_cc_proto",
|
name = "time_series_header_cc_proto",
|
||||||
srcs = ["time_series_header.proto"],
|
srcs = ["time_series_header.proto"],
|
||||||
visibility = [
|
visibility = ["//visibility:public"],
|
||||||
"//mediapipe:__subpackages__",
|
|
||||||
"//mediapipe/java/com/google/mediapipe/framework:__subpackages__",
|
|
||||||
],
|
|
||||||
deps = [":time_series_header_proto"],
|
deps = [":time_series_header_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -249,14 +241,14 @@ cc_test(
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "rect_proto",
|
name = "rect_proto",
|
||||||
srcs = ["rect.proto"],
|
srcs = ["rect.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "rect_cc_proto",
|
name = "rect_cc_proto",
|
||||||
srcs = ["rect.proto"],
|
srcs = ["rect.proto"],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//mediapipe:__subpackages__",
|
"//visibility:public",
|
||||||
],
|
],
|
||||||
deps = [":rect_proto"],
|
deps = [":rect_proto"],
|
||||||
)
|
)
|
||||||
|
@ -264,12 +256,26 @@ mediapipe_cc_proto_library(
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "landmark_proto",
|
name = "landmark_proto",
|
||||||
srcs = ["landmark.proto"],
|
srcs = ["landmark.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "landmark_cc_proto",
|
name = "landmark_cc_proto",
|
||||||
srcs = ["landmark.proto"],
|
srcs = ["landmark.proto"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [":landmark_proto"],
|
||||||
|
)
|
||||||
|
|
||||||
|
java_lite_proto_library(
|
||||||
|
name = "landmark_java_proto_lite",
|
||||||
|
strict_deps = 0,
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//mediapipe:__subpackages__"],
|
||||||
deps = [":landmark_proto"],
|
deps = [":landmark_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Expose the proto source files for building mediapipe AAR.
|
||||||
|
filegroup(
|
||||||
|
name = "protos_src",
|
||||||
|
srcs = glob(["*.proto"]),
|
||||||
|
visibility = ["//mediapipe:__subpackages__"],
|
||||||
|
)
|
||||||
|
|
|
@ -25,26 +25,27 @@ package(default_visibility = ["//visibility:private"])
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "locus_proto",
|
name = "locus_proto",
|
||||||
srcs = ["locus.proto"],
|
srcs = ["locus.proto"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
deps = ["//mediapipe/framework/formats/annotation:rasterization_proto"],
|
deps = ["//mediapipe/framework/formats/annotation:rasterization_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "rasterization_proto",
|
name = "rasterization_proto",
|
||||||
srcs = ["rasterization.proto"],
|
srcs = ["rasterization.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "locus_cc_proto",
|
name = "locus_cc_proto",
|
||||||
srcs = ["locus.proto"],
|
srcs = ["locus.proto"],
|
||||||
cc_deps = [":rasterization_cc_proto"],
|
cc_deps = [":rasterization_cc_proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [":locus_proto"],
|
deps = [":locus_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "rasterization_cc_proto",
|
name = "rasterization_cc_proto",
|
||||||
srcs = ["rasterization.proto"],
|
srcs = ["rasterization.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [":rasterization_proto"],
|
deps = [":rasterization_proto"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,7 +27,7 @@ message Rasterization {
|
||||||
required int32 right_x = 3;
|
required int32 right_x = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intervals are always sorted by y-corrdinate.
|
// Intervals are always sorted by y-coordinate.
|
||||||
// Therefore, a region occupies a set of scanlines ranging
|
// Therefore, a region occupies a set of scanlines ranging
|
||||||
// from interval(0).y() to interval(interval_size() - 1)).y().
|
// from interval(0).y() to interval(interval_size() - 1)).y().
|
||||||
// Note: In video, at some scanlines no interval might be present.
|
// Note: In video, at some scanlines no interval might be present.
|
||||||
|
|
|
@ -16,6 +16,9 @@ syntax = "proto2";
|
||||||
|
|
||||||
package mediapipe;
|
package mediapipe;
|
||||||
|
|
||||||
|
option java_package = "com.google.mediapipe.formats.proto";
|
||||||
|
option java_outer_classname = "LandmarkProto";
|
||||||
|
|
||||||
// A landmark that can have 1 to 3 dimensions. Use x for 1D points, (x, y) for
|
// A landmark that can have 1 to 3 dimensions. Use x for 1D points, (x, y) for
|
||||||
// 2D points and (x, y, z) for 3D points. For more dimensions, consider using
|
// 2D points and (x, y, z) for 3D points. For more dimensions, consider using
|
||||||
// matrix_data.proto.
|
// matrix_data.proto.
|
||||||
|
@ -25,6 +28,11 @@ message Landmark {
|
||||||
optional float z = 3;
|
optional float z = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Group of Landmark protos.
|
||||||
|
message LandmarkList {
|
||||||
|
repeated Landmark landmark = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// A normalized version of above Landmark proto. All coordiates should be within
|
// A normalized version of above Landmark proto. All coordiates should be within
|
||||||
// [0, 1].
|
// [0, 1].
|
||||||
message NormalizedLandmark {
|
message NormalizedLandmark {
|
||||||
|
|
|
@ -27,12 +27,13 @@ package(default_visibility = ["//visibility:private"])
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "optical_flow_field_data_proto",
|
name = "optical_flow_field_data_proto",
|
||||||
srcs = ["optical_flow_field_data.proto"],
|
srcs = ["optical_flow_field_data.proto"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "optical_flow_field_data_cc_proto",
|
name = "optical_flow_field_data_cc_proto",
|
||||||
srcs = ["optical_flow_field_data.proto"],
|
srcs = ["optical_flow_field_data.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [":optical_flow_field_data_proto"],
|
deps = [":optical_flow_field_data_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,7 +42,7 @@ cc_library(
|
||||||
srcs = ["optical_flow_field.cc"],
|
srcs = ["optical_flow_field.cc"],
|
||||||
hdrs = ["optical_flow_field.h"],
|
hdrs = ["optical_flow_field.h"],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//mediapipe:__subpackages__",
|
"//visibility:public",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
"//mediapipe/framework:type_map",
|
"//mediapipe/framework:type_map",
|
||||||
|
|
|
@ -24,12 +24,12 @@ package(default_visibility = ["//visibility:private"])
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "anchor_proto",
|
name = "anchor_proto",
|
||||||
srcs = ["anchor.proto"],
|
srcs = ["anchor.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
)
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "anchor_cc_proto",
|
name = "anchor_cc_proto",
|
||||||
srcs = ["anchor.proto"],
|
srcs = ["anchor.proto"],
|
||||||
visibility = ["//mediapipe:__subpackages__"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [":anchor_proto"],
|
deps = [":anchor_proto"],
|
||||||
)
|
)
|
||||||
|
|
|
@ -229,6 +229,11 @@ Timestamp InputStreamManager::MinTimestampOrBound(bool* is_empty) const {
|
||||||
if (is_empty) {
|
if (is_empty) {
|
||||||
*is_empty = queue_.empty();
|
*is_empty = queue_.empty();
|
||||||
}
|
}
|
||||||
|
return MinTimestampOrBoundHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timestamp InputStreamManager::MinTimestampOrBoundHelper() const
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(stream_mutex_) {
|
||||||
return queue_.empty() ? next_timestamp_bound_ : queue_.front().Timestamp();
|
return queue_.empty() ? next_timestamp_bound_ : queue_.front().Timestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +276,9 @@ Packet InputStreamManager::PopPacketAtTimestamp(Timestamp timestamp,
|
||||||
}
|
}
|
||||||
// Clear value_ if it doesn't have exactly the right timestamp.
|
// Clear value_ if it doesn't have exactly the right timestamp.
|
||||||
if (current_timestamp != timestamp) {
|
if (current_timestamp != timestamp) {
|
||||||
packet = Packet();
|
// The timestamp bound reported when no packet is sent.
|
||||||
|
Timestamp bound = MinTimestampOrBoundHelper();
|
||||||
|
packet = Packet().At(bound.PreviousAllowedInStream());
|
||||||
++(*num_packets_dropped);
|
++(*num_packets_dropped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -189,6 +189,9 @@ class InputStreamManager {
|
||||||
// Returns true if the next timestamp bound reaches Timestamp::Done().
|
// Returns true if the next timestamp bound reaches Timestamp::Done().
|
||||||
bool IsDone() const EXCLUSIVE_LOCKS_REQUIRED(stream_mutex_);
|
bool IsDone() const EXCLUSIVE_LOCKS_REQUIRED(stream_mutex_);
|
||||||
|
|
||||||
|
// Returns the smallest timestamp at which this stream might see an input.
|
||||||
|
Timestamp MinTimestampOrBoundHelper() const;
|
||||||
|
|
||||||
mutable absl::Mutex stream_mutex_;
|
mutable absl::Mutex stream_mutex_;
|
||||||
std::deque<Packet> queue_ GUARDED_BY(stream_mutex_);
|
std::deque<Packet> queue_ GUARDED_BY(stream_mutex_);
|
||||||
// The number of packets added to queue_. Used to verify a packet at
|
// The number of packets added to queue_. Used to verify a packet at
|
||||||
|
|
|
@ -107,6 +107,14 @@ const proto_ns::MessageLite& Packet::GetProtoMessageLite() const {
|
||||||
return *proto;
|
return *proto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatusOr<std::vector<const proto_ns::MessageLite*>>
|
||||||
|
Packet::GetVectorOfProtoMessageLitePtrs() {
|
||||||
|
if (holder_ == nullptr) {
|
||||||
|
return ::mediapipe::InternalError("Packet is empty.");
|
||||||
|
}
|
||||||
|
return holder_->GetVectorOfProtoMessageLite();
|
||||||
|
}
|
||||||
|
|
||||||
MEDIAPIPE_REGISTER_TYPE(::mediapipe::Packet, "::mediapipe::Packet", nullptr,
|
MEDIAPIPE_REGISTER_TYPE(::mediapipe::Packet, "::mediapipe::Packet", nullptr,
|
||||||
nullptr);
|
nullptr);
|
||||||
MEDIAPIPE_REGISTER_TYPE(::std::vector<::mediapipe::Packet>,
|
MEDIAPIPE_REGISTER_TYPE(::std::vector<::mediapipe::Packet>,
|
||||||
|
|
|
@ -163,6 +163,13 @@ class Packet {
|
||||||
// object type is protocol buffer, crashes otherwise.
|
// object type is protocol buffer, crashes otherwise.
|
||||||
const proto_ns::MessageLite& GetProtoMessageLite() const;
|
const proto_ns::MessageLite& GetProtoMessageLite() const;
|
||||||
|
|
||||||
|
// Returns a vector of pointers to MessageLite data, if the underlying
|
||||||
|
// object type is a vector of MessageLite data, returns an error otherwise.
|
||||||
|
// Note: This function is meant to be used internally within the MediaPipe
|
||||||
|
// framework only.
|
||||||
|
StatusOr<std::vector<const proto_ns::MessageLite*>>
|
||||||
|
GetVectorOfProtoMessageLitePtrs();
|
||||||
|
|
||||||
// Returns an error if the packet does not contain data of type T.
|
// Returns an error if the packet does not contain data of type T.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
::mediapipe::Status ValidateAsType() const;
|
::mediapipe::Status ValidateAsType() const;
|
||||||
|
@ -347,6 +354,12 @@ class HolderBase {
|
||||||
// underlying object is protocol buffer type, otherwise, nullptr is returned.
|
// underlying object is protocol buffer type, otherwise, nullptr is returned.
|
||||||
virtual const proto_ns::MessageLite* GetProtoMessageLite() = 0;
|
virtual const proto_ns::MessageLite* GetProtoMessageLite() = 0;
|
||||||
|
|
||||||
|
// Returns a vector<MessageLite*> for the data in the holder, if the
|
||||||
|
// underlying object is a vector of protocol buffer objects, otherwise,
|
||||||
|
// returns an error.
|
||||||
|
virtual StatusOr<std::vector<const proto_ns::MessageLite*>>
|
||||||
|
GetVectorOfProtoMessageLite() = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t type_id_;
|
size_t type_id_;
|
||||||
};
|
};
|
||||||
|
@ -364,6 +377,37 @@ const proto_ns::MessageLite* ConvertToProtoMessageLite(const T* data,
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper structs for determining if a type is an std::vector<Proto>.
|
||||||
|
template <typename Type>
|
||||||
|
struct is_proto_vector : public std::false_type {};
|
||||||
|
|
||||||
|
template <typename ItemT, typename Allocator>
|
||||||
|
struct is_proto_vector<std::vector<ItemT, Allocator>>
|
||||||
|
: public std::is_base_of<proto_ns::MessageLite, ItemT>::type {};
|
||||||
|
|
||||||
|
// Helper function to create and return a vector of pointers to proto message
|
||||||
|
// elements of the vector passed into the function.
|
||||||
|
template <typename T>
|
||||||
|
StatusOr<std::vector<const proto_ns::MessageLite*>>
|
||||||
|
ConvertToVectorOfProtoMessageLitePtrs(const T* data,
|
||||||
|
/*is_proto_vector=*/std::false_type) {
|
||||||
|
return ::mediapipe::InvalidArgumentError(absl::StrCat(
|
||||||
|
"The Packet stores \"", typeid(T).name(), "\"",
|
||||||
|
"which is not convertible to vector<proto_ns::MessageLite*>."));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
StatusOr<std::vector<const proto_ns::MessageLite*>>
|
||||||
|
ConvertToVectorOfProtoMessageLitePtrs(const T* data,
|
||||||
|
/*is_proto_vector=*/std::true_type) {
|
||||||
|
std::vector<const proto_ns::MessageLite*> result;
|
||||||
|
for (auto it = data->begin(); it != data->end(); ++it) {
|
||||||
|
const proto_ns::MessageLite* element = &(*it);
|
||||||
|
result.push_back(element);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Holder : public HolderBase {
|
class Holder : public HolderBase {
|
||||||
public:
|
public:
|
||||||
|
@ -421,6 +465,14 @@ class Holder : public HolderBase {
|
||||||
ptr_, std::is_base_of<proto_ns::MessageLite, T>());
|
ptr_, std::is_base_of<proto_ns::MessageLite, T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a vector<MessageLite*> for the data in the holder, if the
|
||||||
|
// underlying object is a vector of protocol buffer objects, otherwise,
|
||||||
|
// returns an error.
|
||||||
|
StatusOr<std::vector<const proto_ns::MessageLite*>>
|
||||||
|
GetVectorOfProtoMessageLite() override {
|
||||||
|
return ConvertToVectorOfProtoMessageLitePtrs(ptr_, is_proto_vector<T>());
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Call delete[] if T is an array, delete otherwise.
|
// Call delete[] if T is an array, delete otherwise.
|
||||||
template <typename U = T>
|
template <typename U = T>
|
||||||
|
|
|
@ -37,12 +37,16 @@
|
||||||
#if !defined(MEDIAPIPE_IOS) && !TARGET_OS_OSX
|
#if !defined(MEDIAPIPE_IOS) && !TARGET_OS_OSX
|
||||||
#define MEDIAPIPE_IOS
|
#define MEDIAPIPE_IOS
|
||||||
#endif
|
#endif
|
||||||
|
#if !defined(MEDIAPIPE_OSX) && TARGET_OS_OSX
|
||||||
|
#define MEDIAPIPE_OSX
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// These platforms do not support OpenGL ES Compute Shaders (v3.1 and up),
|
// These platforms do not support OpenGL ES Compute Shaders (v3.1 and up),
|
||||||
// but can still run OpenGL ES 3.0 and below.
|
// but may or may not still be able to run other OpenGL code.
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE) && \
|
#if !defined(MEDIAPIPE_DISABLE_GL_COMPUTE) && \
|
||||||
(defined(__APPLE__) || defined(__EMSCRIPTEN__))
|
(defined(__APPLE__) || defined(__EMSCRIPTEN__) || \
|
||||||
|
defined(MEDIAPIPE_DISABLE_GPU))
|
||||||
#define MEDIAPIPE_DISABLE_GL_COMPUTE
|
#define MEDIAPIPE_DISABLE_GL_COMPUTE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#ifdef CV_VERSION_EPOCH // for OpenCV 2.x
|
#ifdef CV_VERSION_EPOCH // for OpenCV 2.x
|
||||||
#include <opencv2/core/core.hpp>
|
#include <opencv2/core/core.hpp>
|
||||||
#else
|
#else
|
||||||
|
#include <opencv2/cvconfig.h>
|
||||||
|
|
||||||
#include <opencv2/core.hpp>
|
#include <opencv2/core.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -158,9 +158,11 @@ void Scheduler::HandleIdle() {
|
||||||
if (!active_sources_.empty() || throttled_graph_input_stream_count_ > 0) {
|
if (!active_sources_.empty() || throttled_graph_input_stream_count_ > 0) {
|
||||||
VLOG(2) << "HandleIdle: unthrottling";
|
VLOG(2) << "HandleIdle: unthrottling";
|
||||||
state_mutex_.Unlock();
|
state_mutex_.Unlock();
|
||||||
graph_->UnthrottleSources();
|
bool did_unthrottle = graph_->UnthrottleSources();
|
||||||
state_mutex_.Lock();
|
state_mutex_.Lock();
|
||||||
continue;
|
if (did_unthrottle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing left to do.
|
// Nothing left to do.
|
||||||
|
|
|
@ -115,9 +115,10 @@ void ImmediateInputStreamHandler::FillInputSet(Timestamp input_timestamp,
|
||||||
AddPacketToShard(&input_set->Get(id), std::move(current_packet),
|
AddPacketToShard(&input_set->Get(id), std::move(current_packet),
|
||||||
stream_is_done);
|
stream_is_done);
|
||||||
} else {
|
} else {
|
||||||
bool empty = false;
|
Timestamp bound = stream->MinTimestampOrBound(nullptr);
|
||||||
bool is_done = stream->MinTimestampOrBound(&empty) == Timestamp::Done();
|
AddPacketToShard(&input_set->Get(id),
|
||||||
AddPacketToShard(&input_set->Get(id), Packet(), is_done);
|
Packet().At(bound.PreviousAllowedInStream()),
|
||||||
|
bound == Timestamp::Done());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,9 +56,16 @@ class SyncSetInputStreamHandler : public InputStreamHandler {
|
||||||
NodeReadiness GetNodeReadiness(Timestamp* min_stream_timestamp) override;
|
NodeReadiness GetNodeReadiness(Timestamp* min_stream_timestamp) override;
|
||||||
|
|
||||||
// Only invoked when associated GetNodeReadiness() returned kReadyForProcess.
|
// Only invoked when associated GetNodeReadiness() returned kReadyForProcess.
|
||||||
|
// Populates packets for the ready sync-set, and populates timestamp bounds
|
||||||
|
// for all sync-sets.
|
||||||
void FillInputSet(Timestamp input_timestamp,
|
void FillInputSet(Timestamp input_timestamp,
|
||||||
InputStreamShardSet* input_set) override;
|
InputStreamShardSet* input_set) override;
|
||||||
|
|
||||||
|
// Populates timestamp bounds for streams outside the ready sync-set.
|
||||||
|
void FillInputBounds(Timestamp input_timestamp,
|
||||||
|
InputStreamShardSet* input_set)
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
absl::Mutex mutex_;
|
absl::Mutex mutex_;
|
||||||
// The ids of each set of inputs.
|
// The ids of each set of inputs.
|
||||||
|
@ -181,6 +188,22 @@ NodeReadiness SyncSetInputStreamHandler::GetNodeReadiness(
|
||||||
return NodeReadiness::kNotReady;
|
return NodeReadiness::kNotReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SyncSetInputStreamHandler::FillInputBounds(
|
||||||
|
Timestamp input_timestamp, InputStreamShardSet* input_set) {
|
||||||
|
for (int i = 0; i < sync_sets_.size(); ++i) {
|
||||||
|
if (i != ready_sync_set_index_) {
|
||||||
|
// Set the input streams for the not-ready sync sets.
|
||||||
|
for (CollectionItemId id : sync_sets_[i]) {
|
||||||
|
const auto stream = input_stream_managers_.Get(id);
|
||||||
|
Timestamp bound = stream->MinTimestampOrBound(nullptr);
|
||||||
|
AddPacketToShard(&input_set->Get(id),
|
||||||
|
Packet().At(bound.PreviousAllowedInStream()),
|
||||||
|
bound == Timestamp::Done());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SyncSetInputStreamHandler::FillInputSet(Timestamp input_timestamp,
|
void SyncSetInputStreamHandler::FillInputSet(Timestamp input_timestamp,
|
||||||
InputStreamShardSet* input_set) {
|
InputStreamShardSet* input_set) {
|
||||||
// Assume that all current packets are already cleared.
|
// Assume that all current packets are already cleared.
|
||||||
|
@ -202,6 +225,7 @@ void SyncSetInputStreamHandler::FillInputSet(Timestamp input_timestamp,
|
||||||
AddPacketToShard(&input_set->Get(id), std::move(current_packet),
|
AddPacketToShard(&input_set->Get(id), std::move(current_packet),
|
||||||
stream_is_done);
|
stream_is_done);
|
||||||
}
|
}
|
||||||
|
FillInputBounds(input_timestamp, input_set);
|
||||||
ready_sync_set_index_ = -1;
|
ready_sync_set_index_ = -1;
|
||||||
ready_timestamp_ = Timestamp::Done();
|
ready_timestamp_ = Timestamp::Done();
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,13 +123,23 @@ std::string TimestampDiff::DebugString() const {
|
||||||
|
|
||||||
Timestamp Timestamp::NextAllowedInStream() const {
|
Timestamp Timestamp::NextAllowedInStream() const {
|
||||||
CHECK(IsAllowedInStream()) << "Timestamp is: " << DebugString();
|
CHECK(IsAllowedInStream()) << "Timestamp is: " << DebugString();
|
||||||
if (IsRangeValue() && *this != Max()) {
|
if (*this >= Max() || *this == PreStream()) {
|
||||||
return *this + 1;
|
// Indicates that no further timestamps may occur.
|
||||||
} else {
|
|
||||||
// Returning this value effectively means no futher timestamps may
|
|
||||||
// occur (however, the stream is not yet closed).
|
|
||||||
return OneOverPostStream();
|
return OneOverPostStream();
|
||||||
|
} else if (*this < Min()) {
|
||||||
|
return Min();
|
||||||
}
|
}
|
||||||
|
return *this + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timestamp Timestamp::PreviousAllowedInStream() const {
|
||||||
|
if (*this <= Min() || *this == PostStream()) {
|
||||||
|
// Indicates that no previous timestamps may occur.
|
||||||
|
return Unstarted();
|
||||||
|
} else if (*this > Max()) {
|
||||||
|
return Max();
|
||||||
|
}
|
||||||
|
return *this - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, Timestamp arg) {
|
std::ostream& operator<<(std::ostream& os, Timestamp arg) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user