# MediaPipe graph to detect/predict face landmarks. (GPU input, and inference is # executed on GPU.) This graph tries to skip face detection as much as possible # by using previously detected/predicted landmarks for new images. # # It is required that "face_detection_short_range.tflite" is available at # "mediapipe/modules/face_detection/face_detection_short_range.tflite" # path during execution. # # It is required that "face_landmark.tflite" is available at # "mediapipe/modules/face_landmark/face_landmark.tflite" # path during execution if `with_attention` is not set or set to `false`. # # It is required that "face_landmark_with_attention.tflite" is available at # "mediapipe/modules/face_landmark/face_landmark_with_attention.tflite" # path during execution if `with_attention` is set to `true`. # # EXAMPLE: # node { # calculator: "FaceLandmarkFrontGpu" # input_stream: "IMAGE:image" # input_side_packet: "NUM_FACES:num_faces" # input_side_packet: "USE_PREV_LANDMARKS:use_prev_landmarks" # input_side_packet: "WITH_ATTENTION:with_attention" # output_stream: "LANDMARKS:multi_face_landmarks" # } type: "FaceLandmarkFrontGpu" # GPU image. (GpuBuffer) input_stream: "IMAGE:image" # Max number of faces to detect/track. (int) input_side_packet: "NUM_FACES:num_faces" # Whether landmarks on the previous image should be used to help localize # landmarks on the current image. (bool) input_side_packet: "USE_PREV_LANDMARKS:use_prev_landmarks" # Whether to run face mesh model with attention on lips and eyes. (bool) # Attention provides more accuracy on lips and eye regions as well as iris # landmarks. input_side_packet: "WITH_ATTENTION:with_attention" # Collection of detected/predicted faces, each represented as a list of 468 face # landmarks. (std::vector) # NOTE: there will not be an output packet in the LANDMARKS stream for this # particular timestamp if none of faces detected. However, the MediaPipe # framework will internally inform the downstream calculators of the absence of # this packet so that they don't wait for it unnecessarily. output_stream: "LANDMARKS:multi_face_landmarks" # Extra outputs (for debugging, for instance). # Detected faces. (std::vector) output_stream: "DETECTIONS:face_detections" # Regions of interest calculated based on landmarks. # (std::vector) output_stream: "ROIS_FROM_LANDMARKS:face_rects_from_landmarks" # Regions of interest calculated based on face detections. # (std::vector) output_stream: "ROIS_FROM_DETECTIONS:face_rects_from_detections" # When the optional input side packet "use_prev_landmarks" is either absent or # set to true, uses the landmarks on the previous image to help localize # landmarks on the current image. node { calculator: "GateCalculator" input_side_packet: "ALLOW:use_prev_landmarks" input_stream: "prev_face_rects_from_landmarks" output_stream: "gated_prev_face_rects_from_landmarks" options: { [mediapipe.GateCalculatorOptions.ext] { allow: true } } } # Determines if an input vector of NormalizedRect has a size greater than or # equal to the provided num_faces. node { calculator: "NormalizedRectVectorHasMinSizeCalculator" input_stream: "ITERABLE:gated_prev_face_rects_from_landmarks" input_side_packet: "num_faces" output_stream: "prev_has_enough_faces" } # Drops the incoming image if enough faces have already been identified from the # previous image. Otherwise, passes the incoming image through to trigger a new # round of face detection. node { calculator: "GateCalculator" input_stream: "image" input_stream: "DISALLOW:prev_has_enough_faces" output_stream: "gated_image" options: { [mediapipe.GateCalculatorOptions.ext] { empty_packets_as_allow: true } } } # Detects faces. node { calculator: "FaceDetectionShortRangeGpu" input_stream: "IMAGE:gated_image" output_stream: "DETECTIONS:all_face_detections" } # Makes sure there are no more detections than the provided num_faces. node { calculator: "ClipDetectionVectorSizeCalculator" input_stream: "all_face_detections" output_stream: "face_detections" input_side_packet: "num_faces" } # Calculate size of the image. node { calculator: "ImagePropertiesCalculator" input_stream: "IMAGE_GPU:gated_image" output_stream: "SIZE:gated_image_size" } # Outputs each element of face_detections at a fake timestamp for the rest of # the graph to process. Clones the image size packet for each face_detection at # the fake timestamp. At the end of the loop, outputs the BATCH_END timestamp # for downstream calculators to inform them that all elements in the vector have # been processed. node { calculator: "BeginLoopDetectionCalculator" input_stream: "ITERABLE:face_detections" input_stream: "CLONE:gated_image_size" output_stream: "ITEM:face_detection" output_stream: "CLONE:detections_loop_image_size" output_stream: "BATCH_END:detections_loop_end_timestamp" } # Calculates region of interest based on face detections, so that can be used # to detect landmarks. node { calculator: "FaceDetectionFrontDetectionToRoi" input_stream: "DETECTION:face_detection" input_stream: "IMAGE_SIZE:detections_loop_image_size" output_stream: "ROI:face_rect_from_detection" } # Collects a NormalizedRect for each face into a vector. Upon receiving the # BATCH_END timestamp, outputs the vector of NormalizedRect at the BATCH_END # timestamp. node { calculator: "EndLoopNormalizedRectCalculator" input_stream: "ITEM:face_rect_from_detection" input_stream: "BATCH_END:detections_loop_end_timestamp" output_stream: "ITERABLE:face_rects_from_detections" } # Performs association between NormalizedRect vector elements from previous # image and rects based on face detections from the current image. This # calculator ensures that the output face_rects vector doesn't contain # overlapping regions based on the specified min_similarity_threshold. node { calculator: "AssociationNormRectCalculator" input_stream: "face_rects_from_detections" input_stream: "gated_prev_face_rects_from_landmarks" output_stream: "face_rects" options: { [mediapipe.AssociationCalculatorOptions.ext] { min_similarity_threshold: 0.5 } } } # Calculate size of the image. node { calculator: "ImagePropertiesCalculator" input_stream: "IMAGE_GPU:image" output_stream: "SIZE:image_size" } # Outputs each element of face_rects at a fake timestamp for the rest of the # graph to process. Clones image and image size packets for each # single_face_rect at the fake timestamp. At the end of the loop, outputs the # BATCH_END timestamp for downstream calculators to inform them that all # elements in the vector have been processed. node { calculator: "BeginLoopNormalizedRectCalculator" input_stream: "ITERABLE:face_rects" input_stream: "CLONE:0:image" input_stream: "CLONE:1:image_size" output_stream: "ITEM:face_rect" output_stream: "CLONE:0:landmarks_loop_image" output_stream: "CLONE:1:landmarks_loop_image_size" output_stream: "BATCH_END:landmarks_loop_end_timestamp" } # Detects face landmarks within specified region of interest of the image. node { calculator: "FaceLandmarkGpu" input_stream: "IMAGE:landmarks_loop_image" input_stream: "ROI:face_rect" input_side_packet: "WITH_ATTENTION:with_attention" output_stream: "LANDMARKS:face_landmarks" } # Calculates region of interest based on face landmarks, so that can be reused # for subsequent image. node { calculator: "FaceLandmarkLandmarksToRoi" input_stream: "LANDMARKS:face_landmarks" input_stream: "IMAGE_SIZE:landmarks_loop_image_size" output_stream: "ROI:face_rect_from_landmarks" } # Collects a set of landmarks for each face into a vector. Upon receiving the # BATCH_END timestamp, outputs the vector of landmarks at the BATCH_END # timestamp. node { calculator: "EndLoopNormalizedLandmarkListVectorCalculator" input_stream: "ITEM:face_landmarks" input_stream: "BATCH_END:landmarks_loop_end_timestamp" output_stream: "ITERABLE:multi_face_landmarks" } # Collects a NormalizedRect for each face into a vector. Upon receiving the # BATCH_END timestamp, outputs the vector of NormalizedRect at the BATCH_END # timestamp. node { calculator: "EndLoopNormalizedRectCalculator" input_stream: "ITEM:face_rect_from_landmarks" input_stream: "BATCH_END:landmarks_loop_end_timestamp" output_stream: "ITERABLE:face_rects_from_landmarks" } # Caches face rects calculated from landmarks, and upon the arrival of the next # input image, sends out the cached rects with timestamps replaced by that of # the input image, essentially generating a packet that carries the previous # face rects. Note that upon the arrival of the very first input image, a # timestamp bound update occurs to jump start the feedback loop. node { calculator: "PreviousLoopbackCalculator" input_stream: "MAIN:image" input_stream: "LOOP:face_rects_from_landmarks" input_stream_info: { tag_index: "LOOP" back_edge: true } output_stream: "PREV_LOOP:prev_face_rects_from_landmarks" }