Project import generated by Copybara.

GitOrigin-RevId: d8caa66de45839696f5bd0786ad3bfbcb9cff632
This commit is contained in:
MediaPipe Team
2020-12-09 22:43:33 -05:00
committed by chuoling
parent f15da632de
commit 2b58cceec9
750 changed files with 22901 additions and 9478 deletions
@@ -81,11 +81,11 @@ constexpr int kFaceLandmarkConnections[] = {
class FaceLandmarksToRenderDataCalculator
: public LandmarksToRenderDataCalculator {
public:
::mediapipe::Status Open(CalculatorContext* cc) override;
mediapipe::Status Open(CalculatorContext* cc) override;
};
REGISTER_CALCULATOR(FaceLandmarksToRenderDataCalculator);
::mediapipe::Status FaceLandmarksToRenderDataCalculator::Open(
mediapipe::Status FaceLandmarksToRenderDataCalculator::Open(
CalculatorContext* cc) {
cc->SetOffset(TimestampDiff(0));
options_ = cc->Options<mediapipe::LandmarksToRenderDataCalculatorOptions>();
@@ -95,7 +95,7 @@ REGISTER_CALCULATOR(FaceLandmarksToRenderDataCalculator);
landmark_connections_.push_back(kFaceLandmarkConnections[i * 2 + 1]);
}
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
} // namespace mediapipe
+73
View File
@@ -0,0 +1,73 @@
# Copyright 2020 The MediaPipe Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load(
"//mediapipe/framework/tool:mediapipe_graph.bzl",
"mediapipe_binary_graph",
"mediapipe_simple_subgraph",
)
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
mediapipe_simple_subgraph(
name = "holistic_tracking_to_render_data",
graph = "holistic_tracking_to_render_data.pbtxt",
register_as = "HolisticTrackingToRenderData",
deps = [
"//mediapipe/calculators/core:concatenate_normalized_landmark_list_calculator",
"//mediapipe/calculators/core:concatenate_vector_calculator",
"//mediapipe/calculators/core:merge_calculator",
"//mediapipe/calculators/core:split_normalized_landmark_list_calculator",
"//mediapipe/calculators/core:split_vector_calculator",
"//mediapipe/calculators/util:detections_to_render_data_calculator",
"//mediapipe/calculators/util:landmarks_to_render_data_calculator",
"//mediapipe/calculators/util:rect_to_render_data_calculator",
"//mediapipe/calculators/util:rect_to_render_scale_calculator",
"//mediapipe/framework/tool:switch_container",
"//mediapipe/modules/holistic_landmark:hand_wrist_for_pose",
],
)
cc_library(
name = "holistic_tracking_gpu_deps",
deps = [
":holistic_tracking_to_render_data",
"//mediapipe/calculators/core:constant_side_packet_calculator",
"//mediapipe/calculators/core:flow_limiter_calculator",
"//mediapipe/calculators/image:image_properties_calculator",
"//mediapipe/calculators/util:annotation_overlay_calculator",
"//mediapipe/modules/holistic_landmark:holistic_landmark_gpu",
],
)
mediapipe_binary_graph(
name = "holistic_tracking_gpu",
graph = "holistic_tracking_gpu.pbtxt",
output_name = "holistic_tracking_gpu.binarypb",
deps = [":holistic_tracking_gpu_deps"],
)
cc_library(
name = "holistic_tracking_cpu_graph_deps",
deps = [
":holistic_tracking_to_render_data",
"//mediapipe/calculators/core:constant_side_packet_calculator",
"//mediapipe/calculators/core:flow_limiter_calculator",
"//mediapipe/calculators/image:image_properties_calculator",
"//mediapipe/calculators/util:annotation_overlay_calculator",
"//mediapipe/modules/holistic_landmark:holistic_landmark_cpu",
],
)
@@ -0,0 +1,90 @@
# Tracks and renders pose + hands + face landmarks.
# CPU image. (ImageFrame)
input_stream: "input_video"
# CPU image with rendered results. (ImageFrame)
output_stream: "output_video"
# Throttles the images flowing downstream for flow control. It passes through
# the very first incoming image unaltered, and waits for downstream nodes
# (calculators and subgraphs) in the graph to finish their tasks before it
# passes through another image. All images that come in while waiting are
# dropped, limiting the number of in-flight images in most part of the graph to
# 1. This prevents the downstream nodes from queuing up incoming images and data
# excessively, which leads to increased latency and memory usage, unwanted in
# real-time mobile applications. It also eliminates unnecessarily computation,
# e.g., the output produced by a node may get dropped downstream if the
# subsequent nodes are still busy processing previous inputs.
node {
calculator: "FlowLimiterCalculator"
input_stream: "input_video"
input_stream: "FINISHED:output_video"
input_stream_info: {
tag_index: "FINISHED"
back_edge: true
}
output_stream: "throttled_input_video"
node_options: {
[type.googleapis.com/mediapipe.FlowLimiterCalculatorOptions] {
max_in_flight: 1
max_in_queue: 1
# Timeout is disabled (set to 0) as first frame processing can take more
# than 1 second.
in_flight_timeout: 0
}
}
}
node {
calculator: "ConstantSidePacketCalculator"
output_side_packet: "PACKET:0:upper_body_only"
output_side_packet: "PACKET:1:smooth_landmarks"
node_options: {
[type.googleapis.com/mediapipe.ConstantSidePacketCalculatorOptions]: {
packet { bool_value: false }
packet { bool_value: true }
}
}
}
node {
calculator: "HolisticLandmarkCpu"
input_stream: "IMAGE:throttled_input_video"
input_side_packet: "UPPER_BODY_ONLY:upper_body_only"
input_side_packet: "SMOOTH_LANDMARKS:smooth_landmarks"
output_stream: "POSE_LANDMARKS:pose_landmarks"
output_stream: "POSE_ROI:pose_roi"
output_stream: "POSE_DETECTION:pose_detection"
output_stream: "FACE_LANDMARKS:face_landmarks"
output_stream: "LEFT_HAND_LANDMARKS:left_hand_landmarks"
output_stream: "RIGHT_HAND_LANDMARKS:right_hand_landmarks"
}
# Gets image size.
node {
calculator: "ImagePropertiesCalculator"
input_stream: "IMAGE:throttled_input_video"
output_stream: "SIZE:image_size"
}
# Converts pose, hands and face landmarks to a render data vector.
node {
calculator: "HolisticTrackingToRenderData"
input_stream: "IMAGE_SIZE:image_size"
input_stream: "POSE_LANDMARKS:pose_landmarks"
input_stream: "POSE_ROI:pose_roi"
input_stream: "LEFT_HAND_LANDMARKS:left_hand_landmarks"
input_stream: "RIGHT_HAND_LANDMARKS:right_hand_landmarks"
input_stream: "FACE_LANDMARKS:face_landmarks"
input_side_packet: "UPPER_BODY_ONLY:upper_body_only"
output_stream: "RENDER_DATA_VECTOR:render_data_vector"
}
# Draws annotations and overlays them on top of the input images.
node {
calculator: "AnnotationOverlayCalculator"
input_stream: "IMAGE:throttled_input_video"
input_stream: "VECTOR:render_data_vector"
output_stream: "IMAGE:output_video"
}
@@ -0,0 +1,90 @@
# Tracks and renders pose + hands + face landmarks.
# GPU buffer. (GpuBuffer)
input_stream: "input_video"
# GPU image with rendered results. (GpuBuffer)
output_stream: "output_video"
# Throttles the images flowing downstream for flow control. It passes through
# the very first incoming image unaltered, and waits for downstream nodes
# (calculators and subgraphs) in the graph to finish their tasks before it
# passes through another image. All images that come in while waiting are
# dropped, limiting the number of in-flight images in most part of the graph to
# 1. This prevents the downstream nodes from queuing up incoming images and data
# excessively, which leads to increased latency and memory usage, unwanted in
# real-time mobile applications. It also eliminates unnecessarily computation,
# e.g., the output produced by a node may get dropped downstream if the
# subsequent nodes are still busy processing previous inputs.
node {
calculator: "FlowLimiterCalculator"
input_stream: "input_video"
input_stream: "FINISHED:output_video"
input_stream_info: {
tag_index: "FINISHED"
back_edge: true
}
output_stream: "throttled_input_video"
node_options: {
[type.googleapis.com/mediapipe.FlowLimiterCalculatorOptions] {
max_in_flight: 1
max_in_queue: 1
# Timeout is disabled (set to 0) as first frame processing can take more
# than 1 second.
in_flight_timeout: 0
}
}
}
node {
calculator: "ConstantSidePacketCalculator"
output_side_packet: "PACKET:0:upper_body_only"
output_side_packet: "PACKET:1:smooth_landmarks"
node_options: {
[type.googleapis.com/mediapipe.ConstantSidePacketCalculatorOptions]: {
packet { bool_value: false }
packet { bool_value: true }
}
}
}
node {
calculator: "HolisticLandmarkGpu"
input_stream: "IMAGE:throttled_input_video"
input_side_packet: "UPPER_BODY_ONLY:upper_body_only"
input_side_packet: "SMOOTH_LANDMARKS:smooth_landmarks"
output_stream: "POSE_LANDMARKS:pose_landmarks"
output_stream: "POSE_ROI:pose_roi"
output_stream: "POSE_DETECTION:pose_detection"
output_stream: "FACE_LANDMARKS:face_landmarks"
output_stream: "LEFT_HAND_LANDMARKS:left_hand_landmarks"
output_stream: "RIGHT_HAND_LANDMARKS:right_hand_landmarks"
}
# Gets image size.
node {
calculator: "ImagePropertiesCalculator"
input_stream: "IMAGE_GPU:throttled_input_video"
output_stream: "SIZE:image_size"
}
# Converts pose, hands and face landmarks to a render data vector.
node {
calculator: "HolisticTrackingToRenderData"
input_stream: "IMAGE_SIZE:image_size"
input_stream: "POSE_LANDMARKS:pose_landmarks"
input_stream: "POSE_ROI:pose_roi"
input_stream: "LEFT_HAND_LANDMARKS:left_hand_landmarks"
input_stream: "RIGHT_HAND_LANDMARKS:right_hand_landmarks"
input_stream: "FACE_LANDMARKS:face_landmarks"
input_side_packet: "UPPER_BODY_ONLY:upper_body_only"
output_stream: "RENDER_DATA_VECTOR:render_data_vector"
}
# Draws annotations and overlays them on top of the input images.
node {
calculator: "AnnotationOverlayCalculator"
input_stream: "IMAGE_GPU:throttled_input_video"
input_stream: "VECTOR:render_data_vector"
output_stream: "IMAGE_GPU:output_video"
}
@@ -0,0 +1,854 @@
# Converts pose + hands + face landmarks to a render data vector.
type: "HolisticTrackingToRenderData"
# Image size. (std::pair<int, int>)
input_stream: "IMAGE_SIZE:image_size"
# Pose landmarks. (NormalizedLandmarkList)
input_stream: "POSE_LANDMARKS:landmarks"
# Region of interest calculated based on pose landmarks. (NormalizedRect)
input_stream: "POSE_ROI:roi"
# Left hand landmarks. (NormalizedLandmarkList)
input_stream: "LEFT_HAND_LANDMARKS:left_hand_landmarks"
# Right hand landmarks. (NormalizedLandmarkList)
input_stream: "RIGHT_HAND_LANDMARKS:right_hand_landmarks"
# Face landmarks. (NormalizedLandmarkList)
input_stream: "FACE_LANDMARKS:face_landmarks"
# Whether to render the full set of pose landmarks, or only those on the
# upper body. If unspecified, functions as set to false. (bool)
input_side_packet: "UPPER_BODY_ONLY:upper_body_only"
# Render data vector. (std::vector<RenderData>)
output_stream: "RENDER_DATA_VECTOR:render_data_vector"
# --------------------------------------------------------------------------- #
# ------------------ Calculates scale for render objects -------------------- #
# --------------------------------------------------------------------------- #
# Calculates rendering scale based on the pose bounding box.
node {
calculator: "RectToRenderScaleCalculator"
input_stream: "NORM_RECT:roi"
input_stream: "IMAGE_SIZE:image_size"
output_stream: "RENDER_SCALE:render_scale"
node_options: {
[type.googleapis.com/mediapipe.RectToRenderScaleCalculatorOptions] {
multiplier: 0.0008
}
}
}
# --------------------------------------------------------------------------- #
# --------------- Combines pose and hands into pose skeleton ---------------- #
# --------------------------------------------------------------------------- #
# Gets pose landmarks before wrists.
node {
calculator: "SplitNormalizedLandmarkListCalculator"
input_stream: "landmarks"
output_stream: "landmarks_before_wrist"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 11 end: 15 }
}
}
}
# Gets pose left wrist landmark.
node {
calculator: "SplitNormalizedLandmarkListCalculator"
input_stream: "landmarks"
output_stream: "landmarks_left_wrist"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 15 end: 16 }
}
}
}
# Gets pose right wrist landmark.
node {
calculator: "SplitNormalizedLandmarkListCalculator"
input_stream: "landmarks"
output_stream: "landmarks_right_wrist"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 16 end: 17 }
}
}
}
# Gets pose landmarks after wrists.
node {
calculator: "SwitchContainer"
input_side_packet: "ENABLE:upper_body_only"
input_stream: "landmarks"
output_stream: "landmarks_after_wrist"
node_options: {
[type.googleapis.com/mediapipe.SwitchContainerOptions] {
contained_node: {
calculator: "SplitNormalizedLandmarkListCalculator"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 23 end: 33 }
}
}
}
contained_node: {
calculator: "SplitNormalizedLandmarkListCalculator"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 23 end: 25 }
}
}
}
}
}
}
# Gets left hand wrist landmark.
node {
calculator: "HandWristForPose"
input_stream: "HAND_LANDMARKS:left_hand_landmarks"
output_stream: "WRIST_LANDMARK:left_hand_wrist_landmark"
}
# Gets left hand wrist landmark or keep pose wrist landmark if hand was not
# predicted.
node {
calculator: "MergeCalculator"
input_stream: "left_hand_wrist_landmark"
input_stream: "landmarks_left_wrist"
output_stream: "merged_left_hand_wrist_landmark"
}
# Gets right hand wrist landmark.
node {
calculator: "HandWristForPose"
input_stream: "HAND_LANDMARKS:right_hand_landmarks"
output_stream: "WRIST_LANDMARK:right_hand_wrist_landmark"
}
# Gets right hand wrist landmark or keep pose wrist landmark if hand was not
# predicted.
node {
calculator: "MergeCalculator"
input_stream: "right_hand_wrist_landmark"
input_stream: "landmarks_right_wrist"
output_stream: "merged_right_hand_wrist_landmark"
}
# Combines pose landmarks all together.
node {
calculator: "ConcatenateNormalizedLandmarkListCalculator"
input_stream: "landmarks_before_wrist"
input_stream: "merged_left_hand_wrist_landmark"
input_stream: "merged_right_hand_wrist_landmark"
input_stream: "landmarks_after_wrist"
output_stream: "landmarks_merged"
node_options: {
[type.googleapis.com/mediapipe.ConcatenateVectorCalculatorOptions] {
only_emit_if_all_present: true
}
}
}
# Takes left pose landmarks.
node {
calculator: "SwitchContainer"
input_side_packet: "ENABLE:upper_body_only"
input_stream: "landmarks_merged"
output_stream: "landmarks_left_side"
node_options: {
[type.googleapis.com/mediapipe.SwitchContainerOptions] {
contained_node: {
calculator: "SplitNormalizedLandmarkListCalculator"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 0 end: 1 }
ranges: { begin: 2 end: 3 }
ranges: { begin: 4 end: 5 }
ranges: { begin: 6 end: 7 }
ranges: { begin: 8 end: 9 }
ranges: { begin: 10 end: 11 }
ranges: { begin: 12 end: 13 }
ranges: { begin: 14 end: 15 }
combine_outputs: true
}
}
}
contained_node: {
calculator: "SplitNormalizedLandmarkListCalculator"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 0 end: 1 }
ranges: { begin: 2 end: 3 }
ranges: { begin: 4 end: 5 }
ranges: { begin: 6 end: 7 }
combine_outputs: true
}
}
}
}
}
}
# Takes right pose landmarks.
node {
calculator: "SwitchContainer"
input_side_packet: "ENABLE:upper_body_only"
input_stream: "landmarks_merged"
output_stream: "landmarks_right_side"
node_options: {
[type.googleapis.com/mediapipe.SwitchContainerOptions] {
contained_node: {
calculator: "SplitNormalizedLandmarkListCalculator"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 1 end: 2 }
ranges: { begin: 3 end: 4 }
ranges: { begin: 5 end: 6 }
ranges: { begin: 7 end: 8 }
ranges: { begin: 9 end: 10 }
ranges: { begin: 11 end: 12 }
ranges: { begin: 13 end: 14 }
ranges: { begin: 15 end: 16 }
combine_outputs: true
}
}
}
contained_node: {
calculator: "SplitNormalizedLandmarkListCalculator"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 1 end: 2 }
ranges: { begin: 3 end: 4 }
ranges: { begin: 5 end: 6 }
ranges: { begin: 7 end: 8 }
combine_outputs: true
}
}
}
}
}
}
# --------------------------------------------------------------------------- #
# ---------------------------------- Pose ----------------------------------- #
# --------------------------------------------------------------------------- #
# Converts pose connections to white lines.
node {
calculator: "SwitchContainer"
input_side_packet: "ENABLE:upper_body_only"
input_stream: "NORM_LANDMARKS:landmarks_merged"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_render_data"
node_options: {
[type.googleapis.com/mediapipe.SwitchContainerOptions] {
contained_node: {
calculator: "LandmarksToRenderDataCalculator"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_connections: 0
landmark_connections: 1
landmark_connections: 0
landmark_connections: 2
landmark_connections: 2
landmark_connections: 4
landmark_connections: 1
landmark_connections: 3
landmark_connections: 3
landmark_connections: 5
landmark_connections: 0
landmark_connections: 6
landmark_connections: 1
landmark_connections: 7
landmark_connections: 6
landmark_connections: 7
landmark_connections: 6
landmark_connections: 8
landmark_connections: 7
landmark_connections: 9
landmark_connections: 8
landmark_connections: 10
landmark_connections: 9
landmark_connections: 11
landmark_connections: 10
landmark_connections: 12
landmark_connections: 11
landmark_connections: 13
landmark_connections: 12
landmark_connections: 14
landmark_connections: 13
landmark_connections: 15
landmark_connections: 10
landmark_connections: 14
landmark_connections: 11
landmark_connections: 15
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.1
}
}
}
contained_node: {
calculator: "LandmarksToRenderDataCalculator"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_connections: 0
landmark_connections: 1
landmark_connections: 0
landmark_connections: 2
landmark_connections: 2
landmark_connections: 4
landmark_connections: 1
landmark_connections: 3
landmark_connections: 3
landmark_connections: 5
landmark_connections: 0
landmark_connections: 6
landmark_connections: 1
landmark_connections: 7
landmark_connections: 6
landmark_connections: 7
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
}
}
}
# Converts pose joints to big white circles.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:landmarks_merged"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_background_joints_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 5.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Converts pose left side joints to orange circles (inside white ones).
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:landmarks_left_side"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_left_joints_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 255 g: 138 b: 0 }
connection_color { r: 255 g: 138 b: 0 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Converts pose right side joints to cyan circles (inside white ones).
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:landmarks_right_side"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_right_joints_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 0 g: 217 b: 231 }
connection_color { r: 0 g: 217 b: 231 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# --------------------------------------------------------------------------- #
# ------------------------------- Left hand --------------------------------- #
# --------------------------------------------------------------------------- #
# Converts left hand connections to white lines.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:left_hand_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:left_hand_landmarks_connections_rd"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_connections: 0
landmark_connections: 1
landmark_connections: 1
landmark_connections: 2
landmark_connections: 2
landmark_connections: 3
landmark_connections: 3
landmark_connections: 4
landmark_connections: 0
landmark_connections: 5
landmark_connections: 5
landmark_connections: 6
landmark_connections: 6
landmark_connections: 7
landmark_connections: 7
landmark_connections: 8
landmark_connections: 5
landmark_connections: 9
landmark_connections: 9
landmark_connections: 10
landmark_connections: 10
landmark_connections: 11
landmark_connections: 11
landmark_connections: 12
landmark_connections: 9
landmark_connections: 13
landmark_connections: 13
landmark_connections: 14
landmark_connections: 14
landmark_connections: 15
landmark_connections: 15
landmark_connections: 16
landmark_connections: 13
landmark_connections: 17
landmark_connections: 0
landmark_connections: 17
landmark_connections: 17
landmark_connections: 18
landmark_connections: 18
landmark_connections: 19
landmark_connections: 19
landmark_connections: 20
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 4.0
visualize_landmark_depth: false
}
}
}
# Converts left hand color joints.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:left_hand_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:left_hand_landmarks_joints_rd"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 255 g: 138 b: 0 }
connection_color { r: 255 g: 138 b: 0 }
thickness: 3.0
visualize_landmark_depth: false
}
}
}
# --------------------------------------------------------------------------- #
# -------------------------------- Right hand ------------------------------- #
# --------------------------------------------------------------------------- #
# Converts right hand connections to white lines.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:right_hand_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:right_hand_landmarks_connections_rd"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_connections: 0
landmark_connections: 1
landmark_connections: 1
landmark_connections: 2
landmark_connections: 2
landmark_connections: 3
landmark_connections: 3
landmark_connections: 4
landmark_connections: 0
landmark_connections: 5
landmark_connections: 5
landmark_connections: 6
landmark_connections: 6
landmark_connections: 7
landmark_connections: 7
landmark_connections: 8
landmark_connections: 5
landmark_connections: 9
landmark_connections: 9
landmark_connections: 10
landmark_connections: 10
landmark_connections: 11
landmark_connections: 11
landmark_connections: 12
landmark_connections: 9
landmark_connections: 13
landmark_connections: 13
landmark_connections: 14
landmark_connections: 14
landmark_connections: 15
landmark_connections: 15
landmark_connections: 16
landmark_connections: 13
landmark_connections: 17
landmark_connections: 0
landmark_connections: 17
landmark_connections: 17
landmark_connections: 18
landmark_connections: 18
landmark_connections: 19
landmark_connections: 19
landmark_connections: 20
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 4.0
visualize_landmark_depth: false
}
}
}
# Converts right hand color joints.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:right_hand_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:right_hand_landmarks_joints_rd"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 0 g: 217 b: 231 }
connection_color { r: 0 g: 217 b: 231 }
thickness: 3.0
visualize_landmark_depth: false
}
}
}
# --------------------------------------------------------------------------- #
# ---------------------------------- Face ----------------------------------- #
# --------------------------------------------------------------------------- #
# Converts face connections to white lines.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:face_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:face_landmarks_connections_rd"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
# Lips.
landmark_connections: 61
landmark_connections: 146
landmark_connections: 146
landmark_connections: 91
landmark_connections: 91
landmark_connections: 181
landmark_connections: 181
landmark_connections: 84
landmark_connections: 84
landmark_connections: 17
landmark_connections: 17
landmark_connections: 314
landmark_connections: 314
landmark_connections: 405
landmark_connections: 405
landmark_connections: 321
landmark_connections: 321
landmark_connections: 375
landmark_connections: 375
landmark_connections: 291
landmark_connections: 61
landmark_connections: 185
landmark_connections: 185
landmark_connections: 40
landmark_connections: 40
landmark_connections: 39
landmark_connections: 39
landmark_connections: 37
landmark_connections: 37
landmark_connections: 0
landmark_connections: 0
landmark_connections: 267
landmark_connections: 267
landmark_connections: 269
landmark_connections: 269
landmark_connections: 270
landmark_connections: 270
landmark_connections: 409
landmark_connections: 409
landmark_connections: 291
landmark_connections: 78
landmark_connections: 95
landmark_connections: 95
landmark_connections: 88
landmark_connections: 88
landmark_connections: 178
landmark_connections: 178
landmark_connections: 87
landmark_connections: 87
landmark_connections: 14
landmark_connections: 14
landmark_connections: 317
landmark_connections: 317
landmark_connections: 402
landmark_connections: 402
landmark_connections: 318
landmark_connections: 318
landmark_connections: 324
landmark_connections: 324
landmark_connections: 308
landmark_connections: 78
landmark_connections: 191
landmark_connections: 191
landmark_connections: 80
landmark_connections: 80
landmark_connections: 81
landmark_connections: 81
landmark_connections: 82
landmark_connections: 82
landmark_connections: 13
landmark_connections: 13
landmark_connections: 312
landmark_connections: 312
landmark_connections: 311
landmark_connections: 311
landmark_connections: 310
landmark_connections: 310
landmark_connections: 415
landmark_connections: 415
landmark_connections: 308
# Left eye.
landmark_connections: 33
landmark_connections: 7
landmark_connections: 7
landmark_connections: 163
landmark_connections: 163
landmark_connections: 144
landmark_connections: 144
landmark_connections: 145
landmark_connections: 145
landmark_connections: 153
landmark_connections: 153
landmark_connections: 154
landmark_connections: 154
landmark_connections: 155
landmark_connections: 155
landmark_connections: 133
landmark_connections: 33
landmark_connections: 246
landmark_connections: 246
landmark_connections: 161
landmark_connections: 161
landmark_connections: 160
landmark_connections: 160
landmark_connections: 159
landmark_connections: 159
landmark_connections: 158
landmark_connections: 158
landmark_connections: 157
landmark_connections: 157
landmark_connections: 173
landmark_connections: 173
landmark_connections: 133
# Left eyebrow.
landmark_connections: 46
landmark_connections: 53
landmark_connections: 53
landmark_connections: 52
landmark_connections: 52
landmark_connections: 65
landmark_connections: 65
landmark_connections: 55
landmark_connections: 70
landmark_connections: 63
landmark_connections: 63
landmark_connections: 105
landmark_connections: 105
landmark_connections: 66
landmark_connections: 66
landmark_connections: 107
# Right eye.
landmark_connections: 263
landmark_connections: 249
landmark_connections: 249
landmark_connections: 390
landmark_connections: 390
landmark_connections: 373
landmark_connections: 373
landmark_connections: 374
landmark_connections: 374
landmark_connections: 380
landmark_connections: 380
landmark_connections: 381
landmark_connections: 381
landmark_connections: 382
landmark_connections: 382
landmark_connections: 362
landmark_connections: 263
landmark_connections: 466
landmark_connections: 466
landmark_connections: 388
landmark_connections: 388
landmark_connections: 387
landmark_connections: 387
landmark_connections: 386
landmark_connections: 386
landmark_connections: 385
landmark_connections: 385
landmark_connections: 384
landmark_connections: 384
landmark_connections: 398
landmark_connections: 398
landmark_connections: 362
# Right eyebrow.
landmark_connections: 276
landmark_connections: 283
landmark_connections: 283
landmark_connections: 282
landmark_connections: 282
landmark_connections: 295
landmark_connections: 295
landmark_connections: 285
landmark_connections: 300
landmark_connections: 293
landmark_connections: 293
landmark_connections: 334
landmark_connections: 334
landmark_connections: 296
landmark_connections: 296
landmark_connections: 336
# Face oval.
landmark_connections: 10
landmark_connections: 338
landmark_connections: 338
landmark_connections: 297
landmark_connections: 297
landmark_connections: 332
landmark_connections: 332
landmark_connections: 284
landmark_connections: 284
landmark_connections: 251
landmark_connections: 251
landmark_connections: 389
landmark_connections: 389
landmark_connections: 356
landmark_connections: 356
landmark_connections: 454
landmark_connections: 454
landmark_connections: 323
landmark_connections: 323
landmark_connections: 361
landmark_connections: 361
landmark_connections: 288
landmark_connections: 288
landmark_connections: 397
landmark_connections: 397
landmark_connections: 365
landmark_connections: 365
landmark_connections: 379
landmark_connections: 379
landmark_connections: 378
landmark_connections: 378
landmark_connections: 400
landmark_connections: 400
landmark_connections: 377
landmark_connections: 377
landmark_connections: 152
landmark_connections: 152
landmark_connections: 148
landmark_connections: 148
landmark_connections: 176
landmark_connections: 176
landmark_connections: 149
landmark_connections: 149
landmark_connections: 150
landmark_connections: 150
landmark_connections: 136
landmark_connections: 136
landmark_connections: 172
landmark_connections: 172
landmark_connections: 58
landmark_connections: 58
landmark_connections: 132
landmark_connections: 132
landmark_connections: 93
landmark_connections: 93
landmark_connections: 234
landmark_connections: 234
landmark_connections: 127
landmark_connections: 127
landmark_connections: 162
landmark_connections: 162
landmark_connections: 21
landmark_connections: 21
landmark_connections: 54
landmark_connections: 54
landmark_connections: 103
landmark_connections: 103
landmark_connections: 67
landmark_connections: 67
landmark_connections: 109
landmark_connections: 109
landmark_connections: 10
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 0.5
visualize_landmark_depth: false
}
}
}
# Converts face joints to cyan circles.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:face_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:face_landmarks_joints_rd"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 0 g: 217 b: 231 }
connection_color { r: 0 g: 217 b: 231 }
thickness: 0.5
visualize_landmark_depth: false
}
}
}
# Concatenates all render data.
node {
calculator: "ConcatenateRenderDataVectorCalculator"
input_stream: "landmarks_render_data"
input_stream: "landmarks_background_joints_render_data"
input_stream: "landmarks_left_joints_render_data"
input_stream: "landmarks_right_joints_render_data"
# Left hand.
input_stream: "left_hand_landmarks_connections_rd"
input_stream: "left_hand_landmarks_joints_rd"
# Right hand.
input_stream: "right_hand_landmarks_connections_rd"
input_stream: "right_hand_landmarks_joints_rd"
# Face.
input_stream: "face_landmarks_connections_rd"
input_stream: "face_landmarks_joints_rd"
output_stream: "render_data_vector"
}
@@ -61,8 +61,8 @@ cc_library(
"//mediapipe/framework/port:opencv_imgproc",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"//mediapipe/graphs/object_detection_3d/calculators:box",
"//mediapipe/graphs/object_detection_3d/calculators:model_matrix_cc_proto",
"//mediapipe/modules/objectron/calculators:box",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
"@eigen_archive//:eigen",
@@ -25,8 +25,8 @@
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/graphs/instant_motion_tracking/calculators/transformations.h"
#include "mediapipe/graphs/object_detection_3d/calculators/box.h"
#include "mediapipe/graphs/object_detection_3d/calculators/model_matrix.pb.h"
#include "mediapipe/modules/objectron/calculators/box.h"
namespace mediapipe {
@@ -87,9 +87,9 @@ constexpr float kInitialZ = -10.0f;
class MatricesManagerCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
static mediapipe::Status GetContract(CalculatorContract* cc);
mediapipe::Status Open(CalculatorContext* cc) override;
mediapipe::Status Process(CalculatorContext* cc) override;
private:
// Device properties that will be preset by side packets
@@ -137,7 +137,7 @@ class MatricesManagerCalculator : public CalculatorBase {
REGISTER_CALCULATOR(MatricesManagerCalculator);
::mediapipe::Status MatricesManagerCalculator::GetContract(
mediapipe::Status MatricesManagerCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(cc->Inputs().HasTag(kAnchorsTag) &&
cc->Inputs().HasTag(kIMUMatrixTag) &&
@@ -162,20 +162,20 @@ REGISTER_CALCULATOR(MatricesManagerCalculator);
cc->InputSidePackets().Tag(kFOVSidePacketTag).Set<float>();
cc->InputSidePackets().Tag(kAspectRatioSidePacketTag).Set<float>();
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status MatricesManagerCalculator::Open(CalculatorContext* cc) {
mediapipe::Status MatricesManagerCalculator::Open(CalculatorContext* cc) {
cc->SetOffset(TimestampDiff(0));
// Set device properties from side packets
vertical_fov_radians_ =
cc->InputSidePackets().Tag(kFOVSidePacketTag).Get<float>();
aspect_ratio_ =
cc->InputSidePackets().Tag(kAspectRatioSidePacketTag).Get<float>();
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status MatricesManagerCalculator::Process(CalculatorContext* cc) {
mediapipe::Status MatricesManagerCalculator::Process(CalculatorContext* cc) {
// Define each object's model matrices
auto asset_matrices_gif =
std::make_unique<mediapipe::TimedModelMatrixProtoList>();
@@ -276,7 +276,7 @@ REGISTER_CALCULATOR(MatricesManagerCalculator);
.Get(cc->Outputs().GetId("MATRICES", 1))
.Add(asset_matrices_1.release(), cc->InputTimestamp());
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
// Using a specified rotation value in radians, generate a rotation matrix for
@@ -53,7 +53,7 @@ constexpr char kRenderDescriptorsTag[] = "RENDER_DATA";
class StickerManagerCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
static mediapipe::Status GetContract(CalculatorContract* cc) {
RET_CHECK(cc->Inputs().HasTag(kProtoDataString));
RET_CHECK(cc->Outputs().HasTag(kAnchorsTag) &&
cc->Outputs().HasTag(kUserRotationsTag) &&
@@ -66,15 +66,15 @@ class StickerManagerCalculator : public CalculatorBase {
cc->Outputs().Tag(kUserScalingsTag).Set<std::vector<UserScaling>>();
cc->Outputs().Tag(kRenderDescriptorsTag).Set<std::vector<int>>();
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status Open(CalculatorContext* cc) override {
mediapipe::Status Open(CalculatorContext* cc) override {
cc->SetOffset(TimestampDiff(0));
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status Process(CalculatorContext* cc) override {
mediapipe::Status Process(CalculatorContext* cc) override {
std::string sticker_proto_string =
cc->Inputs().Tag(kProtoDataString).Get<std::string>();
@@ -138,11 +138,11 @@ class StickerManagerCalculator : public CalculatorBase {
.At(cc->InputTimestamp()));
}
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status Close(CalculatorContext* cc) override {
return ::mediapipe::OkStatus();
mediapipe::Status Close(CalculatorContext* cc) override {
return mediapipe::OkStatus();
}
};
@@ -71,7 +71,7 @@ class TrackedAnchorManagerCalculator : public CalculatorBase {
std::vector<Anchor> previous_anchor_data_;
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
static mediapipe::Status GetContract(CalculatorContract* cc) {
RET_CHECK(cc->Inputs().HasTag(kAnchorsTag) &&
cc->Inputs().HasTag(kSentinelTag));
RET_CHECK(cc->Outputs().HasTag(kAnchorsTag) &&
@@ -91,18 +91,18 @@ class TrackedAnchorManagerCalculator : public CalculatorBase {
cc->Outputs().Tag(kCancelTag).Set<int>();
}
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status Open(CalculatorContext* cc) override {
return ::mediapipe::OkStatus();
mediapipe::Status Open(CalculatorContext* cc) override {
return mediapipe::OkStatus();
}
::mediapipe::Status Process(CalculatorContext* cc) override;
mediapipe::Status Process(CalculatorContext* cc) override;
};
REGISTER_CALCULATOR(TrackedAnchorManagerCalculator);
::mediapipe::Status TrackedAnchorManagerCalculator::Process(
mediapipe::Status TrackedAnchorManagerCalculator::Process(
CalculatorContext* cc) {
mediapipe::Timestamp timestamp = cc->InputTimestamp();
const int sticker_sentinel = cc->Inputs().Tag(kSentinelTag).Get<int>();
@@ -208,6 +208,6 @@ REGISTER_CALCULATOR(TrackedAnchorManagerCalculator);
.Tag(kBoxesOutputTag)
.Add(pos_boxes.release(), cc->InputTimestamp());
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
} // namespace mediapipe
@@ -89,7 +89,7 @@ float CalculateDepth(const NormalizedLandmark& center, float focal_length,
// }
class IrisToDepthCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
static mediapipe::Status GetContract(CalculatorContract* cc) {
cc->Inputs().Tag(kIrisTag).Set<NormalizedLandmarkList>();
cc->Inputs().Tag(kImageSizeTag).Set<std::pair<int, int>>();
@@ -111,12 +111,12 @@ class IrisToDepthCalculator : public CalculatorBase {
if (cc->Outputs().HasTag(kRightIrisDepthTag)) {
cc->Outputs().Tag(kRightIrisDepthTag).Set<float>();
}
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status Open(CalculatorContext* cc) override;
mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
mediapipe::Status Process(CalculatorContext* cc) override;
private:
float focal_length_pixels_ = -1.f;
@@ -134,7 +134,7 @@ class IrisToDepthCalculator : public CalculatorBase {
};
REGISTER_CALCULATOR(IrisToDepthCalculator);
::mediapipe::Status IrisToDepthCalculator::Open(CalculatorContext* cc) {
mediapipe::Status IrisToDepthCalculator::Open(CalculatorContext* cc) {
cc->SetOffset(TimestampDiff(0));
if (cc->InputSidePackets().HasTag(kFocalLengthPixelTag)) {
#if defined(__APPLE__)
@@ -155,13 +155,13 @@ REGISTER_CALCULATOR(IrisToDepthCalculator);
}
options_ = cc->Options<::mediapipe::IrisToDepthCalculatorOptions>();
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status IrisToDepthCalculator::Process(CalculatorContext* cc) {
mediapipe::Status IrisToDepthCalculator::Process(CalculatorContext* cc) {
// Only process if there's input landmarks.
if (cc->Inputs().Tag(kIrisTag).IsEmpty()) {
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
const auto& iris_landmarks =
@@ -220,7 +220,7 @@ REGISTER_CALCULATOR(IrisToDepthCalculator);
.At(cc->InputTimestamp()));
}
}
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
void IrisToDepthCalculator::GetLeftIris(const NormalizedLandmarkList& lds,
@@ -108,7 +108,7 @@ float CalculateDepth(const NormalizedLandmark& center, float focal_length,
// }
class IrisToRenderDataCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
static mediapipe::Status GetContract(CalculatorContract* cc) {
cc->Inputs().Tag(kIrisTag).Set<NormalizedLandmarkList>();
cc->Outputs().Tag(kRenderDataTag).Set<RenderData>();
cc->Inputs().Tag(kImageSizeTag).Set<std::pair<int, int>>();
@@ -119,12 +119,12 @@ class IrisToRenderDataCalculator : public CalculatorBase {
if (cc->Inputs().HasTag(kRightIrisDepthTag)) {
cc->Inputs().Tag(kRightIrisDepthTag).Set<float>();
}
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status Open(CalculatorContext* cc) override;
mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
mediapipe::Status Process(CalculatorContext* cc) override;
private:
void RenderIris(const NormalizedLandmarkList& iris_landmarks,
@@ -150,15 +150,15 @@ class IrisToRenderDataCalculator : public CalculatorBase {
};
REGISTER_CALCULATOR(IrisToRenderDataCalculator);
::mediapipe::Status IrisToRenderDataCalculator::Open(CalculatorContext* cc) {
mediapipe::Status IrisToRenderDataCalculator::Open(CalculatorContext* cc) {
cc->SetOffset(TimestampDiff(0));
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status IrisToRenderDataCalculator::Process(CalculatorContext* cc) {
mediapipe::Status IrisToRenderDataCalculator::Process(CalculatorContext* cc) {
// Only process if there's input landmarks.
if (cc->Inputs().Tag(kIrisTag).IsEmpty()) {
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
const auto& options =
cc->Options<::mediapipe::IrisToRenderDataCalculatorOptions>();
@@ -212,7 +212,7 @@ REGISTER_CALCULATOR(IrisToRenderDataCalculator);
cc->Outputs()
.Tag(kRenderDataTag)
.Add(render_data.release(), cc->InputTimestamp());
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
void IrisToRenderDataCalculator::AddTextRenderData(
@@ -215,28 +215,28 @@ constexpr int kEyeLandmarkIndicesInFaceLandmarks[] = {
//
class UpdateFaceLandmarksCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc) {
static mediapipe::Status GetContract(CalculatorContract* cc) {
cc->Inputs().Tag(kFaceLandmarksTag).Set<NormalizedLandmarkList>();
cc->Inputs().Tag(kNewEyeLandmarksTag).Set<NormalizedLandmarkList>();
cc->Outputs().Tag(kUpdatedFaceLandmarksTag).Set<NormalizedLandmarkList>();
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status Open(CalculatorContext* cc) {
mediapipe::Status Open(CalculatorContext* cc) {
cc->SetOffset(TimestampDiff(0));
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status Process(CalculatorContext* cc) override;
mediapipe::Status Process(CalculatorContext* cc) override;
};
REGISTER_CALCULATOR(UpdateFaceLandmarksCalculator);
::mediapipe::Status UpdateFaceLandmarksCalculator::Process(
mediapipe::Status UpdateFaceLandmarksCalculator::Process(
CalculatorContext* cc) {
if (cc->Inputs().Tag(kFaceLandmarksTag).IsEmpty() ||
cc->Inputs().Tag(kNewEyeLandmarksTag).IsEmpty()) {
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
const auto& face_landmarks =
cc->Inputs().Tag(kFaceLandmarksTag).Get<NormalizedLandmarkList>();
@@ -263,7 +263,7 @@ REGISTER_CALCULATOR(UpdateFaceLandmarksCalculator);
.Tag(kUpdatedFaceLandmarksTag)
.Add(refined_face_landmarks.release(), cc->InputTimestamp());
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
} // namespace mediapipe
+16 -8
View File
@@ -29,16 +29,11 @@ cc_library(
name = "mobile_calculators",
visibility = ["//visibility:public"],
deps = [
"//mediapipe/calculators/core:constant_side_packet_calculator",
"//mediapipe/calculators/core:flow_limiter_calculator",
"//mediapipe/calculators/core:gate_calculator",
"//mediapipe/calculators/core:merge_calculator",
"//mediapipe/calculators/core:previous_loopback_calculator",
"//mediapipe/calculators/image:image_cropping_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:annotations_to_model_matrices_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:gl_animation_overlay_calculator",
"//mediapipe/graphs/object_detection_3d/subgraphs:box_landmark_gpu",
"//mediapipe/graphs/object_detection_3d/subgraphs:object_detection_oid_v4_gpu",
"//mediapipe/modules/objectron:objectron_gpu",
],
)
@@ -51,8 +46,21 @@ cc_library(
"//mediapipe/gpu:gl_scaler_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:annotations_to_model_matrices_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:gl_animation_overlay_calculator",
"//mediapipe/graphs/object_detection_3d/subgraphs:objectron_detection_gpu",
"//mediapipe/graphs/object_detection_3d/subgraphs:objectron_tracking_gpu",
"//mediapipe/modules/objectron:objectron_detection_1stage_gpu",
"//mediapipe/modules/objectron:objectron_tracking_1stage_gpu",
],
)
cc_library(
name = "desktop_cpu_calculators",
visibility = ["//visibility:public"],
deps = [
"//mediapipe/calculators/tflite:tflite_model_calculator",
"//mediapipe/calculators/util:local_file_contents_calculator",
"//mediapipe/calculators/video:opencv_video_decoder_calculator",
"//mediapipe/calculators/video:opencv_video_encoder_calculator",
"//mediapipe/graphs/object_detection_3d/subgraphs:renderer_cpu",
"//mediapipe/modules/objectron:objectron_cpu",
],
)
@@ -18,43 +18,6 @@ licenses(["notice"])
package(default_visibility = ["//visibility:public"])
mediapipe_proto_library(
name = "object_proto",
srcs = ["object.proto"],
visibility = ["//visibility:public"],
)
mediapipe_proto_library(
name = "a_r_capture_metadata_proto",
srcs = ["a_r_capture_metadata.proto"],
visibility = ["//visibility:public"],
)
mediapipe_proto_library(
name = "annotation_proto",
srcs = ["annotation_data.proto"],
visibility = ["//visibility:public"],
deps = [
":a_r_capture_metadata_proto",
":object_proto",
],
)
mediapipe_proto_library(
name = "camera_parameters_proto",
srcs = ["camera_parameters.proto"],
visibility = ["//visibility:public"],
)
mediapipe_proto_library(
name = "frame_annotation_tracker_calculator_proto",
srcs = ["frame_annotation_tracker_calculator.proto"],
visibility = ["//visibility:public"],
deps = [
"//mediapipe/framework:calculator_proto",
],
)
mediapipe_proto_library(
name = "gl_animation_overlay_calculator_proto",
srcs = ["gl_animation_overlay_calculator.proto"],
@@ -64,32 +27,6 @@ mediapipe_proto_library(
],
)
mediapipe_proto_library(
name = "belief_decoder_config_proto",
srcs = ["belief_decoder_config.proto"],
visibility = ["//visibility:public"],
)
mediapipe_proto_library(
name = "tflite_tensors_to_objects_calculator_proto",
srcs = ["tflite_tensors_to_objects_calculator.proto"],
visibility = ["//visibility:public"],
deps = [
":belief_decoder_config_proto",
"//mediapipe/framework:calculator_proto",
],
)
mediapipe_proto_library(
name = "lift_2d_frame_annotation_to_3d_calculator_proto",
srcs = ["lift_2d_frame_annotation_to_3d_calculator.proto"],
visibility = ["//visibility:public"],
deps = [
":belief_decoder_config_proto",
"//mediapipe/framework:calculator_proto",
],
)
mediapipe_proto_library(
name = "annotations_to_model_matrices_calculator_proto",
srcs = ["annotations_to_model_matrices_calculator.proto"],
@@ -118,57 +55,11 @@ mediapipe_proto_library(
],
)
mediapipe_proto_library(
name = "frame_annotation_to_rect_calculator_proto",
srcs = ["frame_annotation_to_rect_calculator.proto"],
visibility = ["//visibility:public"],
deps = [
"//mediapipe/framework:calculator_proto",
],
)
mediapipe_proto_library(
name = "filter_detection_calculator_proto",
srcs = ["filter_detection_calculator.proto"],
visibility = ["//visibility:public"],
deps = [
"//mediapipe/framework:calculator_proto",
],
)
cc_library(
name = "box_util",
srcs = ["box_util.cc"],
hdrs = ["box_util.h"],
deps = [
"//mediapipe/framework/port:logging",
"//mediapipe/framework/port:opencv_core",
"//mediapipe/framework/port:opencv_imgproc",
"//mediapipe/util/tracking:box_tracker_cc_proto",
],
)
cc_library(
name = "frame_annotation_tracker",
srcs = ["frame_annotation_tracker.cc"],
hdrs = ["frame_annotation_tracker.h"],
deps = [
":annotation_cc_proto",
":box_util",
"//mediapipe/framework/port:integral_types",
"//mediapipe/framework/port:logging",
"//mediapipe/util/tracking:box_tracker_cc_proto",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_set",
],
)
cc_library(
name = "gl_animation_overlay_calculator",
srcs = ["gl_animation_overlay_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
":camera_parameters_cc_proto",
":gl_animation_overlay_calculator_cc_proto",
":model_matrix_cc_proto",
"//mediapipe/framework:calculator_framework",
@@ -176,164 +67,25 @@ cc_library(
"//mediapipe/framework/port:status",
"//mediapipe/gpu:gl_calculator_helper",
"//mediapipe/gpu:shader_util",
"//mediapipe/modules/objectron/calculators:camera_parameters_cc_proto",
"//mediapipe/util/android:asset_manager_util",
],
alwayslink = 1,
)
cc_library(
name = "decoder",
srcs = [
"decoder.cc",
],
hdrs = [
"decoder.h",
],
deps = [
":annotation_cc_proto",
":belief_decoder_config_cc_proto",
"//mediapipe/framework/port:logging",
"//mediapipe/framework/port:opencv_core",
"//mediapipe/framework/port:opencv_imgproc",
"//mediapipe/framework/port:status",
"@com_google_absl//absl/status",
"@eigen_archive//:eigen",
],
)
cc_library(
name = "tensor_util",
srcs = [
"tensor_util.cc",
],
hdrs = [
"tensor_util.h",
],
deps = [
"//mediapipe/framework/port:logging",
"//mediapipe/framework/port:opencv_core",
"@org_tensorflow//tensorflow/lite:framework",
],
)
cc_library(
name = "box",
srcs = [
"box.cc",
"model.cc",
],
hdrs = [
"box.h",
"model.h",
"types.h",
],
visibility = ["//visibility:public"],
deps = [
":annotation_cc_proto",
":object_cc_proto",
"//mediapipe/framework/port:logging",
"@eigen_archive//:eigen",
],
)
cc_library(
name = "frame_annotation_to_timed_box_list_calculator",
srcs = ["frame_annotation_to_timed_box_list_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
":annotation_cc_proto",
":box_util",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/port:opencv_core",
"//mediapipe/framework/port:opencv_imgproc",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"//mediapipe/util/tracking:box_tracker_cc_proto",
"@com_google_absl//absl/memory",
],
alwayslink = 1,
)
cc_library(
name = "frame_annotation_tracker_calculator",
srcs = ["frame_annotation_tracker_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
":annotation_cc_proto",
":frame_annotation_tracker",
":frame_annotation_tracker_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"//mediapipe/util/tracking:box_tracker_cc_proto",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/memory",
],
alwayslink = 1,
)
cc_library(
name = "tflite_tensors_to_objects_calculator",
srcs = ["tflite_tensors_to_objects_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
":annotation_cc_proto",
":belief_decoder_config_cc_proto",
":decoder",
":tensor_util",
":tflite_tensors_to_objects_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/deps:file_path",
"//mediapipe/framework/formats:detection_cc_proto",
"//mediapipe/framework/port:opencv_core",
"//mediapipe/framework/port:ret_check",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:span",
"@eigen_archive//:eigen",
"@org_tensorflow//tensorflow/lite:framework",
],
alwayslink = 1,
)
cc_library(
name = "lift_2d_frame_annotation_to_3d_calculator",
srcs = ["lift_2d_frame_annotation_to_3d_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
":annotation_cc_proto",
":belief_decoder_config_cc_proto",
":decoder",
":lift_2d_frame_annotation_to_3d_calculator_cc_proto",
":tensor_util",
":tflite_tensors_to_objects_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/deps:file_path",
"//mediapipe/framework/formats:detection_cc_proto",
"//mediapipe/framework/port:opencv_core",
"//mediapipe/framework/port:ret_check",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:span",
"@eigen_archive//:eigen",
"@org_tensorflow//tensorflow/lite:framework",
],
alwayslink = 1,
)
cc_library(
name = "annotations_to_model_matrices_calculator",
srcs = ["annotations_to_model_matrices_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
":annotation_cc_proto",
":annotations_to_model_matrices_calculator_cc_proto",
":box",
":model_matrix_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:calculator_options_cc_proto",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"//mediapipe/modules/objectron/calculators:annotation_cc_proto",
"//mediapipe/modules/objectron/calculators:box",
"//mediapipe/util:color_cc_proto",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
@@ -347,11 +99,11 @@ cc_library(
srcs = ["annotations_to_render_data_calculator.cc"],
visibility = ["//visibility:public"],
deps = [
":annotation_cc_proto",
":annotations_to_render_data_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework:calculator_options_cc_proto",
"//mediapipe/framework/port:ret_check",
"//mediapipe/modules/objectron/calculators:annotation_cc_proto",
"//mediapipe/util:color_cc_proto",
"//mediapipe/util:render_data_cc_proto",
"@com_google_absl//absl/memory",
@@ -359,76 +111,3 @@ cc_library(
],
alwayslink = 1,
)
cc_library(
name = "frame_annotation_to_rect_calculator",
srcs = ["frame_annotation_to_rect_calculator.cc"],
deps = [
":annotation_cc_proto",
":box",
":frame_annotation_to_rect_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/formats:rect_cc_proto",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"@com_google_absl//absl/memory",
"@eigen_archive//:eigen",
],
alwayslink = 1,
)
cc_library(
name = "landmarks_to_frame_annotation_calculator",
srcs = ["landmarks_to_frame_annotation_calculator.cc"],
deps = [
":annotation_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/formats:landmark_cc_proto",
"//mediapipe/framework/port:ret_check",
"//mediapipe/framework/port:status",
"@com_google_absl//absl/memory",
],
alwayslink = 1,
)
cc_library(
name = "filter_detection_calculator",
srcs = ["filter_detection_calculator.cc"],
deps = [
":filter_detection_calculator_cc_proto",
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/formats:detection_cc_proto",
"//mediapipe/framework/formats:location_data_cc_proto",
"//mediapipe/framework/port:logging",
"//mediapipe/framework/port:map_util",
"//mediapipe/framework/port:re2",
"//mediapipe/framework/port:status",
"@com_google_absl//absl/container:node_hash_set",
"@com_google_absl//absl/strings",
],
alwayslink = 1,
)
cc_test(
name = "box_util_test",
srcs = ["box_util_test.cc"],
deps = [
":box_util",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:opencv_core",
"//mediapipe/util/tracking:box_tracker_cc_proto",
],
)
cc_test(
name = "frame_annotation_tracker_test",
srcs = ["frame_annotation_tracker_test.cc"],
deps = [
":annotation_cc_proto",
":frame_annotation_tracker",
"//mediapipe/framework/port:gtest_main",
"//mediapipe/framework/port:logging",
"//mediapipe/util/tracking:box_tracker_cc_proto",
"@com_google_absl//absl/container:flat_hash_set",
],
)
@@ -1,551 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto2";
package mediapipe;
// Info about the camera characteristics used to capture images and depth data.
// See developer.apple.com/documentation/avfoundation/avcameracalibrationdata
// for more information.
message AVCameraCalibrationData {
// 3x3 row-major matrix relating a camera's internal properties to an ideal
// pinhole-camera model.
// See
// developer.apple.com/documentation/avfoundation/avcameracalibrationdata/2881135-intrinsicmatrix
// for detailed usage information.
repeated float intrinsic_matrix = 1 [packed = true];
// The image dimensions to which the intrinsic_matrix values are relative.
optional float intrinsic_matrix_reference_dimension_width = 2;
optional float intrinsic_matrix_reference_dimension_height = 3;
// 3x4 row-major matrix relating a camera's position and orientation to a
// world or scene coordinate system. Consists of a unitless 3x3 rotation
// matrix (R) on the left and a translation (t) 3x1 vector on the right. The
// translation vector's units are millimeters. For example:
//
// |r1,1 r2,1 r3,1 | t1|
// [R | t] = |r1,2 r2,2 r3,2 | t2|
// |r1,3 r2,3 r3,3 | t3|
//
// is stored as [r11, r21, r31, t1, r12, r22, r32, t2, ...]
//
// See
// developer.apple.com/documentation/avfoundation/avcameracalibrationdata/2881130-extrinsicmatrix?language=objc
// for more information.
repeated float extrinsic_matrix = 4 [packed = true];
// The size, in millimeters, of one image pixel.
optional float pixel_size = 5;
// A list of floating-point values describing radial distortions imparted by
// the camera lens, for use in rectifying camera images.
// See
// developer.apple.com/documentation/avfoundation/avcameracalibrationdata/2881129-lensdistortionlookuptable?language=objc
// for more information.
repeated float lens_distortion_lookup_values = 6 [packed = true];
// A list of floating-point values describing radial distortions for use in
// reapplying camera geometry to a rectified image.
// See
// developer.apple.com/documentation/avfoundation/avcameracalibrationdata/2881132-inverselensdistortionlookuptable?language=objc
// for more information.
repeated float inverse_lens_distortion_lookup_values = 7 [packed = true];
// The offset of the distortion center of the camera lens from the top-left
// corner of the image.
// See
// developer.apple.com/documentation/avfoundation/avcameracalibrationdata/2881131-lensdistortioncenter?language=objc
// for more information.
optional float lens_distortion_center_x = 8;
optional float lens_distortion_center_y = 9;
}
// Container for depth data information.
// See developer.apple.com/documentation/avfoundation/avdepthdata for more info.
message AVDepthData {
// PNG representation of the grayscale depth data map. See discussion about
// depth_data_map_original_minimum_value, below, for information about how
// to interpret the pixel values.
optional bytes depth_data_map = 1;
// Pixel format type of the original captured depth data.
// See
// developer.apple.com/documentation/corevideo/1563591-pixel_format_identifiers?language=objc
// for the complete list of possible pixel format types. This value represents
// a string for the associated OSType/FourCharCode.
optional string depth_data_type = 2;
// Indicates the general accuracy of the depth_data_map.
// See developer.apple.com/documentation/avfoundation/avdepthdataaccuracy for
// more information.
enum Accuracy {
UNDEFINED_ACCURACY = 0;
// Values in the depth map are usable for foreground/background separation
// but are not absolutely accurate in the physical world.
RELATIVE = 1;
// Values in the depth map are absolutely accurate in the physical world.
ABSOLUTE = 2;
}
optional Accuracy depth_data_accuracy = 3 [default = RELATIVE];
// Indicates whether the depth_data_map contains temporally smoothed data.
optional bool depth_data_filtered = 4;
// Quality of the depth_data_map.
enum Quality {
UNDEFINED_QUALITY = 0;
HIGH = 1;
LOW = 2;
}
optional Quality depth_data_quality = 5;
// Associated calibration data for the depth_data_map.
optional AVCameraCalibrationData camera_calibration_data = 6;
// The original range of values expressed by the depth_data_map, before
// grayscale normalization. For example, if the minimum and maximum values
// indicate a range of [0.5, 2.2], and the depth_data_type value indicates
// it was a depth map, then white pixels (255, 255, 255) will map to 0.5 and
// black pixels (0, 0, 0) will map to 2.2 with the grayscale range linearly
// interpolated inbetween. Conversely, if the depth_data_type value indicates
// it was a disparity map, then white pixels will map to 2.2 and black pixels
// will map to 0.5.
optional float depth_data_map_original_minimum_value = 7;
optional float depth_data_map_original_maximum_value = 8;
// The width of the depth buffer map.
optional int32 depth_data_map_width = 9;
// The height of the depth buffer map.
optional int32 depth_data_map_height = 10;
// The row-major flattened array of the depth buffer map pixels. This will be
// either a float32 or float16 byte array, depending on 'depth_data_type'.
optional bytes depth_data_map_raw_values = 11;
}
// Estimated scene lighting information associated with a captured video frame.
// See developer.apple.com/documentation/arkit/arlightestimate for more info.
message ARLightEstimate {
// The estimated intensity, in lumens, of ambient light throughout the scene.
optional double ambient_intensity = 1;
// The estimated color temperature, in degrees Kelvin, of ambient light
// throughout the scene.
optional double ambient_color_temperature = 2;
// Data describing the estimated lighting environment in all directions.
// Second-level spherical harmonics in separate red, green, and blue data
// planes. Thus, this buffer contains 3 sets of 9 coefficients, or a total of
// 27 values.
// See
// https://developer.apple.com/documentation/arkit/ardirectionallightestimate/2928222-sphericalharmonicscoefficients?language=objc
// for more information.
repeated float spherical_harmonics_coefficients = 3 [packed = true];
message DirectionVector {
optional float x = 1;
optional float y = 2;
optional float z = 3;
}
// A vector indicating the orientation of the strongest directional light
// source, normalized in the world-coordinate space.
// See
// https://developer.apple.com/documentation/arkit/ardirectionallightestimate/2928221-primarylightdirection?language=objc
// for more information;
optional DirectionVector primary_light_direction = 4;
// The estimated intensity, in lumens, of the strongest directional light
// source in the scene.
// See
// https://developer.apple.com/documentation/arkit/ardirectionallightestimate/2928219-primarylightintensity?language=objc
// for more information.
optional float primary_light_intensity = 5;
}
// Information about the camera position and imaging characteristics for a
// captured video frame.
// See developer.apple.com/documentation/arkit/arcamera for more information.
message ARCamera {
// The general quality of position tracking available when the camera captured
// a frame.
enum TrackingState {
UNDEFINED_TRACKING_STATE = 0;
// Camera position tracking is not available.
UNAVAILABLE = 1;
// Tracking is available, but the quality of results is questionable.
LIMITED = 2;
// Camera position tracking is providing optimal results.
NORMAL = 3;
}
optional TrackingState tracking_state = 1 [default = UNAVAILABLE];
// A possible diagnosis for limited position tracking quality as of when the
// frame was captured.
enum TrackingStateReason {
UNDEFINED_TRACKING_STATE_REASON = 0;
// The current tracking state is not limited.
NONE = 1;
// Not yet enough camera or motion data to provide tracking information.
INITIALIZING = 2;
// The device is moving too fast for accurate image-based position tracking.
EXCESSIVE_MOTION = 3;
// Not enough distinguishable features for image-based position tracking.
INSUFFICIENT_FEATURES = 4;
// Tracking is limited due to a relocalization in progress.
RELOCALIZING = 5;
}
optional TrackingStateReason tracking_state_reason = 2 [default = NONE];
// 4x4 row-major matrix expressing position and orientation of the camera in
// world coordinate space.
// See developer.apple.com/documentation/arkit/arcamera/2866108-transform for
// more information.
repeated float transform = 3 [packed = true];
// The orientation of the camera, expressed as roll, pitch, and yaw values.
message EulerAngles {
optional float roll = 1;
optional float pitch = 2;
optional float yaw = 3;
}
optional EulerAngles euler_angles = 4;
// The width and height, in pixels, of the captured camera image.
optional int32 image_resolution_width = 5;
optional int32 image_resolution_height = 6;
// 3x3 row-major matrix that converts between the 2D camera plane and 3D world
// coordinate space.
// See developer.apple.com/documentation/arkit/arcamera/2875730-intrinsics for
// usage information.
repeated float intrinsics = 7 [packed = true];
// 4x4 row-major transform matrix appropriate for rendering 3D content to
// match the image captured by the camera.
// See
// developer.apple.com/documentation/arkit/arcamera/2887458-projectionmatrix
// for usage information.
repeated float projection_matrix = 8 [packed = true];
// 4x4 row-major transform matrix appropriate for converting from world-space
// to camera space. Relativized for the captured_image orientation (i.e.
// UILandscapeOrientationRight).
// See
// https://developer.apple.com/documentation/arkit/arcamera/2921672-viewmatrixfororientation?language=objc
// for more information.
repeated float view_matrix = 9 [packed = true];
}
// Container for a 3D mesh describing face topology.
message ARFaceGeometry {
// Each vertex represents a 3D point in the face mesh, in the face coordinate
// space.
// See developer.apple.com/documentation/arkit/arfacegeometry/2928201-vertices
// for more information.
message Vertex {
optional float x = 1;
optional float y = 2;
optional float z = 3;
}
repeated Vertex vertices = 1;
// The number of elements in the vertices list.
optional int32 vertex_count = 2;
// Each texture coordinate represents UV texture coordinates for the vertex at
// the corresponding index in the vertices buffer.
// See
// developer.apple.com/documentation/arkit/arfacegeometry/2928203-texturecoordinates
// for more information.
message TextureCoordinate {
optional float u = 1;
optional float v = 2;
}
repeated TextureCoordinate texture_coordinates = 3;
// The number of elements in the texture_coordinates list.
optional int32 texture_coordinate_count = 4;
// Each integer value in this ordered list represents an index into the
// vertices and texture_coordinates lists. Each set of three indices
// identifies the vertices comprising a single triangle in the mesh. Each set
// of three indices forms a triangle, so the number of indices in the
// triangle_indices buffer is three times the triangle_count value.
// See
// developer.apple.com/documentation/arkit/arfacegeometry/2928199-triangleindices
// for more information.
repeated int32 triangle_indices = 5 [packed = true];
// The number of triangles described by the triangle_indices buffer.
// See
// developer.apple.com/documentation/arkit/arfacegeometry/2928207-trianglecount
// for more information.
optional int32 triangle_count = 6;
}
// Contains a list of blend shape entries wherein each item maps a specific
// blend shape location to its associated coefficient.
message ARBlendShapeMap {
message MapEntry {
// Identifier for the specific facial feature.
// See developer.apple.com/documentation/arkit/arblendshapelocation for a
// complete list of identifiers.
optional string blend_shape_location = 1;
// Indicates the current position of the feature relative to its neutral
// configuration, ranging from 0.0 (neutral) to 1.0 (maximum movement).
optional float blend_shape_coefficient = 2;
}
repeated MapEntry entries = 1;
}
// Information about the pose, topology, and expression of a detected face.
// See developer.apple.com/documentation/arkit/arfaceanchor for more info.
message ARFaceAnchor {
// A coarse triangle mesh representing the topology of the detected face.
optional ARFaceGeometry geometry = 1;
// A map of named coefficients representing the detected facial expression in
// terms of the movement of specific facial features.
optional ARBlendShapeMap blend_shapes = 2;
// 4x4 row-major matrix encoding the position, orientation, and scale of the
// anchor relative to the world coordinate space.
// See
// https://developer.apple.com/documentation/arkit/aranchor/2867981-transform?language=objc
// for more information.
repeated float transform = 3;
// Indicates whether the anchor's transform is valid. Frames that have a face
// anchor with this value set to NO should probably be ignored.
optional bool is_tracked = 4;
}
// Container for a 3D mesh.
message ARPlaneGeometry {
message Vertex {
optional float x = 1;
optional float y = 2;
optional float z = 3;
}
// Each texture coordinate represents UV texture coordinates for the vertex at
// the corresponding index in the vertices buffer.
// See
// https://developer.apple.com/documentation/arkit/arfacegeometry/2928203-texturecoordinates
// for more information.
message TextureCoordinate {
optional float u = 1;
optional float v = 2;
}
// A buffer of vertex positions for each point in the plane mesh.
repeated Vertex vertices = 1;
// The number of elements in the vertices buffer.
optional int32 vertex_count = 2;
// A buffer of texture coordinate values for each point in the plane mesh.
repeated TextureCoordinate texture_coordinates = 3;
// The number of elements in the texture_coordinates buffer.
optional int32 texture_coordinate_count = 4;
// Each integer value in this ordered list represents an index into the
// vertices and texture_coordinates lists. Each set of three indices
// identifies the vertices comprising a single triangle in the mesh. Each set
// of three indices forms a triangle, so the number of indices in the
// triangle_indices buffer is three times the triangle_count value.
// See
// https://developer.apple.com/documentation/arkit/arplanegeometry/2941051-triangleindices
// for more information.
repeated int32 triangle_indices = 5 [packed = true];
// Each set of three indices forms a triangle, so the number of indices in the
// triangle_indices buffer is three times the triangle_count value.
// See
// https://developer.apple.com/documentation/arkit/arplanegeometry/2941058-trianglecount
// for more information.
optional int32 triangle_count = 6;
// Each value in this buffer represents the position of a vertex along the
// boundary polygon of the estimated plane. The owning plane anchor's
// transform matrix defines the coordinate system for these points.
// See
// https://developer.apple.com/documentation/arkit/arplanegeometry/2941052-boundaryvertices
// for more information.
repeated Vertex boundary_vertices = 7;
// The number of elements in the boundary_vertices buffer.
optional int32 boundary_vertex_count = 8;
}
// Information about the position and orientation of a real-world flat surface.
// See https://developer.apple.com/documentation/arkit/arplaneanchor for more
// information.
message ARPlaneAnchor {
enum Alignment {
UNDEFINED = 0;
// The plane is perpendicular to gravity.
HORIZONTAL = 1;
// The plane is parallel to gravity.
VERTICAL = 2;
}
// Wrapper for a 3D point / vector within the plane. See extent and center
// values for more information.
message PlaneVector {
optional float x = 1;
optional float y = 2;
optional float z = 3;
}
enum PlaneClassification {
NONE = 0;
WALL = 1;
FLOOR = 2;
CEILING = 3;
TABLE = 4;
SEAT = 5;
}
// The classification status for the plane.
enum PlaneClassificationStatus {
// The classfication process for the plane anchor has completed but the
// result is inconclusive.
UNKNOWN = 0;
// No classication information can be provided (set on error or if the
// device does not support plane classification).
UNAVAILABLE = 1;
// The classification process has not completed.
UNDETERMINED = 2;
// The classfication process for the plane anchor has completed.
KNOWN = 3;
}
// The ID of the plane.
optional string identifier = 1;
// 4x4 row-major matrix encoding the position, orientation, and scale of the
// anchor relative to the world coordinate space.
// See
// https://developer.apple.com/documentation/arkit/aranchor/2867981-transform
// for more information.
repeated float transform = 2;
// The general orientation of the detected plane with respect to gravity.
optional Alignment alignment = 3;
// A coarse triangle mesh representing the general shape of the detected
// plane.
optional ARPlaneGeometry geometry = 4;
// The center point of the plane relative to its anchor position.
// Although the type of this property is a 3D vector, a plane anchor is always
// two-dimensional, and is always positioned in only the x and z directions
// relative to its transform position. (That is, the y-component of this
// vector is always zero.)
// See
// https://developer.apple.com/documentation/arkit/arplaneanchor/2882056-center
// for more information.
optional PlaneVector center = 5;
// The estimated width and length of the detected plane.
// See
// https://developer.apple.com/documentation/arkit/arplaneanchor/2882055-extent
// for more information.
optional PlaneVector extent = 6;
// A Boolean value that indicates whether plane classification is available on
// the current device. On devices without plane classification support, all
// plane anchors report a classification value of NONE
// and a classification_status value of UNAVAILABLE.
optional bool classification_supported = 7;
// A general characterization of what kind of real-world surface the plane
// anchor represents.
// See
// https://developer.apple.com/documentation/arkit/arplaneanchor/2990936-classification
// for more information.
optional PlaneClassification classification = 8;
// The current state of ARKit's process for classifying the plane anchor.
// When this property's value is KNOWN, the classification property represents
// ARKit's characterization of the real-world surface corresponding to the
// plane anchor.
// See
// https://developer.apple.com/documentation/arkit/arplaneanchor/2990937-classificationstatus
// for more information.
optional PlaneClassificationStatus classification_status = 9;
}
// A collection of points in the world coordinate space.
// See https://developer.apple.com/documentation/arkit/arpointcloud for more
// information.
message ARPointCloud {
message Point {
optional float x = 1;
optional float y = 2;
optional float z = 3;
}
// The number of points in the cloud.
optional int32 count = 1;
// The list of detected points.
repeated Point point = 2;
// A list of unique identifiers corresponding to detected feature points.
// Each identifier in this list corresponds to the point at the same index
// in the points array.
repeated int64 identifier = 3 [packed = true];
}
// Video image and face position tracking information.
// See developer.apple.com/documentation/arkit/arframe for more information.
message ARFrame {
// The timestamp for the frame.
optional double timestamp = 1;
// The depth data associated with the frame. Not all frames have depth data.
optional AVDepthData depth_data = 2;
// The depth data object timestamp associated with the frame. May differ from
// the frame timestamp value. Is only set when the frame has depth_data.
optional double depth_data_timestamp = 3;
// Camera information associated with the frame.
optional ARCamera camera = 4;
// Light information associated with the frame.
optional ARLightEstimate light_estimate = 5;
// Face anchor information associated with the frame. Not all frames have an
// active face anchor.
optional ARFaceAnchor face_anchor = 6;
// Plane anchors associated with the frame. Not all frames have a plane
// anchor. Plane anchors and face anchors are mutually exclusive.
repeated ARPlaneAnchor plane_anchor = 7;
// The current intermediate results of the scene analysis used to perform
// world tracking.
// See
// https://developer.apple.com/documentation/arkit/arframe/2887449-rawfeaturepoints
// for more information.
optional ARPointCloud raw_feature_points = 8;
}
@@ -1,92 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package mediapipe;
import "mediapipe/graphs/object_detection_3d/calculators/a_r_capture_metadata.proto";
import "mediapipe/graphs/object_detection_3d/calculators/object.proto";
// Projection of a 3D point on an image, and its metric depth.
message NormalizedPoint2D {
// x-y position of the 2d keypoint in the image coordinate system.
// u,v \in [0, 1], where top left corner is (0, 0) and the bottom-right corner
// is (1, 1).
float x = 1;
float y = 2;
// The depth of the point in the camera coordinate system (in meters).
float depth = 3;
}
// The 3D point in the camera coordinate system, the scales are in meters.
message Point3D {
float x = 1;
float y = 2;
float z = 3;
}
message AnnotatedKeyPoint {
int32 id = 1;
Point3D point_3d = 2;
NormalizedPoint2D point_2d = 3;
}
message ObjectAnnotation {
// Reference to the object identifier in ObjectInstance.
int32 object_id = 1;
// For each objects, list all the annotated keypoints here.
// E.g. for bounding-boxes, we have 8 keypoints, hands = 21 keypoints, etc.
// These normalized points are the projection of the Object's 3D keypoint
// on the current frame's camera poses.
repeated AnnotatedKeyPoint keypoints = 2;
// Visibiity of this annotation in a frame.
float visibility = 3;
}
message FrameAnnotation {
// Unique frame id, corresponds to images.
int32 frame_id = 1;
// List of the annotated objects in this frame. Depending on how many object
// are observable in this frame, we might have non or as much as
// sequence.objects_size() annotations.
repeated ObjectAnnotation annotations = 2;
// Information about the camera transformation (in the world coordinate) and
// imaging characteristics for a captured video frame.
ARCamera camera = 3;
// The timestamp for the frame.
double timestamp = 4;
// Plane center and normal in camera frame.
repeated float plane_center = 5;
repeated float plane_normal = 6;
}
// The sequence protocol contains the annotation data for the entire video clip.
message Sequence {
// List of all the annotated 3D objects in this sequence in the world
// Coordinate system. Given the camera poses of each frame (also in the
// world-coordinate) these objects bounding boxes can be projected to each
// frame to get the per-frame annotation (i.e. image_annotation below).
repeated Object objects = 1;
// List of annotated data per each frame in sequence + frame information.
repeated FrameAnnotation frame_annotations = 2;
}
@@ -24,10 +24,10 @@
#include "mediapipe/framework/calculator_options.pb.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotations_to_model_matrices_calculator.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/box.h"
#include "mediapipe/graphs/object_detection_3d/calculators/model_matrix.pb.h"
#include "mediapipe/modules/objectron/calculators/annotation_data.pb.h"
#include "mediapipe/modules/objectron/calculators/box.h"
#include "mediapipe/util/color.pb.h"
namespace mediapipe {
@@ -66,14 +66,14 @@ class AnnotationsToModelMatricesCalculator : public CalculatorBase {
AnnotationsToModelMatricesCalculator& operator=(
const AnnotationsToModelMatricesCalculator&) = delete;
static ::mediapipe::Status GetContract(CalculatorContract* cc);
static mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
mediapipe::Status Process(CalculatorContext* cc) override;
private:
::mediapipe::Status GetModelMatricesForAnnotations(
mediapipe::Status GetModelMatricesForAnnotations(
const FrameAnnotation& annotations,
TimedModelMatrixProtoList* model_matrix_list);
@@ -83,7 +83,7 @@ class AnnotationsToModelMatricesCalculator : public CalculatorBase {
};
REGISTER_CALCULATOR(AnnotationsToModelMatricesCalculator);
::mediapipe::Status AnnotationsToModelMatricesCalculator::GetContract(
mediapipe::Status AnnotationsToModelMatricesCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(cc->Inputs().HasTag(kAnnotationTag)) << "No input stream found.";
if (cc->Inputs().HasTag(kAnnotationTag)) {
@@ -101,10 +101,10 @@ REGISTER_CALCULATOR(AnnotationsToModelMatricesCalculator);
if (cc->InputSidePackets().HasTag("MODEL_TRANSFORMATION")) {
cc->InputSidePackets().Tag("MODEL_TRANSFORMATION").Set<float[]>();
}
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status AnnotationsToModelMatricesCalculator::Open(
mediapipe::Status AnnotationsToModelMatricesCalculator::Open(
CalculatorContext* cc) {
RET_CHECK(cc->Inputs().HasTag(kAnnotationTag));
@@ -131,10 +131,10 @@ REGISTER_CALCULATOR(AnnotationsToModelMatricesCalculator);
model_transformation_.setIdentity();
}
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status AnnotationsToModelMatricesCalculator::Process(
mediapipe::Status AnnotationsToModelMatricesCalculator::Process(
CalculatorContext* cc) {
auto model_matrices = std::make_unique<TimedModelMatrixProtoList>();
@@ -142,22 +142,21 @@ REGISTER_CALCULATOR(AnnotationsToModelMatricesCalculator);
cc->Inputs().Tag(kAnnotationTag).Get<FrameAnnotation>();
if (!GetModelMatricesForAnnotations(annotations, model_matrices.get()).ok()) {
return ::mediapipe::InvalidArgumentError(
"Error in GetModelMatricesForBoxes");
return mediapipe::InvalidArgumentError("Error in GetModelMatricesForBoxes");
}
cc->Outputs()
.Tag(kModelMatricesTag)
.Add(model_matrices.release(), cc->InputTimestamp());
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status
mediapipe::Status
AnnotationsToModelMatricesCalculator::GetModelMatricesForAnnotations(
const FrameAnnotation& annotations,
TimedModelMatrixProtoList* model_matrix_list) {
if (model_matrix_list == nullptr) {
return ::mediapipe::InvalidArgumentError("model_matrix_list is nullptr");
return mediapipe::InvalidArgumentError("model_matrix_list is nullptr");
}
model_matrix_list->clear_model_matrix();
@@ -217,7 +216,7 @@ AnnotationsToModelMatricesCalculator::GetModelMatricesForAnnotations(
}
}
}
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
} // namespace mediapipe
@@ -18,8 +18,8 @@
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/calculator_options.pb.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotations_to_render_data_calculator.pb.h"
#include "mediapipe/modules/objectron/calculators/annotation_data.pb.h"
#include "mediapipe/util/color.pb.h"
#include "mediapipe/util/render_data.pb.h"
@@ -98,11 +98,11 @@ class AnnotationsToRenderDataCalculator : public CalculatorBase {
AnnotationsToRenderDataCalculator& operator=(
const AnnotationsToRenderDataCalculator&) = delete;
static ::mediapipe::Status GetContract(CalculatorContract* cc);
static mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
mediapipe::Status Process(CalculatorContext* cc) override;
private:
static void SetRenderAnnotationColorThickness(
@@ -134,7 +134,7 @@ class AnnotationsToRenderDataCalculator : public CalculatorBase {
};
REGISTER_CALCULATOR(AnnotationsToRenderDataCalculator);
::mediapipe::Status AnnotationsToRenderDataCalculator::GetContract(
mediapipe::Status AnnotationsToRenderDataCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(cc->Inputs().HasTag(kAnnotationTag)) << "No input stream found.";
if (cc->Inputs().HasTag(kAnnotationTag)) {
@@ -142,18 +142,18 @@ REGISTER_CALCULATOR(AnnotationsToRenderDataCalculator);
}
cc->Outputs().Tag(kRenderDataTag).Set<RenderData>();
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status AnnotationsToRenderDataCalculator::Open(
mediapipe::Status AnnotationsToRenderDataCalculator::Open(
CalculatorContext* cc) {
cc->SetOffset(TimestampDiff(0));
options_ = cc->Options<AnnotationsToRenderDataCalculatorOptions>();
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
::mediapipe::Status AnnotationsToRenderDataCalculator::Process(
mediapipe::Status AnnotationsToRenderDataCalculator::Process(
CalculatorContext* cc) {
auto render_data = absl::make_unique<RenderData>();
bool visualize_depth = options_.visualize_landmark_depth();
@@ -215,7 +215,7 @@ REGISTER_CALCULATOR(AnnotationsToRenderDataCalculator);
.Tag(kRenderDataTag)
.Add(render_data.release(), cc->InputTimestamp());
return ::mediapipe::OkStatus();
return mediapipe::OkStatus();
}
void AnnotationsToRenderDataCalculator::AddConnectionToRenderData(
@@ -1,38 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto2";
package mediapipe;
message BeliefDecoderConfig {
optional float heatmap_threshold = 1 [default = 0.9];
// Maximum distance in pixels between two local max heatmap values.
optional float local_max_distance = 2 [default = 10.0];
// Coefficient of offset_scale.
// offset_scale = offset_scale_coef * min(rows, cols).
// offset_scale is used to multiply the offset predictions from the network.
optional float offset_scale_coef = 3 [default = 0.5, deprecated = true];
// The radius for vertex voting. Use no voting if the radius is less than or
// euqal to 1. Example: 10.
optional int32 voting_radius = 4;
// The number of pixels to determine whether two points are the same.
// Example: 5 (voting_radius / 2).
optional int32 voting_allowance = 5;
// The threshold of beliefs, with which the points can vote. Example: 0.2.
optional float voting_threshold = 6;
}
@@ -1,255 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/graphs/object_detection_3d/calculators/box.h"
#include "Eigen/src/Core/util/Constants.h"
#include "mediapipe/framework/port/logging.h"
namespace mediapipe {
namespace {
constexpr int kFrontFaceId = 4;
constexpr int kTopFaceId = 2;
constexpr int kNumKeypoints = 8 + 1;
constexpr int kNumberOfAxis = 3;
constexpr int kEdgesPerAxis = 4;
} // namespace
Box::Box(const std::string& category)
: Model(kBoundingBox, kNumKeypoints, category),
bounding_box_(kNumKeypoints) {
transformation_.setIdentity();
scale_ << 0.1, 0.1, 0.1;
// The vertices are ordered according to the left-hand rule, so the normal
// vector of each face will point inward the box.
faces_.push_back({5, 6, 8, 7}); // +x on yz plane
faces_.push_back({1, 3, 4, 2}); // -x on yz plane
faces_.push_back({3, 7, 8, 4}); // +y on xz plane = top
faces_.push_back({1, 2, 6, 5}); // -y on xz plane
faces_.push_back({2, 4, 8, 6}); // +z on xy plane = front
faces_.push_back({1, 5, 7, 3}); // -z on xy plane
// Add the edges in the cube, they are sorted according to axis (x-y-z).
edges_.push_back({1, 5});
edges_.push_back({2, 6});
edges_.push_back({3, 7});
edges_.push_back({4, 8});
edges_.push_back({1, 3});
edges_.push_back({5, 7});
edges_.push_back({2, 4});
edges_.push_back({6, 8});
edges_.push_back({1, 2});
edges_.push_back({3, 4});
edges_.push_back({5, 6});
edges_.push_back({7, 8});
Update();
}
void Box::Update() {
// Compute the eight vertices of the bounding box from Box's parameters
auto w = scale_[0] / 2.f;
auto h = scale_[1] / 2.f;
auto d = scale_[2] / 2.f;
// Define the local coordinate system, w.r.t. the center of the boxs
bounding_box_[0] << 0., 0., 0.;
bounding_box_[1] << -w, -h, -d;
bounding_box_[2] << -w, -h, +d;
bounding_box_[3] << -w, +h, -d;
bounding_box_[4] << -w, +h, +d;
bounding_box_[5] << +w, -h, -d;
bounding_box_[6] << +w, -h, +d;
bounding_box_[7] << +w, +h, -d;
bounding_box_[8] << +w, +h, +d;
// Convert to world coordinate system
for (int i = 0; i < kNumKeypoints; ++i) {
bounding_box_[i] =
transformation_.topLeftCorner<3, 3>() * bounding_box_[i] +
transformation_.col(3).head<3>();
}
}
void Box::Adjust(const std::vector<float>& variables) {
Eigen::Vector3f translation;
translation << variables[0], variables[1], variables[2];
SetTranslation(translation);
const float roll = variables[3];
const float pitch = variables[4];
const float yaw = variables[5];
SetRotation(roll, pitch, yaw);
Eigen::Vector3f scale;
scale << variables[6], variables[7], variables[8];
SetScale(scale);
Update();
}
float* Box::GetVertex(size_t vertex_id) {
CHECK_LT(vertex_id, kNumKeypoints);
return bounding_box_[vertex_id].data();
}
const float* Box::GetVertex(size_t vertex_id) const {
CHECK_LT(vertex_id, kNumKeypoints);
return bounding_box_[vertex_id].data();
}
bool Box::InsideTest(const Eigen::Vector3f& point, int check_axis) const {
const float* v0 = GetVertex(1);
const float* v1 = GetVertex(2);
const float* v2 = GetVertex(3);
const float* v4 = GetVertex(5);
switch (check_axis) {
case 1:
return (v0[0] <= point[0] && point[0] <= v1[0]); // X-axis
case 2:
return (v0[1] <= point[1] && point[1] <= v2[1]); // Y-axis
case 3:
return (v0[2] <= point[2] && point[2] <= v4[2]); // Z-axis
default:
return false;
}
}
void Box::Deserialize(const Object& obj) {
CHECK_EQ(obj.keypoints_size(), kNumKeypoints);
Model::Deserialize(obj);
}
void Box::Serialize(Object* obj) {
Model::Serialize(obj);
obj->set_type(Object::BOUNDING_BOX);
std::vector<Vector3f> local_bounding_box(9);
// Define the local coordinate system, w.r.t. the center of the boxs
local_bounding_box[0] << 0., 0., 0.;
local_bounding_box[1] << -0.5, -0.5, -0.5;
local_bounding_box[2] << -0.5, -0.5, +0.5;
local_bounding_box[3] << -0.5, +0.5, -0.5;
local_bounding_box[4] << -0.5, +0.5, +0.5;
local_bounding_box[5] << +0.5, -0.5, -0.5;
local_bounding_box[6] << +0.5, -0.5, +0.5;
local_bounding_box[7] << +0.5, +0.5, -0.5;
local_bounding_box[8] << +0.5, +0.5, +0.5;
for (int i = 0; i < kNumKeypoints; ++i) {
KeyPoint* keypoint = obj->add_keypoints();
keypoint->set_x(local_bounding_box[i][0]);
keypoint->set_y(local_bounding_box[i][1]);
keypoint->set_z(local_bounding_box[i][2]);
keypoint->set_confidence_radius(0.);
}
}
const Face& Box::GetFrontFace() const { return faces_[kFrontFaceId]; }
const Face& Box::GetTopFace() const { return faces_[kTopFaceId]; }
std::pair<Vector3f, Vector3f> Box::GetGroundPlane() const {
const Vector3f gravity = Vector3f(0., 1., 0.);
int ground_plane_id = 0;
float ground_plane_error = 10.0;
auto get_face_center = [&](const Face& face) {
Vector3f center = Vector3f::Zero();
for (const int vertex_id : face) {
center += Map<const Vector3f>(GetVertex(vertex_id));
}
center /= face.size();
return center;
};
auto get_face_normal = [&](const Face& face, const Vector3f& center) {
Vector3f v1 = Map<const Vector3f>(GetVertex(face[0])) - center;
Vector3f v2 = Map<const Vector3f>(GetVertex(face[1])) - center;
Vector3f normal = v1.cross(v2);
return normal;
};
// The ground plane is defined as a plane aligned with gravity.
// gravity is the (0, 1, 0) vector in the world coordinate system.
const auto& faces = GetFaces();
for (int face_id = 0; face_id < faces.size(); face_id += 2) {
const auto& face = faces[face_id];
Vector3f center = get_face_center(face);
Vector3f normal = get_face_normal(face, center);
Vector3f w = gravity.cross(normal);
const float w_sq_norm = w.squaredNorm();
if (w_sq_norm < ground_plane_error) {
ground_plane_error = w_sq_norm;
ground_plane_id = face_id;
}
}
Vector3f center = get_face_center(faces[ground_plane_id]);
Vector3f normal = get_face_normal(faces[ground_plane_id], center);
// For each face, we also have a parallel face that it's normal is also
// aligned with gravity vector. We pick the face with lower height (y-value).
// The parallel to face 0 is 1, face 2 is 3, and face 4 is 5.
int parallel_face_id = ground_plane_id + 1;
const auto& parallel_face = faces[parallel_face_id];
Vector3f parallel_face_center = get_face_center(parallel_face);
Vector3f parallel_face_normal =
get_face_normal(parallel_face, parallel_face_center);
if (parallel_face_center[1] < center[1]) {
center = parallel_face_center;
normal = parallel_face_normal;
}
return {center, normal};
}
template <typename T>
void Box::Fit(const std::vector<T>& vertices) {
CHECK_EQ(vertices.size(), kNumKeypoints);
scale_.setZero();
// The scale would remain invariant under rotation and translation.
// We can safely estimate the scale from the oriented box.
for (int axis = 0; axis < kNumberOfAxis; ++axis) {
for (int edge_id = 0; edge_id < kEdgesPerAxis; ++edge_id) {
// The edges are stored in quadruples according to each axis
const std::array<int, 2>& edge = edges_[axis * kEdgesPerAxis + edge_id];
scale_[axis] += (vertices[edge[0]] - vertices[edge[1]]).norm();
}
scale_[axis] /= kEdgesPerAxis;
}
// Create a scaled axis-aligned box
transformation_.setIdentity();
Update();
using MatrixN3_RM = Eigen::Matrix<float, kNumKeypoints, 3, Eigen::RowMajor>;
Eigen::Map<const MatrixN3_RM> v(vertices[0].data());
Eigen::Map<const MatrixN3_RM> system(bounding_box_[0].data());
auto system_h = system.rowwise().homogeneous().eval();
auto system_g = system_h.colPivHouseholderQr();
auto solution = system_g.solve(v).eval();
transformation_.topLeftCorner<3, 4>() = solution.transpose();
Update();
}
template void Box::Fit<Vector3f>(const std::vector<Vector3f>&);
template void Box::Fit<Map<Vector3f>>(const std::vector<Map<Vector3f>>&);
template void Box::Fit<Map<const Vector3f>>(
const std::vector<Map<const Vector3f>>&);
} // namespace mediapipe
@@ -1,132 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_BOX_H_
#define MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_BOX_H_
#include <vector>
#include "mediapipe/graphs/object_detection_3d/calculators/model.h"
namespace mediapipe {
// Model for the bounding box in 3D
// The box has 9 degrees of freedom, which uniquely defines 8 keypoints in the
// fixed world-coordinate system.
//
// The 8 keypoints are defined as follows
//
// kp-id axis
// 0 000 ---
// 1 001 --+
// 2 010 -+-
// 3 011 -++
// 4 100 +--
// 5 101 +-+
// 6 110 ++-
// 7 111 +++
//
// where xyz means positive or negative vector along the axis where the center
// of the box is the origin. The resulting bounding box is
//
// x x
// 0 + + + + + + + + 4 .-------
// +\ +\ |\
// + \ y + \ z | \ y
// + \ + \ | \
// + 2 + + + + + + + + 6
// z + + + +
// + + + +
// + + C + +
// + + + +
// 1 + + + + + + + + 5 +
// \ + \ +
// \ + \ +
// \+ \+
// 3 + + + + + + + + 7
//
// World coordinate system: +y is up (aligned with gravity),
// +z is toward the user, +x follows right hand rule.
// The front face is defined as +z axis on xy plane.
// The top face is defined as +y axis on xz plane.
//
class Box : public Model {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
explicit Box(const std::string& category);
~Box() override = default;
bool InsideTest(const Vector3f& point, int check_axis) const;
const std::vector<Face>& GetFaces() const { return faces_; }
const Face& GetFace(size_t face_id) const { return faces_[face_id]; }
const std::vector<std::array<int, 2>>& GetEdges() const { return edges_; }
const std::array<int, 2>& GetEdge(size_t edge_id) const {
return edges_[edge_id];
}
// Returns the keypoints for the front face of the box.
// The front face is defind as a face with +z normal vector on xy plane
// In Box's c'tor, the top face is set to {1, 3, 7, 5}
const Face& GetFrontFace() const;
// Returns the keypoints for the top face of the box.
// The top face is defind as a face with +z normal vector on xy plane
// In Box's c'tor, the top face is set to {1, 3, 7, 5}
const Face& GetTopFace() const;
void Update() override;
void Adjust(const std::vector<float>& variables) override;
float* GetVertex(size_t vertex_id) override;
const float* GetVertex(size_t vertex_id) const override;
void Deserialize(const Object& obj) override;
void Serialize(Object* obj) override;
// Computes the plane center and the normal vector for the plane the object
// is sitting on in the world cooordinate system. The normal vector is roughly
// aligned with gravity.
std::pair<Vector3f, Vector3f> GetGroundPlane() const;
// Estimates a box 9-dof parameters from the given vertices. Directly computes
// the scale of the box, then solves for orientation and translation.
// Expects a std::vector of size 9 of a Eigen::Vector3f or mapped Vector3f.
// If mapping proto messages, we recommend to use the Map<const Vector3f>.
// For example:
//
// using T = Map<const Vector3f>;
// std::vector<T> vertices;
// for (const auto& point : message) { // point is a repeated float message.
// T p(point.data());
// vertices.emplace_back(p);
// }
// box.Fit<T>(vertices);
//
// The Points must be arranged as 1 + 8 (center keypoint followed by 8 box
// vertices) vector. This function will overwrite the scale and transformation
// properties of the class.
template <typename T = Eigen::Map<const Vector3f>>
void Fit(const std::vector<T>& vertices);
private:
std::vector<Face> faces_;
std::vector<std::array<int, 2>> edges_;
std::vector<Vector3f> bounding_box_;
};
} // namespace mediapipe
#endif // MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_BOX_H_
@@ -1,153 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/graphs/object_detection_3d/calculators/box_util.h"
#include <math.h>
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/framework/port/opencv_core_inc.h"
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
#include "mediapipe/util/tracking/box_tracker.pb.h"
namespace mediapipe {
void ComputeBoundingRect(const std::vector<cv::Point2f>& points,
mediapipe::TimedBoxProto* box) {
CHECK(box != nullptr);
float top = 1.0f;
float bottom = 0.0f;
float left = 1.0f;
float right = 0.0f;
for (const auto& point : points) {
top = std::min(top, point.y);
bottom = std::max(bottom, point.y);
left = std::min(left, point.x);
right = std::max(right, point.x);
}
box->set_top(top);
box->set_bottom(bottom);
box->set_left(left);
box->set_right(right);
// We are currently only doing axis aligned bounding box. If we need to
// compute rotated bounding box, then we need the original image aspect ratio,
// map back to original image space, compute cv::convexHull, then for each
// edge of the hull, rotate according to edge orientation, find the box.
box->set_rotation(0.0f);
}
float ComputeBoxIoU(const TimedBoxProto& box1, const TimedBoxProto& box2) {
cv::Point2f box1_center((box1.left() + box1.right()) * 0.5f,
(box1.top() + box1.bottom()) * 0.5f);
cv::Size2f box1_size(box1.right() - box1.left(), box1.bottom() - box1.top());
cv::RotatedRect rect1(box1_center, box1_size,
-box1.rotation() * 180.0f / M_PI);
cv::Point2f box2_center((box2.left() + box2.right()) * 0.5f,
(box2.top() + box2.bottom()) * 0.5f);
cv::Size2f box2_size(box2.right() - box2.left(), box2.bottom() - box2.top());
cv::RotatedRect rect2(box2_center, box2_size,
-box2.rotation() * 180.0f / M_PI);
std::vector<cv::Point2f> intersections_unsorted;
std::vector<cv::Point2f> intersections;
cv::rotatedRectangleIntersection(rect1, rect2, intersections_unsorted);
if (intersections_unsorted.size() < 3) {
return 0.0f;
}
cv::convexHull(intersections_unsorted, intersections);
// We use Shoelace formula to compute area of polygons.
float intersection_area = 0.0f;
for (int i = 0; i < intersections.size(); ++i) {
const auto& curr_pt = intersections[i];
const int i_next = (i + 1) == intersections.size() ? 0 : (i + 1);
const auto& next_pt = intersections[i_next];
intersection_area += (curr_pt.x * next_pt.y - next_pt.x * curr_pt.y);
}
intersection_area = std::abs(intersection_area) * 0.5f;
// Compute union area
const float union_area =
rect1.size.area() + rect2.size.area() - intersection_area + 1e-5f;
const float iou = intersection_area / union_area;
return iou;
}
std::vector<cv::Point2f> ComputeBoxCorners(const TimedBoxProto& box,
float width, float height) {
// Rotate 4 corner w.r.t. center.
const cv::Point2f center(0.5f * (box.left() + box.right()) * width,
0.5f * (box.top() + box.bottom()) * height);
const std::vector<cv::Point2f> corners{
cv::Point2f(box.left() * width, box.top() * height),
cv::Point2f(box.left() * width, box.bottom() * height),
cv::Point2f(box.right() * width, box.bottom() * height),
cv::Point2f(box.right() * width, box.top() * height)};
const float cos_a = std::cos(box.rotation());
const float sin_a = std::sin(box.rotation());
std::vector<cv::Point2f> transformed_corners(4);
for (int k = 0; k < 4; ++k) {
// Scale and rotate w.r.t. center.
const cv::Point2f rad = corners[k] - center;
const cv::Point2f rot_rad(cos_a * rad.x - sin_a * rad.y,
sin_a * rad.x + cos_a * rad.y);
transformed_corners[k] = center + rot_rad;
transformed_corners[k].x /= width;
transformed_corners[k].y /= height;
}
return transformed_corners;
}
cv::Mat PerspectiveTransformBetweenBoxes(const TimedBoxProto& src_box,
const TimedBoxProto& dst_box,
const float aspect_ratio) {
std::vector<cv::Point2f> box1_corners =
ComputeBoxCorners(src_box, /*width*/ aspect_ratio, /*height*/ 1.0f);
std::vector<cv::Point2f> box2_corners =
ComputeBoxCorners(dst_box, /*width*/ aspect_ratio, /*height*/ 1.0f);
cv::Mat affine_transform = cv::getPerspectiveTransform(
/*src*/ box1_corners, /*dst*/ box2_corners);
cv::Mat output_affine;
affine_transform.convertTo(output_affine, CV_32FC1);
return output_affine;
}
cv::Point2f MapPoint(const TimedBoxProto& src_box, const TimedBoxProto& dst_box,
const cv::Point2f& src_point, float width, float height) {
const cv::Point2f src_center(
0.5f * (src_box.left() + src_box.right()) * width,
0.5f * (src_box.top() + src_box.bottom()) * height);
const cv::Point2f dst_center(
0.5f * (dst_box.left() + dst_box.right()) * width,
0.5f * (dst_box.top() + dst_box.bottom()) * height);
const float scale_x =
(dst_box.right() - dst_box.left()) / (src_box.right() - src_box.left());
const float scale_y =
(dst_box.bottom() - dst_box.top()) / (src_box.bottom() - src_box.top());
const float rotation = dst_box.rotation() - src_box.rotation();
const cv::Point2f rad =
cv::Point2f(src_point.x * width, src_point.y * height) - src_center;
const float rad_x = rad.x * scale_x;
const float rad_y = rad.y * scale_y;
const float cos_a = std::cos(rotation);
const float sin_a = std::sin(rotation);
const cv::Point2f rot_rad(cos_a * rad_x - sin_a * rad_y,
sin_a * rad_x + cos_a * rad_y);
const cv::Point2f dst_point_image = dst_center + rot_rad;
const cv::Point2f dst_point(dst_point_image.x / width,
dst_point_image.y / height);
return dst_point;
}
} // namespace mediapipe
@@ -1,50 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_BOX_UTIL_H_
#define MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_BOX_UTIL_H_
#include "mediapipe/framework/port/opencv_core_inc.h"
#include "mediapipe/util/tracking/box_tracker.pb.h"
namespace mediapipe {
// This function fills the geometry of the TimedBoxProto. Id, timestamp etc.
// need to be set outside this function.
void ComputeBoundingRect(const std::vector<cv::Point2f>& points,
mediapipe::TimedBoxProto* box);
// This function computes the intersection over union between two boxes.
float ComputeBoxIoU(const TimedBoxProto& box1, const TimedBoxProto& box2);
// Computes corners of the box.
// width and height are image width and height, which is typically
// needed since the box is in normalized coordinates.
std::vector<cv::Point2f> ComputeBoxCorners(const TimedBoxProto& box,
float width, float height);
// Computes the perspective transform from box1 to box2.
// The input argument aspect_ratio is width / height of the image.
// The returned matrix should be a 3x3 matrix.
cv::Mat PerspectiveTransformBetweenBoxes(const TimedBoxProto& src_box,
const TimedBoxProto& dst_box,
const float aspect_ratio);
// Map point according to source and destination box location.
cv::Point2f MapPoint(const TimedBoxProto& src_box, const TimedBoxProto& dst_box,
const cv::Point2f& src_point, float width, float height);
} // namespace mediapipe
#endif // MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_BOX_UTIL_H_
@@ -1,123 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/graphs/object_detection_3d/calculators/box_util.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/port/opencv_core_inc.h"
#include "mediapipe/util/tracking/box_tracker.pb.h"
namespace mediapipe {
namespace {
TEST(BoxUtilTest, TestComputeBoundingRect) {
std::vector<cv::Point2f> points{
cv::Point2f(0.35f, 0.25f), cv::Point2f(0.3f, 0.3f),
cv::Point2f(0.2f, 0.4f), cv::Point2f(0.3f, 0.1f),
cv::Point2f(0.2f, 0.2f), cv::Point2f(0.5f, 0.3f),
cv::Point2f(0.4f, 0.4f), cv::Point2f(0.5f, 0.1f),
cv::Point2f(0.4f, 0.2f)};
TimedBoxProto box;
ComputeBoundingRect(points, &box);
EXPECT_FLOAT_EQ(0.1f, box.top());
EXPECT_FLOAT_EQ(0.4f, box.bottom());
EXPECT_FLOAT_EQ(0.2f, box.left());
EXPECT_FLOAT_EQ(0.5f, box.right());
}
TEST(BoxUtilTest, TestComputeBoxIoU) {
TimedBoxProto box1;
box1.set_top(0.2f);
box1.set_bottom(0.6f);
box1.set_left(0.1f);
box1.set_right(0.3f);
box1.set_rotation(0.0f);
TimedBoxProto box2 = box1;
box2.set_rotation(/*pi/2*/ 1.570796f);
const float box_area =
(box1.bottom() - box1.top()) * (box1.right() - box1.left());
const float box_intersection =
(box1.right() - box1.left()) * (box1.right() - box1.left());
const float expected_iou =
box_intersection / (box_area * 2 - box_intersection);
EXPECT_NEAR(expected_iou, ComputeBoxIoU(box1, box2), 3e-5f);
TimedBoxProto box3;
box3.set_top(0.2f);
box3.set_bottom(0.6f);
box3.set_left(0.5f);
box3.set_right(0.7f);
EXPECT_NEAR(0.0f, ComputeBoxIoU(box1, box3), 3e-5f);
}
TEST(BoxUtilTest, TestPerspectiveTransformBetweenBoxes) {
TimedBoxProto box1;
const float height = 4.0f;
const float width = 3.0f;
box1.set_top(1.0f / height);
box1.set_bottom(2.0f / height);
box1.set_left(1.0f / width);
box1.set_right(2.0f / width);
TimedBoxProto box2;
box2.set_top(1.0f / height);
box2.set_bottom(2.0f / height);
box2.set_left(1.0f / width);
box2.set_right(2.0f / width);
box2.set_rotation(/*pi/4*/ -0.785398f);
cv::Mat transform =
PerspectiveTransformBetweenBoxes(box1, box2, width / height);
const float kTolerence = 1e-5f;
const cv::Vec3f original_position(1.5f / width, 1.0f / height, 1.0f);
const cv::Mat transformed_position = transform * cv::Mat(original_position);
EXPECT_NEAR(
(1.5f - 0.5f * std::sqrt(2) / 2.0f) / width,
transformed_position.at<float>(0) / transformed_position.at<float>(2),
kTolerence);
EXPECT_NEAR(
(1.5f - 0.5f * std::sqrt(2) / 2.0f) / height,
transformed_position.at<float>(1) / transformed_position.at<float>(2),
kTolerence);
}
TEST(BoxUtilTest, TestMapPoint) {
const float height = 4.0f;
const float width = 3.0f;
TimedBoxProto box1;
box1.set_top(1.0f / height);
box1.set_bottom(2.0f / height);
box1.set_left(1.0f / width);
box1.set_right(2.0f / width);
TimedBoxProto box2;
box2.set_top(1.0f / height);
box2.set_bottom(2.0f / height);
box2.set_left(1.0f / width);
box2.set_right(2.0f / width);
box2.set_rotation(/*pi/4*/ -0.785398f);
cv::Point2f src_point1(1.2f / width, 1.4f / height);
cv::Point2f src_point2(1.3f / width, 1.8f / height);
const float distance1 = std::sqrt(0.1 * 0.1 + 0.4 * 0.4);
cv::Point2f dst_point1 = MapPoint(box1, box2, src_point1, width, height);
cv::Point2f dst_point2 = MapPoint(box1, box2, src_point2, width, height);
const float distance2 =
std::sqrt((dst_point1.x * width - dst_point2.x * width) *
(dst_point1.x * width - dst_point2.x * width) +
(dst_point1.y * height - dst_point2.y * height) *
(dst_point1.y * height - dst_point2.y * height));
EXPECT_NEAR(distance1, distance2, 1e-5f);
}
} // namespace
} // namespace mediapipe
@@ -1,47 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto2";
package mediapipe;
message CameraParametersProto {
// This number is non-negative, it represents camera height above ground
// normalized by focal length.
optional float height_above_ground = 1 [default = 100.0];
// Width of image in portrait orientation normalized by focal length
optional float portrait_width = 2 [default = 1.0103];
// Height of image in portrait orientation normalized by focal length
optional float portrait_height = 3 [default = 1.3435];
enum ImageOrientation {
PORTRAIT_ORIENTATION = 0;
LANDSCAPE_ORIENTATION = 1;
}
// The input image orientation
optional ImageOrientation image_orientation = 4
[default = PORTRAIT_ORIENTATION];
// This defines the projection method from 2D screen to 3D.
enum ProjectionMode {
UNSPECIFIED = 0;
// Projects 2D point to ground plane (horizontal plane).
GROUND_PLANE = 1;
// Projects 2D point to sphere.
SPHERE = 2;
}
optional ProjectionMode projection_mode = 5 [default = GROUND_PLANE];
// Radius of sphere when using the SPHERE projection mode above.
// The value is normalized by focal length.
optional float projection_sphere_radius = 6 [default = 100.0];
}
@@ -1,257 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/graphs/object_detection_3d/calculators/decoder.h"
#include <limits>
#include "Eigen/Dense"
#include "mediapipe/framework/port/canonical_errors.h"
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
namespace mediapipe {
constexpr int Decoder::kNumOffsetmaps = 16;
namespace {
void SetPoint3d(float x, float y, float z, Point3D* point_3d) {
point_3d->set_x(x);
point_3d->set_y(y);
point_3d->set_z(z);
}
} // namespace
FrameAnnotation Decoder::DecodeBoundingBoxKeypoints(
const cv::Mat& heatmap, const cv::Mat& offsetmap) const {
CHECK_EQ(1, heatmap.channels());
CHECK_EQ(kNumOffsetmaps, offsetmap.channels());
CHECK_EQ(heatmap.cols, offsetmap.cols);
CHECK_EQ(heatmap.rows, offsetmap.rows);
const float offset_scale = std::min(offsetmap.cols, offsetmap.rows);
const std::vector<cv::Point> center_points = ExtractCenterKeypoints(heatmap);
std::vector<BeliefBox> boxes;
for (const auto& center_point : center_points) {
BeliefBox box;
box.box_2d.emplace_back(center_point.x, center_point.y);
const int center_x = static_cast<int>(std::round(center_point.x));
const int center_y = static_cast<int>(std::round(center_point.y));
box.belief = heatmap.at<float>(center_y, center_x);
if (config_.voting_radius() > 1) {
DecodeByVoting(heatmap, offsetmap, center_x, center_y, offset_scale,
offset_scale, &box);
} else {
DecodeByPeak(offsetmap, center_x, center_y, offset_scale, offset_scale,
&box);
}
if (IsNewBox(&boxes, &box)) {
boxes.push_back(std::move(box));
}
}
const float x_scale = 1.0f / offsetmap.cols;
const float y_scale = 1.0f / offsetmap.rows;
FrameAnnotation frame_annotations;
for (const auto& box : boxes) {
auto* object = frame_annotations.add_annotations();
for (const auto& point : box.box_2d) {
auto* point2d = object->add_keypoints()->mutable_point_2d();
point2d->set_x(point.first * x_scale);
point2d->set_y(point.second * y_scale);
}
}
return frame_annotations;
}
void Decoder::DecodeByPeak(const cv::Mat& offsetmap, int center_x, int center_y,
float offset_scale_x, float offset_scale_y,
BeliefBox* box) const {
const auto& offset = offsetmap.at<cv::Vec<float, kNumOffsetmaps>>(
/*row*/ center_y, /*col*/ center_x);
for (int i = 0; i < kNumOffsetmaps / 2; ++i) {
const float x_offset = offset[2 * i] * offset_scale_x;
const float y_offset = offset[2 * i + 1] * offset_scale_y;
box->box_2d.emplace_back(center_x + x_offset, center_y + y_offset);
}
}
void Decoder::DecodeByVoting(const cv::Mat& heatmap, const cv::Mat& offsetmap,
int center_x, int center_y, float offset_scale_x,
float offset_scale_y, BeliefBox* box) const {
// Votes at the center.
const auto& center_offset = offsetmap.at<cv::Vec<float, kNumOffsetmaps>>(
/*row*/ center_y, /*col*/ center_x);
std::vector<float> center_votes(kNumOffsetmaps, 0.f);
for (int i = 0; i < kNumOffsetmaps / 2; ++i) {
center_votes[2 * i] = center_x + center_offset[2 * i] * offset_scale_x;
center_votes[2 * i + 1] =
center_y + center_offset[2 * i + 1] * offset_scale_y;
}
// Find voting window.
int x_min = std::max(0, center_x - config_.voting_radius());
int y_min = std::max(0, center_y - config_.voting_radius());
int width = std::min(heatmap.cols - x_min, config_.voting_radius() * 2 + 1);
int height = std::min(heatmap.rows - y_min, config_.voting_radius() * 2 + 1);
cv::Rect rect(x_min, y_min, width, height);
cv::Mat heat = heatmap(rect);
cv::Mat offset = offsetmap(rect);
for (int i = 0; i < kNumOffsetmaps / 2; ++i) {
float x_sum = 0.f;
float y_sum = 0.f;
float votes = 0.f;
for (int r = 0; r < heat.rows; ++r) {
for (int c = 0; c < heat.cols; ++c) {
const float belief = heat.at<float>(r, c);
if (belief < config_.voting_threshold()) {
continue;
}
float offset_x =
offset.at<cv::Vec<float, kNumOffsetmaps>>(r, c)[2 * i] *
offset_scale_x;
float offset_y =
offset.at<cv::Vec<float, kNumOffsetmaps>>(r, c)[2 * i + 1] *
offset_scale_y;
float vote_x = c + rect.x + offset_x;
float vote_y = r + rect.y + offset_y;
float x_diff = std::abs(vote_x - center_votes[2 * i]);
float y_diff = std::abs(vote_y - center_votes[2 * i + 1]);
if (x_diff > config_.voting_allowance() ||
y_diff > config_.voting_allowance()) {
continue;
}
x_sum += vote_x * belief;
y_sum += vote_y * belief;
votes += belief;
}
}
box->box_2d.emplace_back(x_sum / votes, y_sum / votes);
}
}
bool Decoder::IsNewBox(std::vector<BeliefBox>* boxes, BeliefBox* box) const {
for (auto& b : *boxes) {
if (IsIdentical(b, *box)) {
if (b.belief < box->belief) {
std::swap(b, *box);
}
return false;
}
}
return true;
}
bool Decoder::IsIdentical(const BeliefBox& box_1,
const BeliefBox& box_2) const {
// Skip the center point.
for (int i = 1; i < box_1.box_2d.size(); ++i) {
const float x_diff =
std::abs(box_1.box_2d[i].first - box_2.box_2d[i].first);
const float y_diff =
std::abs(box_1.box_2d[i].second - box_2.box_2d[i].second);
if (x_diff > config_.voting_allowance() ||
y_diff > config_.voting_allowance()) {
return false;
}
}
return true;
}
std::vector<cv::Point> Decoder::ExtractCenterKeypoints(
const cv::Mat& center_heatmap) const {
cv::Mat max_filtered_heatmap(center_heatmap.rows, center_heatmap.cols,
center_heatmap.type());
const int kernel_size =
static_cast<int>(config_.local_max_distance() * 2 + 1 + 0.5f);
const cv::Size morph_size(kernel_size, kernel_size);
cv::dilate(center_heatmap, max_filtered_heatmap,
cv::getStructuringElement(cv::MORPH_RECT, morph_size));
cv::Mat peak_map;
cv::bitwise_and((center_heatmap >= max_filtered_heatmap),
(center_heatmap >= config_.heatmap_threshold()), peak_map);
std::vector<cv::Point> locations; // output, locations of non-zero pixels
cv::findNonZero(peak_map, locations);
return locations;
}
absl::Status Decoder::Lift2DTo3D(
const Eigen::Matrix<float, 4, 4, Eigen::RowMajor>& projection_matrix,
bool portrait, FrameAnnotation* estimated_box) const {
CHECK(estimated_box != nullptr);
const float fx = projection_matrix(0, 0);
const float fy = projection_matrix(1, 1);
const float cx = projection_matrix(0, 2);
const float cy = projection_matrix(1, 2);
for (auto& annotation : *estimated_box->mutable_annotations()) {
Eigen::Matrix<float, 16, 12, Eigen::RowMajor> m =
Eigen::Matrix<float, 16, 12, Eigen::RowMajor>::Zero(16, 12);
CHECK_EQ(9, annotation.keypoints_size());
float u, v;
for (int i = 0; i < 8; ++i) {
const auto& keypoint2d = annotation.keypoints(i + 1).point_2d();
if (portrait) {
// swap x and y given that our image is in portrait orientation
u = keypoint2d.y() * 2 - 1;
v = keypoint2d.x() * 2 - 1;
} else {
u = keypoint2d.x() * 2 - 1;
v = 1 - keypoint2d.y() * 2; // (1 - keypoint2d.y()) * 2 - 1
}
for (int j = 0; j < 4; ++j) {
// For each of the 4 control points, formulate two rows of the
// m matrix (two equations).
const float control_alpha = epnp_alpha_(i, j);
m(i * 2, j * 3) = fx * control_alpha;
m(i * 2, j * 3 + 2) = (cx + u) * control_alpha;
m(i * 2 + 1, j * 3 + 1) = fy * control_alpha;
m(i * 2 + 1, j * 3 + 2) = (cy + v) * control_alpha;
}
}
// This is a self adjoint matrix. Use SelfAdjointEigenSolver for a fast
// and stable solution.
Eigen::Matrix<float, 12, 12, Eigen::RowMajor> mt_m = m.transpose() * m;
Eigen::SelfAdjointEigenSolver<Eigen::Matrix<float, 12, 12, Eigen::RowMajor>>
eigen_solver(mt_m);
if (eigen_solver.info() != Eigen::Success) {
return absl::AbortedError("Eigen decomposition failed.");
}
CHECK_EQ(12, eigen_solver.eigenvalues().size());
// Eigenvalues are sorted in increasing order for SelfAdjointEigenSolver
// only! If you use other Eigen Solvers, it's not guaranteed to be in
// increasing order. Here, we just take the eigen vector corresponding
// to first/smallest eigen value, since we used SelfAdjointEigenSolver.
Eigen::VectorXf eigen_vec = eigen_solver.eigenvectors().col(0);
Eigen::Map<Eigen::Matrix<float, 4, 3, Eigen::RowMajor>> control_matrix(
eigen_vec.data());
if (control_matrix(0, 2) > 0) {
control_matrix = -control_matrix;
}
// First set the center keypoint.
SetPoint3d(control_matrix(0, 0), control_matrix(0, 1), control_matrix(0, 2),
annotation.mutable_keypoints(0)->mutable_point_3d());
// Then set the 8 vertices.
Eigen::Matrix<float, 8, 3, Eigen::RowMajor> vertices =
epnp_alpha_ * control_matrix;
for (int i = 0; i < 8; ++i) {
SetPoint3d(vertices(i, 0), vertices(i, 1), vertices(i, 2),
annotation.mutable_keypoints(i + 1)->mutable_point_3d());
}
}
return absl::OkStatus();
}
} // namespace mediapipe
@@ -1,109 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_DECODER_H_
#define MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_DECODER_H_
#include <vector>
#include "Eigen/Dense"
#include "absl/status/status.h"
#include "mediapipe/framework/port/opencv_core_inc.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/belief_decoder_config.pb.h"
namespace mediapipe {
// Decodes 3D bounding box from heatmaps and offset maps. In the future,
// if we want to develop decoder for generic skeleton, then we need to
// generalize this class, and make a few child classes.
class Decoder {
public:
static const int kNumOffsetmaps;
explicit Decoder(const BeliefDecoderConfig& config) : config_(config) {
epnp_alpha_ << 4.0f, -1.0f, -1.0f, -1.0f, 2.0f, -1.0f, -1.0f, 1.0f, 2.0f,
-1.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 1.0f, 2.0f, 1.0f, -1.0f, -1.0f,
0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, -2.0f, 1.0f, 1.0f,
1.0f;
}
// Decodes bounding boxes from predicted heatmap and offset maps.
// Input:
// heatmap: a single channel cv::Mat representing center point heatmap
// offsetmap: a 16 channel cv::Mat representing the 16 offset maps
// (2 for each of the 8 vertices)
// Output:
// Outputs 3D bounding boxes 2D vertices, represented by 'point_2d' field
// in each 'keypoints' field of object annotations.
FrameAnnotation DecodeBoundingBoxKeypoints(const cv::Mat& heatmap,
const cv::Mat& offsetmap) const;
// Lifts the estimated 2D projections of bounding box vertices to 3D.
// This function uses the EPnP approach described in this paper:
// https://icwww.epfl.ch/~lepetit/papers/lepetit_ijcv08.pdf .
// Input:
// projection_matrix: the projection matrix from 3D coordinate
// to screen coordinate.
// The 2D screen coordinate is defined as: u is along the long
// edge of the device, pointing down; v is along the short edge
// of the device, pointing right.
// portrait: a boolen variable indicating whether our images are
// obtained in portrait orientation or not.
// estimated_box: annotation with point_2d field populated with
// 2d vertices.
// Output:
// estimated_box: annotation with point_3d field populated with
// 3d vertices.
absl::Status Lift2DTo3D(
const Eigen::Matrix<float, 4, 4, Eigen::RowMajor>& projection_matrix,
bool portrait, FrameAnnotation* estimated_box) const;
private:
struct BeliefBox {
float belief;
std::vector<std::pair<float, float>> box_2d;
};
std::vector<cv::Point> ExtractCenterKeypoints(
const cv::Mat& center_heatmap) const;
// Decodes 2D keypoints at the peak point.
void DecodeByPeak(const cv::Mat& offsetmap, int center_x, int center_y,
float offset_scale_x, float offset_scale_y,
BeliefBox* box) const;
// Decodes 2D keypoints by voting around the peak.
void DecodeByVoting(const cv::Mat& heatmap, const cv::Mat& offsetmap,
int center_x, int center_y, float offset_scale_x,
float offset_scale_y, BeliefBox* box) const;
// Returns true if it is a new box. Otherwise, it may replace an existing box
// if the new box's belief is higher.
bool IsNewBox(std::vector<BeliefBox>* boxes, BeliefBox* box) const;
// Returns true if the two boxes are identical.
bool IsIdentical(const BeliefBox& box_1, const BeliefBox& box_2) const;
BeliefDecoderConfig config_;
// Following equation (1) in this paper
// https://icwww.epfl.ch/~lepetit/papers/lepetit_ijcv08.pdf,
// this variable denotes the coefficients for the 4 control points
// for each of the 8 3D box vertices.
Eigen::Matrix<float, 8, 4, Eigen::RowMajor> epnp_alpha_;
};
} // namespace mediapipe
#endif // MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_DECODER_H_
@@ -1,265 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <string>
#include <vector>
#include "absl/container/node_hash_set.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/detection.pb.h"
#include "mediapipe/framework/formats/location_data.pb.h"
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/framework/port/map_util.h"
#include "mediapipe/framework/port/re2.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/graphs/object_detection_3d/calculators/filter_detection_calculator.pb.h"
namespace mediapipe {
namespace {
constexpr char kDetectionTag[] = "DETECTION";
constexpr char kDetectionsTag[] = "DETECTIONS";
constexpr char kLabelsTag[] = "LABELS";
constexpr char kLabelsCsvTag[] = "LABELS_CSV";
using ::mediapipe::ContainsKey;
using ::mediapipe::RE2;
using Detections = std::vector<Detection>;
using Strings = std::vector<std::string>;
} // namespace
// Filters the entries in a Detection to only those with valid scores
// for the specified allowed labels. Allowed labels are provided as a
// vector<std::string> in an optional input side packet. Allowed labels can
// contain simple strings or regular expressions. The valid score range
// can be set in the options.The allowed labels can be provided as
// vector<std::string> (LABELS) or CSV std::string (LABELS_CSV) containing class
// names of allowed labels. Note: Providing an empty vector in the input side
// packet Packet causes this calculator to act as a sink if
// empty_allowed_labels_means_allow_everything is set to false (default value).
// To allow all labels, use the calculator with no input side packet stream, or
// set empty_allowed_labels_means_allow_everything to true.
//
// Example config:
// node {
// calculator: "FilterDetectionCalculator"
// input_stream: "DETECTIONS:detections"
// output_stream: "DETECTIONS:filtered_detections"
// input_side_packet: "LABELS:allowed_labels"
// node_options: {
// [type.googleapis.com/mediapipe.FilterDetectionCalculatorOptions]: {
// min_score: 0.5
// }
// }
// }
struct FirstGreaterComparator {
bool operator()(const std::pair<float, int>& a,
const std::pair<float, int>& b) const {
return a.first > b.first;
}
};
::mediapipe::Status SortLabelsByDecreasingScore(const Detection& detection,
Detection* sorted_detection) {
RET_CHECK(sorted_detection);
RET_CHECK_EQ(detection.score_size(), detection.label_size());
if (!detection.label_id().empty()) {
RET_CHECK_EQ(detection.score_size(), detection.label_id_size());
}
// Copies input to keep all fields unchanged, and to reserve space for
// repeated fields. Repeated fields (score, label, and label_id) will be
// overwritten.
*sorted_detection = detection;
std::vector<std::pair<float, int>> scores_and_indices(detection.score_size());
for (int i = 0; i < detection.score_size(); ++i) {
scores_and_indices[i].first = detection.score(i);
scores_and_indices[i].second = i;
}
std::sort(scores_and_indices.begin(), scores_and_indices.end(),
FirstGreaterComparator());
for (int i = 0; i < detection.score_size(); ++i) {
const int index = scores_and_indices[i].second;
sorted_detection->set_score(i, detection.score(index));
sorted_detection->set_label(i, detection.label(index));
}
if (!detection.label_id().empty()) {
for (int i = 0; i < detection.score_size(); ++i) {
const int index = scores_and_indices[i].second;
sorted_detection->set_label_id(i, detection.label_id(index));
}
}
return ::mediapipe::OkStatus();
}
class FilterDetectionCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
private:
bool IsValidLabel(const std::string& label);
bool IsValidScore(float score);
// Stores numeric limits for filtering on the score.
FilterDetectionCalculatorOptions options_;
// We use the next two fields to possibly filter to a limited set of
// classes. The hash_set will be empty in two cases: 1) if no input
// side packet stream is provided (not filtering on labels), or 2)
// if the input side packet contains an empty vector (no labels are
// allowed). We use limit_labels_ to distinguish between the two cases.
bool limit_labels_ = true;
absl::node_hash_set<std::string> allowed_labels_;
};
REGISTER_CALCULATOR(FilterDetectionCalculator);
::mediapipe::Status FilterDetectionCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(!cc->Inputs().GetTags().empty());
RET_CHECK(!cc->Outputs().GetTags().empty());
if (cc->Inputs().HasTag(kDetectionTag)) {
cc->Inputs().Tag(kDetectionTag).Set<Detection>();
cc->Outputs().Tag(kDetectionTag).Set<Detection>();
}
if (cc->Inputs().HasTag(kDetectionsTag)) {
cc->Inputs().Tag(kDetectionsTag).Set<Detections>();
cc->Outputs().Tag(kDetectionsTag).Set<Detections>();
}
if (cc->InputSidePackets().HasTag(kLabelsTag)) {
cc->InputSidePackets().Tag(kLabelsTag).Set<Strings>();
}
if (cc->InputSidePackets().HasTag(kLabelsCsvTag)) {
cc->InputSidePackets().Tag(kLabelsCsvTag).Set<std::string>();
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status FilterDetectionCalculator::Open(CalculatorContext* cc) {
cc->SetOffset(TimestampDiff(0));
options_ = cc->Options<FilterDetectionCalculatorOptions>();
limit_labels_ = cc->InputSidePackets().HasTag(kLabelsTag) ||
cc->InputSidePackets().HasTag(kLabelsCsvTag);
if (limit_labels_) {
Strings whitelist_labels;
if (cc->InputSidePackets().HasTag(kLabelsCsvTag)) {
whitelist_labels = absl::StrSplit(
cc->InputSidePackets().Tag(kLabelsCsvTag).Get<std::string>(), ',',
absl::SkipWhitespace());
for (auto& e : whitelist_labels) {
absl::StripAsciiWhitespace(&e);
}
} else {
whitelist_labels = cc->InputSidePackets().Tag(kLabelsTag).Get<Strings>();
}
allowed_labels_.insert(whitelist_labels.begin(), whitelist_labels.end());
}
if (limit_labels_ && allowed_labels_.empty()) {
if (options_.fail_on_empty_labels()) {
cc->GetCounter("VideosWithEmptyLabelsWhitelist")->Increment();
return tool::StatusFail(
"FilterDetectionCalculator received empty whitelist with "
"fail_on_empty_labels = true.");
}
if (options_.empty_allowed_labels_means_allow_everything()) {
// Continue as if side_input was not provided, i.e. pass all labels.
limit_labels_ = false;
}
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status FilterDetectionCalculator::Process(CalculatorContext* cc) {
if (limit_labels_ && allowed_labels_.empty()) {
return ::mediapipe::OkStatus();
}
Detections detections;
if (cc->Inputs().HasTag(kDetectionsTag)) {
detections = cc->Inputs().Tag(kDetectionsTag).Get<Detections>();
} else if (cc->Inputs().HasTag(kDetectionTag)) {
detections.emplace_back(cc->Inputs().Tag(kDetectionsTag).Get<Detection>());
}
std::unique_ptr<Detections> outputs(new Detections);
for (const auto& input : detections) {
Detection output;
for (int i = 0; i < input.label_size(); ++i) {
const std::string& label = input.label(i);
const float score = input.score(i);
if (IsValidLabel(label) && IsValidScore(score)) {
output.add_label(label);
output.add_score(score);
}
}
if (output.label_size() > 0) {
if (input.has_location_data()) {
*output.mutable_location_data() = input.location_data();
}
Detection output_sorted;
if (!SortLabelsByDecreasingScore(output, &output_sorted).ok()) {
// Uses the orginal output if fails to sort.
cc->GetCounter("FailedToSortLabelsInDetection")->Increment();
output_sorted = output;
}
outputs->emplace_back(output_sorted);
}
}
if (cc->Outputs().HasTag(kDetectionsTag)) {
cc->Outputs()
.Tag(kDetectionsTag)
.Add(outputs.release(), cc->InputTimestamp());
} else if (!outputs->empty()) {
cc->Outputs()
.Tag(kDetectionsTag)
.Add(new Detection((*outputs)[0]), cc->InputTimestamp());
}
return ::mediapipe::OkStatus();
}
bool FilterDetectionCalculator::IsValidLabel(const std::string& label) {
bool match = !limit_labels_ || ContainsKey(allowed_labels_, label);
if (!match) {
// If no exact match is found, check for regular expression
// comparions in the allowed_labels.
for (const auto& label_regexp : allowed_labels_) {
match = match || RE2::FullMatch(label, RE2(label_regexp));
}
}
return match;
}
bool FilterDetectionCalculator::IsValidScore(float score) {
if (options_.has_min_score() && score < options_.min_score()) {
LOG(ERROR) << "Filter out detection with low score " << score;
return false;
}
if (options_.has_max_score() && score > options_.max_score()) {
LOG(ERROR) << "Filter out detection with high score " << score;
return false;
}
LOG(ERROR) << "Pass detection with score " << score;
return true;
}
} // namespace mediapipe
@@ -1,45 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
message FilterDetectionCalculatorOptions {
extend CalculatorOptions {
optional FilterDetectionCalculatorOptions ext = 339582987;
}
optional float min_score = 1;
optional float max_score = 2;
// Setting fail_on_empty_labels to true will cause the calculator to return a
// failure status on Open() if an empty list is provided on the external
// input, immediately terminating the graph run.
optional bool fail_on_empty_labels = 3 [default = false];
// If fail_on_empty_labels is set to false setting
// empty_allowed_labels_means_allow_everything to
// false will cause the calculator to close output stream and ignore remaining
// inputs if an empty list is provided. If
// empty_allowed_labels_means_allow_everything is set to true this will force
// calculator to pass all labels.
optional bool empty_allowed_labels_means_allow_everything = 6
[default = false];
// Determines whether the input format is a vector<Detection> (use-case object
// detectors) or Detection (use-case classifiers).
optional bool use_detection_vector = 4 [deprecated = true];
// Determines whether the input side packet format is a vector of labels, or
// a string with comma separated labels.
optional bool use_allowed_labels_csv = 5 [deprecated = true];
}
@@ -1,185 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
#include <cmath>
#include "Eigen/Dense"
#include "absl/memory/memory.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/rect.pb.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/box.h"
#include "mediapipe/graphs/object_detection_3d/calculators/frame_annotation_to_rect_calculator.pb.h"
namespace mediapipe {
using Matrix3fRM = Eigen::Matrix<float, 3, 3, Eigen::RowMajor>;
using Eigen::Vector2f;
using Eigen::Vector3f;
namespace {
constexpr char kInputFrameAnnotationTag[] = "FRAME_ANNOTATION";
constexpr char kOutputNormRectTag[] = "NORM_RECT";
} // namespace
// A calculator that converts FrameAnnotation proto to NormalizedRect.
// The rotation angle of the NormalizedRect is derived from object's 3d pose.
// The angle is calculated such that after rotation the 2d projection of y-axis.
// on the image plane is always vertical.
class FrameAnnotationToRectCalculator : public CalculatorBase {
public:
enum ViewStatus {
TOP_VIEW_ON,
TOP_VIEW_OFF,
};
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
private:
void AnnotationToRect(const FrameAnnotation& annotation,
NormalizedRect* rect);
float RotationAngleFromAnnotation(const FrameAnnotation& annotation);
float RotationAngleFromPose(const Matrix3fRM& rotation,
const Vector3f& translation, const Vector3f& vec);
ViewStatus status_;
float off_threshold_;
float on_threshold_;
};
REGISTER_CALCULATOR(FrameAnnotationToRectCalculator);
::mediapipe::Status FrameAnnotationToRectCalculator::Open(
CalculatorContext* cc) {
status_ = TOP_VIEW_OFF;
const auto& options = cc->Options<FrameAnnotationToRectCalculatorOptions>();
off_threshold_ = options.off_threshold();
on_threshold_ = options.on_threshold();
RET_CHECK(off_threshold_ <= on_threshold_);
return ::mediapipe::OkStatus();
}
::mediapipe::Status FrameAnnotationToRectCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(!cc->Inputs().GetTags().empty());
RET_CHECK(!cc->Outputs().GetTags().empty());
if (cc->Inputs().HasTag(kInputFrameAnnotationTag)) {
cc->Inputs().Tag(kInputFrameAnnotationTag).Set<FrameAnnotation>();
}
if (cc->Outputs().HasTag(kOutputNormRectTag)) {
cc->Outputs().Tag(kOutputNormRectTag).Set<NormalizedRect>();
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status FrameAnnotationToRectCalculator::Process(
CalculatorContext* cc) {
if (cc->Inputs().Tag(kInputFrameAnnotationTag).IsEmpty()) {
return ::mediapipe::OkStatus();
}
auto output_rect = absl::make_unique<NormalizedRect>();
AnnotationToRect(
cc->Inputs().Tag(kInputFrameAnnotationTag).Get<FrameAnnotation>(),
output_rect.get());
// Output
cc->Outputs()
.Tag(kOutputNormRectTag)
.Add(output_rect.release(), cc->InputTimestamp());
return ::mediapipe::OkStatus();
}
void FrameAnnotationToRectCalculator::AnnotationToRect(
const FrameAnnotation& annotation, NormalizedRect* rect) {
float x_min = std::numeric_limits<float>::max();
float x_max = std::numeric_limits<float>::min();
float y_min = std::numeric_limits<float>::max();
float y_max = std::numeric_limits<float>::min();
const auto& object = annotation.annotations(0);
for (const auto& keypoint : object.keypoints()) {
const auto& point_2d = keypoint.point_2d();
x_min = std::min(x_min, point_2d.x());
x_max = std::max(x_max, point_2d.x());
y_min = std::min(y_min, point_2d.y());
y_max = std::max(y_max, point_2d.y());
}
rect->set_x_center((x_min + x_max) / 2);
rect->set_y_center((y_min + y_max) / 2);
rect->set_width(x_max - x_min);
rect->set_height(y_max - y_min);
rect->set_rotation(RotationAngleFromAnnotation(annotation));
}
float FrameAnnotationToRectCalculator::RotationAngleFromAnnotation(
const FrameAnnotation& annotation) {
const auto& object = annotation.annotations(0);
Box box("category");
std::vector<Vector3f> vertices_3d;
std::vector<Vector2f> vertices_2d;
for (const auto& keypoint : object.keypoints()) {
const auto& point_3d = keypoint.point_3d();
const auto& point_2d = keypoint.point_2d();
vertices_3d.emplace_back(
Vector3f(point_3d.x(), point_3d.y(), point_3d.z()));
vertices_2d.emplace_back(Vector2f(point_2d.x(), point_2d.y()));
}
box.Fit(vertices_3d);
Vector3f scale = box.GetScale();
Matrix3fRM box_rotation = box.GetRotation();
Vector3f box_translation = box.GetTranslation();
// Rotation angle to use when top-view is on(top-view on),
// Which will make z-axis upright after the rotation.
const float angle_on =
RotationAngleFromPose(box_rotation, box_translation, Vector3f::UnitZ());
// Rotation angle to use when side-view is on(top-view off),
// Which will make y-axis upright after the rotation.
const float angle_off =
RotationAngleFromPose(box_rotation, box_translation, Vector3f::UnitY());
// Calculate angle between z-axis and viewing ray in degrees.
const float view_to_z_angle = std::acos(box_rotation(2, 1)) * 180 / M_PI;
// Determine threshold based on current status,
// on_threshold_ is used for TOP_VIEW_ON -> TOP_VIEW_OFF transition,
// off_threshold_ is used for TOP_VIEW_OFF -> TOP_VIEW_ON transition.
const float thresh =
(status_ == TOP_VIEW_ON) ? on_threshold_ : off_threshold_;
// If view_to_z_angle is smaller than threshold, then top-view is on;
// Otherwise top-view is off.
status_ = (view_to_z_angle < thresh) ? TOP_VIEW_ON : TOP_VIEW_OFF;
// Determine which angle to used based on current status_.
float angle_to_rotate = (status_ == TOP_VIEW_ON) ? angle_on : angle_off;
return angle_to_rotate;
}
float FrameAnnotationToRectCalculator::RotationAngleFromPose(
const Matrix3fRM& rotation, const Vector3f& translation,
const Vector3f& vec) {
auto p1 = rotation * vec + translation;
auto p2 = -rotation * vec + translation;
const float dy = p2[2] * p1[1] - p1[2] * p2[1];
const float dx = p2[2] * p1[0] - p1[2] * p2[0];
return std::atan2(-dy, dx);
}
} // namespace mediapipe
@@ -1,31 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
message FrameAnnotationToRectCalculatorOptions {
extend CalculatorOptions {
optional FrameAnnotationToRectCalculatorOptions ext = 338119067;
}
// The threshold to use when top-view is off,to enable hysteresis,
// It's required that off_threshold <= on_threshold.
optional float off_threshold = 1 [default = 40.0];
// The threshold to use when top-view is on.
optional float on_threshold = 2 [default = 41.0];
}
@@ -1,115 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <utility>
#include "absl/memory/memory.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/opencv_core_inc.h"
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/box_util.h"
#include "mediapipe/util/tracking/box_tracker.pb.h"
namespace {
constexpr char kInputStreamTag[] = "FRAME_ANNOTATION";
constexpr char kOutputStreamTag[] = "BOXES";
} // namespace
namespace mediapipe {
// Convert FrameAnnotation 3d bounding box detections to TimedBoxListProto
// 2d bounding boxes.
//
// Input:
// FRAME_ANNOTATION - 3d bounding box annotation.
// Output:
// BOXES - 2d bounding box enclosing the projection of 3d box.
//
// Usage example:
// node {
// calculator: "FrameAnnotationToTimedBoxListCalculator"
// input_stream: "FRAME_ANNOTATION:frame_annotation"
// output_stream: "BOXES:boxes"
// }
class FrameAnnotationToTimedBoxListCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
::mediapipe::Status Close(CalculatorContext* cc) override;
};
REGISTER_CALCULATOR(FrameAnnotationToTimedBoxListCalculator);
::mediapipe::Status FrameAnnotationToTimedBoxListCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(!cc->Inputs().GetTags().empty());
RET_CHECK(!cc->Outputs().GetTags().empty());
if (cc->Inputs().HasTag(kInputStreamTag)) {
cc->Inputs().Tag(kInputStreamTag).Set<FrameAnnotation>();
}
if (cc->Outputs().HasTag(kOutputStreamTag)) {
cc->Outputs().Tag(kOutputStreamTag).Set<TimedBoxProtoList>();
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status FrameAnnotationToTimedBoxListCalculator::Open(
CalculatorContext* cc) {
return ::mediapipe::OkStatus();
}
::mediapipe::Status FrameAnnotationToTimedBoxListCalculator::Process(
CalculatorContext* cc) {
if (cc->Inputs().HasTag(kInputStreamTag) &&
!cc->Inputs().Tag(kInputStreamTag).IsEmpty()) {
const auto& frame_annotation =
cc->Inputs().Tag(kInputStreamTag).Get<FrameAnnotation>();
auto output_objects = absl::make_unique<TimedBoxProtoList>();
for (const auto& annotation : frame_annotation.annotations()) {
std::vector<cv::Point2f> key_points;
for (const auto& keypoint : annotation.keypoints()) {
key_points.push_back(
cv::Point2f(keypoint.point_2d().x(), keypoint.point_2d().y()));
}
TimedBoxProto* added_box = output_objects->add_box();
ComputeBoundingRect(key_points, added_box);
added_box->set_id(annotation.object_id());
const int64 time_msec =
static_cast<int64>(std::round(frame_annotation.timestamp() / 1000));
added_box->set_time_msec(time_msec);
}
// Output
if (cc->Outputs().HasTag(kOutputStreamTag)) {
cc->Outputs()
.Tag(kOutputStreamTag)
.Add(output_objects.release(), cc->InputTimestamp());
}
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status FrameAnnotationToTimedBoxListCalculator::Close(
CalculatorContext* cc) {
return ::mediapipe::OkStatus();
}
} // namespace mediapipe
@@ -1,102 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/graphs/object_detection_3d/calculators/frame_annotation_tracker.h"
#include "absl/container/flat_hash_set.h"
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/box_util.h"
#include "mediapipe/util/tracking/box_tracker.pb.h"
namespace mediapipe {
void FrameAnnotationTracker::AddDetectionResult(
const FrameAnnotation& frame_annotation) {
const int64 time_us =
static_cast<int64>(std::round(frame_annotation.timestamp()));
for (const auto& object_annotation : frame_annotation.annotations()) {
detected_objects_[time_us + object_annotation.object_id()] =
object_annotation;
}
}
FrameAnnotation FrameAnnotationTracker::ConsolidateTrackingResult(
const TimedBoxProtoList& tracked_boxes,
absl::flat_hash_set<int>* cancel_object_ids) {
CHECK(cancel_object_ids != nullptr);
FrameAnnotation frame_annotation;
std::vector<int64> keys_to_be_deleted;
for (const auto& detected_obj : detected_objects_) {
const int object_id = detected_obj.second.object_id();
if (cancel_object_ids->contains(object_id)) {
// Remember duplicated detections' keys.
keys_to_be_deleted.push_back(detected_obj.first);
continue;
}
TimedBoxProto ref_box;
for (const auto& box : tracked_boxes.box()) {
if (box.id() == object_id) {
ref_box = box;
break;
}
}
if (!ref_box.has_id() || ref_box.id() < 0) {
LOG(ERROR) << "Can't find matching tracked box for object id: "
<< object_id << ". Likely lost tracking of it.";
keys_to_be_deleted.push_back(detected_obj.first);
continue;
}
// Find duplicated boxes
for (const auto& box : tracked_boxes.box()) {
if (box.id() != object_id) {
if (ComputeBoxIoU(ref_box, box) > iou_threshold_) {
cancel_object_ids->insert(box.id());
}
}
}
// Map ObjectAnnotation from detection to tracked time.
// First, gather all keypoints from source detection.
std::vector<cv::Point2f> key_points;
for (const auto& keypoint : detected_obj.second.keypoints()) {
key_points.push_back(
cv::Point2f(keypoint.point_2d().x(), keypoint.point_2d().y()));
}
// Second, find source box.
TimedBoxProto src_box;
ComputeBoundingRect(key_points, &src_box);
ObjectAnnotation* tracked_obj = frame_annotation.add_annotations();
tracked_obj->set_object_id(ref_box.id());
// Finally, map all keypoints in the source detection to tracked location.
for (const auto& keypoint : detected_obj.second.keypoints()) {
cv::Point2f dst = MapPoint(
src_box, ref_box,
cv::Point2f(keypoint.point_2d().x(), keypoint.point_2d().y()),
img_width_, img_height_);
auto* dst_point = tracked_obj->add_keypoints()->mutable_point_2d();
dst_point->set_x(dst.x);
dst_point->set_y(dst.y);
}
}
for (const auto& key : keys_to_be_deleted) {
detected_objects_.erase(key);
}
return frame_annotation;
}
} // namespace mediapipe
@@ -1,62 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_FRAME_ANNOTATION_TRACKER_H_
#define MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_FRAME_ANNOTATION_TRACKER_H_
#include <functional>
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_set.h"
#include "mediapipe/framework/port/integral_types.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/util/tracking/box_tracker.pb.h"
namespace mediapipe {
class FrameAnnotationTracker {
public:
// If two bounding boxes have IoU over iou_threshold, then we consider them
// describing the same object.
FrameAnnotationTracker(float iou_threshold, float img_width, float img_height)
: iou_threshold_(iou_threshold),
img_width_(img_width),
img_height_(img_height) {}
// Adds detection results from an external detector.
void AddDetectionResult(const FrameAnnotation& frame_annotation);
// Consolidates tracking result from an external tracker, associates with
// the detection result by the object id, and produces the corresponding
// result in FrameAnnotation. When there are duplicates, output the ids that
// need to be cancelled in cancel_object_ids.
// Note that the returned FrameAnnotation is missing timestamp. Need to fill
// that field.
FrameAnnotation ConsolidateTrackingResult(
const TimedBoxProtoList& tracked_boxes,
absl::flat_hash_set<int>* cancel_object_ids);
private:
float iou_threshold_;
float img_width_;
float img_height_;
// Cached detection results over time.
// Key is timestamp_us + object_id.
absl::btree_map<int64, ObjectAnnotation, std::greater<int64>>
detected_objects_;
};
} // namespace mediapipe
#endif // MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_FRAME_ANNOTATION_TRACKER_H_
@@ -1,137 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/container/flat_hash_set.h"
#include "absl/memory/memory.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/framework/port/status.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/frame_annotation_tracker.h"
#include "mediapipe/graphs/object_detection_3d/calculators/frame_annotation_tracker_calculator.pb.h"
#include "mediapipe/util/tracking/box_tracker.pb.h"
namespace {
constexpr char kInputFrameAnnotationTag[] = "FRAME_ANNOTATION";
constexpr char kInputTrackedBoxesTag[] = "TRACKED_BOXES";
constexpr char kOutputTrackedFrameAnnotationTag[] = "TRACKED_FRAME_ANNOTATION";
constexpr char kOutputCancelObjectIdTag[] = "CANCEL_OBJECT_ID";
} // namespace
namespace mediapipe {
// Tracks frame annotations seeded/updated by FRAME_ANNOTATION input_stream.
// When using this calculator, make sure FRAME_ANNOTATION and TRACKED_BOXES
// are in different sync set.
//
// Input:
// FRAME_ANNOTATION - frame annotation.
// TRACKED_BOXES - 2d box tracking result
// Output:
// TRACKED_FRAME_ANNOTATION - annotation inferred from 2d tracking result.
// CANCEL_OBJECT_ID - object id that needs to be cancelled from the tracker.
//
// Usage example:
// node {
// calculator: "FrameAnnotationTrackerCalculator"
// input_stream: "FRAME_ANNOTATION:frame_annotation"
// input_stream: "TRACKED_BOXES:tracked_boxes"
// output_stream: "TRACKED_FRAME_ANNOTATION:tracked_frame_annotation"
// output_stream: "CANCEL_OBJECT_ID:cancel_object_id"
// }
class FrameAnnotationTrackerCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
::mediapipe::Status Close(CalculatorContext* cc) override;
private:
std::unique_ptr<FrameAnnotationTracker> frame_annotation_tracker_;
};
REGISTER_CALCULATOR(FrameAnnotationTrackerCalculator);
::mediapipe::Status FrameAnnotationTrackerCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(!cc->Inputs().GetTags().empty());
RET_CHECK(!cc->Outputs().GetTags().empty());
if (cc->Inputs().HasTag(kInputFrameAnnotationTag)) {
cc->Inputs().Tag(kInputFrameAnnotationTag).Set<FrameAnnotation>();
}
if (cc->Inputs().HasTag(kInputTrackedBoxesTag)) {
cc->Inputs().Tag(kInputTrackedBoxesTag).Set<TimedBoxProtoList>();
}
if (cc->Outputs().HasTag(kOutputTrackedFrameAnnotationTag)) {
cc->Outputs().Tag(kOutputTrackedFrameAnnotationTag).Set<FrameAnnotation>();
}
if (cc->Outputs().HasTag(kOutputCancelObjectIdTag)) {
cc->Outputs().Tag(kOutputCancelObjectIdTag).Set<int>();
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status FrameAnnotationTrackerCalculator::Open(
CalculatorContext* cc) {
const auto& options = cc->Options<FrameAnnotationTrackerCalculatorOptions>();
frame_annotation_tracker_ = absl::make_unique<FrameAnnotationTracker>(
options.iou_threshold(), options.img_width(), options.img_height());
return ::mediapipe::OkStatus();
}
::mediapipe::Status FrameAnnotationTrackerCalculator::Process(
CalculatorContext* cc) {
if (cc->Inputs().HasTag(kInputFrameAnnotationTag) &&
!cc->Inputs().Tag(kInputFrameAnnotationTag).IsEmpty()) {
frame_annotation_tracker_->AddDetectionResult(
cc->Inputs().Tag(kInputFrameAnnotationTag).Get<FrameAnnotation>());
}
if (cc->Inputs().HasTag(kInputTrackedBoxesTag) &&
!cc->Inputs().Tag(kInputTrackedBoxesTag).IsEmpty() &&
cc->Outputs().HasTag(kOutputTrackedFrameAnnotationTag)) {
absl::flat_hash_set<int> cancel_object_ids;
auto output_frame_annotation = absl::make_unique<FrameAnnotation>();
*output_frame_annotation =
frame_annotation_tracker_->ConsolidateTrackingResult(
cc->Inputs().Tag(kInputTrackedBoxesTag).Get<TimedBoxProtoList>(),
&cancel_object_ids);
output_frame_annotation->set_timestamp(cc->InputTimestamp().Microseconds());
cc->Outputs()
.Tag(kOutputTrackedFrameAnnotationTag)
.Add(output_frame_annotation.release(), cc->InputTimestamp());
if (cc->Outputs().HasTag(kOutputCancelObjectIdTag)) {
auto packet_timestamp = cc->InputTimestamp();
for (const auto& id : cancel_object_ids) {
// The timestamp is incremented (by 1 us) because currently the box
// tracker calculator only accepts one cancel object ID for any given
// timestamp.
cc->Outputs()
.Tag(kOutputCancelObjectIdTag)
.AddPacket(mediapipe::MakePacket<int>(id).At(packet_timestamp++));
}
}
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status FrameAnnotationTrackerCalculator::Close(
CalculatorContext* cc) {
return ::mediapipe::OkStatus();
}
} // namespace mediapipe
@@ -1,36 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The option proto for the FrameAnnotationTrackerCalculatorOptions.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
message FrameAnnotationTrackerCalculatorOptions {
extend CalculatorOptions {
optional FrameAnnotationTrackerCalculatorOptions ext = 291291253;
}
// The threshold on intersection-over-union (IoU). We consider
// boxes with IoU larger than this threshold to be the duplicates.
optional float iou_threshold = 1 [default = 0.5];
// We need image dimension to properly compute annotation locations.
optional float img_width = 2;
optional float img_height = 3;
}
@@ -1,143 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/graphs/object_detection_3d/calculators/frame_annotation_tracker.h"
#include "absl/container/flat_hash_set.h"
#include "mediapipe/framework/port/gmock.h"
#include "mediapipe/framework/port/gtest.h"
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/util/tracking/box_tracker.pb.h"
namespace mediapipe {
namespace {
// Create a new object annotation by shifting a reference
// object annotation.
ObjectAnnotation ShiftObject2d(const ObjectAnnotation& ref_obj, float dx,
float dy) {
ObjectAnnotation obj = ref_obj;
for (auto& keypoint : *(obj.mutable_keypoints())) {
const float ref_x = keypoint.point_2d().x();
const float ref_y = keypoint.point_2d().y();
keypoint.mutable_point_2d()->set_x(ref_x + dx);
keypoint.mutable_point_2d()->set_y(ref_y + dy);
}
return obj;
}
TimedBoxProto ShiftBox(const TimedBoxProto& ref_box, float dx, float dy) {
TimedBoxProto box = ref_box;
box.set_top(ref_box.top() + dy);
box.set_bottom(ref_box.bottom() + dy);
box.set_left(ref_box.left() + dx);
box.set_right(ref_box.right() + dx);
return box;
}
// Constructs a fixed ObjectAnnotation.
ObjectAnnotation ConstructFixedObject(
const std::vector<std::vector<float>>& points) {
ObjectAnnotation obj;
for (const auto& point : points) {
auto* keypoint = obj.add_keypoints();
CHECK_EQ(2, point.size());
keypoint->mutable_point_2d()->set_x(point[0]);
keypoint->mutable_point_2d()->set_y(point[1]);
}
return obj;
}
TEST(FrameAnnotationTrackerTest, TestConsolidation) {
// Add 4 detections represented by FrameAnnotation, of which 3 correspond
// to the same object.
ObjectAnnotation object1, object2, object3, object4;
// The bounding rectangle for these object keypoints is:
// x: [0.2, 0.5], y: [0.1, 0.4]
object3 = ConstructFixedObject({{0.35f, 0.25f},
{0.3f, 0.3f},
{0.2f, 0.4f},
{0.3f, 0.1f},
{0.2f, 0.2f},
{0.5f, 0.3f},
{0.4f, 0.4f},
{0.5f, 0.1f},
{0.4f, 0.2f}});
object3.set_object_id(3);
object1 = ShiftObject2d(object3, -0.05f, -0.05f);
object1.set_object_id(1);
object2 = ShiftObject2d(object3, 0.05f, 0.05f);
object2.set_object_id(2);
object4 = ShiftObject2d(object3, 0.2f, 0.2f);
object4.set_object_id(4);
FrameAnnotation frame_annotation_1;
frame_annotation_1.set_timestamp(30 * 1000); // 30ms
*(frame_annotation_1.add_annotations()) = object1;
*(frame_annotation_1.add_annotations()) = object4;
FrameAnnotation frame_annotation_2;
frame_annotation_2.set_timestamp(60 * 1000); // 60ms
*(frame_annotation_2.add_annotations()) = object2;
FrameAnnotation frame_annotation_3;
frame_annotation_3.set_timestamp(90 * 1000); // 90ms
*(frame_annotation_3.add_annotations()) = object3;
FrameAnnotationTracker frame_annotation_tracker(/*iou_threshold*/ 0.5f, 1.0f,
1.0f);
frame_annotation_tracker.AddDetectionResult(frame_annotation_1);
frame_annotation_tracker.AddDetectionResult(frame_annotation_2);
frame_annotation_tracker.AddDetectionResult(frame_annotation_3);
TimedBoxProtoList timed_box_proto_list;
TimedBoxProto* timed_box_proto = timed_box_proto_list.add_box();
timed_box_proto->set_top(0.4f);
timed_box_proto->set_bottom(0.7f);
timed_box_proto->set_left(0.6f);
timed_box_proto->set_right(0.9f);
timed_box_proto->set_id(3);
timed_box_proto->set_time_msec(150);
timed_box_proto = timed_box_proto_list.add_box();
*timed_box_proto = ShiftBox(timed_box_proto_list.box(0), 0.01f, 0.01f);
timed_box_proto->set_id(1);
timed_box_proto->set_time_msec(150);
timed_box_proto = timed_box_proto_list.add_box();
*timed_box_proto = ShiftBox(timed_box_proto_list.box(0), -0.01f, -0.01f);
timed_box_proto->set_id(2);
timed_box_proto->set_time_msec(150);
absl::flat_hash_set<int> cancel_object_ids;
FrameAnnotation tracked_detection =
frame_annotation_tracker.ConsolidateTrackingResult(timed_box_proto_list,
&cancel_object_ids);
EXPECT_EQ(2, cancel_object_ids.size());
EXPECT_EQ(1, cancel_object_ids.count(1));
EXPECT_EQ(1, cancel_object_ids.count(2));
EXPECT_EQ(1, tracked_detection.annotations_size());
EXPECT_EQ(3, tracked_detection.annotations(0).object_id());
EXPECT_EQ(object3.keypoints_size(),
tracked_detection.annotations(0).keypoints_size());
const float x_offset = 0.4f;
const float y_offset = 0.3f;
const float tolerance = 1e-5f;
for (int i = 0; i < object3.keypoints_size(); ++i) {
const auto& point_2d =
tracked_detection.annotations(0).keypoints(i).point_2d();
EXPECT_NEAR(point_2d.x(), object3.keypoints(i).point_2d().x() + x_offset,
tolerance);
EXPECT_NEAR(point_2d.y(), object3.keypoints(i).point_2d().y() + y_offset,
tolerance);
}
}
} // namespace
} // namespace mediapipe
@@ -24,9 +24,9 @@
#include "mediapipe/framework/port/status.h"
#include "mediapipe/gpu/gl_calculator_helper.h"
#include "mediapipe/gpu/shader_util.h"
#include "mediapipe/graphs/object_detection_3d/calculators/camera_parameters.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/gl_animation_overlay_calculator.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/model_matrix.pb.h"
#include "mediapipe/modules/objectron/calculators/camera_parameters.pb.h"
namespace mediapipe {
@@ -626,7 +626,7 @@ void GlAnimationOverlayCalculator::LoadModelMatrices(
::mediapipe::Status GlAnimationOverlayCalculator::Process(
CalculatorContext *cc) {
return helper_.RunInGlContext([this, &cc]() -> ::mediapipe::Status {
return helper_.RunInGlContext([this, &cc]() -> mediapipe::Status {
if (!initialized_) {
MP_RETURN_IF_ERROR(GlSetup());
initialized_ = true;
@@ -1,76 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
#include "absl/memory/memory.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/status.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
namespace mediapipe {
namespace {
constexpr char kInputLandmarksTag[] = "LANDMARKS";
constexpr char kOutputFrameAnnotationTag[] = "FRAME_ANNOTATION";
} // namespace
// A calculator that converts NormalizedLandmarkList to FrameAnnotation proto.
class LandmarksToFrameAnnotationCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Process(CalculatorContext* cc) override;
};
REGISTER_CALCULATOR(LandmarksToFrameAnnotationCalculator);
::mediapipe::Status LandmarksToFrameAnnotationCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(!cc->Inputs().GetTags().empty());
RET_CHECK(!cc->Outputs().GetTags().empty());
if (cc->Inputs().HasTag(kInputLandmarksTag)) {
cc->Inputs().Tag(kInputLandmarksTag).Set<NormalizedLandmarkList>();
}
if (cc->Outputs().HasTag(kOutputFrameAnnotationTag)) {
cc->Outputs().Tag(kOutputFrameAnnotationTag).Set<FrameAnnotation>();
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status LandmarksToFrameAnnotationCalculator::Process(
CalculatorContext* cc) {
auto frame_annotation = absl::make_unique<FrameAnnotation>();
auto* box_annotation = frame_annotation->add_annotations();
const auto& landmarks =
cc->Inputs().Tag(kInputLandmarksTag).Get<NormalizedLandmarkList>();
RET_CHECK_GT(landmarks.landmark_size(), 0)
<< "Input landmark vector is empty.";
for (int i = 0; i < landmarks.landmark_size(); ++i) {
auto* point2d = box_annotation->add_keypoints()->mutable_point_2d();
point2d->set_x(landmarks.landmark(i).x());
point2d->set_y(landmarks.landmark(i).y());
}
// Output
if (cc->Outputs().HasTag(kOutputFrameAnnotationTag)) {
cc->Outputs()
.Tag(kOutputFrameAnnotationTag)
.Add(frame_annotation.release(), cc->InputTimestamp());
}
return ::mediapipe::OkStatus();
}
} // namespace mediapipe
@@ -1,168 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <unordered_map>
#include <vector>
#include "Eigen/Dense"
#include "absl/memory/memory.h"
#include "absl/strings/str_format.h"
#include "absl/types/span.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/deps/file_path.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/decoder.h"
#include "mediapipe/graphs/object_detection_3d/calculators/lift_2d_frame_annotation_to_3d_calculator.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/tensor_util.h"
namespace {
constexpr char kInputStreamTag[] = "FRAME_ANNOTATION";
constexpr char kOutputStreamTag[] = "LIFTED_FRAME_ANNOTATION";
// Each detection object will be assigned an unique id that starts from 1.
static int object_id = 0;
inline int GetNextObjectId() { return ++object_id; }
} // namespace
namespace mediapipe {
// Lifted the 2D points in a tracked frame annotation to 3D.
//
// Input:
// FRAME_ANNOTATIONS - Frame annotations with detected 2D points
// Output:
// LIFTED_FRAME_ANNOTATIONS - Result FrameAnnotation with lifted 3D points.
//
// Usage example:
// node {
// calculator: "Lift2DFrameAnnotationTo3DCalculator"
// input_stream: "FRAME_ANNOTATIONS:tracked_annotations"
// output_stream: "LIFTED_FRAME_ANNOTATIONS:lifted_3d_annotations"
// }
class Lift2DFrameAnnotationTo3DCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
::mediapipe::Status Close(CalculatorContext* cc) override;
private:
::mediapipe::Status ProcessCPU(CalculatorContext* cc,
FrameAnnotation* output_objects);
::mediapipe::Status LoadOptions(CalculatorContext* cc);
// Increment and assign object ID for each detected object.
// In a single MediaPipe session, the IDs are unique.
// Also assign timestamp for the FrameAnnotation to be the input packet
// timestamp.
void AssignObjectIdAndTimestamp(int64 timestamp_us,
FrameAnnotation* annotation);
std::unique_ptr<Decoder> decoder_;
::mediapipe::Lift2DFrameAnnotationTo3DCalculatorOptions options_;
Eigen::Matrix<float, 4, 4, Eigen::RowMajor> projection_matrix_;
};
REGISTER_CALCULATOR(Lift2DFrameAnnotationTo3DCalculator);
::mediapipe::Status Lift2DFrameAnnotationTo3DCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(cc->Inputs().HasTag(kInputStreamTag));
RET_CHECK(cc->Outputs().HasTag(kOutputStreamTag));
cc->Inputs().Tag(kInputStreamTag).Set<FrameAnnotation>();
cc->Outputs().Tag(kOutputStreamTag).Set<FrameAnnotation>();
return ::mediapipe::OkStatus();
}
::mediapipe::Status Lift2DFrameAnnotationTo3DCalculator::Open(
CalculatorContext* cc) {
MP_RETURN_IF_ERROR(LoadOptions(cc));
// clang-format off
projection_matrix_ <<
1.5731, 0, 0, 0,
0, 2.0975, 0, 0,
0, 0, -1.0002, -0.2,
0, 0, -1, 0;
// clang-format on
decoder_ = absl::make_unique<Decoder>(
BeliefDecoderConfig(options_.decoder_config()));
return ::mediapipe::OkStatus();
}
::mediapipe::Status Lift2DFrameAnnotationTo3DCalculator::Process(
CalculatorContext* cc) {
if (cc->Inputs().Tag(kInputStreamTag).IsEmpty()) {
return ::mediapipe::OkStatus();
}
auto output_objects = absl::make_unique<FrameAnnotation>();
MP_RETURN_IF_ERROR(ProcessCPU(cc, output_objects.get()));
// Output
if (cc->Outputs().HasTag(kOutputStreamTag)) {
cc->Outputs()
.Tag(kOutputStreamTag)
.Add(output_objects.release(), cc->InputTimestamp());
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status Lift2DFrameAnnotationTo3DCalculator::ProcessCPU(
CalculatorContext* cc, FrameAnnotation* output_objects) {
const auto& input_frame_annotations =
cc->Inputs().Tag(kInputStreamTag).Get<FrameAnnotation>();
// Copy the input frame annotation to the output
*output_objects = input_frame_annotations;
auto status = decoder_->Lift2DTo3D(projection_matrix_, /*portrait*/ true,
output_objects);
if (!status.ok()) {
LOG(ERROR) << status;
return status;
}
AssignObjectIdAndTimestamp(cc->InputTimestamp().Microseconds(),
output_objects);
return ::mediapipe::OkStatus();
}
::mediapipe::Status Lift2DFrameAnnotationTo3DCalculator::Close(
CalculatorContext* cc) {
return ::mediapipe::OkStatus();
}
::mediapipe::Status Lift2DFrameAnnotationTo3DCalculator::LoadOptions(
CalculatorContext* cc) {
// Get calculator options specified in the graph.
options_ =
cc->Options<::mediapipe::Lift2DFrameAnnotationTo3DCalculatorOptions>();
return ::mediapipe::OkStatus();
}
void Lift2DFrameAnnotationTo3DCalculator::AssignObjectIdAndTimestamp(
int64 timestamp_us, FrameAnnotation* annotation) {
for (auto& ann : *annotation->mutable_annotations()) {
ann.set_object_id(GetNextObjectId());
}
annotation->set_timestamp(timestamp_us);
}
} // namespace mediapipe
@@ -1,30 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The option proto for the Lift2DFrameAnnotationTo3DCalculatorOptions.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
import "mediapipe/graphs/object_detection_3d/calculators/belief_decoder_config.proto";
message Lift2DFrameAnnotationTo3DCalculatorOptions {
extend CalculatorOptions {
optional Lift2DFrameAnnotationTo3DCalculatorOptions ext = 290166284;
}
optional BeliefDecoderConfig decoder_config = 1;
}
@@ -1,101 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/graphs/object_detection_3d/calculators/model.h"
#include "mediapipe/framework/port/logging.h"
namespace mediapipe {
void Model::SetTransformation(const Eigen::Matrix4f& transform) {
transformation_ = transform;
}
void Model::SetTranslation(const Eigen::Vector3f& translation) {
transformation_.col(3).template head<3>() = translation;
}
void Model::SetRotation(float roll, float pitch, float yaw) {
// In our coordinate system, Y is up. We first rotate the object around Y
// (yaw), then around Z (pitch), and finally around X (roll).
Eigen::Matrix3f r;
r = Eigen::AngleAxisf(yaw, Eigen::Vector3f::UnitY()) *
Eigen::AngleAxisf(pitch, Eigen::Vector3f::UnitZ()) *
Eigen::AngleAxisf(roll, Eigen::Vector3f::UnitX());
transformation_.topLeftCorner<3, 3>() = r;
}
void Model::SetRotation(const Eigen::Matrix3f& rotation) {
transformation_.topLeftCorner<3, 3>() = rotation;
}
void Model::SetScale(const Eigen::Vector3f& scale) { scale_ = scale; }
void Model::SetCategory(const std::string& category) { category_ = category; }
const Eigen::Vector3f Model::GetRotationAngles() const {
Vector3f ypr = transformation_.topLeftCorner<3, 3>().eulerAngles(1, 2, 0);
return Vector3f(ypr(2), ypr(1), ypr(0)); // swap YPR with RPY
}
const Eigen::Matrix4f& Model::GetTransformation() const {
return transformation_;
}
const Eigen::Vector3f& Model::GetScale() const { return scale_; }
const Eigen::Ref<const Eigen::Vector3f> Model::GetTranslation() const {
return transformation_.col(3).template head<3>();
}
const Eigen::Ref<const Eigen::Matrix3f> Model::GetRotation() const {
return transformation_.template topLeftCorner<3, 3>();
}
const std::string& Model::GetCategory() const { return category_; }
void Model::Deserialize(const Object& obj) {
CHECK_EQ(obj.rotation_size(), 9);
CHECK_EQ(obj.translation_size(), 3);
CHECK_EQ(obj.scale_size(), 3);
category_ = obj.category();
using RotationMatrix = Eigen::Matrix<float, 3, 3, Eigen::RowMajor>;
transformation_.setIdentity();
transformation_.topLeftCorner<3, 3>() =
Eigen::Map<const RotationMatrix>(obj.rotation().data());
transformation_.col(3).head<3>() =
Eigen::Map<const Eigen::Vector3f>(obj.translation().data());
scale_ = Eigen::Map<const Eigen::Vector3f>(obj.scale().data());
Update();
}
void Model::Serialize(Object* obj) {
obj->set_category(category_);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
obj->add_rotation(transformation_(i, j));
}
}
for (int i = 0; i < 3; ++i) {
obj->add_translation(transformation_(i, 3));
}
for (int i = 0; i < 3; ++i) {
obj->add_scale(scale_[i]);
}
}
} // namespace mediapipe
@@ -1,92 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_MODEL_H_
#define MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_MODEL_H_
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/object.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/types.h"
namespace mediapipe {
class Model {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
enum Type {
kVisualizationOnly = 0,
kBoundingBox,
kSkeleton,
kShape, // Shape is a virtual object.
kNumModes,
};
virtual ~Model() = default;
virtual void SetTransformation(const Eigen::Matrix4f& transform);
virtual void SetTranslation(const Eigen::Vector3f& translation);
// Compute the rotation matrix from these angles and update the transformation
// matrix accordingly
virtual void SetRotation(float roll, float pitch, float yaw);
virtual void SetRotation(const Eigen::Matrix3f& rotation);
virtual void SetScale(const Eigen::Vector3f& scale);
virtual void SetCategory(const std::string& category);
virtual size_t GetNumberKeypoints() const { return number_keypoints_; }
// Gets Euler angles in the order of roll, pitch, yaw.
virtual const Eigen::Vector3f GetRotationAngles() const;
virtual const Eigen::Matrix4f& GetTransformation() const;
virtual const Eigen::Vector3f& GetScale() const;
virtual const Eigen::Ref<const Eigen::Vector3f> GetTranslation() const;
virtual const Eigen::Ref<const Eigen::Matrix3f> GetRotation() const;
virtual const std::string& GetCategory() const;
// Update the model's keypoints in the world-coordinate system.
// The update includes transforming the model to the world-coordinate system
// as well as scaling the model.
// The user is expected to call this function after Setting the rotation,
// orientation or the scale of the model to get an updated model.
virtual void Update() = 0;
// Update the model's parameters (orientation, position, and scale) from the
// user-provided variables.
virtual void Adjust(const std::vector<float>& variables) = 0;
// Returns a pointer to the model's keypoints.
// Use Eigen::Map to cast the pointer back to Vector3 or Vector4
virtual const float* GetVertex(size_t id) const = 0;
virtual float* GetVertex(size_t id) = 0;
virtual void Deserialize(const Object& obj);
virtual void Serialize(Object* obj);
// TODO: make member variables protected, and add public apis.
// 4x4 transformation matrix mapping the first keypoint to world coordinate
Eigen::Matrix4f transformation_;
Eigen::Vector3f scale_; // width, height, depth
Type model_type_;
size_t number_keypoints_;
std::string category_;
protected:
Model(Type type, size_t number_keypoints, const std::string& category)
: model_type_(type),
number_keypoints_(number_keypoints),
category_(category) {}
};
} // namespace mediapipe
#endif // MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_MODEL_H_
@@ -1,124 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package mediapipe;
message KeyPoint {
// The position of the keypoint in the local coordinate system of the rigid
// object.
float x = 1;
float y = 2;
float z = 3;
// Sphere around the keypoint, indiciating annotator's confidence of the
// position in meters.
float confidence_radius = 4;
// The name of the keypoint (e.g. legs, head, etc.).
// Does not have to be unique.
string name = 5;
// Indicates whether the keypoint is hidden or not.
bool hidden = 6;
}
message Object {
// Unique object id through a sequence. There might be multiple objects of
// the same label in this sequence.
int32 id = 1;
// Describes what category an object is. E.g. object class, attribute,
// instance or person identity. This provides additional context for the
// object type.
string category = 2;
enum Type {
UNDEFINED_TYPE = 0;
BOUNDING_BOX = 1;
SKELETON = 2;
}
Type type = 3;
// 3x3 row-major rotation matrix describing the orientation of the rigid
// object's frame of reference in the world-coordinate system.
repeated float rotation = 4;
// 3x1 vector describing the translation of the rigid object's frame of
// reference in the world-coordinate system in meters.
repeated float translation = 5;
// 3x1 vector describing the scale of the rigid object's frame of reference in
// the world-coordinate system in meters.
repeated float scale = 6;
// List of all the key points associated with this object in the object
// coordinate system.
// The first keypoint is always the object's frame of reference,
// e.g. the centroid of the box.
// E.g. bounding box with its center as frame of reference, the 9 keypoints :
// {0., 0., 0.},
// {-.5, -.5, -.5}, {-.5, -.5, +.5}, {-.5, +.5, -.5}, {-.5, +.5, +.5},
// {+.5, -.5, -.5}, {+.5, -.5, +.5}, {+.5, +.5, -.5}, {+.5, +.5, +.5}
// To get the bounding box in the world-coordinate system, we first scale the
// box then transform the scaled box.
// For example, bounding box in the world coordinate system is
// rotation * scale * keypoints + translation
repeated KeyPoint keypoints = 7;
// Enum to reflect how this object is created.
enum Method {
UNKNOWN_METHOD = 0;
ANNOTATION = 1; // Created by data annotation.
AUGMENTATION = 2; // Created by data augmentation.
}
Method method = 8;
}
// The edge connecting two keypoints together
message Edge {
// keypoint id of the edge's source
int32 source = 1;
// keypoint id of the edge's sink
int32 sink = 2;
}
// The skeleton template for different objects (e.g. humans, chairs, hands, etc)
// The annotation tool reads the skeleton template dictionary.
message Skeleton {
// The origin keypoint in the object coordinate system. (i.e. Point 0, 0, 0)
int32 reference_keypoint = 1;
// The skeleton's category (e.g. human, chair, hand.). Should be unique in the
// dictionary.
string category = 2;
// Initialization value for all the keypoints in the skeleton in the object's
// local coordinate system. Pursuit will transform these points using object's
// transformation to get the keypoint in the world-cooridnate.
repeated KeyPoint keypoints = 3;
// List of edges connecting keypoints
repeated Edge edges = 4;
}
// The list of all the modeled skeletons in our library. These models can be
// objects (chairs, desks, etc), humans (full pose, hands, faces, etc), or box.
// We can have multiple skeletons in the same file.
message Skeletons {
repeated Skeleton object = 1;
}
@@ -1,33 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "mediapipe/graphs/object_detection_3d/calculators/tensor_util.h"
#include "mediapipe/framework/port/logging.h"
namespace mediapipe {
cv::Mat ConvertTfliteTensorToCvMat(const TfLiteTensor& tensor) {
// Check tensor is BxCxWxH (size = 4) and the batch size is one(data[0] = 1)
CHECK(tensor.dims->size == 4 && tensor.dims->data[0] == 1);
CHECK_EQ(kTfLiteFloat32, tensor.type) << "tflite_tensor type is not float";
const size_t num_output_channels = tensor.dims->data[3];
const int dims = 2;
const int sizes[] = {tensor.dims->data[1], tensor.dims->data[2]};
const int type = CV_MAKETYPE(CV_32F, num_output_channels);
return cv::Mat(dims, sizes, type, reinterpret_cast<void*>(tensor.data.f));
}
} // namespace mediapipe
@@ -1,27 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_TENSOR_UTIL_H_
#define MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_TENSOR_UTIL_H_
#include "mediapipe/framework/port/opencv_core_inc.h"
#include "tensorflow/lite/interpreter.h"
namespace mediapipe {
// Converts a single channel tflite tensor to a grayscale image
cv::Mat ConvertTfliteTensorToCvMat(const TfLiteTensor& tensor);
} // namespace mediapipe
#endif // MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_TENSOR_UTIL_H_
@@ -1,216 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <unordered_map>
#include <vector>
#include "Eigen/Dense"
#include "absl/memory/memory.h"
#include "absl/strings/str_format.h"
#include "absl/types/span.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/deps/file_path.h"
#include "mediapipe/framework/port/opencv_core_inc.h"
#include "mediapipe/framework/port/ret_check.h"
#include "mediapipe/graphs/object_detection_3d/calculators/annotation_data.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/belief_decoder_config.pb.h"
#include "mediapipe/graphs/object_detection_3d/calculators/decoder.h"
#include "mediapipe/graphs/object_detection_3d/calculators/tensor_util.h"
#include "mediapipe/graphs/object_detection_3d/calculators/tflite_tensors_to_objects_calculator.pb.h"
#include "tensorflow/lite/interpreter.h"
namespace {
constexpr char kInputStreamTag[] = "TENSORS";
constexpr char kOutputStreamTag[] = "ANNOTATIONS";
// Each detection object will be assigned an unique id that starts from 1.
static int object_id = 0;
inline int GetNextObjectId() { return ++object_id; }
} // namespace
namespace mediapipe {
// Convert result TFLite tensors from deep pursuit 3d model into
// FrameAnnotation.
//
// Input:
// TENSORS - Vector of TfLiteTensor of type kTfLiteFloat32.
// Output:
// ANNOTATIONS - Result FrameAnnotation.
//
// Usage example:
// node {
// calculator: "TfLiteTensorsToObjectsCalculator"
// input_stream: "TENSORS:tensors"
// output_stream: "ANNOTATIONS:annotations"
// }
class TfLiteTensorsToObjectsCalculator : public CalculatorBase {
public:
static ::mediapipe::Status GetContract(CalculatorContract* cc);
::mediapipe::Status Open(CalculatorContext* cc) override;
::mediapipe::Status Process(CalculatorContext* cc) override;
::mediapipe::Status Close(CalculatorContext* cc) override;
private:
::mediapipe::Status ProcessCPU(CalculatorContext* cc,
FrameAnnotation* output_objects);
::mediapipe::Status LoadOptions(CalculatorContext* cc);
// Takes point_3d in FrameAnnotation, projects to 2D, and overwrite the
// point_2d field with the projection.
void Project3DTo2D(bool portrait, FrameAnnotation* annotation) const;
// Increment and assign object ID for each detected object.
// In a single MediaPipe session, the IDs are unique.
// Also assign timestamp for the FrameAnnotation to be the input packet
// timestamp.
void AssignObjectIdAndTimestamp(int64 timestamp_us,
FrameAnnotation* annotation);
int num_classes_ = 0;
int num_keypoints_ = 0;
::mediapipe::TfLiteTensorsToObjectsCalculatorOptions options_;
std::unique_ptr<Decoder> decoder_;
Eigen::Matrix<float, 4, 4, Eigen::RowMajor> projection_matrix_;
};
REGISTER_CALCULATOR(TfLiteTensorsToObjectsCalculator);
::mediapipe::Status TfLiteTensorsToObjectsCalculator::GetContract(
CalculatorContract* cc) {
RET_CHECK(!cc->Inputs().GetTags().empty());
RET_CHECK(!cc->Outputs().GetTags().empty());
if (cc->Inputs().HasTag(kInputStreamTag)) {
cc->Inputs().Tag(kInputStreamTag).Set<std::vector<TfLiteTensor>>();
}
if (cc->Outputs().HasTag(kOutputStreamTag)) {
cc->Outputs().Tag(kOutputStreamTag).Set<FrameAnnotation>();
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status TfLiteTensorsToObjectsCalculator::Open(
CalculatorContext* cc) {
MP_RETURN_IF_ERROR(LoadOptions(cc));
// clang-format off
projection_matrix_ <<
1.5731, 0, 0, 0,
0, 2.0975, 0, 0,
0, 0, -1.0002, -0.2,
0, 0, -1, 0;
// clang-format on
decoder_ = absl::make_unique<Decoder>(
BeliefDecoderConfig(options_.decoder_config()));
return ::mediapipe::OkStatus();
}
::mediapipe::Status TfLiteTensorsToObjectsCalculator::Process(
CalculatorContext* cc) {
if (cc->Inputs().Tag(kInputStreamTag).IsEmpty()) {
return ::mediapipe::OkStatus();
}
auto output_objects = absl::make_unique<FrameAnnotation>();
MP_RETURN_IF_ERROR(ProcessCPU(cc, output_objects.get()));
// Output
if (cc->Outputs().HasTag(kOutputStreamTag)) {
cc->Outputs()
.Tag(kOutputStreamTag)
.Add(output_objects.release(), cc->InputTimestamp());
}
return ::mediapipe::OkStatus();
}
::mediapipe::Status TfLiteTensorsToObjectsCalculator::ProcessCPU(
CalculatorContext* cc, FrameAnnotation* output_objects) {
const auto& input_tensors =
cc->Inputs().Tag(kInputStreamTag).Get<std::vector<TfLiteTensor>>();
cv::Mat prediction_heatmap = ConvertTfliteTensorToCvMat(input_tensors[0]);
cv::Mat offsetmap = ConvertTfliteTensorToCvMat(input_tensors[1]);
*output_objects =
decoder_->DecodeBoundingBoxKeypoints(prediction_heatmap, offsetmap);
auto status = decoder_->Lift2DTo3D(projection_matrix_, /*portrait*/ true,
output_objects);
if (!status.ok()) {
LOG(ERROR) << status;
return status;
}
Project3DTo2D(/*portrait*/ true, output_objects);
AssignObjectIdAndTimestamp(cc->InputTimestamp().Microseconds(),
output_objects);
return ::mediapipe::OkStatus();
}
::mediapipe::Status TfLiteTensorsToObjectsCalculator::Close(
CalculatorContext* cc) {
return ::mediapipe::OkStatus();
}
::mediapipe::Status TfLiteTensorsToObjectsCalculator::LoadOptions(
CalculatorContext* cc) {
// Get calculator options specified in the graph.
options_ =
cc->Options<::mediapipe::TfLiteTensorsToObjectsCalculatorOptions>();
num_classes_ = options_.num_classes();
num_keypoints_ = options_.num_keypoints();
// Currently only support 2D when num_values_per_keypoint equals to 2.
CHECK_EQ(options_.num_values_per_keypoint(), 2);
return ::mediapipe::OkStatus();
}
void TfLiteTensorsToObjectsCalculator::Project3DTo2D(
bool portrait, FrameAnnotation* annotation) const {
for (auto& ann : *annotation->mutable_annotations()) {
for (auto& key_point : *ann.mutable_keypoints()) {
Eigen::Vector4f point3d;
point3d << key_point.point_3d().x(), key_point.point_3d().y(),
key_point.point_3d().z(), 1.0f;
Eigen::Vector4f point3d_projection = projection_matrix_ * point3d;
float u, v;
const float inv_w = 1.0f / point3d_projection(3);
if (portrait) {
u = (point3d_projection(1) * inv_w + 1.0f) * 0.5f;
v = (point3d_projection(0) * inv_w + 1.0f) * 0.5f;
} else {
u = (point3d_projection(0) * inv_w + 1.0f) * 0.5f;
v = (1.0f - point3d_projection(1) * inv_w) * 0.5f;
}
key_point.mutable_point_2d()->set_x(u);
key_point.mutable_point_2d()->set_y(v);
}
}
}
void TfLiteTensorsToObjectsCalculator::AssignObjectIdAndTimestamp(
int64 timestamp_us, FrameAnnotation* annotation) {
for (auto& ann : *annotation->mutable_annotations()) {
ann.set_object_id(GetNextObjectId());
}
annotation->set_timestamp(timestamp_us);
}
} // namespace mediapipe
@@ -1,39 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The option proto for the TfLiteTensorsToObjectsCalculatorOptions.
syntax = "proto2";
package mediapipe;
import "mediapipe/framework/calculator.proto";
import "mediapipe/graphs/object_detection_3d/calculators/belief_decoder_config.proto";
message TfLiteTensorsToObjectsCalculatorOptions {
extend CalculatorOptions {
optional TfLiteTensorsToObjectsCalculatorOptions ext = 263667646;
}
// The number of output classes predicted by the detection model.
optional int32 num_classes = 1;
// The number of predicted keypoints.
optional int32 num_keypoints = 2;
// The dimension of each keypoint, e.g. number of values predicted for each
// keypoint.
optional int32 num_values_per_keypoint = 3 [default = 2];
optional BeliefDecoderConfig decoder_config = 4;
}
@@ -1,56 +0,0 @@
// Copyright 2020 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_TYPES_H_
#define MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_TYPES_H_
#include <array>
#include "Eigen/Geometry"
namespace mediapipe {
using Eigen::Map;
using Eigen::Vector2f;
using Eigen::Vector3f;
using Eigen::Vector4f;
using Matrix4f_RM = Eigen::Matrix<float, 4, 4, Eigen::RowMajor>;
using Matrix3f_RM = Eigen::Matrix<float, 3, 3, Eigen::RowMajor>;
using Face = std::array<int, 4>;
struct SuperPoint {
enum PointSourceType { kPointCloud = 0, kBoundingBox = 1, kSkeleton = 2 };
// The id of the point in the point-cloud
int reference_point;
// The source of the
PointSourceType source;
// The id of the point in set of points in current frame
int id;
// If source is kBoundingBox or kSkeleton, object_id stores the id of which \
// object this point belongs to.
int object_id;
// projected u-v value
Vector2f uv;
Vector2f pixel;
// the 3D point
Vector3f point_3d;
// Color
Eigen::Matrix<unsigned char, 4, 1> color;
bool rendered;
};
} // namespace mediapipe
#endif // MEDIAPIPE_GRAPHS_OBJECT_DETECTION_3D_TYPES_H_
@@ -2,8 +2,15 @@
# Images coming into and out of the graph.
input_stream: "input_video"
input_stream: "input_width"
input_stream: "input_height"
input_stream: "WIDTH:input_width"
input_stream: "HEIGHT:input_height"
input_side_packet: "LABELS_CSV:allowed_labels"
input_side_packet: "MODEL_SCALE:model_scale"
input_side_packet: "MODEL_TRANSFORMATION:model_transformation"
input_side_packet: "TEXTURE:box_texture"
input_side_packet: "ANIMATION_ASSET:box_asset_name"
input_side_packet: "MASK_TEXTURE:obj_texture"
input_side_packet: "MASK_ASSET:obj_asset_name"
output_stream: "output_video"
# Throttles the images flowing downstream for flow control. It passes through
@@ -19,7 +26,7 @@ output_stream: "output_video"
node {
calculator: "FlowLimiterCalculator"
input_stream: "input_video"
input_stream: "FINISHED:box_rect"
input_stream: "FINISHED:lifted_objects"
input_stream_info: {
tag_index: "FINISHED"
back_edge: true
@@ -31,7 +38,7 @@ node {
node: {
calculator: "ImageCroppingCalculator"
input_stream: "IMAGE_GPU:throttled_input_video"
output_stream: "IMAGE_GPU:throttled_input_video_4x3"
output_stream: "IMAGE_GPU:throttled_input_video_3x4"
input_stream: "WIDTH:input_width"
input_stream: "HEIGHT:input_height"
node_options: {
@@ -41,87 +48,11 @@ node: {
}
}
# Caches a box-presence decision fed back from boxLandmarkSubgraph, and upon
# the arrival of the next input image sends out the cached decision with the
# timestamp replaced by that of the input image, essentially generating a packet
# that carries the previous box-presence decision. Note that upon the arrival
# of the very first input image, an empty packet is sent out to jump start the
# feedback loop.
node {
calculator: "PreviousLoopbackCalculator"
input_stream: "MAIN:throttled_input_video_4x3"
input_stream: "LOOP:box_presence"
input_stream_info: {
tag_index: "LOOP"
back_edge: true
}
output_stream: "PREV_LOOP:prev_box_presence"
}
# Drops the incoming image if boxLandmarkSubgraph was able to identify box
# presence in the previous image. Otherwise, passes the incoming image through
# to trigger a new round of box detection in boxDetectionSubgraph.
node {
calculator: "GateCalculator"
input_stream: "throttled_input_video_4x3"
input_stream: "DISALLOW:prev_box_presence"
output_stream: "detection_input_video"
node_options: {
[type.googleapis.com/mediapipe.GateCalculatorOptions] {
empty_packets_as_allow: true
}
}
}
# Subgraph that performs 2D object detection.
node {
calculator: "ObjectDetectionOidV4Subgraph"
input_stream: "detection_input_video"
input_side_packet: "allowed_labels"
output_stream: "NORM_RECT:box_rect_from_object_detections"
}
# Subgraph that localizes box landmarks (see subgraphs/box_landmark_gpu.pbtxt).
node {
calculator: "BoxLandmarkSubgraph"
input_stream: "IMAGE:throttled_input_video_4x3"
input_stream: "NORM_RECT:box_rect"
calculator: "ObjectronGpuSubgraph"
input_stream: "IMAGE_GPU:throttled_input_video_3x4"
input_side_packet: "LABELS_CSV:allowed_labels"
output_stream: "FRAME_ANNOTATION:lifted_objects"
output_stream: "NORM_RECT:box_rect_from_landmarks"
output_stream: "PRESENCE:box_presence"
}
# Caches a box rectangle fed back from boxLandmarkSubgraph, and upon the
# arrival of the next input image sends out the cached rectangle with the
# timestamp replaced by that of the input image, essentially generating a packet
# that carries the previous box rectangle. Note that upon the arrival of the
# very first input image, an empty packet is sent out to jump start the
# feedback loop.
node {
calculator: "PreviousLoopbackCalculator"
input_stream: "MAIN:throttled_input_video_4x3"
input_stream: "LOOP:box_rect_from_landmarks"
input_stream_info: {
tag_index: "LOOP"
back_edge: true
}
output_stream: "PREV_LOOP:prev_box_rect_from_landmarks"
}
# Merges a stream of box rectangles generated by boxDetectionSubgraph and that
# generated by boxLandmarkSubgraph into a single output stream by selecting
# between one of the two streams. The former is selected if the incoming packet
# is not empty, i.e., box detection is performed on the current image by
# boxDetectionSubgraph (because boxLandmarkSubgraph could not identify box
# presence in the previous image). Otherwise, the latter is selected, which is
# never empty because boxLandmarkSubgraphs processes all images (that went
# through FlowLimiterCaculator).
node {
calculator: "MergeCalculator"
input_stream: "box_rect_from_object_detections"
input_stream: "prev_box_rect_from_landmarks"
output_stream: "box_rect"
}
# The rendering nodes:
@@ -171,7 +102,7 @@ node {
# then we render the occlusion mask.
node: {
calculator: "GlAnimationOverlayCalculator"
input_stream: "VIDEO:throttled_input_video_4x3"
input_stream: "VIDEO:throttled_input_video_3x4"
input_stream: "MODEL_MATRICES:model_matrices"
input_stream: "MASK_MODEL_MATRICES:mask_model_matrices"
output_stream: "output_video"
@@ -44,13 +44,13 @@ node {
}
node {
calculator: "ObjectronDetectionSubgraphGpu"
calculator: "ObjectronDetection1StageSubgraphGpu"
input_stream: "IMAGE_GPU:sampled_input_video"
output_stream: "ANNOTATIONS:objects"
}
node {
calculator: "ObjectronTrackingSubgraphGpu"
calculator: "ObjectronTracking1StageSubgraphGpu"
input_stream: "FRAME_ANNOTATION:objects"
input_stream: "IMAGE_GPU:input_video_copy"
output_stream: "LIFTED_FRAME_ANNOTATION:lifted_tracked_objects"
@@ -0,0 +1,60 @@
# MediaPipe Objectron 3D object detection on Desktop CPU.
input_side_packet: "INPUT_FILE_PATH:input_video_path"
input_side_packet: "FILE_PATH:0:box_landmark_model_path"
input_side_packet: "LABELS_CSV:allowed_labels"
input_side_packet: "OUTPUT_FILE_PATH:output_video_path"
# Decodes an input video file into images and a video header.
node {
calculator: "OpenCvVideoDecoderCalculator"
input_side_packet: "INPUT_FILE_PATH:input_video_path"
output_stream: "VIDEO:input_video"
output_stream: "VIDEO_PRESTREAM:input_video_header"
}
node {
calculator: "LocalFileContentsCalculator"
input_side_packet: "FILE_PATH:0:box_landmark_model_path"
output_side_packet: "CONTENTS:0:box_landmark_model_blob"
}
node {
calculator: "TfLiteModelCalculator"
input_side_packet: "MODEL_BLOB:box_landmark_model_blob"
output_side_packet: "MODEL:box_landmark_model"
}
node {
calculator: "ObjectronCpuSubgraph"
input_stream: "IMAGE:input_video"
input_side_packet: "MODEL:box_landmark_model"
input_side_packet: "LABELS_CSV:allowed_labels"
output_stream: "LANDMARKS:box_landmarks"
output_stream: "NORM_RECT:box_rect"
}
# Subgraph that renders annotations and overlays them on top of the input
# images (see renderer_gpu.pbtxt).
node {
calculator: "RendererSubgraph"
input_stream: "IMAGE:input_video"
input_stream: "LANDMARKS:box_landmarks"
input_stream: "NORM_RECT:box_rect"
output_stream: "IMAGE:output_video"
}
# Encodes the annotated images into a video file, adopting properties specified
# in the input video header, e.g., video framerate.
node {
calculator: "OpenCvVideoEncoderCalculator"
input_stream: "VIDEO:output_video"
input_stream: "VIDEO_PRESTREAM:input_video_header"
input_side_packet: "OUTPUT_FILE_PATH:output_video_path"
node_options: {
[type.googleapis.com/mediapipe.OpenCvVideoEncoderCalculatorOptions]: {
codec: "avc1"
video_format: "mp4"
}
}
}
@@ -22,76 +22,14 @@ licenses(["notice"])
package(default_visibility = ["//visibility:public"])
mediapipe_simple_subgraph(
name = "objectron_detection_gpu",
graph = "objectron_detection_gpu.pbtxt",
register_as = "ObjectronDetectionSubgraphGpu",
name = "renderer_cpu",
graph = "renderer_cpu.pbtxt",
register_as = "RendererSubgraph",
visibility = ["//visibility:public"],
deps = [
"//mediapipe/calculators/image:image_transformation_calculator",
"//mediapipe/calculators/tflite:tflite_converter_calculator",
"//mediapipe/calculators/tflite:tflite_custom_op_resolver_calculator",
"//mediapipe/calculators/tflite:tflite_inference_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:tflite_tensors_to_objects_calculator",
],
)
mediapipe_simple_subgraph(
name = "objectron_tracking_gpu",
graph = "objectron_tracking_gpu.pbtxt",
register_as = "ObjectronTrackingSubgraphGpu",
deps = [
"//mediapipe/calculators/image:image_transformation_calculator",
"//mediapipe/calculators/video:box_tracker_calculator",
"//mediapipe/calculators/video:flow_packager_calculator",
"//mediapipe/calculators/video:motion_analysis_calculator",
"//mediapipe/framework/stream_handler:sync_set_input_stream_handler",
"//mediapipe/gpu:gpu_buffer_to_image_frame_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:frame_annotation_to_timed_box_list_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:frame_annotation_tracker_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:lift_2d_frame_annotation_to_3d_calculator",
],
)
mediapipe_simple_subgraph(
name = "box_landmark_gpu",
graph = "box_landmark_gpu.pbtxt",
register_as = "BoxLandmarkSubgraph",
deps = [
"//mediapipe/calculators/core:split_vector_calculator",
"//mediapipe/calculators/image:image_cropping_calculator",
"//mediapipe/calculators/image:image_properties_calculator",
"//mediapipe/calculators/image:image_transformation_calculator",
"//mediapipe/calculators/tflite:tflite_converter_calculator",
"//mediapipe/calculators/tflite:tflite_custom_op_resolver_calculator",
"//mediapipe/calculators/tflite:tflite_inference_calculator",
"//mediapipe/calculators/tflite:tflite_tensors_to_floats_calculator",
"//mediapipe/calculators/tflite:tflite_tensors_to_landmarks_calculator",
"//mediapipe/calculators/util:detections_to_rects_calculator",
"//mediapipe/calculators/util:landmark_letterbox_removal_calculator",
"//mediapipe/calculators/util:landmark_projection_calculator",
"//mediapipe/calculators/util:landmarks_smoothing_calculator",
"//mediapipe/calculators/util:landmarks_to_detection_calculator",
"//mediapipe/calculators/util:rect_transformation_calculator",
"//mediapipe/calculators/util:thresholding_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:frame_annotation_to_rect_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:landmarks_to_frame_annotation_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:lift_2d_frame_annotation_to_3d_calculator",
],
)
mediapipe_simple_subgraph(
name = "object_detection_oid_v4_gpu",
graph = "object_detection_oid_v4_gpu.pbtxt",
register_as = "ObjectDetectionOidV4Subgraph",
deps = [
"//mediapipe/calculators/image:image_transformation_calculator",
"//mediapipe/calculators/tflite:ssd_anchors_calculator",
"//mediapipe/calculators/tflite:tflite_converter_calculator",
"//mediapipe/calculators/tflite:tflite_inference_calculator",
"//mediapipe/calculators/tflite:tflite_tensors_to_detections_calculator",
"//mediapipe/calculators/util:detection_label_id_to_text_calculator",
"//mediapipe/calculators/util:detections_to_rects_calculator",
"//mediapipe/calculators/util:non_max_suppression_calculator",
"//mediapipe/calculators/util:rect_transformation_calculator",
"//mediapipe/graphs/object_detection_3d/calculators:filter_detection_calculator",
"//mediapipe/calculators/util:annotation_overlay_calculator",
"//mediapipe/calculators/util:detections_to_render_data_calculator",
"//mediapipe/calculators/util:landmarks_to_render_data_calculator",
"//mediapipe/calculators/util:rect_to_render_data_calculator",
],
)
@@ -1,205 +0,0 @@
# MediaPipe Box landmark localization subgraph.
type: "BoxLandmarkSubgraph"
input_stream: "IMAGE:input_video"
input_stream: "NORM_RECT:box_rect"
output_stream: "FRAME_ANNOTATION:lifted_box"
output_stream: "NORM_RECT:box_rect_for_next_frame"
output_stream: "PRESENCE:box_presence"
# Crops the rectangle that contains a box from the input image.
node {
calculator: "ImageCroppingCalculator"
input_stream: "IMAGE_GPU:input_video"
input_stream: "NORM_RECT:box_rect"
output_stream: "IMAGE_GPU:box_image"
node_options: {
[type.googleapis.com/mediapipe.ImageCroppingCalculatorOptions] {
border_mode: BORDER_REPLICATE
}
}
}
# Transforms the input image on GPU to a 256x256 image. To scale the input
# image, the scale_mode option is set to FIT to preserve the aspect ratio,
# resulting in potential letterboxing in the transformed image.
node: {
calculator: "ImageTransformationCalculator"
input_stream: "IMAGE_GPU:box_image"
output_stream: "IMAGE_GPU:transformed_box_image"
output_stream: "LETTERBOX_PADDING:letterbox_padding"
node_options: {
[type.googleapis.com/mediapipe.ImageTransformationCalculatorOptions] {
output_width: 224
output_height: 224
scale_mode: FIT
}
}
}
# Converts the transformed input image on GPU into an image tensor stored as a
# TfLiteTensor.
node {
calculator: "TfLiteConverterCalculator"
input_stream: "IMAGE_GPU:transformed_box_image"
output_stream: "TENSORS_GPU:image_tensor"
node_options: {
[type.googleapis.com/mediapipe.TfLiteConverterCalculatorOptions] {
zero_center: false
}
}
}
# Generates a single side packet containing a TensorFlow Lite op resolver that
# supports custom ops needed by the model used in this graph.
node {
calculator: "TfLiteCustomOpResolverCalculator"
output_side_packet: "opresolver"
}
# Runs a TensorFlow Lite model on GPU that takes an image tensor and outputs a
# vector of tensors representing, for instance, detection boxes/keypoints and
# scores.
node {
calculator: "TfLiteInferenceCalculator"
input_stream: "TENSORS_GPU:image_tensor"
output_stream: "TENSORS:output_tensors"
input_side_packet: "CUSTOM_OP_RESOLVER:opresolver"
node_options: {
[type.googleapis.com/mediapipe.TfLiteInferenceCalculatorOptions] {
model_path: "object_detection_3d.tflite"
use_gpu: true
}
}
}
# Splits a vector of tensors into multiple vectors.
node {
calculator: "SplitTfLiteTensorVectorCalculator"
input_stream: "output_tensors"
output_stream: "landmark_tensors"
output_stream: "box_flag_tensor"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 0 end: 1 }
ranges: { begin: 1 end: 2 }
}
}
}
# Converts the box-flag tensor into a float that represents the confidence
# score of box presence.
node {
calculator: "TfLiteTensorsToFloatsCalculator"
input_stream: "TENSORS:box_flag_tensor"
output_stream: "FLOAT:box_presence_score"
}
# Applies a threshold to the confidence score to determine whether a box is
# present.
node {
calculator: "ThresholdingCalculator"
input_stream: "FLOAT:box_presence_score"
output_stream: "FLAG:box_presence"
node_options: {
[type.googleapis.com/mediapipe.ThresholdingCalculatorOptions] {
threshold: 0.99
}
}
}
# Decodes the landmark tensors into a list of landmarks, where the landmark
# coordinates are normalized by the size of the input image to the model.
node {
calculator: "TfLiteTensorsToLandmarksCalculator"
input_stream: "TENSORS:landmark_tensors"
output_stream: "NORM_LANDMARKS:landmarks"
node_options: {
[type.googleapis.com/mediapipe.TfLiteTensorsToLandmarksCalculatorOptions] {
num_landmarks: 9
input_image_width: 224
input_image_height: 224
}
}
}
# Adjusts landmarks (already normalized to [0.f, 1.f]) on the letterboxed box
# image (after image transformation with the FIT scale mode) to the
# corresponding locations on the same image with the letterbox removed (box
# image before image transformation).
node {
calculator: "LandmarkLetterboxRemovalCalculator"
input_stream: "LANDMARKS:landmarks"
input_stream: "LETTERBOX_PADDING:letterbox_padding"
output_stream: "LANDMARKS:scaled_landmarks"
}
# Projects the landmarks from the cropped box image to the corresponding
# locations on the full image before cropping (input to the graph).
node {
calculator: "LandmarkProjectionCalculator"
input_stream: "NORM_LANDMARKS:scaled_landmarks"
input_stream: "NORM_RECT:box_rect"
output_stream: "NORM_LANDMARKS:box_landmarks"
}
# Extracts image size from the input images.
node {
calculator: "ImagePropertiesCalculator"
input_stream: "IMAGE_GPU:input_video"
output_stream: "SIZE:image_size"
}
# Smooth predicted landmarks coordinates.
node {
calculator: "LandmarksSmoothingCalculator"
input_stream: "NORM_LANDMARKS:box_landmarks"
input_stream: "IMAGE_SIZE:image_size"
output_stream: "NORM_FILTERED_LANDMARKS:box_landmarks_filtered"
node_options: {
[type.googleapis.com/mediapipe.LandmarksSmoothingCalculatorOptions] {
velocity_filter: {
window_size: 10
velocity_scale: 7.5
}
}
}
}
# Convert box landmarks to frame annotation.
node {
calculator: "LandmarksToFrameAnnotationCalculator"
input_stream: "LANDMARKS:box_landmarks_filtered"
output_stream: "FRAME_ANNOTATION:box_annotation"
}
# Lift the 2D landmarks to 3D using EPnP algorithm.
node {
calculator: "Lift2DFrameAnnotationTo3DCalculator"
input_stream: "FRAME_ANNOTATION:box_annotation"
output_stream: "LIFTED_FRAME_ANNOTATION:lifted_box"
}
# Get rotated rectangle from lifted box.
node {
calculator: "FrameAnnotationToRectCalculator"
input_stream: "FRAME_ANNOTATION:lifted_box"
output_stream: "NORM_RECT:rect_from_box"
}
# Expands the box rectangle so that in the next video frame it's likely to
# still contain the box even with some motion.
node {
calculator: "RectTransformationCalculator"
input_stream: "NORM_RECT:rect_from_box"
input_stream: "IMAGE_SIZE:image_size"
output_stream: "box_rect_for_next_frame"
node_options: {
[type.googleapis.com/mediapipe.RectTransformationCalculatorOptions] {
scale_x: 1.5
scale_y: 1.5
square_long: true
}
}
}
@@ -1,177 +0,0 @@
# MediaPipe Objectron object bounding box detection subgraph.
type: "ObjectDetectionSubgraph"
input_stream: "input_video"
input_side_packet: "allowed_labels"
output_stream: "NORM_RECT:box_rect_from_object_detections"
# Transforms the input image on GPU to a 320x320 image. To scale the image, by
# default it uses the STRETCH scale mode that maps the entire input image to the
# entire transformed image. As a result, image aspect ratio may be changed and
# objects in the image may be deformed (stretched or squeezed), but the object
# detection model used in this graph is agnostic to that deformation.
node: {
calculator: "ImageTransformationCalculator"
input_stream: "IMAGE_GPU:input_video"
output_stream: "IMAGE_GPU:transformed_input_video"
node_options: {
[type.googleapis.com/mediapipe.ImageTransformationCalculatorOptions] {
output_width: 300
output_height: 300
}
}
}
# Converts the transformed input image on GPU into an image tensor stored as a
# TfLiteTensor.
node {
calculator: "TfLiteConverterCalculator"
input_stream: "IMAGE_GPU:transformed_input_video"
output_stream: "TENSORS_GPU:image_tensor"
}
# Runs a TensorFlow Lite model on GPU that takes an image tensor and outputs a
# vector of tensors representing, for instance, detection boxes/keypoints and
# scores.
node {
calculator: "TfLiteInferenceCalculator"
input_stream: "TENSORS_GPU:image_tensor"
output_stream: "TENSORS_GPU:detection_tensors"
node_options: {
[type.googleapis.com/mediapipe.TfLiteInferenceCalculatorOptions] {
model_path: "object_detection_ssd_mobilenetv2_oidv4_fp16.tflite"
}
}
}
# Generates a single side packet containing a vector of SSD anchors based on
# the specification in the options.
node {
calculator: "SsdAnchorsCalculator"
output_side_packet: "anchors"
node_options: {
[type.googleapis.com/mediapipe.SsdAnchorsCalculatorOptions] {
num_layers: 6
min_scale: 0.2
max_scale: 0.95
input_size_height: 300
input_size_width: 300
anchor_offset_x: 0.5
anchor_offset_y: 0.5
strides: 16
strides: 32
strides: 64
strides: 128
strides: 256
strides: 512
aspect_ratios: 1.0
aspect_ratios: 2.0
aspect_ratios: 0.5
aspect_ratios: 3.0
aspect_ratios: 0.3333
reduce_boxes_in_lowest_layer: true
}
}
}
# Decodes the detection tensors generated by the TensorFlow Lite model, based on
# the SSD anchors and the specification in the options, into a vector of
# detections. Each detection describes a detected object.
node {
calculator: "TfLiteTensorsToDetectionsCalculator"
input_stream: "TENSORS_GPU:detection_tensors"
input_side_packet: "ANCHORS:anchors"
output_stream: "DETECTIONS:detections"
node_options: {
[type.googleapis.com/mediapipe.TfLiteTensorsToDetectionsCalculatorOptions] {
num_classes: 195
num_boxes: 1917
num_coords: 4
ignore_classes: 0
sigmoid_score: true
apply_exponential_on_box_size: true
x_scale: 10.0
y_scale: 10.0
h_scale: 5.0
w_scale: 5.0
min_score_thresh: 0.6
}
}
}
# Performs non-max suppression to remove excessive detections.
node {
calculator: "NonMaxSuppressionCalculator"
input_stream: "detections"
output_stream: "suppressed_detections"
node_options: {
[type.googleapis.com/mediapipe.NonMaxSuppressionCalculatorOptions] {
min_suppression_threshold: 0.4
max_num_detections: 1
overlap_type: INTERSECTION_OVER_UNION
return_empty_detections: true
}
}
}
# Maps detection label IDs to the corresponding label text. The label map is
# provided in the label_map_path option.
node {
calculator: "DetectionLabelIdToTextCalculator"
input_stream: "suppressed_detections"
output_stream: "labeled_detections"
node_options: {
[type.googleapis.com/mediapipe.DetectionLabelIdToTextCalculatorOptions] {
label_map_path: "object_detection_oidv4_labelmap.pbtxt"
}
}
}
node {
calculator: "FilterDetectionCalculator"
input_stream: "DETECTIONS:labeled_detections"
output_stream: "DETECTIONS:filtered_detections"
input_side_packet: "LABELS_CSV:allowed_labels"
node_options: {
[type.googleapis.com/mediapipe.FilterDetectionCalculatorOptions]: {
min_score: 0.4
}
}
}
# Extracts image size from the input images.
node {
calculator: "ImagePropertiesCalculator"
input_stream: "IMAGE_GPU:input_video"
output_stream: "SIZE:image_size"
}
# Converts results of box detection into a rectangle (normalized by image size)
# that encloses the box.
node {
calculator: "DetectionsToRectsCalculator"
input_stream: "DETECTIONS:filtered_detections"
input_stream: "IMAGE_SIZE:image_size"
output_stream: "NORM_RECT:box_rect"
node_options: {
[type.googleapis.com/mediapipe.DetectionsToRectsCalculatorOptions] {
output_zero_rect_for_empty_detections: true
}
}
}
# Expands the rectangle that contains the box so that it's likely to cover the
# entire box.
node {
calculator: "RectTransformationCalculator"
input_stream: "NORM_RECT:box_rect"
input_stream: "IMAGE_SIZE:image_size"
output_stream: "box_rect_from_object_detections"
node_options: {
[type.googleapis.com/mediapipe.RectTransformationCalculatorOptions] {
scale_x: 1.5
scale_y: 1.5
}
}
}
@@ -1,81 +0,0 @@
# MediaPipe Objectron detection gpu subgraph
type: "ObjectronDetectionSubgraphGpu"
input_stream: "IMAGE_GPU:input_video"
output_stream: "ANNOTATIONS:objects"
# Transforms the input image on GPU to a 480x640 image. To scale the input
# image, the scale_mode option is set to FIT to preserve the aspect ratio,
# resulting in potential letterboxing in the transformed image.
node: {
calculator: "ImageTransformationCalculator"
input_stream: "IMAGE_GPU:input_video"
output_stream: "IMAGE_GPU:transformed_input_video"
node_options: {
[type.googleapis.com/mediapipe.ImageTransformationCalculatorOptions] {
output_width: 480
output_height: 640
scale_mode: FIT
}
}
}
# Converts the transformed input image on GPU into an image tensor stored as a
# TfLiteTensor.
node {
calculator: "TfLiteConverterCalculator"
input_stream: "IMAGE_GPU:transformed_input_video"
output_stream: "TENSORS_GPU:image_tensor"
}
# Generates a single side packet containing a TensorFlow Lite op resolver that
# supports custom ops needed by the model used in this graph.
node {
calculator: "TfLiteCustomOpResolverCalculator"
output_side_packet: "opresolver"
node_options: {
[type.googleapis.com/mediapipe.TfLiteCustomOpResolverCalculatorOptions] {
use_gpu: true
}
}
}
# Runs a TensorFlow Lite model on GPU that takes an image tensor and outputs a
# vector of tensors representing, for instance, detection boxes/keypoints and
# scores.
node {
calculator: "TfLiteInferenceCalculator"
input_stream: "TENSORS_GPU:image_tensor"
output_stream: "TENSORS:detection_tensors"
input_side_packet: "CUSTOM_OP_RESOLVER:opresolver"
node_options: {
[type.googleapis.com/mediapipe.TfLiteInferenceCalculatorOptions] {
model_path: "object_detection_3d.tflite"
}
}
}
# Decodes the model's output tensor (the heatmap and the distance fields) to 2D
# keypoints. There are nine 2D keypoints: one center keypoint and eight vertices
# for the 3D bounding box. The calculator parameters determine's the decoder's
# sensitivity.
node {
calculator: "TfLiteTensorsToObjectsCalculator"
input_stream: "TENSORS:detection_tensors"
output_stream: "ANNOTATIONS:objects"
node_options: {
[type.googleapis.com/mediapipe.TfLiteTensorsToObjectsCalculatorOptions] {
num_classes: 1
num_keypoints: 9
decoder_config {
heatmap_threshold: 0.6
local_max_distance: 2
offset_scale_coef: 1.0
voting_radius: 2
voting_allowance: 1
voting_threshold: 0.2
}
}
}
}
@@ -1,170 +0,0 @@
# MediaPipe Objectron tracking gpu subgraph
type: "ObjectronTrackingSubgraphGpu"
input_stream: "FRAME_ANNOTATION:objects"
input_stream: "IMAGE_GPU:input_video"
output_stream: "LIFTED_FRAME_ANNOTATION:lifted_tracked_objects"
# Converts the detected keypoints to Boxes, used by the tracking subgraph.
node {
calculator: "FrameAnnotationToTimedBoxListCalculator"
input_stream: "FRAME_ANNOTATION:objects"
output_stream: "BOXES:start_pos"
}
node: {
calculator: "ImageTransformationCalculator"
input_stream: "IMAGE_GPU:input_video"
output_stream: "IMAGE_GPU:downscaled_input_video"
node_options: {
[type.googleapis.com/mediapipe.ImageTransformationCalculatorOptions] {
output_width: 240
output_height: 320
}
}
}
# Converts GPU buffer to ImageFrame for processing tracking.
node: {
calculator: "GpuBufferToImageFrameCalculator"
input_stream: "downscaled_input_video"
output_stream: "downscaled_input_video_cpu"
}
# Performs motion analysis on an incoming video stream.
node: {
calculator: "MotionAnalysisCalculator"
input_stream: "VIDEO:downscaled_input_video_cpu"
output_stream: "CAMERA:camera_motion"
output_stream: "FLOW:region_flow"
node_options: {
[type.googleapis.com/mediapipe.MotionAnalysisCalculatorOptions]: {
analysis_options {
analysis_policy: ANALYSIS_POLICY_CAMERA_MOBILE
flow_options {
fast_estimation_min_block_size: 100
top_inlier_sets: 1
frac_inlier_error_threshold: 3e-3
downsample_mode: DOWNSAMPLE_TO_INPUT_SIZE
verification_distance: 5.0
verify_long_feature_acceleration: true
verify_long_feature_trigger_ratio: 0.1
tracking_options {
max_features: 500
adaptive_extraction_levels: 2
min_eig_val_settings {
adaptive_lowest_quality_level: 2e-4
}
klt_tracker_implementation: KLT_OPENCV
}
}
}
}
}
}
# Reads optical flow fields defined in
# mediapipe/framework/formats/motion/optical_flow_field.h,
# returns a VideoFrame with 2 channels (v_x and v_y), each channel is quantized
# to 0-255.
node: {
calculator: "FlowPackagerCalculator"
input_stream: "FLOW:region_flow"
input_stream: "CAMERA:camera_motion"
output_stream: "TRACKING:tracking_data"
node_options: {
[type.googleapis.com/mediapipe.FlowPackagerCalculatorOptions]: {
flow_packager_options: {
binary_tracking_data_support: false
}
}
}
}
# Tracks box positions over time.
node: {
calculator: "BoxTrackerCalculator"
input_stream: "TRACKING:tracking_data"
input_stream: "TRACK_TIME:input_video"
input_stream: "START_POS:start_pos"
input_stream: "CANCEL_OBJECT_ID:cancel_object_id"
input_stream_info: {
tag_index: "CANCEL_OBJECT_ID"
back_edge: true
}
output_stream: "BOXES:boxes"
input_stream_handler {
input_stream_handler: "SyncSetInputStreamHandler"
options {
[mediapipe.SyncSetInputStreamHandlerOptions.ext] {
sync_set {
tag_index: "TRACKING"
tag_index: "TRACK_TIME"
}
sync_set {
tag_index: "START_POS"
}
sync_set {
tag_index: "CANCEL_OBJECT_ID"
}
}
}
}
node_options: {
[type.googleapis.com/mediapipe.BoxTrackerCalculatorOptions]: {
tracker_options: {
track_step_options {
track_object_and_camera: true
tracking_degrees: TRACKING_DEGREE_OBJECT_ROTATION_SCALE
inlier_spring_force: 0.0
static_motion_temporal_ratio: 3e-2
}
}
visualize_tracking_data: false
streaming_track_data_cache_size: 100
}
}
}
# Consolidates tracking and detection results.
node {
calculator: "FrameAnnotationTrackerCalculator"
input_stream: "FRAME_ANNOTATION:objects"
input_stream: "TRACKED_BOXES:boxes"
output_stream: "TRACKED_FRAME_ANNOTATION:tracked_objects"
output_stream: "CANCEL_OBJECT_ID:cancel_object_id"
node_options: {
[type.googleapis.com/mediapipe.FrameAnnotationTrackerCalculatorOptions] {
img_width: 240
img_height: 320
iou_threshold: 0.1
}
}
input_stream_handler {
input_stream_handler: "SyncSetInputStreamHandler"
options {
[mediapipe.SyncSetInputStreamHandlerOptions.ext] {
sync_set {
tag_index: "FRAME_ANNOTATION"
}
sync_set {
tag_index: "TRACKED_BOXES"
}
}
}
}
}
# Lift the tracked 2D keypoints to 3D using EPnP algorithm.
node {
calculator: "Lift2DFrameAnnotationTo3DCalculator"
input_stream: "FRAME_ANNOTATION:tracked_objects"
output_stream: "LIFTED_FRAME_ANNOTATION:lifted_tracked_objects"
}
@@ -0,0 +1,57 @@
# MediaPipe Objectron vertices/landmarks rendering CPU subgraph.
type: "RendererSubgraph"
input_stream: "IMAGE:input_image"
input_stream: "LANDMARKS:landmarks"
input_stream: "NORM_RECT:rect"
output_stream: "IMAGE:output_image"
# Converts landmarks to drawing primitives for annotation overlay.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:landmarks"
output_stream: "RENDER_DATA:landmark_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_connections: [1, 2] # edge 1-2
landmark_connections: [1, 3] # edge 1-3
landmark_connections: [1, 5] # edge 1-5
landmark_connections: [2, 4] # edge 2-4
landmark_connections: [2, 6] # edge 2-6
landmark_connections: [3, 4] # edge 3-4
landmark_connections: [3, 7] # edge 3-7
landmark_connections: [4, 8] # edge 4-8
landmark_connections: [5, 6] # edge 5-6
landmark_connections: [5, 7] # edge 5-7
landmark_connections: [6, 8] # edge 6-8
landmark_connections: [7, 8] # edge 7-8
landmark_color { r: 255 g: 0 b: 0 }
connection_color { r: 0 g: 255 b: 0 }
thickness: 4.0
}
}
}
# Converts normalized rects to drawing primitives for annotation overlay.
node {
calculator: "RectToRenderDataCalculator"
input_stream: "NORM_RECT:rect"
output_stream: "RENDER_DATA:rect_render_data"
node_options: {
[type.googleapis.com/mediapipe.RectToRenderDataCalculatorOptions] {
filled: false
color { r: 255 g: 0 b: 0 }
thickness: 4.0
}
}
}
# Draws annotations and overlays them on top of the input images.
node {
calculator: "AnnotationOverlayCalculator"
input_stream: "IMAGE:input_image"
input_stream: "landmark_render_data"
input_stream: "rect_render_data"
output_stream: "IMAGE:output_image"
}
+36
View File
@@ -21,6 +21,42 @@ licenses(["notice"])
package(default_visibility = ["//visibility:public"])
cc_library(
name = "pose_tracking_gpu_deps",
deps = [
"//mediapipe/calculators/core:flow_limiter_calculator",
"//mediapipe/calculators/image:image_properties_calculator",
"//mediapipe/calculators/util:landmarks_smoothing_calculator",
"//mediapipe/graphs/pose_tracking/subgraphs:pose_renderer_gpu",
"//mediapipe/modules/pose_landmark:pose_landmark_gpu",
],
)
mediapipe_binary_graph(
name = "pose_tracking_gpu_binary_graph",
graph = "pose_tracking_gpu.pbtxt",
output_name = "pose_tracking_gpu.binarypb",
deps = [":pose_tracking_gpu_deps"],
)
cc_library(
name = "pose_tracking_cpu_deps",
deps = [
"//mediapipe/calculators/core:flow_limiter_calculator",
"//mediapipe/calculators/image:image_properties_calculator",
"//mediapipe/calculators/util:landmarks_smoothing_calculator",
"//mediapipe/graphs/pose_tracking/subgraphs:pose_renderer_cpu",
"//mediapipe/modules/pose_landmark:pose_landmark_cpu",
],
)
mediapipe_binary_graph(
name = "pose_tracking_cpu_binary_graph",
graph = "pose_tracking_cpu.pbtxt",
output_name = "pose_tracking_cpu.binarypb",
deps = [":pose_tracking_cpu_deps"],
)
cc_library(
name = "upper_body_pose_tracking_gpu_deps",
deps = [
@@ -0,0 +1,72 @@
# MediaPipe graph that performs pose tracking with TensorFlow Lite on CPU.
# CPU buffer. (ImageFrame)
input_stream: "input_video"
# Output image with rendered results. (ImageFrame)
output_stream: "output_video"
# Pose landmarks. (NormalizedLandmarkList)
output_stream: "pose_landmarks"
# Throttles the images flowing downstream for flow control. It passes through
# the very first incoming image unaltered, and waits for downstream nodes
# (calculators and subgraphs) in the graph to finish their tasks before it
# passes through another image. All images that come in while waiting are
# dropped, limiting the number of in-flight images in most part of the graph to
# 1. This prevents the downstream nodes from queuing up incoming images and data
# excessively, which leads to increased latency and memory usage, unwanted in
# real-time mobile applications. It also eliminates unnecessarily computation,
# e.g., the output produced by a node may get dropped downstream if the
# subsequent nodes are still busy processing previous inputs.
node {
calculator: "FlowLimiterCalculator"
input_stream: "input_video"
input_stream: "FINISHED:output_video"
input_stream_info: {
tag_index: "FINISHED"
back_edge: true
}
output_stream: "throttled_input_video"
}
# Subgraph that detects poses and corresponding landmarks.
node {
calculator: "PoseLandmarkCpu"
input_stream: "IMAGE:throttled_input_video"
output_stream: "LANDMARKS:pose_landmarks"
output_stream: "DETECTION:pose_detection"
output_stream: "ROI_FROM_LANDMARKS:roi_from_landmarks"
}
# Calculates size of the image.
node {
calculator: "ImagePropertiesCalculator"
input_stream: "IMAGE:throttled_input_video"
output_stream: "SIZE:image_size"
}
# Smoothes pose landmarks in order to reduce jitter.
node {
calculator: "LandmarksSmoothingCalculator"
input_stream: "NORM_LANDMARKS:pose_landmarks"
input_stream: "IMAGE_SIZE:image_size"
output_stream: "NORM_FILTERED_LANDMARKS:pose_landmarks_smoothed"
node_options: {
[type.googleapis.com/mediapipe.LandmarksSmoothingCalculatorOptions] {
velocity_filter: {
window_size: 5
velocity_scale: 10.0
}
}
}
}
# Subgraph that renders pose-landmark annotation onto the input image.
node {
calculator: "PoseRendererCpu"
input_stream: "IMAGE:throttled_input_video"
input_stream: "LANDMARKS:pose_landmarks_smoothed"
input_stream: "ROI:roi_from_landmarks"
input_stream: "DETECTION:pose_detection"
output_stream: "IMAGE:output_video"
}
@@ -0,0 +1,72 @@
# MediaPipe graph that performs pose tracking with TensorFlow Lite on GPU.
# GPU buffer. (GpuBuffer)
input_stream: "input_video"
# Output image with rendered results. (GpuBuffer)
output_stream: "output_video"
# Pose landmarks. (NormalizedLandmarkList)
output_stream: "pose_landmarks"
# Throttles the images flowing downstream for flow control. It passes through
# the very first incoming image unaltered, and waits for downstream nodes
# (calculators and subgraphs) in the graph to finish their tasks before it
# passes through another image. All images that come in while waiting are
# dropped, limiting the number of in-flight images in most part of the graph to
# 1. This prevents the downstream nodes from queuing up incoming images and data
# excessively, which leads to increased latency and memory usage, unwanted in
# real-time mobile applications. It also eliminates unnecessarily computation,
# e.g., the output produced by a node may get dropped downstream if the
# subsequent nodes are still busy processing previous inputs.
node {
calculator: "FlowLimiterCalculator"
input_stream: "input_video"
input_stream: "FINISHED:output_video"
input_stream_info: {
tag_index: "FINISHED"
back_edge: true
}
output_stream: "throttled_input_video"
}
# Subgraph that detects poses and corresponding landmarks.
node {
calculator: "PoseLandmarkGpu"
input_stream: "IMAGE:throttled_input_video"
output_stream: "LANDMARKS:pose_landmarks"
output_stream: "DETECTION:pose_detection"
output_stream: "ROI_FROM_LANDMARKS:roi_from_landmarks"
}
# Calculates size of the image.
node {
calculator: "ImagePropertiesCalculator"
input_stream: "IMAGE_GPU:throttled_input_video"
output_stream: "SIZE:image_size"
}
# Smoothes pose landmarks in order to reduce jitter.
node {
calculator: "LandmarksSmoothingCalculator"
input_stream: "NORM_LANDMARKS:pose_landmarks"
input_stream: "IMAGE_SIZE:image_size"
output_stream: "NORM_FILTERED_LANDMARKS:pose_landmarks_smoothed"
node_options: {
[type.googleapis.com/mediapipe.LandmarksSmoothingCalculatorOptions] {
velocity_filter: {
window_size: 5
velocity_scale: 10.0
}
}
}
}
# Subgraph that renders pose-landmark annotation onto the input image.
node {
calculator: "PoseRendererGpu"
input_stream: "IMAGE:throttled_input_video"
input_stream: "LANDMARKS:pose_landmarks_smoothed"
input_stream: "ROI:roi_from_landmarks"
input_stream: "DETECTION:pose_detection"
output_stream: "IMAGE:output_video"
}
@@ -21,6 +21,34 @@ licenses(["notice"])
package(default_visibility = ["//visibility:public"])
mediapipe_simple_subgraph(
name = "pose_renderer_gpu",
graph = "pose_renderer_gpu.pbtxt",
register_as = "PoseRendererGpu",
deps = [
"//mediapipe/calculators/core:split_normalized_landmark_list_calculator",
"//mediapipe/calculators/util:annotation_overlay_calculator",
"//mediapipe/calculators/util:detections_to_render_data_calculator",
"//mediapipe/calculators/util:landmarks_to_render_data_calculator",
"//mediapipe/calculators/util:rect_to_render_data_calculator",
"//mediapipe/calculators/util:rect_to_render_scale_calculator",
],
)
mediapipe_simple_subgraph(
name = "pose_renderer_cpu",
graph = "pose_renderer_cpu.pbtxt",
register_as = "PoseRendererCpu",
deps = [
"//mediapipe/calculators/core:split_normalized_landmark_list_calculator",
"//mediapipe/calculators/util:annotation_overlay_calculator",
"//mediapipe/calculators/util:detections_to_render_data_calculator",
"//mediapipe/calculators/util:landmarks_to_render_data_calculator",
"//mediapipe/calculators/util:rect_to_render_data_calculator",
"//mediapipe/calculators/util:rect_to_render_scale_calculator",
],
)
mediapipe_simple_subgraph(
name = "upper_body_pose_renderer_gpu",
graph = "upper_body_pose_renderer_gpu.pbtxt",
@@ -0,0 +1,274 @@
# MediaPipe pose landmarks rendering subgraph.
type: "PoseRendererCpu"
# CPU image. (ImageFrame)
input_stream: "IMAGE:input_image"
# Pose landmarks. (NormalizedLandmarkList)
input_stream: "LANDMARKS:pose_landmarks"
# Region of interest calculated based on landmarks. (NormalizedRect)
input_stream: "ROI:roi"
# Detected pose. (Detection)
input_stream: "DETECTION:detection"
# CPU image with rendered data. (ImageFrame)
output_stream: "IMAGE:output_image"
node {
calculator: "ImagePropertiesCalculator"
input_stream: "IMAGE:input_image"
output_stream: "SIZE:image_size"
}
# Calculates rendering scale based on the pose roi.
node {
calculator: "RectToRenderScaleCalculator"
input_stream: "NORM_RECT:roi"
input_stream: "IMAGE_SIZE:image_size"
output_stream: "RENDER_SCALE:render_scale"
node_options: {
[type.googleapis.com/mediapipe.RectToRenderScaleCalculatorOptions] {
multiplier: 0.0012
}
}
}
# Converts detections to drawing primitives for annotation overlay.
node {
calculator: "DetectionsToRenderDataCalculator"
input_stream: "DETECTION:detection"
output_stream: "RENDER_DATA:detection_render_data"
node_options: {
[type.googleapis.com/mediapipe.DetectionsToRenderDataCalculatorOptions] {
thickness: 4.0
color { r: 0 g: 255 b: 0 }
}
}
}
node {
calculator: "SplitNormalizedLandmarkListCalculator"
input_stream: "pose_landmarks"
output_stream: "visible_pose_landmarks"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 0 end: 25 }
}
}
}
# Converts landmarks to drawing primitives for annotation overlay.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:pose_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_connections: 0
landmark_connections: 1
landmark_connections: 1
landmark_connections: 2
landmark_connections: 2
landmark_connections: 3
landmark_connections: 3
landmark_connections: 7
landmark_connections: 0
landmark_connections: 4
landmark_connections: 4
landmark_connections: 5
landmark_connections: 5
landmark_connections: 6
landmark_connections: 6
landmark_connections: 8
landmark_connections: 9
landmark_connections: 10
landmark_connections: 11
landmark_connections: 12
landmark_connections: 11
landmark_connections: 13
landmark_connections: 13
landmark_connections: 15
landmark_connections: 15
landmark_connections: 17
landmark_connections: 15
landmark_connections: 19
landmark_connections: 15
landmark_connections: 21
landmark_connections: 17
landmark_connections: 19
landmark_connections: 12
landmark_connections: 14
landmark_connections: 14
landmark_connections: 16
landmark_connections: 16
landmark_connections: 18
landmark_connections: 16
landmark_connections: 20
landmark_connections: 16
landmark_connections: 22
landmark_connections: 18
landmark_connections: 20
landmark_connections: 11
landmark_connections: 23
landmark_connections: 12
landmark_connections: 24
landmark_connections: 23
landmark_connections: 24
landmark_connections: 23
landmark_connections: 25
landmark_connections: 24
landmark_connections: 26
landmark_connections: 25
landmark_connections: 27
landmark_connections: 26
landmark_connections: 28
landmark_connections: 27
landmark_connections: 29
landmark_connections: 28
landmark_connections: 30
landmark_connections: 29
landmark_connections: 31
landmark_connections: 30
landmark_connections: 32
landmark_connections: 27
landmark_connections: 31
landmark_connections: 28
landmark_connections: 32
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Take left pose landmarks.
node {
calculator: "SplitNormalizedLandmarkListCalculator"
input_stream: "pose_landmarks"
output_stream: "landmarks_left_side"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 1 end: 4 }
ranges: { begin: 7 end: 8 }
ranges: { begin: 9 end: 10 }
ranges: { begin: 11 end: 12 }
ranges: { begin: 13 end: 14 }
ranges: { begin: 15 end: 16 }
ranges: { begin: 17 end: 18 }
ranges: { begin: 19 end: 20 }
ranges: { begin: 21 end: 22 }
ranges: { begin: 23 end: 24 }
combine_outputs: true
}
}
}
# Take right pose landmarks.
node {
calculator: "SplitNormalizedLandmarkListCalculator"
input_stream: "pose_landmarks"
output_stream: "landmarks_right_side"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 4 end: 7 }
ranges: { begin: 8 end: 9 }
ranges: { begin: 10 end: 11 }
ranges: { begin: 12 end: 13 }
ranges: { begin: 14 end: 15 }
ranges: { begin: 16 end: 17 }
ranges: { begin: 18 end: 19 }
ranges: { begin: 20 end: 21 }
ranges: { begin: 22 end: 23 }
ranges: { begin: 24 end: 25 }
combine_outputs: true
}
}
}
# Render pose joints as big white circles.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:visible_pose_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_background_joints_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 5.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Render pose left side joints as orange circles (inside white ones).
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:landmarks_left_side"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_left_joints_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 255 g: 138 b: 0 }
connection_color { r: 255 g: 138 b: 0 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Render pose right side joints as cyan circles (inside white ones).
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:landmarks_right_side"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_right_joints_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 0 g: 217 b: 231 }
connection_color { r: 0 g: 217 b: 231 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Converts normalized rects to drawing primitives for annotation overlay.
node {
calculator: "RectToRenderDataCalculator"
input_stream: "NORM_RECT:roi"
output_stream: "RENDER_DATA:roi_render_data"
node_options: {
[type.googleapis.com/mediapipe.RectToRenderDataCalculatorOptions] {
filled: false
color { r: 255 g: 0 b: 0 }
thickness: 4.0
}
}
}
# Draws annotations and overlays them on top of the input images.
node {
calculator: "AnnotationOverlayCalculator"
input_stream: "IMAGE:input_image"
input_stream: "detection_render_data"
input_stream: "landmarks_render_data"
input_stream: "landmarks_background_joints_render_data"
input_stream: "landmarks_left_joints_render_data"
input_stream: "landmarks_right_joints_render_data"
input_stream: "roi_render_data"
output_stream: "IMAGE:output_image"
}
@@ -0,0 +1,274 @@
# MediaPipe pose landmarks rendering subgraph.
type: "PoseRendererGpu"
# GPU image. (GpuBuffer)
input_stream: "IMAGE:input_image"
# Pose landmarks. (NormalizedLandmarkList)
input_stream: "LANDMARKS:pose_landmarks"
# Region of interest calculated based on landmarks. (NormalizedRect)
input_stream: "ROI:roi"
# Detected pose. (Detection)
input_stream: "DETECTION:detection"
# GPU image with rendered data. (GpuBuffer)
output_stream: "IMAGE:output_image"
node {
calculator: "ImagePropertiesCalculator"
input_stream: "IMAGE_GPU:input_image"
output_stream: "SIZE:image_size"
}
# Calculates rendering scale based on the pose roi.
node {
calculator: "RectToRenderScaleCalculator"
input_stream: "NORM_RECT:roi"
input_stream: "IMAGE_SIZE:image_size"
output_stream: "RENDER_SCALE:render_scale"
node_options: {
[type.googleapis.com/mediapipe.RectToRenderScaleCalculatorOptions] {
multiplier: 0.0012
}
}
}
# Converts detections to drawing primitives for annotation overlay.
node {
calculator: "DetectionsToRenderDataCalculator"
input_stream: "DETECTION:detection"
output_stream: "RENDER_DATA:detection_render_data"
node_options: {
[type.googleapis.com/mediapipe.DetectionsToRenderDataCalculatorOptions] {
thickness: 4.0
color { r: 0 g: 255 b: 0 }
}
}
}
node {
calculator: "SplitNormalizedLandmarkListCalculator"
input_stream: "pose_landmarks"
output_stream: "visible_pose_landmarks"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 0 end: 25 }
}
}
}
# Converts landmarks to drawing primitives for annotation overlay.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:pose_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_connections: 0
landmark_connections: 1
landmark_connections: 1
landmark_connections: 2
landmark_connections: 2
landmark_connections: 3
landmark_connections: 3
landmark_connections: 7
landmark_connections: 0
landmark_connections: 4
landmark_connections: 4
landmark_connections: 5
landmark_connections: 5
landmark_connections: 6
landmark_connections: 6
landmark_connections: 8
landmark_connections: 9
landmark_connections: 10
landmark_connections: 11
landmark_connections: 12
landmark_connections: 11
landmark_connections: 13
landmark_connections: 13
landmark_connections: 15
landmark_connections: 15
landmark_connections: 17
landmark_connections: 15
landmark_connections: 19
landmark_connections: 15
landmark_connections: 21
landmark_connections: 17
landmark_connections: 19
landmark_connections: 12
landmark_connections: 14
landmark_connections: 14
landmark_connections: 16
landmark_connections: 16
landmark_connections: 18
landmark_connections: 16
landmark_connections: 20
landmark_connections: 16
landmark_connections: 22
landmark_connections: 18
landmark_connections: 20
landmark_connections: 11
landmark_connections: 23
landmark_connections: 12
landmark_connections: 24
landmark_connections: 23
landmark_connections: 24
landmark_connections: 23
landmark_connections: 25
landmark_connections: 24
landmark_connections: 26
landmark_connections: 25
landmark_connections: 27
landmark_connections: 26
landmark_connections: 28
landmark_connections: 27
landmark_connections: 29
landmark_connections: 28
landmark_connections: 30
landmark_connections: 29
landmark_connections: 31
landmark_connections: 30
landmark_connections: 32
landmark_connections: 27
landmark_connections: 31
landmark_connections: 28
landmark_connections: 32
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Take left pose landmarks.
node {
calculator: "SplitNormalizedLandmarkListCalculator"
input_stream: "pose_landmarks"
output_stream: "landmarks_left_side"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 1 end: 4 }
ranges: { begin: 7 end: 8 }
ranges: { begin: 9 end: 10 }
ranges: { begin: 11 end: 12 }
ranges: { begin: 13 end: 14 }
ranges: { begin: 15 end: 16 }
ranges: { begin: 17 end: 18 }
ranges: { begin: 19 end: 20 }
ranges: { begin: 21 end: 22 }
ranges: { begin: 23 end: 24 }
combine_outputs: true
}
}
}
# Take right pose landmarks.
node {
calculator: "SplitNormalizedLandmarkListCalculator"
input_stream: "pose_landmarks"
output_stream: "landmarks_right_side"
node_options: {
[type.googleapis.com/mediapipe.SplitVectorCalculatorOptions] {
ranges: { begin: 4 end: 7 }
ranges: { begin: 8 end: 9 }
ranges: { begin: 10 end: 11 }
ranges: { begin: 12 end: 13 }
ranges: { begin: 14 end: 15 }
ranges: { begin: 16 end: 17 }
ranges: { begin: 18 end: 19 }
ranges: { begin: 20 end: 21 }
ranges: { begin: 22 end: 23 }
ranges: { begin: 24 end: 25 }
combine_outputs: true
}
}
}
# Render pose joints as big white circles.
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:visible_pose_landmarks"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_background_joints_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 255 g: 255 b: 255 }
connection_color { r: 255 g: 255 b: 255 }
thickness: 5.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Render pose left side joints as orange circles (inside white ones).
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:landmarks_left_side"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_left_joints_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 255 g: 138 b: 0 }
connection_color { r: 255 g: 138 b: 0 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Render pose right side joints as cyan circles (inside white ones).
node {
calculator: "LandmarksToRenderDataCalculator"
input_stream: "NORM_LANDMARKS:landmarks_right_side"
input_stream: "RENDER_SCALE:render_scale"
output_stream: "RENDER_DATA:landmarks_right_joints_render_data"
node_options: {
[type.googleapis.com/mediapipe.LandmarksToRenderDataCalculatorOptions] {
landmark_color { r: 0 g: 217 b: 231 }
connection_color { r: 0 g: 217 b: 231 }
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.5
}
}
}
# Converts normalized rects to drawing primitives for annotation overlay.
node {
calculator: "RectToRenderDataCalculator"
input_stream: "NORM_RECT:roi"
output_stream: "RENDER_DATA:roi_render_data"
node_options: {
[type.googleapis.com/mediapipe.RectToRenderDataCalculatorOptions] {
filled: false
color { r: 255 g: 0 b: 0 }
thickness: 4.0
}
}
}
# Draws annotations and overlays them on top of the input images.
node {
calculator: "AnnotationOverlayCalculator"
input_stream: "IMAGE_GPU:input_image"
input_stream: "detection_render_data"
input_stream: "landmarks_render_data"
input_stream: "landmarks_background_joints_render_data"
input_stream: "landmarks_left_joints_render_data"
input_stream: "landmarks_right_joints_render_data"
input_stream: "roi_render_data"
output_stream: "IMAGE_GPU:output_image"
}
@@ -121,7 +121,7 @@ node {
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.1
visibility_threshold: 0.5
}
}
}
@@ -185,7 +185,7 @@ node {
thickness: 5.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.1
visibility_threshold: 0.5
}
}
}
@@ -203,7 +203,7 @@ node {
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.1
visibility_threshold: 0.5
}
}
}
@@ -221,7 +221,7 @@ node {
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.1
visibility_threshold: 0.5
}
}
}
@@ -121,7 +121,7 @@ node {
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.1
visibility_threshold: 0.5
}
}
}
@@ -185,7 +185,7 @@ node {
thickness: 5.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.1
visibility_threshold: 0.5
}
}
}
@@ -203,7 +203,7 @@ node {
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.1
visibility_threshold: 0.5
}
}
}
@@ -221,7 +221,7 @@ node {
thickness: 3.0
visualize_landmark_depth: false
utilize_visibility: true
visibility_threshold: 0.1
visibility_threshold: 0.5
}
}
}