add TensorsToFaceLandmarksGraph to support two types of face mesh models.
PiperOrigin-RevId: 511626032
This commit is contained in:
		
							parent
							
								
									9e7950a69a
								
							
						
					
					
						commit
						2c3c1e664a
					
				| 
						 | 
					@ -18,6 +18,27 @@ package(default_visibility = [
 | 
				
			||||||
 | 
					
 | 
				
			||||||
licenses(["notice"])
 | 
					licenses(["notice"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cc_library(
 | 
				
			||||||
 | 
					    name = "tensors_to_face_landmarks_graph",
 | 
				
			||||||
 | 
					    srcs = ["tensors_to_face_landmarks_graph.cc"],
 | 
				
			||||||
 | 
					    deps = [
 | 
				
			||||||
 | 
					        "//mediapipe/calculators/core:split_vector_calculator",
 | 
				
			||||||
 | 
					        "//mediapipe/calculators/core:split_vector_calculator_cc_proto",
 | 
				
			||||||
 | 
					        "//mediapipe/calculators/tensor:tensors_to_landmarks_calculator",
 | 
				
			||||||
 | 
					        "//mediapipe/calculators/tensor:tensors_to_landmarks_calculator_cc_proto",
 | 
				
			||||||
 | 
					        "//mediapipe/calculators/util:landmarks_refinement_calculator",
 | 
				
			||||||
 | 
					        "//mediapipe/calculators/util:landmarks_refinement_calculator_cc_proto",
 | 
				
			||||||
 | 
					        "//mediapipe/framework:calculator_cc_proto",
 | 
				
			||||||
 | 
					        "//mediapipe/framework:calculator_framework",
 | 
				
			||||||
 | 
					        "//mediapipe/framework:subgraph",
 | 
				
			||||||
 | 
					        "//mediapipe/framework/api2:builder",
 | 
				
			||||||
 | 
					        "//mediapipe/framework/formats:landmark_cc_proto",
 | 
				
			||||||
 | 
					        "//mediapipe/framework/formats:tensor",
 | 
				
			||||||
 | 
					        "//mediapipe/tasks/cc/vision/face_landmarker/proto:tensors_to_face_landmarks_graph_options_cc_proto",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    alwayslink = 1,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cc_library(
 | 
					cc_library(
 | 
				
			||||||
    name = "face_blendshapes_graph",
 | 
					    name = "face_blendshapes_graph",
 | 
				
			||||||
    srcs = ["face_blendshapes_graph.cc"],
 | 
					    srcs = ["face_blendshapes_graph.cc"],
 | 
				
			||||||
| 
						 | 
					@ -48,6 +69,7 @@ cc_library(
 | 
				
			||||||
    name = "face_landmarks_detector_graph",
 | 
					    name = "face_landmarks_detector_graph",
 | 
				
			||||||
    srcs = ["face_landmarks_detector_graph.cc"],
 | 
					    srcs = ["face_landmarks_detector_graph.cc"],
 | 
				
			||||||
    deps = [
 | 
					    deps = [
 | 
				
			||||||
 | 
					        ":tensors_to_face_landmarks_graph",
 | 
				
			||||||
        "//mediapipe/calculators/core:begin_loop_calculator",
 | 
					        "//mediapipe/calculators/core:begin_loop_calculator",
 | 
				
			||||||
        "//mediapipe/calculators/core:end_loop_calculator",
 | 
					        "//mediapipe/calculators/core:end_loop_calculator",
 | 
				
			||||||
        "//mediapipe/calculators/core:split_vector_calculator",
 | 
					        "//mediapipe/calculators/core:split_vector_calculator",
 | 
				
			||||||
| 
						 | 
					@ -80,6 +102,7 @@ cc_library(
 | 
				
			||||||
        "//mediapipe/tasks/cc/core:model_task_graph",
 | 
					        "//mediapipe/tasks/cc/core:model_task_graph",
 | 
				
			||||||
        "//mediapipe/tasks/cc/core:utils",
 | 
					        "//mediapipe/tasks/cc/core:utils",
 | 
				
			||||||
        "//mediapipe/tasks/cc/vision/face_landmarker/proto:face_landmarks_detector_graph_options_cc_proto",
 | 
					        "//mediapipe/tasks/cc/vision/face_landmarker/proto:face_landmarks_detector_graph_options_cc_proto",
 | 
				
			||||||
 | 
					        "//mediapipe/tasks/cc/vision/face_landmarker/proto:tensors_to_face_landmarks_graph_options_cc_proto",
 | 
				
			||||||
        "//mediapipe/tasks/cc/vision/utils:image_tensor_specs",
 | 
					        "//mediapipe/tasks/cc/vision/utils:image_tensor_specs",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    alwayslink = 1,
 | 
					    alwayslink = 1,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,7 @@ limitations under the License.
 | 
				
			||||||
#include "mediapipe/tasks/cc/core/model_task_graph.h"
 | 
					#include "mediapipe/tasks/cc/core/model_task_graph.h"
 | 
				
			||||||
#include "mediapipe/tasks/cc/core/utils.h"
 | 
					#include "mediapipe/tasks/cc/core/utils.h"
 | 
				
			||||||
#include "mediapipe/tasks/cc/vision/face_landmarker/proto/face_landmarks_detector_graph_options.pb.h"
 | 
					#include "mediapipe/tasks/cc/vision/face_landmarker/proto/face_landmarks_detector_graph_options.pb.h"
 | 
				
			||||||
 | 
					#include "mediapipe/tasks/cc/vision/face_landmarker/proto/tensors_to_face_landmarks_graph_options.pb.h"
 | 
				
			||||||
#include "mediapipe/tasks/cc/vision/utils/image_tensor_specs.h"
 | 
					#include "mediapipe/tasks/cc/vision/utils/image_tensor_specs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace mediapipe {
 | 
					namespace mediapipe {
 | 
				
			||||||
| 
						 | 
					@ -72,8 +73,10 @@ constexpr char kBatchEndTag[] = "BATCH_END";
 | 
				
			||||||
constexpr char kItemTag[] = "ITEM";
 | 
					constexpr char kItemTag[] = "ITEM";
 | 
				
			||||||
constexpr char kDetectionTag[] = "DETECTION";
 | 
					constexpr char kDetectionTag[] = "DETECTION";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr int kLandmarksNum = 468;
 | 
					// a landmarks tensor and a scores tensor
 | 
				
			||||||
constexpr int kModelOutputTensorSplitNum = 2;
 | 
					constexpr int kFaceLandmarksOutputTensorsNum = 2;
 | 
				
			||||||
 | 
					// 6 landmarks tensors and a scores tensor.
 | 
				
			||||||
 | 
					constexpr int kAttentionMeshOutputTensorsNum = 7;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct SingleFaceLandmarksOutputs {
 | 
					struct SingleFaceLandmarksOutputs {
 | 
				
			||||||
  Stream<NormalizedLandmarkList> landmarks;
 | 
					  Stream<NormalizedLandmarkList> landmarks;
 | 
				
			||||||
| 
						 | 
					@ -104,18 +107,28 @@ absl::Status SanityCheckOptions(
 | 
				
			||||||
// Split face landmark detection model output tensor into two parts,
 | 
					// Split face landmark detection model output tensor into two parts,
 | 
				
			||||||
// representing landmarks and face presence scores.
 | 
					// representing landmarks and face presence scores.
 | 
				
			||||||
void ConfigureSplitTensorVectorCalculator(
 | 
					void ConfigureSplitTensorVectorCalculator(
 | 
				
			||||||
    mediapipe::SplitVectorCalculatorOptions* options) {
 | 
					    bool is_attention_model, mediapipe::SplitVectorCalculatorOptions* options) {
 | 
				
			||||||
  for (int i = 0; i < kModelOutputTensorSplitNum; ++i) {
 | 
					  if (is_attention_model) {
 | 
				
			||||||
    auto* range = options->add_ranges();
 | 
					    auto* range = options->add_ranges();
 | 
				
			||||||
    range->set_begin(i);
 | 
					    range->set_begin(0);
 | 
				
			||||||
    range->set_end(i + 1);
 | 
					    range->set_end(kAttentionMeshOutputTensorsNum - 1);
 | 
				
			||||||
 | 
					    range = options->add_ranges();
 | 
				
			||||||
 | 
					    range->set_begin(kAttentionMeshOutputTensorsNum - 1);
 | 
				
			||||||
 | 
					    range->set_end(kAttentionMeshOutputTensorsNum);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    auto* range = options->add_ranges();
 | 
				
			||||||
 | 
					    range->set_begin(0);
 | 
				
			||||||
 | 
					    range->set_end(kFaceLandmarksOutputTensorsNum - 1);
 | 
				
			||||||
 | 
					    range = options->add_ranges();
 | 
				
			||||||
 | 
					    range->set_begin(kFaceLandmarksOutputTensorsNum - 1);
 | 
				
			||||||
 | 
					    range->set_end(kFaceLandmarksOutputTensorsNum);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ConfigureTensorsToLandmarksCalculator(
 | 
					void ConfigureTensorsToFaceLandmarksGraph(
 | 
				
			||||||
    const ImageTensorSpecs& input_image_tensor_spec,
 | 
					    const ImageTensorSpecs& input_image_tensor_spec, bool is_attention_model,
 | 
				
			||||||
    mediapipe::TensorsToLandmarksCalculatorOptions* options) {
 | 
					    proto::TensorsToFaceLandmarksGraphOptions* options) {
 | 
				
			||||||
  options->set_num_landmarks(kLandmarksNum);
 | 
					  options->set_is_attention_model(is_attention_model);
 | 
				
			||||||
  options->set_input_image_height(input_image_tensor_spec.image_height);
 | 
					  options->set_input_image_height(input_image_tensor_spec.image_height);
 | 
				
			||||||
  options->set_input_image_width(input_image_tensor_spec.image_width);
 | 
					  options->set_input_image_width(input_image_tensor_spec.image_width);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -138,6 +151,12 @@ void ConfigureFaceRectTransformationCalculator(
 | 
				
			||||||
  options->set_square_long(true);
 | 
					  options->set_square_long(true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool IsAttentionModel(const core::ModelResources& model_resources) {
 | 
				
			||||||
 | 
					  const auto* model = model_resources.GetTfLiteModel();
 | 
				
			||||||
 | 
					  const auto* primary_subgraph = (*model->subgraphs())[0];
 | 
				
			||||||
 | 
					  return primary_subgraph->outputs()->size() == kAttentionMeshOutputTensorsNum;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace
 | 
					}  // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A "mediapipe.tasks.vision.face_landmarker.SingleFaceLandmarksDetectorGraph"
 | 
					// A "mediapipe.tasks.vision.face_landmarker.SingleFaceLandmarksDetectorGraph"
 | 
				
			||||||
| 
						 | 
					@ -246,8 +265,10 @@ class SingleFaceLandmarksDetectorGraph : public core::ModelTaskGraph {
 | 
				
			||||||
    auto output_tensors = inference.Out(kTensorsTag);
 | 
					    auto output_tensors = inference.Out(kTensorsTag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Split model output tensors to multiple streams.
 | 
					    // Split model output tensors to multiple streams.
 | 
				
			||||||
 | 
					    bool is_attention_model = IsAttentionModel(model_resources);
 | 
				
			||||||
    auto& split_tensors_vector = graph.AddNode("SplitTensorVectorCalculator");
 | 
					    auto& split_tensors_vector = graph.AddNode("SplitTensorVectorCalculator");
 | 
				
			||||||
    ConfigureSplitTensorVectorCalculator(
 | 
					    ConfigureSplitTensorVectorCalculator(
 | 
				
			||||||
 | 
					        is_attention_model,
 | 
				
			||||||
        &split_tensors_vector
 | 
					        &split_tensors_vector
 | 
				
			||||||
             .GetOptions<mediapipe::SplitVectorCalculatorOptions>());
 | 
					             .GetOptions<mediapipe::SplitVectorCalculatorOptions>());
 | 
				
			||||||
    output_tensors >> split_tensors_vector.In("");
 | 
					    output_tensors >> split_tensors_vector.In("");
 | 
				
			||||||
| 
						 | 
					@ -256,15 +277,16 @@ class SingleFaceLandmarksDetectorGraph : public core::ModelTaskGraph {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Decodes the landmark tensors into a list of landmarks, where the landmark
 | 
					    // 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.
 | 
					    // coordinates are normalized by the size of the input image to the model.
 | 
				
			||||||
    auto& tensors_to_landmarks = graph.AddNode("TensorsToLandmarksCalculator");
 | 
					 | 
				
			||||||
    ASSIGN_OR_RETURN(auto image_tensor_specs,
 | 
					    ASSIGN_OR_RETURN(auto image_tensor_specs,
 | 
				
			||||||
                     vision::BuildInputImageTensorSpecs(model_resources));
 | 
					                     vision::BuildInputImageTensorSpecs(model_resources));
 | 
				
			||||||
    ConfigureTensorsToLandmarksCalculator(
 | 
					    auto& tensors_to_face_landmarks = graph.AddNode(
 | 
				
			||||||
        image_tensor_specs,
 | 
					        "mediapipe.tasks.vision.face_landmarker.TensorsToFaceLandmarksGraph");
 | 
				
			||||||
        &tensors_to_landmarks
 | 
					    ConfigureTensorsToFaceLandmarksGraph(
 | 
				
			||||||
             .GetOptions<mediapipe::TensorsToLandmarksCalculatorOptions>());
 | 
					        image_tensor_specs, is_attention_model,
 | 
				
			||||||
    landmark_tensors >> tensors_to_landmarks.In(kTensorsTag);
 | 
					        &tensors_to_face_landmarks
 | 
				
			||||||
    auto landmarks = tensors_to_landmarks.Out(kNormLandmarksTag);
 | 
					             .GetOptions<proto::TensorsToFaceLandmarksGraphOptions>());
 | 
				
			||||||
 | 
					    landmark_tensors >> tensors_to_face_landmarks.In(kTensorsTag);
 | 
				
			||||||
 | 
					    auto landmarks = tensors_to_face_landmarks.Out(kNormLandmarksTag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Converts the presence flag tensor into a float that represents the
 | 
					    // Converts the presence flag tensor into a float that represents the
 | 
				
			||||||
    // confidence score of face presence.
 | 
					    // confidence score of face presence.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,7 @@ limitations under the License.
 | 
				
			||||||
#include "mediapipe/framework/port/file_helpers.h"
 | 
					#include "mediapipe/framework/port/file_helpers.h"
 | 
				
			||||||
#include "mediapipe/framework/port/gmock.h"
 | 
					#include "mediapipe/framework/port/gmock.h"
 | 
				
			||||||
#include "mediapipe/framework/port/gtest.h"
 | 
					#include "mediapipe/framework/port/gtest.h"
 | 
				
			||||||
 | 
					#include "mediapipe/tasks/cc/core/mediapipe_builtin_op_resolver.h"
 | 
				
			||||||
#include "mediapipe/tasks/cc/core/proto/base_options.pb.h"
 | 
					#include "mediapipe/tasks/cc/core/proto/base_options.pb.h"
 | 
				
			||||||
#include "mediapipe/tasks/cc/core/proto/external_file.pb.h"
 | 
					#include "mediapipe/tasks/cc/core/proto/external_file.pb.h"
 | 
				
			||||||
#include "mediapipe/tasks/cc/core/task_runner.h"
 | 
					#include "mediapipe/tasks/cc/core/task_runner.h"
 | 
				
			||||||
| 
						 | 
					@ -61,10 +62,14 @@ using ::testing::proto::Partially;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr char kTestDataDirectory[] = "/mediapipe/tasks/testdata/vision/";
 | 
					constexpr char kTestDataDirectory[] = "/mediapipe/tasks/testdata/vision/";
 | 
				
			||||||
constexpr char kFaceLandmarksDetectionModel[] = "face_landmark.tflite";
 | 
					constexpr char kFaceLandmarksDetectionModel[] = "face_landmark.tflite";
 | 
				
			||||||
 | 
					constexpr char kFaceLandmarksDetectionWithAttentionModel[] =
 | 
				
			||||||
 | 
					    "face_landmark_with_attention.tflite";
 | 
				
			||||||
constexpr char kPortraitImageName[] = "portrait.jpg";
 | 
					constexpr char kPortraitImageName[] = "portrait.jpg";
 | 
				
			||||||
constexpr char kCatImageName[] = "cat.jpg";
 | 
					constexpr char kCatImageName[] = "cat.jpg";
 | 
				
			||||||
constexpr char kPortraitExpectedFaceLandamrksName[] =
 | 
					constexpr char kPortraitExpectedFaceLandamrksName[] =
 | 
				
			||||||
    "portrait_expected_face_landmarks.pbtxt";
 | 
					    "portrait_expected_face_landmarks.pbtxt";
 | 
				
			||||||
 | 
					constexpr char kPortraitExpectedFaceLandamrksWithAttentionName[] =
 | 
				
			||||||
 | 
					    "portrait_expected_face_landmarks_with_attention.pbtxt";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr char kImageTag[] = "IMAGE";
 | 
					constexpr char kImageTag[] = "IMAGE";
 | 
				
			||||||
constexpr char kImageName[] = "image";
 | 
					constexpr char kImageName[] = "image";
 | 
				
			||||||
| 
						 | 
					@ -117,8 +122,7 @@ absl::StatusOr<std::unique_ptr<TaskRunner>> CreateSingleFaceLandmarksTaskRunner(
 | 
				
			||||||
      graph[Output<NormalizedRect>(kFaceRectNextFrameTag)];
 | 
					      graph[Output<NormalizedRect>(kFaceRectNextFrameTag)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return TaskRunner::Create(
 | 
					  return TaskRunner::Create(
 | 
				
			||||||
      graph.GetConfig(),
 | 
					      graph.GetConfig(), absl::make_unique<core::MediaPipeBuiltinOpResolver>());
 | 
				
			||||||
      absl::make_unique<tflite_shims::ops::builtin::BuiltinOpResolver>());
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Helper function to create a Multi Face Landmark TaskRunner.
 | 
					// Helper function to create a Multi Face Landmark TaskRunner.
 | 
				
			||||||
| 
						 | 
					@ -154,8 +158,7 @@ absl::StatusOr<std::unique_ptr<TaskRunner>> CreateMultiFaceLandmarksTaskRunner(
 | 
				
			||||||
      graph[Output<std::vector<NormalizedRect>>(kFaceRectsNextFrameTag)];
 | 
					      graph[Output<std::vector<NormalizedRect>>(kFaceRectsNextFrameTag)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return TaskRunner::Create(
 | 
					  return TaskRunner::Create(
 | 
				
			||||||
      graph.GetConfig(),
 | 
					      graph.GetConfig(), absl::make_unique<core::MediaPipeBuiltinOpResolver>());
 | 
				
			||||||
      absl::make_unique<tflite_shims::ops::builtin::BuiltinOpResolver>());
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
NormalizedLandmarkList GetExpectedLandmarkList(absl::string_view filename) {
 | 
					NormalizedLandmarkList GetExpectedLandmarkList(absl::string_view filename) {
 | 
				
			||||||
| 
						 | 
					@ -287,6 +290,16 @@ INSTANTIATE_TEST_SUITE_P(
 | 
				
			||||||
               /*expected_presence = */ true,
 | 
					               /*expected_presence = */ true,
 | 
				
			||||||
               /*expected_landmarks = */
 | 
					               /*expected_landmarks = */
 | 
				
			||||||
               GetExpectedLandmarkList(kPortraitExpectedFaceLandamrksName),
 | 
					               GetExpectedLandmarkList(kPortraitExpectedFaceLandamrksName),
 | 
				
			||||||
 | 
					               /*landmarks_diff_threshold = */ kFractionDiff},
 | 
				
			||||||
 | 
					           SingeFaceTestParams{
 | 
				
			||||||
 | 
					               /* test_name= */ "PortraitWithAttention",
 | 
				
			||||||
 | 
					               /*input_model_name= */ kFaceLandmarksDetectionWithAttentionModel,
 | 
				
			||||||
 | 
					               /*test_image_name=*/kPortraitImageName,
 | 
				
			||||||
 | 
					               /*norm_rect= */ MakeNormRect(0.4987, 0.2211, 0.2877, 0.2303, 0),
 | 
				
			||||||
 | 
					               /*expected_presence = */ true,
 | 
				
			||||||
 | 
					               /*expected_landmarks = */
 | 
				
			||||||
 | 
					               GetExpectedLandmarkList(
 | 
				
			||||||
 | 
					                   kPortraitExpectedFaceLandamrksWithAttentionName),
 | 
				
			||||||
               /*landmarks_diff_threshold = */ kFractionDiff}),
 | 
					               /*landmarks_diff_threshold = */ kFractionDiff}),
 | 
				
			||||||
    [](const TestParamInfo<SingleFaceLandmarksDetectionTest::ParamType>& info) {
 | 
					    [](const TestParamInfo<SingleFaceLandmarksDetectionTest::ParamType>& info) {
 | 
				
			||||||
      return info.param.test_name;
 | 
					      return info.param.test_name;
 | 
				
			||||||
| 
						 | 
					@ -304,6 +317,16 @@ INSTANTIATE_TEST_SUITE_P(
 | 
				
			||||||
            /*expected_landmarks_list = */
 | 
					            /*expected_landmarks_list = */
 | 
				
			||||||
            {{GetExpectedLandmarkList(kPortraitExpectedFaceLandamrksName)}},
 | 
					            {{GetExpectedLandmarkList(kPortraitExpectedFaceLandamrksName)}},
 | 
				
			||||||
            /*landmarks_diff_threshold = */ kFractionDiff},
 | 
					            /*landmarks_diff_threshold = */ kFractionDiff},
 | 
				
			||||||
 | 
					        MultiFaceTestParams{
 | 
				
			||||||
 | 
					            /* test_name= */ "PortraitWithAttention",
 | 
				
			||||||
 | 
					            /*input_model_name= */ kFaceLandmarksDetectionWithAttentionModel,
 | 
				
			||||||
 | 
					            /*test_image_name=*/kPortraitImageName,
 | 
				
			||||||
 | 
					            /*norm_rects= */ {MakeNormRect(0.4987, 0.2211, 0.2877, 0.2303, 0)},
 | 
				
			||||||
 | 
					            /*expected_presence = */ {true},
 | 
				
			||||||
 | 
					            /*expected_landmarks_list = */
 | 
				
			||||||
 | 
					            {{GetExpectedLandmarkList(
 | 
				
			||||||
 | 
					                kPortraitExpectedFaceLandamrksWithAttentionName)}},
 | 
				
			||||||
 | 
					            /*landmarks_diff_threshold = */ kFractionDiff},
 | 
				
			||||||
        MultiFaceTestParams{
 | 
					        MultiFaceTestParams{
 | 
				
			||||||
            /* test_name= */ "NoFace",
 | 
					            /* test_name= */ "NoFace",
 | 
				
			||||||
            /*input_model_name= */ kFaceLandmarksDetectionModel,
 | 
					            /*input_model_name= */ kFaceLandmarksDetectionModel,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,3 +39,13 @@ mediapipe_proto_library(
 | 
				
			||||||
        "//mediapipe/tasks/cc/core/proto:base_options_proto",
 | 
					        "//mediapipe/tasks/cc/core/proto:base_options_proto",
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mediapipe_proto_library(
 | 
				
			||||||
 | 
					    name = "tensors_to_face_landmarks_graph_options_proto",
 | 
				
			||||||
 | 
					    srcs = ["tensors_to_face_landmarks_graph_options.proto"],
 | 
				
			||||||
 | 
					    deps = [
 | 
				
			||||||
 | 
					        "//mediapipe/calculators/tensor:tensors_to_landmarks_calculator_proto",
 | 
				
			||||||
 | 
					        "//mediapipe/framework:calculator_options_proto",
 | 
				
			||||||
 | 
					        "//mediapipe/framework:calculator_proto",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					/* Copyright 2023 The MediaPipe Authors. All Rights Reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.tasks.vision.face_landmarker.proto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "mediapipe/framework/calculator.proto";
 | 
				
			||||||
 | 
					import "mediapipe/framework/calculator_options.proto";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					message TensorsToFaceLandmarksGraphOptions {
 | 
				
			||||||
 | 
					  extend mediapipe.CalculatorOptions {
 | 
				
			||||||
 | 
					    optional TensorsToFaceLandmarksGraphOptions ext = 509621260;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Whether the landmarks model is with attention on lips and eyes. Attention
 | 
				
			||||||
 | 
					  // provides more accuracy on lips and eye regions as well as iris landmarks.
 | 
				
			||||||
 | 
					  optional bool is_attention_model = 1 [default = false];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  optional int32 input_image_width = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  optional int32 input_image_height = 3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,373 @@
 | 
				
			||||||
 | 
					/* Copyright 2023 The MediaPipe Authors. All Rights Reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 <array>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <utility>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "mediapipe/calculators/core/split_vector_calculator.pb.h"
 | 
				
			||||||
 | 
					#include "mediapipe/calculators/tensor/tensors_to_landmarks_calculator.pb.h"
 | 
				
			||||||
 | 
					#include "mediapipe/calculators/util/landmarks_refinement_calculator.pb.h"
 | 
				
			||||||
 | 
					#include "mediapipe/framework/api2/builder.h"
 | 
				
			||||||
 | 
					#include "mediapipe/framework/calculator.pb.h"
 | 
				
			||||||
 | 
					#include "mediapipe/framework/calculator_framework.h"
 | 
				
			||||||
 | 
					#include "mediapipe/framework/formats/landmark.pb.h"
 | 
				
			||||||
 | 
					#include "mediapipe/framework/formats/tensor.h"
 | 
				
			||||||
 | 
					#include "mediapipe/framework/subgraph.h"
 | 
				
			||||||
 | 
					#include "mediapipe/tasks/cc/vision/face_landmarker/proto/tensors_to_face_landmarks_graph_options.pb.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace mediapipe {
 | 
				
			||||||
 | 
					namespace tasks {
 | 
				
			||||||
 | 
					namespace vision {
 | 
				
			||||||
 | 
					namespace face_landmarker {
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using ::mediapipe::api2::builder::Graph;
 | 
				
			||||||
 | 
					using ::mediapipe::api2::builder::SidePacket;
 | 
				
			||||||
 | 
					using ::mediapipe::api2::builder::Stream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr char kTensorsTag[] = "TENSORS";
 | 
				
			||||||
 | 
					constexpr char kNormLandmarksTag[] = "NORM_LANDMARKS";
 | 
				
			||||||
 | 
					constexpr char kLandmarksTag[] = "LANDMARKS";
 | 
				
			||||||
 | 
					constexpr char kRefinedLandmarksTag[] = "REFINED_LANDMARKS";
 | 
				
			||||||
 | 
					constexpr int kAttentionModelSplitNum = 6;
 | 
				
			||||||
 | 
					constexpr int kMeshLandmarksNum = 468;
 | 
				
			||||||
 | 
					constexpr int kLipsLandmarksNum = 80;
 | 
				
			||||||
 | 
					constexpr int kEyeLandmarksNum = 71;
 | 
				
			||||||
 | 
					constexpr int kIrisLandmarksNum = 5;
 | 
				
			||||||
 | 
					constexpr int kContoursNumForIrisAvg = 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO When model metadata for face detector is ready, move the
 | 
				
			||||||
 | 
					// index mapping to metadata.
 | 
				
			||||||
 | 
					constexpr std::array<int, kMeshLandmarksNum> kMeshLandmarksIndicesMapping{
 | 
				
			||||||
 | 
					    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
 | 
				
			||||||
 | 
					    15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
 | 
				
			||||||
 | 
					    30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
 | 
				
			||||||
 | 
					    45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
 | 
				
			||||||
 | 
					    60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,
 | 
				
			||||||
 | 
					    75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
 | 
				
			||||||
 | 
					    90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
 | 
				
			||||||
 | 
					    105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
 | 
				
			||||||
 | 
					    120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
 | 
				
			||||||
 | 
					    135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
 | 
				
			||||||
 | 
					    150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
 | 
				
			||||||
 | 
					    165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
 | 
				
			||||||
 | 
					    180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
 | 
				
			||||||
 | 
					    195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
 | 
				
			||||||
 | 
					    210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
 | 
				
			||||||
 | 
					    225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
 | 
				
			||||||
 | 
					    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
 | 
				
			||||||
 | 
					    255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
 | 
				
			||||||
 | 
					    270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
 | 
				
			||||||
 | 
					    285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
 | 
				
			||||||
 | 
					    300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
 | 
				
			||||||
 | 
					    315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329,
 | 
				
			||||||
 | 
					    330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
 | 
				
			||||||
 | 
					    345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359,
 | 
				
			||||||
 | 
					    360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374,
 | 
				
			||||||
 | 
					    375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389,
 | 
				
			||||||
 | 
					    390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404,
 | 
				
			||||||
 | 
					    405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
 | 
				
			||||||
 | 
					    420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434,
 | 
				
			||||||
 | 
					    435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449,
 | 
				
			||||||
 | 
					    450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464,
 | 
				
			||||||
 | 
					    465, 466, 467};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr std::array<int, kLipsLandmarksNum> kLipsLandmarksIndicesMapping{
 | 
				
			||||||
 | 
					    // Lower outer.
 | 
				
			||||||
 | 
					    61, 146, 91, 181, 84, 17, 314, 405, 321, 375, 291,
 | 
				
			||||||
 | 
					    // Upper outer (excluding corners).
 | 
				
			||||||
 | 
					    185, 40, 39, 37, 0, 267, 269, 270, 409,
 | 
				
			||||||
 | 
					    // Lower inner.
 | 
				
			||||||
 | 
					    78, 95, 88, 178, 87, 14, 317, 402, 318, 324, 308,
 | 
				
			||||||
 | 
					    // Upper inner (excluding corners).
 | 
				
			||||||
 | 
					    191, 80, 81, 82, 13, 312, 311, 310, 415,
 | 
				
			||||||
 | 
					    // Lower semi-outer.
 | 
				
			||||||
 | 
					    76, 77, 90, 180, 85, 16, 315, 404, 320, 307, 306,
 | 
				
			||||||
 | 
					    // Upper semi-outer (excluding corners).
 | 
				
			||||||
 | 
					    184, 74, 73, 72, 11, 302, 303, 304, 408,
 | 
				
			||||||
 | 
					    // Lower semi-inner.
 | 
				
			||||||
 | 
					    62, 96, 89, 179, 86, 15, 316, 403, 319, 325, 292,
 | 
				
			||||||
 | 
					    // Upper semi-inner (excluding corners).
 | 
				
			||||||
 | 
					    183, 42, 41, 38, 12, 268, 271, 272, 407};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr std::array<int, kEyeLandmarksNum> kLeftEyeLandmarksIndicesMapping{
 | 
				
			||||||
 | 
					    // Lower contour.
 | 
				
			||||||
 | 
					    33, 7, 163, 144, 145, 153, 154, 155, 133,
 | 
				
			||||||
 | 
					    // upper contour (excluding corners).
 | 
				
			||||||
 | 
					    246, 161, 160, 159, 158, 157, 173,
 | 
				
			||||||
 | 
					    // Halo x2 lower contour.
 | 
				
			||||||
 | 
					    130, 25, 110, 24, 23, 22, 26, 112, 243,
 | 
				
			||||||
 | 
					    // Halo x2 upper contour (excluding corners).
 | 
				
			||||||
 | 
					    247, 30, 29, 27, 28, 56, 190,
 | 
				
			||||||
 | 
					    // Halo x3 lower contour.
 | 
				
			||||||
 | 
					    226, 31, 228, 229, 230, 231, 232, 233, 244,
 | 
				
			||||||
 | 
					    // Halo x3 upper contour (excluding corners).
 | 
				
			||||||
 | 
					    113, 225, 224, 223, 222, 221, 189,
 | 
				
			||||||
 | 
					    // Halo x4 upper contour (no lower because of mesh structure) or
 | 
				
			||||||
 | 
					    // eyebrow inner contour.
 | 
				
			||||||
 | 
					    35, 124, 46, 53, 52, 65,
 | 
				
			||||||
 | 
					    // Halo x5 lower contour.
 | 
				
			||||||
 | 
					    143, 111, 117, 118, 119, 120, 121, 128, 245,
 | 
				
			||||||
 | 
					    // Halo x5 upper contour (excluding corners) or eyebrow outer contour.
 | 
				
			||||||
 | 
					    156, 70, 63, 105, 66, 107, 55, 193};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr std::array<int, kEyeLandmarksNum> kRightEyeLandmarksIndicesMapping{
 | 
				
			||||||
 | 
					    // Lower contour.
 | 
				
			||||||
 | 
					    263, 249, 390, 373, 374, 380, 381, 382, 362,
 | 
				
			||||||
 | 
					    // Upper contour (excluding corners).
 | 
				
			||||||
 | 
					    466, 388, 387, 386, 385, 384, 398,
 | 
				
			||||||
 | 
					    // Halo x2 lower contour.
 | 
				
			||||||
 | 
					    359, 255, 339, 254, 253, 252, 256, 341, 463,
 | 
				
			||||||
 | 
					    // Halo x2 upper contour (excluding corners).
 | 
				
			||||||
 | 
					    467, 260, 259, 257, 258, 286, 414,
 | 
				
			||||||
 | 
					    // Halo x3 lower contour.
 | 
				
			||||||
 | 
					    446, 261, 448, 449, 450, 451, 452, 453, 464,
 | 
				
			||||||
 | 
					    // Halo x3 upper contour (excluding corners).
 | 
				
			||||||
 | 
					    342, 445, 444, 443, 442, 441, 413,
 | 
				
			||||||
 | 
					    // Halo x4 upper contour (no lower because of mesh structure) or
 | 
				
			||||||
 | 
					    // eyebrow inner contour.
 | 
				
			||||||
 | 
					    265, 353, 276, 283, 282, 295,
 | 
				
			||||||
 | 
					    // Halo x5 lower contour.
 | 
				
			||||||
 | 
					    372, 340, 346, 347, 348, 349, 350, 357, 465,
 | 
				
			||||||
 | 
					    // Halo x5 upper contour (excluding corners) or eyebrow outer contour.
 | 
				
			||||||
 | 
					    383, 300, 293, 334, 296, 336, 285, 417};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr std::array<int, kIrisLandmarksNum> kLeftIrisLandmarksIndicesMapping{
 | 
				
			||||||
 | 
					    // Center.
 | 
				
			||||||
 | 
					    468,
 | 
				
			||||||
 | 
					    // Iris right edge.
 | 
				
			||||||
 | 
					    469,
 | 
				
			||||||
 | 
					    // Iris top edge.
 | 
				
			||||||
 | 
					    470,
 | 
				
			||||||
 | 
					    // Iris left edge.
 | 
				
			||||||
 | 
					    471,
 | 
				
			||||||
 | 
					    // Iris bottom edge.
 | 
				
			||||||
 | 
					    472};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr std::array<int, kContoursNumForIrisAvg> kLeftIrisAvgIndices = {
 | 
				
			||||||
 | 
					    // Lower contour.
 | 
				
			||||||
 | 
					    33, 7, 163, 144, 145, 153, 154, 155, 133,
 | 
				
			||||||
 | 
					    // Upper contour (excluding corners).
 | 
				
			||||||
 | 
					    246, 161, 160, 159, 158, 157, 173};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr std::array<int, kIrisLandmarksNum> kRightIrisLandmarksIndicesMapping{
 | 
				
			||||||
 | 
					    // Center.
 | 
				
			||||||
 | 
					    473,
 | 
				
			||||||
 | 
					    // Iris right edge.
 | 
				
			||||||
 | 
					    474,
 | 
				
			||||||
 | 
					    // Iris top edge.
 | 
				
			||||||
 | 
					    475,
 | 
				
			||||||
 | 
					    // Iris left edge.
 | 
				
			||||||
 | 
					    476,
 | 
				
			||||||
 | 
					    // Iris bottom edge.
 | 
				
			||||||
 | 
					    477};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr std::array<int, kContoursNumForIrisAvg> kRightIrisAvgIndices = {
 | 
				
			||||||
 | 
					    // Lower contour.
 | 
				
			||||||
 | 
					    263, 249, 390, 373, 374, 380, 381, 382, 362,
 | 
				
			||||||
 | 
					    // Upper contour (excluding corners).
 | 
				
			||||||
 | 
					    466, 388, 387, 386, 385, 384, 398};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ConfigureSplitTensorVectorCalculator(
 | 
				
			||||||
 | 
					    mediapipe::SplitVectorCalculatorOptions* options) {
 | 
				
			||||||
 | 
					  for (int i = 0; i < kAttentionModelSplitNum; ++i) {
 | 
				
			||||||
 | 
					    auto* range = options->add_ranges();
 | 
				
			||||||
 | 
					    range->set_begin(i);
 | 
				
			||||||
 | 
					    range->set_end(i + 1);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Stream<NormalizedLandmarkList> ConvertTensorsToLandmarks(
 | 
				
			||||||
 | 
					    int landmarks_num, int input_image_width, int input_image_height,
 | 
				
			||||||
 | 
					    Stream<std::vector<Tensor>> tensors, Graph& graph) {
 | 
				
			||||||
 | 
					  auto& tensors_to_landmarks = graph.AddNode("TensorsToLandmarksCalculator");
 | 
				
			||||||
 | 
					  auto* options =
 | 
				
			||||||
 | 
					      &tensors_to_landmarks
 | 
				
			||||||
 | 
					           .GetOptions<mediapipe::TensorsToLandmarksCalculatorOptions>();
 | 
				
			||||||
 | 
					  options->set_num_landmarks(landmarks_num);
 | 
				
			||||||
 | 
					  options->set_input_image_width(input_image_width);
 | 
				
			||||||
 | 
					  options->set_input_image_height(input_image_height);
 | 
				
			||||||
 | 
					  tensors >> tensors_to_landmarks.In(kTensorsTag);
 | 
				
			||||||
 | 
					  return tensors_to_landmarks.Out(kNormLandmarksTag)
 | 
				
			||||||
 | 
					      .Cast<NormalizedLandmarkList>();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Stream<NormalizedLandmarkList> RefineFaceLandmarks(
 | 
				
			||||||
 | 
					    Stream<NormalizedLandmarkList> mesh_landmarks,
 | 
				
			||||||
 | 
					    Stream<NormalizedLandmarkList> lips_landmarks,
 | 
				
			||||||
 | 
					    Stream<NormalizedLandmarkList> left_eye_landmarks,
 | 
				
			||||||
 | 
					    Stream<NormalizedLandmarkList> right_eye_landmarks,
 | 
				
			||||||
 | 
					    Stream<NormalizedLandmarkList> left_iris_landmarks,
 | 
				
			||||||
 | 
					    Stream<NormalizedLandmarkList> right_iris_landmarks, Graph& graph) {
 | 
				
			||||||
 | 
					  auto& refine_landmarks = graph.AddNode("LandmarksRefinementCalculator");
 | 
				
			||||||
 | 
					  auto& refinement_options =
 | 
				
			||||||
 | 
					      refine_landmarks
 | 
				
			||||||
 | 
					          .GetOptions<mediapipe::LandmarksRefinementCalculatorOptions>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Face mesh landmarks.
 | 
				
			||||||
 | 
					  auto* refinement_for_mesh = refinement_options.add_refinement();
 | 
				
			||||||
 | 
					  refinement_for_mesh->mutable_indexes_mapping()->Assign(
 | 
				
			||||||
 | 
					      kMeshLandmarksIndicesMapping.begin(), kMeshLandmarksIndicesMapping.end());
 | 
				
			||||||
 | 
					  refinement_for_mesh->mutable_z_refinement()->mutable_copy();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Lips landmarks.
 | 
				
			||||||
 | 
					  auto* refinement_for_lips = refinement_options.add_refinement();
 | 
				
			||||||
 | 
					  refinement_for_lips->mutable_indexes_mapping()->Assign(
 | 
				
			||||||
 | 
					      kLipsLandmarksIndicesMapping.begin(), kLipsLandmarksIndicesMapping.end());
 | 
				
			||||||
 | 
					  refinement_for_lips->mutable_z_refinement()->mutable_none();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Left eye landmarks.
 | 
				
			||||||
 | 
					  auto* refinement_for_left_eye = refinement_options.add_refinement();
 | 
				
			||||||
 | 
					  refinement_for_left_eye->mutable_indexes_mapping()->Assign(
 | 
				
			||||||
 | 
					      kLeftEyeLandmarksIndicesMapping.begin(),
 | 
				
			||||||
 | 
					      kLeftEyeLandmarksIndicesMapping.end());
 | 
				
			||||||
 | 
					  refinement_for_left_eye->mutable_z_refinement()->mutable_none();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Right eye landmarks.
 | 
				
			||||||
 | 
					  auto* refinement_for_right_eye = refinement_options.add_refinement();
 | 
				
			||||||
 | 
					  refinement_for_right_eye->mutable_indexes_mapping()->Assign(
 | 
				
			||||||
 | 
					      kRightEyeLandmarksIndicesMapping.begin(),
 | 
				
			||||||
 | 
					      kRightEyeLandmarksIndicesMapping.end());
 | 
				
			||||||
 | 
					  refinement_for_right_eye->mutable_z_refinement()->mutable_none();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Left iris landmarks.
 | 
				
			||||||
 | 
					  auto* refinement_for_left_iris = refinement_options.add_refinement();
 | 
				
			||||||
 | 
					  refinement_for_left_iris->mutable_indexes_mapping()->Assign(
 | 
				
			||||||
 | 
					      kLeftIrisLandmarksIndicesMapping.begin(),
 | 
				
			||||||
 | 
					      kLeftIrisLandmarksIndicesMapping.end());
 | 
				
			||||||
 | 
					  refinement_for_left_iris->mutable_z_refinement()
 | 
				
			||||||
 | 
					      ->mutable_assign_average()
 | 
				
			||||||
 | 
					      ->mutable_indexes_for_average()
 | 
				
			||||||
 | 
					      ->Assign(kLeftIrisAvgIndices.begin(), kLeftIrisAvgIndices.end());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Right iris landmarks.
 | 
				
			||||||
 | 
					  auto* refinement_for_right_iris = refinement_options.add_refinement();
 | 
				
			||||||
 | 
					  refinement_for_right_iris->mutable_indexes_mapping()->Assign(
 | 
				
			||||||
 | 
					      kRightIrisLandmarksIndicesMapping.begin(),
 | 
				
			||||||
 | 
					      kRightIrisLandmarksIndicesMapping.end());
 | 
				
			||||||
 | 
					  refinement_for_right_iris->mutable_z_refinement()
 | 
				
			||||||
 | 
					      ->mutable_assign_average()
 | 
				
			||||||
 | 
					      ->mutable_indexes_for_average()
 | 
				
			||||||
 | 
					      ->Assign(kRightIrisAvgIndices.begin(), kRightIrisAvgIndices.end());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mesh_landmarks >> refine_landmarks.In(kLandmarksTag)[0];
 | 
				
			||||||
 | 
					  lips_landmarks >> refine_landmarks.In(kLandmarksTag)[1];
 | 
				
			||||||
 | 
					  left_eye_landmarks >> refine_landmarks.In(kLandmarksTag)[2];
 | 
				
			||||||
 | 
					  right_eye_landmarks >> refine_landmarks.In(kLandmarksTag)[3];
 | 
				
			||||||
 | 
					  left_iris_landmarks >> refine_landmarks.In(kLandmarksTag)[4];
 | 
				
			||||||
 | 
					  right_iris_landmarks >> refine_landmarks.In(kLandmarksTag)[5];
 | 
				
			||||||
 | 
					  return refine_landmarks.Out(kRefinedLandmarksTag)
 | 
				
			||||||
 | 
					      .Cast<NormalizedLandmarkList>();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Graph to transform face landmarks model output tensors into landmarks.
 | 
				
			||||||
 | 
					// The graph can support two types of model: regular and attention model with
 | 
				
			||||||
 | 
					// refined lips, eye and irises.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Inputs:
 | 
				
			||||||
 | 
					//   TENSORS - std::vector<Tensor>
 | 
				
			||||||
 | 
					//     Landmarks tensors to be transformed. If regular model, a vector of single
 | 
				
			||||||
 | 
					//     Tensor is expected. If a model with attention, a vector of 6 Tensors is
 | 
				
			||||||
 | 
					//     expected.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Outputs:
 | 
				
			||||||
 | 
					//   NORM_LANDMARKS: - NormalizedLandmarkList
 | 
				
			||||||
 | 
					//     Transformed face landmarks.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Example:
 | 
				
			||||||
 | 
					// node {
 | 
				
			||||||
 | 
					//   calculator:
 | 
				
			||||||
 | 
					//   "mediapipe.tasks.vision.face_landmarker.TensorsToFaceLandmarksGraph"
 | 
				
			||||||
 | 
					//   input_stream: "TENSORS:tensors"
 | 
				
			||||||
 | 
					//   output_stream: "NORM_LANDMARKS:norm_landmarks"
 | 
				
			||||||
 | 
					//   options {
 | 
				
			||||||
 | 
					//     [mediapipe.tasks.vision.face_landmarker.proto.TensorsToFaceLandmarksGraphOptions.ext]
 | 
				
			||||||
 | 
					//     {
 | 
				
			||||||
 | 
					//        input_image_width: 192
 | 
				
			||||||
 | 
					//        input_image_height: 192
 | 
				
			||||||
 | 
					//     }
 | 
				
			||||||
 | 
					//   }
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					class TensorsToFaceLandmarksGraph : public Subgraph {
 | 
				
			||||||
 | 
					 public:
 | 
				
			||||||
 | 
					  absl::StatusOr<CalculatorGraphConfig> GetConfig(
 | 
				
			||||||
 | 
					      SubgraphContext* sc) override {
 | 
				
			||||||
 | 
					    Graph graph;
 | 
				
			||||||
 | 
					    auto norm_landmarks = BuildTensorsToFaceLandmarksGraph(
 | 
				
			||||||
 | 
					        sc->Options<proto::TensorsToFaceLandmarksGraphOptions>(),
 | 
				
			||||||
 | 
					        graph.In(kTensorsTag).Cast<std::vector<Tensor>>(), graph);
 | 
				
			||||||
 | 
					    norm_landmarks >>
 | 
				
			||||||
 | 
					        graph.Out(kNormLandmarksTag).Cast<NormalizedLandmarkList>();
 | 
				
			||||||
 | 
					    return graph.GetConfig();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 private:
 | 
				
			||||||
 | 
					  Stream<NormalizedLandmarkList> BuildTensorsToFaceLandmarksGraph(
 | 
				
			||||||
 | 
					      const proto::TensorsToFaceLandmarksGraphOptions& subgraph_options,
 | 
				
			||||||
 | 
					      Stream<std::vector<Tensor>> tensors, Graph& graph) {
 | 
				
			||||||
 | 
					    const int input_image_width = subgraph_options.input_image_width();
 | 
				
			||||||
 | 
					    const int input_image_height = subgraph_options.input_image_height();
 | 
				
			||||||
 | 
					    if (subgraph_options.is_attention_model()) {
 | 
				
			||||||
 | 
					      // Split tensors from attention model to 6 streams: mesh, lips, left_eye,
 | 
				
			||||||
 | 
					      // right_eye, left_iris and right iris.
 | 
				
			||||||
 | 
					      auto& split_tensors_vector = graph.AddNode("SplitTensorVectorCalculator");
 | 
				
			||||||
 | 
					      ConfigureSplitTensorVectorCalculator(
 | 
				
			||||||
 | 
					          &split_tensors_vector
 | 
				
			||||||
 | 
					               .GetOptions<mediapipe::SplitVectorCalculatorOptions>());
 | 
				
			||||||
 | 
					      tensors >> split_tensors_vector.In("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      auto mesh_landmarks = ConvertTensorsToLandmarks(
 | 
				
			||||||
 | 
					          kMeshLandmarksNum, input_image_width, input_image_height,
 | 
				
			||||||
 | 
					          split_tensors_vector.Out(0).Cast<std::vector<Tensor>>(), graph);
 | 
				
			||||||
 | 
					      auto lips_landmarks = ConvertTensorsToLandmarks(
 | 
				
			||||||
 | 
					          kLipsLandmarksNum, input_image_width, input_image_height,
 | 
				
			||||||
 | 
					          split_tensors_vector.Out(1).Cast<std::vector<Tensor>>(), graph);
 | 
				
			||||||
 | 
					      auto left_eye_landmarks = ConvertTensorsToLandmarks(
 | 
				
			||||||
 | 
					          kEyeLandmarksNum, input_image_width, input_image_height,
 | 
				
			||||||
 | 
					          split_tensors_vector.Out(2).Cast<std::vector<Tensor>>(), graph);
 | 
				
			||||||
 | 
					      auto right_eye_landmarks = ConvertTensorsToLandmarks(
 | 
				
			||||||
 | 
					          kEyeLandmarksNum, input_image_width, input_image_height,
 | 
				
			||||||
 | 
					          split_tensors_vector.Out(3).Cast<std::vector<Tensor>>(), graph);
 | 
				
			||||||
 | 
					      auto left_iris_landmarks = ConvertTensorsToLandmarks(
 | 
				
			||||||
 | 
					          kIrisLandmarksNum, input_image_width, input_image_height,
 | 
				
			||||||
 | 
					          split_tensors_vector.Out(4).Cast<std::vector<Tensor>>(), graph);
 | 
				
			||||||
 | 
					      auto right_iris_landmarks = ConvertTensorsToLandmarks(
 | 
				
			||||||
 | 
					          kIrisLandmarksNum, input_image_width, input_image_height,
 | 
				
			||||||
 | 
					          split_tensors_vector.Out(5).Cast<std::vector<Tensor>>(), graph);
 | 
				
			||||||
 | 
					      return RefineFaceLandmarks(mesh_landmarks, lips_landmarks,
 | 
				
			||||||
 | 
					                                 left_eye_landmarks, right_eye_landmarks,
 | 
				
			||||||
 | 
					                                 left_iris_landmarks, right_iris_landmarks,
 | 
				
			||||||
 | 
					                                 graph);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return ConvertTensorsToLandmarks(kMeshLandmarksNum, input_image_width,
 | 
				
			||||||
 | 
					                                       input_image_height, tensors, graph);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// clang-format off
 | 
				
			||||||
 | 
					REGISTER_MEDIAPIPE_GRAPH(
 | 
				
			||||||
 | 
					  ::mediapipe::tasks::vision::face_landmarker::TensorsToFaceLandmarksGraph); // NOLINT
 | 
				
			||||||
 | 
					// clang-format on
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}  // namespace face_landmarker
 | 
				
			||||||
 | 
					}  // namespace vision
 | 
				
			||||||
 | 
					}  // namespace tasks
 | 
				
			||||||
 | 
					}  // namespace mediapipe
 | 
				
			||||||
							
								
								
									
										3
									
								
								mediapipe/tasks/testdata/vision/BUILD
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								mediapipe/tasks/testdata/vision/BUILD
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -41,6 +41,7 @@ mediapipe_files(srcs = [
 | 
				
			||||||
    "face_detection_full_range_sparse.tflite",
 | 
					    "face_detection_full_range_sparse.tflite",
 | 
				
			||||||
    "face_detection_short_range.tflite",
 | 
					    "face_detection_short_range.tflite",
 | 
				
			||||||
    "face_landmark.tflite",
 | 
					    "face_landmark.tflite",
 | 
				
			||||||
 | 
					    "face_landmark_with_attention.tflite",
 | 
				
			||||||
    "fist.jpg",
 | 
					    "fist.jpg",
 | 
				
			||||||
    "fist.png",
 | 
					    "fist.png",
 | 
				
			||||||
    "hand_landmark_full.tflite",
 | 
					    "hand_landmark_full.tflite",
 | 
				
			||||||
| 
						 | 
					@ -140,6 +141,7 @@ filegroup(
 | 
				
			||||||
        "face_detection_full_range_sparse.tflite",
 | 
					        "face_detection_full_range_sparse.tflite",
 | 
				
			||||||
        "face_detection_short_range.tflite",
 | 
					        "face_detection_short_range.tflite",
 | 
				
			||||||
        "face_landmark.tflite",
 | 
					        "face_landmark.tflite",
 | 
				
			||||||
 | 
					        "face_landmark_with_attention.tflite",
 | 
				
			||||||
        "hand_landmark_full.tflite",
 | 
					        "hand_landmark_full.tflite",
 | 
				
			||||||
        "hand_landmark_lite.tflite",
 | 
					        "hand_landmark_lite.tflite",
 | 
				
			||||||
        "hand_landmarker.task",
 | 
					        "hand_landmarker.task",
 | 
				
			||||||
| 
						 | 
					@ -175,6 +177,7 @@ filegroup(
 | 
				
			||||||
        "pointing_up_rotated_landmarks.pbtxt",
 | 
					        "pointing_up_rotated_landmarks.pbtxt",
 | 
				
			||||||
        "portrait_expected_detection.pbtxt",
 | 
					        "portrait_expected_detection.pbtxt",
 | 
				
			||||||
        "portrait_expected_face_landmarks.pbtxt",
 | 
					        "portrait_expected_face_landmarks.pbtxt",
 | 
				
			||||||
 | 
					        "portrait_expected_face_landmarks_with_attention.pbtxt",
 | 
				
			||||||
        "thumb_up_landmarks.pbtxt",
 | 
					        "thumb_up_landmarks.pbtxt",
 | 
				
			||||||
        "thumb_up_rotated_landmarks.pbtxt",
 | 
					        "thumb_up_rotated_landmarks.pbtxt",
 | 
				
			||||||
        "victory_landmarks.pbtxt",
 | 
					        "victory_landmarks.pbtxt",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1914
									
								
								mediapipe/tasks/testdata/vision/portrait_expected_face_landmarks_with_attention.pbtxt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1914
									
								
								mediapipe/tasks/testdata/vision/portrait_expected_face_landmarks_with_attention.pbtxt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										6
									
								
								third_party/external_files.bzl
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								third_party/external_files.bzl
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -724,6 +724,12 @@ def external_files():
 | 
				
			||||||
        urls = ["https://storage.googleapis.com/mediapipe-assets/portrait_expected_face_landmarks.pbtxt?generation=1676316357333369"],
 | 
					        urls = ["https://storage.googleapis.com/mediapipe-assets/portrait_expected_face_landmarks.pbtxt?generation=1676316357333369"],
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    http_file(
 | 
				
			||||||
 | 
					        name = "com_google_mediapipe_portrait_expected_face_landmarks_with_attention_pbtxt",
 | 
				
			||||||
 | 
					        sha256 = "f2ccd889654b914996e4aab0d7831a3e73d3b63d6c14f6bac4bec5cd3415bce4",
 | 
				
			||||||
 | 
					        urls = ["https://storage.googleapis.com/mediapipe-assets/portrait_expected_face_landmarks_with_attention.pbtxt?generation=1676415475626542"],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    http_file(
 | 
					    http_file(
 | 
				
			||||||
        name = "com_google_mediapipe_portrait_jpg",
 | 
					        name = "com_google_mediapipe_portrait_jpg",
 | 
				
			||||||
        sha256 = "a6f11efaa834706db23f275b6115058fa87fc7f14362681e6abe14e82749de3e",
 | 
					        sha256 = "a6f11efaa834706db23f275b6115058fa87fc7f14362681e6abe14e82749de3e",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user