Compare commits
@@ -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.
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
+4
-3
@@ -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(
|
||||||
|
|||||||
+4
-4
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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 {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
Reference in New Issue
Block a user