Compare commits

...
94 Commits
Author SHA1 Message Date
talksik 10214fa67c adding in personal config 2023-10-05 11:46:33 -07:00
Copybara-Service d686b42b85 Merge pull request #4852 from kinaryml:c-text-embedder-api
PiperOrigin-RevId: 571065228
2023-10-05 10:43:30 -07:00
Kinar ebfd7284c9 Fixed some issues with documentation 2023-10-05 04:25:18 -07:00
Kinar 92e13d43e4 Resolved some issues 2023-10-05 03:54:54 -07:00
MediaPipe TeamandCopybara-Service edc4db287c No public description
PiperOrigin-RevId: 570904787
2023-10-04 22:18:50 -07:00
MediaPipe TeamandCopybara-Service 7ab3d70aa4 Add stream API presence utils.
PiperOrigin-RevId: 570901832
2023-10-04 21:59:05 -07:00
Nevena KotlajaandCopybara-Service 2dd20822be No public description
PiperOrigin-RevId: 570789405
2023-10-04 13:33:51 -07:00
Sebastian SchmidtandCopybara-Service 1d8bd9c3ee No public description
PiperOrigin-RevId: 570765754
2023-10-04 12:14:31 -07:00
Daniel ChengandCopybara-Service d2baba6dbb Internal change
PiperOrigin-RevId: 570745425
2023-10-04 11:09:36 -07:00
MediaPipe TeamandCopybara-Service 3b99f8d9dd Introduce SetJointsVisibilityCalculator
PiperOrigin-RevId: 570745171
2023-10-04 11:04:40 -07:00
MediaPipe TeamandCopybara-Service c81624d7b2 Introduce CombineJointsCalculator
PiperOrigin-RevId: 570739088
2023-10-04 10:45:11 -07:00
Daniel ChengandCopybara-Service 7f1c17065a Prefix status macro implementation with MP_.
This makes it less likely for the implementation to conflict with other
ASSIGN_OR_RETURN() and RETURN_IF_ERROR() implementations.

PiperOrigin-RevId: 570726994
2023-10-04 10:04:59 -07:00
MediaPipe TeamandCopybara-Service 9bb042cc86 GlSurfaceViewRenderer: Capture graph output texture
Captures the original graph output texture, not what has been copied to the screen. This will be important for zooming to prevent top/bottom letterboxes, and it preserves the original quality, so that high resolution images can be used.

PiperOrigin-RevId: 570604422
2023-10-04 00:07:13 -07:00
MediaPipe TeamandCopybara-Service da8fcb6bb2 Smooth pose landmarks
PiperOrigin-RevId: 570441366
2023-10-03 11:12:46 -07:00
Sebastian SchmidtandCopybara-Service a72839ef99 See memory of freed result to nullptr
PiperOrigin-RevId: 570410751
2023-10-03 09:33:48 -07:00
MediaPipe TeamandCopybara-Service d0183b2c70 Fixes typos in the file mediapipe/python/pybind/image.cc and mediapipe/python/pybind
/image_frame.cc.

PiperOrigin-RevId: 570388388
2023-10-03 08:01:25 -07:00
Kinar 753ba916a1 Fixed some typos 2023-10-03 01:51:18 -07:00
Kinar 3564fc0d9b Added files for the TextEmbedder C API and tests 2023-10-03 01:48:07 -07:00
MediaPipe TeamandCopybara-Service 5366aa9d0a Internal update
PiperOrigin-RevId: 570204415
2023-10-02 15:55:31 -07:00
Sebastian SchmidtandCopybara-Service a00759007d Add error handling to C API
PiperOrigin-RevId: 570094642
2023-10-02 09:49:09 -07:00
Sebastian SchmidtandCopybara-Service c7402efe5e Add End to End test for Text Classifier C API
PiperOrigin-RevId: 569658768
2023-09-29 20:53:54 -07:00
Sebastian SchmidtandCopybara-Service 96fa10b906 Add unit tests for C layer for the input types of Text Classifier
PiperOrigin-RevId: 569553038
2023-09-29 12:08:00 -07:00
Sebastian SchmidtandCopybara-Service 6915a79e28 Add tests for C API containers
PiperOrigin-RevId: 569526282
2023-09-29 10:27:15 -07:00
Sebastian SchmidtandCopybara-Service d4561fb5c2 Do not use full filename when FileLocator decides which asset to load
Fixes https://github.com/google/mediapipe/issues/4819

PiperOrigin-RevId: 569506907
2023-09-29 09:11:01 -07:00
MediaPipe TeamandCopybara-Service 09a51bcdeb Internal change
PiperOrigin-RevId: 569310433
2023-09-28 15:44:37 -07:00
Sebastian SchmidtandCopybara-Service 5ca1be6f21 Populate the classification result output param instead of a copy
PiperOrigin-RevId: 569293617
2023-09-28 14:38:02 -07:00
MediaPipe TeamandCopybara-Service f78f24f576 segmentation smoothing stream utility function.
PiperOrigin-RevId: 569283980
2023-09-28 14:03:24 -07:00
Sebastian SchmidtandCopybara-Service 636cf99a3e Fix memory access issue in C layer
PiperOrigin-RevId: 569279959
2023-09-28 13:50:28 -07:00
MediaPipe TeamandCopybara-Service e169849041 No public description
PiperOrigin-RevId: 569274219
2023-09-28 13:31:00 -07:00
Sebastian SchmidtandCopybara-Service 33d6143a1a Don't convert nullptr to std::string in C layer
PiperOrigin-RevId: 569232756
2023-09-28 11:04:44 -07:00
MediaPipe TeamandCopybara-Service a577dc3043 smoothing stream utility function.
PiperOrigin-RevId: 569074973
2023-09-27 23:19:37 -07:00
MediaPipe TeamandCopybara-Service 9edb4cd753 Introduce LandmarksTransformationCalculator
PiperOrigin-RevId: 569050686
2023-09-27 21:12:19 -07:00
MediaPipe TeamandCopybara-Service 66a279418c tensor_to_joints stream utility function.
PiperOrigin-RevId: 569043195
2023-09-27 20:34:20 -07:00
MediaPipe TeamandCopybara-Service 0ae9ff6b98 Introduce TensorToJointsCalculator
PiperOrigin-RevId: 569040914
2023-09-27 20:27:24 -07:00
MediaPipe TeamandCopybara-Service da02052c70 landmarks_to_tensor stream utility function.
PiperOrigin-RevId: 569003241
2023-09-27 17:13:46 -07:00
MediaPipe TeamandCopybara-Service 8837b49026 get_vector_item stream utility function.
PiperOrigin-RevId: 568998504
2023-09-27 16:55:43 -07:00
MediaPipe TeamandCopybara-Service 2ecccaf076 concatenate stream utility function.
PiperOrigin-RevId: 568997695
2023-09-27 16:50:48 -07:00
MediaPipe TeamandCopybara-Service 983fda5d4e No public description
PiperOrigin-RevId: 568953918
2023-09-27 14:04:33 -07:00
MediaPipe TeamandCopybara-Service 8f8c66430f Update PackMediaSequenceCalculator to support index feature inputs on the CLIP_MEDIA_ input tag.
For Detection protos representing index features, the `label` field might be empty.

With this change, only the `Detection::score` field is required, and `Detection.label` and `Detection.label_id` are both optional but at least one of them should be set.

PiperOrigin-RevId: 568944596
2023-09-27 13:33:55 -07:00
MediaPipe TeamandCopybara-Service 698b154ff4 Only recreate immutable texture when necessary for Android TensorsToSegmentationCalculator.
PiperOrigin-RevId: 568937611
2023-09-27 13:09:29 -07:00
MediaPipe TeamandCopybara-Service 787371cfba No public description
PiperOrigin-RevId: 568927297
2023-09-27 12:27:57 -07:00
Sebastian SchmidtandCopybara-Service 61ce228576 Add cc_binary target for C Libraries
PiperOrigin-RevId: 568902427
2023-09-27 11:04:41 -07:00
Sebastian SchmidtandCopybara-Service b01ad84c6f Add export declaration to FaceDetector.detect()
PiperOrigin-RevId: 568872459
2023-09-27 09:28:53 -07:00
Sebastian SchmidtandCopybara-Service 3134625508 No public description
PiperOrigin-RevId: 568660415
2023-09-26 15:14:16 -07:00
Copybara-Service 0417817886 Merge pull request #4829 from priankakariatyml:ios-vision-api-name-changes
PiperOrigin-RevId: 568622587
2023-09-26 12:51:31 -07:00
MediaPipe TeamandCopybara-Service 199b42278b Fixes multiple typos in the tasks internal files.
PiperOrigin-RevId: 568585517
2023-09-26 10:45:07 -07:00
Sebastian SchmidtandCopybara-Service e5e75eac5e No public description
PiperOrigin-RevId: 568581409
2023-09-26 10:34:47 -07:00
MediaPipe TeamandCopybara-Service 573fdad173 Add export_model_with_tokenizer to Text Classifier API.
PiperOrigin-RevId: 567744604
2023-09-22 16:32:38 -07:00
Chris McClanahanandCopybara-Service 9d85141227 No public description
PiperOrigin-RevId: 567726262
2023-09-22 15:07:11 -07:00
Prianka Liz Kariat 2d4e5a75b3 Updated iOS Object Detector Objective C API names 2023-09-23 00:08:43 +05:30
Prianka Liz Kariat 435bee71e8 Updated iOS hand landmarker Objective C API names 2023-09-23 00:08:07 +05:30
Prianka Liz Kariat 9d42744f8a Updated iOS Face Landmarker Objective C API names 2023-09-23 00:07:49 +05:30
Prianka Liz Kariat d7c57e4eda Updated iOS Face Detector Objective C API names 2023-09-23 00:07:20 +05:30
MediaPipe TeamandCopybara-Service 34cedb980b No public description
PiperOrigin-RevId: 567562548
2023-09-22 02:24:25 -07:00
MediaPipe TeamandCopybara-Service 743118a04a Fixes multiple typos in the tasks internal files.
PiperOrigin-RevId: 567506050
2023-09-21 21:24:45 -07:00
MediaPipe TeamandCopybara-Service 859d90b68b No public description
PiperOrigin-RevId: 567439132
2023-09-21 15:40:19 -07:00
MediaPipe TeamandCopybara-Service abf0ee892a Internal Changes
PiperOrigin-RevId: 567374962
2023-09-21 11:55:02 -07:00
MediaPipe TeamandCopybara-Service 82d83f2dd8 Update glog to latest commit
PiperOrigin-RevId: 567337708
2023-09-21 10:10:04 -07:00
Copybara-Service 19c9d328cb Merge pull request #4815 from priankakariatyml:ios-image-segmenter-basic-tests
PiperOrigin-RevId: 567102079
2023-09-20 15:46:14 -07:00
MediaPipe TeamandCopybara-Service 223641a73c Fix depth condition bug when only depth condition is configured.
PiperOrigin-RevId: 567080598
2023-09-20 14:29:18 -07:00
MediaPipe TeamandCopybara-Service 41a012721f Fix bug missing SHOW_RESULT in image generator
PiperOrigin-RevId: 566810243
2023-09-19 18:37:35 -07:00
MediaPipe TeamandCopybara-Service 867d5dc5a7 No public description
PiperOrigin-RevId: 566794388
2023-09-19 17:27:55 -07:00
MediaPipe TeamandCopybara-Service bbf40cba87 split stream utility function.
PiperOrigin-RevId: 566722901
2023-09-19 13:21:39 -07:00
Copybara-Service 58bb2d1b92 Merge pull request #4811 from shmishra99:patch-1
PiperOrigin-RevId: 566676755
2023-09-19 10:43:12 -07:00
Sebastian SchmidtandCopybara-Service 12600e03e9 Do not convert milliseconds to microseconds twice
Fixes https://github.com/google/mediapipe/issues/4809

PiperOrigin-RevId: 566644379
2023-09-19 08:47:31 -07:00
Prianka Liz Kariat f1a5c8d549 Fixed formatting in MPPImageSegmenter.mm 2023-09-19 20:10:23 +05:30
Prianka Liz Kariat d9d4016334 Uncommented live stream test in iOS image segmenter tests 2023-09-19 20:06:25 +05:30
Prianka Liz Kariat bac60548dc Added selfie segmentation and running mode tests to image segmenter 2023-09-19 20:00:41 +05:30
Prianka Liz Kariat 08a5d55ac1 Updated interface of iOS image segmenter 2023-09-19 19:59:33 +05:30
Prianka Liz Kariat 6e80941215 Fixed premature deallocation of C++ masks in iOS Image Segmenter 2023-09-19 19:59:07 +05:30
MediaPipe TeamandCopybara-Service 0ed199186b Fix glScalerCalculator not clearing background in FIT mode
In FIT mode, the image is scaled in an aspect ratio preserving way. That means, the calculator does not render a full-screen quad. The letterbox areas are not drawn. This can cause artifacts, e.g. when MediaPipe reuses the GPU buffer from some other operation. This CL always clears the render target in FIT mode.

PiperOrigin-RevId: 566562715
2023-09-19 02:19:22 -07:00
Shivam MishraandGitHub fd062a2c3f Remove 'awaiting' labels when user issue/PR updated.
Remove the label "stat:awaiting response", when issue/PR unstale.
2023-09-19 11:28:20 +05:30
MediaPipe TeamandCopybara-Service 94cda40a83 No public description
PiperOrigin-RevId: 566435327
2023-09-18 15:50:10 -07:00
MediaPipe TeamandCopybara-Service 36f78f6e4a threshold stream utility function.
PiperOrigin-RevId: 566417914
2023-09-18 14:44:28 -07:00
MediaPipe TeamandCopybara-Service 58a7790081 detections_to_rects stream utility function.
PiperOrigin-RevId: 566358715
2023-09-18 11:17:27 -07:00
MediaPipe TeamandCopybara-Service f4477f1739 A minor typo fix in model maker image classifier hyperparameters python docstring.
PiperOrigin-RevId: 566355937
2023-09-18 11:07:58 -07:00
Copybara-Service d5fa4a157e Merge pull request #4802 from priankakariatyml:ios-image-segmenter-basic-tests
PiperOrigin-RevId: 565797057
2023-09-15 15:45:54 -07:00
Copybara-Service 838c89a3ff Merge pull request #4801 from priankakariatyml:ios-face-stylizer
PiperOrigin-RevId: 565784200
2023-09-15 14:49:50 -07:00
Sebastian SchmidtandCopybara-Service 94477b1342 No public description
PiperOrigin-RevId: 565755048
2023-09-15 12:55:00 -07:00
Sebastian SchmidtandCopybara-Service a933e324b5 Add export declaration for FaceDetector
Fixes https://github.com/google/mediapipe/issues/4799

PiperOrigin-RevId: 565706741
2023-09-15 10:06:06 -07:00
Sebastian SchmidtandCopybara-Service 30590fe8d3 Add helper to create Connection array
PiperOrigin-RevId: 565706612
2023-09-15 10:00:28 -07:00
Prianka Liz Kariat 0f511d52d6 Fixed typo in iOS MPPImageSegmenterResult helpers 2023-09-15 14:24:15 +05:30
Prianka Liz Kariat b3be1418da Updated multiply function in iOS Image Segmenter tests to use C++ vectors 2023-09-15 14:21:33 +05:30
Prianka Liz Kariat fad7f9cdb4 Added iOS image segmenter basic Objective C tests 2023-09-15 14:18:54 +05:30
Prianka Liz Kariat d3f7368b27 Added iOS MPPMask test utils 2023-09-15 14:18:23 +05:30
Prianka Liz Kariat 81ec5801ea Added new initializers for iOS MPPImage in test utils 2023-09-15 14:18:10 +05:30
Prianka Liz Kariat e0b059da58 Added iOS MPPFileInfo for tests 2023-09-15 14:16:50 +05:30
Prianka Liz Kariat 21d000490c Added iOS face stylizer header 2023-09-15 14:06:11 +05:30
Prianka Liz Kariat bb93b775f4 Added iOS face stylizer options 2023-09-15 14:05:59 +05:30
Prianka Liz Kariat a259300bfe Added iOS face stylizer result 2023-09-15 14:05:38 +05:30
Sebastian SchmidtandCopybara-Service 4a8a811373 No public description
PiperOrigin-RevId: 565516592
2023-09-14 17:00:02 -07:00
MediaPipe TeamandCopybara-Service 81964608ba Fixes typo in MediaPipe namespace
PiperOrigin-RevId: 565478366
2023-09-14 14:32:35 -07:00
Daniel ChengandCopybara-Service 65e7cd5236 Fix win32 build break in mediapipe.
buffer_aligned_size_ is not used in tflite_support, but is used
in mediapipe.

PiperOrigin-RevId: 565477047
2023-09-14 14:27:05 -07:00
MediaPipe TeamandCopybara-Service f2b11bf250 No public description
PiperOrigin-RevId: 565446429
2023-09-14 12:41:12 -07:00
347 changed files with 10872 additions and 2318 deletions
+3 -1
View File
@@ -39,7 +39,9 @@ jobs:
# Limit the No. of API calls in one run default value is 30. # Limit the No. of API calls in one run default value is 30.
operations-per-run: 500 operations-per-run: 500
# Prevent to remove stale label when PRs or issues are updated. # Prevent to remove stale label when PRs or issues are updated.
remove-stale-when-updated: false remove-stale-when-updated: true
# List of labels to remove when issues/PRs unstale.
labels-to-remove-when-unstale: 'stat:awaiting response'
# comment on issue if not active for more then 7 days. # comment on issue if not active for more then 7 days.
stale-issue-message: 'This issue has been marked stale because it has no recent activity since 7 days. It will be closed if no further activity occurs. Thank you.' stale-issue-message: 'This issue has been marked stale because it has no recent activity since 7 days. It will be closed if no further activity occurs. Thank you.'
# comment on PR if not active for more then 14 days. # comment on PR if not active for more then 14 days.
+25 -6
View File
@@ -154,19 +154,19 @@ http_archive(
# 2020-08-21 # 2020-08-21
http_archive( http_archive(
name = "com_github_glog_glog", name = "com_github_glog_glog",
strip_prefix = "glog-3a0d4d22c5ae0b9a2216988411cfa6bf860cc372", strip_prefix = "glog-0.6.0",
sha256 = "170d08f80210b82d95563f4723a15095eff1aad1863000e8eeb569c96a98fefb", sha256 = "8a83bf982f37bb70825df71a9709fa90ea9f4447fb3c099e1d720a439d88bad6",
urls = [ urls = [
"https://github.com/google/glog/archive/3a0d4d22c5ae0b9a2216988411cfa6bf860cc372.zip", "https://github.com/google/glog/archive/v0.6.0.tar.gz",
], ],
) )
http_archive( http_archive(
name = "com_github_glog_glog_no_gflags", name = "com_github_glog_glog_no_gflags",
strip_prefix = "glog-3a0d4d22c5ae0b9a2216988411cfa6bf860cc372", strip_prefix = "glog-0.6.0",
sha256 = "170d08f80210b82d95563f4723a15095eff1aad1863000e8eeb569c96a98fefb", sha256 = "8a83bf982f37bb70825df71a9709fa90ea9f4447fb3c099e1d720a439d88bad6",
build_file = "@//third_party:glog_no_gflags.BUILD", build_file = "@//third_party:glog_no_gflags.BUILD",
urls = [ urls = [
"https://github.com/google/glog/archive/3a0d4d22c5ae0b9a2216988411cfa6bf860cc372.zip", "https://github.com/google/glog/archive/v0.6.0.tar.gz",
], ],
patches = [ patches = [
"@//third_party:com_github_glog_glog.diff", "@//third_party:com_github_glog_glog.diff",
@@ -176,6 +176,25 @@ http_archive(
], ],
) )
# 2023-06-05
# This version of Glog is required for Windows support, but currently causes
# crashes on some Android devices.
http_archive(
name = "com_github_glog_glog_windows",
strip_prefix = "glog-3a0d4d22c5ae0b9a2216988411cfa6bf860cc372",
sha256 = "170d08f80210b82d95563f4723a15095eff1aad1863000e8eeb569c96a98fefb",
urls = [
"https://github.com/google/glog/archive/3a0d4d22c5ae0b9a2216988411cfa6bf860cc372.zip",
],
patches = [
"@//third_party:com_github_glog_glog.diff",
"@//third_party:com_github_glog_glog_windows_patch.diff",
],
patch_args = [
"-p1",
],
)
# easyexif # easyexif
http_archive( http_archive(
name = "easyexif", name = "easyexif",
+25
View File
@@ -325,6 +325,7 @@ cc_library(
":concatenate_vector_calculator_cc_proto", ":concatenate_vector_calculator_cc_proto",
"//mediapipe/framework:calculator_framework", "//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:node", "//mediapipe/framework/api2:node",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:classification_cc_proto", "//mediapipe/framework/formats:classification_cc_proto",
"//mediapipe/framework/formats:landmark_cc_proto", "//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/port:ret_check", "//mediapipe/framework/port:ret_check",
@@ -944,6 +945,7 @@ cc_library(
deps = [ deps = [
":split_vector_calculator_cc_proto", ":split_vector_calculator_cc_proto",
"//mediapipe/framework:calculator_framework", "//mediapipe/framework:calculator_framework",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:landmark_cc_proto", "//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/port:ret_check", "//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status", "//mediapipe/framework/port:status",
@@ -1389,3 +1391,26 @@ cc_test(
"@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:optional",
], ],
) )
cc_library(
name = "value_or_default_calculator",
srcs = ["value_or_default_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/port:status",
],
alwayslink = True,
)
cc_test(
name = "value_or_default_calculator_test",
srcs = ["value_or_default_calculator_test.cc"],
deps = [
":value_or_default_calculator",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:calculator_runner",
"//mediapipe/framework:packet",
"//mediapipe/framework/port:gtest_main",
],
)
@@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
@@ -163,6 +164,75 @@ TEST_F(BeginEndLoopCalculatorGraphTest, MultipleVectors) {
PacketOfIntsEq(input_timestamp2, std::vector<int>{3, 4}))); PacketOfIntsEq(input_timestamp2, std::vector<int>{3, 4})));
} }
TEST(BeginEndLoopCalculatorPossibleDataRaceTest,
EndLoopForIntegersDoesNotRace) {
auto graph_config = ParseTextProtoOrDie<CalculatorGraphConfig>(
R"pb(
num_threads: 4
input_stream: "ints"
node {
calculator: "BeginLoopIntegerCalculator"
input_stream: "ITERABLE:ints"
output_stream: "ITEM:int"
output_stream: "BATCH_END:timestamp"
}
node {
calculator: "IncrementCalculator"
input_stream: "int"
output_stream: "int_plus_one"
}
# BEGIN: Data race possibility
# EndLoop###Calculator and another calculator using the same input
# may introduce race due to EndLoop###Calculator possibly consuming
# packet.
node {
calculator: "EndLoopIntegersCalculator"
input_stream: "ITEM:int_plus_one"
input_stream: "BATCH_END:timestamp"
output_stream: "ITERABLE:ints_plus_one"
}
node {
calculator: "IncrementCalculator"
input_stream: "int_plus_one"
output_stream: "int_plus_two"
}
# END: Data race possibility
node {
calculator: "EndLoopIntegersCalculator"
input_stream: "ITEM:int_plus_two"
input_stream: "BATCH_END:timestamp"
output_stream: "ITERABLE:ints_plus_two"
}
)pb");
std::vector<Packet> int_plus_one_packets;
tool::AddVectorSink("ints_plus_one", &graph_config, &int_plus_one_packets);
std::vector<Packet> int_original_packets;
tool::AddVectorSink("ints_plus_two", &graph_config, &int_original_packets);
CalculatorGraph graph;
MP_ASSERT_OK(graph.Initialize(graph_config));
MP_ASSERT_OK(graph.StartRun({}));
for (int i = 0; i < 100; ++i) {
std::vector<int> ints = {i, i + 1, i + 2};
Timestamp ts = Timestamp(i);
MP_ASSERT_OK(graph.AddPacketToInputStream(
"ints", MakePacket<std::vector<int>>(std::move(ints)).At(ts)));
MP_ASSERT_OK(graph.WaitUntilIdle());
EXPECT_THAT(int_plus_one_packets,
testing::ElementsAre(
PacketOfIntsEq(ts, std::vector<int>{i + 1, i + 2, i + 3})));
EXPECT_THAT(int_original_packets,
testing::ElementsAre(
PacketOfIntsEq(ts, std::vector<int>{i + 2, i + 3, i + 4})));
int_plus_one_packets.clear();
int_original_packets.clear();
}
MP_ASSERT_OK(graph.CloseAllPacketSources());
MP_ASSERT_OK(graph.WaitUntilDone());
}
// Passes non empty vector through or outputs empty vector in case of timestamp // Passes non empty vector through or outputs empty vector in case of timestamp
// bound update. // bound update.
class PassThroughOrEmptyVectorCalculator : public CalculatorBase { class PassThroughOrEmptyVectorCalculator : public CalculatorBase {
@@ -92,7 +92,7 @@ class BypassCalculator : public Node {
auto options = cc->Options<BypassCalculatorOptions>(); auto options = cc->Options<BypassCalculatorOptions>();
RET_CHECK_EQ(options.pass_input_stream().size(), RET_CHECK_EQ(options.pass_input_stream().size(),
options.pass_output_stream().size()); options.pass_output_stream().size());
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
auto pass_streams, auto pass_streams,
GetPassMap(options, *cc->Inputs().TagMap(), *cc->Outputs().TagMap())); GetPassMap(options, *cc->Inputs().TagMap(), *cc->Outputs().TagMap()));
std::set<CollectionItemId> pass_out; std::set<CollectionItemId> pass_out;
@@ -121,8 +121,9 @@ class BypassCalculator : public Node {
// Saves the map of passthrough input and output stream ids. // Saves the map of passthrough input and output stream ids.
absl::Status Open(CalculatorContext* cc) override { absl::Status Open(CalculatorContext* cc) override {
auto options = cc->Options<BypassCalculatorOptions>(); auto options = cc->Options<BypassCalculatorOptions>();
ASSIGN_OR_RETURN(pass_streams_, GetPassMap(options, *cc->Inputs().TagMap(), MP_ASSIGN_OR_RETURN(
*cc->Outputs().TagMap())); pass_streams_,
GetPassMap(options, *cc->Inputs().TagMap(), *cc->Outputs().TagMap()));
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -18,6 +18,7 @@
#include "mediapipe/calculators/core/concatenate_vector_calculator.pb.h" #include "mediapipe/calculators/core/concatenate_vector_calculator.pb.h"
#include "mediapipe/framework/api2/node.h" #include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/calculator_framework.h" #include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/classification.pb.h" #include "mediapipe/framework/formats/classification.pb.h"
#include "mediapipe/framework/formats/landmark.pb.h" #include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/port/canonical_errors.h" #include "mediapipe/framework/port/canonical_errors.h"
@@ -128,6 +129,19 @@ class ConcatenateClassificationListCalculator
}; };
MEDIAPIPE_REGISTER_NODE(ConcatenateClassificationListCalculator); MEDIAPIPE_REGISTER_NODE(ConcatenateClassificationListCalculator);
class ConcatenateJointListCalculator
: public ConcatenateListsCalculator<Joint, JointList> {
protected:
int ListSize(const JointList& list) const override {
return list.joint_size();
}
const Joint GetItem(const JointList& list, int idx) const override {
return list.joint(idx);
}
Joint* AddItem(JointList& list) const override { return list.add_joint(); }
};
MEDIAPIPE_REGISTER_NODE(ConcatenateJointListCalculator);
} // namespace api2 } // namespace api2
} // namespace mediapipe } // namespace mediapipe
@@ -55,16 +55,16 @@ class EndLoopCalculator : public CalculatorBase {
if (!input_stream_collection_) { if (!input_stream_collection_) {
input_stream_collection_.reset(new IterableT); input_stream_collection_.reset(new IterableT);
} }
// Try to consume the item and move it into the collection. If the items
// are not consumable, then try to copy them instead. If the items are if constexpr (std::is_copy_constructible_v<ItemT>) {
// not copyable, then an error will be returned. input_stream_collection_->push_back(
auto item_ptr_or = cc->Inputs().Tag("ITEM").Value().Consume<ItemT>(); cc->Inputs().Tag("ITEM").Get<ItemT>());
if (item_ptr_or.ok()) {
input_stream_collection_->push_back(std::move(*item_ptr_or.value()));
} else { } else {
if constexpr (std::is_copy_constructible_v<ItemT>) { // Try to consume the item and move it into the collection. Return an
input_stream_collection_->push_back( // error if the items are not consumable.
cc->Inputs().Tag("ITEM").template Get<ItemT>()); auto item_ptr_or = cc->Inputs().Tag("ITEM").Value().Consume<ItemT>();
if (item_ptr_or.ok()) {
input_stream_collection_->push_back(std::move(*item_ptr_or.value()));
} else { } else {
return absl::InternalError( return absl::InternalError(
"The item type is not copiable. Consider making the " "The item type is not copiable. Consider making the "
@@ -17,6 +17,7 @@
#include "mediapipe/calculators/core/split_vector_calculator.pb.h" #include "mediapipe/calculators/core/split_vector_calculator.pb.h"
#include "mediapipe/framework/calculator_framework.h" #include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/landmark.pb.h" #include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/port/canonical_errors.h" #include "mediapipe/framework/port/canonical_errors.h"
#include "mediapipe/framework/port/ret_check.h" #include "mediapipe/framework/port/ret_check.h"
@@ -196,6 +197,18 @@ class SplitLandmarkListCalculator
}; };
REGISTER_CALCULATOR(SplitLandmarkListCalculator); REGISTER_CALCULATOR(SplitLandmarkListCalculator);
class SplitJointListCalculator : public SplitListsCalculator<Joint, JointList> {
protected:
int ListSize(const JointList& list) const override {
return list.joint_size();
}
const Joint GetItem(const JointList& list, int idx) const override {
return list.joint(idx);
}
Joint* AddItem(JointList& list) const override { return list.add_joint(); }
};
REGISTER_CALCULATOR(SplitJointListCalculator);
} // namespace mediapipe } // namespace mediapipe
// NOLINTNEXTLINE // NOLINTNEXTLINE
@@ -0,0 +1,90 @@
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/status.h"
namespace mediapipe {
namespace {
constexpr char kInputValueTag[] = "IN";
constexpr char kTickerTag[] = "TICK";
constexpr char kOutputTag[] = "OUT";
constexpr char kIndicationTag[] = "FLAG";
} // namespace
// For every packet received on the TICK stream, if the IN stream is not
// empty - emit its value as is as OUT. Otherwise output a default packet.
// FLAG outputs true every time the default value has been used. It does not
// output anything when IN has a value.
//
// Example config:
// node {
// calculator: "ValueOrDefaultCalculator"
// input_stream: "IN:sometimes_missing_value"
// input_stream: "TICK:clock"
// output_stream: "OUT:value_or_default"
// output_stream: "FLAG:used_default"
// input_side_packet: "default"
// }
//
// TODO: Consider adding an option for a default value as a input-stream
// instead of a side-packet, so it will enable using standard calculators
// instead of creating a new packet-generators. It will also allow a dynamic
// default value.
class ValueOrDefaultCalculator : public mediapipe::CalculatorBase {
public:
ValueOrDefaultCalculator() {}
ValueOrDefaultCalculator(const ValueOrDefaultCalculator&) = delete;
ValueOrDefaultCalculator& operator=(const ValueOrDefaultCalculator&) = delete;
static mediapipe::Status GetContract(mediapipe::CalculatorContract* cc) {
cc->Inputs().Tag(kInputValueTag).SetAny();
cc->Inputs().Tag(kTickerTag).SetAny();
cc->Outputs().Tag(kOutputTag).SetSameAs(&cc->Inputs().Tag(kInputValueTag));
cc->Outputs().Tag(kIndicationTag).Set<bool>();
cc->InputSidePackets().Index(0).SetSameAs(
&cc->Inputs().Tag(kInputValueTag));
return mediapipe::OkStatus();
}
mediapipe::Status Open(mediapipe::CalculatorContext* cc) override {
if (!cc->Inputs().Tag(kInputValueTag).Header().IsEmpty()) {
cc->Outputs()
.Tag(kOutputTag)
.SetHeader(cc->Inputs().Tag(kInputValueTag).Header());
}
default_ = cc->InputSidePackets().Index(0);
cc->SetOffset(mediapipe::TimestampDiff(0));
return mediapipe::OkStatus();
}
mediapipe::Status Process(mediapipe::CalculatorContext* cc) override {
// Output according to the TICK signal.
if (cc->Inputs().Tag(kTickerTag).IsEmpty()) {
return mediapipe::OkStatus();
}
if (!cc->Inputs().Tag(kInputValueTag).IsEmpty()) {
// Output the input as is:
cc->Outputs()
.Tag(kOutputTag)
.AddPacket(cc->Inputs().Tag(kInputValueTag).Value());
} else {
// Output default:
cc->Outputs()
.Tag(kOutputTag)
.AddPacket(default_.At(cc->InputTimestamp()));
cc->Outputs()
.Tag(kIndicationTag)
.Add(new bool(true), cc->InputTimestamp());
}
return mediapipe::OkStatus();
}
private:
// The default value to replicate every time there is no new value.
mediapipe::Packet default_;
};
REGISTER_CALCULATOR(ValueOrDefaultCalculator);
} // namespace mediapipe
@@ -0,0 +1,240 @@
#include <algorithm>
#include <cstdint>
#include <vector>
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/calculator_runner.h"
#include "mediapipe/framework/packet.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/port/status_matchers.h"
namespace mediapipe {
namespace {
using ::testing::AllOf;
using ::testing::ContainerEq;
using ::testing::Each;
using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::SizeIs;
using ::testing::Test;
const int kDefaultValue = 0;
// Utility to a create a mediapipe graph runner with the tested calculator and a
// default value, for all the tests.
class ValueOrDefaultRunner : public mediapipe::CalculatorRunner {
public:
ValueOrDefaultRunner()
: mediapipe::CalculatorRunner(R"pb(
calculator: "ValueOrDefaultCalculator"
input_stream: "IN:in"
input_stream: "TICK:tick"
input_side_packet: "default"
output_stream: "OUT:out"
output_stream: "FLAG:used_default"
)pb") {
MutableSidePackets()->Index(0) = mediapipe::MakePacket<int>(kDefaultValue);
}
// Utility to push inputs to the runner to the TICK stream, so we could easily
// tick.
void TickAt(int64_t time) {
// The type or value of the stream isn't relevant, we use just a bool.
MutableInputs()->Tag("TICK").packets.push_back(
mediapipe::Adopt(new bool(false)).At(mediapipe::Timestamp(time)));
}
// Utility to push the real inputs to the runner (IN stream).
void ProvideInput(int64_t time, int value) {
MutableInputs()->Tag("IN").packets.push_back(
mediapipe::Adopt(new int(value)).At(mediapipe::Timestamp(time)));
}
// Extracts the timestamps (as int64) of the output stream of the calculator.
std::vector<int64_t> GetOutputTimestamps() const {
std::vector<int64_t> timestamps;
for (const mediapipe::Packet& packet : Outputs().Tag("OUT").packets) {
timestamps.emplace_back(packet.Timestamp().Value());
}
return timestamps;
}
// Extracts the values from the output stream of the calculator.
std::vector<int> GetOutputValues() const {
std::vector<int> values;
for (const mediapipe::Packet& packet : Outputs().Tag("OUT").packets) {
values.emplace_back(packet.Get<int>());
}
return values;
}
// Extracts the timestamps (as int64) of the flag stream, which indicates on
// times without an input value (i.e. using the default value).
std::vector<int64_t> GetFlagTimestamps() const {
std::vector<int64_t> timestamps;
for (const mediapipe::Packet& packet : Outputs().Tag("FLAG").packets) {
timestamps.emplace_back(packet.Timestamp().Value());
}
return timestamps;
}
// Extracts the output from the flags stream (which should always be true).
std::vector<bool> GetFlagValues() const {
std::vector<bool> flags;
for (const mediapipe::Packet& packet : Outputs().Tag("FLAG").packets) {
flags.emplace_back(packet.Get<bool>());
}
return flags;
}
};
// To be used as input values:
std::vector<int> GetIntegersRange(int size) {
std::vector<int> result;
for (int i = 0; i < size; ++i) {
// We start with default-value+1 so it won't contain the default value.
result.push_back(kDefaultValue + 1 + i);
}
return result;
}
TEST(ValueOrDefaultCalculatorTest, NoInputs) {
// Check that when no real inputs are provided - we get the default value over
// and over, with the correct timestamps.
ValueOrDefaultRunner runner;
const std::vector<int64_t> ticks = {0, 1, 2, 5, 8, 12, 33, 231};
for (int tick : ticks) {
runner.TickAt(tick);
}
MP_EXPECT_OK(runner.Run());
// Make sure we get the right timestamps:
EXPECT_THAT(runner.GetOutputTimestamps(), ContainerEq(ticks));
// All should be default value:
EXPECT_THAT(runner.GetOutputValues(),
AllOf(Each(kDefaultValue), SizeIs(ticks.size())));
// We should get the default indication all the time:
EXPECT_THAT(runner.GetFlagTimestamps(), ContainerEq(ticks));
}
TEST(ValueOrDefaultCalculatorTest, NeverDefault) {
// Check that when we provide the inputs on time - we get them as outputs.
ValueOrDefaultRunner runner;
const std::vector<int64_t> ticks = {0, 1, 2, 5, 8, 12, 33, 231};
const std::vector<int> values = GetIntegersRange(ticks.size());
for (int i = 0; i < ticks.size(); ++i) {
runner.TickAt(ticks[i]);
runner.ProvideInput(ticks[i], values[i]);
}
MP_EXPECT_OK(runner.Run());
// Make sure we get the right timestamps:
EXPECT_THAT(runner.GetOutputTimestamps(), ContainerEq(ticks));
// Should get the inputs values:
EXPECT_THAT(runner.GetOutputValues(), ContainerEq(values));
// We should never get the default indication:
EXPECT_THAT(runner.GetFlagTimestamps(), IsEmpty());
}
TEST(ValueOrDefaultCalculatorTest, DefaultAndValues) {
// Check that when we provide inputs only part of the time - we get them, but
// defaults at the missing times.
// That's the usual use case for this calculator.
ValueOrDefaultRunner runner;
const std::vector<int64_t> ticks = {0, 1, 5, 8, 12, 231};
// Provide inputs only part of the ticks.
// Chosen so there will be defaults before the first input, between the
// inputs and after the last input.
const std::vector<int64_t> in_ticks = {/*0,*/ 1, 5, /*8,*/ 12, /*, 231*/};
const std::vector<int> in_values = GetIntegersRange(in_ticks.size());
for (int tick : ticks) {
runner.TickAt(tick);
}
for (int i = 0; i < in_ticks.size(); ++i) {
runner.ProvideInput(in_ticks[i], in_values[i]);
}
MP_EXPECT_OK(runner.Run());
// Make sure we get all the timestamps:
EXPECT_THAT(runner.GetOutputTimestamps(), ContainerEq(ticks));
// The timestamps of the flag should be exactly the ones not in in_ticks.
EXPECT_THAT(runner.GetFlagTimestamps(), ElementsAre(0, 8, 231));
// And the values are default in these times, and the input values for
// in_ticks.
EXPECT_THAT(
runner.GetOutputValues(),
ElementsAre(kDefaultValue, 1, 2, kDefaultValue, 3, kDefaultValue));
}
TEST(ValueOrDefaultCalculatorTest, TimestampsMissmatch) {
// Check that when we provide the inputs not on time - we don't get them.
ValueOrDefaultRunner runner;
const std::vector<int64_t> ticks = {1, 2, 5, 8, 12, 33, 231};
// The timestamps chosen so it will be before the first tick, in between ticks
// and after the last one. Also - more inputs than ticks.
const std::vector<int64_t> in_ticks = {0, 3, 4, 6, 7, 9, 10,
11, 13, 14, 15, 16, 232};
const std::vector<int> in_values = GetIntegersRange(in_ticks.size());
for (int tick : ticks) {
runner.TickAt(tick);
}
for (int i = 0; i < in_ticks.size(); ++i) {
runner.ProvideInput(in_ticks[i], in_values[i]);
}
MP_EXPECT_OK(runner.Run());
// Non of the in_ticks should be inserted:
EXPECT_THAT(runner.GetOutputTimestamps(), ContainerEq(ticks));
EXPECT_THAT(runner.GetOutputValues(),
AllOf(Each(kDefaultValue), SizeIs(ticks.size())));
// All (and only) ticks should get the default.
EXPECT_THAT(runner.GetFlagTimestamps(), ContainerEq(ticks));
}
TEST(ValueOrDefaultCalculatorTest, FlagValue) {
// Since we anyway suppose that the Flag is a bool - there is nothing
// interesting to check, but we should check once that the value is the right
// (true) one.
ValueOrDefaultRunner runner;
runner.TickAt(0);
MP_EXPECT_OK(runner.Run());
EXPECT_THAT(runner.GetFlagValues(), ElementsAre(true));
}
TEST(ValueOrDefaultCalculatorTest, FullTest) {
// Make sure that nothing gets wrong with an input that have both right and
// wrong timestamps, some defaults etc.
ValueOrDefaultRunner runner;
const std::vector<int64_t> ticks = {1, 2, 5, 8, 12, 33, 231};
const std::vector<int64_t> in_ticks = {0, 2, 4, 6, 8, 9, 12, 33, 54, 232};
const std::vector<int> in_values = GetIntegersRange(in_ticks.size());
for (int tick : ticks) {
runner.TickAt(tick);
}
for (int i = 0; i < in_ticks.size(); ++i) {
runner.ProvideInput(in_ticks[i], in_values[i]);
}
MP_EXPECT_OK(runner.Run());
EXPECT_THAT(runner.GetOutputTimestamps(), ContainerEq(ticks));
// Calculated by hand:
EXPECT_THAT(
runner.GetOutputValues(),
ElementsAre(kDefaultValue, 2, kDefaultValue, 5, 7, 8, kDefaultValue));
EXPECT_THAT(runner.GetFlagTimestamps(), ElementsAre(1, 5, 231));
EXPECT_THAT(runner.GetFlagValues(), AllOf(Each(true), SizeIs(3)));
}
} // namespace
} // namespace mediapipe
@@ -223,7 +223,7 @@ class GlTextureWarpAffineRunner
absl::StrCat(mediapipe::kMediaPipeFragmentShaderPreamble, absl::StrCat(mediapipe::kMediaPipeFragmentShaderPreamble,
interpolation_def, kFragShader); interpolation_def, kFragShader);
ASSIGN_OR_RETURN(program_, create_fn(vert_src, frag_src)); MP_ASSIGN_OR_RETURN(program_, create_fn(vert_src, frag_src));
auto create_custom_zero_fn = [&]() -> absl::StatusOr<Program> { auto create_custom_zero_fn = [&]() -> absl::StatusOr<Program> {
std::string custom_zero_border_mode_def = R"( std::string custom_zero_border_mode_def = R"(
@@ -236,10 +236,10 @@ class GlTextureWarpAffineRunner
}; };
#if GL_CLAMP_TO_BORDER_MAY_BE_SUPPORTED #if GL_CLAMP_TO_BORDER_MAY_BE_SUPPORTED
if (!IsGlClampToBorderSupported(gl_helper_->GetGlContext())) { if (!IsGlClampToBorderSupported(gl_helper_->GetGlContext())) {
ASSIGN_OR_RETURN(program_custom_zero_, create_custom_zero_fn()); MP_ASSIGN_OR_RETURN(program_custom_zero_, create_custom_zero_fn());
} }
#else #else
ASSIGN_OR_RETURN(program_custom_zero_, create_custom_zero_fn()); MP_ASSIGN_OR_RETURN(program_custom_zero_, create_custom_zero_fn());
#endif // GL_CLAMP_TO_BORDER_MAY_BE_SUPPORTED #endif // GL_CLAMP_TO_BORDER_MAY_BE_SUPPORTED
glGenFramebuffers(1, &framebuffer_); glGenFramebuffers(1, &framebuffer_);
@@ -92,11 +92,11 @@ absl::StatusOr<ImageFileProperties> GetImageFileProperites(
properties.set_focal_length_mm(result.FocalLength); properties.set_focal_length_mm(result.FocalLength);
properties.set_focal_length_35mm(result.FocalLengthIn35mm); properties.set_focal_length_35mm(result.FocalLengthIn35mm);
ASSIGN_OR_RETURN(auto focal_length_pixels, MP_ASSIGN_OR_RETURN(auto focal_length_pixels,
ComputeFocalLengthInPixels(properties.image_width(), ComputeFocalLengthInPixels(properties.image_width(),
properties.image_height(), properties.image_height(),
properties.focal_length_35mm(), properties.focal_length_35mm(),
properties.focal_length_mm())); properties.focal_length_mm()));
properties.set_focal_length_pixels(focal_length_pixels); properties.set_focal_length_pixels(focal_length_pixels);
return properties; return properties;
@@ -151,7 +151,7 @@ class ImageFilePropertiesCalculator : public CalculatorBase {
if (cc->InputSidePackets().NumEntries() == 1) { if (cc->InputSidePackets().NumEntries() == 1) {
const std::string& image_bytes = const std::string& image_bytes =
cc->InputSidePackets().Index(0).Get<std::string>(); cc->InputSidePackets().Index(0).Get<std::string>();
ASSIGN_OR_RETURN(properties_, GetImageFileProperites(image_bytes)); MP_ASSIGN_OR_RETURN(properties_, GetImageFileProperites(image_bytes));
read_properties_ = true; read_properties_ = true;
} }
@@ -169,7 +169,7 @@ class ImageFilePropertiesCalculator : public CalculatorBase {
return absl::OkStatus(); return absl::OkStatus();
} }
const std::string& image_bytes = cc->Inputs().Index(0).Get<std::string>(); const std::string& image_bytes = cc->Inputs().Index(0).Get<std::string>();
ASSIGN_OR_RETURN(properties_, GetImageFileProperites(image_bytes)); MP_ASSIGN_OR_RETURN(properties_, GetImageFileProperites(image_bytes));
read_properties_ = true; read_properties_ = true;
} }
if (read_properties_) { if (read_properties_) {
@@ -79,8 +79,8 @@ class WarpAffineRunnerHolder<ImageFrame> {
} }
absl::StatusOr<RunnerType*> GetRunner() { absl::StatusOr<RunnerType*> GetRunner() {
if (!runner_) { if (!runner_) {
ASSIGN_OR_RETURN(runner_, MP_ASSIGN_OR_RETURN(
CreateAffineTransformationOpenCvRunner(interpolation_)); runner_, CreateAffineTransformationOpenCvRunner(interpolation_));
} }
return runner_.get(); return runner_.get();
} }
@@ -108,8 +108,9 @@ class WarpAffineRunnerHolder<mediapipe::GpuBuffer> {
} }
absl::StatusOr<RunnerType*> GetRunner() { absl::StatusOr<RunnerType*> GetRunner() {
if (!runner_) { if (!runner_) {
ASSIGN_OR_RETURN(runner_, CreateAffineTransformationGlRunner( MP_ASSIGN_OR_RETURN(
gl_helper_, gpu_origin_, interpolation_)); runner_, CreateAffineTransformationGlRunner(gl_helper_, gpu_origin_,
interpolation_));
} }
return runner_.get(); return runner_.get();
} }
@@ -151,24 +152,25 @@ class WarpAffineRunnerHolder<mediapipe::Image> {
AffineTransformation::BorderMode border_mode) override { AffineTransformation::BorderMode border_mode) override {
if (input.UsesGpu()) { if (input.UsesGpu()) {
#if !MEDIAPIPE_DISABLE_GPU #if !MEDIAPIPE_DISABLE_GPU
ASSIGN_OR_RETURN(auto* runner, gpu_holder_.GetRunner()); MP_ASSIGN_OR_RETURN(auto* runner, gpu_holder_.GetRunner());
ASSIGN_OR_RETURN(auto result, runner->Run(input.GetGpuBuffer(), matrix, MP_ASSIGN_OR_RETURN(
size, border_mode)); auto result,
runner->Run(input.GetGpuBuffer(), matrix, size, border_mode));
return mediapipe::Image(*result); return mediapipe::Image(*result);
#else #else
return absl::UnavailableError("GPU support is disabled"); return absl::UnavailableError("GPU support is disabled");
#endif // !MEDIAPIPE_DISABLE_GPU #endif // !MEDIAPIPE_DISABLE_GPU
} }
#if !MEDIAPIPE_DISABLE_OPENCV #if !MEDIAPIPE_DISABLE_OPENCV
ASSIGN_OR_RETURN(auto* runner, cpu_holder_.GetRunner()); MP_ASSIGN_OR_RETURN(auto* runner, cpu_holder_.GetRunner());
const auto& frame_ptr = input.GetImageFrameSharedPtr(); const auto& frame_ptr = input.GetImageFrameSharedPtr();
// Wrap image into image frame. // Wrap image into image frame.
const ImageFrame image_frame(frame_ptr->Format(), frame_ptr->Width(), const ImageFrame image_frame(frame_ptr->Format(), frame_ptr->Width(),
frame_ptr->Height(), frame_ptr->WidthStep(), frame_ptr->Height(), frame_ptr->WidthStep(),
const_cast<uint8_t*>(frame_ptr->PixelData()), const_cast<uint8_t*>(frame_ptr->PixelData()),
[](uint8_t* data){}); [](uint8_t* data){});
ASSIGN_OR_RETURN(auto result, MP_ASSIGN_OR_RETURN(auto result,
runner->Run(image_frame, matrix, size, border_mode)); runner->Run(image_frame, matrix, size, border_mode));
return mediapipe::Image(std::make_shared<ImageFrame>(std::move(result))); return mediapipe::Image(std::make_shared<ImageFrame>(std::move(result)));
#else #else
return absl::UnavailableError("OpenCV support is disabled"); return absl::UnavailableError("OpenCV support is disabled");
@@ -213,8 +215,8 @@ class WarpAffineCalculatorImpl : public mediapipe::api2::NodeImpl<InterfaceT> {
AffineTransformation::Size output_size; AffineTransformation::Size output_size;
output_size.width = out_width; output_size.width = out_width;
output_size.height = out_height; output_size.height = out_height;
ASSIGN_OR_RETURN(auto* runner, holder_.GetRunner()); MP_ASSIGN_OR_RETURN(auto* runner, holder_.GetRunner());
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
auto result, auto result,
runner->Run( runner->Run(
*InterfaceT::kInImage(cc), transform, output_size, *InterfaceT::kInImage(cc), transform, output_size,
+42
View File
@@ -980,6 +980,48 @@ cc_test(
], ],
) )
cc_library(
name = "tensor_to_joints_calculator",
srcs = ["tensor_to_joints_calculator.cc"],
hdrs = ["tensor_to_joints_calculator.h"],
deps = [
":tensor_to_joints_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:node",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:tensor",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
],
alwayslink = 1,
)
mediapipe_proto_library(
name = "tensor_to_joints_calculator_proto",
srcs = ["tensor_to_joints_calculator.proto"],
deps = [
"//mediapipe/framework:calculator_options_proto",
"//mediapipe/framework:calculator_proto",
],
)
cc_test(
name = "tensor_to_joints_calculator_test",
srcs = ["tensor_to_joints_calculator_test.cc"],
deps = [
":tensor_to_joints_calculator",
":tensor_to_joints_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:calculator_runner",
"//mediapipe/framework:timestamp",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:tensor",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"@com_google_absl//absl/strings",
],
)
cc_library( cc_library(
name = "image_to_tensor_calculator", name = "image_to_tensor_calculator",
srcs = ["image_to_tensor_calculator.cc"], srcs = ["image_to_tensor_calculator.cc"],
@@ -517,8 +517,8 @@ absl::Status AudioToTensorCalculator::OutputTensor(const Matrix& block,
// The last two elements are Nyquist component. // The last two elements are Nyquist component.
fft_output_matrix(fft_size_ - 2) = fft_output_[1]; // Nyquist real part fft_output_matrix(fft_size_ - 2) = fft_output_[1]; // Nyquist real part
fft_output_matrix(fft_size_ - 1) = 0.0f; // Nyquist imagery part fft_output_matrix(fft_size_ - 1) = 0.0f; // Nyquist imagery part
ASSIGN_OR_RETURN(output_tensor, ConvertToTensor(fft_output_matrix, MP_ASSIGN_OR_RETURN(output_tensor, ConvertToTensor(fft_output_matrix,
{2, fft_size_ / 2})); {2, fft_size_ / 2}));
break; break;
} }
case Options::WITH_DC_AND_NYQUIST: { case Options::WITH_DC_AND_NYQUIST: {
@@ -529,7 +529,7 @@ absl::Status AudioToTensorCalculator::OutputTensor(const Matrix& block,
// The last two elements are Nyquist component. // The last two elements are Nyquist component.
fft_output_matrix(fft_size_) = fft_output_[1]; // Nyquist real part fft_output_matrix(fft_size_) = fft_output_[1]; // Nyquist real part
fft_output_matrix(fft_size_ + 1) = 0.0f; // Nyquist imagery part fft_output_matrix(fft_size_ + 1) = 0.0f; // Nyquist imagery part
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
output_tensor, output_tensor,
ConvertToTensor(fft_output_matrix, {2, (fft_size_ + 2) / 2})); ConvertToTensor(fft_output_matrix, {2, (fft_size_ + 2) / 2}));
break; break;
@@ -537,7 +537,7 @@ absl::Status AudioToTensorCalculator::OutputTensor(const Matrix& block,
case Options::WITHOUT_DC_AND_NYQUIST: { case Options::WITHOUT_DC_AND_NYQUIST: {
Matrix fft_output_matrix = Matrix fft_output_matrix =
Eigen::Map<const Matrix>(fft_output_.data() + 2, 1, fft_size_ - 2); Eigen::Map<const Matrix>(fft_output_.data() + 2, 1, fft_size_ - 2);
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
output_tensor, output_tensor,
ConvertToTensor(fft_output_matrix, {2, (fft_size_ - 2) / 2})); ConvertToTensor(fft_output_matrix, {2, (fft_size_ - 2) / 2}));
break; break;
@@ -547,8 +547,8 @@ absl::Status AudioToTensorCalculator::OutputTensor(const Matrix& block,
} }
} else { } else {
ASSIGN_OR_RETURN(output_tensor, MP_ASSIGN_OR_RETURN(output_tensor,
ConvertToTensor(block, {num_channels_, num_samples_})); ConvertToTensor(block, {num_channels_, num_samples_}));
} }
kTensorsOut(cc).Send(std::move(output_tensor), timestamp); kTensorsOut(cc).Send(std::move(output_tensor), timestamp);
return absl::OkStatus(); return absl::OkStatus();
@@ -161,9 +161,9 @@ absl::Status BertPreprocessorCalculator::Open(CalculatorContext* cc) {
&kMetadataExtractorSideIn(cc).Get(); &kMetadataExtractorSideIn(cc).Get();
const tflite::ProcessUnit* tokenizer_metadata = const tflite::ProcessUnit* tokenizer_metadata =
metadata_extractor->GetInputProcessUnit(kTokenizerProcessUnitIndex); metadata_extractor->GetInputProcessUnit(kTokenizerProcessUnitIndex);
ASSIGN_OR_RETURN(tokenizer_, MP_ASSIGN_OR_RETURN(tokenizer_,
tasks::text::tokenizers::CreateTokenizerFromProcessUnit( tasks::text::tokenizers::CreateTokenizerFromProcessUnit(
tokenizer_metadata, metadata_extractor)); tokenizer_metadata, metadata_extractor));
auto* input_tensors_metadata = metadata_extractor->GetInputTensorMetadata(); auto* input_tensors_metadata = metadata_extractor->GetInputTensorMetadata();
input_ids_tensor_index_ = FindTensorIndexByMetadataName( input_ids_tensor_index_ = FindTensorIndexByMetadataName(
@@ -67,9 +67,10 @@ absl::StatusOr<std::vector<std::vector<int>>> RunBertPreprocessorCalculator(
tool::AddVectorSink("tensors", &graph_config, &output_packets); tool::AddVectorSink("tensors", &graph_config, &output_packets);
std::string model_buffer = tasks::core::LoadBinaryContent(model_path.data()); std::string model_buffer = tasks::core::LoadBinaryContent(model_path.data());
ASSIGN_OR_RETURN(std::unique_ptr<ModelMetadataExtractor> metadata_extractor, MP_ASSIGN_OR_RETURN(
ModelMetadataExtractor::CreateFromModelBuffer( std::unique_ptr<ModelMetadataExtractor> metadata_extractor,
model_buffer.data(), model_buffer.size())); ModelMetadataExtractor::CreateFromModelBuffer(model_buffer.data(),
model_buffer.size()));
// Run the graph. // Run the graph.
CalculatorGraph graph; CalculatorGraph graph;
MP_RETURN_IF_ERROR(graph.Initialize( MP_RETURN_IF_ERROR(graph.Initialize(
@@ -192,18 +192,19 @@ class ImageToTensorCalculator : public Node {
} }
#if MEDIAPIPE_DISABLE_GPU #if MEDIAPIPE_DISABLE_GPU
ASSIGN_OR_RETURN(auto image, GetInputImage(kIn(cc))); MP_ASSIGN_OR_RETURN(auto image, GetInputImage(kIn(cc)));
#else #else
const bool is_input_gpu = kInGpu(cc).IsConnected(); const bool is_input_gpu = kInGpu(cc).IsConnected();
ASSIGN_OR_RETURN(auto image, is_input_gpu ? GetInputImage(kInGpu(cc)) MP_ASSIGN_OR_RETURN(auto image, is_input_gpu ? GetInputImage(kInGpu(cc))
: GetInputImage(kIn(cc))); : GetInputImage(kIn(cc)));
#endif // MEDIAPIPE_DISABLE_GPU #endif // MEDIAPIPE_DISABLE_GPU
RotatedRect roi = GetRoi(image->width(), image->height(), norm_rect); RotatedRect roi = GetRoi(image->width(), image->height(), norm_rect);
const int tensor_width = params_.output_width.value_or(image->width()); const int tensor_width = params_.output_width.value_or(image->width());
const int tensor_height = params_.output_height.value_or(image->height()); const int tensor_height = params_.output_height.value_or(image->height());
ASSIGN_OR_RETURN(auto padding, PadRoi(tensor_width, tensor_height, MP_ASSIGN_OR_RETURN(auto padding,
options_.keep_aspect_ratio(), &roi)); PadRoi(tensor_width, tensor_height,
options_.keep_aspect_ratio(), &roi));
if (kOutLetterboxPadding(cc).IsConnected()) { if (kOutLetterboxPadding(cc).IsConnected()) {
kOutLetterboxPadding(cc).Send(padding); kOutLetterboxPadding(cc).Send(padding);
} }
@@ -247,20 +248,20 @@ class ImageToTensorCalculator : public Node {
if (!gpu_converter_) { if (!gpu_converter_) {
#if !MEDIAPIPE_DISABLE_GPU #if !MEDIAPIPE_DISABLE_GPU
#if MEDIAPIPE_METAL_ENABLED #if MEDIAPIPE_METAL_ENABLED
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
gpu_converter_, gpu_converter_,
CreateMetalConverter(cc, GetBorderMode(options_.border_mode()))); CreateMetalConverter(cc, GetBorderMode(options_.border_mode())));
#elif MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31 #elif MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31
ASSIGN_OR_RETURN(gpu_converter_, MP_ASSIGN_OR_RETURN(gpu_converter_,
CreateImageToGlBufferTensorConverter( CreateImageToGlBufferTensorConverter(
cc, DoesGpuInputStartAtBottom(options_), cc, DoesGpuInputStartAtBottom(options_),
GetBorderMode(options_.border_mode()))); GetBorderMode(options_.border_mode())));
#else #else
if (!gpu_converter_) { if (!gpu_converter_) {
ASSIGN_OR_RETURN(gpu_converter_, MP_ASSIGN_OR_RETURN(gpu_converter_,
CreateImageToGlTextureTensorConverter( CreateImageToGlTextureTensorConverter(
cc, DoesGpuInputStartAtBottom(options_), cc, DoesGpuInputStartAtBottom(options_),
GetBorderMode(options_.border_mode()))); GetBorderMode(options_.border_mode())));
} }
if (!gpu_converter_) { if (!gpu_converter_) {
return absl::UnimplementedError( return absl::UnimplementedError(
@@ -272,18 +273,20 @@ class ImageToTensorCalculator : public Node {
} else { } else {
if (!cpu_converter_) { if (!cpu_converter_) {
#if !MEDIAPIPE_DISABLE_OPENCV #if !MEDIAPIPE_DISABLE_OPENCV
ASSIGN_OR_RETURN(cpu_converter_, MP_ASSIGN_OR_RETURN(
CreateOpenCvConverter( cpu_converter_,
cc, GetBorderMode(options_.border_mode()), CreateOpenCvConverter(
GetOutputTensorType(/*uses_gpu=*/false, params_))); cc, GetBorderMode(options_.border_mode()),
GetOutputTensorType(/*uses_gpu=*/false, params_)));
// TODO: FrameBuffer-based converter needs to call GetGpuBuffer() // TODO: FrameBuffer-based converter needs to call GetGpuBuffer()
// to get access to a FrameBuffer view. Investigate if GetGpuBuffer() can be // to get access to a FrameBuffer view. Investigate if GetGpuBuffer() can be
// made available even with MEDIAPIPE_DISABLE_GPU set. // made available even with MEDIAPIPE_DISABLE_GPU set.
#elif MEDIAPIPE_ENABLE_HALIDE #elif MEDIAPIPE_ENABLE_HALIDE
ASSIGN_OR_RETURN(cpu_converter_, MP_ASSIGN_OR_RETURN(
CreateFrameBufferConverter( cpu_converter_,
cc, GetBorderMode(options_.border_mode()), CreateFrameBufferConverter(
GetOutputTensorType(/*uses_gpu=*/false, params_))); cc, GetBorderMode(options_.border_mode()),
GetOutputTensorType(/*uses_gpu=*/false, params_)));
#else #else
ABSL_LOG(FATAL) << "Cannot create image to tensor CPU converter since " ABSL_LOG(FATAL) << "Cannot create image to tensor CPU converter since "
"MEDIAPIPE_DISABLE_OPENCV is defined and " "MEDIAPIPE_DISABLE_OPENCV is defined and "
@@ -175,9 +175,9 @@ absl::Status FrameBufferProcessor::CropRotateResize90Degrees(
cropped_buffer_ = std::make_unique<uint8_t[]>(cropped_buffer_size); cropped_buffer_ = std::make_unique<uint8_t[]>(cropped_buffer_size);
cropped_buffer_size_ = cropped_buffer_size; cropped_buffer_size_ = cropped_buffer_size;
} }
ASSIGN_OR_RETURN(cropped, MP_ASSIGN_OR_RETURN(
frame_buffer::CreateFromRawBuffer( cropped, frame_buffer::CreateFromRawBuffer(
cropped_buffer_.get(), cropped_dims, input->format())); cropped_buffer_.get(), cropped_dims, input->format()));
} }
MP_RETURN_IF_ERROR( MP_RETURN_IF_ERROR(
frame_buffer::Crop(*input, left, top, right, bottom, cropped.get())); frame_buffer::Crop(*input, left, top, right, bottom, cropped.get()));
@@ -194,9 +194,9 @@ absl::Status FrameBufferProcessor::CropRotateResize90Degrees(
rotated_buffer_ = std::make_unique<uint8_t[]>(rotated_buffer_size); rotated_buffer_ = std::make_unique<uint8_t[]>(rotated_buffer_size);
rotated_buffer_size_ = rotated_buffer_size; rotated_buffer_size_ = rotated_buffer_size;
} }
ASSIGN_OR_RETURN(auto rotated, frame_buffer::CreateFromRawBuffer( MP_ASSIGN_OR_RETURN(auto rotated, frame_buffer::CreateFromRawBuffer(
rotated_buffer_.get(), rotated_dims, rotated_buffer_.get(), rotated_dims,
cropped->format())); cropped->format()));
} }
MP_RETURN_IF_ERROR( MP_RETURN_IF_ERROR(
frame_buffer::Rotate(*cropped, rotation_degrees, rotated.get())); frame_buffer::Rotate(*cropped, rotation_degrees, rotated.get()));
@@ -217,9 +217,10 @@ absl::Status FrameBufferProcessor::ConvertToFloatTensor(
RET_CHECK(output_tensor.element_type() == Tensor::ElementType::kFloat32); RET_CHECK(output_tensor.element_type() == Tensor::ElementType::kFloat32);
constexpr float kInputImageRangeMin = 0.0f; constexpr float kInputImageRangeMin = 0.0f;
constexpr float kInputImageRangeMax = 255.0f; constexpr float kInputImageRangeMax = 255.0f;
ASSIGN_OR_RETURN(auto transform, GetValueRangeTransformation( MP_ASSIGN_OR_RETURN(
kInputImageRangeMin, kInputImageRangeMax, auto transform,
range_min, range_max)); GetValueRangeTransformation(kInputImageRangeMin, kInputImageRangeMax,
range_min, range_max));
return frame_buffer::ToFloatTensor(*input_frame, transform.scale, return frame_buffer::ToFloatTensor(*input_frame, transform.scale,
transform.offset, output_tensor); transform.offset, output_tensor);
} }
@@ -255,7 +255,7 @@ class GlProcessor : public ImageToTensorConverter {
<< "OpenGL ES 3.1 is required."; << "OpenGL ES 3.1 is required.";
command_queue_ = tflite::gpu::gl::NewCommandQueue(gpu_info); command_queue_ = tflite::gpu::gl::NewCommandQueue(gpu_info);
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
auto extractor, auto extractor,
SubRectExtractorGl::Create(gl_helper_.GetGlContext(), SubRectExtractorGl::Create(gl_helper_.GetGlContext(),
input_starts_at_bottom, border_mode)); input_starts_at_bottom, border_mode));
@@ -293,10 +293,10 @@ class GlProcessor : public ImageToTensorConverter {
constexpr float kInputImageRangeMin = 0.0f; constexpr float kInputImageRangeMin = 0.0f;
constexpr float kInputImageRangeMax = 1.0f; constexpr float kInputImageRangeMax = 1.0f;
ASSIGN_OR_RETURN(auto transform, MP_ASSIGN_OR_RETURN(auto transform,
GetValueRangeTransformation(kInputImageRangeMin, GetValueRangeTransformation(
kInputImageRangeMax, kInputImageRangeMin, kInputImageRangeMax,
range_min, range_max)); range_min, range_max));
const int output_size = output_tensor.bytes() / output_shape.dims[0]; const int output_size = output_tensor.bytes() / output_shape.dims[0];
auto buffer_view = output_tensor.GetOpenGlBufferWriteView(); auto buffer_view = output_tensor.GetOpenGlBufferWriteView();
@@ -193,10 +193,10 @@ class GlProcessor : public ImageToTensorConverter {
constexpr float kInputImageRangeMin = 0.0f; constexpr float kInputImageRangeMin = 0.0f;
constexpr float kInputImageRangeMax = 1.0f; constexpr float kInputImageRangeMax = 1.0f;
ASSIGN_OR_RETURN(auto transform, MP_ASSIGN_OR_RETURN(auto transform,
GetValueRangeTransformation(kInputImageRangeMin, GetValueRangeTransformation(
kInputImageRangeMax, kInputImageRangeMin, kInputImageRangeMax,
range_min, range_max)); range_min, range_max));
auto tensor_view = output_tensor.GetOpenGlTexture2dWriteView(); auto tensor_view = output_tensor.GetOpenGlTexture2dWriteView();
MP_RETURN_IF_ERROR(ExtractSubRect(input_texture, roi, MP_RETURN_IF_ERROR(ExtractSubRect(input_texture, roi,
/*flip_horizontaly=*/false, /*flip_horizontaly=*/false,
@@ -304,6 +304,7 @@ class GlProcessor : public ImageToTensorConverter {
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
glFlush();
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -345,9 +345,9 @@ class MetalProcessor : public ImageToTensorConverter {
absl::Status Init(CalculatorContext* cc, BorderMode border_mode) { absl::Status Init(CalculatorContext* cc, BorderMode border_mode) {
metal_helper_ = [[MPPMetalHelper alloc] initWithCalculatorContext:cc]; metal_helper_ = [[MPPMetalHelper alloc] initWithCalculatorContext:cc];
RET_CHECK(metal_helper_); RET_CHECK(metal_helper_);
ASSIGN_OR_RETURN(extractor_, SubRectExtractorMetal::Make( MP_ASSIGN_OR_RETURN(extractor_, SubRectExtractorMetal::Make(
metal_helper_.mtlDevice, metal_helper_.mtlDevice,
OutputFormat::kF32C4, border_mode)); OutputFormat::kF32C4, border_mode));
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -373,7 +373,7 @@ class MetalProcessor : public ImageToTensorConverter {
constexpr float kInputImageRangeMin = 0.0f; constexpr float kInputImageRangeMin = 0.0f;
constexpr float kInputImageRangeMax = 1.0f; constexpr float kInputImageRangeMax = 1.0f;
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
auto transform, auto transform,
GetValueRangeTransformation(kInputImageRangeMin, kInputImageRangeMax, GetValueRangeTransformation(kInputImageRangeMin, kInputImageRangeMax,
range_min, range_max)); range_min, range_max));
@@ -159,7 +159,7 @@ class OpenCvProcessor : public ImageToTensorConverter {
constexpr float kInputImageRangeMin = 0.0f; constexpr float kInputImageRangeMin = 0.0f;
constexpr float kInputImageRangeMax = 255.0f; constexpr float kInputImageRangeMax = 255.0f;
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
auto transform, auto transform,
GetValueRangeTransformation(kInputImageRangeMin, kInputImageRangeMax, GetValueRangeTransformation(kInputImageRangeMin, kInputImageRangeMax,
range_min, range_max)); range_min, range_max));
@@ -60,7 +60,7 @@ absl::Status InferenceCalculatorCpuImpl::UpdateContract(
} }
absl::Status InferenceCalculatorCpuImpl::Open(CalculatorContext* cc) { absl::Status InferenceCalculatorCpuImpl::Open(CalculatorContext* cc) {
ASSIGN_OR_RETURN(inference_runner_, CreateInferenceRunner(cc)); MP_ASSIGN_OR_RETURN(inference_runner_, CreateInferenceRunner(cc));
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -71,8 +71,8 @@ absl::Status InferenceCalculatorCpuImpl::Process(CalculatorContext* cc) {
const auto& input_tensors = *kInTensors(cc); const auto& input_tensors = *kInTensors(cc);
RET_CHECK(!input_tensors.empty()); RET_CHECK(!input_tensors.empty());
ASSIGN_OR_RETURN(std::vector<Tensor> output_tensors, MP_ASSIGN_OR_RETURN(std::vector<Tensor> output_tensors,
inference_runner_->Run(cc, input_tensors)); inference_runner_->Run(cc, input_tensors));
kOutTensors(cc).Send(std::move(output_tensors)); kOutTensors(cc).Send(std::move(output_tensors));
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -84,11 +84,11 @@ absl::Status InferenceCalculatorCpuImpl::Close(CalculatorContext* cc) {
absl::StatusOr<std::unique_ptr<InferenceRunner>> absl::StatusOr<std::unique_ptr<InferenceRunner>>
InferenceCalculatorCpuImpl::CreateInferenceRunner(CalculatorContext* cc) { InferenceCalculatorCpuImpl::CreateInferenceRunner(CalculatorContext* cc) {
ASSIGN_OR_RETURN(auto model_packet, GetModelAsPacket(cc)); MP_ASSIGN_OR_RETURN(auto model_packet, GetModelAsPacket(cc));
ASSIGN_OR_RETURN(auto op_resolver_packet, GetOpResolverAsPacket(cc)); MP_ASSIGN_OR_RETURN(auto op_resolver_packet, GetOpResolverAsPacket(cc));
const int interpreter_num_threads = const int interpreter_num_threads =
cc->Options<mediapipe::InferenceCalculatorOptions>().cpu_num_thread(); cc->Options<mediapipe::InferenceCalculatorOptions>().cpu_num_thread();
ASSIGN_OR_RETURN(TfLiteDelegatePtr delegate, MaybeCreateDelegate(cc)); MP_ASSIGN_OR_RETURN(TfLiteDelegatePtr delegate, MaybeCreateDelegate(cc));
return CreateInferenceInterpreterDelegateRunner( return CreateInferenceInterpreterDelegateRunner(
std::move(model_packet), std::move(op_resolver_packet), std::move(model_packet), std::move(op_resolver_packet),
std::move(delegate), interpreter_num_threads); std::move(delegate), interpreter_num_threads);
@@ -100,7 +100,7 @@ absl::Status InferenceCalculatorGlImpl::GpuInferenceRunner::Init(
absl::Status InferenceCalculatorGlImpl::GpuInferenceRunner::LoadModel( absl::Status InferenceCalculatorGlImpl::GpuInferenceRunner::LoadModel(
CalculatorContext* cc) { CalculatorContext* cc) {
ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(cc)); MP_ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(cc));
const auto& model = *model_packet_.Get(); const auto& model = *model_packet_.Get();
if (kSideInOpResolver(cc).IsConnected()) { if (kSideInOpResolver(cc).IsConnected()) {
const tflite::OpResolver& op_resolver = kSideInOpResolver(cc).Get(); const tflite::OpResolver& op_resolver = kSideInOpResolver(cc).Get();
@@ -170,7 +170,7 @@ absl::Status
InferenceCalculatorGlAdvancedImpl::GpuInferenceRunner::InitTFLiteGPURunner( InferenceCalculatorGlAdvancedImpl::GpuInferenceRunner::InitTFLiteGPURunner(
CalculatorContext* cc, CalculatorContext* cc,
const mediapipe::InferenceCalculatorOptions::Delegate& delegate) { const mediapipe::InferenceCalculatorOptions::Delegate& delegate) {
ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(cc)); MP_ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(cc));
const auto& model = *model_packet_.Get(); const auto& model = *model_packet_.Get();
bool allow_precision_loss = delegate.gpu().allow_precision_loss(); bool allow_precision_loss = delegate.gpu().allow_precision_loss();
@@ -306,16 +306,16 @@ InferenceCalculatorGlAdvancedImpl::OnDiskCacheHelper::SaveGpuCaches(
tflite::gpu::TFLiteGPURunner* gpu_runner) const { tflite::gpu::TFLiteGPURunner* gpu_runner) const {
if (use_kernel_caching_) { if (use_kernel_caching_) {
// Save kernel file. // Save kernel file.
ASSIGN_OR_RETURN(std::vector<uint8_t> kernel_cache, MP_ASSIGN_OR_RETURN(std::vector<uint8_t> kernel_cache,
gpu_runner->GetSerializedBinaryCache()); gpu_runner->GetSerializedBinaryCache());
std::string cache_str(kernel_cache.begin(), kernel_cache.end()); std::string cache_str(kernel_cache.begin(), kernel_cache.end());
MP_RETURN_IF_ERROR( MP_RETURN_IF_ERROR(
mediapipe::file::SetContents(cached_kernel_filename_, cache_str)); mediapipe::file::SetContents(cached_kernel_filename_, cache_str));
} }
if (use_serialized_model_) { if (use_serialized_model_) {
// Save serialized model file. // Save serialized model file.
ASSIGN_OR_RETURN(std::vector<uint8_t> serialized_model_vec, MP_ASSIGN_OR_RETURN(std::vector<uint8_t> serialized_model_vec,
gpu_runner->GetSerializedModel()); gpu_runner->GetSerializedModel());
absl::string_view serialized_model( absl::string_view serialized_model(
reinterpret_cast<char*>(serialized_model_vec.data()), reinterpret_cast<char*>(serialized_model_vec.data()),
serialized_model_vec.size()); serialized_model_vec.size());
@@ -412,8 +412,8 @@ absl::Status InferenceCalculatorGlAdvancedImpl::Process(CalculatorContext* cc) {
RET_CHECK(!input_tensors.empty()); RET_CHECK(!input_tensors.empty());
auto output_tensors = absl::make_unique<std::vector<Tensor>>(); auto output_tensors = absl::make_unique<std::vector<Tensor>>();
ASSIGN_OR_RETURN(*output_tensors, MP_ASSIGN_OR_RETURN(*output_tensors,
gpu_inference_runner_->Process(cc, input_tensors)); gpu_inference_runner_->Process(cc, input_tensors));
kOutTensors(cc).Send(std::move(output_tensors)); kOutTensors(cc).Send(std::move(output_tensors));
return absl::OkStatus(); return absl::OkStatus();
@@ -208,9 +208,9 @@ absl::Status InferenceCalculatorMetalImpl::Close(CalculatorContext* cc) {
absl::Status InferenceCalculatorMetalImpl::InitInterpreter( absl::Status InferenceCalculatorMetalImpl::InitInterpreter(
CalculatorContext* cc) { CalculatorContext* cc) {
ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(cc)); MP_ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(cc));
const auto& model = *model_packet_.Get(); const auto& model = *model_packet_.Get();
ASSIGN_OR_RETURN(auto op_resolver_packet, GetOpResolverAsPacket(cc)); MP_ASSIGN_OR_RETURN(auto op_resolver_packet, GetOpResolverAsPacket(cc));
const auto& op_resolver = op_resolver_packet.Get(); const auto& op_resolver = op_resolver_packet.Get();
tflite::InterpreterBuilder interpreter_builder(model, op_resolver); tflite::InterpreterBuilder interpreter_builder(model, op_resolver);
AddDelegate(cc, &interpreter_builder); AddDelegate(cc, &interpreter_builder);
@@ -58,7 +58,7 @@ absl::Status InferenceCalculatorXnnpackImpl::UpdateContract(
} }
absl::Status InferenceCalculatorXnnpackImpl::Open(CalculatorContext* cc) { absl::Status InferenceCalculatorXnnpackImpl::Open(CalculatorContext* cc) {
ASSIGN_OR_RETURN(inference_runner_, CreateInferenceRunner(cc)); MP_ASSIGN_OR_RETURN(inference_runner_, CreateInferenceRunner(cc));
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -69,8 +69,8 @@ absl::Status InferenceCalculatorXnnpackImpl::Process(CalculatorContext* cc) {
const auto& input_tensors = *kInTensors(cc); const auto& input_tensors = *kInTensors(cc);
RET_CHECK(!input_tensors.empty()); RET_CHECK(!input_tensors.empty());
ASSIGN_OR_RETURN(std::vector<Tensor> output_tensors, MP_ASSIGN_OR_RETURN(std::vector<Tensor> output_tensors,
inference_runner_->Run(cc, input_tensors)); inference_runner_->Run(cc, input_tensors));
kOutTensors(cc).Send(std::move(output_tensors)); kOutTensors(cc).Send(std::move(output_tensors));
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -82,11 +82,11 @@ absl::Status InferenceCalculatorXnnpackImpl::Close(CalculatorContext* cc) {
absl::StatusOr<std::unique_ptr<InferenceRunner>> absl::StatusOr<std::unique_ptr<InferenceRunner>>
InferenceCalculatorXnnpackImpl::CreateInferenceRunner(CalculatorContext* cc) { InferenceCalculatorXnnpackImpl::CreateInferenceRunner(CalculatorContext* cc) {
ASSIGN_OR_RETURN(auto model_packet, GetModelAsPacket(cc)); MP_ASSIGN_OR_RETURN(auto model_packet, GetModelAsPacket(cc));
ASSIGN_OR_RETURN(auto op_resolver_packet, GetOpResolverAsPacket(cc)); MP_ASSIGN_OR_RETURN(auto op_resolver_packet, GetOpResolverAsPacket(cc));
const int interpreter_num_threads = const int interpreter_num_threads =
cc->Options<mediapipe::InferenceCalculatorOptions>().cpu_num_thread(); cc->Options<mediapipe::InferenceCalculatorOptions>().cpu_num_thread();
ASSIGN_OR_RETURN(TfLiteDelegatePtr delegate, CreateDelegate(cc)); MP_ASSIGN_OR_RETURN(TfLiteDelegatePtr delegate, CreateDelegate(cc));
return CreateInferenceInterpreterDelegateRunner( return CreateInferenceInterpreterDelegateRunner(
std::move(model_packet), std::move(op_resolver_packet), std::move(model_packet), std::move(op_resolver_packet),
std::move(delegate), interpreter_num_threads); std::move(delegate), interpreter_num_threads);
@@ -106,7 +106,7 @@ absl::Status RegexPreprocessorCalculator::Open(CalculatorContext* cc) {
return absl::InvalidArgumentError("No tensor metadata found"); return absl::InvalidArgumentError("No tensor metadata found");
} }
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
const auto* tokenizer_metadata, const auto* tokenizer_metadata,
metadata_extractor->FindFirstProcessUnit( metadata_extractor->FindFirstProcessUnit(
*tensor_metadata, tflite::ProcessUnitOptions_RegexTokenizerOptions)); *tensor_metadata, tflite::ProcessUnitOptions_RegexTokenizerOptions));
@@ -115,9 +115,9 @@ absl::Status RegexPreprocessorCalculator::Open(CalculatorContext* cc) {
} }
const tflite::RegexTokenizerOptions* regex_tokenizer_options = const tflite::RegexTokenizerOptions* regex_tokenizer_options =
tokenizer_metadata->options_as<tflite::RegexTokenizerOptions>(); tokenizer_metadata->options_as<tflite::RegexTokenizerOptions>();
ASSIGN_OR_RETURN(tokenizer_, MP_ASSIGN_OR_RETURN(tokenizer_,
tasks::text::tokenizers::CreateRegexTokenizerFromOptions( tasks::text::tokenizers::CreateRegexTokenizerFromOptions(
regex_tokenizer_options, metadata_extractor)); regex_tokenizer_options, metadata_extractor));
const auto& options = const auto& options =
cc->Options<mediapipe::RegexPreprocessorCalculatorOptions>(); cc->Options<mediapipe::RegexPreprocessorCalculatorOptions>();
@@ -67,9 +67,10 @@ absl::StatusOr<std::vector<int>> RunRegexPreprocessorCalculator(
tool::AddVectorSink("tensors", &graph_config, &output_packets); tool::AddVectorSink("tensors", &graph_config, &output_packets);
std::string model_buffer = tasks::core::LoadBinaryContent(kTestModelPath); std::string model_buffer = tasks::core::LoadBinaryContent(kTestModelPath);
ASSIGN_OR_RETURN(std::unique_ptr<ModelMetadataExtractor> metadata_extractor, MP_ASSIGN_OR_RETURN(
ModelMetadataExtractor::CreateFromModelBuffer( std::unique_ptr<ModelMetadataExtractor> metadata_extractor,
model_buffer.data(), model_buffer.size())); ModelMetadataExtractor::CreateFromModelBuffer(model_buffer.data(),
model_buffer.size()));
// Run the graph. // Run the graph.
CalculatorGraph graph; CalculatorGraph graph;
MP_RETURN_IF_ERROR(graph.Initialize( MP_RETURN_IF_ERROR(graph.Initialize(
@@ -406,6 +406,7 @@ absl::Status TensorConverterCalculator::ProcessGPU(CalculatorContext* cc) {
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
#endif // MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31 #endif // MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31
glFlush();
src.Release(); src.Release();
return absl::OkStatus(); return absl::OkStatus();
})); }));
@@ -655,7 +656,7 @@ absl::Status TensorConverterCalculator::LoadOptions(CalculatorContext* cc,
} }
// Get y-flip mode. // Get y-flip mode.
ASSIGN_OR_RETURN(flip_vertically_, ShouldFlipVertically(options, use_gpu)); MP_ASSIGN_OR_RETURN(flip_vertically_, ShouldFlipVertically(options, use_gpu));
// Get row_major_matrix mode. // Get row_major_matrix mode.
row_major_matrix_ = options.row_major_matrix(); row_major_matrix_ = options.row_major_matrix();
@@ -0,0 +1,84 @@
// Copyright 2023 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/calculators/tensor/tensor_to_joints_calculator.h"
#include <utility>
#include "mediapipe/calculators/tensor/tensor_to_joints_calculator.pb.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/tensor.h"
#include "mediapipe/framework/port/ret_check.h"
namespace mediapipe {
namespace api2 {
namespace {
// Number of values in 6D representation of rotation.
constexpr int kRotation6dSize = 6;
} // namespace
class TensorToJointsCalculatorImpl
: public mediapipe::api2::NodeImpl<TensorToJointsCalculator> {
public:
absl::Status Open(CalculatorContext* cc) override {
const auto& options = cc->Options<TensorToJointsCalculatorOptions>();
// Get number of joints.
RET_CHECK_GE(options.num_joints(), 0);
num_joints_ = options.num_joints();
// Get start index.
start_index_ = options.start_index();
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) override {
// Skip if Tensor is empty.
if (kInTensor(cc).IsEmpty()) {
return absl::OkStatus();
}
// Get raw floats from the Tensor.
const Tensor& tensor = kInTensor(cc).Get();
RET_CHECK_EQ(tensor.shape().num_elements(),
num_joints_ * kRotation6dSize + start_index_)
<< "Unexpected number of values in Tensor";
const float* raw_floats = tensor.GetCpuReadView().buffer<float>();
// Convert raw floats into Joint rotations.
JointList joints;
for (int joint_idx = 0; joint_idx < num_joints_; ++joint_idx) {
Joint* joint = joints.add_joint();
for (int idx_6d = 0; idx_6d < kRotation6dSize; ++idx_6d) {
joint->add_rotation_6d(
raw_floats[start_index_ + joint_idx * kRotation6dSize + idx_6d]);
}
}
kOutJoints(cc).Send(std::move(joints));
return absl::OkStatus();
}
private:
int num_joints_ = 0;
int start_index_ = 0;
};
MEDIAPIPE_NODE_IMPLEMENTATION(TensorToJointsCalculatorImpl);
} // namespace api2
} // namespace mediapipe
@@ -0,0 +1,64 @@
// Copyright 2023 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_TENSOR_TENSOR_TO_JOINTS_CALCULATOR_H_
#define MEDIAPIPE_CALCULATORS_TENSOR_TENSOR_TO_JOINTS_CALCULATOR_H_
#include <memory>
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/tensor.h"
namespace mediapipe {
namespace api2 {
// A calculator to convert Tensors to JointList.
//
// Calculator fills in only rotation of the joints leaving visibility undefined.
//
// Input:
// TENSOR - std::vector<Tensor> with kFloat32 values
// Vector of tensors to be converted to joints. Only the first tensor will
// be used. Number of values is expected to be multiple of six.
//
// Output:
// JOINTS - JointList
// List of joints with rotations extracted from given tensor and undefined
// visibility.
//
// Example:
// node {
// calculator: "TensorToJointsCalculator"
// input_stream: "TENSOR:tensor"
// output_stream: "JOINTS:joints"
// options: {
// [mediapipe.TensorToJointsCalculatorOptions.ext] {
// num_joints: 56
// start_index: 3
// }
// }
// }
class TensorToJointsCalculator : public NodeIntf {
public:
static constexpr Input<mediapipe::Tensor> kInTensor{"TENSOR"};
static constexpr Output<mediapipe::JointList> kOutJoints{"JOINTS"};
MEDIAPIPE_NODE_INTERFACE(TensorToJointsCalculator, kInTensor, kOutJoints);
};
} // namespace api2
} // namespace mediapipe
#endif // MEDIAPIPE_CALCULATORS_TENSOR_TENSOR_TO_JOINTS_CALCULATOR_H_
@@ -0,0 +1,32 @@
// Copyright 2023 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.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
message TensorToJointsCalculatorOptions {
extend CalculatorOptions {
optional TensorToJointsCalculatorOptions ext = 406440177;
}
// Number of joints from the output of the model. Calculator will expect the
// tensor to contain `6 * num_joints + start_index` values.
optional int32 num_joints = 1;
// Index to start reading 6 value blocks from.
optional int32 start_index = 2 [default = 0];
}
@@ -0,0 +1,123 @@
// Copyright 2023 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 <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/substitute.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/calculator_runner.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/tensor.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"
#include "mediapipe/framework/timestamp.h"
namespace mediapipe {
namespace api2 {
namespace {
using Node = ::mediapipe::CalculatorGraphConfig::Node;
struct TensorToJointsTestCase {
std::string test_name;
int num_joints;
int start_index;
std::vector<float> raw_values;
std::vector<std::vector<float>> expected_rotations;
};
using TensorToJointsTest = ::testing::TestWithParam<TensorToJointsTestCase>;
TEST_P(TensorToJointsTest, TensorToJointsTest) {
const TensorToJointsTestCase& tc = GetParam();
// Prepare graph.
mediapipe::CalculatorRunner runner(ParseTextProtoOrDie<Node>(absl::Substitute(
R"(
calculator: "TensorToJointsCalculator"
input_stream: "TENSOR:tensor"
output_stream: "JOINTS:joints"
options: {
[mediapipe.TensorToJointsCalculatorOptions.ext] {
num_joints: $0
start_index: $1
}
}
)",
tc.num_joints, tc.start_index)));
// Prepare tensor.
Tensor tensor(Tensor::ElementType::kFloat32,
Tensor::Shape{1, 1, static_cast<int>(tc.raw_values.size()), 1});
float* tensor_buffer = tensor.GetCpuWriteView().buffer<float>();
ASSERT_NE(tensor_buffer, nullptr);
for (int i = 0; i < tc.raw_values.size(); ++i) {
tensor_buffer[i] = tc.raw_values[i];
}
// Send tensor to the graph.
runner.MutableInputs()->Tag("TENSOR").packets.push_back(
mediapipe::MakePacket<Tensor>(std::move(tensor)).At(Timestamp(0)));
// Run the graph.
MP_ASSERT_OK(runner.Run());
const auto& output_packets = runner.Outputs().Tag("JOINTS").packets;
EXPECT_EQ(1, output_packets.size());
const auto& joints = output_packets[0].Get<JointList>();
EXPECT_EQ(joints.joint_size(), tc.expected_rotations.size());
for (int i = 0; i < joints.joint_size(); ++i) {
const Joint& joint = joints.joint(i);
std::vector<float> expected_rotation_6d = tc.expected_rotations[i];
EXPECT_EQ(joint.rotation_6d_size(), expected_rotation_6d.size())
<< "Unexpected joint #" << i << " rotation";
for (int j = 0; j < joint.rotation_6d_size(); ++j) {
EXPECT_EQ(joint.rotation_6d(j), expected_rotation_6d[j])
<< "Unexpected joint #" << i << " rotation";
}
EXPECT_FALSE(joint.has_visibility());
}
}
INSTANTIATE_TEST_SUITE_P(
TensorToJointsTests, TensorToJointsTest,
testing::ValuesIn<TensorToJointsTestCase>({
{"Empty", 0, 3, {0, 0, 0}, {}},
{"Single",
1,
3,
{0, 0, 0, 10, 11, 12, 13, 14, 15},
{{10, 11, 12, 13, 14, 15}}},
{"Double",
2,
3,
{0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21},
{{10, 11, 12, 13, 14, 15}, {16, 17, 18, 19, 20, 21}}},
}),
[](const testing::TestParamInfo<TensorToJointsTest::ParamType>& info) {
return info.param.test_name;
});
} // namespace
} // namespace api2
} // namespace mediapipe
@@ -110,8 +110,8 @@ absl::Status TensorsToClassificationCalculator::Open(CalculatorContext* cc) {
sort_by_descending_score_ = options.sort_by_descending_score(); sort_by_descending_score_ = options.sort_by_descending_score();
if (options.has_label_map_path()) { if (options.has_label_map_path()) {
std::string string_path; std::string string_path;
ASSIGN_OR_RETURN(string_path, MP_ASSIGN_OR_RETURN(string_path,
PathToResourceAsFile(options.label_map_path())); PathToResourceAsFile(options.label_map_path()));
std::string label_map_string; std::string label_map_string;
MP_RETURN_IF_ERROR( MP_RETURN_IF_ERROR(
mediapipe::GetResourceContents(string_path, &label_map_string)); mediapipe::GetResourceContents(string_path, &label_map_string));
@@ -174,6 +174,9 @@ class TensorsToSegmentationCalculator : public CalculatorBase {
mediapipe::GlCalculatorHelper gpu_helper_; mediapipe::GlCalculatorHelper gpu_helper_;
GLuint upsample_program_; GLuint upsample_program_;
#if MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31 #if MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31
int cached_width_ = 0;
int cached_height_ = 0;
std::unique_ptr<tflite::gpu::gl::GlTexture> small_mask_texture_;
std::unique_ptr<GlProgram> mask_program_31_; std::unique_ptr<GlProgram> mask_program_31_;
#else #else
GLuint mask_program_20_; GLuint mask_program_20_;
@@ -264,7 +267,8 @@ absl::Status TensorsToSegmentationCalculator::Process(CalculatorContext* cc) {
{ {
RET_CHECK(!input_tensors.empty()); RET_CHECK(!input_tensors.empty());
RET_CHECK(input_tensors[0].element_type() == Tensor::ElementType::kFloat32); RET_CHECK(input_tensors[0].element_type() == Tensor::ElementType::kFloat32);
ASSIGN_OR_RETURN(auto hwc, GetHwcFromDims(input_tensors[0].shape().dims)); MP_ASSIGN_OR_RETURN(auto hwc,
GetHwcFromDims(input_tensors[0].shape().dims));
int tensor_channels = std::get<2>(hwc); int tensor_channels = std::get<2>(hwc);
typedef mediapipe::TensorsToSegmentationCalculatorOptions Options; typedef mediapipe::TensorsToSegmentationCalculatorOptions Options;
switch (options_.activation()) { switch (options_.activation()) {
@@ -307,6 +311,7 @@ absl::Status TensorsToSegmentationCalculator::Close(CalculatorContext* cc) {
upsample_program_ = 0; upsample_program_ = 0;
#if MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31 #if MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31
mask_program_31_.reset(); mask_program_31_.reset();
small_mask_texture_.reset();
#else #else
if (mask_program_20_) glDeleteProgram(mask_program_20_); if (mask_program_20_) glDeleteProgram(mask_program_20_);
mask_program_20_ = 0; mask_program_20_ = 0;
@@ -326,7 +331,7 @@ absl::Status TensorsToSegmentationCalculator::ProcessCpu(
// Get input streams, and dimensions. // Get input streams, and dimensions.
const auto& input_tensors = const auto& input_tensors =
cc->Inputs().Tag(kTensorsTag).Get<std::vector<Tensor>>(); cc->Inputs().Tag(kTensorsTag).Get<std::vector<Tensor>>();
ASSIGN_OR_RETURN(auto hwc, GetHwcFromDims(input_tensors[0].shape().dims)); MP_ASSIGN_OR_RETURN(auto hwc, GetHwcFromDims(input_tensors[0].shape().dims));
auto [tensor_height, tensor_width, tensor_channels] = hwc; auto [tensor_height, tensor_width, tensor_channels] = hwc;
int output_width = tensor_width, output_height = tensor_height; int output_width = tensor_width, output_height = tensor_height;
if (cc->Inputs().HasTag(kOutputSizeTag)) { if (cc->Inputs().HasTag(kOutputSizeTag)) {
@@ -437,7 +442,7 @@ absl::Status TensorsToSegmentationCalculator::ProcessGpu(
// Get input streams, and dimensions. // Get input streams, and dimensions.
const auto& input_tensors = const auto& input_tensors =
cc->Inputs().Tag(kTensorsTag).Get<std::vector<Tensor>>(); cc->Inputs().Tag(kTensorsTag).Get<std::vector<Tensor>>();
ASSIGN_OR_RETURN(auto hwc, GetHwcFromDims(input_tensors[0].shape().dims)); MP_ASSIGN_OR_RETURN(auto hwc, GetHwcFromDims(input_tensors[0].shape().dims));
auto [tensor_height, tensor_width, tensor_channels] = hwc; auto [tensor_height, tensor_width, tensor_channels] = hwc;
int output_width = tensor_width, output_height = tensor_height; int output_width = tensor_width, output_height = tensor_height;
if (cc->Inputs().HasTag(kOutputSizeTag)) { if (cc->Inputs().HasTag(kOutputSizeTag)) {
@@ -448,21 +453,24 @@ absl::Status TensorsToSegmentationCalculator::ProcessGpu(
} }
// Create initial working mask texture. // Create initial working mask texture.
#if MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31 #if !(MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31)
tflite::gpu::gl::GlTexture small_mask_texture;
#else
mediapipe::GlTexture small_mask_texture; mediapipe::GlTexture small_mask_texture;
#endif // MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31 #endif // MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31
// Run shader, process mask tensor. // Run shader, process mask tensor.
#if MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31 #if MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31
{ {
MP_RETURN_IF_ERROR(CreateReadWriteRgbaImageTexture( // Only recreate if the size has changed. See b/297809673 for more details.
tflite::gpu::DataType::UINT8, // GL_RGBA8 if (tensor_width != cached_width_ || tensor_height != cached_height_) {
{tensor_width, tensor_height}, &small_mask_texture)); MP_RETURN_IF_ERROR(CreateReadWriteRgbaImageTexture(
tflite::gpu::DataType::UINT8, // GL_RGBA8
{tensor_width, tensor_height}, small_mask_texture_.get()));
cached_width_ = tensor_width;
cached_height_ = tensor_height;
}
const int output_index = 0; const int output_index = 0;
glBindImageTexture(output_index, small_mask_texture.id(), 0, GL_FALSE, 0, glBindImageTexture(output_index, small_mask_texture_->id(), 0, GL_FALSE, 0,
GL_WRITE_ONLY, GL_RGBA8); GL_WRITE_ONLY, GL_RGBA8);
auto read_view = input_tensors[0].GetOpenGlBufferReadView(); auto read_view = input_tensors[0].GetOpenGlBufferReadView();
@@ -547,7 +555,7 @@ absl::Status TensorsToSegmentationCalculator::ProcessGpu(
gpu_helper_.BindFramebuffer(output_texture); gpu_helper_.BindFramebuffer(output_texture);
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
#if MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31 #if MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31
glBindTexture(GL_TEXTURE_2D, small_mask_texture.id()); glBindTexture(GL_TEXTURE_2D, small_mask_texture_->id());
#else #else
glBindTexture(GL_TEXTURE_2D, small_mask_texture.name()); glBindTexture(GL_TEXTURE_2D, small_mask_texture.name());
#endif // MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31 #endif // MEDIAPIPE_OPENGL_ES_VERSION >= MEDIAPIPE_OPENGL_ES_31
@@ -854,6 +862,7 @@ void main() {
mask_program_31_ = absl::make_unique<GlProgram>(); mask_program_31_ = absl::make_unique<GlProgram>();
MP_RETURN_IF_ERROR(GlProgram::CreateWithShader(shader_without_previous, MP_RETURN_IF_ERROR(GlProgram::CreateWithShader(shader_without_previous,
mask_program_31_.get())); mask_program_31_.get()));
small_mask_texture_ = absl::make_unique<tflite::gpu::gl::GlTexture>();
#elif MEDIAPIPE_METAL_ENABLED #elif MEDIAPIPE_METAL_ENABLED
id<MTLDevice> device = metal_helper_.mtlDevice; id<MTLDevice> device = metal_helper_.mtlDevice;
NSString* library_source = NSString* library_source =
@@ -61,9 +61,10 @@ RunUniversalSentenceEncoderPreprocessorCalculator(absl::string_view text) {
std::string model_buffer = std::string model_buffer =
tasks::core::LoadBinaryContent(kTestModelPath.data()); tasks::core::LoadBinaryContent(kTestModelPath.data());
ASSIGN_OR_RETURN(std::unique_ptr<ModelMetadataExtractor> metadata_extractor, MP_ASSIGN_OR_RETURN(
ModelMetadataExtractor::CreateFromModelBuffer( std::unique_ptr<ModelMetadataExtractor> metadata_extractor,
model_buffer.data(), model_buffer.size())); ModelMetadataExtractor::CreateFromModelBuffer(model_buffer.data(),
model_buffer.size()));
// Run the graph. // Run the graph.
CalculatorGraph graph; CalculatorGraph graph;
MP_RETURN_IF_ERROR(graph.Initialize( MP_RETURN_IF_ERROR(graph.Initialize(
@@ -151,7 +151,7 @@ class ObjectDetectionTensorsToDetectionsCalculator : public CalculatorBase {
tf::Tensor input_num_detections_tensor = tf::Tensor input_num_detections_tensor =
tf::Tensor(tf::DT_FLOAT, tf::TensorShape({0})); tf::Tensor(tf::DT_FLOAT, tf::TensorShape({0}));
if (cc->Inputs().HasTag(kClasses)) { if (cc->Inputs().HasTag(kClasses)) {
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
input_num_detections_tensor, input_num_detections_tensor,
MaybeSqueezeDims(kNumDetections, MaybeSqueezeDims(kNumDetections,
cc->Inputs().Tag(kNumDetections).Get<tf::Tensor>())); cc->Inputs().Tag(kNumDetections).Get<tf::Tensor>()));
@@ -160,12 +160,12 @@ class ObjectDetectionTensorsToDetectionsCalculator : public CalculatorBase {
RET_CHECK_EQ(input_num_detections_tensor.dtype(), tf::DT_FLOAT); RET_CHECK_EQ(input_num_detections_tensor.dtype(), tf::DT_FLOAT);
} }
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
auto input_boxes_tensor, auto input_boxes_tensor,
MaybeSqueezeDims(kBoxes, cc->Inputs().Tag(kBoxes).Get<tf::Tensor>())); MaybeSqueezeDims(kBoxes, cc->Inputs().Tag(kBoxes).Get<tf::Tensor>()));
RET_CHECK_EQ(input_boxes_tensor.dtype(), tf::DT_FLOAT); RET_CHECK_EQ(input_boxes_tensor.dtype(), tf::DT_FLOAT);
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
auto input_scores_tensor, auto input_scores_tensor,
MaybeSqueezeDims(kScores, cc->Inputs().Tag(kScores).Get<tf::Tensor>())); MaybeSqueezeDims(kScores, cc->Inputs().Tag(kScores).Get<tf::Tensor>()));
RET_CHECK_EQ(input_scores_tensor.dtype(), tf::DT_FLOAT); RET_CHECK_EQ(input_scores_tensor.dtype(), tf::DT_FLOAT);
@@ -173,7 +173,7 @@ class ObjectDetectionTensorsToDetectionsCalculator : public CalculatorBase {
tf::Tensor input_classes_tensor = tf::Tensor input_classes_tensor =
tf::Tensor(tf::DT_FLOAT, tf::TensorShape({0})); tf::Tensor(tf::DT_FLOAT, tf::TensorShape({0}));
if (cc->Inputs().HasTag(kClasses)) { if (cc->Inputs().HasTag(kClasses)) {
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
input_classes_tensor, input_classes_tensor,
MaybeSqueezeDims(kClasses, MaybeSqueezeDims(kClasses,
cc->Inputs().Tag(kClasses).Get<tf::Tensor>())); cc->Inputs().Tag(kClasses).Get<tf::Tensor>()));
@@ -75,7 +75,8 @@ namespace mpms = mediapipe::mediasequence;
// vector<pair<float, float>>>, // vector<pair<float, float>>>,
// * "CLIP_MEDIA_ID", which stores the clip's media ID as a string. // * "CLIP_MEDIA_ID", which stores the clip's media ID as a string.
// * "CLIP_LABEL_${NAME}" which stores sparse feature labels, ID and scores in // * "CLIP_LABEL_${NAME}" which stores sparse feature labels, ID and scores in
// mediapipe::Detection. // mediapipe::Detection. In the input Detection, the score field is required,
// and label and label_id are optional but at least one of them should be set.
// "IMAGE_${NAME}", "BBOX_${NAME}", and "KEYPOINTS_${NAME}" will also store // "IMAGE_${NAME}", "BBOX_${NAME}", and "KEYPOINTS_${NAME}" will also store
// prefixed versions of each stream, which allows for multiple image streams to // prefixed versions of each stream, which allows for multiple image streams to
// be included. However, the default names are suppored by more tools. // be included. However, the default names are suppored by more tools.
@@ -514,24 +515,37 @@ class PackMediaSequenceCalculator : public CalculatorBase {
const std::string& key = tag.substr( const std::string& key = tag.substr(
sizeof(kClipLabelPrefixTag) / sizeof(*kClipLabelPrefixTag) - 1); sizeof(kClipLabelPrefixTag) / sizeof(*kClipLabelPrefixTag) - 1);
const Detection& detection = cc->Inputs().Tag(tag).Get<Detection>(); const Detection& detection = cc->Inputs().Tag(tag).Get<Detection>();
if (detection.label().size() != detection.score().size()) { if (detection.score().empty()) {
return absl::InvalidArgumentError( continue;
"Different size of detection.label and detection.score");
} }
// Allow empty label_ids, but if label_ids is not empty, it should have if (detection.label().empty() && detection.label_id().empty()) {
// the same size as the label and score fields. return absl::InvalidArgumentError(
if (!detection.label_id().empty()) { "detection.label and detection.label_id can't be both empty");
if (detection.label_id().size() != detection.label().size()) { }
// Allow empty label (for indexed feature inputs), but if label is not
// empty, it should have the same size as the score field.
if (!detection.label().empty()) {
if (detection.label().size() != detection.score().size()) {
return absl::InvalidArgumentError( return absl::InvalidArgumentError(
"Different size of detection.label_id and detection.label"); "Different size of detection.label and detection.score");
} }
} }
for (int i = 0; i < detection.label().size(); ++i) { // Allow empty label_ids, but if label_ids is not empty, it should have
// the same size as the score field.
if (!detection.label_id().empty()) {
if (detection.label_id().size() != detection.score().size()) {
return absl::InvalidArgumentError(
"Different size of detection.label_id and detection.score");
}
}
for (int i = 0; i < detection.score().size(); ++i) {
if (!detection.label_id().empty()) { if (!detection.label_id().empty()) {
mpms::AddClipLabelIndex(key, detection.label_id(i), mpms::AddClipLabelIndex(key, detection.label_id(i),
sequence_.get()); sequence_.get());
} }
mpms::AddClipLabelString(key, detection.label(i), sequence_.get()); if (!detection.label().empty()) {
mpms::AddClipLabelString(key, detection.label(i), sequence_.get());
}
mpms::AddClipLabelConfidence(key, detection.score(i), mpms::AddClipLabelConfidence(key, detection.score(i),
sequence_.get()); sequence_.get());
} }
@@ -75,6 +75,7 @@ constexpr char kImageTag[] = "IMAGE";
constexpr char kClipMediaIdTag[] = "CLIP_MEDIA_ID"; constexpr char kClipMediaIdTag[] = "CLIP_MEDIA_ID";
constexpr char kClipLabelTestTag[] = "CLIP_LABEL_TEST"; constexpr char kClipLabelTestTag[] = "CLIP_LABEL_TEST";
constexpr char kClipLabelOtherTag[] = "CLIP_LABEL_OTHER"; constexpr char kClipLabelOtherTag[] = "CLIP_LABEL_OTHER";
constexpr char kClipLabelAnotherTag[] = "CLIP_LABEL_ANOTHER";
class PackMediaSequenceCalculatorTest : public ::testing::Test { class PackMediaSequenceCalculatorTest : public ::testing::Test {
protected: protected:
@@ -1166,9 +1167,10 @@ TEST_F(PackMediaSequenceCalculatorTest, PacksTwoMaskDetections) {
testing::ElementsAreArray(::std::vector<std::string>({"mask"}))); testing::ElementsAreArray(::std::vector<std::string>({"mask"})));
} }
TEST_F(PackMediaSequenceCalculatorTest, PackTwoClipLabels) { TEST_F(PackMediaSequenceCalculatorTest, PackThreeClipLabels) {
SetUpCalculator( SetUpCalculator(
/*input_streams=*/{"CLIP_LABEL_TEST:test", "CLIP_LABEL_OTHER:test2"}, /*input_streams=*/{"CLIP_LABEL_TEST:test", "CLIP_LABEL_OTHER:test2",
"CLIP_LABEL_ANOTHER:test3"},
/*features=*/{}, /*output_only_if_all_present=*/false, /*features=*/{}, /*output_only_if_all_present=*/false,
/*replace_instead_of_append=*/true); /*replace_instead_of_append=*/true);
auto input_sequence = ::absl::make_unique<tf::SequenceExample>(); auto input_sequence = ::absl::make_unique<tf::SequenceExample>();
@@ -1192,6 +1194,16 @@ TEST_F(PackMediaSequenceCalculatorTest, PackTwoClipLabels) {
runner_->MutableInputs() runner_->MutableInputs()
->Tag(kClipLabelOtherTag) ->Tag(kClipLabelOtherTag)
.packets.push_back(MakePacket<Detection>(detection_2).At(Timestamp(2))); .packets.push_back(MakePacket<Detection>(detection_2).At(Timestamp(2)));
// No label for detection_3.
Detection detection_3;
detection_3.add_label_id(3);
detection_3.add_label_id(4);
detection_3.add_score(0.3);
detection_3.add_score(0.4);
runner_->MutableInputs()
->Tag(kClipLabelAnotherTag)
.packets.push_back(MakePacket<Detection>(detection_3).At(Timestamp(3)));
runner_->MutableSidePackets()->Tag(kSequenceExampleTag) = runner_->MutableSidePackets()->Tag(kSequenceExampleTag) =
Adopt(input_sequence.release()); Adopt(input_sequence.release());
@@ -1214,6 +1226,86 @@ TEST_F(PackMediaSequenceCalculatorTest, PackTwoClipLabels) {
ASSERT_FALSE(mpms::HasClipLabelIndex("OTHER", output_sequence)); ASSERT_FALSE(mpms::HasClipLabelIndex("OTHER", output_sequence));
ASSERT_THAT(mpms::GetClipLabelConfidence("OTHER", output_sequence), ASSERT_THAT(mpms::GetClipLabelConfidence("OTHER", output_sequence),
testing::ElementsAre(0.3, 0.4)); testing::ElementsAre(0.3, 0.4));
ASSERT_FALSE(mpms::HasClipLabelString("ANOTHER", output_sequence));
ASSERT_THAT(mpms::GetClipLabelIndex("ANOTHER", output_sequence),
testing::ElementsAre(3, 4));
ASSERT_THAT(mpms::GetClipLabelConfidence("ANOTHER", output_sequence),
testing::ElementsAre(0.3, 0.4));
}
TEST_F(PackMediaSequenceCalculatorTest, PackTwoClipLabels_EmptyScore) {
SetUpCalculator(
/*input_streams=*/{"CLIP_LABEL_TEST:test", "CLIP_LABEL_OTHER:test2"},
/*features=*/{}, /*output_only_if_all_present=*/false,
/*replace_instead_of_append=*/true);
auto input_sequence = ::absl::make_unique<tf::SequenceExample>();
// No score in detection_1. detection_1 is ignored.
Detection detection_1;
detection_1.add_label("label_1");
detection_1.add_label("label_2");
runner_->MutableInputs()
->Tag(kClipLabelTestTag)
.packets.push_back(MakePacket<Detection>(detection_1).At(Timestamp(1)));
Detection detection_2;
detection_2.add_label("label_3");
detection_2.add_label("label_4");
detection_2.add_score(0.3);
detection_2.add_score(0.4);
runner_->MutableInputs()
->Tag(kClipLabelOtherTag)
.packets.push_back(MakePacket<Detection>(detection_2).At(Timestamp(2)));
runner_->MutableSidePackets()->Tag(kSequenceExampleTag) =
Adopt(input_sequence.release());
MP_ASSERT_OK(runner_->Run());
const std::vector<Packet>& output_packets =
runner_->Outputs().Tag(kSequenceExampleTag).packets;
ASSERT_EQ(1, output_packets.size());
const tf::SequenceExample& output_sequence =
output_packets[0].Get<tf::SequenceExample>();
ASSERT_FALSE(mpms::HasClipLabelString("TEST", output_sequence));
ASSERT_FALSE(mpms::HasClipLabelIndex("TEST", output_sequence));
ASSERT_FALSE(mpms::HasClipLabelConfidence("TEST", output_sequence));
ASSERT_THAT(mpms::GetClipLabelString("OTHER", output_sequence),
testing::ElementsAre("label_3", "label_4"));
ASSERT_FALSE(mpms::HasClipLabelIndex("OTHER", output_sequence));
ASSERT_THAT(mpms::GetClipLabelConfidence("OTHER", output_sequence),
testing::ElementsAre(0.3, 0.4));
}
TEST_F(PackMediaSequenceCalculatorTest, PackTwoClipLabels_NoLabelOrLabelIndex) {
SetUpCalculator(
/*input_streams=*/{"CLIP_LABEL_TEST:test", "CLIP_LABEL_OTHER:test2"},
/*features=*/{}, /*output_only_if_all_present=*/false,
/*replace_instead_of_append=*/true);
auto input_sequence = ::absl::make_unique<tf::SequenceExample>();
// No label or label_index in detection_1.
Detection detection_1;
detection_1.add_score(0.1);
runner_->MutableInputs()
->Tag(kClipLabelTestTag)
.packets.push_back(MakePacket<Detection>(detection_1).At(Timestamp(1)));
Detection detection_2;
detection_2.add_label("label_3");
detection_2.add_label("label_4");
detection_2.add_score(0.3);
detection_2.add_score(0.4);
runner_->MutableInputs()
->Tag(kClipLabelOtherTag)
.packets.push_back(MakePacket<Detection>(detection_2).At(Timestamp(2)));
runner_->MutableSidePackets()->Tag(kSequenceExampleTag) =
Adopt(input_sequence.release());
ASSERT_THAT(
runner_->Run(),
testing::status::StatusIs(
absl::StatusCode::kInvalidArgument,
testing::HasSubstr(
"detection.label and detection.label_id can't be both empty")));
} }
TEST_F(PackMediaSequenceCalculatorTest, TEST_F(PackMediaSequenceCalculatorTest,
@@ -1259,7 +1351,7 @@ TEST_F(PackMediaSequenceCalculatorTest,
/*replace_instead_of_append=*/true); /*replace_instead_of_append=*/true);
auto input_sequence = ::absl::make_unique<tf::SequenceExample>(); auto input_sequence = ::absl::make_unique<tf::SequenceExample>();
// 2 labels and 1 label_id in detection_1. // 2 scores and 1 label_id in detection_1.
Detection detection_1; Detection detection_1;
detection_1.add_label("label_1"); detection_1.add_label("label_1");
detection_1.add_label("label_2"); detection_1.add_label("label_2");
@@ -1285,7 +1377,7 @@ TEST_F(PackMediaSequenceCalculatorTest,
testing::status::StatusIs( testing::status::StatusIs(
absl::StatusCode::kInvalidArgument, absl::StatusCode::kInvalidArgument,
testing::HasSubstr( testing::HasSubstr(
"Different size of detection.label_id and detection.label"))); "Different size of detection.label_id and detection.score")));
} }
TEST_F(PackMediaSequenceCalculatorTest, ReplaceTwoClipLabels) { TEST_F(PackMediaSequenceCalculatorTest, ReplaceTwoClipLabels) {
@@ -489,8 +489,8 @@ absl::Status TfLiteInferenceCalculator::WriteKernelsToFile() {
#if MEDIAPIPE_TFLITE_GL_INFERENCE && defined(MEDIAPIPE_ANDROID) #if MEDIAPIPE_TFLITE_GL_INFERENCE && defined(MEDIAPIPE_ANDROID)
if (use_kernel_caching_) { if (use_kernel_caching_) {
// Save kernel file. // Save kernel file.
ASSIGN_OR_RETURN(std::vector<uint8_t> kernel_cache, MP_ASSIGN_OR_RETURN(std::vector<uint8_t> kernel_cache,
tflite_gpu_runner_->GetSerializedBinaryCache()); tflite_gpu_runner_->GetSerializedBinaryCache());
std::string cache_str(kernel_cache.begin(), kernel_cache.end()); std::string cache_str(kernel_cache.begin(), kernel_cache.end());
MP_RETURN_IF_ERROR( MP_RETURN_IF_ERROR(
mediapipe::file::SetContents(cached_kernel_filename_, cache_str)); mediapipe::file::SetContents(cached_kernel_filename_, cache_str));
@@ -733,7 +733,7 @@ absl::Status TfLiteInferenceCalculator::ReadKernelsFromFile() {
absl::Status TfLiteInferenceCalculator::InitTFLiteGPURunner( absl::Status TfLiteInferenceCalculator::InitTFLiteGPURunner(
CalculatorContext* cc) { CalculatorContext* cc) {
#if MEDIAPIPE_TFLITE_GL_INFERENCE #if MEDIAPIPE_TFLITE_GL_INFERENCE
ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(*cc)); MP_ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(*cc));
const auto& model = *model_packet_.Get<TfLiteModelPtr>(); const auto& model = *model_packet_.Get<TfLiteModelPtr>();
tflite::ops::builtin::BuiltinOpResolverWithoutDefaultDelegates tflite::ops::builtin::BuiltinOpResolverWithoutDefaultDelegates
@@ -817,8 +817,8 @@ absl::Status TfLiteInferenceCalculator::InitTFLiteGPURunner(
gpu_data_out_.resize(tflite_gpu_runner_->outputs_size()); gpu_data_out_.resize(tflite_gpu_runner_->outputs_size());
for (int i = 0; i < tflite_gpu_runner_->outputs_size(); ++i) { for (int i = 0; i < tflite_gpu_runner_->outputs_size(); ++i) {
gpu_data_out_[i] = absl::make_unique<GPUData>(); gpu_data_out_[i] = absl::make_unique<GPUData>();
ASSIGN_OR_RETURN(gpu_data_out_[i]->elements, MP_ASSIGN_OR_RETURN(gpu_data_out_[i]->elements,
tflite_gpu_runner_->GetOutputElements(i)); tflite_gpu_runner_->GetOutputElements(i));
// Create and bind input buffer. // Create and bind input buffer.
MP_RETURN_IF_ERROR( MP_RETURN_IF_ERROR(
::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer<float>( ::tflite::gpu::gl::CreateReadWriteShaderStorageBuffer<float>(
@@ -839,7 +839,7 @@ absl::Status TfLiteInferenceCalculator::LoadModel(CalculatorContext* cc) {
return absl::OkStatus(); return absl::OkStatus();
} }
ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(*cc)); MP_ASSIGN_OR_RETURN(model_packet_, GetModelAsPacket(*cc));
const auto& model = *model_packet_.Get<TfLiteModelPtr>(); const auto& model = *model_packet_.Get<TfLiteModelPtr>();
tflite::ops::builtin::BuiltinOpResolverWithoutDefaultDelegates tflite::ops::builtin::BuiltinOpResolverWithoutDefaultDelegates
@@ -101,8 +101,8 @@ absl::Status TfLiteTensorsToClassificationCalculator::Open(
top_k_ = options_.top_k(); top_k_ = options_.top_k();
if (options_.has_label_map_path()) { if (options_.has_label_map_path()) {
std::string string_path; std::string string_path;
ASSIGN_OR_RETURN(string_path, MP_ASSIGN_OR_RETURN(string_path,
PathToResourceAsFile(options_.label_map_path())); PathToResourceAsFile(options_.label_map_path()));
std::string label_map_string; std::string label_map_string;
MP_RETURN_IF_ERROR(file::GetContents(string_path, &label_map_string)); MP_RETURN_IF_ERROR(file::GetContents(string_path, &label_map_string));
+157
View File
@@ -1575,3 +1575,160 @@ cc_test(
"@com_google_absl//absl/status", "@com_google_absl//absl/status",
], ],
) )
mediapipe_proto_library(
name = "landmarks_transformation_calculator_proto",
srcs = ["landmarks_transformation_calculator.proto"],
deps = [
"//mediapipe/framework:calculator_options_proto",
"//mediapipe/framework:calculator_proto",
],
)
cc_library(
name = "landmarks_transformation_calculator",
srcs = ["landmarks_transformation_calculator.cc"],
hdrs = ["landmarks_transformation_calculator.h"],
deps = [
":landmarks_transformation_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:node",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:statusor",
],
alwayslink = 1,
)
cc_test(
name = "landmarks_transformation_calculator_test",
srcs = ["landmarks_transformation_calculator_test.cc"],
deps = [
":landmarks_transformation_calculator",
":landmarks_transformation_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:calculator_runner",
"//mediapipe/framework:packet",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "combine_joints_calculator",
srcs = ["combine_joints_calculator.cc"],
hdrs = ["combine_joints_calculator.h"],
deps = [
":combine_joints_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:node",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/port:ret_check",
],
alwayslink = 1,
)
mediapipe_proto_library(
name = "combine_joints_calculator_proto",
srcs = ["combine_joints_calculator.proto"],
deps = [
"//mediapipe/framework:calculator_options_proto",
"//mediapipe/framework:calculator_proto",
"//mediapipe/framework/formats:body_rig_proto",
],
)
cc_test(
name = "combine_joints_calculator_test",
srcs = ["combine_joints_calculator_test.cc"],
deps = [
":combine_joints_calculator",
":combine_joints_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:calculator_runner",
"//mediapipe/framework:packet",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)
cc_library(
name = "set_joints_visibility_calculator",
srcs = ["set_joints_visibility_calculator.cc"],
hdrs = ["set_joints_visibility_calculator.h"],
deps = [
":set_joints_visibility_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:node",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
],
alwayslink = 1,
)
mediapipe_proto_library(
name = "set_joints_visibility_calculator_proto",
srcs = ["set_joints_visibility_calculator.proto"],
deps = [
"//mediapipe/framework:calculator_options_proto",
"//mediapipe/framework:calculator_proto",
],
)
cc_test(
name = "set_joints_visibility_calculator_test",
srcs = ["set_joints_visibility_calculator_test.cc"],
deps = [
":set_joints_visibility_calculator",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:calculator_runner",
"//mediapipe/framework:packet",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:optional",
],
)
cc_library(
name = "pass_through_or_empty_detection_vector_calculator",
srcs = ["pass_through_or_empty_detection_vector_calculator.cc"],
hdrs = ["pass_through_or_empty_detection_vector_calculator.h"],
deps = [
"//mediapipe/framework:calculator_context",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/api2:node",
"//mediapipe/framework/api2:packet",
"//mediapipe/framework/api2:port",
"//mediapipe/framework/formats:detection_cc_proto",
"@com_google_absl//absl/status",
],
alwayslink = 1,
)
cc_test(
name = "pass_through_or_empty_detection_vector_calculator_test",
srcs = ["pass_through_or_empty_detection_vector_calculator_test.cc"],
tags = ["desktop_only_test"],
deps = [
":pass_through_or_empty_detection_vector_calculator",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:detection_cc_proto",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:status_matchers",
"@com_google_absl//absl/status",
],
)
@@ -171,13 +171,13 @@ class AssociationCalculator : public CalculatorBase {
// Compare this element with elements of the input collection. If this // Compare this element with elements of the input collection. If this
// element has high overlap with elements of the collection, remove // element has high overlap with elements of the collection, remove
// those elements from the collection and add this element. // those elements from the collection and add this element.
ASSIGN_OR_RETURN(auto cur_rect, GetRectangle(element)); MP_ASSIGN_OR_RETURN(auto cur_rect, GetRectangle(element));
bool change_id = false; bool change_id = false;
int new_elem_id = -1; int new_elem_id = -1;
for (auto uit = current->begin(); uit != current->end();) { for (auto uit = current->begin(); uit != current->end();) {
ASSIGN_OR_RETURN(auto prev_rect, GetRectangle(*uit)); MP_ASSIGN_OR_RETURN(auto prev_rect, GetRectangle(*uit));
if (CalculateIou(cur_rect, prev_rect) > if (CalculateIou(cur_rect, prev_rect) >
options_.min_similarity_threshold()) { options_.min_similarity_threshold()) {
std::pair<bool, int> prev_id = GetId(*uit); std::pair<bool, int> prev_id = GetId(*uit);
@@ -0,0 +1,79 @@
// Copyright 2023 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/calculators/util/combine_joints_calculator.h"
#include <utility>
#include "mediapipe/calculators/util/combine_joints_calculator.pb.h"
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/port/ret_check.h"
namespace mediapipe {
namespace api2 {
namespace {} // namespace
class CombineJointsCalculatorImpl : public NodeImpl<CombineJointsCalculator> {
public:
absl::Status Open(CalculatorContext* cc) override {
options_ = cc->Options<CombineJointsCalculatorOptions>();
RET_CHECK_GE(options_.num_joints(), 0);
RET_CHECK_GT(kInJoints(cc).Count(), 0);
RET_CHECK_EQ(kInJoints(cc).Count(), options_.joints_mapping_size());
RET_CHECK(options_.has_default_joint());
for (const auto& mapping : options_.joints_mapping()) {
for (int idx : mapping.idx()) {
RET_CHECK_GE(idx, 0);
RET_CHECK_LT(idx, options_.num_joints());
}
}
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) override {
// Initialize output joints with default values.
JointList out_joints;
for (int i = 0; i < options_.num_joints(); ++i) {
*out_joints.add_joint() = options_.default_joint();
}
// Override default joints with provided joints.
for (int i = 0; i < kInJoints(cc).Count(); ++i) {
// Skip empty joint streams.
if (kInJoints(cc)[i].IsEmpty()) {
continue;
}
const JointList& in_joints = kInJoints(cc)[i].Get();
const auto& mapping = options_.joints_mapping(i);
RET_CHECK_EQ(in_joints.joint_size(), mapping.idx_size());
for (int j = 0; j < in_joints.joint_size(); ++j) {
*out_joints.mutable_joint(mapping.idx(j)) = in_joints.joint(j);
}
}
kOutJoints(cc).Send(std::move(out_joints));
return absl::OkStatus();
}
private:
CombineJointsCalculatorOptions options_;
};
MEDIAPIPE_NODE_IMPLEMENTATION(CombineJointsCalculatorImpl);
} // namespace api2
} // namespace mediapipe
@@ -0,0 +1,64 @@
// Copyright 2023 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_UTIL_COMBINE_JOINTS_CALCULATOR_H_
#define MEDIAPIPE_CALCULATORS_UTIL_COMBINE_JOINTS_CALCULATOR_H_
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
namespace mediapipe {
namespace api2 {
// A calculator to combine several joint sets into one.
//
// Input:
// JOINTS - Multiple JointList
// Joint sets to combine into one. Subsets are applied in provided order and
// overwrite each other.
//
// Output:
// JOINTS - JointList
// Combined joints.
//
// Example:
// node {
// calculator: "CombineJointsCalculator"
// input_stream: "JOINTS:0:joints_0"
// input_stream: "JOINTS:1:joints_1"
// output_stream: "JOINTS:combined_joints"
// options: {
// [mediapipe.CombineJointsCalculatorOptions.ext] {
// num_joints: 63
// joints_mapping: { idx: [0, 1, 2] }
// joints_mapping: { idx: [2, 3] }
// default_joint: {
// rotation_6d: [1, 0, 0, 1, 0, 0]
// visibility: 1.0
// }
// }
// }
// }
class CombineJointsCalculator : public NodeIntf {
public:
static constexpr Input<mediapipe::JointList>::Multiple kInJoints{"JOINTS"};
static constexpr Output<mediapipe::JointList> kOutJoints{"JOINTS"};
MEDIAPIPE_NODE_INTERFACE(CombineJointsCalculator, kInJoints, kOutJoints);
};
} // namespace api2
} // namespace mediapipe
#endif // MEDIAPIPE_CALCULATORS_UTIL_COMBINE_JOINTS_CALCULATOR_H_
@@ -0,0 +1,46 @@
// Copyright 2023 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.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
import "mediapipe/framework/formats/body_rig.proto";
message CombineJointsCalculatorOptions {
extend CalculatorOptions {
optional CombineJointsCalculatorOptions ext = 406440185;
}
// Mapping from joint set to the resulting set.
message JointsMapping {
// Indexes of provided joints in the resulting joint set.
// All indexes must be within the [0, num_joints - 1] range.
repeated int32 idx = 1 [packed = true];
}
// Number of joints in the resulting set.
optional int32 num_joints = 1;
// Mapping from joint sets to the resulting set.
// Number of mappings must be equal to number of provided joint sets. Number
// of indexes in each mapping must be equal to number of joints in
// corresponding joint set. Mappings are applied in the provided order and can
// overwrite each other.
repeated JointsMapping joints_mapping = 2;
// Default joint to initialize joints in the resulting set.
optional Joint default_joint = 3;
}
@@ -0,0 +1,174 @@
// Copyright 2023 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 <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/str_format.h"
#include "absl/strings/substitute.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/calculator_runner.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/packet.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 api2 {
namespace {
using Node = ::mediapipe::CalculatorGraphConfig::Node;
Joint MakeJoint(const std::vector<float>& rotation_6d,
std::optional<float> visibility) {
Joint joint;
for (float r : rotation_6d) {
joint.add_rotation_6d(r);
}
if (visibility) {
joint.set_visibility(visibility.value());
}
return joint;
}
struct CombineJointsTestCase {
std::string test_name;
int num_joints;
std::string joints_mapping;
std::vector<std::vector<Joint>> in_joints;
std::vector<Joint> out_joints;
};
using CombineJointsTest = ::testing::TestWithParam<CombineJointsTestCase>;
TEST_P(CombineJointsTest, CombineJointsTest) {
const CombineJointsTestCase& tc = GetParam();
std::string input_joint_streams = "";
for (int i = 0; i < tc.in_joints.size(); ++i) {
input_joint_streams +=
absl::StrFormat("input_stream: \"JOINTS:%d:joints_%d\"\n", i, i);
}
// Prepare graph.
mediapipe::CalculatorRunner runner(ParseTextProtoOrDie<Node>(absl::Substitute(
R"(
calculator: "CombineJointsCalculator"
$0
output_stream: "JOINTS:combined_joints"
options: {
[mediapipe.CombineJointsCalculatorOptions.ext] {
num_joints: $1
joints_mapping: [ $2 ]
default_joint: {
rotation_6d: [1, 0, 0, 1, 0, 0]
visibility: 1.0
}
}
}
)",
input_joint_streams, tc.num_joints, tc.joints_mapping)));
// Prepare and send joints.
for (int i = 0; i < tc.in_joints.size(); ++i) {
JointList in_joints;
for (const auto& joint : tc.in_joints[i]) {
*in_joints.add_joint() = joint;
}
runner.MutableInputs()
->Get("JOINTS", i)
.packets.push_back(MakePacket<JointList>(std::move(in_joints))
.At(mediapipe::Timestamp(0)));
}
// Run the graph.
MP_ASSERT_OK(runner.Run());
const auto& output_packets = runner.Outputs().Tag("JOINTS").packets;
EXPECT_EQ(1, output_packets.size());
const auto& out_joints = output_packets[0].Get<JointList>();
EXPECT_EQ(out_joints.joint_size(), tc.out_joints.size());
for (int i = 0; i < out_joints.joint_size(); ++i) {
const Joint& actual = out_joints.joint(i);
const Joint& expected = tc.out_joints[i];
EXPECT_EQ(actual.rotation_6d_size(), expected.rotation_6d_size())
<< "Unexpected joint #" << i << " rotation";
for (int j = 0; j < actual.rotation_6d_size(); ++j) {
EXPECT_NEAR(actual.rotation_6d(j), expected.rotation_6d(j), 1e-5)
<< "Unexpected joint #" << i << " rotation";
}
EXPECT_EQ(actual.has_visibility(), expected.has_visibility())
<< "Unexpected joint #" << i << " visibility";
if (actual.has_visibility()) {
EXPECT_NEAR(actual.visibility(), expected.visibility(), 1e-5)
<< "Unexpected joint #" << i << " visibility";
}
}
}
INSTANTIATE_TEST_SUITE_P(
CombineJointsTests, CombineJointsTest,
testing::ValuesIn<CombineJointsTestCase>({
{"Empty_NoOutJoints", 0, "{ idx: [] }", {{}}, {}},
{"Empty_SingleOutJoint",
1,
"{ idx: [] }",
{{}},
{MakeJoint({1, 0, 0, 1, 0, 0}, 1)}},
{"Single_SetFirst",
2,
"{ idx: [0] }",
{{MakeJoint({3, 3, 3, 3, 3, 3}, 4)}},
{MakeJoint({3, 3, 3, 3, 3, 3}, 4), MakeJoint({1, 0, 0, 1, 0, 0}, 1)}},
{"Single_SetBoth",
2,
"{ idx: [0, 1] }",
{{MakeJoint({3, 3, 3, 3, 3, 3}, 4), MakeJoint({7, 7, 7, 7, 7, 7}, 8)}},
{MakeJoint({3, 3, 3, 3, 3, 3}, 4), MakeJoint({7, 7, 7, 7, 7, 7}, 8)}},
{"Single_SetBoth_ReverseOrder",
2,
"{ idx: [1, 0] }",
{{MakeJoint({3, 3, 3, 3, 3, 3}, 4), MakeJoint({7, 7, 7, 7, 7, 7}, 8)}},
{MakeJoint({7, 7, 7, 7, 7, 7}, 8), MakeJoint({3, 3, 3, 3, 3, 3}, 4)}},
{"Double_NoOverwrite",
3,
"{ idx: [0] }, { idx: [1] }",
{{MakeJoint({3, 3, 3, 3, 3, 3}, 4)},
{MakeJoint({7, 7, 7, 7, 7, 7}, 8)}},
{MakeJoint({3, 3, 3, 3, 3, 3}, 4), MakeJoint({7, 7, 7, 7, 7, 7}, 8),
MakeJoint({1, 0, 0, 1, 0, 0}, 1)}},
{"Double_OverwriteSecond",
3,
"{ idx: [0, 1] }, { idx: [1, 2] }",
{{MakeJoint({3, 3, 3, 3, 3, 3}, 4), MakeJoint({4, 4, 4, 4, 4, 4}, 5)},
{MakeJoint({6, 6, 6, 6, 6, 6}, 7), MakeJoint({8, 8, 8, 8, 8, 8}, 9)}},
{MakeJoint({3, 3, 3, 3, 3, 3}, 4), MakeJoint({6, 6, 6, 6, 6, 6}, 7),
MakeJoint({8, 8, 8, 8, 8, 8}, 9)}},
}),
[](const testing::TestParamInfo<CombineJointsTest::ParamType>& info) {
return info.param.test_name;
});
} // namespace
} // namespace api2
} // namespace mediapipe
@@ -83,8 +83,8 @@ absl::Status DetectionLabelIdToTextCalculator::Open(CalculatorContext* cc) {
<< "Only can set one of the following fields in the CalculatorOptions: " << "Only can set one of the following fields in the CalculatorOptions: "
"label_map_path, label, and label_items."; "label_map_path, label, and label_items.";
std::string string_path; std::string string_path;
ASSIGN_OR_RETURN(string_path, MP_ASSIGN_OR_RETURN(string_path,
PathToResourceAsFile(options.label_map_path())); PathToResourceAsFile(options.label_map_path()));
std::string label_map_string; std::string label_map_string;
MP_RETURN_IF_ERROR( MP_RETURN_IF_ERROR(
mediapipe::GetResourceContents(string_path, &label_map_string)); mediapipe::GetResourceContents(string_path, &label_map_string));
@@ -96,10 +96,10 @@ absl::StatusOr<LocationData::Format> GetLocationDataFormat(
std::vector<Detection>& detections) { std::vector<Detection>& detections) {
RET_CHECK(!detections.empty()); RET_CHECK(!detections.empty());
LocationData::Format output_format; LocationData::Format output_format;
ASSIGN_OR_RETURN(output_format, GetLocationDataFormat(detections[0])); MP_ASSIGN_OR_RETURN(output_format, GetLocationDataFormat(detections[0]));
for (int i = 1; i < detections.size(); ++i) { for (int i = 1; i < detections.size(); ++i) {
ASSIGN_OR_RETURN(LocationData::Format format, MP_ASSIGN_OR_RETURN(LocationData::Format format,
GetLocationDataFormat(detections[i])); GetLocationDataFormat(detections[i]));
if (output_format != format) { if (output_format != format) {
return absl::InvalidArgumentError( return absl::InvalidArgumentError(
"Input detections have different location data formats."); "Input detections have different location data formats.");
@@ -243,8 +243,8 @@ class DetectionTransformationCalculator : public Node {
OutputEmptyDetections(cc); OutputEmptyDetections(cc);
return absl::OkStatus(); return absl::OkStatus();
} }
ASSIGN_OR_RETURN(input_location_data_format, MP_ASSIGN_OR_RETURN(input_location_data_format,
GetLocationDataFormat(transformed_detections)); GetLocationDataFormat(transformed_detections));
for (Detection& detection : transformed_detections) { for (Detection& detection : transformed_detections) {
MP_RETURN_IF_ERROR(ConvertBoundingBox(image_size, &detection)); MP_RETURN_IF_ERROR(ConvertBoundingBox(image_size, &detection));
} }
@@ -254,8 +254,8 @@ class DetectionTransformationCalculator : public Node {
OutputEmptyDetections(cc); OutputEmptyDetections(cc);
return absl::OkStatus(); return absl::OkStatus();
} }
ASSIGN_OR_RETURN(input_location_data_format, MP_ASSIGN_OR_RETURN(input_location_data_format,
GetLocationDataFormat(kInDetection(cc).Get())); GetLocationDataFormat(kInDetection(cc).Get()));
MP_RETURN_IF_ERROR( MP_RETURN_IF_ERROR(
ConvertBoundingBox(image_size, &transformed_detection)); ConvertBoundingBox(image_size, &transformed_detection));
transformed_detections.push_back(transformed_detection); transformed_detections.push_back(transformed_detection);
@@ -137,8 +137,8 @@ class LandmarksRefinementCalculatorImpl
} }
// Validate indexes mapping and get total number of refined landmarks. // Validate indexes mapping and get total number of refined landmarks.
ASSIGN_OR_RETURN(n_refined_landmarks_, MP_ASSIGN_OR_RETURN(n_refined_landmarks_,
GetNumberOfRefinedLandmarks(options_.refinement())); GetNumberOfRefinedLandmarks(options_.refinement()));
// Validate that number of refinements and landmark streams is the same. // Validate that number of refinements and landmark streams is the same.
RET_CHECK_EQ(kLandmarks(cc).Count(), options_.refinement_size()) RET_CHECK_EQ(kLandmarks(cc).Count(), options_.refinement_size())
@@ -43,9 +43,10 @@ class LandmarksSmoothingCalculatorImpl
: public NodeImpl<LandmarksSmoothingCalculator> { : public NodeImpl<LandmarksSmoothingCalculator> {
public: public:
absl::Status Open(CalculatorContext* cc) override { absl::Status Open(CalculatorContext* cc) override {
ASSIGN_OR_RETURN(landmarks_filter_, MP_ASSIGN_OR_RETURN(
InitializeLandmarksFilter( landmarks_filter_,
cc->Options<LandmarksSmoothingCalculatorOptions>())); InitializeLandmarksFilter(
cc->Options<LandmarksSmoothingCalculatorOptions>()));
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -348,7 +348,8 @@ absl::StatusOr<LandmarksFilter*> MultiLandmarkFilters::GetOrCreate(
return it->second.get(); return it->second.get();
} }
ASSIGN_OR_RETURN(auto landmarks_filter, InitializeLandmarksFilter(options)); MP_ASSIGN_OR_RETURN(auto landmarks_filter,
InitializeLandmarksFilter(options));
filters_[tracking_id] = std::move(landmarks_filter); filters_[tracking_id] = std::move(landmarks_filter);
return filters_[tracking_id].get(); return filters_[tracking_id].get();
} }
@@ -0,0 +1,142 @@
// Copyright 2023 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/calculators/util/landmarks_transformation_calculator.h"
#include <utility>
#include "mediapipe/calculators/util/landmarks_transformation_calculator.pb.h"
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/statusor.h"
namespace mediapipe {
namespace api2 {
namespace {
StatusOr<LandmarkList> NormalizeTranslation(const LandmarkList& in_landmarks) {
RET_CHECK_GT(in_landmarks.landmark_size(), 0);
double x_sum = 0.0f;
double y_sum = 0.0f;
double z_sum = 0.0f;
for (auto& in_landmark : in_landmarks.landmark()) {
x_sum += in_landmark.x();
y_sum += in_landmark.y();
z_sum += in_landmark.z();
}
float x_mean = x_sum / in_landmarks.landmark_size();
float y_mean = y_sum / in_landmarks.landmark_size();
float z_mean = z_sum / in_landmarks.landmark_size();
LandmarkList out_landmarks;
for (auto& in_landmark : in_landmarks.landmark()) {
auto* out_landmark = out_landmarks.add_landmark();
*out_landmark = in_landmark;
out_landmark->set_x(in_landmark.x() - x_mean);
out_landmark->set_y(in_landmark.y() - y_mean);
out_landmark->set_z(in_landmark.z() - z_mean);
}
return out_landmarks;
}
StatusOr<LandmarkList> FlipAxis(
const LandmarkList& in_landmarks,
const LandmarksTransformationCalculatorOptions::FlipAxis& options) {
float x_mul = options.flip_x() ? -1 : 1;
float y_mul = options.flip_y() ? -1 : 1;
float z_mul = options.flip_z() ? -1 : 1;
LandmarkList out_landmarks;
for (auto& in_landmark : in_landmarks.landmark()) {
auto* out_landmark = out_landmarks.add_landmark();
*out_landmark = in_landmark;
out_landmark->set_x(in_landmark.x() * x_mul);
out_landmark->set_y(in_landmark.y() * y_mul);
out_landmark->set_z(in_landmark.z() * z_mul);
}
return out_landmarks;
}
} // namespace
class LandmarksTransformationCalculatorImpl
: public NodeImpl<LandmarksTransformationCalculator> {
public:
static absl::Status UpdateContract(CalculatorContract* cc) {
// Check that if options input stream is connected there should be no static
// options in calculator. Currently there is no such functionality, so we'll
// just check for the number of transforms.
if (kInOptions(cc).IsConnected()) {
RET_CHECK_EQ(cc->Options<LandmarksTransformationCalculatorOptions>()
.transformation_size(),
0);
}
return absl::OkStatus();
}
absl::Status Open(CalculatorContext* cc) override {
options_ = cc->Options<LandmarksTransformationCalculatorOptions>();
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) override {
if (kInLandmarks(cc).IsEmpty()) {
return absl::OkStatus();
}
// Get transformation options for either calculator parameters or input
// stream. Input stream has higher priority.
LandmarksTransformationCalculatorOptions options;
if (kInOptions(cc).IsConnected()) {
// If input stream is connected but is empty - use no transformations and
// return landmarks as is.
if (!kInOptions(cc).IsEmpty()) {
options = kInOptions(cc).Get();
}
} else {
options = options_;
}
LandmarkList landmarks = kInLandmarks(cc).Get();
for (auto& transformation : options.transformation()) {
if (transformation.has_normalize_translation()) {
MP_ASSIGN_OR_RETURN(landmarks, NormalizeTranslation(landmarks));
} else if (transformation.has_flip_axis()) {
MP_ASSIGN_OR_RETURN(landmarks,
FlipAxis(landmarks, transformation.flip_axis()));
} else {
RET_CHECK_FAIL() << "Unknown landmarks transformation";
}
}
kOutLandmarks(cc).Send(std::move(landmarks));
return absl::OkStatus();
}
private:
LandmarksTransformationCalculatorOptions options_;
};
MEDIAPIPE_NODE_IMPLEMENTATION(LandmarksTransformationCalculatorImpl);
} // namespace api2
} // namespace mediapipe
@@ -0,0 +1,62 @@
// Copyright 2023 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_UTIL_LANDMARKS_TRANSFORMATION_CALCULATOR_H_
#define MEDIAPIPE_CALCULATORS_UTIL_LANDMARKS_TRANSFORMATION_CALCULATOR_H_
#include "mediapipe/calculators/util/landmarks_transformation_calculator.pb.h"
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/landmark.pb.h"
namespace mediapipe {
namespace api2 {
// A calculator to transform landmarks.
//
// Input:
// LANDMARKS - LandmarkList
// Landmarks to transform.
//
// Output:
// LANDMARKS - LandmarkList
// Transformed landmarks.
//
// Example:
// node {
// calculator: "LandmarksTransformationCalculator"
// input_stream: "LANDMARKS:in_landmarks"
// output_stream: "LANDMARKS:out_landmarks"
// options: {
// [mediapipe.LandmarksTransformationCalculatorOptions.ext] {
// transformation: { normalize_translation: {} }
// transformation: { flip_axis: { flip_x: true } }
// }
// }
// }
class LandmarksTransformationCalculator : public NodeIntf {
public:
static constexpr Input<mediapipe::LandmarkList> kInLandmarks{"LANDMARKS"};
static constexpr Input<
mediapipe::LandmarksTransformationCalculatorOptions>::Optional kInOptions{
"OPTIONS"};
static constexpr Output<mediapipe::LandmarkList> kOutLandmarks{"LANDMARKS"};
MEDIAPIPE_NODE_INTERFACE(LandmarksTransformationCalculator, kInLandmarks,
kInOptions, kOutLandmarks);
};
} // namespace api2
} // namespace mediapipe
#endif // MEDIAPIPE_CALCULATORS_UTIL_LANDMARKS_TRANSFORMATION_CALCULATOR_H_
@@ -0,0 +1,45 @@
// Copyright 2023 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.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
message LandmarksTransformationCalculatorOptions {
extend CalculatorOptions {
optional LandmarksTransformationCalculatorOptions ext = 421309928;
}
// Normalize translation so that center of mass is in (0, 0, 0).
message NormalizeTranslation {}
// Flip axis by multiplying coordinates along it by `-1`.
message FlipAxis {
optional bool flip_x = 1 [default = false];
optional bool flip_y = 2 [default = false];
optional bool flip_z = 3 [default = false];
}
message Transformation {
oneof transformation {
NormalizeTranslation normalize_translation = 1;
FlipAxis flip_axis = 2;
}
}
// Transformations applied in given order.
repeated Transformation transformation = 1;
}
@@ -0,0 +1,167 @@
// Copyright 2023 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 <string>
#include <utility>
#include <vector>
#include "absl/strings/substitute.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/calculator_runner.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/packet.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 api2 {
namespace {
using Node = ::mediapipe::CalculatorGraphConfig::Node;
Landmark CreateLandmark(float x, float y, float z) {
Landmark lmk;
lmk.set_x(x);
lmk.set_y(y);
lmk.set_z(z);
return lmk;
}
Landmark CreateLandmark(float x, float y, float z, float visibility,
float presence) {
Landmark lmk;
lmk.set_x(x);
lmk.set_y(y);
lmk.set_z(z);
lmk.set_visibility(visibility);
lmk.set_presence(presence);
return lmk;
}
struct LandmarksTransformationestCase {
std::string test_name;
std::string transformations;
std::vector<Landmark> in_landmarks;
std::vector<Landmark> out_landmarks;
};
using LandmarksTransformationest =
::testing::TestWithParam<LandmarksTransformationestCase>;
TEST_P(LandmarksTransformationest, LandmarksTransformationest) {
const LandmarksTransformationestCase& tc = GetParam();
// Prepare graph.
mediapipe::CalculatorRunner runner(ParseTextProtoOrDie<Node>(absl::Substitute(
R"(
calculator: "LandmarksTransformationCalculator"
input_stream: "LANDMARKS:in_landmarks"
output_stream: "LANDMARKS:out_landmarks"
options: {
[mediapipe.LandmarksTransformationCalculatorOptions.ext] {
$0
}
}
)",
tc.transformations)));
// In landmarks.
LandmarkList in_landmarks;
for (auto& lmk : tc.in_landmarks) {
*in_landmarks.add_landmark() = lmk;
}
// Send landmarks to the graph.
runner.MutableInputs()
->Tag("LANDMARKS")
.packets.push_back(MakePacket<LandmarkList>(std::move(in_landmarks))
.At(mediapipe::Timestamp(0)));
// Run the graph.
MP_ASSERT_OK(runner.Run());
const auto& output_packets = runner.Outputs().Tag("LANDMARKS").packets;
EXPECT_EQ(1, output_packets.size());
const auto& out_landmarks = output_packets[0].Get<LandmarkList>();
EXPECT_EQ(out_landmarks.landmark_size(), tc.out_landmarks.size());
for (int i = 0; i < out_landmarks.landmark_size(); ++i) {
auto& lmk = out_landmarks.landmark(i);
auto& exp_lmk = tc.out_landmarks[i];
EXPECT_EQ(lmk.x(), exp_lmk.x()) << "Unexpected lmk[" << i << "].x";
EXPECT_EQ(lmk.y(), exp_lmk.y()) << "Unexpected lmk[" << i << "].y";
EXPECT_EQ(lmk.z(), exp_lmk.z()) << "Unexpected lmk[" << i << "].z";
if (exp_lmk.has_visibility()) {
EXPECT_EQ(lmk.visibility(), exp_lmk.visibility())
<< "Unexpected lmk[" << i << "].visibility";
}
if (exp_lmk.has_presence()) {
EXPECT_EQ(lmk.presence(), exp_lmk.presence())
<< "Unexpected lmk[" << i << "].presence";
}
}
}
INSTANTIATE_TEST_SUITE_P(
LandmarksTransformationests, LandmarksTransformationest,
testing::ValuesIn<LandmarksTransformationestCase>({
{"NoTransformations",
"",
{CreateLandmark(1, 2, 3), CreateLandmark(4, 5, 6)},
{CreateLandmark(1, 2, 3), CreateLandmark(4, 5, 6)}},
{"NormalizeTranslation_OneLandmark",
"transformation: { normalize_translation: {} }",
{CreateLandmark(2, 2, 2)},
{CreateLandmark(0, 0, 0)}},
{"NormalizeTranslation_TwoLandmarks",
"transformation: { normalize_translation: {} }",
{CreateLandmark(2, 2, 2), CreateLandmark(4, 4, 4)},
{CreateLandmark(-1, -1, -1), CreateLandmark(1, 1, 1)}},
{"NormalizeTranslation_ThreeLandmarks",
"transformation: { normalize_translation: {} }",
{CreateLandmark(2, 2, 2), CreateLandmark(4, 4, 4),
CreateLandmark(9, 9, 9)},
{CreateLandmark(-3, -3, -3), CreateLandmark(-1, -1, -1),
CreateLandmark(4, 4, 4)}},
{"NormalizeTranslation_VisibilityAndPresence",
"transformation: { normalize_translation: {} }",
{CreateLandmark(0, 0, 0, 4, 5)},
{CreateLandmark(0, 0, 0, 4, 5)}},
{"FlipAxis_X",
"transformation: { flip_axis: { flip_x: true } }",
{CreateLandmark(2, 2, 2)},
{CreateLandmark(-2, 2, 2)}},
{"FlipAxis_Y",
"transformation: { flip_axis: { flip_y: true } }",
{CreateLandmark(2, 2, 2)},
{CreateLandmark(2, -2, 2)}},
{"FlipAxis_Z",
"transformation: { flip_axis: { flip_z: true } }",
{CreateLandmark(2, 2, 2)},
{CreateLandmark(2, 2, -2)}},
{"FlipAxis_VisibilityAndPresence",
"transformation: { flip_axis: { flip_x: true } }",
{CreateLandmark(0, 0, 0, 4, 5)},
{CreateLandmark(0, 0, 0, 4, 5)}},
}),
[](const testing::TestParamInfo<LandmarksTransformationest::ParamType>&
info) { return info.param.test_name; });
} // namespace
} // namespace api2
} // namespace mediapipe
@@ -86,7 +86,7 @@ class LocalFileContentsCalculator : public CalculatorBase {
++input_id, ++output_id) { ++input_id, ++output_id) {
std::string file_path = std::string file_path =
cc->InputSidePackets().Get(input_id).Get<std::string>(); cc->InputSidePackets().Get(input_id).Get<std::string>();
ASSIGN_OR_RETURN(file_path, PathToResourceAsFile(file_path)); MP_ASSIGN_OR_RETURN(file_path, PathToResourceAsFile(file_path));
std::string contents; std::string contents;
MP_RETURN_IF_ERROR(GetResourceContents( MP_RETURN_IF_ERROR(GetResourceContents(
@@ -83,10 +83,11 @@ class MultiLandmarksSmoothingCalculatorImpl
image_width, image_height); image_width, image_height);
} }
ASSIGN_OR_RETURN(auto* landmarks_filter, MP_ASSIGN_OR_RETURN(
multi_filters_.GetOrCreate( auto* landmarks_filter,
tracking_ids[i], multi_filters_.GetOrCreate(
cc->Options<LandmarksSmoothingCalculatorOptions>())); tracking_ids[i],
cc->Options<LandmarksSmoothingCalculatorOptions>()));
LandmarkList out_landmarks; LandmarkList out_landmarks;
MP_RETURN_IF_ERROR(landmarks_filter->Apply(in_landmarks, timestamp, MP_RETURN_IF_ERROR(landmarks_filter->Apply(in_landmarks, timestamp,
@@ -74,10 +74,11 @@ class MultiWorldLandmarksSmoothingCalculatorImpl
object_scale = GetObjectScale(object_scale_roi_vec.value()[i]); object_scale = GetObjectScale(object_scale_roi_vec.value()[i]);
} }
ASSIGN_OR_RETURN(auto* landmarks_filter, MP_ASSIGN_OR_RETURN(
multi_filters_.GetOrCreate( auto* landmarks_filter,
tracking_ids[i], multi_filters_.GetOrCreate(
cc->Options<LandmarksSmoothingCalculatorOptions>())); tracking_ids[i],
cc->Options<LandmarksSmoothingCalculatorOptions>()));
LandmarkList out_landmarks; LandmarkList out_landmarks;
MP_RETURN_IF_ERROR(landmarks_filter->Apply(in_landmarks, timestamp, MP_RETURN_IF_ERROR(landmarks_filter->Apply(in_landmarks, timestamp,
@@ -0,0 +1,27 @@
#include "mediapipe/calculators/util/pass_through_or_empty_detection_vector_calculator.h"
#include <vector>
#include "absl/status/status.h"
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/calculator_context.h"
#include "mediapipe/framework/formats/detection.pb.h"
namespace mediapipe {
class PassThroughOrEmptyDetectionVectorCalculatorImpl
: public mediapipe::api2::NodeImpl<
PassThroughOrEmptyDetectionVectorCalculator> {
public:
absl::Status Process(CalculatorContext* cc) override {
if (kInputVector(cc).IsEmpty()) {
kOutputVector(cc).Send(std::vector<mediapipe::Detection>{});
return absl::OkStatus();
}
kOutputVector(cc).Send(kInputVector(cc));
return absl::OkStatus();
}
};
MEDIAPIPE_NODE_IMPLEMENTATION(PassThroughOrEmptyDetectionVectorCalculatorImpl);
} // namespace mediapipe
@@ -0,0 +1,55 @@
#ifndef MEDIAPIPE_CALCULATORS_UTILS_PASS_THROUGH_OR_EMPTY_DETECTION_VECTOR_CALCULATOR_H_
#define MEDIAPIPE_CALCULATORS_UTILS_PASS_THROUGH_OR_EMPTY_DETECTION_VECTOR_CALCULATOR_H_
#include <vector>
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/api2/packet.h"
#include "mediapipe/framework/api2/port.h"
#include "mediapipe/framework/formats/detection.pb.h"
namespace mediapipe {
// Calculator to pass through input vector of detections if packet is not empty,
// otherwise - outputing a new empty vector. So, instead of empty packet you get
// a packet containing empty vector.
//
// Example:
// node {
// calculator: "PassThroughOrEmptyDetectionVectorCalculator"
// input_stream: "TICK:tick"
// input_stream: "VECTOR:input_detections"
// output_stream: "VECTOR:output_detections"
// }
class PassThroughOrEmptyDetectionVectorCalculator
: public mediapipe::api2::NodeIntf {
public:
static constexpr mediapipe::api2::Input<std::vector<mediapipe::Detection>>
kInputVector{"VECTOR"};
static constexpr mediapipe::api2::Input<mediapipe::api2::AnyType> kTick{
"TICK"};
static constexpr mediapipe::api2::Output<std::vector<mediapipe::Detection>>
kOutputVector{"VECTOR"};
MEDIAPIPE_NODE_INTERFACE(
::mediapipe::PassThroughOrEmptyDetectionVectorCalculator, kInputVector,
kTick, kOutputVector);
};
template <typename TickT>
api2::builder::Stream<std::vector<mediapipe::Detection>>
PassThroughOrEmptyDetectionVector(
api2::builder::Stream<std::vector<mediapipe::Detection>> detections,
api2::builder::Stream<TickT> tick, mediapipe::api2::builder::Graph& graph) {
auto& node =
graph.AddNode("mediapipe.PassThroughOrEmptyDetectionVectorCalculator");
detections.ConnectTo(
node[PassThroughOrEmptyDetectionVectorCalculator::kInputVector]);
tick.ConnectTo(node[PassThroughOrEmptyDetectionVectorCalculator::kTick]);
return node[PassThroughOrEmptyDetectionVectorCalculator::kOutputVector];
}
} // namespace mediapipe
#endif // MEDIAPIPE_CALCULATORS_UTILS_PASS_THROUGH_OR_EMPTY_DETECTION_VECTOR_CALCULATOR_H_
@@ -0,0 +1,113 @@
#include "mediapipe/calculators/util/pass_through_or_empty_detection_vector_calculator.h"
#include <string>
#include <utility>
#include <vector>
#include "absl/status/status.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/detection.pb.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/port/status_matchers.h"
namespace mediapipe {
namespace {
CalculatorGraphConfig GetGraphConfig() {
mediapipe::api2::builder::Graph graph;
mediapipe::api2::builder::Stream<std::string> tick =
graph.In("TICK").SetName("tick").Cast<std::string>();
mediapipe::api2::builder::Stream<std::vector<mediapipe::Detection>>
detections = graph.In("DETECTIONS")
.SetName("input_detections")
.Cast<std::vector<mediapipe::Detection>>();
mediapipe::api2::builder::Stream<std::vector<mediapipe::Detection>>
output_detections =
PassThroughOrEmptyDetectionVector(detections, tick, graph);
output_detections.SetName("output_detections");
return graph.GetConfig();
}
absl::Status SendTick(CalculatorGraph& graph, int at) {
return graph.AddPacketToInputStream(
"tick",
mediapipe::MakePacket<std::string>("tick").At(mediapipe::Timestamp(at)));
}
absl::Status SendDetections(CalculatorGraph& graph,
std::vector<mediapipe::Detection> detections,
int at) {
return graph.AddPacketToInputStream(
"input_detections",
mediapipe::MakePacket<std::vector<mediapipe::Detection>>(
std::move(detections))
.At(mediapipe::Timestamp(at)));
}
TEST(PassThroughOrEmptyDetectionVectorCalculatorTest, PassThrough) {
CalculatorGraphConfig graph_config = GetGraphConfig();
std::vector<Packet> output_packets;
tool::AddVectorSink("output_detections", &graph_config, &output_packets);
CalculatorGraph calculator_graph(graph_config);
MP_ASSERT_OK(calculator_graph.StartRun({}));
// Sending empty vector.
MP_ASSERT_OK(SendTick(calculator_graph, /*at=*/1));
MP_ASSERT_OK(SendDetections(calculator_graph,
std::vector<mediapipe::Detection>{},
/*at=*/1));
MP_ASSERT_OK(calculator_graph.WaitUntilIdle());
ASSERT_EQ(output_packets.size(), 1);
EXPECT_TRUE(
output_packets[0].Get<std::vector<mediapipe::Detection>>().empty());
// Sending non empty vector.
output_packets.clear();
mediapipe::Detection detection;
detection.set_detection_id(1000);
MP_ASSERT_OK(SendTick(calculator_graph, /*at=*/2));
MP_ASSERT_OK(SendDetections(calculator_graph, {detection}, /*at=*/2));
MP_ASSERT_OK(calculator_graph.WaitUntilIdle());
ASSERT_EQ(output_packets.size(), 1);
}
TEST(PassThroughOrEmptyDetectionVectorCalculatorTest, OrEmptyVector) {
CalculatorGraphConfig graph_config = GetGraphConfig();
std::vector<Packet> output_packets;
tool::AddVectorSink("output_detections", &graph_config, &output_packets);
CalculatorGraph calculator_graph(graph_config);
MP_ASSERT_OK(calculator_graph.StartRun({}));
mediapipe::Detection detection;
detection.set_detection_id(1000);
MP_ASSERT_OK(SendTick(calculator_graph, /*at=*/1));
MP_ASSERT_OK(SendDetections(calculator_graph, {detection}, /*at=*/1));
MP_ASSERT_OK(SendTick(calculator_graph, /*at=*/2));
MP_ASSERT_OK(SendTick(calculator_graph, /*at=*/3));
MP_ASSERT_OK(SendTick(calculator_graph, /*at=*/4));
// This should trigger trigger calculator at 2, 3, 4 as detections are not
// expected.
MP_ASSERT_OK(SendDetections(calculator_graph,
std::vector<mediapipe::Detection>{},
/*at=*/5));
MP_ASSERT_OK(calculator_graph.WaitUntilIdle());
ASSERT_EQ(output_packets.size(), 4);
for (int i = 1; i < output_packets.size(); ++i) {
EXPECT_TRUE(
output_packets[i].Get<std::vector<mediapipe::Detection>>().empty());
}
}
} // namespace
} // namespace mediapipe
@@ -77,7 +77,7 @@ class RefineLandmarksFromHeatmapCalculatorImpl
const auto& options = const auto& options =
cc->Options<mediapipe::RefineLandmarksFromHeatmapCalculatorOptions>(); cc->Options<mediapipe::RefineLandmarksFromHeatmapCalculatorOptions>();
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
auto out_lms, auto out_lms,
RefineLandmarksFromHeatMap( RefineLandmarksFromHeatMap(
in_lms, hm_raw, hm_tensor.shape().dims, options.kernel_size(), in_lms, hm_raw, hm_tensor.shape().dims, options.kernel_size(),
@@ -108,7 +108,7 @@ absl::StatusOr<mediapipe::NormalizedLandmarkList> RefineLandmarksFromHeatMap(
const float* heatmap_raw_data, const std::vector<int>& heatmap_dims, const float* heatmap_raw_data, const std::vector<int>& heatmap_dims,
int kernel_size, float min_confidence_to_refine, bool refine_presence, int kernel_size, float min_confidence_to_refine, bool refine_presence,
bool refine_visibility) { bool refine_visibility) {
ASSIGN_OR_RETURN(auto hm_dims, GetHwcFromDims(heatmap_dims)); MP_ASSIGN_OR_RETURN(auto hm_dims, GetHwcFromDims(heatmap_dims));
auto [hm_height, hm_width, hm_channels] = hm_dims; auto [hm_height, hm_width, hm_channels] = hm_dims;
RET_CHECK_EQ(in_lms.landmark_size(), hm_channels) RET_CHECK_EQ(in_lms.landmark_size(), hm_channels)
@@ -0,0 +1,108 @@
// Copyright 2023 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/calculators/util/set_joints_visibility_calculator.h"
#include <algorithm>
#include <optional>
#include <utility>
#include "mediapipe/calculators/util/set_joints_visibility_calculator.pb.h"
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/port/ret_check.h"
namespace mediapipe {
namespace api2 {
namespace {} // namespace
class SetJointsVisibilityCalculatorImpl
: public NodeImpl<SetJointsVisibilityCalculator> {
public:
absl::Status Open(CalculatorContext* cc) override {
options_ = cc->Options<SetJointsVisibilityCalculatorOptions>();
return absl::OkStatus();
}
absl::Status Process(CalculatorContext* cc) override {
// Skip if Joints are empty.
if (kInJoints(cc).IsEmpty()) {
return absl::OkStatus();
}
// Get joints.
const JointList& in_joints = kInJoints(cc).Get();
RET_CHECK_EQ(in_joints.joint_size(), options_.mapping_size())
<< "Number of joints doesn't match number of mappings";
// Get landmarks.
RET_CHECK(!kInLandmarks(cc).IsEmpty()) << "Landmarks must be provided";
const LandmarkList& in_landmarks = kInLandmarks(cc).Get();
// Set joints visibility.
JointList out_joints;
for (int i = 0; i < in_joints.joint_size(); ++i) {
// Initialize output joint.
Joint* out_joint = out_joints.add_joint();
*out_joint = in_joints.joint(i);
// Get visibility. But only if it exists in the source landmark(s).
std::optional<float> visibility;
auto& mapping = options_.mapping(i);
if (mapping.has_unchanged()) {
continue;
} else if (mapping.has_copy()) {
const int idx = mapping.copy().idx();
RET_CHECK(idx >= 0 && idx < in_landmarks.landmark_size())
<< "Landmark index out of range";
if (in_landmarks.landmark(idx).has_visibility()) {
visibility = in_landmarks.landmark(idx).visibility();
}
} else if (mapping.has_highest()) {
RET_CHECK_GT(mapping.highest().idx_size(), 0) << "No indexes provided";
for (int idx : mapping.highest().idx()) {
RET_CHECK(idx >= 0 && idx < in_landmarks.landmark_size())
<< "Landmark index out of range";
if (in_landmarks.landmark(idx).has_visibility()) {
const float landmark_visibility =
in_landmarks.landmark(idx).visibility();
visibility = visibility.has_value()
? std::max(visibility.value(), landmark_visibility)
: landmark_visibility;
}
}
} else {
RET_CHECK_FAIL() << "Unknown mapping";
}
// Set visibility. But only if it was possible to obtain it.
if (visibility.has_value()) {
out_joint->set_visibility(visibility.value());
}
}
kOutJoints(cc).Send(std::move(out_joints));
return absl::OkStatus();
}
private:
SetJointsVisibilityCalculatorOptions options_;
};
MEDIAPIPE_NODE_IMPLEMENTATION(SetJointsVisibilityCalculatorImpl);
} // namespace api2
} // namespace mediapipe
@@ -0,0 +1,68 @@
// Copyright 2023 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_UTIL_SET_JOINTS_VISIBILITY_CALCULATOR_H_
#define MEDIAPIPE_CALCULATORS_UTIL_SET_JOINTS_VISIBILITY_CALCULATOR_H_
#include "mediapipe/framework/api2/node.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/landmark.pb.h"
namespace mediapipe {
namespace api2 {
// A calculator set Joints visibility from Landmarks.
//
// Calculator allows to either copy visibility right from the landmark or
// somehow combine visibilities of several landmarks.
//
// Input:
// JOINTS - JointList
// Joints to to update visibility.
// LANDMARKS - LandmarkList
// Landmarks to take visibility from.
//
// Output:
// JOINTS - JointList
// Joints with updated visibility.
//
// Example:
// node {
// calculator: "SetJointsVisibilityCalculator"
// input_stream: "JOINTS:joints"
// input_stream: "LANDMARKS:landmarks"
// output_stream: "JOINTS:joints_with_visibility"
// options: {
// [mediapipe.SetJointsVisibilityCalculatorOptions.ext] {
// mapping: [
// { copy: { idx: 0 } },
// { highest: { idx: [5, 6] } }
// ]
// }
// }
// }
class SetJointsVisibilityCalculator : public NodeIntf {
public:
static constexpr Input<mediapipe::JointList> kInJoints{"JOINTS"};
static constexpr Input<mediapipe::LandmarkList> kInLandmarks{"LANDMARKS"};
static constexpr Output<mediapipe::JointList> kOutJoints{"JOINTS"};
MEDIAPIPE_NODE_INTERFACE(SetJointsVisibilityCalculator, kInJoints,
kInLandmarks, kOutJoints);
};
} // namespace api2
} // namespace mediapipe
#endif // MEDIAPIPE_CALCULATORS_UTIL_SET_JOINTS_VISIBILITY_CALCULATOR_H_
@@ -0,0 +1,55 @@
// Copyright 2023 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.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
message SetJointsVisibilityCalculatorOptions {
extend CalculatorOptions {
optional SetJointsVisibilityCalculatorOptions ext = 406440186;
}
// Mapping that tells where to take visibility for the joint.
message Mapping {
// Keep visibility unchanged.
message Unchanged {}
// Copy visibility as is from the given landmark.
message Copy {
// Index of the landmark.
optional int32 idx = 1;
}
// Take the highest visibility among the given landmarks.
message Highest {
// Indexes of landmarks to take the highest visibility value from. At
// least one index must be provided.
repeated int32 idx = 1 [packed = true];
}
oneof mapping {
Unchanged unchanged = 1;
Copy copy = 2;
Highest highest = 3;
}
}
// Mapping that tells where to take visibility for each joint.
// Number of mappings must be equal to number of provided joints. Each mapping
// must contain exactly one rule for how to set the joint visibility.
repeated Mapping mapping = 1;
}
@@ -0,0 +1,155 @@
// Copyright 2023 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 <string>
#include <utility>
#include <vector>
#include "absl/strings/substitute.h"
#include "absl/types/optional.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/calculator_runner.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/packet.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 api2 {
namespace {
using Node = ::mediapipe::CalculatorGraphConfig::Node;
struct SetJointsVisibilityTestCase {
std::string test_name;
std::string mapping;
std::vector<absl::optional<float>> in_joint_visibilities;
std::vector<absl::optional<float>> landmark_visibilities;
std::vector<absl::optional<float>> out_joint_visibilities;
};
using SetJointsVisibilityTest =
::testing::TestWithParam<SetJointsVisibilityTestCase>;
TEST_P(SetJointsVisibilityTest, SetJointsVisibilityTest) {
const SetJointsVisibilityTestCase& tc = GetParam();
// Prepare graph.
mediapipe::CalculatorRunner runner(ParseTextProtoOrDie<Node>(absl::Substitute(
R"(
calculator: "SetJointsVisibilityCalculator"
input_stream: "JOINTS:joints"
input_stream: "LANDMARKS:landmarks"
output_stream: "JOINTS:joints_with_visibility"
options: {
[mediapipe.SetJointsVisibilityCalculatorOptions.ext] {
mapping: [
$0
]
}
}
)",
tc.mapping)));
// Prepare joints.
JointList in_joints;
for (auto vis_opt : tc.in_joint_visibilities) {
Joint* joint = in_joints.add_joint();
if (vis_opt) {
joint->set_visibility(vis_opt.value());
}
}
// Prepare landmarks.
LandmarkList landmarks;
for (auto vis_opt : tc.landmark_visibilities) {
Landmark* lmk = landmarks.add_landmark();
if (vis_opt) {
lmk->set_visibility(vis_opt.value());
}
}
// Send joints and landmarks to the graph.
runner.MutableInputs()->Tag("JOINTS").packets.push_back(
MakePacket<JointList>(std::move(in_joints)).At(mediapipe::Timestamp(0)));
runner.MutableInputs()
->Tag("LANDMARKS")
.packets.push_back(MakePacket<LandmarkList>(std::move(landmarks))
.At(mediapipe::Timestamp(0)));
// Run the graph.
MP_ASSERT_OK(runner.Run());
const auto& output_packets = runner.Outputs().Tag("JOINTS").packets;
EXPECT_EQ(1, output_packets.size());
const auto& out_joints = output_packets[0].Get<JointList>();
EXPECT_EQ(out_joints.joint_size(), tc.out_joint_visibilities.size());
for (int i = 0; i < out_joints.joint_size(); ++i) {
const Joint& joint = out_joints.joint(i);
auto expected_vis_opt = tc.out_joint_visibilities[i];
if (expected_vis_opt) {
EXPECT_NEAR(joint.visibility(), expected_vis_opt.value(), 1e-5);
} else {
EXPECT_FALSE(joint.has_visibility());
}
}
}
INSTANTIATE_TEST_SUITE_P(
SetJointsVisibilityTests, SetJointsVisibilityTest,
testing::ValuesIn<SetJointsVisibilityTestCase>({
{"Empty_LandmarksAndJoints", "", {}, {}, {}},
{"Empty_Joints", "", {}, {1, 2, 3}, {}},
{"Empty_Landmarks",
"{ unchanged: {} }, { unchanged: {} }, { unchanged: {} }",
{1, 2, absl::nullopt},
{},
{1, 2, absl::nullopt}},
{"Mapping_Unchanged", "{ unchanged: {} }", {1}, {2}, {1}},
{"Mapping_Unchanged_UnsetJointVisRemainsUnset",
"{ unchanged: {} }",
{absl::nullopt},
{2},
{absl::nullopt}},
{"Mapping_Copy", "{ copy: { idx: 0 } }", {1}, {2}, {2}},
{"Mapping_Copy_UnsetLmkVisResultsIntoZeroJointVis",
"{ copy: { idx: 0 } }",
{absl::nullopt},
{absl::nullopt},
{0}},
{"Mapping_Highest",
"{ highest: { idx: [0, 1, 2] } }",
{absl::nullopt},
{2, 4, 3},
{4}},
{"Mapping_Highest_UnsetLmkIsIgnored",
"{ highest: { idx: [0, 1, 2] } }",
{absl::nullopt},
{-2, absl::nullopt, -3},
{-2}},
}),
[](const testing::TestParamInfo<SetJointsVisibilityTest::ParamType>& info) {
return info.param.test_name;
});
} // namespace
} // namespace api2
} // namespace mediapipe
@@ -73,7 +73,8 @@ absl::Status TimedBoxListIdToLabelCalculator::Open(CalculatorContext* cc) {
cc->Options<::mediapipe::TimedBoxListIdToLabelCalculatorOptions>(); cc->Options<::mediapipe::TimedBoxListIdToLabelCalculatorOptions>();
std::string string_path; std::string string_path;
ASSIGN_OR_RETURN(string_path, PathToResourceAsFile(options.label_map_path())); MP_ASSIGN_OR_RETURN(string_path,
PathToResourceAsFile(options.label_map_path()));
std::string label_map_string; std::string label_map_string;
MP_RETURN_IF_ERROR(file::GetContents(string_path, &label_map_string)); MP_RETURN_IF_ERROR(file::GetContents(string_path, &label_map_string));
@@ -86,7 +86,7 @@ absl::Status ToImageCalculator::UpdateContract(CalculatorContract* cc) {
} }
absl::Status ToImageCalculator::Process(CalculatorContext* cc) { absl::Status ToImageCalculator::Process(CalculatorContext* cc) {
ASSIGN_OR_RETURN(auto output, GetInputImage(cc)); MP_ASSIGN_OR_RETURN(auto output, GetInputImage(cc));
kOut(cc).Send(output.At(cc->InputTimestamp())); kOut(cc).Send(output.At(cc->InputTimestamp()));
return absl::OkStatus(); return absl::OkStatus();
} }
@@ -227,7 +227,7 @@ absl::Status TopKScoresCalculator::Process(CalculatorContext* cc) {
absl::Status TopKScoresCalculator::LoadLabelmap(std::string label_map_path) { absl::Status TopKScoresCalculator::LoadLabelmap(std::string label_map_path) {
std::string string_path; std::string string_path;
ASSIGN_OR_RETURN(string_path, PathToResourceAsFile(label_map_path)); MP_ASSIGN_OR_RETURN(string_path, PathToResourceAsFile(label_map_path));
std::string label_map_string; std::string label_map_string;
MP_RETURN_IF_ERROR(file::GetContents(string_path, &label_map_string)); MP_RETURN_IF_ERROR(file::GetContents(string_path, &label_map_string));
@@ -207,7 +207,7 @@ absl::Status BoxDetectorCalculator::Open(CalculatorContext* cc) {
for (const auto& filename : options_.index_proto_filename()) { for (const auto& filename : options_.index_proto_filename()) {
std::string string_path; std::string string_path;
ASSIGN_OR_RETURN(string_path, PathToResourceAsFile(filename)); MP_ASSIGN_OR_RETURN(string_path, PathToResourceAsFile(filename));
std::string index_string; std::string index_string;
MP_RETURN_IF_ERROR(file::GetContents(string_path, &index_string)); MP_RETURN_IF_ERROR(file::GetContents(string_path, &index_string));
BoxDetectorIndex predefined_index; BoxDetectorIndex predefined_index;
@@ -478,7 +478,7 @@ absl::Status MotionAnalysisCalculator::Process(CalculatorContext* cc) {
// Fill in timestamps we process. // Fill in timestamps we process.
if (!selection_stream->Value().IsEmpty()) { if (!selection_stream->Value().IsEmpty()) {
ASSIGN_OR_RETURN( MP_ASSIGN_OR_RETURN(
frame_selection_result, frame_selection_result,
selection_stream->Value().ConsumeOrCopy<FrameSelectionResult>()); selection_stream->Value().ConsumeOrCopy<FrameSelectionResult>());
use_frame = true; use_frame = true;
@@ -87,8 +87,8 @@ absl::Status RunMPPGraph() {
} }
ABSL_LOG(INFO) << "Start running the calculator graph."; ABSL_LOG(INFO) << "Start running the calculator graph.";
ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller poller, MP_ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller poller,
graph.AddOutputStreamPoller(kOutputStream)); graph.AddOutputStreamPoller(kOutputStream));
MP_RETURN_IF_ERROR(graph.StartRun({})); MP_RETURN_IF_ERROR(graph.StartRun({}));
ABSL_LOG(INFO) << "Start grabbing and processing frames."; ABSL_LOG(INFO) << "Start grabbing and processing frames.";
@@ -79,8 +79,8 @@ absl::Status RunMPPGraph() {
} }
ABSL_LOG(INFO) << "Start running the calculator graph."; ABSL_LOG(INFO) << "Start running the calculator graph.";
ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller poller, MP_ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller poller,
graph.AddOutputStreamPoller(kOutputStream)); graph.AddOutputStreamPoller(kOutputStream));
MP_RETURN_IF_ERROR(graph.StartRun({})); MP_RETURN_IF_ERROR(graph.StartRun({}));
ABSL_LOG(INFO) << "Start grabbing and processing frames."; ABSL_LOG(INFO) << "Start grabbing and processing frames.";
@@ -62,7 +62,7 @@ absl::Status RunMPPGraph() {
MP_RETURN_IF_ERROR(graph.Initialize(config)); MP_RETURN_IF_ERROR(graph.Initialize(config));
ABSL_LOG(INFO) << "Initialize the GPU."; ABSL_LOG(INFO) << "Initialize the GPU.";
ASSIGN_OR_RETURN(auto gpu_resources, mediapipe::GpuResources::Create()); MP_ASSIGN_OR_RETURN(auto gpu_resources, mediapipe::GpuResources::Create());
MP_RETURN_IF_ERROR(graph.SetGpuResources(std::move(gpu_resources))); MP_RETURN_IF_ERROR(graph.SetGpuResources(std::move(gpu_resources)));
mediapipe::GlCalculatorHelper gpu_helper; mediapipe::GlCalculatorHelper gpu_helper;
gpu_helper.InitializeForTest(graph.GetGpuResources().get()); gpu_helper.InitializeForTest(graph.GetGpuResources().get());
@@ -89,8 +89,8 @@ absl::Status RunMPPGraph() {
} }
ABSL_LOG(INFO) << "Start running the calculator graph."; ABSL_LOG(INFO) << "Start running the calculator graph.";
ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller poller, MP_ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller poller,
graph.AddOutputStreamPoller(kOutputStream)); graph.AddOutputStreamPoller(kOutputStream));
MP_RETURN_IF_ERROR(graph.StartRun({})); MP_RETURN_IF_ERROR(graph.StartRun({}));
ABSL_LOG(INFO) << "Start grabbing and processing frames."; ABSL_LOG(INFO) << "Start grabbing and processing frames.";
@@ -42,8 +42,8 @@ absl::Status PrintHelloWorld() {
CalculatorGraph graph; CalculatorGraph graph;
MP_RETURN_IF_ERROR(graph.Initialize(config)); MP_RETURN_IF_ERROR(graph.Initialize(config));
ASSIGN_OR_RETURN(OutputStreamPoller poller, MP_ASSIGN_OR_RETURN(OutputStreamPoller poller,
graph.AddOutputStreamPoller("out")); graph.AddOutputStreamPoller("out"));
MP_RETURN_IF_ERROR(graph.StartRun({})); MP_RETURN_IF_ERROR(graph.StartRun({}));
// Give 10 input packets that contains the same string "Hello World!". // Give 10 input packets that contains the same string "Hello World!".
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
@@ -57,16 +57,16 @@ absl::StatusOr<std::string> ReadFileToString(const std::string& file_path) {
absl::Status ProcessImage(std::unique_ptr<mediapipe::CalculatorGraph> graph) { absl::Status ProcessImage(std::unique_ptr<mediapipe::CalculatorGraph> graph) {
ABSL_LOG(INFO) << "Load the image."; ABSL_LOG(INFO) << "Load the image.";
ASSIGN_OR_RETURN(const std::string raw_image, MP_ASSIGN_OR_RETURN(const std::string raw_image,
ReadFileToString(absl::GetFlag(FLAGS_input_image_path))); ReadFileToString(absl::GetFlag(FLAGS_input_image_path)));
ABSL_LOG(INFO) << "Start running the calculator graph."; ABSL_LOG(INFO) << "Start running the calculator graph.";
ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller output_image_poller, MP_ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller output_image_poller,
graph->AddOutputStreamPoller(kOutputImageStream)); graph->AddOutputStreamPoller(kOutputImageStream));
ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller left_iris_depth_poller, MP_ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller left_iris_depth_poller,
graph->AddOutputStreamPoller(kLeftIrisDepthMmStream)); graph->AddOutputStreamPoller(kLeftIrisDepthMmStream));
ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller right_iris_depth_poller, MP_ASSIGN_OR_RETURN(mediapipe::OutputStreamPoller right_iris_depth_poller,
graph->AddOutputStreamPoller(kRightIrisDepthMmStream)); graph->AddOutputStreamPoller(kRightIrisDepthMmStream));
MP_RETURN_IF_ERROR(graph->StartRun({})); MP_RETURN_IF_ERROR(graph->StartRun({}));
// Send image packet into the graph. // Send image packet into the graph.
@@ -83,8 +83,8 @@ absl::Status OutputSidePacketsToLocalFile(mediapipe::CalculatorGraph& graph) {
std::vector<std::string> side_packet_names = std::vector<std::string> side_packet_names =
absl::StrSplit(absl::GetFlag(FLAGS_output_side_packets), ','); absl::StrSplit(absl::GetFlag(FLAGS_output_side_packets), ',');
for (const std::string& side_packet_name : side_packet_names) { for (const std::string& side_packet_name : side_packet_names) {
ASSIGN_OR_RETURN(auto status_or_packet, MP_ASSIGN_OR_RETURN(auto status_or_packet,
graph.GetOutputSidePacket(side_packet_name)); graph.GetOutputSidePacket(side_packet_name));
file << absl::StrCat(side_packet_name, ":", file << absl::StrCat(side_packet_name, ":",
status_or_packet.Get<std::string>(), "\n"); status_or_packet.Get<std::string>(), "\n");
} }
@@ -125,8 +125,8 @@ absl::Status RunMPPGraph() {
MP_RETURN_IF_ERROR(graph.Initialize(config, input_side_packets)); MP_RETURN_IF_ERROR(graph.Initialize(config, input_side_packets));
if (!absl::GetFlag(FLAGS_output_stream).empty() && if (!absl::GetFlag(FLAGS_output_stream).empty() &&
!absl::GetFlag(FLAGS_output_stream_file).empty()) { !absl::GetFlag(FLAGS_output_stream_file).empty()) {
ASSIGN_OR_RETURN(auto poller, graph.AddOutputStreamPoller( MP_ASSIGN_OR_RETURN(auto poller, graph.AddOutputStreamPoller(
absl::GetFlag(FLAGS_output_stream))); absl::GetFlag(FLAGS_output_stream)));
ABSL_LOG(INFO) << "Start running the calculator graph."; ABSL_LOG(INFO) << "Start running the calculator graph.";
MP_RETURN_IF_ERROR(graph.StartRun({})); MP_RETURN_IF_ERROR(graph.StartRun({}));
MP_RETURN_IF_ERROR(OutputStreamToLocalFile(poller)); MP_RETURN_IF_ERROR(OutputStreamToLocalFile(poller));
+5 -1
View File
@@ -191,7 +191,10 @@ cc_library(
name = "calculator_context", name = "calculator_context",
srcs = ["calculator_context.cc"], srcs = ["calculator_context.cc"],
hdrs = ["calculator_context.h"], hdrs = ["calculator_context.h"],
visibility = [":mediapipe_internal"], visibility = [
":mediapipe_internal",
"//speech/videos/mediapipe/calculator:__pkg__",
],
deps = [ deps = [
":calculator_state", ":calculator_state",
":counter", ":counter",
@@ -233,6 +236,7 @@ cc_library(
hdrs = ["calculator_contract.h"], hdrs = ["calculator_contract.h"],
visibility = [ visibility = [
":mediapipe_internal", ":mediapipe_internal",
"//speech/videos/mediapipe/calculator:__pkg__",
], ],
deps = [ deps = [
":calculator_cc_proto", ":calculator_cc_proto",
+3 -3
View File
@@ -530,9 +530,9 @@ struct ConsumerNode : public Node {
MEDIAPIPE_NODE_CONTRACT(kInt, kGeneric, kOneOf); MEDIAPIPE_NODE_CONTRACT(kInt, kGeneric, kOneOf);
absl::Status Process(CalculatorContext* cc) override { absl::Status Process(CalculatorContext* cc) override {
ASSIGN_OR_RETURN(auto maybe_int, kInt(cc).Consume()); MP_ASSIGN_OR_RETURN(auto maybe_int, kInt(cc).Consume());
ASSIGN_OR_RETURN(auto maybe_float, kGeneric(cc).Consume<float>()); MP_ASSIGN_OR_RETURN(auto maybe_float, kGeneric(cc).Consume<float>());
ASSIGN_OR_RETURN(auto maybe_int2, kOneOf(cc).Consume<int>()); MP_ASSIGN_OR_RETURN(auto maybe_int2, kOneOf(cc).Consume<int>());
return {}; return {};
} }
}; };
+304
View File
@@ -2,6 +2,99 @@ package(default_visibility = ["//visibility:public"])
licenses(["notice"]) licenses(["notice"])
cc_library(
name = "concatenate",
hdrs = ["concatenate.h"],
deps = [
"//mediapipe/calculators/core:concatenate_proto_list_calculator",
"//mediapipe/calculators/core:concatenate_vector_calculator",
"//mediapipe/calculators/core:concatenate_vector_calculator_cc_proto",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/api2:port",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:tensor",
],
)
cc_test(
name = "concatenate_test",
srcs = ["concatenate_test.cc"],
deps = [
":concatenate",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:tensor",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
],
)
cc_library(
name = "detections_to_rects",
srcs = ["detections_to_rects.cc"],
hdrs = ["detections_to_rects.h"],
deps = [
"//mediapipe/calculators/util:alignment_points_to_rects_calculator",
"//mediapipe/calculators/util:detections_to_rects_calculator",
"//mediapipe/calculators/util:detections_to_rects_calculator_cc_proto",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:detection_cc_proto",
"//mediapipe/framework/formats:rect_cc_proto",
],
)
cc_test(
name = "detections_to_rects_test",
srcs = ["detections_to_rects_test.cc"],
deps = [
":detections_to_rects",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:detection_cc_proto",
"//mediapipe/framework/formats:rect_cc_proto",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
],
)
cc_library(
name = "get_vector_item",
hdrs = ["get_vector_item.h"],
deps = [
"//mediapipe/calculators/core:get_vector_item_calculator",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/api2:port",
"//mediapipe/framework/formats:classification_cc_proto",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:rect_cc_proto",
"@org_tensorflow//tensorflow/lite/c:common",
],
)
cc_test(
name = "get_vector_item_test",
srcs = ["get_vector_item_test.cc"],
deps = [
":get_vector_item",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:classification_cc_proto",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:rect_cc_proto",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
],
)
cc_library( cc_library(
name = "landmarks_to_detection", name = "landmarks_to_detection",
srcs = ["landmarks_to_detection.cc"], srcs = ["landmarks_to_detection.cc"],
@@ -30,6 +123,38 @@ cc_test(
], ],
) )
cc_library(
name = "landmarks_to_tensor",
srcs = ["landmarks_to_tensor.cc"],
hdrs = ["landmarks_to_tensor.h"],
deps = [
"//mediapipe/calculators/tensor:landmarks_to_tensor_calculator",
"//mediapipe/calculators/tensor:landmarks_to_tensor_calculator_cc_proto",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/api2:port",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:tensor",
"@com_google_absl//absl/types:span",
],
)
cc_test(
name = "landmarks_to_tensor_test",
srcs = ["landmarks_to_tensor_test.cc"],
deps = [
":landmarks_to_tensor",
"//mediapipe/calculators/tensor:landmarks_to_tensor_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:tensor",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
],
)
cc_library( cc_library(
name = "landmarks_projection", name = "landmarks_projection",
srcs = ["landmarks_projection.cc"], srcs = ["landmarks_projection.cc"],
@@ -112,6 +237,28 @@ cc_test(
], ],
) )
cc_library(
name = "presence",
hdrs = ["presence.h"],
deps = [
"//mediapipe/calculators/core:packet_presence_calculator",
"//mediapipe/framework/api2:builder",
],
)
cc_test(
name = "presence_test",
srcs = ["presence_test.cc"],
deps = [
":presence",
"//mediapipe/framework:calculator_cc_proto",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
],
)
cc_library( cc_library(
name = "rect_transformation", name = "rect_transformation",
srcs = ["rect_transformation.cc"], srcs = ["rect_transformation.cc"],
@@ -137,3 +284,160 @@ cc_test(
"//mediapipe/framework/port:parse_text_proto", "//mediapipe/framework/port:parse_text_proto",
], ],
) )
cc_library(
name = "threshold",
srcs = ["threshold.cc"],
hdrs = ["threshold.h"],
deps = [
"//mediapipe/calculators/util:thresholding_calculator",
"//mediapipe/calculators/util:thresholding_calculator_cc_proto",
"//mediapipe/framework/api2:builder",
],
)
cc_test(
name = "threshold_test",
srcs = ["threshold_test.cc"],
deps = [
":threshold",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
],
)
cc_library(
name = "smoothing",
srcs = ["smoothing.cc"],
hdrs = ["smoothing.h"],
deps = [
"//mediapipe/calculators/util:landmarks_smoothing_calculator",
"//mediapipe/calculators/util:landmarks_smoothing_calculator_cc_proto",
"//mediapipe/calculators/util:multi_landmarks_smoothing_calculator",
"//mediapipe/calculators/util:multi_world_landmarks_smoothing_calculator",
"//mediapipe/calculators/util:visibility_smoothing_calculator",
"//mediapipe/calculators/util:visibility_smoothing_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:rect_cc_proto",
"@com_google_absl//absl/types:optional",
],
)
cc_test(
name = "smoothing_test",
srcs = ["smoothing_test.cc"],
deps = [
":smoothing",
"//mediapipe/calculators/util:landmarks_smoothing_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:rect_cc_proto",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
"@com_google_absl//absl/types:optional",
],
)
cc_library(
name = "segmentation_smoothing",
srcs = ["segmentation_smoothing.cc"],
hdrs = ["segmentation_smoothing.h"],
deps = [
"//mediapipe/calculators/image:segmentation_smoothing_calculator",
"//mediapipe/calculators/image:segmentation_smoothing_calculator_cc_proto",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:image",
],
)
cc_test(
name = "segmentation_smoothing_test",
srcs = ["segmentation_smoothing_test.cc"],
deps = [
":segmentation_smoothing",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:image",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
],
)
cc_library(
name = "split",
hdrs = ["split.h"],
deps = [
"//mediapipe/calculators/core:split_proto_list_calculator",
"//mediapipe/calculators/core:split_vector_calculator",
"//mediapipe/calculators/core:split_vector_calculator_cc_proto",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/api2:port",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:classification_cc_proto",
"//mediapipe/framework/formats:detection_cc_proto",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:matrix",
"//mediapipe/framework/formats:rect_cc_proto",
"//mediapipe/framework/formats:tensor",
"@org_tensorflow//tensorflow/lite/c:common",
],
)
cc_test(
name = "split_test",
srcs = ["split_test.cc"],
deps = [
":split",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/formats:rect_cc_proto",
"//mediapipe/framework/formats:tensor",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
],
)
cc_library(
name = "tensor_to_joints",
srcs = ["tensor_to_joints.cc"],
hdrs = ["tensor_to_joints.h"],
deps = [
"//mediapipe/calculators/tensor:tensor_to_joints_calculator",
"//mediapipe/calculators/tensor:tensor_to_joints_calculator_cc_proto",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/api2:port",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:tensor",
],
)
cc_test(
name = "tensor_to_joints_test",
srcs = ["tensor_to_joints_test.cc"],
deps = [
":tensor_to_joints",
"//mediapipe/calculators/tensor:tensor_to_joints_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/api2:builder",
"//mediapipe/framework/formats:body_rig_cc_proto",
"//mediapipe/framework/formats:tensor",
"//mediapipe/framework/port:gtest",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status_matchers",
],
)
@@ -0,0 +1,69 @@
#ifndef MEDIAPIPE_FRAMEWORK_API2_STREAM_CONCATENATE_H_
#define MEDIAPIPE_FRAMEWORK_API2_STREAM_CONCATENATE_H_
#include <vector>
#include "mediapipe/calculators/core/concatenate_vector_calculator.pb.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/api2/port.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/formats/tensor.h"
namespace mediapipe::api2::builder {
namespace internal_stream_concatenate {
// Helper function that adds a node to a graph, that is capable of concatenating
// a specific type (T).
template <class T>
GenericNode& AddConcatenateVectorNode(Graph& graph) {
if constexpr (std::is_same_v<T, mediapipe::LandmarkList>) {
return graph.AddNode("ConcatenateLandmarkListCalculator");
} else if constexpr (std::is_same_v<T, mediapipe::JointList>) {
return graph.AddNode("ConcatenateJointListCalculator");
} else if constexpr (std::is_same_v<T, std::vector<Tensor>>) {
return graph.AddNode("ConcatenateTensorVectorCalculator");
} else {
static_assert(dependent_false<T>::value,
"Concatenate node is not available for the specified type.");
}
}
template <typename StreamsT,
typename PayloadT = typename StreamsT::value_type::PayloadT>
Stream<PayloadT> Concatenate(StreamsT& streams,
const bool only_emit_if_all_present,
Graph& graph) {
auto& concatenator = AddConcatenateVectorNode<PayloadT>(graph);
for (int i = 0; i < streams.size(); ++i) {
streams[i].ConnectTo(concatenator.In("")[i]);
}
auto& concatenator_opts =
concatenator
.template GetOptions<mediapipe::ConcatenateVectorCalculatorOptions>();
concatenator_opts.set_only_emit_if_all_present(only_emit_if_all_present);
return concatenator.Out("").template Cast<PayloadT>();
}
} // namespace internal_stream_concatenate
template <typename StreamsT,
typename PayloadT = typename StreamsT::value_type::PayloadT>
Stream<PayloadT> Concatenate(StreamsT& streams, Graph& graph) {
return internal_stream_concatenate::Concatenate(
streams, /*only_emit_if_all_present=*/false, graph);
}
template <typename StreamsT,
typename PayloadT = typename StreamsT::value_type::PayloadT>
Stream<PayloadT> ConcatenateIfAllPresent(StreamsT& streams, Graph& graph) {
return internal_stream_concatenate::Concatenate(
streams, /*only_emit_if_all_present=*/true, graph);
}
} // namespace mediapipe::api2::builder
#endif // MEDIAPIPE_FRAMEWORK_API2_STREAM_CONCATENATE_H_
@@ -0,0 +1,188 @@
#include "mediapipe/framework/api2/stream/concatenate.h"
#include <vector>
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/body_rig.pb.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/formats/tensor.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::api2::builder {
namespace {
TEST(Concatenate, ConcatenateLandmarkList) {
Graph graph;
std::vector<Stream<LandmarkList>> items = {
graph.In("LMK_LIST")[0].Cast<LandmarkList>(),
graph.In("LMK_LIST")[1].Cast<LandmarkList>()};
Stream<LandmarkList> landmark_list = Concatenate(items, graph);
landmark_list.SetName("landmark_list");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "ConcatenateLandmarkListCalculator"
input_stream: "__stream_0"
input_stream: "__stream_1"
output_stream: "landmark_list"
options {
[mediapipe.ConcatenateVectorCalculatorOptions.ext] {
only_emit_if_all_present: false
}
}
}
input_stream: "LMK_LIST:0:__stream_0"
input_stream: "LMK_LIST:1:__stream_1"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(Concatenate, ConcatenateLandmarkList_IfAllPresent) {
Graph graph;
std::vector<Stream<LandmarkList>> items = {
graph.In("LMK_LIST")[0].Cast<LandmarkList>(),
graph.In("LMK_LIST")[1].Cast<LandmarkList>()};
Stream<LandmarkList> landmark_list = ConcatenateIfAllPresent(items, graph);
landmark_list.SetName("landmark_list");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "ConcatenateLandmarkListCalculator"
input_stream: "__stream_0"
input_stream: "__stream_1"
output_stream: "landmark_list"
options {
[mediapipe.ConcatenateVectorCalculatorOptions.ext] {
only_emit_if_all_present: true
}
}
}
input_stream: "LMK_LIST:0:__stream_0"
input_stream: "LMK_LIST:1:__stream_1"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(Concatenate, ConcatenateJointList) {
Graph graph;
std::vector<Stream<JointList>> items = {
graph.In("JT_LIST")[0].Cast<JointList>(),
graph.In("JT_LIST")[1].Cast<JointList>()};
Stream<JointList> joint_list = Concatenate(items, graph);
joint_list.SetName("joint_list");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "ConcatenateJointListCalculator"
input_stream: "__stream_0"
input_stream: "__stream_1"
output_stream: "joint_list"
options {
[mediapipe.ConcatenateVectorCalculatorOptions.ext] {
only_emit_if_all_present: false
}
}
}
input_stream: "JT_LIST:0:__stream_0"
input_stream: "JT_LIST:1:__stream_1"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(Concatenate, ConcatenateJointList_IfAllPresent) {
Graph graph;
std::vector<Stream<JointList>> items = {
graph.In("JT_LIST")[0].Cast<JointList>(),
graph.In("JT_LIST")[1].Cast<JointList>()};
Stream<JointList> joint_list = ConcatenateIfAllPresent(items, graph);
joint_list.SetName("joint_list");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "ConcatenateJointListCalculator"
input_stream: "__stream_0"
input_stream: "__stream_1"
output_stream: "joint_list"
options {
[mediapipe.ConcatenateVectorCalculatorOptions.ext] {
only_emit_if_all_present: true
}
}
}
input_stream: "JT_LIST:0:__stream_0"
input_stream: "JT_LIST:1:__stream_1"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(Concatenate, ConcatenateTensorVectorList) {
Graph graph;
std::vector<Stream<std::vector<Tensor>>> items = {
graph.In("VT_LIST")[0].Cast<std::vector<Tensor>>(),
graph.In("VT_LIST")[1].Cast<std::vector<Tensor>>()};
Stream<std::vector<Tensor>> tensors = Concatenate(items, graph);
tensors.SetName("joint_list");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "ConcatenateTensorVectorCalculator"
input_stream: "__stream_0"
input_stream: "__stream_1"
output_stream: "joint_list"
options {
[mediapipe.ConcatenateVectorCalculatorOptions.ext] {
only_emit_if_all_present: false
}
}
}
input_stream: "VT_LIST:0:__stream_0"
input_stream: "VT_LIST:1:__stream_1"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(Concatenate, ConcatenateTensorVectorList_IfAllPresent) {
Graph graph;
std::vector<Stream<std::vector<Tensor>>> items = {
graph.In("VT_LIST")[0].Cast<std::vector<Tensor>>(),
graph.In("VT_LIST")[1].Cast<std::vector<Tensor>>()};
Stream<std::vector<Tensor>> tensors = ConcatenateIfAllPresent(items, graph);
tensors.SetName("joint_list");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "ConcatenateTensorVectorCalculator"
input_stream: "__stream_0"
input_stream: "__stream_1"
output_stream: "joint_list"
options {
[mediapipe.ConcatenateVectorCalculatorOptions.ext] {
only_emit_if_all_present: true
}
}
}
input_stream: "VT_LIST:0:__stream_0"
input_stream: "VT_LIST:1:__stream_1"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
} // namespace
} // namespace mediapipe::api2::builder
@@ -0,0 +1,100 @@
#include "mediapipe/framework/api2/stream/detections_to_rects.h"
#include <utility>
#include <vector>
#include "mediapipe/calculators/util/detections_to_rects_calculator.pb.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/formats/detection.pb.h"
#include "mediapipe/framework/formats/rect.pb.h"
namespace mediapipe::api2::builder {
namespace {
using ::mediapipe::NormalizedRect;
using ::mediapipe::api2::builder::Graph;
void AddOptions(int start_keypoint_index, int end_keypoint_index,
float target_angle,
mediapipe::api2::builder::GenericNode& node) {
auto& options = node.GetOptions<DetectionsToRectsCalculatorOptions>();
options.set_rotation_vector_start_keypoint_index(start_keypoint_index);
options.set_rotation_vector_end_keypoint_index(end_keypoint_index);
options.set_rotation_vector_target_angle_degrees(target_angle);
}
} // namespace
Stream<NormalizedRect> ConvertAlignmentPointsDetectionToRect(
Stream<Detection> detection, Stream<std::pair<int, int>> image_size,
int start_keypoint_index, int end_keypoint_index, float target_angle,
Graph& graph) {
auto& align_node = graph.AddNode("AlignmentPointsRectsCalculator");
AddOptions(start_keypoint_index, end_keypoint_index, target_angle,
align_node);
detection.ConnectTo(align_node.In("DETECTION"));
image_size.ConnectTo(align_node.In("IMAGE_SIZE"));
return align_node.Out("NORM_RECT").Cast<NormalizedRect>();
}
Stream<NormalizedRect> ConvertAlignmentPointsDetectionsToRect(
Stream<std::vector<Detection>> detections,
Stream<std::pair<int, int>> image_size, int start_keypoint_index,
int end_keypoint_index, float target_angle, Graph& graph) {
auto& align_node = graph.AddNode("AlignmentPointsRectsCalculator");
AddOptions(start_keypoint_index, end_keypoint_index, target_angle,
align_node);
detections.ConnectTo(align_node.In("DETECTIONS"));
image_size.ConnectTo(align_node.In("IMAGE_SIZE"));
return align_node.Out("NORM_RECT").Cast<NormalizedRect>();
}
Stream<NormalizedRect> ConvertDetectionToRect(
Stream<Detection> detection, Stream<std::pair<int, int>> image_size,
int start_keypoint_index, int end_keypoint_index, float target_angle,
mediapipe::api2::builder::Graph& graph) {
auto& align_node = graph.AddNode("DetectionsToRectsCalculator");
AddOptions(start_keypoint_index, end_keypoint_index, target_angle,
align_node);
detection.ConnectTo(align_node.In("DETECTION"));
image_size.ConnectTo(align_node.In("IMAGE_SIZE"));
return align_node.Out("NORM_RECT").Cast<NormalizedRect>();
}
Stream<std::vector<NormalizedRect>> ConvertDetectionsToRects(
Stream<std::vector<Detection>> detections,
Stream<std::pair<int, int>> image_size, int start_keypoint_index,
int end_keypoint_index, float target_angle,
mediapipe::api2::builder::Graph& graph) {
// TODO: check if we can substitute DetectionsToRectsCalculator
// with AlignmentPointsRectsCalculator and use it instead. Ideally, merge or
// remove one of calculators.
auto& align_node = graph.AddNode("DetectionsToRectsCalculator");
AddOptions(start_keypoint_index, end_keypoint_index, target_angle,
align_node);
detections.ConnectTo(align_node.In("DETECTIONS"));
image_size.ConnectTo(align_node.In("IMAGE_SIZE"));
return align_node.Out("NORM_RECTS").Cast<std::vector<NormalizedRect>>();
}
Stream<NormalizedRect> ConvertDetectionsToRectUsingKeypoints(
Stream<std::vector<Detection>> detections,
Stream<std::pair<int, int>> image_size, int start_keypoint_index,
int end_keypoint_index, float target_angle,
mediapipe::api2::builder::Graph& graph) {
auto& node = graph.AddNode("DetectionsToRectsCalculator");
auto& options = node.GetOptions<DetectionsToRectsCalculatorOptions>();
options.set_rotation_vector_start_keypoint_index(start_keypoint_index);
options.set_rotation_vector_end_keypoint_index(end_keypoint_index);
options.set_rotation_vector_target_angle_degrees(target_angle);
options.set_conversion_mode(
DetectionsToRectsCalculatorOptions::USE_KEYPOINTS);
detections.ConnectTo(node.In("DETECTIONS"));
image_size.ConnectTo(node.In("IMAGE_SIZE"));
return node.Out("NORM_RECT").Cast<NormalizedRect>();
}
} // namespace mediapipe::api2::builder
@@ -0,0 +1,55 @@
#ifndef MEDIAPIPE_FRAMEWORK_API2_STREAM_DETECTIONS_TO_RECTS_H_
#define MEDIAPIPE_FRAMEWORK_API2_STREAM_DETECTIONS_TO_RECTS_H_
#include <utility>
#include <vector>
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/formats/detection.pb.h"
#include "mediapipe/framework/formats/rect.pb.h"
namespace mediapipe::api2::builder {
// Updates @graph to convert @detection into a `NormalizedRect` according to
// passed parameters.
Stream<mediapipe::NormalizedRect> ConvertAlignmentPointsDetectionToRect(
Stream<mediapipe::Detection> detection,
Stream<std::pair<int, int>> image_size, int start_keypoint_index,
int end_keypoint_index, float target_angle,
mediapipe::api2::builder::Graph& graph);
// Updates @graph to convert first detection from @detections into a
// `NormalizedRect` according to passed parameters.
Stream<mediapipe::NormalizedRect> ConvertAlignmentPointsDetectionsToRect(
Stream<std::vector<mediapipe::Detection>> detections,
Stream<std::pair<int, int>> image_size, int start_keypoint_index,
int end_keypoint_index, float target_angle,
mediapipe::api2::builder::Graph& graph);
// Updates @graph to convert @detection into a `NormalizedRect` according to
// passed parameters.
Stream<mediapipe::NormalizedRect> ConvertDetectionToRect(
Stream<mediapipe::Detection> detections,
Stream<std::pair<int, int>> image_size, int start_keypoint_index,
int end_keypoint_index, float target_angle,
mediapipe::api2::builder::Graph& graph);
// Updates @graph to convert @detections into a stream holding vector of
// `NormalizedRect` according to passed parameters.
Stream<std::vector<mediapipe::NormalizedRect>> ConvertDetectionsToRects(
Stream<std::vector<mediapipe::Detection>> detections,
Stream<std::pair<int, int>> image_size, int start_keypoint_index,
int end_keypoint_index, float target_angle,
mediapipe::api2::builder::Graph& graph);
// Updates @graph to convert @detections into a stream holding vector of
// `NormalizedRect` according to passed parameters and using keypoints.
Stream<mediapipe::NormalizedRect> ConvertDetectionsToRectUsingKeypoints(
Stream<std::vector<mediapipe::Detection>> detections,
Stream<std::pair<int, int>> image_size, int start_keypoint_index,
int end_keypoint_index, float target_angle,
mediapipe::api2::builder::Graph& graph);
} // namespace mediapipe::api2::builder
#endif // MEDIAPIPE_FRAMEWORK_API2_STREAM_DETECTIONS_TO_RECTS_H_
@@ -0,0 +1,208 @@
#include "mediapipe/framework/api2/stream/detections_to_rects.h"
#include <utility>
#include <vector>
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/detection.pb.h"
#include "mediapipe/framework/formats/rect.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"
namespace mediapipe::api2::builder {
namespace {
TEST(DetectionsToRects, ConvertAlignmentPointsDetectionToRect) {
mediapipe::api2::builder::Graph graph;
Stream<Detection> detection = graph.In("DETECTION").Cast<Detection>();
detection.SetName("detection");
Stream<std::pair<int, int>> size =
graph.In("SIZE").Cast<std::pair<int, int>>();
size.SetName("size");
Stream<NormalizedRect> rect = ConvertAlignmentPointsDetectionToRect(
detection, size, /*start_keypoint_index=*/0, /*end_keypoint_index=*/100,
/*target_angle=*/200, graph);
rect.SetName("rect");
EXPECT_THAT(
graph.GetConfig(),
EqualsProto(mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "AlignmentPointsRectsCalculator"
input_stream: "DETECTION:detection"
input_stream: "IMAGE_SIZE:size"
output_stream: "NORM_RECT:rect"
options {
[mediapipe.DetectionsToRectsCalculatorOptions.ext] {
rotation_vector_start_keypoint_index: 0
rotation_vector_end_keypoint_index: 100
rotation_vector_target_angle_degrees: 200
}
}
}
input_stream: "DETECTION:detection"
input_stream: "SIZE:size"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(DetectionsToRects, ConvertAlignmentPointsDetectionsToRect) {
mediapipe::api2::builder::Graph graph;
Stream<std::vector<Detection>> detections =
graph.In("DETECTIONS").Cast<std::vector<Detection>>();
detections.SetName("detections");
Stream<std::pair<int, int>> size =
graph.In("SIZE").Cast<std::pair<int, int>>();
size.SetName("size");
Stream<NormalizedRect> rect = ConvertAlignmentPointsDetectionsToRect(
detections, size, /*start_keypoint_index=*/0, /*end_keypoint_index=*/100,
/*target_angle=*/200, graph);
rect.SetName("rect");
EXPECT_THAT(
graph.GetConfig(),
EqualsProto(mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "AlignmentPointsRectsCalculator"
input_stream: "DETECTIONS:detections"
input_stream: "IMAGE_SIZE:size"
output_stream: "NORM_RECT:rect"
options {
[mediapipe.DetectionsToRectsCalculatorOptions.ext] {
rotation_vector_start_keypoint_index: 0
rotation_vector_end_keypoint_index: 100
rotation_vector_target_angle_degrees: 200
}
}
}
input_stream: "DETECTIONS:detections"
input_stream: "SIZE:size"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(DetectionsToRects, ConvertDetectionToRect) {
mediapipe::api2::builder::Graph graph;
Stream<Detection> detection = graph.In("DETECTION").Cast<Detection>();
detection.SetName("detection");
Stream<std::pair<int, int>> size =
graph.In("SIZE").Cast<std::pair<int, int>>();
size.SetName("size");
Stream<NormalizedRect> rect = ConvertDetectionToRect(
detection, size, /*start_keypoint_index=*/0, /*end_keypoint_index=*/100,
/*target_angle=*/200, graph);
rect.SetName("rect");
EXPECT_THAT(
graph.GetConfig(),
EqualsProto(mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "DetectionsToRectsCalculator"
input_stream: "DETECTION:detection"
input_stream: "IMAGE_SIZE:size"
output_stream: "NORM_RECT:rect"
options {
[mediapipe.DetectionsToRectsCalculatorOptions.ext] {
rotation_vector_start_keypoint_index: 0
rotation_vector_end_keypoint_index: 100
rotation_vector_target_angle_degrees: 200
}
}
}
input_stream: "DETECTION:detection"
input_stream: "SIZE:size"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(DetectionsToRects, ConvertDetectionsToRects) {
mediapipe::api2::builder::Graph graph;
Stream<std::vector<Detection>> detections =
graph.In("DETECTIONS").Cast<std::vector<Detection>>();
detections.SetName("detections");
Stream<std::pair<int, int>> size =
graph.In("SIZE").Cast<std::pair<int, int>>();
size.SetName("size");
Stream<std::vector<NormalizedRect>> rects = ConvertDetectionsToRects(
detections, size, /*start_keypoint_index=*/0, /*end_keypoint_index=*/100,
/*target_angle=*/200, graph);
rects.SetName("rects");
EXPECT_THAT(
graph.GetConfig(),
EqualsProto(mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "DetectionsToRectsCalculator"
input_stream: "DETECTIONS:detections"
input_stream: "IMAGE_SIZE:size"
output_stream: "NORM_RECTS:rects"
options {
[mediapipe.DetectionsToRectsCalculatorOptions.ext] {
rotation_vector_start_keypoint_index: 0
rotation_vector_end_keypoint_index: 100
rotation_vector_target_angle_degrees: 200
}
}
}
input_stream: "DETECTIONS:detections"
input_stream: "SIZE:size"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(DetectionsToRects, ConvertDetectionsToRectUsingKeypoints) {
mediapipe::api2::builder::Graph graph;
Stream<std::vector<Detection>> detections =
graph.In("DETECTIONS").Cast<std::vector<Detection>>();
detections.SetName("detections");
Stream<std::pair<int, int>> size =
graph.In("SIZE").Cast<std::pair<int, int>>();
size.SetName("size");
Stream<NormalizedRect> rect = ConvertDetectionsToRectUsingKeypoints(
detections, size, /*start_keypoint_index=*/0, /*end_keypoint_index=*/100,
/*target_angle=*/200, graph);
rect.SetName("rect");
EXPECT_THAT(
graph.GetConfig(),
EqualsProto(mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "DetectionsToRectsCalculator"
input_stream: "DETECTIONS:detections"
input_stream: "IMAGE_SIZE:size"
output_stream: "NORM_RECT:rect"
options {
[mediapipe.DetectionsToRectsCalculatorOptions.ext] {
rotation_vector_start_keypoint_index: 0
rotation_vector_end_keypoint_index: 100
rotation_vector_target_angle_degrees: 200
conversion_mode: USE_KEYPOINTS
}
}
}
input_stream: "DETECTIONS:detections"
input_stream: "SIZE:size"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
} // namespace
} // namespace mediapipe::api2::builder
@@ -0,0 +1,66 @@
#ifndef MEDIAPIPE_FRAMEWORK_API2_STREAM_GET_VECTOR_ITEM_H_
#define MEDIAPIPE_FRAMEWORK_API2_STREAM_GET_VECTOR_ITEM_H_
#include <type_traits>
#include <vector>
#include "mediapipe/calculators/core/get_vector_item_calculator.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/api2/port.h"
#include "mediapipe/framework/formats/classification.pb.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/formats/rect.pb.h"
#include "tensorflow/lite/c/common.h"
namespace mediapipe::api2::builder {
namespace internal_get_vector_item {
// Helper function that adds a node to a graph, that is capable of getting item
// from a vector of type (T).
template <class T>
mediapipe::api2::builder::GenericNode& AddGetVectorItemNode(
mediapipe::api2::builder::Graph& graph) {
if constexpr (std::is_same_v<T, mediapipe::NormalizedLandmarkList>) {
return graph.AddNode("GetNormalizedLandmarkListVectorItemCalculator");
} else if constexpr (std::is_same_v<T, mediapipe::LandmarkList>) {
return graph.AddNode("GetLandmarkListVectorItemCalculator");
} else if constexpr (std::is_same_v<T, mediapipe::ClassificationList>) {
return graph.AddNode("GetClassificationListVectorItemCalculator");
} else if constexpr (std::is_same_v<T, mediapipe::NormalizedRect>) {
return graph.AddNode("GetNormalizedRectVectorItemCalculator");
} else if constexpr (std::is_same_v<T, mediapipe::Rect>) {
return graph.AddNode("GetRectVectorItemCalculator");
} else {
static_assert(
dependent_false<T>::value,
"Get vector item node is not available for the specified type.");
}
}
} // namespace internal_get_vector_item
// Gets item from the vector.
//
// Example:
// ```
//
// Graph graph;
//
// Stream<std::vector<LandmarkList>> multi_landmarks = ...;
// Stream<LandmarkList> landmarks =
// GetItem(multi_landmarks, 0, graph);
//
// ```
template <typename T>
Stream<T> GetItem(Stream<std::vector<T>> items, Stream<int> idx,
mediapipe::api2::builder::Graph& graph) {
auto& getter = internal_get_vector_item::AddGetVectorItemNode<T>(graph);
items.ConnectTo(getter.In("VECTOR"));
idx.ConnectTo(getter.In("INDEX"));
return getter.Out("ITEM").template Cast<T>();
}
} // namespace mediapipe::api2::builder
#endif // MEDIAPIPE_FRAMEWORK_API2_STREAM_GET_VECTOR_ITEM_H_
@@ -0,0 +1,130 @@
#include "mediapipe/framework/api2/stream/get_vector_item.h"
#include <vector>
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/classification.pb.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/formats/rect.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"
namespace mediapipe::api2::builder {
namespace {
using ::mediapipe::api2::builder::Graph;
TEST(GetItem, GetNormalizedLandmarkListVectorItem) {
Graph graph;
Stream<std::vector<NormalizedLandmarkList>> items =
graph.In("ITEMS").Cast<std::vector<NormalizedLandmarkList>>();
Stream<int> idx = graph.In("IDX").Cast<int>();
Stream<NormalizedLandmarkList> item = GetItem(items, idx, graph);
item.SetName("item");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "GetNormalizedLandmarkListVectorItemCalculator"
input_stream: "INDEX:__stream_0"
input_stream: "VECTOR:__stream_1"
output_stream: "ITEM:item"
}
input_stream: "IDX:__stream_0"
input_stream: "ITEMS:__stream_1"
)pb")));
CalculatorGraph calculator_graph;
MP_EXPECT_OK(calculator_graph.Initialize(graph.GetConfig()));
}
TEST(GetItem, GetLandmarkListVectorItem) {
Graph graph;
Stream<std::vector<LandmarkList>> items =
graph.In("ITEMS").Cast<std::vector<LandmarkList>>();
Stream<int> idx = graph.In("IDX").Cast<int>();
Stream<LandmarkList> item = GetItem(items, idx, graph);
item.SetName("item");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "GetLandmarkListVectorItemCalculator"
input_stream: "INDEX:__stream_0"
input_stream: "VECTOR:__stream_1"
output_stream: "ITEM:item"
}
input_stream: "IDX:__stream_0"
input_stream: "ITEMS:__stream_1"
)pb")));
CalculatorGraph calculator_graph;
MP_EXPECT_OK(calculator_graph.Initialize(graph.GetConfig()));
}
TEST(GetItem, GetClassificationListVectorItem) {
Graph graph;
Stream<std::vector<ClassificationList>> items =
graph.In("ITEMS").Cast<std::vector<ClassificationList>>();
Stream<int> idx = graph.In("IDX").Cast<int>();
Stream<ClassificationList> item = GetItem(items, idx, graph);
item.SetName("item");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "GetClassificationListVectorItemCalculator"
input_stream: "INDEX:__stream_0"
input_stream: "VECTOR:__stream_1"
output_stream: "ITEM:item"
}
input_stream: "IDX:__stream_0"
input_stream: "ITEMS:__stream_1"
)pb")));
CalculatorGraph calculator_graph;
MP_EXPECT_OK(calculator_graph.Initialize(graph.GetConfig()));
}
TEST(GetItem, GetNormalizedRectVectorItem) {
Graph graph;
Stream<std::vector<NormalizedRect>> items =
graph.In("ITEMS").Cast<std::vector<NormalizedRect>>();
Stream<int> idx = graph.In("IDX").Cast<int>();
Stream<NormalizedRect> item = GetItem(items, idx, graph);
item.SetName("item");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "GetNormalizedRectVectorItemCalculator"
input_stream: "INDEX:__stream_0"
input_stream: "VECTOR:__stream_1"
output_stream: "ITEM:item"
}
input_stream: "IDX:__stream_0"
input_stream: "ITEMS:__stream_1"
)pb")));
CalculatorGraph calculator_graph;
MP_EXPECT_OK(calculator_graph.Initialize(graph.GetConfig()));
}
TEST(GetItem, GetRectVectorItem) {
Graph graph;
Stream<std::vector<Rect>> items = graph.In("ITEMS").Cast<std::vector<Rect>>();
Stream<int> idx = graph.In("IDX").Cast<int>();
Stream<Rect> item = GetItem(items, idx, graph);
item.SetName("item");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "GetRectVectorItemCalculator"
input_stream: "INDEX:__stream_0"
input_stream: "VECTOR:__stream_1"
output_stream: "ITEM:item"
}
input_stream: "IDX:__stream_0"
input_stream: "ITEMS:__stream_1"
)pb")));
CalculatorGraph calculator_graph;
MP_EXPECT_OK(calculator_graph.Initialize(graph.GetConfig()));
}
} // namespace
} // namespace mediapipe::api2::builder
@@ -0,0 +1,64 @@
#include "mediapipe/framework/api2/stream/landmarks_to_tensor.h"
#include <optional>
#include <utility>
#include <vector>
#include "mediapipe/calculators/tensor/landmarks_to_tensor_calculator.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/api2/port.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/formats/tensor.h"
namespace mediapipe::api2::builder {
namespace {
using ::mediapipe::api2::LandmarksToTensorCalculator;
template <typename LandmarkListType>
Stream<std::vector<Tensor>> InternalConvertToTensor(
Stream<LandmarkListType> landmarks,
std::optional<Stream<std::pair<int, int>>> image_size,
absl::Span<const LandmarksToTensorCalculatorOptions::Attribute> attributes,
const bool flatten, Graph& graph) {
auto& to_tensor = graph.AddNode<LandmarksToTensorCalculator>();
auto& to_tensor_options =
to_tensor.GetOptions<LandmarksToTensorCalculatorOptions>();
for (const auto& attribute : attributes) {
to_tensor_options.add_attributes(attribute);
}
to_tensor_options.set_flatten(flatten);
if constexpr (std::is_same_v<LandmarkListType, LandmarkList>) {
landmarks.ConnectTo(
to_tensor[LandmarksToTensorCalculator::kInLandmarkList]);
} else {
landmarks.ConnectTo(
to_tensor[LandmarksToTensorCalculator::kInNormLandmarkList]);
}
if (image_size.has_value()) {
image_size->ConnectTo(to_tensor[LandmarksToTensorCalculator::kImageSize]);
}
return to_tensor[LandmarksToTensorCalculator::kOutTensors];
}
} // namespace
Stream<std::vector<Tensor>> ConvertLandmarksToTensor(
Stream<LandmarkList> landmarks,
absl::Span<const LandmarksToTensorCalculatorOptions::Attribute> attributes,
const bool flatten, Graph& graph) {
return InternalConvertToTensor(landmarks, /*image_size=*/std::nullopt,
attributes, flatten, graph);
}
Stream<std::vector<Tensor>> ConvertNormalizedLandmarksToTensor(
Stream<NormalizedLandmarkList> normalized_landmarks,
Stream<std::pair<int, int>> image_size,
absl::Span<const LandmarksToTensorCalculatorOptions::Attribute> attributes,
const bool flatten, Graph& graph) {
return InternalConvertToTensor(normalized_landmarks, image_size, attributes,
flatten, graph);
}
} // namespace mediapipe::api2::builder
@@ -0,0 +1,37 @@
#ifndef MEDIAPIPE_FRAMEWORK_API2_STREAM_LANDMARKS_TO_TENSOR_H_
#define MEDIAPIPE_FRAMEWORK_API2_STREAM_LANDMARKS_TO_TENSOR_H_
#include <utility>
#include <vector>
#include "absl/types/span.h"
#include "mediapipe/calculators/tensor/landmarks_to_tensor_calculator.pb.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/formats/tensor.h"
namespace mediapipe::api2::builder {
// Updates @graph to convert @landmarks to a Tensor. Values and their order are
// defined by @attributes. If @flatten is true resulting tensor will be 1D,
// otherwise tensor will be 2D with (n_landmarks, n_attributes) shape.
Stream<std::vector<Tensor>> ConvertLandmarksToTensor(
Stream<mediapipe::LandmarkList> landmarks,
absl::Span<const mediapipe::LandmarksToTensorCalculatorOptions::Attribute>
attributes,
bool flatten, Graph& graph);
// Updates @graph to convert @normalized_landmarks to a Tensor. Values and their
// order are defined by @attributes. X, Y and Z values are scaled using
// @image_size. If @flatten is true resulting tensor will be 1D, otherwise
// tensor will be 2D with (n_landmarks, n_attributes) shape.
Stream<std::vector<Tensor>> ConvertNormalizedLandmarksToTensor(
Stream<mediapipe::NormalizedLandmarkList> normalized_landmarks,
Stream<std::pair<int, int>> image_size,
absl::Span<const mediapipe::LandmarksToTensorCalculatorOptions::Attribute>
attributes,
bool flatten, Graph& graph);
} // namespace mediapipe::api2::builder
#endif // MEDIAPIPE_FRAMEWORK_API2_STREAM_LANDMARKS_TO_TENSOR_H_
@@ -0,0 +1,89 @@
#include "mediapipe/framework/api2/stream/landmarks_to_tensor.h"
#include <utility>
#include <vector>
#include "mediapipe/calculators/tensor/landmarks_to_tensor_calculator.pb.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/formats/tensor.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::api2::builder {
namespace {
TEST(ConvertLandmarksToTensor, ConvertLandmarksToTensor) {
Graph graph;
Stream<LandmarkList> landmarks = graph.In("LANDMARKS").Cast<LandmarkList>();
Stream<std::vector<Tensor>> tensors =
ConvertLandmarksToTensor(landmarks,
{LandmarksToTensorCalculatorOptions::X,
LandmarksToTensorCalculatorOptions::Y,
LandmarksToTensorCalculatorOptions::Z},
/*flatten=*/true, graph);
tensors.SetName("tensors");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "LandmarksToTensorCalculator"
input_stream: "LANDMARKS:__stream_0"
output_stream: "TENSORS:tensors"
options {
[mediapipe.LandmarksToTensorCalculatorOptions.ext] {
attributes: [ X, Y, Z ]
flatten: true
}
}
}
input_stream: "LANDMARKS:__stream_0"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
TEST(ConvertLandmarksToTensor, ConvertNormalizedLandmarksToTensor) {
Graph graph;
Stream<NormalizedLandmarkList> landmarks =
graph.In("LANDMARKS").Cast<NormalizedLandmarkList>();
Stream<std::pair<int, int>> image_size =
graph.In("IMAGE_SIZE").Cast<std::pair<int, int>>();
Stream<std::vector<Tensor>> tensors = ConvertNormalizedLandmarksToTensor(
landmarks, image_size,
{LandmarksToTensorCalculatorOptions::X,
LandmarksToTensorCalculatorOptions::Y,
LandmarksToTensorCalculatorOptions::Z},
/*flatten=*/false, graph);
tensors.SetName("tensors");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "LandmarksToTensorCalculator"
input_stream: "IMAGE_SIZE:__stream_0"
input_stream: "NORM_LANDMARKS:__stream_1"
output_stream: "TENSORS:tensors"
options {
[mediapipe.LandmarksToTensorCalculatorOptions.ext] {
attributes: [ X, Y, Z ]
flatten: false
}
}
}
input_stream: "IMAGE_SIZE:__stream_0"
input_stream: "LANDMARKS:__stream_1"
)pb")));
CalculatorGraph calcualtor_graph;
MP_EXPECT_OK(calcualtor_graph.Initialize(graph.GetConfig()));
}
} // namespace
} // namespace mediapipe::api2::builder
@@ -0,0 +1,19 @@
#ifndef MEDIAPIPE_FRAMEWORK_API2_STREAM_PRESENCE_H_
#define MEDIAPIPE_FRAMEWORK_API2_STREAM_PRESENCE_H_
#include "mediapipe/framework/api2/builder.h"
namespace mediapipe::api2::builder {
// Updates @graph to emit a stream containing `bool` packets, where each packet
// indicates whether @stream has a packet with corresponding timestamp or not.
template <typename T>
Stream<bool> IsPresent(Stream<T> stream, Graph& graph) {
auto& presence_node = graph.AddNode("PacketPresenceCalculator");
stream.ConnectTo(presence_node.In("PACKET"));
return presence_node.Out("PRESENCE").Cast<bool>();
}
} // namespace mediapipe::api2::builder
#endif // MEDIAPIPE_FRAMEWORK_API2_STREAM_PRESENCE_H_
@@ -0,0 +1,33 @@
#include "mediapipe/framework/api2/stream/presence.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/calculator.pb.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/port/parse_text_proto.h"
namespace mediapipe::api2::builder {
namespace {
TEST(Presence, VerifyConfig) {
Graph graph;
Stream<int> stream = graph.In("STREAM").Cast<int>();
stream.SetName("stream_to_check");
Stream<bool> is_present_stream = IsPresent(stream, graph);
is_present_stream.SetName("is_present_stream");
EXPECT_THAT(
graph.GetConfig(),
EqualsProto(mediapipe::ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "PacketPresenceCalculator"
input_stream: "PACKET:stream_to_check"
output_stream: "PRESENCE:is_present_stream"
}
input_stream: "STREAM:stream_to_check"
)pb")));
}
} // namespace
} // namespace mediapipe::api2::builder
@@ -0,0 +1,24 @@
#include "mediapipe/framework/api2/stream/segmentation_smoothing.h"
#include "mediapipe/calculators/image/segmentation_smoothing_calculator.pb.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/formats/image.h"
namespace mediapipe::api2::builder {
Stream<Image> SmoothSegmentationMask(Stream<Image> mask,
Stream<Image> previous_mask,
float combine_with_previous_ratio,
Graph& graph) {
auto& smoothing_node = graph.AddNode("SegmentationSmoothingCalculator");
auto& smoothing_node_opts =
smoothing_node
.GetOptions<mediapipe::SegmentationSmoothingCalculatorOptions>();
smoothing_node_opts.set_combine_with_previous_ratio(
combine_with_previous_ratio);
mask.ConnectTo(smoothing_node.In("MASK"));
previous_mask.ConnectTo(smoothing_node.In("MASK_PREVIOUS"));
return smoothing_node.Out("MASK_SMOOTHED").Cast<Image>();
}
} // namespace mediapipe::api2::builder
@@ -0,0 +1,19 @@
#ifndef MEDIAPIPE_FRAMEWORK_API2_STREAM_SEGMENTATION_SMOOTHING_H_
#define MEDIAPIPE_FRAMEWORK_API2_STREAM_SEGMENTATION_SMOOTHING_H_
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/formats/image.h"
namespace mediapipe::api2::builder {
// Updates @graph to smooth @mask by mixing @mask and @previous_mask based on an
// uncertantity probability estimate calculated per each @mask pixel multiplied
// by @combine_with_previous_ratio.
Stream<Image> SmoothSegmentationMask(Stream<Image> mask,
Stream<Image> previous_mask,
float combine_with_previous_ratio,
Graph& graph);
} // namespace mediapipe::api2::builder
#endif // MEDIAPIPE_FRAMEWORK_API2_STREAM_SEGMENTATION_SMOOTHING_H_
@@ -0,0 +1,43 @@
#include "mediapipe/framework/api2/stream/segmentation_smoothing.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/image.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/port/parse_text_proto.h"
namespace mediapipe::api2::builder {
namespace {
using ::mediapipe::Image;
TEST(SegmentationSmoothing, VerifyConfig) {
Graph graph;
Stream<Image> mask = graph.In("MASK").Cast<Image>();
Stream<Image> prev_mask = graph.In("PREV_MASK").Cast<Image>();
Stream<Image> smoothed_mask = SmoothSegmentationMask(
mask, prev_mask, /*combine_with_previous_ratio=*/0.1f, graph);
smoothed_mask.SetName("smoothed_mask");
EXPECT_THAT(graph.GetConfig(),
EqualsProto(ParseTextProtoOrDie<CalculatorGraphConfig>(R"pb(
node {
calculator: "SegmentationSmoothingCalculator"
input_stream: "MASK:__stream_0"
input_stream: "MASK_PREVIOUS:__stream_1"
output_stream: "MASK_SMOOTHED:smoothed_mask"
options {
[mediapipe.SegmentationSmoothingCalculatorOptions.ext] {
combine_with_previous_ratio: 0.1
}
}
}
input_stream: "MASK:__stream_0"
input_stream: "PREV_MASK:__stream_1"
)pb")));
}
} // namespace
} // namespace mediapipe::api2::builder
@@ -0,0 +1,131 @@
#include "mediapipe/framework/api2/stream/smoothing.h"
#include <optional>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "mediapipe/calculators/util/landmarks_smoothing_calculator.pb.h"
#include "mediapipe/calculators/util/visibility_smoothing_calculator.pb.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/formats/landmark.pb.h"
namespace mediapipe::api2::builder {
namespace {
void SetFilterConfig(const OneEuroFilterConfig& config,
bool disable_value_scaling, GenericNode& node) {
auto& smoothing_node_opts =
node.GetOptions<LandmarksSmoothingCalculatorOptions>();
auto& one_euro_filter = *smoothing_node_opts.mutable_one_euro_filter();
one_euro_filter.set_min_cutoff(config.min_cutoff);
one_euro_filter.set_derivate_cutoff(config.derivate_cutoff);
one_euro_filter.set_beta(config.beta);
one_euro_filter.set_disable_value_scaling(disable_value_scaling);
}
void SetFilterConfig(const LandmarksSmoothingCalculatorOptions& config,
GenericNode& node) {
auto& smoothing_node_opts =
node.GetOptions<LandmarksSmoothingCalculatorOptions>();
smoothing_node_opts = config;
}
GenericNode& AddVisibilitySmoothingNode(float low_pass_filter_alpha,
Graph& graph) {
auto& smoothing_node = graph.AddNode("VisibilitySmoothingCalculator");
auto& smoothing_node_opts =
smoothing_node.GetOptions<VisibilitySmoothingCalculatorOptions>();
smoothing_node_opts.mutable_low_pass_filter()->set_alpha(
low_pass_filter_alpha);
return smoothing_node;
}
} // namespace
Stream<NormalizedLandmarkList> SmoothLandmarks(
Stream<NormalizedLandmarkList> landmarks,
Stream<std::pair<int, int>> image_size,
std::optional<Stream<NormalizedRect>> scale_roi,
const OneEuroFilterConfig& config, Graph& graph) {
auto& smoothing_node = graph.AddNode("LandmarksSmoothingCalculator");
SetFilterConfig(config, /*disable_value_scaling=*/false, smoothing_node);
landmarks.ConnectTo(smoothing_node.In("NORM_LANDMARKS"));
image_size.ConnectTo(smoothing_node.In("IMAGE_SIZE"));
if (scale_roi) {
scale_roi->ConnectTo(smoothing_node.In("OBJECT_SCALE_ROI"));
}
return smoothing_node.Out("NORM_FILTERED_LANDMARKS")
.Cast<NormalizedLandmarkList>();
}
Stream<LandmarkList> SmoothLandmarks(
Stream<LandmarkList> landmarks,
std::optional<Stream<NormalizedRect>> scale_roi,
const OneEuroFilterConfig& config, Graph& graph) {
auto& smoothing_node = graph.AddNode("LandmarksSmoothingCalculator");
SetFilterConfig(config, /*disable_value_scaling=*/true, smoothing_node);
landmarks.ConnectTo(smoothing_node.In("LANDMARKS"));
if (scale_roi) {
scale_roi->ConnectTo(smoothing_node.In("OBJECT_SCALE_ROI"));
}
return smoothing_node.Out("FILTERED_LANDMARKS").Cast<LandmarkList>();
}
Stream<std::vector<NormalizedLandmarkList>> SmoothMultiLandmarks(
Stream<std::vector<NormalizedLandmarkList>> landmarks,
Stream<std::vector<int64_t>> tracking_ids,
Stream<std::pair<int, int>> image_size,
std::optional<Stream<std::vector<NormalizedRect>>> scale_roi,
const LandmarksSmoothingCalculatorOptions& config, Graph& graph) {
auto& smoothing_node = graph.AddNode("MultiLandmarksSmoothingCalculator");
SetFilterConfig(config, smoothing_node);
landmarks.ConnectTo(smoothing_node.In("NORM_LANDMARKS"));
tracking_ids.ConnectTo(smoothing_node.In("TRACKING_IDS"));
image_size.ConnectTo(smoothing_node.In("IMAGE_SIZE"));
if (scale_roi) {
scale_roi->ConnectTo(smoothing_node.In("OBJECT_SCALE_ROI"));
}
return smoothing_node.Out("NORM_FILTERED_LANDMARKS")
.Cast<std::vector<NormalizedLandmarkList>>();
}
Stream<std::vector<LandmarkList>> SmoothMultiWorldLandmarks(
Stream<std::vector<LandmarkList>> landmarks,
Stream<std::vector<int64_t>> tracking_ids,
std::optional<Stream<std::vector<Rect>>> scale_roi,
const LandmarksSmoothingCalculatorOptions& config, Graph& graph) {
auto& smoothing_node =
graph.AddNode("MultiWorldLandmarksSmoothingCalculator");
SetFilterConfig(config, smoothing_node);
landmarks.ConnectTo(smoothing_node.In("LANDMARKS"));
tracking_ids.ConnectTo(smoothing_node.In("TRACKING_IDS"));
if (scale_roi) {
scale_roi->ConnectTo(smoothing_node.In("OBJECT_SCALE_ROI"));
}
return smoothing_node.Out("FILTERED_LANDMARKS")
.Cast<std::vector<LandmarkList>>();
}
Stream<NormalizedLandmarkList> SmoothLandmarksVisibility(
Stream<NormalizedLandmarkList> landmarks, float low_pass_filter_alpha,
Graph& graph) {
auto& node = AddVisibilitySmoothingNode(low_pass_filter_alpha, graph);
landmarks.ConnectTo(node.In("NORM_LANDMARKS"));
return node.Out("NORM_FILTERED_LANDMARKS").Cast<NormalizedLandmarkList>();
}
Stream<LandmarkList> SmoothLandmarksVisibility(Stream<LandmarkList> landmarks,
float low_pass_filter_alpha,
Graph& graph) {
auto& node = AddVisibilitySmoothingNode(low_pass_filter_alpha, graph);
landmarks.ConnectTo(node.In("LANDMARKS"));
return node.Out("FILTERED_LANDMARKS").Cast<LandmarkList>();
}
} // namespace mediapipe::api2::builder
+119
View File
@@ -0,0 +1,119 @@
#ifndef MEDIAPIPE_FRAMEWORK_API2_STREAM_SMOOTHING_H_
#define MEDIAPIPE_FRAMEWORK_API2_STREAM_SMOOTHING_H_
#include <cstdint>
#include <optional>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "mediapipe/calculators/util/landmarks_smoothing_calculator.pb.h"
#include "mediapipe/framework/api2/builder.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/formats/rect.pb.h"
namespace mediapipe::api2::builder {
struct OneEuroFilterConfig {
float min_cutoff;
float beta;
float derivate_cutoff;
};
// Updates graph to smooth normalized landmarks and returns resulting stream.
//
// @landmarks - normalized landmarks.
// @image_size - size of image where landmarks were detected.
// @scale_roi - can be used to specify object scale.
// @config - filter config.
// @graph - graph to update.
//
// Returns: smoothed/filtered normalized landmarks.
//
// NOTE: one-euro filter is exposed only. Other filter options can be exposed
// on demand.
Stream<mediapipe::NormalizedLandmarkList> SmoothLandmarks(
Stream<mediapipe::NormalizedLandmarkList> landmarks,
Stream<std::pair<int, int>> image_size,
std::optional<Stream<NormalizedRect>> scale_roi,
const OneEuroFilterConfig& config, Graph& graph);
// Updates graph to smooth absolute landmarks and returns resulting stream.
//
// @landmarks - absolute landmarks.
// @scale_roi - can be used to specify object scale.
// @config - filter config.
// @graph - graph to update.
//
// Returns: smoothed/filtered absolute landmarks.
//
// NOTE: one-euro filter is exposed only. Other filter options can be exposed
// on demand.
Stream<mediapipe::LandmarkList> SmoothLandmarks(
Stream<mediapipe::LandmarkList> landmarks,
std::optional<Stream<NormalizedRect>> scale_roi,
const OneEuroFilterConfig& config, Graph& graph);
// Updates graph to smooth normalized landmarks and returns resulting stream.
//
// @landmarks - normalized landmarks vector.
// @tracking_ids - tracking IDs associated with landmarks
// @image_size - size of image where landmarks were detected.
// @scale_roi - can be used to specify object scales.
// @config - filter config.
// @graph - graph to update.
//
// Returns: smoothed/filtered normalized landmarks.
//
// NOTE: one-euro filter is exposed only. Other filter options can be exposed
// on demand.
Stream<std::vector<mediapipe::NormalizedLandmarkList>> SmoothMultiLandmarks(
Stream<std::vector<mediapipe::NormalizedLandmarkList>> landmarks,
Stream<std::vector<int64_t>> tracking_ids,
Stream<std::pair<int, int>> image_size,
std::optional<Stream<std::vector<NormalizedRect>>> scale_roi,
const mediapipe::LandmarksSmoothingCalculatorOptions& config, Graph& graph);
// Updates graph to smooth absolute landmarks and returns resulting stream.
//
// @landmarks - absolute landmarks vector.
// @tracking_ids - tracking IDs associated with landmarks
// @scale_roi - can be used to specify object scales.
// @config - filter config.
// @graph - graph to update.
//
// Returns: smoothed/filtered absolute landmarks.
//
// NOTE: one-euro filter is exposed only. Other filter options can be exposed
// on demand.
Stream<std::vector<mediapipe::LandmarkList>> SmoothMultiWorldLandmarks(
Stream<std::vector<mediapipe::LandmarkList>> landmarks,
Stream<std::vector<int64_t>> tracking_ids,
std::optional<Stream<std::vector<mediapipe::Rect>>> scale_roi,
const mediapipe::LandmarksSmoothingCalculatorOptions& config, Graph& graph);
// Updates graph to smooth visibility of landmarks.
//
// @landmarks - normalized landmarks.
// @low_pass_filter_alpha - low pass filter alpha to use for smoothing.
// @graph - graph to update.
//
// Returns: normalized landmarks containing smoothed visibility.
Stream<mediapipe::NormalizedLandmarkList> SmoothLandmarksVisibility(
Stream<mediapipe::NormalizedLandmarkList> landmarks,
float low_pass_filter_alpha, Graph& graph);
// Updates graph to smooth visibility of landmarks.
//
// @landmarks - absolute landmarks.
// @low_pass_filter_alpha - low pass filter alpha to use for smoothing.
// @graph - graph to update.
//
// Returns: absolute landmarks containing smoothed visibility.
Stream<mediapipe::LandmarkList> SmoothLandmarksVisibility(
Stream<mediapipe::LandmarkList> landmarks, float low_pass_filter_alpha,
mediapipe::api2::builder::Graph& graph);
} // namespace mediapipe::api2::builder
#endif // MEDIAPIPE_FRAMEWORK_API2_STREAM_SMOOTHING_H_

Some files were not shown because too many files have changed in this diff Show More