# MediaPipe graph to filter landmarks temporally (across packets with
# incremental timestamps) to reduce jitter.
#
# EXAMPLE:
#   node {
#     calculator: "PoseLandmarkFiltering"
#     input_side_packet: "ENABLE:enable"
#     input_stream: "IMAGE_SIZE:image_size"
#     input_stream: "NORM_LANDMARKS:landmarks"
#     input_stream: "AUX_NORM_LANDMARKS:aux_landmarks"
#     input_stream: "WORLD_LANDMARKS:world_landmarks"
#     output_stream: "FILTERED_NORM_LANDMARKS:filtered_landmarks"
#     output_stream: "FILTERED_AUX_NORM_LANDMARKS:filtered_aux_landmarks"
#     output_stream: "FILTERED_WORLD_LANDMARKS:filtered_world_landmarks"
#   }

type: "PoseLandmarkFiltering"

# Whether to enable filtering. If unspecified, functions as enabled. (bool)
input_side_packet: "ENABLE:enable"

# Size of the image (width & height) where the landmarks are estimated from.
# (std::pair<int, int>)
input_stream: "IMAGE_SIZE:image_size"
# Normalized landmarks. (NormalizedLandmarkList)
input_stream: "NORM_LANDMARKS:landmarks"
# Auxiliary set of normalized landmarks. (NormalizedLandmarkList)
input_stream: "AUX_NORM_LANDMARKS:aux_landmarks"
# World landmarks. (LandmarkList)
input_stream: "WORLD_LANDMARKS:world_landmarks"
# Filtered normalized landmarks. (NormalizedLandmarkList)
output_stream: "FILTERED_NORM_LANDMARKS:filtered_landmarks"
# Filtered auxiliary set of normalized landmarks. (NormalizedLandmarkList)
output_stream: "FILTERED_AUX_NORM_LANDMARKS:filtered_aux_landmarks"
# Filtered world landmarks. (LandmarkList)
output_stream: "FILTERED_WORLD_LANDMARKS:filtered_world_landmarks"

# Converts landmarks to a detection that tightly encloses all landmarks.
node {
  calculator: "LandmarksToDetectionCalculator"
  input_stream: "NORM_LANDMARKS:aux_landmarks"
  output_stream: "DETECTION:aux_detection"
}

# Converts detection into a rectangle based on center and scale alignment
# points.
node {
  calculator: "AlignmentPointsRectsCalculator"
  input_stream: "DETECTION:aux_detection"
  input_stream: "IMAGE_SIZE:image_size"
  output_stream: "NORM_RECT:roi"
  options: {
    [mediapipe.DetectionsToRectsCalculatorOptions.ext] {
      rotation_vector_start_keypoint_index: 0
      rotation_vector_end_keypoint_index: 1
      rotation_vector_target_angle_degrees: 90
    }
  }
}

# Smoothes pose landmark visibilities to reduce jitter.
node {
  calculator: "SwitchContainer"
  input_side_packet: "ENABLE:enable"
  input_stream: "NORM_LANDMARKS:landmarks"
  output_stream: "NORM_FILTERED_LANDMARKS:filtered_visibility"
  options: {
    [mediapipe.SwitchContainerOptions.ext] {
      enable: true
      contained_node: {
        calculator: "VisibilitySmoothingCalculator"
        options: {
          [mediapipe.VisibilitySmoothingCalculatorOptions.ext] {
            no_filter: {}
          }
        }
      }
      contained_node: {
        calculator: "VisibilitySmoothingCalculator"
        options: {
          [mediapipe.VisibilitySmoothingCalculatorOptions.ext] {
            low_pass_filter {
              alpha: 0.1
            }
          }
        }
      }
    }
  }
}

# Smoothes pose landmark coordinates to reduce jitter.
node {
  calculator: "SwitchContainer"
  input_side_packet: "ENABLE:enable"
  input_stream: "NORM_LANDMARKS:filtered_visibility"
  input_stream: "IMAGE_SIZE:image_size"
  input_stream: "OBJECT_SCALE_ROI:roi"
  output_stream: "NORM_FILTERED_LANDMARKS:filtered_landmarks"
  options: {
    [mediapipe.SwitchContainerOptions.ext] {
      enable: true
      contained_node: {
        calculator: "LandmarksSmoothingCalculator"
        options: {
          [mediapipe.LandmarksSmoothingCalculatorOptions.ext] {
            no_filter: {}
          }
        }
      }
      contained_node: {
        calculator: "LandmarksSmoothingCalculator"
        options: {
          [mediapipe.LandmarksSmoothingCalculatorOptions.ext] {
            one_euro_filter {
              # Min cutoff 0.1 results into ~0.01 alpha in landmark EMA filter
              # when landmark is static.
              min_cutoff: 0.05
              # Beta 80.0 in combintation with min_cutoff 0.05 results into
              # ~0.94 alpha in landmark EMA filter when landmark is moving fast.
              beta: 80.0
              # Derivative cutoff 1.0 results into ~0.17 alpha in landmark
              # velocity EMA filter.
              derivate_cutoff: 1.0
            }
          }
        }
      }
    }
  }
}

# Smoothes world landmark visibilities to reduce jitter.
node {
  calculator: "SwitchContainer"
  input_side_packet: "ENABLE:enable"
  input_stream: "LANDMARKS:world_landmarks"
  output_stream: "FILTERED_LANDMARKS:filtered_world_visibility"
  options: {
    [mediapipe.SwitchContainerOptions.ext] {
      enable: true
      contained_node: {
        calculator: "VisibilitySmoothingCalculator"
        options: {
          [mediapipe.VisibilitySmoothingCalculatorOptions.ext] {
            no_filter: {}
          }
        }
      }
      contained_node: {
        calculator: "VisibilitySmoothingCalculator"
        options: {
          [mediapipe.VisibilitySmoothingCalculatorOptions.ext] {
            low_pass_filter {
              alpha: 0.1
            }
          }
        }
      }
    }
  }
}

# Smoothes world landmark coordinates to reduce jitter.
node {
  calculator: "SwitchContainer"
  input_side_packet: "ENABLE:enable"
  input_stream: "LANDMARKS:filtered_world_visibility"
  output_stream: "FILTERED_LANDMARKS:filtered_world_landmarks"
  options: {
    [mediapipe.SwitchContainerOptions.ext] {
      enable: true
      contained_node: {
        calculator: "LandmarksSmoothingCalculator"
        options: {
          [mediapipe.LandmarksSmoothingCalculatorOptions.ext] {
            no_filter: {}
          }
        }
      }
      contained_node: {
        calculator: "LandmarksSmoothingCalculator"
        options: {
          [mediapipe.LandmarksSmoothingCalculatorOptions.ext] {
            one_euro_filter {
              # Min cutoff 0.1 results into ~ 0.02 alpha in landmark EMA filter
              # when landmark is static.
              min_cutoff: 0.1
              # Beta 40.0 in combintation with min_cutoff 0.1 results into ~0.8
              # alpha in landmark EMA filter when landmark is moving fast.
              beta: 40.0
              # Derivative cutoff 1.0 results into ~0.17 alpha in landmark
              # velocity EMA filter.
              derivate_cutoff: 1.0
              # As world landmdarks are predicted in real world 3D coordintates
              # in meters (rather than in pixels of input image) prediction
              # scale does not depend on the pose size in the image.
              disable_value_scaling: true
            }
          }
        }
      }
    }
  }
}

# Smoothes auxiliary landmark visibilities to reduce jitter.
node {
  calculator: "VisibilitySmoothingCalculator"
  input_stream: "NORM_LANDMARKS:aux_landmarks"
  output_stream: "NORM_FILTERED_LANDMARKS:filtered_aux_visibility"
  options: {
    [mediapipe.VisibilitySmoothingCalculatorOptions.ext] {
      low_pass_filter {
        alpha: 0.1
      }
    }
  }
}

# Smoothes auxiliary landmarks to reduce jitter.
node {
  calculator: "LandmarksSmoothingCalculator"
  input_stream: "NORM_LANDMARKS:filtered_aux_visibility"
  input_stream: "IMAGE_SIZE:image_size"
  input_stream: "OBJECT_SCALE_ROI:roi"
  output_stream: "NORM_FILTERED_LANDMARKS:filtered_aux_landmarks"
  options: {
    [mediapipe.LandmarksSmoothingCalculatorOptions.ext] {
      # Auxiliary landmarks are smoothed heavier than main landmarks to
      # make ROI crop for pose landmarks prediction very stable when
      # object is not moving but responsive enough in case of sudden
      # movements.
      one_euro_filter {
        # Min cutoff 0.01 results into ~0.002 alpha in landmark EMA
        # filter when landmark is static.
        min_cutoff: 0.01
        # Beta 10.0 in combintation with min_cutoff 0.01 results into ~0.68
        # alpha in landmark EMA filter when landmark is moving fast.
        beta: 10.0
        # Derivative cutoff 1.0 results into ~0.17 alpha in landmark
        # velocity EMA filter.
        derivate_cutoff: 1.0
      }
    }
  }
}
