Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73f4475c17 | ||
|
|
f57ff46845 | ||
|
|
d7c287c4e9 | ||
|
|
6b0ab0e012 | ||
|
|
2f86a459b6 |
@@ -12,6 +12,6 @@
|
|||||||
# 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
|
licenses(["notice"])
|
||||||
|
|
||||||
exports_files(["LICENSE"])
|
exports_files(["LICENSE"])
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
global-exclude .git*
|
||||||
|
global-exclude *_test.py
|
||||||
|
|
||||||
|
recursive-include mediapipe/models *.tflite *.txt
|
||||||
|
recursive-include mediapipe/modules *.tflite *.txt
|
||||||
|
recursive-include mediapipe/graphs *.binarypb
|
||||||
@@ -22,30 +22,32 @@ desktop/cloud, web and IoT devices.
|
|||||||
|
|
||||||
## ML solutions in MediaPipe
|
## ML solutions in MediaPipe
|
||||||
|
|
||||||
Face Detection | Face Mesh | Hands | Hair Segmentation
|
Face Detection | Face Mesh | Iris 🆕 | Hands | Pose 🆕
|
||||||
:----------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------: | :---------------:
|
:----------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------: | :----:
|
||||||
[](https://google.github.io/mediapipe/solutions/face_detection) | [](https://google.github.io/mediapipe/solutions/face_mesh) | [](https://google.github.io/mediapipe/solutions/hands) | [](https://google.github.io/mediapipe/solutions/hair_segmentation)
|
[](https://google.github.io/mediapipe/solutions/face_detection) | [](https://google.github.io/mediapipe/solutions/face_mesh) | [](https://google.github.io/mediapipe/solutions/iris) | [](https://google.github.io/mediapipe/solutions/hands) | [](https://google.github.io/mediapipe/solutions/pose)
|
||||||
|
|
||||||
Object Detection | Box Tracking | Objectron | KNIFT
|
Hair Segmentation | Object Detection | Box Tracking | Objectron | KNIFT
|
||||||
:----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------: | :---:
|
:-------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------: | :---:
|
||||||
[](https://google.github.io/mediapipe/solutions/object_detection) | [](https://google.github.io/mediapipe/solutions/box_tracking) | [](https://google.github.io/mediapipe/solutions/objectron) | [](https://google.github.io/mediapipe/solutions/knift)
|
[](https://google.github.io/mediapipe/solutions/hair_segmentation) | [](https://google.github.io/mediapipe/solutions/object_detection) | [](https://google.github.io/mediapipe/solutions/box_tracking) | [](https://google.github.io/mediapipe/solutions/objectron) | [](https://google.github.io/mediapipe/solutions/knift)
|
||||||
|
|
||||||
<!-- []() in the first cell is needed to preserve table formatting in GitHub Pages. -->
|
<!-- []() in the first cell is needed to preserve table formatting in GitHub Pages. -->
|
||||||
<!-- Whenever this table is updated, paste a copy to solutions/solutions.md. -->
|
<!-- Whenever this table is updated, paste a copy to solutions/solutions.md. -->
|
||||||
|
|
||||||
[]() | Android | iOS | Desktop | Web | Coral
|
[]() | Android | iOS | Desktop | Python | Web | Coral
|
||||||
:---------------------------------------------------------------------------- | :-----: | :-: | :-----: | :-: | :---:
|
:---------------------------------------------------------------------------- | :-----: | :-: | :-----: | :----: | :-: | :---:
|
||||||
[Face Detection](https://google.github.io/mediapipe/solutions/face_detection) | ✅ | ✅ | ✅ | ✅ | ✅
|
[Face Detection](https://google.github.io/mediapipe/solutions/face_detection) | ✅ | ✅ | ✅ | | ✅ | ✅
|
||||||
[Face Mesh](https://google.github.io/mediapipe/solutions/face_mesh) | ✅ | ✅ | ✅ | |
|
[Face Mesh](https://google.github.io/mediapipe/solutions/face_mesh) | ✅ | ✅ | ✅ | | |
|
||||||
[Hands](https://google.github.io/mediapipe/solutions/hands) | ✅ | ✅ | ✅ | ✅ |
|
[Iris](https://google.github.io/mediapipe/solutions/iris) 🆕 | ✅ | ✅ | ✅ | | ✅ |
|
||||||
[Hair Segmentation](https://google.github.io/mediapipe/solutions/hair_segmentation) | ✅ | | ✅ | ✅ |
|
[Hands](https://google.github.io/mediapipe/solutions/hands) | ✅ | ✅ | ✅ | | ✅ |
|
||||||
[Object Detection](https://google.github.io/mediapipe/solutions/object_detection) | ✅ | ✅ | ✅ | | ✅
|
[Pose](https://google.github.io/mediapipe/solutions/pose) 🆕 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
[Box Tracking](https://google.github.io/mediapipe/solutions/box_tracking) | ✅ | ✅ | ✅ | |
|
[Hair Segmentation](https://google.github.io/mediapipe/solutions/hair_segmentation) | ✅ | | ✅ | | ✅ |
|
||||||
[Objectron](https://google.github.io/mediapipe/solutions/objectron) | ✅ | | | |
|
[Object Detection](https://google.github.io/mediapipe/solutions/object_detection) | ✅ | ✅ | ✅ | | | ✅
|
||||||
[KNIFT](https://google.github.io/mediapipe/solutions/knift) | ✅ | | | |
|
[Box Tracking](https://google.github.io/mediapipe/solutions/box_tracking) | ✅ | ✅ | ✅ | | |
|
||||||
[AutoFlip](https://google.github.io/mediapipe/solutions/autoflip) | | | ✅ | |
|
[Objectron](https://google.github.io/mediapipe/solutions/objectron) | ✅ | | | | |
|
||||||
[MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | |
|
[KNIFT](https://google.github.io/mediapipe/solutions/knift) | ✅ | | | | |
|
||||||
[YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | |
|
[AutoFlip](https://google.github.io/mediapipe/solutions/autoflip) | | | ✅ | | |
|
||||||
|
[MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | | |
|
||||||
|
[YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | | |
|
||||||
|
|
||||||
## MediaPipe on the Web
|
## MediaPipe on the Web
|
||||||
|
|
||||||
@@ -63,8 +65,11 @@ never leaves your device.
|
|||||||

|

|
||||||
|
|
||||||
* [MediaPipe Face Detection](https://viz.mediapipe.dev/demo/face_detection)
|
* [MediaPipe Face Detection](https://viz.mediapipe.dev/demo/face_detection)
|
||||||
|
* [MediaPipe Iris](https://viz.mediapipe.dev/demo/iris_tracking)
|
||||||
|
* [MediaPipe Iris: Depth-from-Iris](https://viz.mediapipe.dev/demo/iris_depth)
|
||||||
* [MediaPipe Hands](https://viz.mediapipe.dev/demo/hand_tracking)
|
* [MediaPipe Hands](https://viz.mediapipe.dev/demo/hand_tracking)
|
||||||
* [MediaPipe Hands (palm/hand detection only)](https://viz.mediapipe.dev/demo/hand_detection)
|
* [MediaPipe Hands (palm/hand detection only)](https://viz.mediapipe.dev/demo/hand_detection)
|
||||||
|
* [MediaPipe Pose](https://viz.mediapipe.dev/demo/pose_tracking)
|
||||||
* [MediaPipe Hair Segmentation](https://viz.mediapipe.dev/demo/hair_segmentation)
|
* [MediaPipe Hair Segmentation](https://viz.mediapipe.dev/demo/hair_segmentation)
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
@@ -83,6 +88,10 @@ run code search using
|
|||||||
|
|
||||||
## Publications
|
## Publications
|
||||||
|
|
||||||
|
* [BlazePose - On-device Real-time Body Pose Tracking](https://mediapipe.page.link/blazepose-blog)
|
||||||
|
in Google AI Blog
|
||||||
|
* [MediaPipe Iris: Real-time Eye Tracking and Depth Estimation](https://ai.googleblog.com/2020/08/mediapipe-iris-real-time-iris-tracking.html)
|
||||||
|
in Google AI Blog
|
||||||
* [MediaPipe KNIFT: Template-based feature matching](https://developers.googleblog.com/2020/04/mediapipe-knift-template-based-feature-matching.html)
|
* [MediaPipe KNIFT: Template-based feature matching](https://developers.googleblog.com/2020/04/mediapipe-knift-template-based-feature-matching.html)
|
||||||
in Google Developers Blog
|
in Google Developers Blog
|
||||||
* [Alfred Camera: Smart camera features using MediaPipe](https://developers.googleblog.com/2020/03/alfred-camera-smart-camera-features-using-mediapipe.html)
|
* [Alfred Camera: Smart camera features using MediaPipe](https://developers.googleblog.com/2020/03/alfred-camera-smart-camera-features-using-mediapipe.html)
|
||||||
|
|||||||
@@ -137,6 +137,25 @@ http_archive(
|
|||||||
urls = ["https://github.com/google/multichannel-audio-tools/archive/master.zip"],
|
urls = ["https://github.com/google/multichannel-audio-tools/archive/master.zip"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 2020-07-09
|
||||||
|
http_archive(
|
||||||
|
name = "pybind11_bazel",
|
||||||
|
strip_prefix = "pybind11_bazel-203508e14aab7309892a1c5f7dd05debda22d9a5",
|
||||||
|
urls = ["https://github.com/pybind/pybind11_bazel/archive/203508e14aab7309892a1c5f7dd05debda22d9a5.zip"],
|
||||||
|
sha256 = "75922da3a1bdb417d820398eb03d4e9bd067c4905a4246d35a44c01d62154d91",
|
||||||
|
)
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "pybind11",
|
||||||
|
urls = [
|
||||||
|
"https://storage.googleapis.com/mirror.tensorflow.org/github.com/pybind/pybind11/archive/v2.4.3.tar.gz",
|
||||||
|
"https://github.com/pybind/pybind11/archive/v2.4.3.tar.gz",
|
||||||
|
],
|
||||||
|
sha256 = "1eed57bc6863190e35637290f97a20c81cfe4d9090ac0a24f3bbf08f265eb71d",
|
||||||
|
strip_prefix = "pybind11-2.4.3",
|
||||||
|
build_file = "@pybind11_bazel//:pybind11.BUILD",
|
||||||
|
)
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "ceres_solver",
|
name = "ceres_solver",
|
||||||
url = "https://github.com/ceres-solver/ceres-solver/archive/1.14.0.zip",
|
url = "https://github.com/ceres-solver/ceres-solver/archive/1.14.0.zip",
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ apps="${app_dir}/*"
|
|||||||
for app in ${apps}; do
|
for app in ${apps}; do
|
||||||
if [[ -d "${app}" ]]; then
|
if [[ -d "${app}" ]]; then
|
||||||
target_name=${app##*/}
|
target_name=${app##*/}
|
||||||
|
if [[ "${target_name}" == "common" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
target="${app}:${target_name}"
|
target="${app}:${target_name}"
|
||||||
|
|
||||||
echo "=== Target: ${target}"
|
echo "=== Target: ${target}"
|
||||||
|
|||||||
@@ -402,11 +402,6 @@ node {
|
|||||||
The diagram below shows how the `PacketClonerCalculator` defines its output
|
The diagram below shows how the `PacketClonerCalculator` defines its output
|
||||||
packets (bottom) based on its series of input packets (top).
|
packets (bottom) based on its series of input packets (top).
|
||||||
|
|
||||||
|  |
|
||||||
: PacketClonerCalculator](../images/packet_cloner_calculator.png) :
|
| :---------------------------------------------------------------------------: |
|
||||||
| :--------------------------------------------------------------------------: |
|
| *Each time it receives a packet on its TICK input stream, the PacketClonerCalculator outputs the most recent packet from each of its input streams. The sequence of output packets (bottom) is determined by the sequence of input packets (top) and their timestamps. The timestamps are shown along the right side of the diagram.* |
|
||||||
| *Each time it receives a packet on its TICK input stream, the |
|
|
||||||
: PacketClonerCalculator outputs the most recent packet from each of its input :
|
|
||||||
: streams. The sequence of output packets (bottom) is determined by the :
|
|
||||||
: sequence of input packets (top) and their timestamps. The timestamps are :
|
|
||||||
: shown along the right side of the diagram.* :
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Packets are generally created with `MediaPipe::Adopt()` (from packet.h).
|
|||||||
|
|
||||||
```c++
|
```c++
|
||||||
// Create some data.
|
// Create some data.
|
||||||
auto data = gtl::MakeUnique<MyDataClass>("constructor_argument");
|
auto data = absl::make_unique<MyDataClass>("constructor_argument");
|
||||||
// Create a packet to own the data.
|
// Create a packet to own the data.
|
||||||
Packet p = Adopt(data.release());
|
Packet p = Adopt(data.release());
|
||||||
// Make a new packet with the same data and a different timestamp.
|
// Make a new packet with the same data and a different timestamp.
|
||||||
|
|||||||
@@ -280,16 +280,16 @@ are two options:
|
|||||||
2. In the project navigator in the left sidebar, select the "Mediapipe"
|
2. In the project navigator in the left sidebar, select the "Mediapipe"
|
||||||
project.
|
project.
|
||||||
|
|
||||||
3. Select the "Signing & Capabilities" tab.
|
3. Select one of the application targets, e.g. HandTrackingGpuApp.
|
||||||
|
|
||||||
4. Select one of the application targets, e.g. HandTrackingGpuApp.
|
4. Select the "Signing & Capabilities" tab.
|
||||||
|
|
||||||
5. Check "Automatically manage signing", and confirm the dialog box.
|
5. Check "Automatically manage signing", and confirm the dialog box.
|
||||||
|
|
||||||
6. Select "_Your Name_ (Personal Team)" in the Team pop-up menu.
|
6. Select "_Your Name_ (Personal Team)" in the Team pop-up menu.
|
||||||
|
|
||||||
7. This set-up needs to be done once for each application you want to install.
|
7. This set-up needs to be done once for each application you want to install.
|
||||||
Repeat steps 4-6 as needed.
|
Repeat steps 3-6 as needed.
|
||||||
|
|
||||||
This generates provisioning profiles for each app you have selected. Now we need
|
This generates provisioning profiles for each app you have selected. Now we need
|
||||||
to tell Bazel to use them. We have provided a script to make this easier.
|
to tell Bazel to use them. We have provided a script to make this easier.
|
||||||
@@ -390,9 +390,6 @@ developer (yourself) is trusted.
|
|||||||
bazel build -c opt --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/examples/desktop/hand_tracking:hand_tracking_cpu
|
bazel build -c opt --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/examples/desktop/hand_tracking:hand_tracking_cpu
|
||||||
```
|
```
|
||||||
|
|
||||||
This will open up your webcam as long as it is connected and on. Any errors
|
|
||||||
is likely due to your webcam being not accessible.
|
|
||||||
|
|
||||||
2. To run the application:
|
2. To run the application:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -400,6 +397,9 @@ developer (yourself) is trusted.
|
|||||||
--calculator_graph_config_file=mediapipe/graphs/hand_tracking/hand_tracking_desktop_live.pbtxt
|
--calculator_graph_config_file=mediapipe/graphs/hand_tracking/hand_tracking_desktop_live.pbtxt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will open up your webcam as long as it is connected and on. Any errors
|
||||||
|
is likely due to your webcam being not accessible.
|
||||||
|
|
||||||
### Option 2: Running on GPU
|
### Option 2: Running on GPU
|
||||||
|
|
||||||
Note: This currently works only on Linux, and please first follow
|
Note: This currently works only on Linux, and please first follow
|
||||||
@@ -412,13 +412,83 @@ Note: This currently works only on Linux, and please first follow
|
|||||||
mediapipe/examples/desktop/hand_tracking:hand_tracking_gpu
|
mediapipe/examples/desktop/hand_tracking:hand_tracking_gpu
|
||||||
```
|
```
|
||||||
|
|
||||||
This will open up your webcam as long as it is connected and on. Any errors
|
|
||||||
is likely due to your webcam being not accessible, or GPU drivers not setup
|
|
||||||
properly.
|
|
||||||
|
|
||||||
2. To run the application:
|
2. To run the application:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
GLOG_logtostderr=1 bazel-bin/mediapipe/examples/desktop/hand_tracking/hand_tracking_gpu \
|
GLOG_logtostderr=1 bazel-bin/mediapipe/examples/desktop/hand_tracking/hand_tracking_gpu \
|
||||||
--calculator_graph_config_file=mediapipe/graphs/hand_tracking/hand_tracking_mobile.pbtxt
|
--calculator_graph_config_file=mediapipe/graphs/hand_tracking/hand_tracking_mobile.pbtxt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will open up your webcam as long as it is connected and on. Any errors
|
||||||
|
is likely due to your webcam being not accessible, or GPU drivers not setup
|
||||||
|
properly.
|
||||||
|
|
||||||
|
## Python
|
||||||
|
|
||||||
|
### Prerequisite
|
||||||
|
|
||||||
|
1. Make sure that Bazel and OpenCV are correctly installed and configured for
|
||||||
|
MediaPipe. Please see [Installation](./install.md) for how to setup Bazel
|
||||||
|
and OpenCV for MediaPipe on Linux and macOS.
|
||||||
|
|
||||||
|
2. Install the following dependencies.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Debian or Ubuntu
|
||||||
|
$ sudo apt install python3-dev
|
||||||
|
$ sudo apt install python3-venv
|
||||||
|
$ sudo apt install -y protobuf-compiler
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# macOS
|
||||||
|
$ brew install protobuf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Set up Python virtual environment.
|
||||||
|
|
||||||
|
1. Activate a Python virtual environment.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ python3 -m venv mp_env && source mp_env/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
2. In the virtual environment, go to the MediaPipe repo directory.
|
||||||
|
|
||||||
|
3. Install the required Python packages.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(mp_env)mediapipe$ pip3 install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Generate and install MediaPipe package.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(mp_env)mediapipe$ python3 setup.py gen_protos
|
||||||
|
(mp_env)mediapipe$ python3 setup.py install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run in Python interpreter
|
||||||
|
|
||||||
|
Make sure you are not in the MediaPipe repo directory.
|
||||||
|
|
||||||
|
Using [MediaPipe Pose](../solutions/pose.md) as an example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(mp_env)$ python3
|
||||||
|
>>> import mediapipe as mp
|
||||||
|
>>> pose_tracker = mp.examples.UpperBodyPoseTracker()
|
||||||
|
|
||||||
|
# For image input
|
||||||
|
>>> pose_landmarks, _ = pose_tracker.run(input_file='/path/to/input/file', output_file='/path/to/output/file')
|
||||||
|
>>> pose_landmarks, annotated_image = pose_tracker.run(input_file='/path/to/file')
|
||||||
|
|
||||||
|
# For live camera input
|
||||||
|
# (Press Esc within the output image window to stop the run or let it self terminate after 30 seconds.)
|
||||||
|
>>> pose_tracker.run_live()
|
||||||
|
|
||||||
|
# Close the tracker.
|
||||||
|
>>> pose_tracker.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
Tip: Use command `deactivate` to exit the Python virtual environment.
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ This codelab uses MediaPipe on an iOS device.
|
|||||||
|
|
||||||
### What you will learn
|
### What you will learn
|
||||||
|
|
||||||
How to develop an iOS application that uses MediaPipe and run a MediaPipe
|
How to develop an iOS application that uses MediaPipe and run a MediaPipe graph
|
||||||
graph on iOS.
|
on iOS.
|
||||||
|
|
||||||
### What you will build
|
### What you will build
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ We will be using the following graph, [`edge_detection_mobile_gpu.pbtxt`]:
|
|||||||
```
|
```
|
||||||
# MediaPipe graph that performs GPU Sobel edge detection on a live video stream.
|
# MediaPipe graph that performs GPU Sobel edge detection on a live video stream.
|
||||||
# Used in the examples
|
# Used in the examples
|
||||||
# mediapipe/examples/android/src/java/com/mediapipe/apps/edgedetectiongpu.
|
# mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:helloworld
|
||||||
# mediapipe/examples/ios/edgedetectiongpu.
|
# and mediapipe/examples/ios/helloworld.
|
||||||
|
|
||||||
# Images coming into and out of the graph.
|
# Images coming into and out of the graph.
|
||||||
input_stream: "input_video"
|
input_stream: "input_video"
|
||||||
@@ -89,21 +89,21 @@ to build it.
|
|||||||
|
|
||||||
First, create an XCode project via File > New > Single View App.
|
First, create an XCode project via File > New > Single View App.
|
||||||
|
|
||||||
Set the product name to "EdgeDetectionGpu", and use an appropriate organization
|
Set the product name to "HelloWorld", and use an appropriate organization
|
||||||
identifier, such as `com.google.mediapipe`. The organization identifier
|
identifier, such as `com.google.mediapipe`. The organization identifier
|
||||||
alongwith the product name will be the `bundle_id` for the application, such as
|
alongwith the product name will be the `bundle_id` for the application, such as
|
||||||
`com.google.mediapipe.EdgeDetectionGpu`.
|
`com.google.mediapipe.HelloWorld`.
|
||||||
|
|
||||||
Set the language to Objective-C.
|
Set the language to Objective-C.
|
||||||
|
|
||||||
Save the project to an appropriate location. Let's call this
|
Save the project to an appropriate location. Let's call this
|
||||||
`$PROJECT_TEMPLATE_LOC`. So your project will be in the
|
`$PROJECT_TEMPLATE_LOC`. So your project will be in the
|
||||||
`$PROJECT_TEMPLATE_LOC/EdgeDetectionGpu` directory. This directory will contain
|
`$PROJECT_TEMPLATE_LOC/HelloWorld` directory. This directory will contain
|
||||||
another directory named `EdgeDetectionGpu` and an `EdgeDetectionGpu.xcodeproj` file.
|
another directory named `HelloWorld` and an `HelloWorld.xcodeproj` file.
|
||||||
|
|
||||||
The `EdgeDetectionGpu.xcodeproj` will not be useful for this tutorial, as we will
|
The `HelloWorld.xcodeproj` will not be useful for this tutorial, as we will use
|
||||||
use bazel to build the iOS application. The content of the
|
bazel to build the iOS application. The content of the
|
||||||
`$PROJECT_TEMPLATE_LOC/EdgeDetectionGpu/EdgeDetectionGpu` directory is listed below:
|
`$PROJECT_TEMPLATE_LOC/HelloWorld/HelloWorld` directory is listed below:
|
||||||
|
|
||||||
1. `AppDelegate.h` and `AppDelegate.m`
|
1. `AppDelegate.h` and `AppDelegate.m`
|
||||||
2. `ViewController.h` and `ViewController.m`
|
2. `ViewController.h` and `ViewController.m`
|
||||||
@@ -112,10 +112,10 @@ use bazel to build the iOS application. The content of the
|
|||||||
5. `Main.storyboard` and `Launch.storyboard`
|
5. `Main.storyboard` and `Launch.storyboard`
|
||||||
6. `Assets.xcassets` directory.
|
6. `Assets.xcassets` directory.
|
||||||
|
|
||||||
Copy these files to a directory named `EdgeDetectionGpu` to a location that can
|
Copy these files to a directory named `HelloWorld` to a location that can access
|
||||||
access the MediaPipe source code. For example, the source code of the
|
the MediaPipe source code. For example, the source code of the application that
|
||||||
application that we will build in this tutorial is located in
|
we will build in this tutorial is located in
|
||||||
`mediapipe/examples/ios/EdgeDetectionGpu`. We will refer to this path as the
|
`mediapipe/examples/ios/HelloWorld`. We will refer to this path as the
|
||||||
`$APPLICATION_PATH` throughout the codelab.
|
`$APPLICATION_PATH` throughout the codelab.
|
||||||
|
|
||||||
Note: MediaPipe provides Objective-C bindings for iOS. The edge detection
|
Note: MediaPipe provides Objective-C bindings for iOS. The edge detection
|
||||||
@@ -134,8 +134,8 @@ load(
|
|||||||
)
|
)
|
||||||
|
|
||||||
ios_application(
|
ios_application(
|
||||||
name = "EdgeDetectionGpuApp",
|
name = "HelloWorldApp",
|
||||||
bundle_id = "com.google.mediapipe.EdgeDetectionGpu",
|
bundle_id = "com.google.mediapipe.HelloWorld",
|
||||||
families = [
|
families = [
|
||||||
"iphone",
|
"iphone",
|
||||||
"ipad",
|
"ipad",
|
||||||
@@ -143,11 +143,11 @@ ios_application(
|
|||||||
infoplists = ["Info.plist"],
|
infoplists = ["Info.plist"],
|
||||||
minimum_os_version = MIN_IOS_VERSION,
|
minimum_os_version = MIN_IOS_VERSION,
|
||||||
provisioning_profile = "//mediapipe/examples/ios:developer_provisioning_profile",
|
provisioning_profile = "//mediapipe/examples/ios:developer_provisioning_profile",
|
||||||
deps = [":EdgeDetectionGpuAppLibrary"],
|
deps = [":HelloWorldAppLibrary"],
|
||||||
)
|
)
|
||||||
|
|
||||||
objc_library(
|
objc_library(
|
||||||
name = "EdgeDetectionGpuAppLibrary",
|
name = "HelloWorldAppLibrary",
|
||||||
srcs = [
|
srcs = [
|
||||||
"AppDelegate.m",
|
"AppDelegate.m",
|
||||||
"ViewController.m",
|
"ViewController.m",
|
||||||
@@ -172,9 +172,8 @@ The `objc_library` rule adds dependencies for the `AppDelegate` and
|
|||||||
`ViewController` classes, `main.m` and the application storyboards. The
|
`ViewController` classes, `main.m` and the application storyboards. The
|
||||||
templated app depends only on the `UIKit` SDK.
|
templated app depends only on the `UIKit` SDK.
|
||||||
|
|
||||||
The `ios_application` rule uses the `EdgeDetectionGpuAppLibrary` Objective-C
|
The `ios_application` rule uses the `HelloWorldAppLibrary` Objective-C library
|
||||||
library generated to build an iOS application for installation on your iOS
|
generated to build an iOS application for installation on your iOS device.
|
||||||
device.
|
|
||||||
|
|
||||||
Note: You need to point to your own iOS developer provisioning profile to be
|
Note: You need to point to your own iOS developer provisioning profile to be
|
||||||
able to run the application on your iOS device.
|
able to run the application on your iOS device.
|
||||||
@@ -182,21 +181,20 @@ able to run the application on your iOS device.
|
|||||||
To build the app, use the following command in a terminal:
|
To build the app, use the following command in a terminal:
|
||||||
|
|
||||||
```
|
```
|
||||||
bazel build -c opt --config=ios_arm64 <$APPLICATION_PATH>:EdgeDetectionGpuApp'
|
bazel build -c opt --config=ios_arm64 <$APPLICATION_PATH>:HelloWorldApp'
|
||||||
```
|
```
|
||||||
|
|
||||||
For example, to build the `EdgeDetectionGpuApp` application in
|
For example, to build the `HelloWorldApp` application in
|
||||||
`mediapipe/examples/ios/edgedetectiongpu`, use the following
|
`mediapipe/examples/ios/helloworld`, use the following command:
|
||||||
command:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
bazel build -c opt --config=ios_arm64 mediapipe/examples/ios/edgedetectiongpu:EdgeDetectionGpuApp
|
bazel build -c opt --config=ios_arm64 mediapipe/examples/ios/helloworld:HelloWorldApp
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, go back to XCode, open Window > Devices and Simulators, select your
|
Then, go back to XCode, open Window > Devices and Simulators, select your
|
||||||
device, and add the `.ipa` file generated by the command above to your device.
|
device, and add the `.ipa` file generated by the command above to your device.
|
||||||
Here is the document on [setting up and compiling](./building_examples.md#ios) iOS
|
Here is the document on [setting up and compiling](./building_examples.md#ios)
|
||||||
MediaPipe apps.
|
iOS MediaPipe apps.
|
||||||
|
|
||||||
Open the application on your device. Since it is empty, it should display a
|
Open the application on your device. Since it is empty, it should display a
|
||||||
blank white screen.
|
blank white screen.
|
||||||
@@ -502,8 +500,8 @@ in our app:
|
|||||||
}];
|
}];
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: It is important to start the graph before starting the camera, so that
|
Note: It is important to start the graph before starting the camera, so that the
|
||||||
the graph is ready to process frames as soon as the camera starts sending them.
|
graph is ready to process frames as soon as the camera starts sending them.
|
||||||
|
|
||||||
Earlier, when we received frames from the camera in the `processVideoFrame`
|
Earlier, when we received frames from the camera in the `processVideoFrame`
|
||||||
function, we displayed them in the `_liveView` using the `_renderer`. Now, we
|
function, we displayed them in the `_liveView` using the `_renderer`. Now, we
|
||||||
@@ -552,9 +550,12 @@ results of running the edge detection graph on a live video feed. Congrats!
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you ran into any issues, please see the full code of the tutorial
|
Please note that the iOS examples now use a [common] template app. The code in
|
||||||
[here](https://github.com/google/mediapipe/tree/master/mediapipe/examples/ios/edgedetectiongpu).
|
this tutorial is used in the [common] template app. The [helloworld] app has the
|
||||||
|
appropriate `BUILD` file dependencies for the edge detection graph.
|
||||||
|
|
||||||
[Bazel]:https://bazel.build/
|
[Bazel]:https://bazel.build/
|
||||||
[`edge_detection_mobile_gpu.pbtxt`]:https://github.com/google/mediapipe/tree/master/mediapipe/graphs/object_detection/object_detection_mobile_gpu.pbtxt
|
[`edge_detection_mobile_gpu.pbtxt`]:https://github.com/google/mediapipe/tree/master/mediapipe/graphs/edge_detection/edge_detection_mobile_gpu.pbtxt
|
||||||
[MediaPipe installation guide]:./install.md
|
[MediaPipe installation guide]:./install.md
|
||||||
|
[common]:(https://github.com/google/mediapipe/tree/master/mediapipe/examples/ios/common)
|
||||||
|
[helloworld]:(https://github.com/google/mediapipe/tree/master/mediapipe/examples/ios/helloworld)
|
||||||
|
|||||||
@@ -27,13 +27,14 @@ Repository command failed
|
|||||||
|
|
||||||
usually indicates that Bazel fails to find the local Python binary. To solve
|
usually indicates that Bazel fails to find the local Python binary. To solve
|
||||||
this issue, please first find where the python binary is and then add
|
this issue, please first find where the python binary is and then add
|
||||||
`--action_env PYTHON_BIN_PATH=<path to python binary>` to the Bazel command like
|
`--action_env PYTHON_BIN_PATH=<path to python binary>` to the Bazel command. For
|
||||||
the following:
|
example, you can switch to use the system default python3 binary by the
|
||||||
|
following command:
|
||||||
|
|
||||||
```
|
```
|
||||||
bazel build -c opt \
|
bazel build -c opt \
|
||||||
--define MEDIAPIPE_DISABLE_GPU=1 \
|
--define MEDIAPIPE_DISABLE_GPU=1 \
|
||||||
--action_env PYTHON_BIN_PATH="/path/to/python" \
|
--action_env PYTHON_BIN_PATH=$(which python3) \
|
||||||
mediapipe/examples/desktop/hello_world
|
mediapipe/examples/desktop/hello_world
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 3.4 MiB |
|
After Width: | Height: | Size: 1019 KiB |
|
After Width: | Height: | Size: 10 MiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 996 KiB |
|
After Width: | Height: | Size: 313 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 6.9 MiB |
|
After Width: | Height: | Size: 224 KiB |
@@ -22,30 +22,32 @@ desktop/cloud, web and IoT devices.
|
|||||||
|
|
||||||
## ML solutions in MediaPipe
|
## ML solutions in MediaPipe
|
||||||
|
|
||||||
Face Detection | Face Mesh | Hands | Hair Segmentation
|
Face Detection | Face Mesh | Iris 🆕 | Hands | Pose 🆕
|
||||||
:----------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------: | :---------------:
|
:----------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------: | :----:
|
||||||
[](https://google.github.io/mediapipe/solutions/face_detection) | [](https://google.github.io/mediapipe/solutions/face_mesh) | [](https://google.github.io/mediapipe/solutions/hands) | [](https://google.github.io/mediapipe/solutions/hair_segmentation)
|
[](https://google.github.io/mediapipe/solutions/face_detection) | [](https://google.github.io/mediapipe/solutions/face_mesh) | [](https://google.github.io/mediapipe/solutions/iris) | [](https://google.github.io/mediapipe/solutions/hands) | [](https://google.github.io/mediapipe/solutions/pose)
|
||||||
|
|
||||||
Object Detection | Box Tracking | Objectron | KNIFT
|
Hair Segmentation | Object Detection | Box Tracking | Objectron | KNIFT
|
||||||
:----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------: | :---:
|
:-------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------: | :---:
|
||||||
[](https://google.github.io/mediapipe/solutions/object_detection) | [](https://google.github.io/mediapipe/solutions/box_tracking) | [](https://google.github.io/mediapipe/solutions/objectron) | [](https://google.github.io/mediapipe/solutions/knift)
|
[](https://google.github.io/mediapipe/solutions/hair_segmentation) | [](https://google.github.io/mediapipe/solutions/object_detection) | [](https://google.github.io/mediapipe/solutions/box_tracking) | [](https://google.github.io/mediapipe/solutions/objectron) | [](https://google.github.io/mediapipe/solutions/knift)
|
||||||
|
|
||||||
<!-- []() in the first cell is needed to preserve table formatting in GitHub Pages. -->
|
<!-- []() in the first cell is needed to preserve table formatting in GitHub Pages. -->
|
||||||
<!-- Whenever this table is updated, paste a copy to solutions/solutions.md. -->
|
<!-- Whenever this table is updated, paste a copy to solutions/solutions.md. -->
|
||||||
|
|
||||||
[]() | Android | iOS | Desktop | Web | Coral
|
[]() | Android | iOS | Desktop | Python | Web | Coral
|
||||||
:---------------------------------------------------------------------------- | :-----: | :-: | :-----: | :-: | :---:
|
:---------------------------------------------------------------------------- | :-----: | :-: | :-----: | :----: | :-: | :---:
|
||||||
[Face Detection](https://google.github.io/mediapipe/solutions/face_detection) | ✅ | ✅ | ✅ | ✅ | ✅
|
[Face Detection](https://google.github.io/mediapipe/solutions/face_detection) | ✅ | ✅ | ✅ | | ✅ | ✅
|
||||||
[Face Mesh](https://google.github.io/mediapipe/solutions/face_mesh) | ✅ | ✅ | ✅ | |
|
[Face Mesh](https://google.github.io/mediapipe/solutions/face_mesh) | ✅ | ✅ | ✅ | | |
|
||||||
[Hands](https://google.github.io/mediapipe/solutions/hands) | ✅ | ✅ | ✅ | ✅ |
|
[Iris](https://google.github.io/mediapipe/solutions/iris) 🆕 | ✅ | ✅ | ✅ | | ✅ |
|
||||||
[Hair Segmentation](https://google.github.io/mediapipe/solutions/hair_segmentation) | ✅ | | ✅ | ✅ |
|
[Hands](https://google.github.io/mediapipe/solutions/hands) | ✅ | ✅ | ✅ | | ✅ |
|
||||||
[Object Detection](https://google.github.io/mediapipe/solutions/object_detection) | ✅ | ✅ | ✅ | | ✅
|
[Pose](https://google.github.io/mediapipe/solutions/pose) 🆕 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
[Box Tracking](https://google.github.io/mediapipe/solutions/box_tracking) | ✅ | ✅ | ✅ | |
|
[Hair Segmentation](https://google.github.io/mediapipe/solutions/hair_segmentation) | ✅ | | ✅ | | ✅ |
|
||||||
[Objectron](https://google.github.io/mediapipe/solutions/objectron) | ✅ | | | |
|
[Object Detection](https://google.github.io/mediapipe/solutions/object_detection) | ✅ | ✅ | ✅ | | | ✅
|
||||||
[KNIFT](https://google.github.io/mediapipe/solutions/knift) | ✅ | | | |
|
[Box Tracking](https://google.github.io/mediapipe/solutions/box_tracking) | ✅ | ✅ | ✅ | | |
|
||||||
[AutoFlip](https://google.github.io/mediapipe/solutions/autoflip) | | | ✅ | |
|
[Objectron](https://google.github.io/mediapipe/solutions/objectron) | ✅ | | | | |
|
||||||
[MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | |
|
[KNIFT](https://google.github.io/mediapipe/solutions/knift) | ✅ | | | | |
|
||||||
[YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | |
|
[AutoFlip](https://google.github.io/mediapipe/solutions/autoflip) | | | ✅ | | |
|
||||||
|
[MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | | |
|
||||||
|
[YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | | |
|
||||||
|
|
||||||
## MediaPipe on the Web
|
## MediaPipe on the Web
|
||||||
|
|
||||||
@@ -63,8 +65,11 @@ never leaves your device.
|
|||||||

|

|
||||||
|
|
||||||
* [MediaPipe Face Detection](https://viz.mediapipe.dev/demo/face_detection)
|
* [MediaPipe Face Detection](https://viz.mediapipe.dev/demo/face_detection)
|
||||||
|
* [MediaPipe Iris](https://viz.mediapipe.dev/demo/iris_tracking)
|
||||||
|
* [MediaPipe Iris: Depth-from-Iris](https://viz.mediapipe.dev/demo/iris_depth)
|
||||||
* [MediaPipe Hands](https://viz.mediapipe.dev/demo/hand_tracking)
|
* [MediaPipe Hands](https://viz.mediapipe.dev/demo/hand_tracking)
|
||||||
* [MediaPipe Hands (palm/hand detection only)](https://viz.mediapipe.dev/demo/hand_detection)
|
* [MediaPipe Hands (palm/hand detection only)](https://viz.mediapipe.dev/demo/hand_detection)
|
||||||
|
* [MediaPipe Pose](https://viz.mediapipe.dev/demo/pose_tracking)
|
||||||
* [MediaPipe Hair Segmentation](https://viz.mediapipe.dev/demo/hair_segmentation)
|
* [MediaPipe Hair Segmentation](https://viz.mediapipe.dev/demo/hair_segmentation)
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
@@ -83,6 +88,10 @@ run code search using
|
|||||||
|
|
||||||
## Publications
|
## Publications
|
||||||
|
|
||||||
|
* [BlazePose - On-device Real-time Body Pose Tracking](https://mediapipe.page.link/blazepose-blog)
|
||||||
|
in Google AI Blog
|
||||||
|
* [MediaPipe Iris: Real-time Eye Tracking and Depth Estimation](https://ai.googleblog.com/2020/08/mediapipe-iris-real-time-iris-tracking.html)
|
||||||
|
in Google AI Blog
|
||||||
* [MediaPipe KNIFT: Template-based feature matching](https://developers.googleblog.com/2020/04/mediapipe-knift-template-based-feature-matching.html)
|
* [MediaPipe KNIFT: Template-based feature matching](https://developers.googleblog.com/2020/04/mediapipe-knift-template-based-feature-matching.html)
|
||||||
in Google Developers Blog
|
in Google Developers Blog
|
||||||
* [Alfred Camera: Smart camera features using MediaPipe](https://developers.googleblog.com/2020/03/alfred-camera-smart-camera-features-using-mediapipe.html)
|
* [Alfred Camera: Smart camera features using MediaPipe](https://developers.googleblog.com/2020/03/alfred-camera-smart-camera-features-using-mediapipe.html)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layout: default
|
layout: default
|
||||||
title: AutoFlip (Saliency-aware Video Cropping)
|
title: AutoFlip (Saliency-aware Video Cropping)
|
||||||
parent: Solutions
|
parent: Solutions
|
||||||
nav_order: 9
|
nav_order: 11
|
||||||
---
|
---
|
||||||
|
|
||||||
# AutoFlip: Saliency-aware Video Cropping
|
# AutoFlip: Saliency-aware Video Cropping
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layout: default
|
layout: default
|
||||||
title: Box Tracking
|
title: Box Tracking
|
||||||
parent: Solutions
|
parent: Solutions
|
||||||
nav_order: 6
|
nav_order: 8
|
||||||
---
|
---
|
||||||
|
|
||||||
# MediaPipe Box Tracking
|
# MediaPipe Box Tracking
|
||||||
|
|||||||
@@ -107,4 +107,4 @@ to cross-compile and run MediaPipe examples on the
|
|||||||
[TFLite model quantized for EdgeTPU/Coral](https://github.com/google/mediapipe/tree/master/mediapipe/examples/coral/models/face-detector-quantized_edgetpu.tflite)
|
[TFLite model quantized for EdgeTPU/Coral](https://github.com/google/mediapipe/tree/master/mediapipe/examples/coral/models/face-detector-quantized_edgetpu.tflite)
|
||||||
* For back-facing camera:
|
* For back-facing camera:
|
||||||
[TFLite model ](https://github.com/google/mediapipe/tree/master/mediapipe/models/face_detection_back.tflite)
|
[TFLite model ](https://github.com/google/mediapipe/tree/master/mediapipe/models/face_detection_back.tflite)
|
||||||
* [Model card](https://drive.google.com/file/d/1f39lSzU5Oq-j_OXgS67KfN5wNsoeAZ4V/view)
|
* [Model card](https://mediapipe.page.link/blazeface-mc)
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ Tip: Maximum number of faces to detect/process is set to 1 by default. To change
|
|||||||
it, for Android modify `NUM_FACES` in
|
it, for Android modify `NUM_FACES` in
|
||||||
[MainActivity.java](https://github.com/google/mediapipe/tree/master/mediapipe/examples/android/src/java/com/google/mediapipe/apps/facemeshgpu/MainActivity.java),
|
[MainActivity.java](https://github.com/google/mediapipe/tree/master/mediapipe/examples/android/src/java/com/google/mediapipe/apps/facemeshgpu/MainActivity.java),
|
||||||
and for iOS modify `kNumFaces` in
|
and for iOS modify `kNumFaces` in
|
||||||
[ViewController.mm](https://github.com/google/mediapipe/tree/master/mediapipe/examples/ios/facemeshgpu/ViewController.mm).
|
[FaceMeshGpuViewController.mm](https://github.com/google/mediapipe/tree/master/mediapipe/examples/ios/facemeshgpu/FaceMeshGpuViewController.mm).
|
||||||
|
|
||||||
### Desktop
|
### Desktop
|
||||||
|
|
||||||
@@ -153,8 +153,8 @@ it, in the graph file modify the option of `ConstantSidePacketCalculator`.
|
|||||||
[Real-time Facial Surface Geometry from Monocular Video on Mobile GPUs](https://arxiv.org/abs/1907.06724)
|
[Real-time Facial Surface Geometry from Monocular Video on Mobile GPUs](https://arxiv.org/abs/1907.06724)
|
||||||
([poster](https://docs.google.com/presentation/d/1-LWwOMO9TzEVdrZ1CS1ndJzciRHfYDJfbSxH_ke_JRg/present?slide=id.g5986dd4b4c_4_212))
|
([poster](https://docs.google.com/presentation/d/1-LWwOMO9TzEVdrZ1CS1ndJzciRHfYDJfbSxH_ke_JRg/present?slide=id.g5986dd4b4c_4_212))
|
||||||
* Face detection model:
|
* Face detection model:
|
||||||
[TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/models/face_detection_front.tflite)
|
[TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/modules/face_detection/face_detection_front.tflite)
|
||||||
* Face landmark mode:
|
* Face landmark model:
|
||||||
[TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/models/face_landmark.tflite),
|
[TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/modules/face_landmark/face_landmark.tflite),
|
||||||
[TF.js model](https://tfhub.dev/mediapipe/facemesh/1)
|
[TF.js model](https://tfhub.dev/mediapipe/facemesh/1)
|
||||||
* [Model card](https://drive.google.com/file/d/1VFC_wIpw4O7xBOiTgUldl79d9LA-LsnA/view)
|
* [Model card](https://mediapipe.page.link/facemesh-mc)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layout: default
|
layout: default
|
||||||
title: Hair Segmentation
|
title: Hair Segmentation
|
||||||
parent: Solutions
|
parent: Solutions
|
||||||
nav_order: 4
|
nav_order: 6
|
||||||
---
|
---
|
||||||
|
|
||||||
# MediaPipe Hair Segmentation
|
# MediaPipe Hair Segmentation
|
||||||
@@ -55,4 +55,4 @@ Please refer to [these instructions](../index.md#mediapipe-on-the-web).
|
|||||||
([presentation](https://drive.google.com/file/d/1C8WYlWdDRNtU1_pYBvkkG5Z5wqYqf0yj/view))
|
([presentation](https://drive.google.com/file/d/1C8WYlWdDRNtU1_pYBvkkG5Z5wqYqf0yj/view))
|
||||||
([supplementary video](https://drive.google.com/file/d/1LPtM99Ch2ogyXYbDNpEqnUfhFq0TfLuf/view))
|
([supplementary video](https://drive.google.com/file/d/1LPtM99Ch2ogyXYbDNpEqnUfhFq0TfLuf/view))
|
||||||
* [TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/models/hair_segmentation.tflite)
|
* [TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/models/hair_segmentation.tflite)
|
||||||
* [Model card](https://drive.google.com/file/d/1lPwJ8BD_-3UUor4LayQ0xpa_RIC_hoRh/view)
|
* [Model card](https://mediapipe.page.link/hairsegmentation-mc)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layout: default
|
layout: default
|
||||||
title: Hands
|
title: Hands
|
||||||
parent: Solutions
|
parent: Solutions
|
||||||
nav_order: 3
|
nav_order: 4
|
||||||
---
|
---
|
||||||
|
|
||||||
# MediaPipe Hands
|
# MediaPipe Hands
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: Iris
|
||||||
|
parent: Solutions
|
||||||
|
nav_order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# MediaPipe Iris
|
||||||
|
{: .no_toc }
|
||||||
|
|
||||||
|
1. TOC
|
||||||
|
{:toc}
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
A wide range of real-world applications, including computational photography
|
||||||
|
(glint reflection) and augmented reality effects (virtual avatars) rely on
|
||||||
|
accurately tracking the iris within an eye. This is a challenging task to solve
|
||||||
|
on mobile devices, due to the limited computing resources, variable light
|
||||||
|
conditions and the presence of occlusions, such as hair or people squinting.
|
||||||
|
Iris tracking can also be utilized to determine the metric distance of the
|
||||||
|
camera to the user. This can improve a variety of use cases, ranging from
|
||||||
|
virtual try-on of properly sized glasses and hats to accessibility features that
|
||||||
|
adopt the font size depending on the viewer’s distance. Often, sophisticated
|
||||||
|
specialized hardware is employed to compute the metric distance, limiting the
|
||||||
|
range of devices on which the solution could be applied.
|
||||||
|
|
||||||
|
MediaPipe Iris is a ML solution for accurate iris estimation, able to track
|
||||||
|
landmarks involving the iris, pupil and the eye contours using a single RGB
|
||||||
|
camera, in real-time, without the need for specialized hardware. Through use of
|
||||||
|
iris landmarks, the solution is also able to determine the metric distance
|
||||||
|
between the subject and the camera with relative error less than 10%. Note that
|
||||||
|
iris tracking does not infer the location at which people are looking, nor does
|
||||||
|
it provide any form of identity recognition. With the cross-platfrom capability
|
||||||
|
of the MediaPipe framework, MediaPipe Iris can run on most modern
|
||||||
|
[mobile phones](#mobile), [desktops/laptops](#desktop) and even on the
|
||||||
|
[web](#web).
|
||||||
|
|
||||||
|
 |
|
||||||
|
:------------------------------------------------------------------------: |
|
||||||
|
*Fig 1. Example of MediaPipe Iris: eyelid (red) and iris (blue) contours.* |
|
||||||
|
|
||||||
|
## ML Pipeline
|
||||||
|
|
||||||
|
The first step in the pipeline leverages [MediaPipe Face Mesh](./face_mesh.md),
|
||||||
|
which generates a mesh of the approximate face geometry. From this mesh, we
|
||||||
|
isolate the eye region in the original image for use in the subsequent iris
|
||||||
|
tracking step.
|
||||||
|
|
||||||
|
The pipeline is implemented as a MediaPipe
|
||||||
|
[graph](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/iris_tracking/iris_tracking_gpu.pbtxt)
|
||||||
|
that uses a
|
||||||
|
[face landmark subgraph](https://github.com/google/mediapipe/tree/master/mediapipe/modules/face_landmark/face_landmark_front_gpu.pbtxt)
|
||||||
|
from the
|
||||||
|
[face landmark module](https://github.com/google/mediapipe/tree/master/mediapipe/modules/face_landmark),
|
||||||
|
an
|
||||||
|
[iris landmark subgraph](https://github.com/google/mediapipe/tree/master/mediapipe/modules/iris_tracking/iris_landmark_left_and_right_gpu.pbtxt)
|
||||||
|
from the
|
||||||
|
[iris landmark module](https://github.com/google/mediapipe/tree/master/mediapipe/modules/iris_landmark),
|
||||||
|
and renders using a dedicated
|
||||||
|
[iris-and-depth renderer subgraph](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/iris_tracking/subgraphs/iris_and_depth_renderer_gpu.pbtxt).
|
||||||
|
The
|
||||||
|
[face landmark subgraph](https://github.com/google/mediapipe/tree/master/mediapipe/modules/face_landmark/face_landmark_front_gpu.pbtxt)
|
||||||
|
internally uses a
|
||||||
|
[face detection subgraph](https://github.com/google/mediapipe/tree/master/mediapipe/modules/face_detection/face_detection_front_gpu.pbtxt)
|
||||||
|
from the
|
||||||
|
[face detection module](https://github.com/google/mediapipe/tree/master/mediapipe/modules/face_detection).
|
||||||
|
|
||||||
|
Note: To visualize a graph, copy the graph and paste it into
|
||||||
|
[MediaPipe Visualizer](https://viz.mediapipe.dev/). For more information on how
|
||||||
|
to visualize its associated subgraphs, please see
|
||||||
|
[visualizer documentation](../tools/visualizer.md).
|
||||||
|
|
||||||
|
## Models
|
||||||
|
|
||||||
|
### Face Detection Model
|
||||||
|
|
||||||
|
The face detector is the same [BlazeFace](https://arxiv.org/abs/1907.05047)
|
||||||
|
model used in [MediaPipe Face Detection](./face_detection.md).
|
||||||
|
|
||||||
|
### Face Landmark Model
|
||||||
|
|
||||||
|
The face landmark model is the same as in [MediaPipe Face Mesh](./face_mesh.md).
|
||||||
|
You can also find more details in this
|
||||||
|
[paper](https://arxiv.org/abs/1907.06724).
|
||||||
|
|
||||||
|
### Iris Landmark Model
|
||||||
|
|
||||||
|
The iris model takes an image patch of the eye region and estimates both the eye
|
||||||
|
landmarks (along the eyelid) and iris landmarks (along ths iris contour). You
|
||||||
|
can find more details in this [paper](https://arxiv.org/abs/2006.11341).
|
||||||
|
|
||||||
|
 |
|
||||||
|
:----------------------------------------------------------------------------------------------------: |
|
||||||
|
*Fig 2. Eye landmarks (red) and iris landmarks (green).* |
|
||||||
|
|
||||||
|
## Depth-from-Iris
|
||||||
|
|
||||||
|
MediaPipe Iris is able to determine the metric distance of a subject to the
|
||||||
|
camera with less than 10% error, without requiring any specialized hardware.
|
||||||
|
This is done by relying on the fact that the horizontal iris diameter of the
|
||||||
|
human eye remains roughly constant at 11.7±0.5 mm across a wide population,
|
||||||
|
along with some simple geometric arguments. For more details please refer to our
|
||||||
|
[Google AI Blog post](https://ai.googleblog.com/2020/08/mediapipe-iris-real-time-iris-tracking.html).
|
||||||
|
|
||||||
|
 |
|
||||||
|
:--------------------------------------------------------------------------------------------: |
|
||||||
|
*Fig 3. (Left) MediaPipe Iris predicting metric distance in cm on a Pixel 2 from iris tracking without use of a depth sensor. (Right) Ground-truth depth.* |
|
||||||
|
|
||||||
|
## Example Apps
|
||||||
|
|
||||||
|
Please first see general instructions for
|
||||||
|
[Android](../getting_started/building_examples.md#android),
|
||||||
|
[iOS](../getting_started/building_examples.md#ios) and
|
||||||
|
[desktop](../getting_started/building_examples.md#desktop) on how to build
|
||||||
|
MediaPipe examples.
|
||||||
|
|
||||||
|
Note: To visualize a graph, copy the graph and paste it into
|
||||||
|
[MediaPipe Visualizer](https://viz.mediapipe.dev/). For more information on how
|
||||||
|
to visualize its associated subgraphs, please see
|
||||||
|
[visualizer documentation](../tools/visualizer.md).
|
||||||
|
|
||||||
|
### Mobile
|
||||||
|
|
||||||
|
* Graph:
|
||||||
|
[`mediapipe/graphs/iris_tracking/iris_tracking_gpu.pbtxt`](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/iris_tracking/iris_tracking_gpu.pbtxt)
|
||||||
|
* Android target:
|
||||||
|
[(or download prebuilt ARM64 APK)](https://drive.google.com/file/d/1cywcNtqk764TlZf1lvSTV4F3NGB2aL1R/view?usp=sharing)
|
||||||
|
[`mediapipe/examples/android/src/java/com/google/mediapipe/apps/iristrackinggpu:iristrackinggpu`](https://github.com/google/mediapipe/tree/master/mediapipe/examples/android/src/java/com/google/mediapipe/apps/iristrackinggpu/BUILD)
|
||||||
|
* iOS target:
|
||||||
|
[`mediapipe/examples/ios/iristrackinggpu:IrisTrackingGpuApp`](http:/mediapipe/examples/ios/iristrackinggpu/BUILD)
|
||||||
|
|
||||||
|
### Desktop
|
||||||
|
|
||||||
|
#### Live Camera Input
|
||||||
|
|
||||||
|
Please first see general instructions for
|
||||||
|
[desktop](../getting_started/building_examples.md#desktop) on how to build
|
||||||
|
MediaPipe examples.
|
||||||
|
|
||||||
|
* Running on CPU
|
||||||
|
* Graph:
|
||||||
|
[`mediapipe/graphs/iris_tracking/iris_tracking_cpu.pbtxt`](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/iris_tracking/iris_tracking_cpu.pbtxt)
|
||||||
|
* Target:
|
||||||
|
[`mediapipe/examples/desktop/iris_tracking:iris_tracking_cpu`](https://github.com/google/mediapipe/tree/master/mediapipe/examples/desktop/iris_tracking/BUILD)
|
||||||
|
* Running on GPU
|
||||||
|
* Graph:
|
||||||
|
[`mediapipe/graphs/iris_tracking/iris_tracking_gpu.pbtxt`](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/iris_tracking/iris_tracking_gpu.pbtxt)
|
||||||
|
* Target:
|
||||||
|
[`mediapipe/examples/desktop/iris_tracking:iris_tracking_gpu`](https://github.com/google/mediapipe/tree/master/mediapipe/examples/desktop/iris_tracking/BUILD)
|
||||||
|
|
||||||
|
#### Video File Input
|
||||||
|
|
||||||
|
1. To build the application, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bazel build -c opt --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/examples/desktop/iris_tracking:iris_tracking_cpu_video_input
|
||||||
|
```
|
||||||
|
|
||||||
|
2. To run the application, replace `<input video path>` and `<output video
|
||||||
|
path>` in the command below with your own paths:
|
||||||
|
|
||||||
|
```
|
||||||
|
bazel-bin/mediapipe/examples/desktop/iris_tracking/iris_tracking_cpu_video_input \
|
||||||
|
--calculator_graph_config_file=mediapipe/graphs/iris_tracking/iris_tracking_cpu_video_input.pbtxt \
|
||||||
|
--input_side_packets=input_video_path=<input video path>,output_video_path=<output video path>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Single-image Depth Estimation
|
||||||
|
|
||||||
|
1. To build the application, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bazel build -c opt --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/examples/desktop/iris_tracking:iris_depth_from_image_desktop
|
||||||
|
```
|
||||||
|
|
||||||
|
2. To run the application, replace `<input image path>` and `<output image
|
||||||
|
path>` in the command below with your own paths:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GLOG_logtostderr=1 bazel-bin/mediapipe/examples/desktop/iris_tracking/iris_depth_from_image_desktop \
|
||||||
|
--input_image_path=<input image path> --output_image_path=<output image path>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web
|
||||||
|
|
||||||
|
Please refer to [these instructions](../index.md#mediapipe-on-the-web).
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
* Google AI Blog:
|
||||||
|
[MediaPipe Iris: Real-time Eye Tracking and Depth Estimation](https://ai.googleblog.com/2020/08/mediapipe-iris-real-time-iris-tracking.html)
|
||||||
|
* Paper:
|
||||||
|
[Real-time Pupil Tracking from Monocular Video for Digital Puppetry](https://arxiv.org/abs/2006.11341)
|
||||||
|
([presentation](https://youtu.be/cIhXkiiapQI))
|
||||||
|
* Face detection model:
|
||||||
|
[TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/modules/face_detection/face_detection_front.tflite)
|
||||||
|
* Face landmark model:
|
||||||
|
[TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/modules/face_landmark/face_landmark.tflite),
|
||||||
|
[TF.js model](https://tfhub.dev/mediapipe/facemesh/1)
|
||||||
|
* Iris landmark model:
|
||||||
|
[TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/modules/iris_landmark/iris_landmark.tflite)
|
||||||
|
* [Model card](https://mediapipe.page.link/iris-mc)
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
layout: default
|
layout: default
|
||||||
title: KNIFT (Template-based Feature Matching)
|
title: KNIFT (Template-based Feature Matching)
|
||||||
parent: Solutions
|
parent: Solutions
|
||||||
nav_order: 8
|
nav_order: 10
|
||||||
---
|
---
|
||||||
|
|
||||||
# MediaPipe KNIFT
|
# MediaPipe KNIFT
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layout: default
|
layout: default
|
||||||
title: Dataset Preparation with MediaSequence
|
title: Dataset Preparation with MediaSequence
|
||||||
parent: Solutions
|
parent: Solutions
|
||||||
nav_order: 10
|
nav_order: 12
|
||||||
---
|
---
|
||||||
|
|
||||||
# Dataset Preparation with MediaSequence
|
# Dataset Preparation with MediaSequence
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layout: default
|
layout: default
|
||||||
title: Object Detection
|
title: Object Detection
|
||||||
parent: Solutions
|
parent: Solutions
|
||||||
nav_order: 5
|
nav_order: 7
|
||||||
---
|
---
|
||||||
|
|
||||||
# MediaPipe Object Detection
|
# MediaPipe Object Detection
|
||||||
@@ -95,8 +95,8 @@ Please first see general instructions for
|
|||||||
|
|
||||||
```
|
```
|
||||||
GLOG_logtostderr=1 bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tflite \
|
GLOG_logtostderr=1 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=<input video path>,output_video_path=<output video path>
|
--input_side_packets=input_video_path=<input video path>,output_video_path=<output video path>
|
||||||
```
|
```
|
||||||
|
|
||||||
* With a TensorFlow Model
|
* With a TensorFlow Model
|
||||||
@@ -131,8 +131,8 @@ Please first see general instructions for
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
GLOG_logtostderr=1 bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tflite \
|
GLOG_logtostderr=1 bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tflite \
|
||||||
--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=<input video path>,output_video_path=<output video path>
|
--input_side_packets=input_video_path=<input video path>,output_video_path=<output video path>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Coral
|
### Coral
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layout: default
|
layout: default
|
||||||
title: Objectron (3D Object Detection)
|
title: Objectron (3D Object Detection)
|
||||||
parent: Solutions
|
parent: Solutions
|
||||||
nav_order: 7
|
nav_order: 9
|
||||||
---
|
---
|
||||||
|
|
||||||
# MediaPipe Objectron
|
# MediaPipe Objectron
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: Pose
|
||||||
|
parent: Solutions
|
||||||
|
nav_order: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
# MediaPipe Pose
|
||||||
|
{: .no_toc }
|
||||||
|
|
||||||
|
1. TOC
|
||||||
|
{:toc}
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Human pose estimation from video plays a critical role in various applications
|
||||||
|
such as quantifying physical exercises, sign language recognition, and full-body
|
||||||
|
gesture control. For example, it can form the basis for yoga, dance, and fitness
|
||||||
|
applications. It can also enable the overlay of digital content and information
|
||||||
|
on top of the physical world in augmented reality.
|
||||||
|
|
||||||
|
MediaPipe Pose is a ML solution for high-fidelity upper-body pose tracking,
|
||||||
|
inferring 25 2D upper-body landmarks from RGB video frames utilizing our
|
||||||
|
[BlazePose](https://mediapipe.page.link/blazepose-blog) research. Current
|
||||||
|
state-of-the-art approaches rely primarily on powerful desktop environments for
|
||||||
|
inference, whereas our method achieves real-time performance on most modern
|
||||||
|
[mobile phones](#mobile), [desktops/laptops](#desktop), in [python](#python) and
|
||||||
|
even on the [web](#web). A variant of MediaPipe Pose that performs full-body
|
||||||
|
pose tracking on mobile phones will be included in an upcoming release of
|
||||||
|
[ML Kit](https://developers.google.com/ml-kit/early-access/pose-detection).
|
||||||
|
|
||||||
|
 |
|
||||||
|
:--------------------------------------------------------------------------------------------: |
|
||||||
|
*Fig 1. Example of MediaPipe Pose for upper-body pose tracking.* |
|
||||||
|
|
||||||
|
## ML Pipeline
|
||||||
|
|
||||||
|
The solution utilizes a two-step detector-tracker ML pipeline, proven to be
|
||||||
|
effective in our [MediaPipe Hands](./hands.md) and
|
||||||
|
[MediaPipe Face Mesh](./face_mesh.md) solutions. Using a detector, the pipeline
|
||||||
|
first locates the pose region-of-interest (ROI) within the frame. The tracker
|
||||||
|
subsequently predicts the pose landmarks within the ROI using the ROI-cropped
|
||||||
|
frame as input. Note that for video use cases the detector is invoked only as
|
||||||
|
needed, i.e., for the very first frame and when the tracker could no longer
|
||||||
|
identify body pose presence in the previous frame. For other frames the pipeline
|
||||||
|
simply derives the ROI from the previous frame’s pose landmarks.
|
||||||
|
|
||||||
|
The pipeline is implemented as a MediaPipe
|
||||||
|
[graph](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/pose_tracking/upper_body_pose_tracking_gpu.pbtxt)
|
||||||
|
that uses a
|
||||||
|
[pose landmark subgraph](https://github.com/google/mediapipe/tree/master/mediapipe/modules/pose_landmark/pose_landmark_upper_body_gpu.pbtxt)
|
||||||
|
from the
|
||||||
|
[pose landmark module](https://github.com/google/mediapipe/tree/master/mediapipe/modules/pose_landmark)
|
||||||
|
and renders using a dedicated
|
||||||
|
[upper-body pose renderer subgraph](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/pose_tracking/subgraphs/upper_body_pose_renderer_gpu.pbtxt).
|
||||||
|
The
|
||||||
|
[pose landmark subgraph](https://github.com/google/mediapipe/tree/master/mediapipe/modules/pose_landmark/pose_landmark_upper_body_gpu.pbtxt)
|
||||||
|
internally uses a
|
||||||
|
[pose detection subgraph](https://github.com/google/mediapipe/tree/master/mediapipe/modules/pose_detection/pose_detection_gpu.pbtxt)
|
||||||
|
from the
|
||||||
|
[pose detection module](https://github.com/google/mediapipe/tree/master/mediapipe/modules/pose_detection).
|
||||||
|
|
||||||
|
Note: To visualize a graph, copy the graph and paste it into
|
||||||
|
[MediaPipe Visualizer](https://viz.mediapipe.dev/). For more information on how
|
||||||
|
to visualize its associated subgraphs, please see
|
||||||
|
[visualizer documentation](../tools/visualizer.md).
|
||||||
|
|
||||||
|
## Models
|
||||||
|
|
||||||
|
### Pose Detection Model (BlazePose Detector)
|
||||||
|
|
||||||
|
The detector is inspired by our own lightweight
|
||||||
|
[BlazeFace](https://arxiv.org/abs/1907.05047) model, used in
|
||||||
|
[MediaPipe Face Detection](./face_detection.md), as a proxy for a person
|
||||||
|
detector. It explicitly predicts two additional virtual keypoints that firmly
|
||||||
|
describe the human body center, rotation and scale as a circle. Inspired by
|
||||||
|
[Leonardo’s Vitruvian man](https://en.wikipedia.org/wiki/Vitruvian_Man), we
|
||||||
|
predict the midpoint of a person's hips, the radius of a circle circumscribing
|
||||||
|
the whole person, and the incline angle of the line connecting the shoulder and
|
||||||
|
hip midpoints.
|
||||||
|
|
||||||
|
 |
|
||||||
|
:----------------------------------------------------------------------------------------------------: |
|
||||||
|
*Fig 2. Vitruvian man aligned via two virtual keypoints predicted by BlazePose detector in addition to the face bounding box.* |
|
||||||
|
|
||||||
|
### Pose Landmark Model (BlazePose Tracker)
|
||||||
|
|
||||||
|
The landmark model currently included in MediaPipe Pose predicts the location of
|
||||||
|
25 upper-body landmarks (see figure below), with three degrees of freedom each
|
||||||
|
(x, y location and visibility), plus two virtual alignment keypoints. It shares
|
||||||
|
the same architecture as the full-body version that predicts 33 landmarks,
|
||||||
|
described in more detail in the
|
||||||
|
[BlazePose Google AI Blog](https://mediapipe.page.link/blazepose-blog) and in
|
||||||
|
this [paper](https://arxiv.org/abs/2006.10204).
|
||||||
|
|
||||||
|
 |
|
||||||
|
:------------------------------------------------------------------------------------------------: |
|
||||||
|
*Fig 3. 25 upper-body pose landmarks.* |
|
||||||
|
|
||||||
|
## Example Apps
|
||||||
|
|
||||||
|
Please first see general instructions for
|
||||||
|
[Android](../getting_started/building_examples.md#android),
|
||||||
|
[iOS](../getting_started/building_examples.md#ios),
|
||||||
|
[desktop](../getting_started/building_examples.md#desktop) and
|
||||||
|
[Python](../getting_started/building_examples.md#python) on how to build
|
||||||
|
MediaPipe examples.
|
||||||
|
|
||||||
|
Note: To visualize a graph, copy the graph and paste it into
|
||||||
|
[MediaPipe Visualizer](https://viz.mediapipe.dev/). For more information on how
|
||||||
|
to visualize its associated subgraphs, please see
|
||||||
|
[visualizer documentation](../tools/visualizer.md).
|
||||||
|
|
||||||
|
### Mobile
|
||||||
|
|
||||||
|
* Graph:
|
||||||
|
[`mediapipe/graphs/pose_tracking/upper_body_pose_tracking_gpu.pbtxt`](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/pose_tracking/upper_body_pose_tracking_gpu.pbtxt)
|
||||||
|
* Android target:
|
||||||
|
[(or download prebuilt ARM64 APK)](https://drive.google.com/file/d/1uKc6T7KSuA0Mlq2URi5YookHu0U3yoh_/view?usp=sharing)
|
||||||
|
[`mediapipe/examples/android/src/java/com/google/mediapipe/apps/upperbodyposetrackinggpu:upperbodyposetrackinggpu`](https://github.com/google/mediapipe/tree/master/mediapipe/examples/android/src/java/com/google/mediapipe/apps/upperbodyposetrackinggpu/BUILD)
|
||||||
|
* iOS target:
|
||||||
|
[`mediapipe/examples/ios/upperbodyposetrackinggpu:UpperBodyPoseTrackingGpuApp`](http:/mediapipe/examples/ios/upperbodyposetrackinggpu/BUILD)
|
||||||
|
|
||||||
|
### Desktop
|
||||||
|
|
||||||
|
Please first see general instructions for
|
||||||
|
[desktop](../getting_started/building_examples.md#desktop) on how to build
|
||||||
|
MediaPipe examples.
|
||||||
|
|
||||||
|
* Running on CPU
|
||||||
|
* Graph:
|
||||||
|
[`mediapipe/graphs/pose_tracking/upper_body_pose_tracking_cpu.pbtxt`](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/pose_tracking/upper_body_pose_tracking_cpu.pbtxt)
|
||||||
|
* Target:
|
||||||
|
[`mediapipe/examples/desktop/upper_body_pose_tracking:upper_body_pose_tracking_cpu`](https://github.com/google/mediapipe/tree/master/mediapipe/examples/desktop/upper_body_pose_tracking/BUILD)
|
||||||
|
* Running on GPU
|
||||||
|
* Graph:
|
||||||
|
[`mediapipe/graphs/pose_tracking/upper_body_pose_tracking_gpu.pbtxt`](https://github.com/google/mediapipe/tree/master/mediapipe/graphs/pose_tracking/upper_body_pose_tracking_gpu.pbtxt)
|
||||||
|
* Target:
|
||||||
|
[`mediapipe/examples/desktop/upper_body_pose_tracking:upper_body_pose_tracking_gpu`](https://github.com/google/mediapipe/tree/master/mediapipe/examples/desktop/upper_body_pose_tracking/BUILD)
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
Please first see general instructions for
|
||||||
|
[Python](../getting_started/building_examples.md#python) examples.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
(mp_env)$ python3
|
||||||
|
>>> import mediapipe as mp
|
||||||
|
>>> pose_tracker = mp.examples.UpperBodyPoseTracker()
|
||||||
|
|
||||||
|
# For image input
|
||||||
|
>>> pose_landmarks, _ = pose_tracker.run(input_file='/path/to/input/file', output_file='/path/to/output/file')
|
||||||
|
>>> pose_landmarks, annotated_image = pose_tracker.run(input_file='/path/to/file')
|
||||||
|
|
||||||
|
# For live camera input
|
||||||
|
# (Press Esc within the output image window to stop the run or let it self terminate after 30 seconds.)
|
||||||
|
>>> pose_tracker.run_live()
|
||||||
|
|
||||||
|
# Close the tracker.
|
||||||
|
>>> pose_tracker.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web
|
||||||
|
|
||||||
|
Please refer to [these instructions](../index.md#mediapipe-on-the-web).
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
* Google AI Blog:
|
||||||
|
[BlazePose - On-device Real-time Body Pose Tracking](https://mediapipe.page.link/blazepose-blog)
|
||||||
|
* Paper:
|
||||||
|
[BlazePose: On-device Real-time Body Pose Tracking](https://arxiv.org/abs/2006.10204)
|
||||||
|
([presentation](https://youtu.be/YPpUOTRn5tA))
|
||||||
|
* Pose detection model:
|
||||||
|
[TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/modules/pose_detection/pose_detection.tflite)
|
||||||
|
* Upper-body pose landmark model:
|
||||||
|
[TFLite model](https://github.com/google/mediapipe/tree/master/mediapipe/modules/pose_landmark/pose_landmark_upper_body.tflite)
|
||||||
|
* [Model card](https://mediapipe.page.link/blazepose-mc)
|
||||||
@@ -14,18 +14,20 @@ has_toc: false
|
|||||||
---
|
---
|
||||||
|
|
||||||
<!-- []() in the first cell is needed to preserve table formatting in GitHub Pages. -->
|
<!-- []() in the first cell is needed to preserve table formatting in GitHub Pages. -->
|
||||||
<!-- Whenever this table is updated, paste a copy to ../index.md. -->
|
<!-- Whenever this table is updated, paste a copy to ../external_index.md. -->
|
||||||
|
|
||||||
[]() | Android | iOS | Desktop | Web | Coral
|
[]() | Android | iOS | Desktop | Python | Web | Coral
|
||||||
:---------------------------------------------------------------------------- | :-----: | :-: | :-----: | :-: | :---:
|
:---------------------------------------------------------------------------- | :-----: | :-: | :-----: | :----: | :-: | :---:
|
||||||
[Face Detection](https://google.github.io/mediapipe/solutions/face_detection) | ✅ | ✅ | ✅ | ✅ | ✅
|
[Face Detection](https://google.github.io/mediapipe/solutions/face_detection) | ✅ | ✅ | ✅ | | ✅ | ✅
|
||||||
[Face Mesh](https://google.github.io/mediapipe/solutions/face_mesh) | ✅ | ✅ | ✅ | |
|
[Face Mesh](https://google.github.io/mediapipe/solutions/face_mesh) | ✅ | ✅ | ✅ | | |
|
||||||
[Hands](https://google.github.io/mediapipe/solutions/hands) | ✅ | ✅ | ✅ | ✅ |
|
[Iris](https://google.github.io/mediapipe/solutions/iris) 🆕 | ✅ | ✅ | ✅ | | ✅ |
|
||||||
[Hair Segmentation](https://google.github.io/mediapipe/solutions/hair_segmentation) | ✅ | | ✅ | ✅ |
|
[Hands](https://google.github.io/mediapipe/solutions/hands) | ✅ | ✅ | ✅ | | ✅ |
|
||||||
[Object Detection](https://google.github.io/mediapipe/solutions/object_detection) | ✅ | ✅ | ✅ | | ✅
|
[Pose](https://google.github.io/mediapipe/solutions/pose) 🆕 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
[Box Tracking](https://google.github.io/mediapipe/solutions/box_tracking) | ✅ | ✅ | ✅ | |
|
[Hair Segmentation](https://google.github.io/mediapipe/solutions/hair_segmentation) | ✅ | | ✅ | | ✅ |
|
||||||
[Objectron](https://google.github.io/mediapipe/solutions/objectron) | ✅ | | | |
|
[Object Detection](https://google.github.io/mediapipe/solutions/object_detection) | ✅ | ✅ | ✅ | | | ✅
|
||||||
[KNIFT](https://google.github.io/mediapipe/solutions/knift) | ✅ | | | |
|
[Box Tracking](https://google.github.io/mediapipe/solutions/box_tracking) | ✅ | ✅ | ✅ | | |
|
||||||
[AutoFlip](https://google.github.io/mediapipe/solutions/autoflip) | | | ✅ | |
|
[Objectron](https://google.github.io/mediapipe/solutions/objectron) | ✅ | | | | |
|
||||||
[MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | |
|
[KNIFT](https://google.github.io/mediapipe/solutions/knift) | ✅ | | | | |
|
||||||
[YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | |
|
[AutoFlip](https://google.github.io/mediapipe/solutions/autoflip) | | | ✅ | | |
|
||||||
|
[MediaSequence](https://google.github.io/mediapipe/solutions/media_sequence) | | | ✅ | | |
|
||||||
|
[YouTube 8M](https://google.github.io/mediapipe/solutions/youtube_8m) | | | ✅ | | |
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
layout: default
|
layout: default
|
||||||
title: YouTube-8M Feature Extraction and Model Inference
|
title: YouTube-8M Feature Extraction and Model Inference
|
||||||
parent: Solutions
|
parent: Solutions
|
||||||
nav_order: 11
|
nav_order: 13
|
||||||
---
|
---
|
||||||
|
|
||||||
# YouTube-8M Feature Extraction and Model Inference
|
# YouTube-8M Feature Extraction and Model Inference
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ trace_log_margin_usec
|
|||||||
in trace log output. This margin allows time for events to be appended to
|
in trace log output. This margin allows time for events to be appended to
|
||||||
the TraceBuffer.
|
the TraceBuffer.
|
||||||
|
|
||||||
trace_log_duration_events
|
trace_log_instant_events
|
||||||
: False specifies an event for each calculator invocation. True specifies a
|
: False specifies an event for each calculator invocation. True specifies a
|
||||||
separate event for each start and finish time.
|
separate event for each start and finish time.
|
||||||
|
|
||||||
|
|||||||
@@ -3,27 +3,34 @@
|
|||||||
"/BUILD",
|
"/BUILD",
|
||||||
"mediapipe/BUILD",
|
"mediapipe/BUILD",
|
||||||
"mediapipe/objc/BUILD",
|
"mediapipe/objc/BUILD",
|
||||||
"mediapipe/examples/ios/BUILD",
|
"mediapipe/framework/BUILD",
|
||||||
"mediapipe/examples/ios/edgedetectiongpu/BUILD",
|
"mediapipe/gpu/BUILD",
|
||||||
|
"mediapipe/objc/testing/app/BUILD",
|
||||||
|
"mediapipe/examples/ios/common/BUILD",
|
||||||
|
"mediapipe/examples/ios/helloworld/BUILD",
|
||||||
"mediapipe/examples/ios/facedetectioncpu/BUILD",
|
"mediapipe/examples/ios/facedetectioncpu/BUILD",
|
||||||
"mediapipe/examples/ios/facedetectiongpu/BUILD",
|
"mediapipe/examples/ios/facedetectiongpu/BUILD",
|
||||||
"mediapipe/examples/ios/facemeshgpu/BUILD",
|
"mediapipe/examples/ios/facemeshgpu/BUILD",
|
||||||
"mediapipe/examples/ios/handdetectiongpu/BUILD",
|
"mediapipe/examples/ios/handdetectiongpu/BUILD",
|
||||||
"mediapipe/examples/ios/handtrackinggpu/BUILD",
|
"mediapipe/examples/ios/handtrackinggpu/BUILD",
|
||||||
|
"mediapipe/examples/ios/iristrackinggpu/BUILD",
|
||||||
"mediapipe/examples/ios/multihandtrackinggpu/BUILD",
|
"mediapipe/examples/ios/multihandtrackinggpu/BUILD",
|
||||||
"mediapipe/examples/ios/objectdetectioncpu/BUILD",
|
"mediapipe/examples/ios/objectdetectioncpu/BUILD",
|
||||||
"mediapipe/examples/ios/objectdetectiongpu/BUILD"
|
"mediapipe/examples/ios/objectdetectiongpu/BUILD",
|
||||||
|
"mediapipe/examples/ios/upperbodyposetrackinggpu/BUILD"
|
||||||
],
|
],
|
||||||
"buildTargets" : [
|
"buildTargets" : [
|
||||||
"//mediapipe/examples/ios/edgedetectiongpu:EdgeDetectionGpuApp",
|
"//mediapipe/examples/ios/helloworld:HelloWorldApp",
|
||||||
"//mediapipe/examples/ios/facedetectioncpu:FaceDetectionCpuApp",
|
"//mediapipe/examples/ios/facedetectioncpu:FaceDetectionCpuApp",
|
||||||
"//mediapipe/examples/ios/facedetectiongpu:FaceDetectionGpuApp",
|
"//mediapipe/examples/ios/facedetectiongpu:FaceDetectionGpuApp",
|
||||||
"//mediapipe/examples/ios/facemeshgpu:FaceMeshGpuApp",
|
"//mediapipe/examples/ios/facemeshgpu:FaceMeshGpuApp",
|
||||||
"//mediapipe/examples/ios/handdetectiongpu:HandDetectionGpuApp",
|
"//mediapipe/examples/ios/handdetectiongpu:HandDetectionGpuApp",
|
||||||
"//mediapipe/examples/ios/handtrackinggpu:HandTrackingGpuApp",
|
"//mediapipe/examples/ios/handtrackinggpu:HandTrackingGpuApp",
|
||||||
|
"//mediapipe/examples/ios/iristrackinggpu:IrisTrackingGpuApp",
|
||||||
"//mediapipe/examples/ios/multihandtrackinggpu:MultiHandTrackingGpuApp",
|
"//mediapipe/examples/ios/multihandtrackinggpu:MultiHandTrackingGpuApp",
|
||||||
"//mediapipe/examples/ios/objectdetectioncpu:ObjectDetectionCpuApp",
|
"//mediapipe/examples/ios/objectdetectioncpu:ObjectDetectionCpuApp",
|
||||||
"//mediapipe/examples/ios/objectdetectiongpu:ObjectDetectionGpuApp",
|
"//mediapipe/examples/ios/objectdetectiongpu:ObjectDetectionGpuApp",
|
||||||
|
"//mediapipe/examples/ios/upperbodyposetrackinggpu:UpperBodyPoseTrackingGpuApp",
|
||||||
"//mediapipe/objc:mediapipe_framework_ios"
|
"//mediapipe/objc:mediapipe_framework_ios"
|
||||||
],
|
],
|
||||||
"optionSet" : {
|
"optionSet" : {
|
||||||
@@ -78,22 +85,18 @@
|
|||||||
"mediapipe/calculators/util",
|
"mediapipe/calculators/util",
|
||||||
"mediapipe/examples",
|
"mediapipe/examples",
|
||||||
"mediapipe/examples/ios",
|
"mediapipe/examples/ios",
|
||||||
"mediapipe/examples/ios/edgedetectiongpu",
|
"mediapipe/examples/ios/common",
|
||||||
"mediapipe/examples/ios/edgedetectiongpu/Base.lproj",
|
"mediapipe/examples/ios/common/Base.lproj",
|
||||||
|
"mediapipe/examples/ios/helloworld",
|
||||||
"mediapipe/examples/ios/facedetectioncpu",
|
"mediapipe/examples/ios/facedetectioncpu",
|
||||||
"mediapipe/examples/ios/facedetectioncpu/Base.lproj",
|
|
||||||
"mediapipe/examples/ios/facedetectiongpu",
|
"mediapipe/examples/ios/facedetectiongpu",
|
||||||
"mediapipe/examples/ios/facedetectiongpu/Base.lproj",
|
|
||||||
"mediapipe/examples/ios/handdetectiongpu",
|
"mediapipe/examples/ios/handdetectiongpu",
|
||||||
"mediapipe/examples/ios/handdetectiongpu/Base.lproj",
|
|
||||||
"mediapipe/examples/ios/handtrackinggpu",
|
"mediapipe/examples/ios/handtrackinggpu",
|
||||||
"mediapipe/examples/ios/handtrackinggpu/Base.lproj",
|
"mediapipe/examples/ios/iristrackinggpu",
|
||||||
"mediapipe/examples/ios/multihandtrackinggpu",
|
"mediapipe/examples/ios/multihandtrackinggpu",
|
||||||
"mediapipe/examples/ios/multihandtrackinggpu/Base.lproj",
|
|
||||||
"mediapipe/examples/ios/objectdetectioncpu",
|
"mediapipe/examples/ios/objectdetectioncpu",
|
||||||
"mediapipe/examples/ios/objectdetectioncpu/Base.lproj",
|
|
||||||
"mediapipe/examples/ios/objectdetectiongpu",
|
"mediapipe/examples/ios/objectdetectiongpu",
|
||||||
"mediapipe/examples/ios/objectdetectiongpu/Base.lproj",
|
"mediapipe/examples/ios/upperbodyposetrackinggpu",
|
||||||
"mediapipe/framework",
|
"mediapipe/framework",
|
||||||
"mediapipe/framework/deps",
|
"mediapipe/framework/deps",
|
||||||
"mediapipe/framework/formats",
|
"mediapipe/framework/formats",
|
||||||
@@ -109,7 +112,9 @@
|
|||||||
"mediapipe/graphs/face_detection",
|
"mediapipe/graphs/face_detection",
|
||||||
"mediapipe/graphs/hand_tracking",
|
"mediapipe/graphs/hand_tracking",
|
||||||
"mediapipe/graphs/object_detection",
|
"mediapipe/graphs/object_detection",
|
||||||
|
"mediapipe/graphs/pose_tracking",
|
||||||
"mediapipe/models",
|
"mediapipe/models",
|
||||||
|
"mediapipe/modules",
|
||||||
"mediapipe/objc",
|
"mediapipe/objc",
|
||||||
"mediapipe/util",
|
"mediapipe/util",
|
||||||
"mediapipe/util/android",
|
"mediapipe/util/android",
|
||||||
|
|||||||
@@ -11,15 +11,16 @@
|
|||||||
"mediapipe",
|
"mediapipe",
|
||||||
"mediapipe/objc",
|
"mediapipe/objc",
|
||||||
"mediapipe/examples/ios",
|
"mediapipe/examples/ios",
|
||||||
"mediapipe/examples/ios/edgedetectiongpu",
|
|
||||||
"mediapipe/examples/ios/facedetectioncpu",
|
"mediapipe/examples/ios/facedetectioncpu",
|
||||||
"mediapipe/examples/ios/facedetectiongpu",
|
"mediapipe/examples/ios/facedetectiongpu",
|
||||||
"mediapipe/examples/ios/facemeshgpu",
|
"mediapipe/examples/ios/facemeshgpu",
|
||||||
"mediapipe/examples/ios/handdetectiongpu",
|
"mediapipe/examples/ios/handdetectiongpu",
|
||||||
"mediapipe/examples/ios/handtrackinggpu",
|
"mediapipe/examples/ios/handtrackinggpu",
|
||||||
|
"mediapipe/examples/ios/iristrackinggpu",
|
||||||
"mediapipe/examples/ios/multihandtrackinggpu",
|
"mediapipe/examples/ios/multihandtrackinggpu",
|
||||||
"mediapipe/examples/ios/objectdetectioncpu",
|
"mediapipe/examples/ios/objectdetectioncpu",
|
||||||
"mediapipe/examples/ios/objectdetectiongpu"
|
"mediapipe/examples/ios/objectdetectiongpu",
|
||||||
|
"mediapipe/examples/ios/upperbodyposetrackinggpu"
|
||||||
],
|
],
|
||||||
"projectName" : "Mediapipe",
|
"projectName" : "Mediapipe",
|
||||||
"workspaceRoot" : "../.."
|
"workspaceRoot" : "../.."
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Copyright 2019 The MediaPipe Authors.
|
"""Copyright 2019 - 2020 The MediaPipe Authors.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -12,3 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import mediapipe.examples.python as examples
|
||||||
|
from mediapipe.python import *
|
||||||
|
import mediapipe.util as util
|
||||||
|
|||||||
@@ -316,6 +316,37 @@ cc_library(
|
|||||||
alwayslink = 1,
|
alwayslink = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "concatenate_normalized_landmark_list_calculator",
|
||||||
|
srcs = ["concatenate_normalized_landmark_list_calculator.cc"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
":concatenate_vector_calculator_cc_proto",
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework/formats:landmark_cc_proto",
|
||||||
|
"//mediapipe/framework/port:ret_check",
|
||||||
|
"//mediapipe/framework/port:status",
|
||||||
|
],
|
||||||
|
alwayslink = 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "concatenate_normalized_landmark_list_calculator_test",
|
||||||
|
srcs = ["concatenate_normalized_landmark_list_calculator_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":concatenate_normalized_landmark_list_calculator",
|
||||||
|
":concatenate_vector_calculator_cc_proto",
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework:calculator_runner",
|
||||||
|
"//mediapipe/framework:timestamp",
|
||||||
|
"//mediapipe/framework/formats:landmark_cc_proto",
|
||||||
|
"//mediapipe/framework/port:gtest_main",
|
||||||
|
"//mediapipe/framework/port:parse_text_proto",
|
||||||
|
"//mediapipe/framework/port:status",
|
||||||
|
"@com_google_absl//absl/strings",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
name = "concatenate_vector_calculator_test",
|
name = "concatenate_vector_calculator_test",
|
||||||
srcs = ["concatenate_vector_calculator_test.cc"],
|
srcs = ["concatenate_vector_calculator_test.cc"],
|
||||||
@@ -575,6 +606,35 @@ cc_library(
|
|||||||
alwayslink = 1,
|
alwayslink = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "packet_presence_calculator",
|
||||||
|
srcs = ["packet_presence_calculator.cc"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework:packet",
|
||||||
|
"//mediapipe/framework:timestamp",
|
||||||
|
"//mediapipe/framework/port:status",
|
||||||
|
],
|
||||||
|
alwayslink = 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "packet_presence_calculator_test",
|
||||||
|
srcs = ["packet_presence_calculator_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":gate_calculator",
|
||||||
|
":packet_presence_calculator",
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework:calculator_runner",
|
||||||
|
"//mediapipe/framework:timestamp",
|
||||||
|
"//mediapipe/framework/port:gtest_main",
|
||||||
|
"//mediapipe/framework/port:parse_text_proto",
|
||||||
|
"//mediapipe/framework/port:status",
|
||||||
|
"//mediapipe/framework/tool:sink",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "previous_loopback_calculator",
|
name = "previous_loopback_calculator",
|
||||||
srcs = ["previous_loopback_calculator.cc"],
|
srcs = ["previous_loopback_calculator.cc"],
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef MEDIAPIPE_CALCULATORS_CORE_CONCATENATE_NORMALIZED_LIST_CALCULATOR_H_ // NOLINT
|
||||||
|
#define MEDIAPIPE_CALCULATORS_CORE_CONCATENATE_NORMALIZED_LIST_CALCULATOR_H_ // NOLINT
|
||||||
|
|
||||||
|
#include "mediapipe/calculators/core/concatenate_vector_calculator.pb.h"
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/formats/landmark.pb.h"
|
||||||
|
#include "mediapipe/framework/port/canonical_errors.h"
|
||||||
|
#include "mediapipe/framework/port/ret_check.h"
|
||||||
|
#include "mediapipe/framework/port/status.h"
|
||||||
|
|
||||||
|
namespace mediapipe {
|
||||||
|
|
||||||
|
// Concatenates several NormalizedLandmarkList protos following stream index
|
||||||
|
// order. This class assumes that every input stream contains a
|
||||||
|
// NormalizedLandmarkList proto object.
|
||||||
|
class ConcatenateNormalizedLandmarkListCalculator : public CalculatorBase {
|
||||||
|
public:
|
||||||
|
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
||||||
|
RET_CHECK(cc->Inputs().NumEntries() != 0);
|
||||||
|
RET_CHECK(cc->Outputs().NumEntries() == 1);
|
||||||
|
|
||||||
|
for (int i = 0; i < cc->Inputs().NumEntries(); ++i) {
|
||||||
|
cc->Inputs().Index(i).Set<NormalizedLandmarkList>();
|
||||||
|
}
|
||||||
|
|
||||||
|
cc->Outputs().Index(0).Set<NormalizedLandmarkList>();
|
||||||
|
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status Open(CalculatorContext* cc) override {
|
||||||
|
cc->SetOffset(TimestampDiff(0));
|
||||||
|
only_emit_if_all_present_ =
|
||||||
|
cc->Options<::mediapipe::ConcatenateVectorCalculatorOptions>()
|
||||||
|
.only_emit_if_all_present();
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status Process(CalculatorContext* cc) override {
|
||||||
|
if (only_emit_if_all_present_) {
|
||||||
|
for (int i = 0; i < cc->Inputs().NumEntries(); ++i) {
|
||||||
|
if (cc->Inputs().Index(i).IsEmpty()) return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NormalizedLandmarkList output;
|
||||||
|
for (int i = 0; i < cc->Inputs().NumEntries(); ++i) {
|
||||||
|
if (cc->Inputs().Index(i).IsEmpty()) continue;
|
||||||
|
const NormalizedLandmarkList& input =
|
||||||
|
cc->Inputs().Index(i).Get<NormalizedLandmarkList>();
|
||||||
|
for (int j = 0; j < input.landmark_size(); ++j) {
|
||||||
|
const NormalizedLandmark& input_landmark = input.landmark(j);
|
||||||
|
*output.add_landmark() = input_landmark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cc->Outputs().Index(0).AddPacket(
|
||||||
|
MakePacket<NormalizedLandmarkList>(output).At(cc->InputTimestamp()));
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool only_emit_if_all_present_;
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_CALCULATOR(ConcatenateNormalizedLandmarkListCalculator);
|
||||||
|
|
||||||
|
} // namespace mediapipe
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE
|
||||||
|
#endif // MEDIAPIPE_CALCULATORS_CORE_CONCATENATE_NORMALIZED_LIST_CALCULATOR_H_
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/calculator_runner.h"
|
||||||
|
#include "mediapipe/framework/formats/landmark.pb.h"
|
||||||
|
#include "mediapipe/framework/port/gmock.h"
|
||||||
|
#include "mediapipe/framework/port/gtest.h"
|
||||||
|
#include "mediapipe/framework/port/parse_text_proto.h"
|
||||||
|
#include "mediapipe/framework/port/status_matchers.h" // NOLINT
|
||||||
|
|
||||||
|
namespace mediapipe {
|
||||||
|
|
||||||
|
constexpr float kLocationValue = 3;
|
||||||
|
|
||||||
|
NormalizedLandmarkList GenerateLandmarks(int landmarks_size,
|
||||||
|
int value_multiplier) {
|
||||||
|
NormalizedLandmarkList landmarks;
|
||||||
|
for (int i = 0; i < landmarks_size; ++i) {
|
||||||
|
NormalizedLandmark* landmark = landmarks.add_landmark();
|
||||||
|
landmark->set_x(value_multiplier * kLocationValue);
|
||||||
|
landmark->set_y(value_multiplier * kLocationValue);
|
||||||
|
landmark->set_z(value_multiplier * kLocationValue);
|
||||||
|
}
|
||||||
|
return landmarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateCombinedLandmarks(
|
||||||
|
const std::vector<NormalizedLandmarkList>& inputs,
|
||||||
|
const NormalizedLandmarkList& result) {
|
||||||
|
int element_id = 0;
|
||||||
|
int expected_size = 0;
|
||||||
|
for (int i = 0; i < inputs.size(); ++i) {
|
||||||
|
const NormalizedLandmarkList& landmarks_i = inputs[i];
|
||||||
|
expected_size += landmarks_i.landmark_size();
|
||||||
|
for (int j = 0; j < landmarks_i.landmark_size(); ++j) {
|
||||||
|
const NormalizedLandmark& expected = landmarks_i.landmark(j);
|
||||||
|
const NormalizedLandmark& got = result.landmark(element_id);
|
||||||
|
EXPECT_FLOAT_EQ(expected.x(), got.x());
|
||||||
|
EXPECT_FLOAT_EQ(expected.y(), got.y());
|
||||||
|
EXPECT_FLOAT_EQ(expected.z(), got.z());
|
||||||
|
++element_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_EQ(expected_size, result.landmark_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddInputLandmarkLists(
|
||||||
|
const std::vector<NormalizedLandmarkList>& input_landmarks_vec,
|
||||||
|
int64 timestamp, CalculatorRunner* runner) {
|
||||||
|
for (int i = 0; i < input_landmarks_vec.size(); ++i) {
|
||||||
|
runner->MutableInputs()->Index(i).packets.push_back(
|
||||||
|
MakePacket<NormalizedLandmarkList>(input_landmarks_vec[i])
|
||||||
|
.At(Timestamp(timestamp)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConcatenateNormalizedLandmarkListCalculatorTest, EmptyVectorInputs) {
|
||||||
|
CalculatorRunner runner("ConcatenateNormalizedLandmarkListCalculator",
|
||||||
|
/*options_string=*/"", /*num_inputs=*/3,
|
||||||
|
/*num_outputs=*/1, /*num_side_packets=*/0);
|
||||||
|
|
||||||
|
NormalizedLandmarkList empty_list;
|
||||||
|
std::vector<NormalizedLandmarkList> inputs = {empty_list, empty_list,
|
||||||
|
empty_list};
|
||||||
|
AddInputLandmarkLists(inputs, /*timestamp=*/1, &runner);
|
||||||
|
MP_ASSERT_OK(runner.Run());
|
||||||
|
|
||||||
|
const std::vector<Packet>& outputs = runner.Outputs().Index(0).packets;
|
||||||
|
EXPECT_EQ(1, outputs.size());
|
||||||
|
EXPECT_EQ(0, outputs[0].Get<NormalizedLandmarkList>().landmark_size());
|
||||||
|
EXPECT_EQ(Timestamp(1), outputs[0].Timestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConcatenateNormalizedLandmarkListCalculatorTest, OneTimestamp) {
|
||||||
|
CalculatorRunner runner("ConcatenateNormalizedLandmarkListCalculator",
|
||||||
|
/*options_string=*/"", /*num_inputs=*/3,
|
||||||
|
/*num_outputs=*/1, /*num_side_packets=*/0);
|
||||||
|
|
||||||
|
NormalizedLandmarkList input_0 =
|
||||||
|
GenerateLandmarks(/*landmarks_size=*/3, /*value_multiplier=*/0);
|
||||||
|
NormalizedLandmarkList input_1 =
|
||||||
|
GenerateLandmarks(/*landmarks_size=*/1, /*value_multiplier=*/1);
|
||||||
|
NormalizedLandmarkList input_2 =
|
||||||
|
GenerateLandmarks(/*landmarks_size=*/2, /*value_multiplier=*/2);
|
||||||
|
std::vector<NormalizedLandmarkList> inputs = {input_0, input_1, input_2};
|
||||||
|
AddInputLandmarkLists(inputs, /*timestamp=*/1, &runner);
|
||||||
|
MP_ASSERT_OK(runner.Run());
|
||||||
|
|
||||||
|
const std::vector<Packet>& outputs = runner.Outputs().Index(0).packets;
|
||||||
|
EXPECT_EQ(1, outputs.size());
|
||||||
|
EXPECT_EQ(Timestamp(1), outputs[0].Timestamp());
|
||||||
|
const NormalizedLandmarkList& result =
|
||||||
|
outputs[0].Get<NormalizedLandmarkList>();
|
||||||
|
ValidateCombinedLandmarks(inputs, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConcatenateNormalizedLandmarkListCalculatorTest,
|
||||||
|
TwoInputsAtTwoTimestamps) {
|
||||||
|
CalculatorRunner runner("ConcatenateNormalizedLandmarkListCalculator",
|
||||||
|
/*options_string=*/"", /*num_inputs=*/3,
|
||||||
|
/*num_outputs=*/1, /*num_side_packets=*/0);
|
||||||
|
|
||||||
|
NormalizedLandmarkList input_0 =
|
||||||
|
GenerateLandmarks(/*landmarks_size=*/3, /*value_multiplier=*/0);
|
||||||
|
NormalizedLandmarkList input_1 =
|
||||||
|
GenerateLandmarks(/*landmarks_size=*/1, /*value_multiplier=*/1);
|
||||||
|
NormalizedLandmarkList input_2 =
|
||||||
|
GenerateLandmarks(/*landmarks_size=*/2, /*value_multiplier=*/2);
|
||||||
|
std::vector<NormalizedLandmarkList> inputs = {input_0, input_1, input_2};
|
||||||
|
{ AddInputLandmarkLists(inputs, /*timestamp=*/1, &runner); }
|
||||||
|
{ AddInputLandmarkLists(inputs, /*timestamp=*/2, &runner); }
|
||||||
|
MP_ASSERT_OK(runner.Run());
|
||||||
|
|
||||||
|
const std::vector<Packet>& outputs = runner.Outputs().Index(0).packets;
|
||||||
|
EXPECT_EQ(2, outputs.size());
|
||||||
|
{
|
||||||
|
EXPECT_EQ(Timestamp(1), outputs[0].Timestamp());
|
||||||
|
const NormalizedLandmarkList& result =
|
||||||
|
outputs[0].Get<NormalizedLandmarkList>();
|
||||||
|
ValidateCombinedLandmarks(inputs, result);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
EXPECT_EQ(Timestamp(2), outputs[1].Timestamp());
|
||||||
|
const NormalizedLandmarkList& result =
|
||||||
|
outputs[1].Get<NormalizedLandmarkList>();
|
||||||
|
ValidateCombinedLandmarks(inputs, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConcatenateNormalizedLandmarkListCalculatorTest,
|
||||||
|
OneEmptyStreamStillOutput) {
|
||||||
|
CalculatorRunner runner("ConcatenateNormalizedLandmarkListCalculator",
|
||||||
|
/*options_string=*/"", /*num_inputs=*/2,
|
||||||
|
/*num_outputs=*/1, /*num_side_packets=*/0);
|
||||||
|
|
||||||
|
NormalizedLandmarkList input_0 =
|
||||||
|
GenerateLandmarks(/*landmarks_size=*/3, /*value_multiplier=*/0);
|
||||||
|
std::vector<NormalizedLandmarkList> inputs = {input_0};
|
||||||
|
AddInputLandmarkLists(inputs, /*timestamp=*/1, &runner);
|
||||||
|
MP_ASSERT_OK(runner.Run());
|
||||||
|
|
||||||
|
const std::vector<Packet>& outputs = runner.Outputs().Index(0).packets;
|
||||||
|
EXPECT_EQ(1, outputs.size());
|
||||||
|
EXPECT_EQ(Timestamp(1), outputs[0].Timestamp());
|
||||||
|
const NormalizedLandmarkList& result =
|
||||||
|
outputs[0].Get<NormalizedLandmarkList>();
|
||||||
|
ValidateCombinedLandmarks(inputs, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ConcatenateNormalizedLandmarkListCalculatorTest, OneEmptyStreamNoOutput) {
|
||||||
|
CalculatorRunner runner("ConcatenateNormalizedLandmarkListCalculator",
|
||||||
|
/*options_string=*/
|
||||||
|
"[mediapipe.ConcatenateVectorCalculatorOptions.ext]: "
|
||||||
|
"{only_emit_if_all_present: true}",
|
||||||
|
/*num_inputs=*/2,
|
||||||
|
/*num_outputs=*/1, /*num_side_packets=*/0);
|
||||||
|
|
||||||
|
NormalizedLandmarkList input_0 =
|
||||||
|
GenerateLandmarks(/*landmarks_size=*/3, /*value_multiplier=*/0);
|
||||||
|
std::vector<NormalizedLandmarkList> inputs = {input_0};
|
||||||
|
AddInputLandmarkLists(inputs, /*timestamp=*/1, &runner);
|
||||||
|
MP_ASSERT_OK(runner.Run());
|
||||||
|
|
||||||
|
const std::vector<Packet>& outputs = runner.Outputs().Index(0).packets;
|
||||||
|
EXPECT_EQ(0, outputs.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mediapipe
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2020 The MediaPipe Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/port/status.h"
|
||||||
|
|
||||||
|
namespace mediapipe {
|
||||||
|
|
||||||
|
// For each non empty input packet, emits a single output packet containing a
|
||||||
|
// boolean value "true", "false" in response to empty packets (a.k.a. timestamp
|
||||||
|
// bound updates) This can be used to "flag" the presence of an arbitrary packet
|
||||||
|
// type as input into a downstream calculator.
|
||||||
|
//
|
||||||
|
// Inputs:
|
||||||
|
// PACKET - any type.
|
||||||
|
//
|
||||||
|
// Outputs:
|
||||||
|
// PRESENCE - bool.
|
||||||
|
// "true" if packet is not empty, "false" if there's timestamp bound update
|
||||||
|
// instead.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// node: {
|
||||||
|
// calculator: "PacketPresenceCalculator"
|
||||||
|
// input_stream: "PACKET:packet"
|
||||||
|
// output_stream: "PRESENCE:presence"
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// This calculator can be used in conjuction with GateCalculator in order to
|
||||||
|
// allow/disallow processing. For instance:
|
||||||
|
// node: {
|
||||||
|
// calculator: "PacketPresenceCalculator"
|
||||||
|
// input_stream: "PACKET:value"
|
||||||
|
// output_stream: "PRESENCE:disallow_if_present"
|
||||||
|
// }
|
||||||
|
// node {
|
||||||
|
// calculator: "GateCalculator"
|
||||||
|
// input_stream: "image"
|
||||||
|
// input_stream: "DISALLOW:disallow_if_present"
|
||||||
|
// output_stream: "image_for_processing"
|
||||||
|
// options: {
|
||||||
|
// [mediapipe.GateCalculatorOptions.ext] {
|
||||||
|
// empty_packets_as_allow: true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
class PacketPresenceCalculator : public CalculatorBase {
|
||||||
|
public:
|
||||||
|
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
||||||
|
cc->Inputs().Tag("PACKET").SetAny();
|
||||||
|
cc->Outputs().Tag("PRESENCE").Set<bool>();
|
||||||
|
// Process() function is invoked in response to input stream timestamp
|
||||||
|
// bound updates.
|
||||||
|
cc->SetProcessTimestampBounds(true);
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status Open(CalculatorContext* cc) override {
|
||||||
|
cc->SetOffset(TimestampDiff(0));
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status Process(CalculatorContext* cc) final {
|
||||||
|
cc->Outputs()
|
||||||
|
.Tag("PRESENCE")
|
||||||
|
.AddPacket(MakePacket<bool>(!cc->Inputs().Tag("PACKET").IsEmpty())
|
||||||
|
.At(cc->InputTimestamp()));
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
REGISTER_CALCULATOR(PacketPresenceCalculator);
|
||||||
|
|
||||||
|
} // namespace mediapipe
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2020 The MediaPipe Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/calculator_runner.h"
|
||||||
|
#include "mediapipe/framework/port/gmock.h"
|
||||||
|
#include "mediapipe/framework/port/gtest.h"
|
||||||
|
#include "mediapipe/framework/port/parse_text_proto.h"
|
||||||
|
#include "mediapipe/framework/port/status.h"
|
||||||
|
#include "mediapipe/framework/port/status_matchers.h"
|
||||||
|
#include "mediapipe/framework/timestamp.h"
|
||||||
|
#include "mediapipe/framework/tool/sink.h"
|
||||||
|
|
||||||
|
namespace mediapipe {
|
||||||
|
using ::testing::ElementsAre;
|
||||||
|
using ::testing::Eq;
|
||||||
|
using ::testing::Value;
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
MATCHER_P2(BoolPacket, value, timestamp, "") {
|
||||||
|
return Value(arg.template Get<bool>(), Eq(value)) &&
|
||||||
|
Value(arg.Timestamp(), Eq(timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PreviousLoopbackCalculator, CorrectTimestamps) {
|
||||||
|
std::vector<Packet> output_packets;
|
||||||
|
CalculatorGraphConfig graph_config =
|
||||||
|
ParseTextProtoOrDie<CalculatorGraphConfig>(R"(
|
||||||
|
input_stream: 'allow'
|
||||||
|
input_stream: 'value'
|
||||||
|
node {
|
||||||
|
calculator: "GateCalculator"
|
||||||
|
input_stream: 'value'
|
||||||
|
input_stream: 'ALLOW:allow'
|
||||||
|
output_stream: 'gated_value'
|
||||||
|
}
|
||||||
|
node {
|
||||||
|
calculator: 'PacketPresenceCalculator'
|
||||||
|
input_stream: 'PACKET:gated_value'
|
||||||
|
output_stream: 'PRESENCE:presence'
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
tool::AddVectorSink("presence", &graph_config, &output_packets);
|
||||||
|
|
||||||
|
CalculatorGraph graph;
|
||||||
|
MP_ASSERT_OK(graph.Initialize(graph_config, {}));
|
||||||
|
MP_ASSERT_OK(graph.StartRun({}));
|
||||||
|
|
||||||
|
auto send_packet = [&graph](int value, bool allow, Timestamp timestamp) {
|
||||||
|
MP_ASSERT_OK(graph.AddPacketToInputStream(
|
||||||
|
"value", MakePacket<int>(value).At(timestamp)));
|
||||||
|
MP_ASSERT_OK(graph.AddPacketToInputStream(
|
||||||
|
"allow", MakePacket<bool>(allow).At(timestamp)));
|
||||||
|
};
|
||||||
|
|
||||||
|
send_packet(10, false, Timestamp(10));
|
||||||
|
MP_EXPECT_OK(graph.WaitUntilIdle());
|
||||||
|
EXPECT_THAT(output_packets, ElementsAre(BoolPacket(false, Timestamp(10))));
|
||||||
|
|
||||||
|
output_packets.clear();
|
||||||
|
send_packet(20, true, Timestamp(11));
|
||||||
|
MP_EXPECT_OK(graph.WaitUntilIdle());
|
||||||
|
EXPECT_THAT(output_packets, ElementsAre(BoolPacket(true, Timestamp(11))));
|
||||||
|
|
||||||
|
MP_EXPECT_OK(graph.CloseAllInputStreams());
|
||||||
|
MP_EXPECT_OK(graph.WaitUntilDone());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace mediapipe
|
||||||
@@ -630,3 +630,34 @@ cc_library(
|
|||||||
],
|
],
|
||||||
alwayslink = 1,
|
alwayslink = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "image_file_properties_calculator",
|
||||||
|
srcs = ["image_file_properties_calculator.cc"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework/formats:image_file_properties_cc_proto",
|
||||||
|
"//mediapipe/framework/port:ret_check",
|
||||||
|
"//mediapipe/framework/port:status",
|
||||||
|
"@easyexif",
|
||||||
|
],
|
||||||
|
alwayslink = 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "image_file_properties_calculator_test",
|
||||||
|
srcs = ["image_file_properties_calculator_test.cc"],
|
||||||
|
data = ["//mediapipe/calculators/image/testdata:test_images"],
|
||||||
|
deps = [
|
||||||
|
":image_file_properties_calculator",
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework:calculator_runner",
|
||||||
|
"//mediapipe/framework/deps:file_path",
|
||||||
|
"//mediapipe/framework/formats:image_file_properties_cc_proto",
|
||||||
|
"//mediapipe/framework/port:file_helpers",
|
||||||
|
"//mediapipe/framework/port:gtest_main",
|
||||||
|
"//mediapipe/framework/port:parse_text_proto",
|
||||||
|
"//mediapipe/framework/port:status",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|||||||
@@ -127,6 +127,10 @@ REGISTER_CALCULATOR(ImageCroppingCalculator);
|
|||||||
}
|
}
|
||||||
|
|
||||||
options_ = cc->Options<mediapipe::ImageCroppingCalculatorOptions>();
|
options_ = cc->Options<mediapipe::ImageCroppingCalculatorOptions>();
|
||||||
|
output_max_width_ =
|
||||||
|
options_.has_output_max_width() ? options_.output_max_width() : FLT_MAX;
|
||||||
|
output_max_height_ =
|
||||||
|
options_.has_output_max_height() ? options_.output_max_height() : FLT_MAX;
|
||||||
|
|
||||||
if (use_gpu_) {
|
if (use_gpu_) {
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU)
|
#if !defined(MEDIAPIPE_DISABLE_GPU)
|
||||||
@@ -234,20 +238,27 @@ REGISTER_CALCULATOR(ImageCroppingCalculator);
|
|||||||
cv::Mat src_points;
|
cv::Mat src_points;
|
||||||
cv::boxPoints(min_rect, src_points);
|
cv::boxPoints(min_rect, src_points);
|
||||||
|
|
||||||
|
float output_width = min_rect.size.width;
|
||||||
|
float output_height = min_rect.size.height;
|
||||||
|
float scale = std::min({1.0f, output_max_width_ / output_width,
|
||||||
|
output_max_height_ / output_height});
|
||||||
|
output_width *= scale;
|
||||||
|
output_height *= scale;
|
||||||
|
|
||||||
float dst_corners[8] = {0,
|
float dst_corners[8] = {0,
|
||||||
min_rect.size.height - 1,
|
output_height - 1,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
min_rect.size.width - 1,
|
output_width - 1,
|
||||||
0,
|
0,
|
||||||
min_rect.size.width - 1,
|
output_width - 1,
|
||||||
min_rect.size.height - 1};
|
output_height - 1};
|
||||||
cv::Mat dst_points = cv::Mat(4, 2, CV_32F, dst_corners);
|
cv::Mat dst_points = cv::Mat(4, 2, CV_32F, dst_corners);
|
||||||
cv::Mat projection_matrix =
|
cv::Mat projection_matrix =
|
||||||
cv::getPerspectiveTransform(src_points, dst_points);
|
cv::getPerspectiveTransform(src_points, dst_points);
|
||||||
cv::Mat cropped_image;
|
cv::Mat cropped_image;
|
||||||
cv::warpPerspective(input_mat, cropped_image, projection_matrix,
|
cv::warpPerspective(input_mat, cropped_image, projection_matrix,
|
||||||
cv::Size(min_rect.size.width, min_rect.size.height),
|
cv::Size(output_width, output_height),
|
||||||
/* flags = */ 0,
|
/* flags = */ 0,
|
||||||
/* borderMode = */ border_mode);
|
/* borderMode = */ border_mode);
|
||||||
|
|
||||||
@@ -439,6 +450,12 @@ void ImageCroppingCalculator::GetOutputDimensions(CalculatorContext* cc,
|
|||||||
|
|
||||||
int width = static_cast<int>(std::round((col_max - col_min) * src_width));
|
int width = static_cast<int>(std::round((col_max - col_min) * src_width));
|
||||||
int height = static_cast<int>(std::round((row_max - row_min) * src_height));
|
int height = static_cast<int>(std::round((row_max - row_min) * src_height));
|
||||||
|
|
||||||
|
float scale =
|
||||||
|
std::min({1.0f, output_max_width_ / width, output_max_height_ / height});
|
||||||
|
width *= scale;
|
||||||
|
height *= scale;
|
||||||
|
|
||||||
// Minimum output dimension 1x1 prevents creation of textures with 0x0.
|
// Minimum output dimension 1x1 prevents creation of textures with 0x0.
|
||||||
*dst_width = std::max(1, width);
|
*dst_width = std::max(1, width);
|
||||||
*dst_height = std::max(1, height);
|
*dst_height = std::max(1, height);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef MEDIAPIPE_CALCULATORS_IMAGE_IMAGE_CROPPING_CALCULATOR_H_
|
#ifndef MEDIAPIPE_CALCULATORS_IMAGE_IMAGE_CROPPING_CALCULATOR_H_
|
||||||
#define MEDIAPIPE_CALCULATORS_IMAGE_IMAGE_CROPPING_CALCULATOR_H_
|
#define MEDIAPIPE_CALCULATORS_IMAGE_IMAGE_CROPPING_CALCULATOR_H_
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
#include "mediapipe/calculators/image/image_cropping_calculator.pb.h"
|
#include "mediapipe/calculators/image/image_cropping_calculator.pb.h"
|
||||||
#include "mediapipe/framework/calculator_framework.h"
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
|
||||||
@@ -80,6 +82,8 @@ class ImageCroppingCalculator : public CalculatorBase {
|
|||||||
bool use_gpu_ = false;
|
bool use_gpu_ = false;
|
||||||
// Output texture corners (4) after transoformation in normalized coordinates.
|
// Output texture corners (4) after transoformation in normalized coordinates.
|
||||||
float transformed_points_[8];
|
float transformed_points_[8];
|
||||||
|
float output_max_width_ = FLT_MAX;
|
||||||
|
float output_max_height_ = FLT_MAX;
|
||||||
#if !defined(MEDIAPIPE_DISABLE_GPU)
|
#if !defined(MEDIAPIPE_DISABLE_GPU)
|
||||||
bool gpu_initialized_ = false;
|
bool gpu_initialized_ = false;
|
||||||
mediapipe::GlCalculatorHelper gpu_helper_;
|
mediapipe::GlCalculatorHelper gpu_helper_;
|
||||||
|
|||||||
@@ -51,4 +51,10 @@ message ImageCroppingCalculatorOptions {
|
|||||||
|
|
||||||
// Specifies behaviour for crops that go beyond image borders.
|
// Specifies behaviour for crops that go beyond image borders.
|
||||||
optional BorderMode border_mode = 8 [default = BORDER_ZERO];
|
optional BorderMode border_mode = 8 [default = BORDER_ZERO];
|
||||||
|
|
||||||
|
// Specifies limits for the size of the output image. It will be scaled down,
|
||||||
|
// preserving ratio, to fit within. These do not change which area of the
|
||||||
|
// input is selected for cropping.
|
||||||
|
optional int32 output_max_width = 9;
|
||||||
|
optional int32 output_max_height = 10;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "exif.h"
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/formats/image_file_properties.pb.h"
|
||||||
|
#include "mediapipe/framework/port/canonical_errors.h"
|
||||||
|
#include "mediapipe/framework/port/status.h"
|
||||||
|
|
||||||
|
namespace mediapipe {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// 35 MM sensor has dimensions 36 mm x 24 mm, so diagonal length is
|
||||||
|
// sqrt(36^2 + 24^2).
|
||||||
|
static const double SENSOR_DIAGONAL_35MM = std::sqrt(1872.0);
|
||||||
|
|
||||||
|
::mediapipe::StatusOr<double> ComputeFocalLengthInPixels(
|
||||||
|
int image_width, int image_height, double focal_length_35mm,
|
||||||
|
double focal_length_mm) {
|
||||||
|
// TODO: Allow returning image file properties even when focal length
|
||||||
|
// computation is not possible.
|
||||||
|
if (image_width == 0 || image_height == 0) {
|
||||||
|
return ::mediapipe::InternalError(
|
||||||
|
"Image dimensions should be non-zero to compute focal length in "
|
||||||
|
"pixels.");
|
||||||
|
}
|
||||||
|
if (focal_length_mm == 0) {
|
||||||
|
return ::mediapipe::InternalError(
|
||||||
|
"Focal length in mm should be non-zero to compute focal length in "
|
||||||
|
"pixels.");
|
||||||
|
}
|
||||||
|
if (focal_length_35mm == 0) {
|
||||||
|
return ::mediapipe::InternalError(
|
||||||
|
"Focal length in 35 mm should be non-zero to compute focal length in "
|
||||||
|
"pixels.");
|
||||||
|
}
|
||||||
|
// Derived from
|
||||||
|
// https://en.wikipedia.org/wiki/35_mm_equivalent_focal_length#Calculation.
|
||||||
|
/// Using focal_length_35mm = focal_length_mm * SENSOR_DIAGONAL_35MM /
|
||||||
|
/// sensor_diagonal_mm, we can calculate the diagonal length of the sensor in
|
||||||
|
/// millimeters i.e. sensor_diagonal_mm.
|
||||||
|
double sensor_diagonal_mm =
|
||||||
|
SENSOR_DIAGONAL_35MM / focal_length_35mm * focal_length_mm;
|
||||||
|
// Note that for the following computations, the longer dimension is treated
|
||||||
|
// as image width and the shorter dimension is treated as image height.
|
||||||
|
int width = image_width;
|
||||||
|
int height = image_height;
|
||||||
|
if (image_height > image_width) {
|
||||||
|
width = image_height;
|
||||||
|
height = image_width;
|
||||||
|
}
|
||||||
|
double inv_aspect_ratio = (double)height / width;
|
||||||
|
// Compute sensor width.
|
||||||
|
/// Using Pythagoras theorem, sensor_width^2 + sensor_height^2 =
|
||||||
|
/// sensor_diagonal_mm^2. We can substitute sensor_width / sensor_height with
|
||||||
|
/// the aspect ratio calculated in pixels to compute the sensor width.
|
||||||
|
double sensor_width = std::sqrt((sensor_diagonal_mm * sensor_diagonal_mm) /
|
||||||
|
(1.0 + inv_aspect_ratio * inv_aspect_ratio));
|
||||||
|
|
||||||
|
// Compute focal length in pixels.
|
||||||
|
double focal_length_pixels = width * focal_length_mm / sensor_width;
|
||||||
|
return focal_length_pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::StatusOr<ImageFileProperties> GetImageFileProperites(
|
||||||
|
const std::string& image_bytes) {
|
||||||
|
easyexif::EXIFInfo result;
|
||||||
|
int code = result.parseFrom(image_bytes);
|
||||||
|
if (code) {
|
||||||
|
return ::mediapipe::InternalError("Error parsing EXIF, code: " +
|
||||||
|
std::to_string(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageFileProperties properties;
|
||||||
|
properties.set_image_width(result.ImageWidth);
|
||||||
|
properties.set_image_height(result.ImageHeight);
|
||||||
|
properties.set_focal_length_mm(result.FocalLength);
|
||||||
|
properties.set_focal_length_35mm(result.FocalLengthIn35mm);
|
||||||
|
|
||||||
|
ASSIGN_OR_RETURN(auto focal_length_pixels,
|
||||||
|
ComputeFocalLengthInPixels(properties.image_width(),
|
||||||
|
properties.image_height(),
|
||||||
|
properties.focal_length_35mm(),
|
||||||
|
properties.focal_length_mm()));
|
||||||
|
properties.set_focal_length_pixels(focal_length_pixels);
|
||||||
|
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// Calculator to extract EXIF information from an image file. The input is
|
||||||
|
// a std::string containing raw byte data from a file, and the output is an
|
||||||
|
// ImageFileProperties proto object with the relevant fields filled in.
|
||||||
|
// The calculator accepts the input as a stream or a side packet, and can output
|
||||||
|
// the result as a stream or a side packet. The calculator checks that if an
|
||||||
|
// output stream is present, it outputs to that stream, and if not, it checks if
|
||||||
|
// it can output to a side packet.
|
||||||
|
//
|
||||||
|
// Example config with input and output streams:
|
||||||
|
// node {
|
||||||
|
// calculator: "ImageFilePropertiesCalculator"
|
||||||
|
// input_stream: "image_bytes"
|
||||||
|
// output_stream: "image_properties"
|
||||||
|
// }
|
||||||
|
// Example config with input and output side packets:
|
||||||
|
// node {
|
||||||
|
// calculator: "ImageFilePropertiesCalculator"
|
||||||
|
// input_side_packet: "image_bytes"
|
||||||
|
// output_side_packet: "image_properties"
|
||||||
|
// }
|
||||||
|
class ImageFilePropertiesCalculator : public CalculatorBase {
|
||||||
|
public:
|
||||||
|
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
|
||||||
|
if (cc->Inputs().NumEntries() != 0) {
|
||||||
|
RET_CHECK(cc->Inputs().NumEntries() == 1);
|
||||||
|
cc->Inputs().Index(0).Set<std::string>();
|
||||||
|
} else {
|
||||||
|
RET_CHECK(cc->InputSidePackets().NumEntries() == 1);
|
||||||
|
cc->InputSidePackets().Index(0).Set<std::string>();
|
||||||
|
}
|
||||||
|
if (cc->Outputs().NumEntries() != 0) {
|
||||||
|
RET_CHECK(cc->Outputs().NumEntries() == 1);
|
||||||
|
cc->Outputs().Index(0).Set<::mediapipe::ImageFileProperties>();
|
||||||
|
} else {
|
||||||
|
RET_CHECK(cc->OutputSidePackets().NumEntries() == 1);
|
||||||
|
cc->OutputSidePackets().Index(0).Set<::mediapipe::ImageFileProperties>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status Open(CalculatorContext* cc) override {
|
||||||
|
cc->SetOffset(TimestampDiff(0));
|
||||||
|
|
||||||
|
if (cc->InputSidePackets().NumEntries() == 1) {
|
||||||
|
const std::string& image_bytes =
|
||||||
|
cc->InputSidePackets().Index(0).Get<std::string>();
|
||||||
|
ASSIGN_OR_RETURN(properties_, GetImageFileProperites(image_bytes));
|
||||||
|
read_properties_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_properties_ && cc->OutputSidePackets().NumEntries() == 1) {
|
||||||
|
cc->OutputSidePackets().Index(0).Set(
|
||||||
|
MakePacket<ImageFileProperties>(properties_));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status Process(CalculatorContext* cc) override {
|
||||||
|
if (cc->Inputs().NumEntries() == 1) {
|
||||||
|
if (cc->Inputs().Index(0).IsEmpty()) {
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
const std::string& image_bytes = cc->Inputs().Index(0).Get<std::string>();
|
||||||
|
ASSIGN_OR_RETURN(properties_, GetImageFileProperites(image_bytes));
|
||||||
|
read_properties_ = true;
|
||||||
|
}
|
||||||
|
if (read_properties_) {
|
||||||
|
if (cc->Outputs().NumEntries() == 1) {
|
||||||
|
cc->Outputs().Index(0).AddPacket(
|
||||||
|
MakePacket<ImageFileProperties>(properties_)
|
||||||
|
.At(cc->InputTimestamp()));
|
||||||
|
} else {
|
||||||
|
cc->OutputSidePackets().Index(0).Set(
|
||||||
|
MakePacket<ImageFileProperties>(properties_)
|
||||||
|
.At(::mediapipe::Timestamp::Unset()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImageFileProperties properties_;
|
||||||
|
bool read_properties_ = false;
|
||||||
|
};
|
||||||
|
REGISTER_CALCULATOR(ImageFilePropertiesCalculator);
|
||||||
|
|
||||||
|
} // namespace mediapipe
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/calculator_runner.h"
|
||||||
|
#include "mediapipe/framework/deps/file_path.h"
|
||||||
|
#include "mediapipe/framework/formats/image_file_properties.pb.h"
|
||||||
|
#include "mediapipe/framework/port/file_helpers.h"
|
||||||
|
#include "mediapipe/framework/port/gmock.h"
|
||||||
|
#include "mediapipe/framework/port/gtest.h"
|
||||||
|
#include "mediapipe/framework/port/parse_text_proto.h"
|
||||||
|
#include "mediapipe/framework/port/status_matchers.h"
|
||||||
|
|
||||||
|
namespace mediapipe {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr char kImageFilePath[] =
|
||||||
|
"/mediapipe/calculators/image/testdata/"
|
||||||
|
"front_camera_pixel2.jpg";
|
||||||
|
constexpr int kExpectedWidth = 2448;
|
||||||
|
constexpr int kExpectedHeight = 3264;
|
||||||
|
constexpr double kExpectedFocalLengthMm = 3.38;
|
||||||
|
constexpr double kExpectedFocalLengthIn35Mm = 25;
|
||||||
|
constexpr double kExpectedFocalLengthPixels = 2357.48;
|
||||||
|
|
||||||
|
double RoundToNDecimals(double value, int n) {
|
||||||
|
return std::round(value * pow(10.0, n)) / pow(10.0, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ImageFilePropertiesCalculatorTest, ReadsFocalLengthFromJpegInStreams) {
|
||||||
|
std::string image_filepath = file::JoinPath("./", kImageFilePath);
|
||||||
|
std::string image_contents;
|
||||||
|
MP_ASSERT_OK(file::GetContents(image_filepath, &image_contents));
|
||||||
|
|
||||||
|
CalculatorGraphConfig::Node node_config =
|
||||||
|
ParseTextProtoOrDie<CalculatorGraphConfig::Node>(R"(
|
||||||
|
calculator: "ImageFilePropertiesCalculator"
|
||||||
|
input_stream: "image_bytes"
|
||||||
|
output_stream: "properties"
|
||||||
|
)");
|
||||||
|
|
||||||
|
CalculatorRunner runner(node_config);
|
||||||
|
runner.MutableInputs()->Index(0).packets.push_back(
|
||||||
|
MakePacket<std::string>(image_contents).At(Timestamp(0)));
|
||||||
|
MP_ASSERT_OK(runner.Run());
|
||||||
|
const auto& outputs = runner.Outputs();
|
||||||
|
ASSERT_EQ(1, outputs.NumEntries());
|
||||||
|
const std::vector<Packet>& packets = outputs.Index(0).packets;
|
||||||
|
ASSERT_EQ(1, packets.size());
|
||||||
|
const auto& result = packets[0].Get<::mediapipe::ImageFileProperties>();
|
||||||
|
EXPECT_EQ(kExpectedWidth, result.image_width());
|
||||||
|
EXPECT_EQ(kExpectedHeight, result.image_height());
|
||||||
|
EXPECT_DOUBLE_EQ(kExpectedFocalLengthMm, result.focal_length_mm());
|
||||||
|
EXPECT_DOUBLE_EQ(kExpectedFocalLengthIn35Mm, result.focal_length_35mm());
|
||||||
|
EXPECT_DOUBLE_EQ(kExpectedFocalLengthPixels,
|
||||||
|
RoundToNDecimals(result.focal_length_pixels(), /*n=*/2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ImageFilePropertiesCalculatorTest, ReadsFocalLengthFromJpegInSidePackets) {
|
||||||
|
std::string image_filepath = file::JoinPath("./", kImageFilePath);
|
||||||
|
std::string image_contents;
|
||||||
|
MP_ASSERT_OK(file::GetContents(image_filepath, &image_contents));
|
||||||
|
|
||||||
|
CalculatorGraphConfig::Node node_config =
|
||||||
|
ParseTextProtoOrDie<CalculatorGraphConfig::Node>(R"(
|
||||||
|
calculator: "ImageFilePropertiesCalculator"
|
||||||
|
input_side_packet: "image_bytes"
|
||||||
|
output_side_packet: "properties"
|
||||||
|
)");
|
||||||
|
|
||||||
|
CalculatorRunner runner(node_config);
|
||||||
|
runner.MutableSidePackets()->Index(0) =
|
||||||
|
MakePacket<std::string>(image_contents).At(Timestamp(0));
|
||||||
|
MP_ASSERT_OK(runner.Run());
|
||||||
|
const auto& outputs = runner.OutputSidePackets();
|
||||||
|
EXPECT_EQ(1, outputs.NumEntries());
|
||||||
|
const auto& packet = outputs.Index(0);
|
||||||
|
const auto& result = packet.Get<::mediapipe::ImageFileProperties>();
|
||||||
|
EXPECT_EQ(kExpectedWidth, result.image_width());
|
||||||
|
EXPECT_EQ(kExpectedHeight, result.image_height());
|
||||||
|
EXPECT_DOUBLE_EQ(kExpectedFocalLengthMm, result.focal_length_mm());
|
||||||
|
EXPECT_DOUBLE_EQ(kExpectedFocalLengthIn35Mm, result.focal_length_35mm());
|
||||||
|
EXPECT_DOUBLE_EQ(kExpectedFocalLengthPixels,
|
||||||
|
RoundToNDecimals(result.focal_length_pixels(), /*n=*/2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ImageFilePropertiesCalculatorTest,
|
||||||
|
ReadsFocalLengthFromJpegStreamToSidePacket) {
|
||||||
|
std::string image_filepath = file::JoinPath("./", kImageFilePath);
|
||||||
|
std::string image_contents;
|
||||||
|
MP_ASSERT_OK(file::GetContents(image_filepath, &image_contents));
|
||||||
|
|
||||||
|
CalculatorGraphConfig::Node node_config =
|
||||||
|
ParseTextProtoOrDie<CalculatorGraphConfig::Node>(R"(
|
||||||
|
calculator: "ImageFilePropertiesCalculator"
|
||||||
|
input_stream: "image_bytes"
|
||||||
|
output_side_packet: "properties"
|
||||||
|
)");
|
||||||
|
|
||||||
|
CalculatorRunner runner(node_config);
|
||||||
|
runner.MutableInputs()->Index(0).packets.push_back(
|
||||||
|
MakePacket<std::string>(image_contents).At(Timestamp(0)));
|
||||||
|
MP_ASSERT_OK(runner.Run());
|
||||||
|
const auto& outputs = runner.OutputSidePackets();
|
||||||
|
EXPECT_EQ(1, outputs.NumEntries());
|
||||||
|
const auto& packet = outputs.Index(0);
|
||||||
|
const auto& result = packet.Get<::mediapipe::ImageFileProperties>();
|
||||||
|
EXPECT_EQ(kExpectedWidth, result.image_width());
|
||||||
|
EXPECT_EQ(kExpectedHeight, result.image_height());
|
||||||
|
EXPECT_DOUBLE_EQ(kExpectedFocalLengthMm, result.focal_length_mm());
|
||||||
|
EXPECT_DOUBLE_EQ(kExpectedFocalLengthIn35Mm, result.focal_length_35mm());
|
||||||
|
EXPECT_DOUBLE_EQ(kExpectedFocalLengthPixels,
|
||||||
|
RoundToNDecimals(result.focal_length_pixels(), /*n=*/2));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace mediapipe
|
||||||
@@ -201,7 +201,7 @@ int GetXnnpackNumThreads(
|
|||||||
// Input tensors are assumed to be of the correct size and already normalized.
|
// Input tensors are assumed to be of the correct size and already normalized.
|
||||||
// All output TfLiteTensors will be destroyed when the graph closes,
|
// All output TfLiteTensors will be destroyed when the graph closes,
|
||||||
// (i.e. after calling graph.WaitUntilDone()).
|
// (i.e. after calling graph.WaitUntilDone()).
|
||||||
// GPU tensors are currently only supported on Android and iOS.
|
// GPU tensor support rquires OpenGL ES 3.1+.
|
||||||
// This calculator uses FixedSizeInputStreamHandler by default.
|
// This calculator uses FixedSizeInputStreamHandler by default.
|
||||||
//
|
//
|
||||||
class TfLiteInferenceCalculator : public CalculatorBase {
|
class TfLiteInferenceCalculator : public CalculatorBase {
|
||||||
|
|||||||
@@ -20,6 +20,24 @@ package(default_visibility = ["//visibility:public"])
|
|||||||
|
|
||||||
exports_files(["LICENSE"])
|
exports_files(["LICENSE"])
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "alignment_points_to_rects_calculator",
|
||||||
|
srcs = ["alignment_points_to_rects_calculator.cc"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/calculators/util:detections_to_rects_calculator",
|
||||||
|
"//mediapipe/calculators/util:detections_to_rects_calculator_cc_proto",
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework:calculator_options_cc_proto",
|
||||||
|
"//mediapipe/framework/formats:detection_cc_proto",
|
||||||
|
"//mediapipe/framework/formats:location_data_cc_proto",
|
||||||
|
"//mediapipe/framework/formats:rect_cc_proto",
|
||||||
|
"//mediapipe/framework/port:ret_check",
|
||||||
|
"//mediapipe/framework/port:status",
|
||||||
|
],
|
||||||
|
alwayslink = 1,
|
||||||
|
)
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "annotation_overlay_calculator_proto",
|
name = "annotation_overlay_calculator_proto",
|
||||||
srcs = ["annotation_overlay_calculator.proto"],
|
srcs = ["annotation_overlay_calculator.proto"],
|
||||||
@@ -586,6 +604,15 @@ proto_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
proto_library(
|
||||||
|
name = "rect_to_render_scale_calculator_proto",
|
||||||
|
srcs = ["rect_to_render_scale_calculator.proto"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/framework:calculator_proto",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
proto_library(
|
proto_library(
|
||||||
name = "detections_to_render_data_calculator_proto",
|
name = "detections_to_render_data_calculator_proto",
|
||||||
srcs = ["detections_to_render_data_calculator.proto"],
|
srcs = ["detections_to_render_data_calculator.proto"],
|
||||||
@@ -700,7 +727,15 @@ mediapipe_cc_proto_library(
|
|||||||
deps = [":rect_to_render_data_calculator_proto"],
|
deps = [":rect_to_render_data_calculator_proto"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: What is that one for?
|
mediapipe_cc_proto_library(
|
||||||
|
name = "rect_to_render_scale_calculator_cc_proto",
|
||||||
|
srcs = ["rect_to_render_scale_calculator.proto"],
|
||||||
|
cc_deps = [
|
||||||
|
"//mediapipe/framework:calculator_cc_proto",
|
||||||
|
],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [":rect_to_render_scale_calculator_proto"],
|
||||||
|
)
|
||||||
|
|
||||||
mediapipe_cc_proto_library(
|
mediapipe_cc_proto_library(
|
||||||
name = "detections_to_render_data_calculator_cc_proto",
|
name = "detections_to_render_data_calculator_cc_proto",
|
||||||
@@ -830,6 +865,19 @@ cc_library(
|
|||||||
alwayslink = 1,
|
alwayslink = 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "rect_to_render_scale_calculator",
|
||||||
|
srcs = ["rect_to_render_scale_calculator.cc"],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
":rect_to_render_scale_calculator_cc_proto",
|
||||||
|
"//mediapipe/framework:calculator_framework",
|
||||||
|
"//mediapipe/framework/formats:rect_cc_proto",
|
||||||
|
"//mediapipe/framework/port:ret_check",
|
||||||
|
],
|
||||||
|
alwayslink = 1,
|
||||||
|
)
|
||||||
|
|
||||||
cc_test(
|
cc_test(
|
||||||
name = "detections_to_render_data_calculator_test",
|
name = "detections_to_render_data_calculator_test",
|
||||||
size = "small",
|
size = "small",
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include "mediapipe/calculators/util/detections_to_rects_calculator.h"
|
||||||
|
#include "mediapipe/calculators/util/detections_to_rects_calculator.pb.h"
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/calculator_options.pb.h"
|
||||||
|
#include "mediapipe/framework/formats/detection.pb.h"
|
||||||
|
#include "mediapipe/framework/formats/location_data.pb.h"
|
||||||
|
#include "mediapipe/framework/formats/rect.pb.h"
|
||||||
|
#include "mediapipe/framework/port/ret_check.h"
|
||||||
|
#include "mediapipe/framework/port/status.h"
|
||||||
|
|
||||||
|
namespace mediapipe {
|
||||||
|
|
||||||
|
namespace {} // namespace
|
||||||
|
|
||||||
|
// A calculator that converts Detection with two alignment points to Rect.
|
||||||
|
//
|
||||||
|
// Detection should contain two points:
|
||||||
|
// * Center point - center of the crop
|
||||||
|
// * Scale point - vector from center to scale point defines size and rotation
|
||||||
|
// of the Rect. Not that Y coordinate of this vector is flipped before
|
||||||
|
// computing the rotation (it is caused by the fact that Y axis is
|
||||||
|
// directed downwards). So define target rotation vector accordingly.
|
||||||
|
//
|
||||||
|
// Example config:
|
||||||
|
// node {
|
||||||
|
// calculator: "AlignmentPointsRectsCalculator"
|
||||||
|
// input_stream: "DETECTIONS:detections"
|
||||||
|
// input_stream: "IMAGE_SIZE:image_size"
|
||||||
|
// output_stream: "NORM_RECT:rect"
|
||||||
|
// options: {
|
||||||
|
// [mediapipe.DetectionsToRectsCalculatorOptions.ext] {
|
||||||
|
// rotation_vector_start_keypoint_index: 0
|
||||||
|
// rotation_vector_end_keypoint_index: 1
|
||||||
|
// rotation_vector_target_angle_degrees: 90
|
||||||
|
// output_zero_rect_for_empty_detections: true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
class AlignmentPointsRectsCalculator : public DetectionsToRectsCalculator {
|
||||||
|
public:
|
||||||
|
::mediapipe::Status Open(CalculatorContext* cc) override {
|
||||||
|
RET_CHECK_OK(DetectionsToRectsCalculator::Open(cc));
|
||||||
|
|
||||||
|
// Make sure that start and end keypoints are provided.
|
||||||
|
// They are required for the rect size calculation and will also force base
|
||||||
|
// calculator to compute rotation.
|
||||||
|
options_ = cc->Options<DetectionsToRectsCalculatorOptions>();
|
||||||
|
RET_CHECK(options_.has_rotation_vector_start_keypoint_index())
|
||||||
|
<< "Start keypoint is required to calculate rect size and rotation";
|
||||||
|
RET_CHECK(options_.has_rotation_vector_end_keypoint_index())
|
||||||
|
<< "End keypoint is required to calculate rect size and rotation";
|
||||||
|
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
::mediapipe::Status DetectionToNormalizedRect(
|
||||||
|
const ::mediapipe::Detection& detection,
|
||||||
|
const DetectionSpec& detection_spec,
|
||||||
|
::mediapipe::NormalizedRect* rect) override;
|
||||||
|
};
|
||||||
|
REGISTER_CALCULATOR(AlignmentPointsRectsCalculator);
|
||||||
|
|
||||||
|
::mediapipe::Status AlignmentPointsRectsCalculator::DetectionToNormalizedRect(
|
||||||
|
const Detection& detection, const DetectionSpec& detection_spec,
|
||||||
|
NormalizedRect* rect) {
|
||||||
|
const auto& location_data = detection.location_data();
|
||||||
|
const auto& image_size = detection_spec.image_size;
|
||||||
|
RET_CHECK(image_size) << "Image size is required to calculate the rect";
|
||||||
|
|
||||||
|
const float x_center =
|
||||||
|
location_data.relative_keypoints(start_keypoint_index_).x() *
|
||||||
|
image_size->first;
|
||||||
|
const float y_center =
|
||||||
|
location_data.relative_keypoints(start_keypoint_index_).y() *
|
||||||
|
image_size->second;
|
||||||
|
|
||||||
|
const float x_scale =
|
||||||
|
location_data.relative_keypoints(end_keypoint_index_).x() *
|
||||||
|
image_size->first;
|
||||||
|
const float y_scale =
|
||||||
|
location_data.relative_keypoints(end_keypoint_index_).y() *
|
||||||
|
image_size->second;
|
||||||
|
|
||||||
|
// Bounding box size as double distance from center to scale point.
|
||||||
|
const float box_size =
|
||||||
|
std::sqrt((x_scale - x_center) * (x_scale - x_center) +
|
||||||
|
(y_scale - y_center) * (y_scale - y_center)) *
|
||||||
|
2.0;
|
||||||
|
|
||||||
|
// Set resulting bounding box.
|
||||||
|
rect->set_x_center(x_center / image_size->first);
|
||||||
|
rect->set_y_center(y_center / image_size->second);
|
||||||
|
rect->set_width(box_size / image_size->first);
|
||||||
|
rect->set_height(box_size / image_size->second);
|
||||||
|
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mediapipe
|
||||||
@@ -160,8 +160,8 @@ class AnnotationOverlayCalculator : public CalculatorBase {
|
|||||||
GLuint image_mat_tex_ = 0; // Overlay drawing image for GPU.
|
GLuint image_mat_tex_ = 0; // Overlay drawing image for GPU.
|
||||||
int width_ = 0;
|
int width_ = 0;
|
||||||
int height_ = 0;
|
int height_ = 0;
|
||||||
int width_gpu_ = 0; // Size of overlay drawing texture.
|
int width_canvas_ = 0; // Size of overlay drawing texture canvas.
|
||||||
int height_gpu_ = 0;
|
int height_canvas_ = 0;
|
||||||
#endif // MEDIAPIPE_DISABLE_GPU
|
#endif // MEDIAPIPE_DISABLE_GPU
|
||||||
};
|
};
|
||||||
REGISTER_CALCULATOR(AnnotationOverlayCalculator);
|
REGISTER_CALCULATOR(AnnotationOverlayCalculator);
|
||||||
@@ -250,6 +250,7 @@ REGISTER_CALCULATOR(AnnotationOverlayCalculator);
|
|||||||
// Initialize the helper renderer library.
|
// Initialize the helper renderer library.
|
||||||
renderer_ = absl::make_unique<AnnotationRenderer>();
|
renderer_ = absl::make_unique<AnnotationRenderer>();
|
||||||
renderer_->SetFlipTextVertically(options_.flip_text_vertically());
|
renderer_->SetFlipTextVertically(options_.flip_text_vertically());
|
||||||
|
if (use_gpu_) renderer_->SetScaleFactor(options_.gpu_scale_factor());
|
||||||
|
|
||||||
// Set the output header based on the input header (if present).
|
// Set the output header based on the input header (if present).
|
||||||
const char* input_tag = use_gpu_ ? kInputFrameTagGpu : kInputFrameTag;
|
const char* input_tag = use_gpu_ ? kInputFrameTagGpu : kInputFrameTag;
|
||||||
@@ -391,8 +392,8 @@ REGISTER_CALCULATOR(AnnotationOverlayCalculator);
|
|||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, image_mat_tex_);
|
glBindTexture(GL_TEXTURE_2D, image_mat_tex_);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_gpu_, height_gpu_, GL_RGB,
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_canvas_, height_canvas_,
|
||||||
GL_UNSIGNED_BYTE, overlay_image);
|
GL_RGB, GL_UNSIGNED_BYTE, overlay_image);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,12 +495,13 @@ REGISTER_CALCULATOR(AnnotationOverlayCalculator);
|
|||||||
if (format != mediapipe::ImageFormat::SRGBA &&
|
if (format != mediapipe::ImageFormat::SRGBA &&
|
||||||
format != mediapipe::ImageFormat::SRGB)
|
format != mediapipe::ImageFormat::SRGB)
|
||||||
RET_CHECK_FAIL() << "Unsupported GPU input format: " << format;
|
RET_CHECK_FAIL() << "Unsupported GPU input format: " << format;
|
||||||
image_mat = absl::make_unique<cv::Mat>(height_gpu_, width_gpu_, CV_8UC3);
|
image_mat =
|
||||||
|
absl::make_unique<cv::Mat>(height_canvas_, width_canvas_, CV_8UC3);
|
||||||
memset(image_mat->data, kAnnotationBackgroundColor,
|
memset(image_mat->data, kAnnotationBackgroundColor,
|
||||||
height_gpu_ * width_gpu_ * image_mat->elemSize());
|
height_canvas_ * width_canvas_ * image_mat->elemSize());
|
||||||
} else {
|
} else {
|
||||||
image_mat = absl::make_unique<cv::Mat>(
|
image_mat = absl::make_unique<cv::Mat>(
|
||||||
height_gpu_, width_gpu_, CV_8UC3,
|
height_canvas_, width_canvas_, CV_8UC3,
|
||||||
cv::Scalar(options_.canvas_color().r(), options_.canvas_color().g(),
|
cv::Scalar(options_.canvas_color().r(), options_.canvas_color().g(),
|
||||||
options_.canvas_color().b()));
|
options_.canvas_color().b()));
|
||||||
}
|
}
|
||||||
@@ -646,8 +648,8 @@ REGISTER_CALCULATOR(AnnotationOverlayCalculator);
|
|||||||
width_ = RoundUp(options_.canvas_width_px(), alignment);
|
width_ = RoundUp(options_.canvas_width_px(), alignment);
|
||||||
height_ = RoundUp(options_.canvas_height_px(), alignment);
|
height_ = RoundUp(options_.canvas_height_px(), alignment);
|
||||||
}
|
}
|
||||||
width_gpu_ = RoundUp(width_ * scale_factor, alignment);
|
width_canvas_ = RoundUp(width_ * scale_factor, alignment);
|
||||||
height_gpu_ = RoundUp(height_ * scale_factor, alignment);
|
height_canvas_ = RoundUp(height_ * scale_factor, alignment);
|
||||||
|
|
||||||
// Init texture for opencv rendered frame.
|
// Init texture for opencv rendered frame.
|
||||||
{
|
{
|
||||||
@@ -655,8 +657,8 @@ REGISTER_CALCULATOR(AnnotationOverlayCalculator);
|
|||||||
glBindTexture(GL_TEXTURE_2D, image_mat_tex_);
|
glBindTexture(GL_TEXTURE_2D, image_mat_tex_);
|
||||||
// TODO
|
// TODO
|
||||||
// OpenCV only renders to RGB images, not RGBA. Ideally this should be RGBA.
|
// OpenCV only renders to RGB images, not RGBA. Ideally this should be RGBA.
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width_gpu_, height_gpu_, 0, GL_RGB,
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width_canvas_, height_canvas_, 0,
|
||||||
GL_UNSIGNED_BYTE, nullptr);
|
GL_RGB, GL_UNSIGNED_BYTE, nullptr);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
|||||||
@@ -50,7 +50,5 @@ message AnnotationOverlayCalculatorOptions {
|
|||||||
// This can be used to speed up annotation by drawing the annotation on an
|
// This can be used to speed up annotation by drawing the annotation on an
|
||||||
// intermediate image with a reduced scale, e.g. 0.5 (of the input image width
|
// intermediate image with a reduced scale, e.g. 0.5 (of the input image width
|
||||||
// and height), before resizing and overlaying it on top of the input image.
|
// and height), before resizing and overlaying it on top of the input image.
|
||||||
// Should only be used if *all* render data uses normalized coordinates
|
|
||||||
// (or absolute coordinates are updated to scale accordingly).
|
|
||||||
optional float gpu_scale_factor = 7 [default = 1.0];
|
optional float gpu_scale_factor = 7 [default = 1.0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
#include "mediapipe/calculators/util/rect_to_render_scale_calculator.pb.h"
|
||||||
|
#include "mediapipe/framework/calculator_framework.h"
|
||||||
|
#include "mediapipe/framework/formats/rect.pb.h"
|
||||||
|
|
||||||
|
namespace mediapipe {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr char kNormRectTag[] = "NORM_RECT";
|
||||||
|
constexpr char kImageSizeTag[] = "IMAGE_SIZE";
|
||||||
|
constexpr char kRenderScaleTag[] = "RENDER_SCALE";
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// A calculator to get scale for RenderData primitives.
|
||||||
|
//
|
||||||
|
// This calculator allows you to make RenderData primitives size (configured via
|
||||||
|
// `thickness`) to depend on actual size of the object they should highlight
|
||||||
|
// (e.g. pose, hand or face). It will give you bigger rendered primitives for
|
||||||
|
// bigger/closer objects and smaller primitives for smaller/far objects.
|
||||||
|
//
|
||||||
|
// IMPORTANT NOTE: RenderData primitives are rendered via OpenCV, which accepts
|
||||||
|
// only integer thickness. So when object goes further/closer you'll see 1 pixel
|
||||||
|
// jumps.
|
||||||
|
//
|
||||||
|
// Check `mediapipe/util/render_data.proto` for details on
|
||||||
|
// RenderData primitives and `thickness` parameter.
|
||||||
|
//
|
||||||
|
// Inputs:
|
||||||
|
// NORM_RECT: Normalized rectangle to compute object size from as maximum of
|
||||||
|
// width and height.
|
||||||
|
// IMAGE_SIZE: A std::pair<int, int> represention of image width and height to
|
||||||
|
// transform normalized object width and height to absolute pixel
|
||||||
|
// coordinates.
|
||||||
|
//
|
||||||
|
// Outputs:
|
||||||
|
// RENDER_SCALE: Float value that should be used to scale RenderData
|
||||||
|
// primitives calculated as `rect_size * multiplier`.
|
||||||
|
//
|
||||||
|
// Example config:
|
||||||
|
// node {
|
||||||
|
// calculator: "RectToRenderScaleCalculator"
|
||||||
|
// input_stream: "NORM_RECT:pose_landmarks_rect"
|
||||||
|
// input_stream: "IMAGE_SIZE:image_size"
|
||||||
|
// output_stream: "RENDER_SCALE:render_scale"
|
||||||
|
// options: {
|
||||||
|
// [mediapipe.RectToRenderScaleCalculatorOptions.ext] {
|
||||||
|
// multiplier: 0.001
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
class RectToRenderScaleCalculator : public CalculatorBase {
|
||||||
|
public:
|
||||||
|
static ::mediapipe::Status GetContract(CalculatorContract* cc);
|
||||||
|
::mediapipe::Status Open(CalculatorContext* cc) override;
|
||||||
|
::mediapipe::Status Process(CalculatorContext* cc) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
RectToRenderScaleCalculatorOptions options_;
|
||||||
|
};
|
||||||
|
REGISTER_CALCULATOR(RectToRenderScaleCalculator);
|
||||||
|
|
||||||
|
::mediapipe::Status RectToRenderScaleCalculator::GetContract(
|
||||||
|
CalculatorContract* cc) {
|
||||||
|
cc->Inputs().Tag(kNormRectTag).Set<NormalizedRect>();
|
||||||
|
cc->Inputs().Tag(kImageSizeTag).Set<std::pair<int, int>>();
|
||||||
|
cc->Outputs().Tag(kRenderScaleTag).Set<float>();
|
||||||
|
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status RectToRenderScaleCalculator::Open(CalculatorContext* cc) {
|
||||||
|
cc->SetOffset(TimestampDiff(0));
|
||||||
|
options_ = cc->Options<RectToRenderScaleCalculatorOptions>();
|
||||||
|
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status RectToRenderScaleCalculator::Process(
|
||||||
|
CalculatorContext* cc) {
|
||||||
|
if (cc->Inputs().Tag(kNormRectTag).IsEmpty()) {
|
||||||
|
cc->Outputs()
|
||||||
|
.Tag(kRenderScaleTag)
|
||||||
|
.AddPacket(
|
||||||
|
MakePacket<float>(options_.multiplier()).At(cc->InputTimestamp()));
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get image size.
|
||||||
|
int image_width;
|
||||||
|
int image_height;
|
||||||
|
std::tie(image_width, image_height) =
|
||||||
|
cc->Inputs().Tag(kImageSizeTag).Get<std::pair<int, int>>();
|
||||||
|
|
||||||
|
// Get rect size in absolute pixel coordinates.
|
||||||
|
const auto& rect = cc->Inputs().Tag(kNormRectTag).Get<NormalizedRect>();
|
||||||
|
const float rect_width = rect.width() * image_width;
|
||||||
|
const float rect_height = rect.height() * image_height;
|
||||||
|
|
||||||
|
// Calculate render scale.
|
||||||
|
const float rect_size = std::max(rect_width, rect_height);
|
||||||
|
const float render_scale = rect_size * options_.multiplier();
|
||||||
|
|
||||||
|
cc->Outputs()
|
||||||
|
.Tag(kRenderScaleTag)
|
||||||
|
.AddPacket(MakePacket<float>(render_scale).At(cc->InputTimestamp()));
|
||||||
|
|
||||||
|
return ::mediapipe::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mediapipe
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package mediapipe;
|
||||||
|
|
||||||
|
import "mediapipe/framework/calculator.proto";
|
||||||
|
|
||||||
|
message RectToRenderScaleCalculatorOptions {
|
||||||
|
extend CalculatorOptions {
|
||||||
|
optional RectToRenderScaleCalculatorOptions ext = 299463409;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiplier to apply to the rect size.
|
||||||
|
// If one defined `thickness` for RenderData primitives for object (e.g. pose,
|
||||||
|
// hand or face) of size `A` then multiplier should be `1/A`. It means that
|
||||||
|
// when actual object size on the image will be `B`, than all RenderData
|
||||||
|
// primitives will be scaled with factor `B/A`.
|
||||||
|
optional float multiplier = 1 [default = 0.01];
|
||||||
|
}
|
||||||
@@ -316,6 +316,7 @@ cc_library(
|
|||||||
"//mediapipe/util/tracking",
|
"//mediapipe/util/tracking",
|
||||||
"//mediapipe/util/tracking:box_tracker",
|
"//mediapipe/util/tracking:box_tracker",
|
||||||
"//mediapipe/util/tracking:tracking_visualization_utilities",
|
"//mediapipe/util/tracking:tracking_visualization_utilities",
|
||||||
|
"@com_google_absl//absl/container:flat_hash_set",
|
||||||
"@com_google_absl//absl/container:node_hash_set",
|
"@com_google_absl//absl/container:node_hash_set",
|
||||||
"@com_google_absl//absl/strings",
|
"@com_google_absl//absl/strings",
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "absl/container/flat_hash_set.h"
|
||||||
#include "absl/container/node_hash_set.h"
|
#include "absl/container/node_hash_set.h"
|
||||||
#include "absl/strings/numbers.h"
|
#include "absl/strings/numbers.h"
|
||||||
#include "mediapipe/calculators/video/box_tracker_calculator.pb.h"
|
#include "mediapipe/calculators/video/box_tracker_calculator.pb.h"
|
||||||
@@ -238,6 +239,11 @@ class BoxTrackerCalculator : public CalculatorBase {
|
|||||||
// Queued track time requests.
|
// Queued track time requests.
|
||||||
std::vector<Timestamp> queued_track_requests_;
|
std::vector<Timestamp> queued_track_requests_;
|
||||||
|
|
||||||
|
// Stores the tracked ids that have been discarded actively, from continuous
|
||||||
|
// tracking data. It may accumulate across multiple frames. Once consumed, it
|
||||||
|
// should be cleared immediately.
|
||||||
|
absl::flat_hash_set<int> actively_discarded_tracked_ids_;
|
||||||
|
|
||||||
// Add smooth transition between re-acquisition and previous tracked boxes.
|
// Add smooth transition between re-acquisition and previous tracked boxes.
|
||||||
// `result_box` is the tracking result of one specific timestamp. The smoothed
|
// `result_box` is the tracking result of one specific timestamp. The smoothed
|
||||||
// result will be updated in place.
|
// result will be updated in place.
|
||||||
@@ -1144,9 +1150,16 @@ void BoxTrackerCalculator::StreamTrack(const TrackingData& data,
|
|||||||
CHECK(box_map);
|
CHECK(box_map);
|
||||||
CHECK(failed_ids);
|
CHECK(failed_ids);
|
||||||
|
|
||||||
|
// Cache the actively discarded tracked ids from the new tracking data.
|
||||||
|
for (const int discarded_id :
|
||||||
|
data.motion_data().actively_discarded_tracked_ids()) {
|
||||||
|
actively_discarded_tracked_ids_.insert(discarded_id);
|
||||||
|
}
|
||||||
|
|
||||||
// Track all existing boxes by one frame.
|
// Track all existing boxes by one frame.
|
||||||
MotionVectorFrame mvf; // Holds motion from current to previous frame.
|
MotionVectorFrame mvf; // Holds motion from current to previous frame.
|
||||||
MotionVectorFrameFromTrackingData(data, &mvf);
|
MotionVectorFrameFromTrackingData(data, &mvf);
|
||||||
|
mvf.actively_discarded_tracked_ids = &actively_discarded_tracked_ids_;
|
||||||
|
|
||||||
if (forward) {
|
if (forward) {
|
||||||
MotionVectorFrame mvf_inverted;
|
MotionVectorFrame mvf_inverted;
|
||||||
|
|||||||
@@ -197,90 +197,88 @@ REGISTER_CALCULATOR(TrackedDetectionManagerCalculator);
|
|||||||
|
|
||||||
::mediapipe::Status TrackedDetectionManagerCalculator::Process(
|
::mediapipe::Status TrackedDetectionManagerCalculator::Process(
|
||||||
CalculatorContext* cc) {
|
CalculatorContext* cc) {
|
||||||
if (cc->Inputs().HasTag("TRACKING_BOXES")) {
|
if (cc->Inputs().HasTag(kTrackingBoxesTag) &&
|
||||||
if (!cc->Inputs().Tag("TRACKING_BOXES").IsEmpty()) {
|
!cc->Inputs().Tag(kTrackingBoxesTag).IsEmpty()) {
|
||||||
const TimedBoxProtoList& tracked_boxes =
|
const TimedBoxProtoList& tracked_boxes =
|
||||||
cc->Inputs().Tag("TRACKING_BOXES").Get<TimedBoxProtoList>();
|
cc->Inputs().Tag(kTrackingBoxesTag).Get<TimedBoxProtoList>();
|
||||||
|
|
||||||
// Collect all detections that are removed.
|
// Collect all detections that are removed.
|
||||||
auto removed_detection_ids = absl::make_unique<std::vector<int>>();
|
auto removed_detection_ids = absl::make_unique<std::vector<int>>();
|
||||||
for (const TimedBoxProto& tracked_box : tracked_boxes.box()) {
|
for (const TimedBoxProto& tracked_box : tracked_boxes.box()) {
|
||||||
NormalizedRect bounding_box;
|
NormalizedRect bounding_box;
|
||||||
bounding_box.set_x_center((tracked_box.left() + tracked_box.right()) /
|
bounding_box.set_x_center((tracked_box.left() + tracked_box.right()) /
|
||||||
2.f);
|
2.f);
|
||||||
bounding_box.set_y_center((tracked_box.bottom() + tracked_box.top()) /
|
bounding_box.set_y_center((tracked_box.bottom() + tracked_box.top()) /
|
||||||
2.f);
|
2.f);
|
||||||
bounding_box.set_height(tracked_box.bottom() - tracked_box.top());
|
bounding_box.set_height(tracked_box.bottom() - tracked_box.top());
|
||||||
bounding_box.set_width(tracked_box.right() - tracked_box.left());
|
bounding_box.set_width(tracked_box.right() - tracked_box.left());
|
||||||
bounding_box.set_rotation(tracked_box.rotation());
|
bounding_box.set_rotation(tracked_box.rotation());
|
||||||
// First check if this box updates a detection that's waiting for
|
// First check if this box updates a detection that's waiting for
|
||||||
// update from the tracker.
|
// update from the tracker.
|
||||||
auto waiting_for_update_detectoin_ptr =
|
auto waiting_for_update_detectoin_ptr =
|
||||||
waiting_for_update_detections_.find(tracked_box.id());
|
waiting_for_update_detections_.find(tracked_box.id());
|
||||||
if (waiting_for_update_detectoin_ptr !=
|
if (waiting_for_update_detectoin_ptr !=
|
||||||
waiting_for_update_detections_.end()) {
|
waiting_for_update_detections_.end()) {
|
||||||
// Add the detection and remove duplicated detections.
|
// Add the detection and remove duplicated detections.
|
||||||
auto removed_ids = tracked_detection_manager_.AddDetection(
|
auto removed_ids = tracked_detection_manager_.AddDetection(
|
||||||
std::move(waiting_for_update_detectoin_ptr->second));
|
std::move(waiting_for_update_detectoin_ptr->second));
|
||||||
MoveIds(removed_detection_ids.get(), std::move(removed_ids));
|
|
||||||
|
|
||||||
waiting_for_update_detections_.erase(
|
|
||||||
waiting_for_update_detectoin_ptr);
|
|
||||||
}
|
|
||||||
auto removed_ids = tracked_detection_manager_.UpdateDetectionLocation(
|
|
||||||
tracked_box.id(), bounding_box, tracked_box.time_msec());
|
|
||||||
MoveIds(removed_detection_ids.get(), std::move(removed_ids));
|
MoveIds(removed_detection_ids.get(), std::move(removed_ids));
|
||||||
|
|
||||||
|
waiting_for_update_detections_.erase(waiting_for_update_detectoin_ptr);
|
||||||
}
|
}
|
||||||
// TODO: Should be handled automatically in detection manager.
|
auto removed_ids = tracked_detection_manager_.UpdateDetectionLocation(
|
||||||
auto removed_ids = tracked_detection_manager_.RemoveObsoleteDetections(
|
tracked_box.id(), bounding_box, tracked_box.time_msec());
|
||||||
GetInputTimestampMs(cc) - kDetectionUpdateTimeOutMS);
|
|
||||||
MoveIds(removed_detection_ids.get(), std::move(removed_ids));
|
MoveIds(removed_detection_ids.get(), std::move(removed_ids));
|
||||||
|
}
|
||||||
|
// TODO: Should be handled automatically in detection manager.
|
||||||
|
auto removed_ids = tracked_detection_manager_.RemoveObsoleteDetections(
|
||||||
|
GetInputTimestampMs(cc) - kDetectionUpdateTimeOutMS);
|
||||||
|
MoveIds(removed_detection_ids.get(), std::move(removed_ids));
|
||||||
|
|
||||||
// TODO: Should be handled automatically in detection manager.
|
// TODO: Should be handled automatically in detection manager.
|
||||||
removed_ids = tracked_detection_manager_.RemoveOutOfViewDetections();
|
removed_ids = tracked_detection_manager_.RemoveOutOfViewDetections();
|
||||||
MoveIds(removed_detection_ids.get(), std::move(removed_ids));
|
MoveIds(removed_detection_ids.get(), std::move(removed_ids));
|
||||||
|
|
||||||
if (!removed_detection_ids->empty() &&
|
if (!removed_detection_ids->empty() &&
|
||||||
cc->Outputs().HasTag(kCancelObjectIdTag)) {
|
cc->Outputs().HasTag(kCancelObjectIdTag)) {
|
||||||
auto timestamp = cc->InputTimestamp();
|
auto timestamp = cc->InputTimestamp();
|
||||||
for (int box_id : *removed_detection_ids) {
|
for (int box_id : *removed_detection_ids) {
|
||||||
// The timestamp is incremented (by 1 us) because currently the box
|
// The timestamp is incremented (by 1 us) because currently the box
|
||||||
// tracker calculator only accepts one cancel object ID for any given
|
// tracker calculator only accepts one cancel object ID for any given
|
||||||
// timestamp.
|
// timestamp.
|
||||||
cc->Outputs()
|
|
||||||
.Tag(kCancelObjectIdTag)
|
|
||||||
.AddPacket(mediapipe::MakePacket<int>(box_id).At(timestamp++));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output detections and corresponding bounding boxes.
|
|
||||||
const auto& all_detections =
|
|
||||||
tracked_detection_manager_.GetAllTrackedDetections();
|
|
||||||
auto output_detections = absl::make_unique<std::vector<Detection>>();
|
|
||||||
auto output_boxes = absl::make_unique<std::vector<NormalizedRect>>();
|
|
||||||
|
|
||||||
for (const auto& detection_ptr : all_detections) {
|
|
||||||
const auto& detection = *detection_ptr.second;
|
|
||||||
// Only output detections that are synced.
|
|
||||||
if (detection.last_updated_timestamp() <
|
|
||||||
cc->InputTimestamp().Microseconds() / 1000) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
output_detections->emplace_back(
|
|
||||||
GetAxisAlignedDetectionFromTrackedDetection(detection));
|
|
||||||
output_boxes->emplace_back(detection.bounding_box());
|
|
||||||
}
|
|
||||||
if (cc->Outputs().HasTag(kDetectionsTag)) {
|
|
||||||
cc->Outputs()
|
cc->Outputs()
|
||||||
.Tag(kDetectionsTag)
|
.Tag(kCancelObjectIdTag)
|
||||||
.Add(output_detections.release(), cc->InputTimestamp());
|
.AddPacket(mediapipe::MakePacket<int>(box_id).At(timestamp++));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cc->Outputs().HasTag(kDetectionBoxesTag)) {
|
// Output detections and corresponding bounding boxes.
|
||||||
cc->Outputs()
|
const auto& all_detections =
|
||||||
.Tag(kDetectionBoxesTag)
|
tracked_detection_manager_.GetAllTrackedDetections();
|
||||||
.Add(output_boxes.release(), cc->InputTimestamp());
|
auto output_detections = absl::make_unique<std::vector<Detection>>();
|
||||||
|
auto output_boxes = absl::make_unique<std::vector<NormalizedRect>>();
|
||||||
|
|
||||||
|
for (const auto& detection_ptr : all_detections) {
|
||||||
|
const auto& detection = *detection_ptr.second;
|
||||||
|
// Only output detections that are synced.
|
||||||
|
if (detection.last_updated_timestamp() <
|
||||||
|
cc->InputTimestamp().Microseconds() / 1000) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
output_detections->emplace_back(
|
||||||
|
GetAxisAlignedDetectionFromTrackedDetection(detection));
|
||||||
|
output_boxes->emplace_back(detection.bounding_box());
|
||||||
|
}
|
||||||
|
if (cc->Outputs().HasTag(kDetectionsTag)) {
|
||||||
|
cc->Outputs()
|
||||||
|
.Tag(kDetectionsTag)
|
||||||
|
.Add(output_detections.release(), cc->InputTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cc->Outputs().HasTag(kDetectionBoxesTag)) {
|
||||||
|
cc->Outputs()
|
||||||
|
.Tag(kDetectionBoxesTag)
|
||||||
|
.Add(output_boxes.release(), cc->InputTimestamp());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# 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:private"])
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "libmediapipe_jni.so",
|
||||||
|
linkshared = 1,
|
||||||
|
linkstatic = 1,
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/graphs/iris_tracking:iris_tracking_gpu_deps",
|
||||||
|
"//mediapipe/java/com/google/mediapipe/framework/jni:mediapipe_framework_jni",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "mediapipe_jni_lib",
|
||||||
|
srcs = [":libmediapipe_jni.so"],
|
||||||
|
alwayslink = 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
android_binary(
|
||||||
|
name = "iristrackinggpu",
|
||||||
|
srcs = glob(["*.java"]),
|
||||||
|
assets = [
|
||||||
|
"//mediapipe/graphs/iris_tracking:iris_tracking_gpu.binarypb",
|
||||||
|
"//mediapipe/modules/face_landmark:face_landmark.tflite",
|
||||||
|
"//mediapipe/modules/iris_landmark:iris_landmark.tflite",
|
||||||
|
"//mediapipe/modules/face_detection:face_detection_front.tflite",
|
||||||
|
],
|
||||||
|
assets_dir = "",
|
||||||
|
manifest = "//mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:AndroidManifest.xml",
|
||||||
|
manifest_values = {
|
||||||
|
"applicationId": "com.google.mediapipe.apps.iristrackinggpu",
|
||||||
|
"appName": "Iris Tracking",
|
||||||
|
"mainActivity": ".MainActivity",
|
||||||
|
"cameraFacingFront": "True",
|
||||||
|
"binaryGraphName": "iris_tracking_gpu.binarypb",
|
||||||
|
"inputVideoStreamName": "input_video",
|
||||||
|
"outputVideoStreamName": "output_video",
|
||||||
|
"flipFramesVertically": "True",
|
||||||
|
},
|
||||||
|
multidex = "native",
|
||||||
|
deps = [
|
||||||
|
":mediapipe_jni_lib",
|
||||||
|
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:basic_lib",
|
||||||
|
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
||||||
|
],
|
||||||
|
)
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2019 The MediaPipe Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.mediapipe.apps.iristrackinggpu;
|
||||||
|
|
||||||
|
import android.graphics.SurfaceTexture;
|
||||||
|
import com.google.mediapipe.framework.Packet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/** Main activity of MediaPipe iris tracking app. */
|
||||||
|
public class MainActivity extends com.google.mediapipe.apps.basic.MainActivity {
|
||||||
|
private static final String TAG = "MainActivity";
|
||||||
|
|
||||||
|
private static final String FOCAL_LENGTH_STREAM_NAME = "focal_length_pixel";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCameraStarted(SurfaceTexture surfaceTexture) {
|
||||||
|
super.onCameraStarted(surfaceTexture);
|
||||||
|
|
||||||
|
float focalLength = cameraHelper.getFocalLengthPixels();
|
||||||
|
if (focalLength != Float.MIN_VALUE) {
|
||||||
|
Packet focalLengthSidePacket = processor.getPacketCreator().createFloat32(focalLength);
|
||||||
|
Map<String, Packet> inputSidePackets = new HashMap<>();
|
||||||
|
inputSidePackets.put(FOCAL_LENGTH_STREAM_NAME, focalLengthSidePacket);
|
||||||
|
processor.setInputSidePackets(inputSidePackets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# 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:private"])
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "libmediapipe_jni.so",
|
||||||
|
linkshared = 1,
|
||||||
|
linkstatic = 1,
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/graphs/pose_tracking:upper_body_pose_tracking_gpu_deps",
|
||||||
|
"//mediapipe/java/com/google/mediapipe/framework/jni:mediapipe_framework_jni",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "mediapipe_jni_lib",
|
||||||
|
srcs = [":libmediapipe_jni.so"],
|
||||||
|
alwayslink = 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
android_binary(
|
||||||
|
name = "upperbodyposetrackinggpu",
|
||||||
|
srcs = glob(["*.java"]),
|
||||||
|
assets = [
|
||||||
|
"//mediapipe/graphs/pose_tracking:upper_body_pose_tracking_gpu.binarypb",
|
||||||
|
"//mediapipe/modules/pose_landmark:pose_landmark_upper_body.tflite",
|
||||||
|
"//mediapipe/modules/pose_detection:pose_detection.tflite",
|
||||||
|
],
|
||||||
|
assets_dir = "",
|
||||||
|
manifest = "//mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:AndroidManifest.xml",
|
||||||
|
manifest_values = {
|
||||||
|
"applicationId": "com.google.mediapipe.apps.upperbodyposetrackinggpu",
|
||||||
|
"appName": "Upper Body Pose Tracking",
|
||||||
|
"mainActivity": ".MainActivity",
|
||||||
|
"cameraFacingFront": "False",
|
||||||
|
"binaryGraphName": "upper_body_pose_tracking_gpu.binarypb",
|
||||||
|
"inputVideoStreamName": "input_video",
|
||||||
|
"outputVideoStreamName": "output_video",
|
||||||
|
"flipFramesVertically": "True",
|
||||||
|
},
|
||||||
|
multidex = "native",
|
||||||
|
deps = [
|
||||||
|
":mediapipe_jni_lib",
|
||||||
|
"//mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:basic_lib",
|
||||||
|
"//mediapipe/framework/formats:landmark_java_proto_lite",
|
||||||
|
"//mediapipe/java/com/google/mediapipe/framework:android_framework",
|
||||||
|
],
|
||||||
|
)
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright 2019 The MediaPipe Authors.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package com.google.mediapipe.apps.upperbodyposetrackinggpu;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmark;
|
||||||
|
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmarkList;
|
||||||
|
import com.google.mediapipe.framework.PacketGetter;
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
|
/** Main activity of MediaPipe upper-body pose tracking app. */
|
||||||
|
public class MainActivity extends com.google.mediapipe.apps.basic.MainActivity {
|
||||||
|
private static final String TAG = "MainActivity";
|
||||||
|
|
||||||
|
private static final String OUTPUT_LANDMARKS_STREAM_NAME = "pose_landmarks";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// To show verbose logging, run:
|
||||||
|
// adb shell setprop log.tag.MainActivity VERBOSE
|
||||||
|
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||||
|
processor.addPacketCallback(
|
||||||
|
OUTPUT_LANDMARKS_STREAM_NAME,
|
||||||
|
(packet) -> {
|
||||||
|
Log.v(TAG, "Received pose landmarks packet.");
|
||||||
|
try {
|
||||||
|
NormalizedLandmarkList poseLandmarks =
|
||||||
|
PacketGetter.getProto(packet, NormalizedLandmarkList.class);
|
||||||
|
Log.v(
|
||||||
|
TAG,
|
||||||
|
"[TS:"
|
||||||
|
+ packet.getTimestamp()
|
||||||
|
+ "] "
|
||||||
|
+ getPoseLandmarksDebugString(poseLandmarks));
|
||||||
|
} catch (InvalidProtocolBufferException exception) {
|
||||||
|
Log.e(TAG, "Failed to get proto.", exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPoseLandmarksDebugString(NormalizedLandmarkList poseLandmarks) {
|
||||||
|
String poseLandmarkStr = "Pose landmarks: " + poseLandmarks.getLandmarkCount() + "\n";
|
||||||
|
int landmarkIndex = 0;
|
||||||
|
for (NormalizedLandmark landmark : poseLandmarks.getLandmarkList()) {
|
||||||
|
poseLandmarkStr +=
|
||||||
|
"\tLandmark ["
|
||||||
|
+ landmarkIndex
|
||||||
|
+ "]: ("
|
||||||
|
+ landmark.getX()
|
||||||
|
+ ", "
|
||||||
|
+ landmark.getY()
|
||||||
|
+ ", "
|
||||||
|
+ landmark.getZ()
|
||||||
|
+ ")\n";
|
||||||
|
++landmarkIndex;
|
||||||
|
}
|
||||||
|
return poseLandmarkStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -165,28 +165,53 @@ REGISTER_CALCULATOR(ContentZoomingCalculator);
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
::mediapipe::Status UpdateRanges(const SalientRegion& region, float* xmin,
|
mediapipe::LocationData::RelativeBoundingBox ShiftDetection(
|
||||||
|
const mediapipe::LocationData::RelativeBoundingBox& relative_bounding_box,
|
||||||
|
const float y_offset_percent, const float x_offset_percent) {
|
||||||
|
auto shifted_bb = relative_bounding_box;
|
||||||
|
shifted_bb.set_ymin(relative_bounding_box.ymin() +
|
||||||
|
relative_bounding_box.height() * y_offset_percent);
|
||||||
|
shifted_bb.set_xmin(relative_bounding_box.xmin() +
|
||||||
|
relative_bounding_box.width() * x_offset_percent);
|
||||||
|
return shifted_bb;
|
||||||
|
}
|
||||||
|
mediapipe::autoflip::RectF ShiftDetection(
|
||||||
|
const mediapipe::autoflip::RectF& relative_bounding_box,
|
||||||
|
const float y_offset_percent, const float x_offset_percent) {
|
||||||
|
auto shifted_bb = relative_bounding_box;
|
||||||
|
shifted_bb.set_y(relative_bounding_box.y() +
|
||||||
|
relative_bounding_box.height() * y_offset_percent);
|
||||||
|
shifted_bb.set_x(relative_bounding_box.x() +
|
||||||
|
relative_bounding_box.width() * x_offset_percent);
|
||||||
|
return shifted_bb;
|
||||||
|
}
|
||||||
|
::mediapipe::Status UpdateRanges(const SalientRegion& region,
|
||||||
|
const float shift_vertical,
|
||||||
|
const float shift_horizontal, float* xmin,
|
||||||
float* xmax, float* ymin, float* ymax) {
|
float* xmax, float* ymin, float* ymax) {
|
||||||
if (!region.has_location_normalized()) {
|
if (!region.has_location_normalized()) {
|
||||||
return ::mediapipe::UnknownErrorBuilder(MEDIAPIPE_LOC)
|
return ::mediapipe::UnknownErrorBuilder(MEDIAPIPE_LOC)
|
||||||
<< "SalientRegion did not have location normalized set.";
|
<< "SalientRegion did not have location normalized set.";
|
||||||
}
|
}
|
||||||
*xmin = fmin(*xmin, region.location_normalized().x());
|
auto location = ShiftDetection(region.location_normalized(), shift_vertical,
|
||||||
*xmax = fmax(*xmax, region.location_normalized().x() +
|
shift_horizontal);
|
||||||
region.location_normalized().width());
|
*xmin = fmin(*xmin, location.x());
|
||||||
*ymin = fmin(*ymin, region.location_normalized().y());
|
*xmax = fmax(*xmax, location.x() + location.width());
|
||||||
*ymax = fmax(*ymax, region.location_normalized().y() +
|
*ymin = fmin(*ymin, location.y());
|
||||||
region.location_normalized().height());
|
*ymax = fmax(*ymax, location.y() + location.height());
|
||||||
|
|
||||||
return ::mediapipe::OkStatus();
|
return ::mediapipe::OkStatus();
|
||||||
}
|
}
|
||||||
::mediapipe::Status UpdateRanges(const mediapipe::Detection& detection,
|
::mediapipe::Status UpdateRanges(const mediapipe::Detection& detection,
|
||||||
float* xmin, float* xmax, float* ymin,
|
const float shift_vertical,
|
||||||
float* ymax) {
|
const float shift_horizontal, float* xmin,
|
||||||
|
float* xmax, float* ymin, float* ymax) {
|
||||||
RET_CHECK(detection.location_data().format() ==
|
RET_CHECK(detection.location_data().format() ==
|
||||||
mediapipe::LocationData::RELATIVE_BOUNDING_BOX)
|
mediapipe::LocationData::RELATIVE_BOUNDING_BOX)
|
||||||
<< "Face detection input is lacking required relative_bounding_box()";
|
<< "Face detection input is lacking required relative_bounding_box()";
|
||||||
const auto& location = detection.location_data().relative_bounding_box();
|
const auto& location =
|
||||||
|
ShiftDetection(detection.location_data().relative_bounding_box(),
|
||||||
|
shift_vertical, shift_horizontal);
|
||||||
*xmin = fmin(*xmin, location.xmin());
|
*xmin = fmin(*xmin, location.xmin());
|
||||||
*xmax = fmax(*xmax, location.xmin() + location.width());
|
*xmax = fmax(*xmax, location.xmin() + location.width());
|
||||||
*ymin = fmin(*ymin, location.ymin());
|
*ymin = fmin(*ymin, location.ymin());
|
||||||
@@ -270,7 +295,9 @@ void MakeStaticFeatures(const int top_border, const int bottom_border,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
only_required_found = true;
|
only_required_found = true;
|
||||||
MP_RETURN_IF_ERROR(UpdateRanges(region, &xmin, &xmax, &ymin, &ymax));
|
MP_RETURN_IF_ERROR(UpdateRanges(
|
||||||
|
region, options_.detection_shift_vertical(),
|
||||||
|
options_.detection_shift_horizontal(), &xmin, &xmax, &ymin, &ymax));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,7 +306,9 @@ void MakeStaticFeatures(const int top_border, const int bottom_border,
|
|||||||
cc->Inputs().Tag(kDetections).Get<std::vector<mediapipe::Detection>>();
|
cc->Inputs().Tag(kDetections).Get<std::vector<mediapipe::Detection>>();
|
||||||
for (const auto& detection : raw_detections) {
|
for (const auto& detection : raw_detections) {
|
||||||
only_required_found = true;
|
only_required_found = true;
|
||||||
MP_RETURN_IF_ERROR(UpdateRanges(detection, &xmin, &xmax, &ymin, &ymax));
|
MP_RETURN_IF_ERROR(UpdateRanges(
|
||||||
|
detection, options_.detection_shift_vertical(),
|
||||||
|
options_.detection_shift_horizontal(), &xmin, &xmax, &ymin, &ymax));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package mediapipe.autoflip;
|
|||||||
import "mediapipe/examples/desktop/autoflip/quality/kinematic_path_solver.proto";
|
import "mediapipe/examples/desktop/autoflip/quality/kinematic_path_solver.proto";
|
||||||
import "mediapipe/framework/calculator.proto";
|
import "mediapipe/framework/calculator.proto";
|
||||||
|
|
||||||
|
// NextTag: 13
|
||||||
message ContentZoomingCalculatorOptions {
|
message ContentZoomingCalculatorOptions {
|
||||||
extend mediapipe.CalculatorOptions {
|
extend mediapipe.CalculatorOptions {
|
||||||
optional ContentZoomingCalculatorOptions ext = 313091992;
|
optional ContentZoomingCalculatorOptions ext = 313091992;
|
||||||
@@ -44,6 +45,12 @@ message ContentZoomingCalculatorOptions {
|
|||||||
optional int64 height = 2;
|
optional int64 height = 2;
|
||||||
}
|
}
|
||||||
optional Size target_size = 8;
|
optional Size target_size = 8;
|
||||||
|
// Amount to shift an input detection as a ratio of the size (positive:
|
||||||
|
// down/right, negative: up/left). Use a negative value to increase padding
|
||||||
|
// above/left of an object, positive to increase padding below/right of an
|
||||||
|
// object.
|
||||||
|
optional float detection_shift_vertical = 11 [default = 0.0];
|
||||||
|
optional float detection_shift_horizontal = 12 [default = 0.0];
|
||||||
|
|
||||||
// Deprecated parameters
|
// Deprecated parameters
|
||||||
optional KinematicOptions kinematic_options = 2 [deprecated = true];
|
optional KinematicOptions kinematic_options = 2 [deprecated = true];
|
||||||
|
|||||||
@@ -366,6 +366,45 @@ TEST(ContentZoomingCalculatorTest, ZoomTestNearInsideBorder) {
|
|||||||
CheckCropRect(42, 42, 83, 83, 1, runner->Outputs().Tag("CROP_RECT").packets);
|
CheckCropRect(42, 42, 83, 83, 1, runner->Outputs().Tag("CROP_RECT").packets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ContentZoomingCalculatorTest, VerticalShift) {
|
||||||
|
auto config = ParseTextProtoOrDie<CalculatorGraphConfig::Node>(kConfigD);
|
||||||
|
auto* options = config.mutable_options()->MutableExtension(
|
||||||
|
ContentZoomingCalculatorOptions::ext);
|
||||||
|
options->set_detection_shift_vertical(0.2);
|
||||||
|
auto runner = ::absl::make_unique<CalculatorRunner>(config);
|
||||||
|
AddDetection(cv::Rect_<float>(.1, .1, .1, .1), 0, runner.get());
|
||||||
|
MP_ASSERT_OK(runner->Run());
|
||||||
|
// 1000px * .1 offset + 1000*.1*.1 shift = 170
|
||||||
|
CheckCropRect(150, 170, 111, 111, 0,
|
||||||
|
runner->Outputs().Tag("CROP_RECT").packets);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ContentZoomingCalculatorTest, HorizontalShift) {
|
||||||
|
auto config = ParseTextProtoOrDie<CalculatorGraphConfig::Node>(kConfigD);
|
||||||
|
auto* options = config.mutable_options()->MutableExtension(
|
||||||
|
ContentZoomingCalculatorOptions::ext);
|
||||||
|
options->set_detection_shift_horizontal(0.2);
|
||||||
|
auto runner = ::absl::make_unique<CalculatorRunner>(config);
|
||||||
|
AddDetection(cv::Rect_<float>(.1, .1, .1, .1), 0, runner.get());
|
||||||
|
MP_ASSERT_OK(runner->Run());
|
||||||
|
// 1000px * .1 offset + 1000*.1*.1 shift = 170
|
||||||
|
CheckCropRect(170, 150, 111, 111, 0,
|
||||||
|
runner->Outputs().Tag("CROP_RECT").packets);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ContentZoomingCalculatorTest, ShiftOutsideBounds) {
|
||||||
|
auto config = ParseTextProtoOrDie<CalculatorGraphConfig::Node>(kConfigD);
|
||||||
|
auto* options = config.mutable_options()->MutableExtension(
|
||||||
|
ContentZoomingCalculatorOptions::ext);
|
||||||
|
options->set_detection_shift_vertical(-0.2);
|
||||||
|
options->set_detection_shift_horizontal(0.2);
|
||||||
|
auto runner = ::absl::make_unique<CalculatorRunner>(config);
|
||||||
|
AddDetection(cv::Rect_<float>(.9, 0, .1, .1), 0, runner.get());
|
||||||
|
MP_ASSERT_OK(runner->Run());
|
||||||
|
CheckCropRect(944, 56, 111, 111, 0,
|
||||||
|
runner->Outputs().Tag("CROP_RECT").packets);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace autoflip
|
} // namespace autoflip
|
||||||
|
|
||||||
|
|||||||
@@ -144,11 +144,9 @@ namespace autoflip {
|
|||||||
// renderer.
|
// renderer.
|
||||||
for (int i = 0; i < num_scene_frames; i++) {
|
for (int i = 0; i < num_scene_frames; i++) {
|
||||||
const int left = -(scene_frame_xforms[i].at<float>(0, 2));
|
const int left = -(scene_frame_xforms[i].at<float>(0, 2));
|
||||||
const int right = left + crop_width;
|
const int top =
|
||||||
const int top = top_static_border_size;
|
top_static_border_size - (scene_frame_xforms[i].at<float>(1, 2));
|
||||||
const int bottom = frame_height_ - bottom_static_border_size;
|
crop_from_location->push_back(cv::Rect(left, top, crop_width, crop_height));
|
||||||
crop_from_location->push_back(
|
|
||||||
cv::Rect(left, top, right - left, bottom - top));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no cropped_frames is passed in, return directly.
|
// If no cropped_frames is passed in, return directly.
|
||||||
|
|||||||
@@ -201,5 +201,29 @@ TEST(SceneCropperTest, CropFramesWorksWithPriorFocusPointFrames) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks that crop_from_locations gets the correct results.
|
||||||
|
TEST(SceneCropperTest, CropFromLocation) {
|
||||||
|
CameraMotionOptions options;
|
||||||
|
options.mutable_polynomial_path_solver()->set_prior_frame_buffer_size(30);
|
||||||
|
SceneCropper scene_cropper(options, kSceneWidth, kSceneHeight);
|
||||||
|
std::vector<cv::Mat> cropped_frames;
|
||||||
|
std::vector<cv::Rect> crop_from_locations;
|
||||||
|
const auto& scene_frames = GetDefaultSceneFrames();
|
||||||
|
MP_EXPECT_OK(scene_cropper.CropFrames(
|
||||||
|
GetDefaultSceneKeyFrameCropSummary(), GetTimestamps(scene_frames.size()),
|
||||||
|
GetIsKeyframe(scene_frames.size()), scene_frames,
|
||||||
|
GetDefaultFocusPointFrames(), GetFocusPointFrames(3), 0, 0, false,
|
||||||
|
&crop_from_locations, &cropped_frames));
|
||||||
|
EXPECT_EQ(cropped_frames.size(), kNumSceneFrames);
|
||||||
|
for (int i = 0; i < kNumSceneFrames; ++i) {
|
||||||
|
EXPECT_EQ(cropped_frames[i].rows, kCropHeight);
|
||||||
|
EXPECT_EQ(cropped_frames[i].cols, kCropWidth);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < kNumSceneFrames; ++i) {
|
||||||
|
EXPECT_EQ(crop_from_locations[i].height, kCropHeight);
|
||||||
|
EXPECT_EQ(crop_from_locations[i].width, kCropWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace autoflip
|
} // namespace autoflip
|
||||||
} // namespace mediapipe
|
} // namespace mediapipe
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# 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 = ["//mediapipe/examples:__subpackages__"])
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "iris_depth_from_image_desktop",
|
||||||
|
srcs = ["iris_depth_from_image_desktop.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",
|
||||||
|
"//mediapipe/graphs/iris_tracking:iris_depth_cpu_deps",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "iris_tracking_cpu_video_input",
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/examples/desktop:simple_run_graph_main",
|
||||||
|
"//mediapipe/graphs/iris_tracking:iris_tracking_cpu_video_input_deps",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "iris_tracking_cpu",
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/examples/desktop:demo_run_graph_main",
|
||||||
|
"//mediapipe/graphs/iris_tracking:iris_tracking_cpu_deps",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Linux only
|
||||||
|
cc_binary(
|
||||||
|
name = "iris_tracking_gpu",
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/examples/desktop:demo_run_graph_main_gpu",
|
||||||
|
"//mediapipe/graphs/iris_tracking:iris_tracking_gpu_deps",
|
||||||
|
],
|
||||||
|
)
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// A utility to extract iris depth from a single image of face using the graph
|
||||||
|
// mediapipe/graphs/iris_tracking/iris_depth_cpu.pbtxt.
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#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/canonical_errors.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_image_bytes";
|
||||||
|
constexpr char kOutputImageStream[] = "output_image";
|
||||||
|
constexpr char kLeftIrisDepthMmStream[] = "left_iris_depth_mm";
|
||||||
|
constexpr char kRightIrisDepthMmStream[] = "right_iris_depth_mm";
|
||||||
|
constexpr char kWindowName[] = "MediaPipe";
|
||||||
|
constexpr char kCalculatorGraphConfigFile[] =
|
||||||
|
"mediapipe/graphs/iris_tracking/iris_depth_cpu.pbtxt";
|
||||||
|
constexpr float kMicrosPerSecond = 1e6;
|
||||||
|
|
||||||
|
DEFINE_string(input_image_path, "",
|
||||||
|
"Full path of image to load. "
|
||||||
|
"If not provided, nothing will run.");
|
||||||
|
DEFINE_string(output_image_path, "",
|
||||||
|
"Full path of where to save image result (.jpg only). "
|
||||||
|
"If not provided, show result in a window.");
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
::mediapipe::StatusOr<std::string> ReadFileToString(
|
||||||
|
const std::string& file_path) {
|
||||||
|
std::string contents;
|
||||||
|
MP_RETURN_IF_ERROR(::mediapipe::file::GetContents(file_path, &contents));
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status ProcessImage(
|
||||||
|
std::unique_ptr<::mediapipe::CalculatorGraph> graph) {
|
||||||
|
LOG(INFO) << "Load the image.";
|
||||||
|
ASSIGN_OR_RETURN(const std::string raw_image,
|
||||||
|
ReadFileToString(FLAGS_input_image_path));
|
||||||
|
|
||||||
|
LOG(INFO) << "Start running the calculator graph.";
|
||||||
|
ASSIGN_OR_RETURN(::mediapipe::OutputStreamPoller output_image_poller,
|
||||||
|
graph->AddOutputStreamPoller(kOutputImageStream));
|
||||||
|
ASSIGN_OR_RETURN(::mediapipe::OutputStreamPoller left_iris_depth_poller,
|
||||||
|
graph->AddOutputStreamPoller(kLeftIrisDepthMmStream));
|
||||||
|
ASSIGN_OR_RETURN(::mediapipe::OutputStreamPoller right_iris_depth_poller,
|
||||||
|
graph->AddOutputStreamPoller(kRightIrisDepthMmStream));
|
||||||
|
MP_RETURN_IF_ERROR(graph->StartRun({}));
|
||||||
|
|
||||||
|
// Send image packet into the graph.
|
||||||
|
const size_t fake_timestamp_us = (double)cv::getTickCount() /
|
||||||
|
(double)cv::getTickFrequency() *
|
||||||
|
kMicrosPerSecond;
|
||||||
|
MP_RETURN_IF_ERROR(graph->AddPacketToInputStream(
|
||||||
|
kInputStream, ::mediapipe::MakePacket<std::string>(raw_image).At(
|
||||||
|
::mediapipe::Timestamp(fake_timestamp_us))));
|
||||||
|
|
||||||
|
// Get the graph result packets, or stop if that fails.
|
||||||
|
::mediapipe::Packet left_iris_depth_packet;
|
||||||
|
if (!left_iris_depth_poller.Next(&left_iris_depth_packet)) {
|
||||||
|
return ::mediapipe::UnknownError(
|
||||||
|
"Failed to get packet from output stream 'left_iris_depth_mm'.");
|
||||||
|
}
|
||||||
|
const auto& left_iris_depth_mm = left_iris_depth_packet.Get<float>();
|
||||||
|
const int left_iris_depth_cm = std::round(left_iris_depth_mm / 10);
|
||||||
|
std::cout << "Left Iris Depth: " << left_iris_depth_cm << " cm." << std::endl;
|
||||||
|
|
||||||
|
::mediapipe::Packet right_iris_depth_packet;
|
||||||
|
if (!right_iris_depth_poller.Next(&right_iris_depth_packet)) {
|
||||||
|
return ::mediapipe::UnknownError(
|
||||||
|
"Failed to get packet from output stream 'right_iris_depth_mm'.");
|
||||||
|
}
|
||||||
|
const auto& right_iris_depth_mm = right_iris_depth_packet.Get<float>();
|
||||||
|
const int right_iris_depth_cm = std::round(right_iris_depth_mm / 10);
|
||||||
|
std::cout << "Right Iris Depth: " << right_iris_depth_cm << " cm."
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
::mediapipe::Packet output_image_packet;
|
||||||
|
if (!output_image_poller.Next(&output_image_packet)) {
|
||||||
|
return ::mediapipe::UnknownError(
|
||||||
|
"Failed to get packet from output stream 'output_image'.");
|
||||||
|
}
|
||||||
|
auto& output_frame = output_image_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);
|
||||||
|
const bool save_image = !FLAGS_output_image_path.empty();
|
||||||
|
if (save_image) {
|
||||||
|
LOG(INFO) << "Saving image to file...";
|
||||||
|
cv::imwrite(FLAGS_output_image_path, output_frame_mat);
|
||||||
|
} else {
|
||||||
|
cv::namedWindow(kWindowName, /*flags=WINDOW_AUTOSIZE*/ 1);
|
||||||
|
cv::imshow(kWindowName, output_frame_mat);
|
||||||
|
// Press any key to exit.
|
||||||
|
cv::waitKey(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Shutting down.";
|
||||||
|
MP_RETURN_IF_ERROR(graph->CloseInputStream(kInputStream));
|
||||||
|
return graph->WaitUntilDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
::mediapipe::Status RunMPPGraph() {
|
||||||
|
std::string calculator_graph_config_contents;
|
||||||
|
MP_RETURN_IF_ERROR(::mediapipe::file::GetContents(
|
||||||
|
kCalculatorGraphConfigFile, &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.";
|
||||||
|
std::unique_ptr<::mediapipe::CalculatorGraph> graph =
|
||||||
|
absl::make_unique<::mediapipe::CalculatorGraph>();
|
||||||
|
MP_RETURN_IF_ERROR(graph->Initialize(config));
|
||||||
|
|
||||||
|
const bool load_image = !FLAGS_input_image_path.empty();
|
||||||
|
if (load_image) {
|
||||||
|
return ProcessImage(std::move(graph));
|
||||||
|
} else {
|
||||||
|
return ::mediapipe::InvalidArgumentError("Missing image file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
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();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
LOG(INFO) << "Success!";
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# Copyright 2020 The MediaPipe Authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
licenses(["notice"]) # Apache 2.0
|
||||||
|
|
||||||
|
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "upper_body_pose_tracking_cpu",
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/examples/desktop:demo_run_graph_main",
|
||||||
|
"//mediapipe/graphs/pose_tracking:upper_body_pose_tracking_cpu_deps",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Linux only
|
||||||
|
cc_binary(
|
||||||
|
name = "upper_body_pose_tracking_gpu",
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/examples/desktop:demo_run_graph_main_gpu",
|
||||||
|
"//mediapipe/graphs/pose_tracking:upper_body_pose_tracking_gpu_deps",
|
||||||
|
],
|
||||||
|
)
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
|
#import "mediapipe/examples/ios/common/CommonViewController.h"
|
||||||
|
|
||||||
@interface AppDelegate ()
|
@interface AppDelegate ()
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
After Width: | Height: | Size: 396 B |
|
After Width: | Height: | Size: 686 B |
|
After Width: | Height: | Size: 855 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
@@ -23,21 +23,25 @@
|
|||||||
{
|
{
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"size" : "40x40",
|
"size" : "40x40",
|
||||||
|
"filename" : "40_c_2x.png",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"size" : "40x40",
|
"size" : "40x40",
|
||||||
|
"filename" : "40_c_3x.png",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"size" : "60x60",
|
"size" : "60x60",
|
||||||
|
"filename" : "60_c_iphone_2x.png",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"size" : "60x60",
|
"size" : "60x60",
|
||||||
|
"filename" : "60_c_iphone_3x.png",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -63,6 +67,7 @@
|
|||||||
{
|
{
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"size" : "40x40",
|
"size" : "40x40",
|
||||||
|
"filename" : "40_c_1x.png",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -73,6 +78,7 @@
|
|||||||
{
|
{
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"size" : "76x76",
|
"size" : "76x76",
|
||||||
|
"filename" : "76_c_Ipad.png",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
objc_library(
|
||||||
|
name = "CommonMediaPipeAppLibrary",
|
||||||
|
srcs = [
|
||||||
|
"AppDelegate.mm",
|
||||||
|
"CommonViewController.mm",
|
||||||
|
"main.m",
|
||||||
|
],
|
||||||
|
hdrs = [
|
||||||
|
"AppDelegate.h",
|
||||||
|
"CommonViewController.h",
|
||||||
|
],
|
||||||
|
data = [
|
||||||
|
"Base.lproj/LaunchScreen.storyboard",
|
||||||
|
"Base.lproj/Main.storyboard",
|
||||||
|
],
|
||||||
|
sdk_frameworks = [
|
||||||
|
"AVFoundation",
|
||||||
|
"CoreGraphics",
|
||||||
|
"CoreMedia",
|
||||||
|
"UIKit",
|
||||||
|
],
|
||||||
|
visibility = ["//mediapipe:__subpackages__"],
|
||||||
|
deps = [
|
||||||
|
"//mediapipe/objc:mediapipe_framework_ios",
|
||||||
|
"//mediapipe/objc:mediapipe_input_sources_ios",
|
||||||
|
"//mediapipe/objc:mediapipe_layer_renderer",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
exports_files(["Info.plist"])
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "AppIcon",
|
||||||
|
srcs = glob(["Assets.xcassets/AppIcon.appiconset/*"]),
|
||||||
|
visibility = ["//mediapipe:__subpackages__"],
|
||||||
|
)
|
||||||
@@ -1,28 +1,26 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||||
<device id="retina4_7" orientation="portrait">
|
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||||
<adaptation id="fullscreen"/>
|
|
||||||
</device>
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--View Controller-->
|
<!--Common View Controller-->
|
||||||
<scene sceneID="tne-QT-ifu">
|
<scene sceneID="tne-QT-ifu">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
|
<viewController id="BYZ-38-t0r" customClass="CommonViewController" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EfB-xq-knP">
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EfB-xq-knP">
|
||||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Camera access needed for this demo. Please enable camera access in the Settings app." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="emf-N5-sEd">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Camera access needed for this demo. Please enable camera access in the Settings app." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="emf-N5-sEd">
|
||||||
<rect key="frame" x="57" y="248" width="260" height="151"/>
|
<rect key="frame" x="57" y="258" width="260" height="151"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
@@ -39,8 +37,8 @@
|
|||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="_liveView" destination="EfB-xq-knP" id="JQp-2n-q9q"/>
|
<outlet property="liveView" destination="8bC-Xf-vdC" id="3qM-tM-inb"/>
|
||||||
<outlet property="_noCameraLabel" destination="emf-N5-sEd" id="91G-3Z-cU3"/>
|
<outlet property="noCameraLabel" destination="emf-N5-sEd" id="TUU-KL-fTU"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
#import "mediapipe/objc/MPPCameraInputSource.h"
|
||||||
|
#import "mediapipe/objc/MPPGraph.h"
|
||||||
|
#import "mediapipe/objc/MPPLayerRenderer.h"
|
||||||
|
#import "mediapipe/objc/MPPPlayerInputSource.h"
|
||||||
|
|
||||||
|
typedef NS_ENUM(NSInteger, MediaPipeDemoSourceMode) {
|
||||||
|
MediaPipeDemoSourceCamera,
|
||||||
|
MediaPipeDemoSourceVideo
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface CommonViewController : UIViewController <MPPGraphDelegate, MPPInputSourceDelegate>
|
||||||
|
|
||||||
|
// The MediaPipe graph currently in use. Initialized in viewDidLoad, started in
|
||||||
|
// viewWillAppear: and sent video frames on videoQueue.
|
||||||
|
@property(nonatomic) MPPGraph* mediapipeGraph;
|
||||||
|
|
||||||
|
// Handles camera access via AVCaptureSession library.
|
||||||
|
@property(nonatomic) MPPCameraInputSource* cameraSource;
|
||||||
|
|
||||||
|
// Provides data from a video.
|
||||||
|
@property(nonatomic) MPPPlayerInputSource* videoSource;
|
||||||
|
|
||||||
|
// The data source for the demo.
|
||||||
|
@property(nonatomic) MediaPipeDemoSourceMode sourceMode;
|
||||||
|
|
||||||
|
// Inform the user when camera is unavailable.
|
||||||
|
@property(nonatomic) IBOutlet UILabel* noCameraLabel;
|
||||||
|
|
||||||
|
// Display the camera preview frames.
|
||||||
|
@property(strong, nonatomic) IBOutlet UIView* liveView;
|
||||||
|
|
||||||
|
// Render frames in a layer.
|
||||||
|
@property(nonatomic) MPPLayerRenderer* renderer;
|
||||||
|
|
||||||
|
// Process camera frames on this queue.
|
||||||
|
@property(nonatomic) dispatch_queue_t videoQueue;
|
||||||
|
|
||||||
|
// Graph name.
|
||||||
|
@property(nonatomic) NSString* graphName;
|
||||||
|
|
||||||
|
// Graph input stream.
|
||||||
|
@property(nonatomic) const char* graphInputStream;
|
||||||
|
|
||||||
|
// Graph output stream.
|
||||||
|
@property(nonatomic) const char* graphOutputStream;
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -12,46 +12,24 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#import "ViewController.h"
|
#import "CommonViewController.h"
|
||||||
|
|
||||||
#import "mediapipe/objc/MPPGraph.h"
|
|
||||||
#import "mediapipe/objc/MPPCameraInputSource.h"
|
|
||||||
#import "mediapipe/objc/MPPLayerRenderer.h"
|
|
||||||
#import "mediapipe/objc/MPPPlayerInputSource.h"
|
|
||||||
|
|
||||||
static NSString* const kGraphName = @"mobile_gpu";
|
|
||||||
|
|
||||||
static const char* kInputStream = "input_video";
|
|
||||||
static const char* kOutputStream = "output_video";
|
|
||||||
static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
||||||
|
|
||||||
@interface ViewController () <MPPGraphDelegate, MPPInputSourceDelegate>
|
@implementation CommonViewController
|
||||||
|
|
||||||
// The MediaPipe graph currently in use. Initialized in viewDidLoad, started in viewWillAppear: and
|
// This provides a hook to replace the basic ViewController with a subclass when it's created from a
|
||||||
// sent video frames on _videoQueue.
|
// storyboard, without having to change the storyboard itself.
|
||||||
@property(nonatomic) MPPGraph* mediapipeGraph;
|
+ (instancetype)allocWithZone:(struct _NSZone*)zone {
|
||||||
|
NSString* subclassName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MainViewController"];
|
||||||
@end
|
if (subclassName.length > 0) {
|
||||||
|
Class customClass = NSClassFromString(subclassName);
|
||||||
@implementation ViewController {
|
Class baseClass = [CommonViewController class];
|
||||||
/// Handles camera access via AVCaptureSession library.
|
NSAssert([customClass isSubclassOfClass:baseClass], @"%@ must be a subclass of %@", customClass,
|
||||||
MPPCameraInputSource* _cameraSource;
|
baseClass);
|
||||||
MPPPlayerInputSource* _videoSource;
|
if (self == baseClass) return [customClass allocWithZone:zone];
|
||||||
MediaPipeDemoSourceMode _sourceMode;
|
}
|
||||||
|
return [super allocWithZone:zone];
|
||||||
/// Inform the user when camera is unavailable.
|
|
||||||
IBOutlet UILabel* _noCameraLabel;
|
|
||||||
/// Display the camera preview frames.
|
|
||||||
IBOutlet UIView* _liveView;
|
|
||||||
/// Render frames in a layer.
|
|
||||||
MPPLayerRenderer* _renderer;
|
|
||||||
|
|
||||||
/// Process camera frames on this queue.
|
|
||||||
dispatch_queue_t _videoQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setSourceMode:(MediaPipeDemoSourceMode)mode {
|
|
||||||
_sourceMode = mode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Cleanup methods
|
#pragma mark - Cleanup methods
|
||||||
@@ -86,7 +64,6 @@ 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];
|
|
||||||
return newGraph;
|
return newGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,19 +72,26 @@ static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
|||||||
- (void)viewDidLoad {
|
- (void)viewDidLoad {
|
||||||
[super viewDidLoad];
|
[super viewDidLoad];
|
||||||
|
|
||||||
_renderer = [[MPPLayerRenderer alloc] init];
|
self.renderer = [[MPPLayerRenderer alloc] init];
|
||||||
_renderer.layer.frame = _liveView.layer.bounds;
|
self.renderer.layer.frame = self.liveView.layer.bounds;
|
||||||
[_liveView.layer addSublayer:_renderer.layer];
|
[self.liveView.layer addSublayer:self.renderer.layer];
|
||||||
_renderer.frameScaleMode = MPPFrameScaleModeFillAndCrop;
|
self.renderer.frameScaleMode = MPPFrameScaleModeFillAndCrop;
|
||||||
|
|
||||||
dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class(
|
dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class(
|
||||||
DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, /*relative_priority=*/0);
|
DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, /*relative_priority=*/0);
|
||||||
_videoQueue = dispatch_queue_create(kVideoQueueLabel, qosAttribute);
|
self.videoQueue = dispatch_queue_create(kVideoQueueLabel, qosAttribute);
|
||||||
|
|
||||||
|
self.graphName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"GraphName"];
|
||||||
|
self.graphInputStream =
|
||||||
|
[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"GraphInputStream"] UTF8String];
|
||||||
|
self.graphOutputStream =
|
||||||
|
[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"GraphOutputStream"] UTF8String];
|
||||||
|
|
||||||
|
self.mediapipeGraph = [[self class] loadGraphFromResource:self.graphName];
|
||||||
|
[self.mediapipeGraph addFrameOutputStream:self.graphOutputStream
|
||||||
|
outputPacketType:MPPPacketTypePixelBuffer];
|
||||||
|
|
||||||
self.mediapipeGraph = [[self class] loadGraphFromResource:kGraphName];
|
|
||||||
self.mediapipeGraph.delegate = self;
|
self.mediapipeGraph.delegate = self;
|
||||||
// Set maxFramesInFlight to a small value to avoid memory contention for real-time processing.
|
|
||||||
self.mediapipeGraph.maxFramesInFlight = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this application, there is only one ViewController which has no navigation to other view
|
// In this application, there is only one ViewController which has no navigation to other view
|
||||||
@@ -119,43 +103,77 @@ static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
|||||||
- (void)viewWillAppear:(BOOL)animated {
|
- (void)viewWillAppear:(BOOL)animated {
|
||||||
[super viewWillAppear:animated];
|
[super viewWillAppear:animated];
|
||||||
|
|
||||||
|
switch (self.sourceMode) {
|
||||||
|
case MediaPipeDemoSourceVideo: {
|
||||||
|
NSString* videoName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"VideoName"];
|
||||||
|
AVAsset* video = [AVAsset assetWithURL:[[NSBundle mainBundle] URLForResource:videoName
|
||||||
|
withExtension:@"mov"]];
|
||||||
|
self.videoSource = [[MPPPlayerInputSource alloc] initWithAVAsset:video];
|
||||||
|
[self.videoSource setDelegate:self queue:self.videoQueue];
|
||||||
|
dispatch_async(self.videoQueue, ^{
|
||||||
|
[self.videoSource start];
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MediaPipeDemoSourceCamera: {
|
||||||
|
self.cameraSource = [[MPPCameraInputSource alloc] init];
|
||||||
|
[self.cameraSource setDelegate:self queue:self.videoQueue];
|
||||||
|
self.cameraSource.sessionPreset = AVCaptureSessionPresetHigh;
|
||||||
|
|
||||||
|
NSString* cameraPosition =
|
||||||
|
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CameraPosition"];
|
||||||
|
if (cameraPosition.length > 0 && [cameraPosition isEqualToString:@"back"]) {
|
||||||
|
self.cameraSource.cameraPosition = AVCaptureDevicePositionBack;
|
||||||
|
} else {
|
||||||
|
self.cameraSource.cameraPosition = AVCaptureDevicePositionFront;
|
||||||
|
// When using the front camera, mirror the input for a more natural look.
|
||||||
|
_cameraSource.videoMirrored = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The frame's native format is rotated with respect to the portrait orientation.
|
||||||
|
_cameraSource.orientation = AVCaptureVideoOrientationPortrait;
|
||||||
|
|
||||||
|
[self.cameraSource requestCameraAccessWithCompletionHandler:^void(BOOL granted) {
|
||||||
|
if (granted) {
|
||||||
|
[self startGraphAndCamera];
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
self.noCameraLabel.hidden = YES;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)startGraphAndCamera {
|
||||||
// Start running self.mediapipeGraph.
|
// Start running self.mediapipeGraph.
|
||||||
NSError* error;
|
NSError* error;
|
||||||
if (![self.mediapipeGraph startWithError:&error]) {
|
if (![self.mediapipeGraph startWithError:&error]) {
|
||||||
NSLog(@"Failed to start graph: %@", error);
|
NSLog(@"Failed to start graph: %@", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_sourceMode) {
|
// Start fetching frames from the camera.
|
||||||
case MediaPipeDemoSourceVideo: {
|
dispatch_async(self.videoQueue, ^{
|
||||||
AVAsset* video =
|
[self.cameraSource start];
|
||||||
[AVAsset assetWithURL:[[NSBundle mainBundle] URLForResource:@"object_detection"
|
});
|
||||||
withExtension:@"mov"]];
|
}
|
||||||
_videoSource = [[MPPPlayerInputSource alloc] initWithAVAsset:video];
|
|
||||||
[_videoSource setDelegate:self queue:_videoQueue];
|
#pragma mark - MPPInputSourceDelegate methods
|
||||||
dispatch_async(_videoQueue, ^{
|
|
||||||
[_videoSource start];
|
// Must be invoked on self.videoQueue.
|
||||||
});
|
- (void)processVideoFrame:(CVPixelBufferRef)imageBuffer
|
||||||
break;
|
timestamp:(CMTime)timestamp
|
||||||
}
|
fromSource:(MPPInputSource*)source {
|
||||||
case MediaPipeDemoSourceBackCamera:
|
if (source != self.cameraSource && source != self.videoSource) {
|
||||||
_cameraSource = [[MPPCameraInputSource alloc] init];
|
NSLog(@"Unknown source: %@", source);
|
||||||
[_cameraSource setDelegate:self queue:_videoQueue];
|
return;
|
||||||
_cameraSource.sessionPreset = AVCaptureSessionPresetHigh;
|
|
||||||
_cameraSource.cameraPosition = AVCaptureDevicePositionBack;
|
|
||||||
// The frame's native format is rotated with respect to the portrait orientation.
|
|
||||||
_cameraSource.orientation = AVCaptureVideoOrientationPortrait;
|
|
||||||
[_cameraSource requestCameraAccessWithCompletionHandler:^void(BOOL granted) {
|
|
||||||
if (granted) {
|
|
||||||
dispatch_async(_videoQueue, ^{
|
|
||||||
[_cameraSource start];
|
|
||||||
});
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
_noCameraLabel.hidden = YES;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[self.mediapipeGraph sendPixelBuffer:imageBuffer
|
||||||
|
intoStream:self.graphInputStream
|
||||||
|
packetType:MPPPacketTypePixelBuffer];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - MPPGraphDelegate methods
|
#pragma mark - MPPGraphDelegate methods
|
||||||
@@ -164,29 +182,14 @@ static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
|||||||
- (void)mediapipeGraph:(MPPGraph*)graph
|
- (void)mediapipeGraph:(MPPGraph*)graph
|
||||||
didOutputPixelBuffer:(CVPixelBufferRef)pixelBuffer
|
didOutputPixelBuffer:(CVPixelBufferRef)pixelBuffer
|
||||||
fromStream:(const std::string&)streamName {
|
fromStream:(const std::string&)streamName {
|
||||||
if (streamName == kOutputStream) {
|
if (streamName == self.graphOutputStream) {
|
||||||
// Display the captured image on the screen.
|
// Display the captured image on the screen.
|
||||||
CVPixelBufferRetain(pixelBuffer);
|
CVPixelBufferRetain(pixelBuffer);
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[_renderer renderPixelBuffer:pixelBuffer];
|
[self.renderer renderPixelBuffer:pixelBuffer];
|
||||||
CVPixelBufferRelease(pixelBuffer);
|
CVPixelBufferRelease(pixelBuffer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - MPPInputSourceDelegate methods
|
|
||||||
|
|
||||||
// Must be invoked on _videoQueue.
|
|
||||||
- (void)processVideoFrame:(CVPixelBufferRef)imageBuffer
|
|
||||||
timestamp:(CMTime)timestamp
|
|
||||||
fromSource:(MPPInputSource*)source {
|
|
||||||
if (source != _cameraSource && source != _videoSource) {
|
|
||||||
NSLog(@"Unknown source: %@", source);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[self.mediapipeGraph sendPixelBuffer:imageBuffer
|
|
||||||
intoStream:kInputStream
|
|
||||||
packetType:MPPPacketTypePixelBuffer];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@@ -37,7 +37,6 @@
|
|||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
@interface AppDelegate ()
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AppDelegate
|
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application
|
|
||||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
|
||||||
// Override point for customization after application launch.
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
|
||||||
// Sent when the application is about to move from active to inactive state. This can occur for
|
|
||||||
// certain types of temporary interruptions (such as an incoming phone call or SMS message) or
|
|
||||||
// when the user quits the application and it begins the transition to the background state. Use
|
|
||||||
// this method to pause ongoing tasks, disable timers, and invalidate graphics rendering
|
|
||||||
// callbacks. Games should use this method to pause the game.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
|
||||||
// Use this method to release shared resources, save user data, invalidate timers, and store
|
|
||||||
// enough application state information to restore your application to its current state in case
|
|
||||||
// it is terminated later. If your application supports background execution, this method is
|
|
||||||
// called instead of applicationWillTerminate: when the user quits.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
|
||||||
// Called as part of the transition from the background to the active state; here you can undo
|
|
||||||
// many of the changes made on entering the background.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
|
||||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If
|
|
||||||
// the application was previously in the background, optionally refresh the user interface.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
|
||||||
// Called when the application is about to terminate. Save data if appropriate. See also
|
|
||||||
// applicationDidEnterBackground:.
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "20x20",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "20x20",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "29x29",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "29x29",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "40x40",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "40x40",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "60x60",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "60x60",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "20x20",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "20x20",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "29x29",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "29x29",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "40x40",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "40x40",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "76x76",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "76x76",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "83.5x83.5",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"size" : "1024x1024",
|
|
||||||
"scale" : "1x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
@interface ViewController : UIViewController
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#import "ViewController.h"
|
|
||||||
|
|
||||||
#import "mediapipe/objc/MPPGraph.h"
|
|
||||||
#import "mediapipe/objc/MPPCameraInputSource.h"
|
|
||||||
#import "mediapipe/objc/MPPLayerRenderer.h"
|
|
||||||
|
|
||||||
static NSString* const kGraphName = @"mobile_gpu";
|
|
||||||
|
|
||||||
static const char* kInputStream = "input_video";
|
|
||||||
static const char* kOutputStream = "output_video";
|
|
||||||
static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
|
|
||||||
|
|
||||||
@interface ViewController () <MPPGraphDelegate, MPPInputSourceDelegate>
|
|
||||||
|
|
||||||
// The MediaPipe graph currently in use. Initialized in viewDidLoad, started in viewWillAppear: and
|
|
||||||
// sent video frames on _videoQueue.
|
|
||||||
@property(nonatomic) MPPGraph* mediapipeGraph;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation ViewController {
|
|
||||||
/// Handles camera access via AVCaptureSession library.
|
|
||||||
MPPCameraInputSource* _cameraSource;
|
|
||||||
|
|
||||||
/// Inform the user when camera is unavailable.
|
|
||||||
IBOutlet UILabel* _noCameraLabel;
|
|
||||||
/// Display the camera preview frames.
|
|
||||||
IBOutlet UIView* _liveView;
|
|
||||||
/// Render frames in a layer.
|
|
||||||
MPPLayerRenderer* _renderer;
|
|
||||||
|
|
||||||
/// Process camera frames on this queue.
|
|
||||||
dispatch_queue_t _videoQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Cleanup methods
|
|
||||||
|
|
||||||
- (void)dealloc {
|
|
||||||
self.mediapipeGraph.delegate = nil;
|
|
||||||
[self.mediapipeGraph cancel];
|
|
||||||
// Ignore errors since we're cleaning up.
|
|
||||||
[self.mediapipeGraph closeAllInputStreamsWithError:nil];
|
|
||||||
[self.mediapipeGraph waitUntilDoneWithError:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - MediaPipe graph methods
|
|
||||||
|
|
||||||
+ (MPPGraph*)loadGraphFromResource:(NSString*)resource {
|
|
||||||
// Load the graph config resource.
|
|
||||||
NSError* configLoadError = nil;
|
|
||||||
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
|
|
||||||
if (!resource || resource.length == 0) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
NSURL* graphURL = [bundle URLForResource:resource withExtension:@"binarypb"];
|
|
||||||
NSData* data = [NSData dataWithContentsOfURL:graphURL options:0 error:&configLoadError];
|
|
||||||
if (!data) {
|
|
||||||
NSLog(@"Failed to load MediaPipe graph config: %@", configLoadError);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the graph config resource into mediapipe::CalculatorGraphConfig proto object.
|
|
||||||
mediapipe::CalculatorGraphConfig config;
|
|
||||||
config.ParseFromArray(data.bytes, data.length);
|
|
||||||
|
|
||||||
// Create MediaPipe graph with mediapipe::CalculatorGraphConfig proto object.
|
|
||||||
MPPGraph* newGraph = [[MPPGraph alloc] initWithGraphConfig:config];
|
|
||||||
[newGraph addFrameOutputStream:kOutputStream outputPacketType:MPPPacketTypePixelBuffer];
|
|
||||||
return newGraph;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UIViewController methods
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
|
||||||
[super viewDidLoad];
|
|
||||||
|
|
||||||
_renderer = [[MPPLayerRenderer alloc] init];
|
|
||||||
_renderer.layer.frame = _liveView.layer.bounds;
|
|
||||||
[_liveView.layer addSublayer:_renderer.layer];
|
|
||||||
_renderer.frameScaleMode = MPPFrameScaleModeFillAndCrop;
|
|
||||||
|
|
||||||
dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class(
|
|
||||||
DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, /*relative_priority=*/0);
|
|
||||||
_videoQueue = dispatch_queue_create(kVideoQueueLabel, qosAttribute);
|
|
||||||
|
|
||||||
_cameraSource = [[MPPCameraInputSource alloc] init];
|
|
||||||
[_cameraSource setDelegate:self queue:_videoQueue];
|
|
||||||
_cameraSource.sessionPreset = AVCaptureSessionPresetHigh;
|
|
||||||
_cameraSource.cameraPosition = AVCaptureDevicePositionBack;
|
|
||||||
// The frame's native format is rotated with respect to the portrait orientation.
|
|
||||||
_cameraSource.orientation = AVCaptureVideoOrientationPortrait;
|
|
||||||
|
|
||||||
self.mediapipeGraph = [[self class] loadGraphFromResource:kGraphName];
|
|
||||||
self.mediapipeGraph.delegate = self;
|
|
||||||
// Set maxFramesInFlight to a small value to avoid memory contention for real-time processing.
|
|
||||||
self.mediapipeGraph.maxFramesInFlight = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In this application, there is only one ViewController which has no navigation to other view
|
|
||||||
// controllers, and there is only one View with live display showing the result of running the
|
|
||||||
// MediaPipe graph on the live video feed. If more view controllers are needed later, the graph
|
|
||||||
// setup/teardown and camera start/stop logic should be updated appropriately in response to the
|
|
||||||
// appearance/disappearance of this ViewController, as viewWillAppear: can be invoked multiple times
|
|
||||||
// depending on the application navigation flow in that case.
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
|
||||||
[super viewWillAppear:animated];
|
|
||||||
|
|
||||||
[_cameraSource requestCameraAccessWithCompletionHandler:^void(BOOL granted) {
|
|
||||||
if (granted) {
|
|
||||||
[self startGraphAndCamera];
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[_noCameraLabel setHidden:YES];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)startGraphAndCamera {
|
|
||||||
// Start running self.mediapipeGraph.
|
|
||||||
NSError* error;
|
|
||||||
if (![self.mediapipeGraph startWithError:&error]) {
|
|
||||||
NSLog(@"Failed to start graph: %@", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start fetching frames from the camera.
|
|
||||||
dispatch_async(_videoQueue, ^{
|
|
||||||
[_cameraSource start];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - MPPGraphDelegate methods
|
|
||||||
|
|
||||||
// Receives CVPixelBufferRef from the MediaPipe graph. Invoked on a MediaPipe worker thread.
|
|
||||||
- (void)mediapipeGraph:(MPPGraph*)graph
|
|
||||||
didOutputPixelBuffer:(CVPixelBufferRef)pixelBuffer
|
|
||||||
fromStream:(const std::string&)streamName {
|
|
||||||
if (streamName == kOutputStream) {
|
|
||||||
// Display the captured image on the screen.
|
|
||||||
CVPixelBufferRetain(pixelBuffer);
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
|
||||||
[_renderer renderPixelBuffer:pixelBuffer];
|
|
||||||
CVPixelBufferRelease(pixelBuffer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - MPPInputSourceDelegate methods
|
|
||||||
|
|
||||||
// Must be invoked on _videoQueue.
|
|
||||||
- (void)processVideoFrame:(CVPixelBufferRef)imageBuffer
|
|
||||||
timestamp:(CMTime)timestamp
|
|
||||||
fromSource:(MPPInputSource*)source {
|
|
||||||
if (source != _cameraSource) {
|
|
||||||
NSLog(@"Unknown source: %@", source);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[self.mediapipeGraph sendPixelBuffer:imageBuffer
|
|
||||||
intoStream:kInputStream
|
|
||||||
packetType:MPPPacketTypePixelBuffer];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "20x20",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "20x20",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "29x29",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "29x29",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "40x40",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "40x40",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "60x60",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "60x60",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "20x20",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "20x20",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "29x29",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "29x29",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "40x40",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "40x40",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "76x76",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "76x76",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ipad",
|
|
||||||
"size" : "83.5x83.5",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "ios-marketing",
|
|
||||||
"size" : "1024x1024",
|
|
||||||
"scale" : "1x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -33,12 +33,16 @@ alias(
|
|||||||
|
|
||||||
ios_application(
|
ios_application(
|
||||||
name = "FaceDetectionCpuApp",
|
name = "FaceDetectionCpuApp",
|
||||||
|
app_icons = ["//mediapipe/examples/ios/common:AppIcon"],
|
||||||
bundle_id = BUNDLE_ID_PREFIX + ".FaceDetectionCpu",
|
bundle_id = BUNDLE_ID_PREFIX + ".FaceDetectionCpu",
|
||||||
families = [
|
families = [
|
||||||
"iphone",
|
"iphone",
|
||||||
"ipad",
|
"ipad",
|
||||||
],
|
],
|
||||||
infoplists = ["Info.plist"],
|
infoplists = [
|
||||||
|
"//mediapipe/examples/ios/common:Info.plist",
|
||||||
|
"Info.plist",
|
||||||
|
],
|
||||||
minimum_os_version = MIN_IOS_VERSION,
|
minimum_os_version = MIN_IOS_VERSION,
|
||||||
provisioning_profile = example_provisioning(),
|
provisioning_profile = example_provisioning(),
|
||||||
deps = [
|
deps = [
|
||||||
@@ -49,32 +53,13 @@ ios_application(
|
|||||||
|
|
||||||
objc_library(
|
objc_library(
|
||||||
name = "FaceDetectionCpuAppLibrary",
|
name = "FaceDetectionCpuAppLibrary",
|
||||||
srcs = [
|
|
||||||
"AppDelegate.m",
|
|
||||||
"ViewController.mm",
|
|
||||||
"main.m",
|
|
||||||
],
|
|
||||||
hdrs = [
|
|
||||||
"AppDelegate.h",
|
|
||||||
"ViewController.h",
|
|
||||||
],
|
|
||||||
data = [
|
data = [
|
||||||
"Base.lproj/LaunchScreen.storyboard",
|
|
||||||
"Base.lproj/Main.storyboard",
|
|
||||||
"//mediapipe/graphs/face_detection:mobile_cpu_binary_graph",
|
"//mediapipe/graphs/face_detection:mobile_cpu_binary_graph",
|
||||||
"//mediapipe/models:face_detection_front.tflite",
|
"//mediapipe/models:face_detection_front.tflite",
|
||||||
"//mediapipe/models:face_detection_front_labelmap.txt",
|
"//mediapipe/models:face_detection_front_labelmap.txt",
|
||||||
],
|
],
|
||||||
sdk_frameworks = [
|
|
||||||
"AVFoundation",
|
|
||||||
"CoreGraphics",
|
|
||||||
"CoreMedia",
|
|
||||||
"UIKit",
|
|
||||||
],
|
|
||||||
deps = [
|
deps = [
|
||||||
"//mediapipe/objc:mediapipe_framework_ios",
|
"//mediapipe/examples/ios/common:CommonMediaPipeAppLibrary",
|
||||||
"//mediapipe/objc:mediapipe_input_sources_ios",
|
|
||||||
"//mediapipe/objc:mediapipe_layer_renderer",
|
|
||||||
] + select({
|
] + select({
|
||||||
"//mediapipe:ios_i386": [],
|
"//mediapipe:ios_i386": [],
|
||||||
"//mediapipe:ios_x86_64": [],
|
"//mediapipe:ios_x86_64": [],
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
||||||
<dependencies>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--View Controller-->
|
|
||||||
<scene sceneID="EHf-IW-A2E">
|
|
||||||
<objects>
|
|
||||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
</document>
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
|
||||||
<device id="retina4_7" orientation="portrait">
|
|
||||||
<adaptation id="fullscreen"/>
|
|
||||||
</device>
|
|
||||||
<dependencies>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--View Controller-->
|
|
||||||
<scene sceneID="tne-QT-ifu">
|
|
||||||
<objects>
|
|
||||||
<viewController id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EfB-xq-knP">
|
|
||||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Camera access needed for this demo. Please enable camera access in the Settings app." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GOf-1M-7j5">
|
|
||||||
<rect key="frame" x="57.478260869565474" y="248.17580340264635" width="260" height="151.00000000000014"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
<accessibility key="accessibilityConfiguration" label="PreviewDisplayView">
|
|
||||||
<bool key="isElement" value="YES"/>
|
|
||||||
</accessibility>
|
|
||||||
</view>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
|
||||||
</view>
|
|
||||||
<connections>
|
|
||||||
<outlet property="_liveView" destination="EfB-xq-knP" id="wac-VF-etz"/>
|
|
||||||
<outlet property="_noCameraLabel" destination="GOf-1M-7j5" id="gL5-Oy-Gkr"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="48.799999999999997" y="20.239880059970016"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
</document>
|
|
||||||
@@ -2,41 +2,13 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>CameraPosition</key>
|
||||||
<string>This app uses the camera to demonstrate live video processing.</string>
|
<string>front</string>
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>GraphOutputStream</key>
|
||||||
<string>en</string>
|
<string>output_video</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>GraphInputStream</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>input_video</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>GraphName</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>mobile_cpu</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1</string>
|
|
||||||
<key>LSRequiresIPhoneOS</key>
|
|
||||||
<true/>
|
|
||||||
<key>UILaunchStoryboardName</key>
|
|
||||||
<string>LaunchScreen</string>
|
|
||||||
<key>UIMainStoryboardFile</key>
|
|
||||||
<string>Main</string>
|
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
|
||||||
<array>
|
|
||||||
<string>armv7</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
</array>
|
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
|
||||||
<array>
|
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
@interface ViewController : UIViewController
|
|
||||||
|
|
||||||
@end
|
|
||||||