Java gesture recognizer Tasks API and unit test.
PiperOrigin-RevId: 480978244
This commit is contained in:
		
							parent
							
								
									12c323ffde
								
							
						
					
					
						commit
						9353ed6cce
					
				| 
						 | 
				
			
			@ -87,9 +87,8 @@ struct GestureRecognizerOptions {
 | 
			
		|||
// Performs hand gesture recognition on the given image.
 | 
			
		||||
//
 | 
			
		||||
// TODO  add the link to DevSite.
 | 
			
		||||
// This API expects expects a pre-trained hand gesture model asset bundle, or a
 | 
			
		||||
// custom one created using Model Maker. See <link to the DevSite documentation
 | 
			
		||||
// page>.
 | 
			
		||||
// This API expects a pre-trained hand gesture model asset bundle, or a custom
 | 
			
		||||
// one created using Model Maker. See <link to the DevSite documentation page>.
 | 
			
		||||
//
 | 
			
		||||
// Inputs:
 | 
			
		||||
//   Image
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,6 +40,7 @@ cc_binary(
 | 
			
		|||
    deps = [
 | 
			
		||||
        "//mediapipe/calculators/core:flow_limiter_calculator",
 | 
			
		||||
        "//mediapipe/java/com/google/mediapipe/framework/jni:mediapipe_framework_jni",
 | 
			
		||||
        "//mediapipe/tasks/cc/vision/gesture_recognizer:gesture_recognizer_graph",
 | 
			
		||||
        "//mediapipe/tasks/cc/vision/image_classifier:image_classifier_graph",
 | 
			
		||||
        "//mediapipe/tasks/cc/vision/object_detector:object_detector_graph",
 | 
			
		||||
        "//mediapipe/tasks/java/com/google/mediapipe/tasks/core/jni:model_resources_cache_jni",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ android_library(
 | 
			
		|||
    name = "gesturerecognizer",
 | 
			
		||||
    srcs = [
 | 
			
		||||
        "GestureRecognitionResult.java",
 | 
			
		||||
        "GestureRecognizer.java",
 | 
			
		||||
    ],
 | 
			
		||||
    javacopts = [
 | 
			
		||||
        "-Xep:AndroidJdkLibsChecker:OFF",
 | 
			
		||||
| 
						 | 
				
			
			@ -29,11 +30,19 @@ android_library(
 | 
			
		|||
        "//mediapipe/framework:calculator_options_java_proto_lite",
 | 
			
		||||
        "//mediapipe/framework/formats:classification_java_proto_lite",
 | 
			
		||||
        "//mediapipe/framework/formats:landmark_java_proto_lite",
 | 
			
		||||
        "//mediapipe/java/com/google/mediapipe/framework:android_framework",
 | 
			
		||||
        "//mediapipe/java/com/google/mediapipe/framework/image",
 | 
			
		||||
        "//mediapipe/tasks/cc/components/processors/proto:classifier_options_java_proto_lite",
 | 
			
		||||
        "//mediapipe/tasks/cc/core/proto:base_options_java_proto_lite",
 | 
			
		||||
        "//mediapipe/tasks/cc/vision/gesture_recognizer/proto:gesture_recognizer_graph_options_java_proto_lite",
 | 
			
		||||
        "//mediapipe/tasks/cc/vision/gesture_recognizer/proto:hand_gesture_recognizer_graph_options_java_proto_lite",
 | 
			
		||||
        "//mediapipe/tasks/cc/vision/hand_detector/proto:hand_detector_graph_options_java_proto_lite",
 | 
			
		||||
        "//mediapipe/tasks/cc/vision/hand_landmarker/proto:hand_landmarker_graph_options_java_proto_lite",
 | 
			
		||||
        "//mediapipe/tasks/cc/vision/hand_landmarker/proto:hand_landmarks_detector_graph_options_java_proto_lite",
 | 
			
		||||
        "//mediapipe/tasks/java/com/google/mediapipe/tasks/components/containers:category",
 | 
			
		||||
        "//mediapipe/tasks/java/com/google/mediapipe/tasks/components/containers:landmark",
 | 
			
		||||
        "//mediapipe/tasks/java/com/google/mediapipe/tasks/core",
 | 
			
		||||
        "//mediapipe/tasks/java/com/google/mediapipe/tasks/vision/core",
 | 
			
		||||
        "//third_party:autovalue",
 | 
			
		||||
        "@maven//:com_google_guava_guava",
 | 
			
		||||
    ],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,466 @@
 | 
			
		|||
// Copyright 2022 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.
 | 
			
		||||
 | 
			
		||||
package com.google.mediapipe.tasks.vision.gesturerecognizer;
 | 
			
		||||
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.os.ParcelFileDescriptor;
 | 
			
		||||
import com.google.auto.value.AutoValue;
 | 
			
		||||
import com.google.mediapipe.formats.proto.LandmarkProto.LandmarkList;
 | 
			
		||||
import com.google.mediapipe.formats.proto.LandmarkProto.NormalizedLandmarkList;
 | 
			
		||||
import com.google.mediapipe.proto.CalculatorOptionsProto.CalculatorOptions;
 | 
			
		||||
import com.google.mediapipe.formats.proto.ClassificationProto.ClassificationList;
 | 
			
		||||
import com.google.mediapipe.framework.AndroidPacketGetter;
 | 
			
		||||
import com.google.mediapipe.framework.Packet;
 | 
			
		||||
import com.google.mediapipe.framework.PacketGetter;
 | 
			
		||||
import com.google.mediapipe.framework.image.BitmapImageBuilder;
 | 
			
		||||
import com.google.mediapipe.framework.image.Image;
 | 
			
		||||
import com.google.mediapipe.tasks.components.processors.proto.ClassifierOptionsProto;
 | 
			
		||||
import com.google.mediapipe.tasks.core.BaseOptions;
 | 
			
		||||
import com.google.mediapipe.tasks.core.ErrorListener;
 | 
			
		||||
import com.google.mediapipe.tasks.core.OutputHandler;
 | 
			
		||||
import com.google.mediapipe.tasks.core.OutputHandler.ResultListener;
 | 
			
		||||
import com.google.mediapipe.tasks.core.TaskInfo;
 | 
			
		||||
import com.google.mediapipe.tasks.core.TaskOptions;
 | 
			
		||||
import com.google.mediapipe.tasks.core.TaskRunner;
 | 
			
		||||
import com.google.mediapipe.tasks.core.proto.BaseOptionsProto;
 | 
			
		||||
import com.google.mediapipe.tasks.vision.core.BaseVisionTaskApi;
 | 
			
		||||
import com.google.mediapipe.tasks.vision.core.RunningMode;
 | 
			
		||||
import com.google.mediapipe.tasks.vision.handdetector.HandDetectorGraphOptionsProto;
 | 
			
		||||
import com.google.mediapipe.tasks.vision.handlandmarker.HandLandmarkerGraphOptionsProto;
 | 
			
		||||
import com.google.mediapipe.tasks.vision.handlandmarker.HandLandmarksDetectorGraphOptionsProto;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.nio.ByteBuffer;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Performs gesture recognition on images.
 | 
			
		||||
 *
 | 
			
		||||
 * <p>This API expects a pre-trained hand gesture model asset bundle, or a custom one created using
 | 
			
		||||
 * Model Maker. See <TODO link to the DevSite documentation page>.
 | 
			
		||||
 *
 | 
			
		||||
 * <ul>
 | 
			
		||||
 *   <li>Input image {@link Image}
 | 
			
		||||
 *       <ul>
 | 
			
		||||
 *         <li>The image that gesture recognition runs on.
 | 
			
		||||
 *       </ul>
 | 
			
		||||
 *   <li>Output GestureRecognitionResult {@link GestureRecognitionResult}
 | 
			
		||||
 *       <ul>
 | 
			
		||||
 *         <li>A GestureRecognitionResult containing hand landmarks and recognized hand gestures.
 | 
			
		||||
 *       </ul>
 | 
			
		||||
 * </ul>
 | 
			
		||||
 */
 | 
			
		||||
public final class GestureRecognizer extends BaseVisionTaskApi {
 | 
			
		||||
  private static final String TAG = GestureRecognizer.class.getSimpleName();
 | 
			
		||||
  private static final String IMAGE_IN_STREAM_NAME = "image_in";
 | 
			
		||||
  private static final List<String> INPUT_STREAMS =
 | 
			
		||||
      Collections.unmodifiableList(Arrays.asList("IMAGE:" + IMAGE_IN_STREAM_NAME));
 | 
			
		||||
  private static final List<String> OUTPUT_STREAMS =
 | 
			
		||||
      Collections.unmodifiableList(
 | 
			
		||||
          Arrays.asList(
 | 
			
		||||
              "LANDMARKS:hand_landmarks",
 | 
			
		||||
              "WORLD_LANDMARKS:world_hand_landmarks",
 | 
			
		||||
              "HANDEDNESS:handedness",
 | 
			
		||||
              "HAND_GESTURES:hand_gestures",
 | 
			
		||||
              "IMAGE:image_out"));
 | 
			
		||||
  private static final int LANDMARKS_OUT_STREAM_INDEX = 0;
 | 
			
		||||
  private static final int WORLD_LANDMARKS_OUT_STREAM_INDEX = 1;
 | 
			
		||||
  private static final int HANDEDNESS_OUT_STREAM_INDEX = 2;
 | 
			
		||||
  private static final int HAND_GESTURES_OUT_STREAM_INDEX = 3;
 | 
			
		||||
  private static final int IMAGE_OUT_STREAM_INDEX = 4;
 | 
			
		||||
  private static final String TASK_GRAPH_NAME =
 | 
			
		||||
      "mediapipe.tasks.vision.gesture_recognizer.GestureRecognizerGraph";
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a {@link GestureRecognizer} instance from a model file and the default {@link
 | 
			
		||||
   * GestureRecognizerOptions}.
 | 
			
		||||
   *
 | 
			
		||||
   * @param context an Android {@link Context}.
 | 
			
		||||
   * @param modelPath path to the gesture recognition model with metadata in the assets.
 | 
			
		||||
   * @throws MediaPipeException if there is an error during {@link GestureRecognizer} creation.
 | 
			
		||||
   */
 | 
			
		||||
  public static GestureRecognizer createFromFile(Context context, String modelPath) {
 | 
			
		||||
    BaseOptions baseOptions = BaseOptions.builder().setModelAssetPath(modelPath).build();
 | 
			
		||||
    return createFromOptions(
 | 
			
		||||
        context, GestureRecognizerOptions.builder().setBaseOptions(baseOptions).build());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a {@link GestureRecognizer} instance from a model file and the default {@link
 | 
			
		||||
   * GestureRecognizerOptions}.
 | 
			
		||||
   *
 | 
			
		||||
   * @param context an Android {@link Context}.
 | 
			
		||||
   * @param modelFile the gesture recognition model {@link File} instance.
 | 
			
		||||
   * @throws IOException if an I/O error occurs when opening the tflite model file.
 | 
			
		||||
   * @throws MediaPipeException if there is an error during {@link GestureRecognizer} creation.
 | 
			
		||||
   */
 | 
			
		||||
  public static GestureRecognizer createFromFile(Context context, File modelFile)
 | 
			
		||||
      throws IOException {
 | 
			
		||||
    try (ParcelFileDescriptor descriptor =
 | 
			
		||||
        ParcelFileDescriptor.open(modelFile, ParcelFileDescriptor.MODE_READ_ONLY)) {
 | 
			
		||||
      BaseOptions baseOptions =
 | 
			
		||||
          BaseOptions.builder().setModelAssetFileDescriptor(descriptor.getFd()).build();
 | 
			
		||||
      return createFromOptions(
 | 
			
		||||
          context, GestureRecognizerOptions.builder().setBaseOptions(baseOptions).build());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a {@link GestureRecognizer} instance from a model buffer and the default {@link
 | 
			
		||||
   * GestureRecognizerOptions}.
 | 
			
		||||
   *
 | 
			
		||||
   * @param context an Android {@link Context}.
 | 
			
		||||
   * @param modelBuffer a direct {@link ByteBuffer} or a {@link MappedByteBuffer} of the detection
 | 
			
		||||
   *     model.
 | 
			
		||||
   * @throws MediaPipeException if there is an error during {@link GestureRecognizer} creation.
 | 
			
		||||
   */
 | 
			
		||||
  public static GestureRecognizer createFromBuffer(Context context, final ByteBuffer modelBuffer) {
 | 
			
		||||
    BaseOptions baseOptions = BaseOptions.builder().setModelAssetBuffer(modelBuffer).build();
 | 
			
		||||
    return createFromOptions(
 | 
			
		||||
        context, GestureRecognizerOptions.builder().setBaseOptions(baseOptions).build());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a {@link GestureRecognizer} instance from a {@link GestureRecognizerOptions}.
 | 
			
		||||
   *
 | 
			
		||||
   * @param context an Android {@link Context}.
 | 
			
		||||
   * @param recognizerOptions a {@link GestureRecognizerOptions} instance.
 | 
			
		||||
   * @throws MediaPipeException if there is an error during {@link GestureRecognizer} creation.
 | 
			
		||||
   */
 | 
			
		||||
  public static GestureRecognizer createFromOptions(
 | 
			
		||||
      Context context, GestureRecognizerOptions recognizerOptions) {
 | 
			
		||||
    // TODO: Consolidate OutputHandler and TaskRunner.
 | 
			
		||||
    OutputHandler<GestureRecognitionResult, Image> handler = new OutputHandler<>();
 | 
			
		||||
    handler.setOutputPacketConverter(
 | 
			
		||||
        new OutputHandler.OutputPacketConverter<GestureRecognitionResult, Image>() {
 | 
			
		||||
          @Override
 | 
			
		||||
          public GestureRecognitionResult convertToTaskResult(List<Packet> packets) {
 | 
			
		||||
            // If there is no hands detected in the image, just returns empty lists.
 | 
			
		||||
            if (packets.get(HAND_GESTURES_OUT_STREAM_INDEX).isEmpty()) {
 | 
			
		||||
              return GestureRecognitionResult.create(
 | 
			
		||||
                  new ArrayList<>(),
 | 
			
		||||
                  new ArrayList<>(),
 | 
			
		||||
                  new ArrayList<>(),
 | 
			
		||||
                  new ArrayList<>(),
 | 
			
		||||
                  packets.get(HAND_GESTURES_OUT_STREAM_INDEX).getTimestamp());
 | 
			
		||||
            }
 | 
			
		||||
            return GestureRecognitionResult.create(
 | 
			
		||||
                PacketGetter.getProtoVector(
 | 
			
		||||
                    packets.get(LANDMARKS_OUT_STREAM_INDEX), NormalizedLandmarkList.parser()),
 | 
			
		||||
                PacketGetter.getProtoVector(
 | 
			
		||||
                    packets.get(WORLD_LANDMARKS_OUT_STREAM_INDEX), LandmarkList.parser()),
 | 
			
		||||
                PacketGetter.getProtoVector(
 | 
			
		||||
                    packets.get(HANDEDNESS_OUT_STREAM_INDEX), ClassificationList.parser()),
 | 
			
		||||
                PacketGetter.getProtoVector(
 | 
			
		||||
                    packets.get(HAND_GESTURES_OUT_STREAM_INDEX), ClassificationList.parser()),
 | 
			
		||||
                packets.get(HAND_GESTURES_OUT_STREAM_INDEX).getTimestamp());
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          @Override
 | 
			
		||||
          public Image convertToTaskInput(List<Packet> packets) {
 | 
			
		||||
            return new BitmapImageBuilder(
 | 
			
		||||
                    AndroidPacketGetter.getBitmapFromRgb(packets.get(IMAGE_OUT_STREAM_INDEX)))
 | 
			
		||||
                .build();
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
    recognizerOptions.resultListener().ifPresent(handler::setResultListener);
 | 
			
		||||
    recognizerOptions.errorListener().ifPresent(handler::setErrorListener);
 | 
			
		||||
    TaskRunner runner =
 | 
			
		||||
        TaskRunner.create(
 | 
			
		||||
            context,
 | 
			
		||||
            TaskInfo.<GestureRecognizerOptions>builder()
 | 
			
		||||
                .setTaskGraphName(TASK_GRAPH_NAME)
 | 
			
		||||
                .setInputStreams(INPUT_STREAMS)
 | 
			
		||||
                .setOutputStreams(OUTPUT_STREAMS)
 | 
			
		||||
                .setTaskOptions(recognizerOptions)
 | 
			
		||||
                .setEnableFlowLimiting(recognizerOptions.runningMode() == RunningMode.LIVE_STREAM)
 | 
			
		||||
                .build(),
 | 
			
		||||
            handler);
 | 
			
		||||
    return new GestureRecognizer(runner, recognizerOptions.runningMode());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Constructor to initialize an {@link GestureRecognizer} from a {@link TaskRunner} and a {@link
 | 
			
		||||
   * RunningMode}.
 | 
			
		||||
   *
 | 
			
		||||
   * @param taskRunner a {@link TaskRunner}.
 | 
			
		||||
   * @param runningMode a mediapipe vision task {@link RunningMode}.
 | 
			
		||||
   */
 | 
			
		||||
  private GestureRecognizer(TaskRunner taskRunner, RunningMode runningMode) {
 | 
			
		||||
    super(taskRunner, runningMode, IMAGE_IN_STREAM_NAME);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Performs gesture recognition on the provided single image. Only use this method when the {@link
 | 
			
		||||
   * GestureRecognizer} is created with {@link RunningMode.IMAGE}. TODO update java doc
 | 
			
		||||
   * for input image format.
 | 
			
		||||
   *
 | 
			
		||||
   * <p>{@link GestureRecognizer} supports the following color space types:
 | 
			
		||||
   *
 | 
			
		||||
   * <ul>
 | 
			
		||||
   *   <li>{@link Bitmap.Config.ARGB_8888}
 | 
			
		||||
   * </ul>
 | 
			
		||||
   *
 | 
			
		||||
   * @param inputImage a MediaPipe {@link Image} object for processing.
 | 
			
		||||
   * @throws MediaPipeException if there is an internal error.
 | 
			
		||||
   */
 | 
			
		||||
  public GestureRecognitionResult recognize(Image inputImage) {
 | 
			
		||||
    return (GestureRecognitionResult) processImageData(inputImage);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Performs gesture recognition on the provided video frame. Only use this method when the {@link
 | 
			
		||||
   * GestureRecognizer} is created with {@link RunningMode.VIDEO}.
 | 
			
		||||
   *
 | 
			
		||||
   * <p>It's required to provide the video frame's timestamp (in milliseconds). The input timestamps
 | 
			
		||||
   * must be monotonically increasing.
 | 
			
		||||
   *
 | 
			
		||||
   * <p>{@link GestureRecognizer} supports the following color space types:
 | 
			
		||||
   *
 | 
			
		||||
   * <ul>
 | 
			
		||||
   *   <li>{@link Bitmap.Config.ARGB_8888}
 | 
			
		||||
   * </ul>
 | 
			
		||||
   *
 | 
			
		||||
   * @param inputImage a MediaPipe {@link Image} object for processing.
 | 
			
		||||
   * @param inputTimestampMs the input timestamp (in milliseconds).
 | 
			
		||||
   * @throws MediaPipeException if there is an internal error.
 | 
			
		||||
   */
 | 
			
		||||
  public GestureRecognitionResult recognizeForVideo(Image inputImage, long inputTimestampMs) {
 | 
			
		||||
    return (GestureRecognitionResult) processVideoData(inputImage, inputTimestampMs);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sends live image data to perform gesture recognition, and the results will be available via the
 | 
			
		||||
   * {@link ResultListener} provided in the {@link GestureRecognizerOptions}. Only use this method
 | 
			
		||||
   * when the {@link GestureRecognition} is created with {@link RunningMode.LIVE_STREAM}.
 | 
			
		||||
   *
 | 
			
		||||
   * <p>It's required to provide a timestamp (in milliseconds) to indicate when the input image is
 | 
			
		||||
   * sent to the gesture recognizer. The input timestamps must be monotonically increasing.
 | 
			
		||||
   *
 | 
			
		||||
   * <p>{@link GestureRecognizer} supports the following color space types:
 | 
			
		||||
   *
 | 
			
		||||
   * <ul>
 | 
			
		||||
   *   <li>{@link Bitmap.Config.ARGB_8888}
 | 
			
		||||
   * </ul>
 | 
			
		||||
   *
 | 
			
		||||
   * @param inputImage a MediaPipe {@link Image} object for processing.
 | 
			
		||||
   * @param inputTimestampMs the input timestamp (in milliseconds).
 | 
			
		||||
   * @throws MediaPipeException if there is an internal error.
 | 
			
		||||
   */
 | 
			
		||||
  public void recognizeAsync(Image inputImage, long inputTimestampMs) {
 | 
			
		||||
    sendLiveStreamData(inputImage, inputTimestampMs);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Options for setting up an {@link GestureRecognizer}. */
 | 
			
		||||
  @AutoValue
 | 
			
		||||
  public abstract static class GestureRecognizerOptions extends TaskOptions {
 | 
			
		||||
 | 
			
		||||
    /** Builder for {@link GestureRecognizerOptions}. */
 | 
			
		||||
    @AutoValue.Builder
 | 
			
		||||
    public abstract static class Builder {
 | 
			
		||||
      /** Sets the base options for the gesture recognizer task. */
 | 
			
		||||
      public abstract Builder setBaseOptions(BaseOptions value);
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
       * Sets the running mode for the gesture recognizer task. Default to the image mode. Gesture
 | 
			
		||||
       * recognizer has three modes:
 | 
			
		||||
       *
 | 
			
		||||
       * <ul>
 | 
			
		||||
       *   <li>IMAGE: The mode for recognizing gestures on single image inputs.
 | 
			
		||||
       *   <li>VIDEO: The mode for recognizing gestures on the decoded frames of a video.
 | 
			
		||||
       *   <li>LIVE_STREAM: The mode for for recognizing gestures on a live stream of input data,
 | 
			
		||||
       *       such as from camera. In this mode, {@code setResultListener} must be called to set up
 | 
			
		||||
       *       a listener to receive the recognition results asynchronously.
 | 
			
		||||
       * </ul>
 | 
			
		||||
       */
 | 
			
		||||
      public abstract Builder setRunningMode(RunningMode value);
 | 
			
		||||
 | 
			
		||||
      // TODO: remove these. Temporary solutions before bundle asset is ready.
 | 
			
		||||
      public abstract Builder setBaseOptionsHandDetector(BaseOptions value);
 | 
			
		||||
 | 
			
		||||
      public abstract Builder setBaseOptionsHandLandmarker(BaseOptions value);
 | 
			
		||||
 | 
			
		||||
      public abstract Builder setBaseOptionsGestureRecognizer(BaseOptions value);
 | 
			
		||||
 | 
			
		||||
      /** Sets the maximum number of hands can be detected by the GestureRecognizer. */
 | 
			
		||||
      public abstract Builder setNumHands(Integer value);
 | 
			
		||||
 | 
			
		||||
      /** Sets minimum confidence score for the hand detection to be considered successfully */
 | 
			
		||||
      public abstract Builder setMinHandDetectionConfidence(Float value);
 | 
			
		||||
 | 
			
		||||
      /** Sets minimum confidence score of hand presence score in the hand landmark detection. */
 | 
			
		||||
      public abstract Builder setMinHandPresenceConfidence(Float value);
 | 
			
		||||
 | 
			
		||||
      /** Sets the minimum confidence score for the hand tracking to be considered successfully. */
 | 
			
		||||
      public abstract Builder setMinTrackingConfidence(Float value);
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
       * Sets the minimum confidence score for the gestures to be considered successfully. If < 0,
 | 
			
		||||
       * the gesture confidence threshold=0.5 for the model is used.
 | 
			
		||||
       *
 | 
			
		||||
       * <p>TODO  Note this option is subject to change, after scoring merging
 | 
			
		||||
       * calculator is implemented.
 | 
			
		||||
       */
 | 
			
		||||
      public abstract Builder setMinGestureConfidence(Float value);
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
       * Sets the result listener to receive the detection results asynchronously when the gesture
 | 
			
		||||
       * recognizer is in the live stream mode.
 | 
			
		||||
       */
 | 
			
		||||
      public abstract Builder setResultListener(
 | 
			
		||||
          ResultListener<GestureRecognitionResult, Image> value);
 | 
			
		||||
 | 
			
		||||
      /** Sets an optional error listener. */
 | 
			
		||||
      public abstract Builder setErrorListener(ErrorListener value);
 | 
			
		||||
 | 
			
		||||
      abstract GestureRecognizerOptions autoBuild();
 | 
			
		||||
 | 
			
		||||
      /**
 | 
			
		||||
       * Validates and builds the {@link GestureRecognizerOptions} instance.
 | 
			
		||||
       *
 | 
			
		||||
       * @throws IllegalArgumentException if the result listener and the running mode are not
 | 
			
		||||
       *     properly configured. The result listener should only be set when the object detector is
 | 
			
		||||
       *     in the live stream mode.
 | 
			
		||||
       */
 | 
			
		||||
      public final GestureRecognizerOptions build() {
 | 
			
		||||
        GestureRecognizerOptions options = autoBuild();
 | 
			
		||||
        if (options.runningMode() == RunningMode.LIVE_STREAM) {
 | 
			
		||||
          if (!options.resultListener().isPresent()) {
 | 
			
		||||
            throw new IllegalArgumentException(
 | 
			
		||||
                "The gesture recognizer is in the live stream mode, a user-defined result listener"
 | 
			
		||||
                    + " must be provided in GestureRecognizerOptions.");
 | 
			
		||||
          }
 | 
			
		||||
        } else if (options.resultListener().isPresent()) {
 | 
			
		||||
          throw new IllegalArgumentException(
 | 
			
		||||
              "The gesture recognizer is in the image or the video mode, a user-defined result"
 | 
			
		||||
                  + " listener shouldn't be provided in GestureRecognizerOptions.");
 | 
			
		||||
        }
 | 
			
		||||
        return options;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    abstract BaseOptions baseOptions();
 | 
			
		||||
 | 
			
		||||
    // TODO: remove these. Temporary solutions before bundle asset is ready.
 | 
			
		||||
    abstract BaseOptions baseOptionsHandDetector();
 | 
			
		||||
 | 
			
		||||
    abstract BaseOptions baseOptionsHandLandmarker();
 | 
			
		||||
 | 
			
		||||
    abstract BaseOptions baseOptionsGestureRecognizer();
 | 
			
		||||
 | 
			
		||||
    abstract RunningMode runningMode();
 | 
			
		||||
 | 
			
		||||
    abstract Optional<Integer> numHands();
 | 
			
		||||
 | 
			
		||||
    abstract Optional<Float> minHandDetectionConfidence();
 | 
			
		||||
 | 
			
		||||
    abstract Optional<Float> minHandPresenceConfidence();
 | 
			
		||||
 | 
			
		||||
    abstract Optional<Float> minTrackingConfidence();
 | 
			
		||||
 | 
			
		||||
    // TODO update gesture confidence options after score merging calculator is ready.
 | 
			
		||||
    abstract Optional<Float> minGestureConfidence();
 | 
			
		||||
 | 
			
		||||
    abstract Optional<ResultListener<GestureRecognitionResult, Image>> resultListener();
 | 
			
		||||
 | 
			
		||||
    abstract Optional<ErrorListener> errorListener();
 | 
			
		||||
 | 
			
		||||
    public static Builder builder() {
 | 
			
		||||
      return new AutoValue_GestureRecognizer_GestureRecognizerOptions.Builder()
 | 
			
		||||
          .setRunningMode(RunningMode.IMAGE)
 | 
			
		||||
          .setNumHands(1)
 | 
			
		||||
          .setMinHandDetectionConfidence(0.5f)
 | 
			
		||||
          .setMinHandPresenceConfidence(0.5f)
 | 
			
		||||
          .setMinTrackingConfidence(0.5f)
 | 
			
		||||
          .setMinGestureConfidence(-1f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts a {@link GestureRecognizerOptions} to a {@link CalculatorOptions} protobuf message.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public CalculatorOptions convertToCalculatorOptionsProto() {
 | 
			
		||||
      BaseOptionsProto.BaseOptions.Builder baseOptionsBuilder =
 | 
			
		||||
          BaseOptionsProto.BaseOptions.newBuilder()
 | 
			
		||||
              .setUseStreamMode(runningMode() != RunningMode.IMAGE)
 | 
			
		||||
              .mergeFrom(convertBaseOptionsToProto(baseOptions()));
 | 
			
		||||
      GestureRecognizerGraphOptionsProto.GestureRecognizerGraphOptions.Builder taskOptionsBuilder =
 | 
			
		||||
          GestureRecognizerGraphOptionsProto.GestureRecognizerGraphOptions.newBuilder()
 | 
			
		||||
              .setBaseOptions(baseOptionsBuilder);
 | 
			
		||||
 | 
			
		||||
      // Setup HandDetectorGraphOptions.
 | 
			
		||||
      HandDetectorGraphOptionsProto.HandDetectorGraphOptions.Builder
 | 
			
		||||
          handDetectorGraphOptionsBuilder =
 | 
			
		||||
              HandDetectorGraphOptionsProto.HandDetectorGraphOptions.newBuilder()
 | 
			
		||||
                  .setBaseOptions(
 | 
			
		||||
                      BaseOptionsProto.BaseOptions.newBuilder()
 | 
			
		||||
                          .setUseStreamMode(runningMode() != RunningMode.IMAGE)
 | 
			
		||||
                          .mergeFrom(convertBaseOptionsToProto(baseOptionsHandDetector())));
 | 
			
		||||
      numHands().ifPresent(handDetectorGraphOptionsBuilder::setNumHands);
 | 
			
		||||
      minHandDetectionConfidence()
 | 
			
		||||
          .ifPresent(handDetectorGraphOptionsBuilder::setMinDetectionConfidence);
 | 
			
		||||
 | 
			
		||||
      // Setup HandLandmarkerGraphOptions.
 | 
			
		||||
      HandLandmarksDetectorGraphOptionsProto.HandLandmarksDetectorGraphOptions.Builder
 | 
			
		||||
          handLandmarksDetectorGraphOptionsBuilder =
 | 
			
		||||
              HandLandmarksDetectorGraphOptionsProto.HandLandmarksDetectorGraphOptions.newBuilder()
 | 
			
		||||
                  .setBaseOptions(
 | 
			
		||||
                      BaseOptionsProto.BaseOptions.newBuilder()
 | 
			
		||||
                          .setUseStreamMode(runningMode() != RunningMode.IMAGE)
 | 
			
		||||
                          .mergeFrom(convertBaseOptionsToProto(baseOptionsHandLandmarker())));
 | 
			
		||||
      minHandPresenceConfidence()
 | 
			
		||||
          .ifPresent(handLandmarksDetectorGraphOptionsBuilder::setMinDetectionConfidence);
 | 
			
		||||
      HandLandmarkerGraphOptionsProto.HandLandmarkerGraphOptions.Builder
 | 
			
		||||
          handLandmarkerGraphOptionsBuilder =
 | 
			
		||||
              HandLandmarkerGraphOptionsProto.HandLandmarkerGraphOptions.newBuilder()
 | 
			
		||||
                  .setBaseOptions(
 | 
			
		||||
                      BaseOptionsProto.BaseOptions.newBuilder()
 | 
			
		||||
                          .setUseStreamMode(runningMode() != RunningMode.IMAGE)
 | 
			
		||||
                          .mergeFrom(convertBaseOptionsToProto(baseOptionsHandLandmarker())));
 | 
			
		||||
      minTrackingConfidence()
 | 
			
		||||
          .ifPresent(handLandmarkerGraphOptionsBuilder::setMinTrackingConfidence);
 | 
			
		||||
      handLandmarkerGraphOptionsBuilder
 | 
			
		||||
          .setHandDetectorGraphOptions(handDetectorGraphOptionsBuilder.build())
 | 
			
		||||
          .setHandLandmarksDetectorGraphOptions(handLandmarksDetectorGraphOptionsBuilder.build());
 | 
			
		||||
 | 
			
		||||
      // Setup HandGestureRecognizerGraphOptions.
 | 
			
		||||
      HandGestureRecognizerGraphOptionsProto.HandGestureRecognizerGraphOptions.Builder
 | 
			
		||||
          handGestureRecognizerGraphOptionsBuilder =
 | 
			
		||||
              HandGestureRecognizerGraphOptionsProto.HandGestureRecognizerGraphOptions.newBuilder()
 | 
			
		||||
                  .setBaseOptions(
 | 
			
		||||
                      BaseOptionsProto.BaseOptions.newBuilder()
 | 
			
		||||
                          .setUseStreamMode(runningMode() != RunningMode.IMAGE)
 | 
			
		||||
                          .mergeFrom(convertBaseOptionsToProto(baseOptionsGestureRecognizer())));
 | 
			
		||||
      ClassifierOptionsProto.ClassifierOptions.Builder classifierOptionsBuilder =
 | 
			
		||||
          ClassifierOptionsProto.ClassifierOptions.newBuilder();
 | 
			
		||||
      minGestureConfidence().ifPresent(classifierOptionsBuilder::setScoreThreshold);
 | 
			
		||||
      handGestureRecognizerGraphOptionsBuilder.setClassifierOptions(
 | 
			
		||||
          classifierOptionsBuilder.build());
 | 
			
		||||
 | 
			
		||||
      taskOptionsBuilder
 | 
			
		||||
          .setHandLandmarkerGraphOptions(handLandmarkerGraphOptionsBuilder.build())
 | 
			
		||||
          .setHandGestureRecognizerGraphOptions(handGestureRecognizerGraphOptionsBuilder.build());
 | 
			
		||||
      return CalculatorOptions.newBuilder()
 | 
			
		||||
          .setExtension(
 | 
			
		||||
              GestureRecognizerGraphOptionsProto.GestureRecognizerGraphOptions.ext,
 | 
			
		||||
              taskOptionsBuilder.build())
 | 
			
		||||
          .build();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="com.google.mediapipe.tasks.vision.gesturerecognizertest"
 | 
			
		||||
    android:versionCode="1"
 | 
			
		||||
    android:versionName="1.0" >
 | 
			
		||||
 | 
			
		||||
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 | 
			
		||||
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 | 
			
		||||
 | 
			
		||||
    <uses-sdk android:minSdkVersion="24"
 | 
			
		||||
        android:targetSdkVersion="30" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        android:label="gesturerecognizertest"
 | 
			
		||||
        android:name="android.support.multidex.MultiDexApplication"
 | 
			
		||||
        android:taskAffinity="">
 | 
			
		||||
        <uses-library android:name="android.test.runner" />
 | 
			
		||||
    </application>
 | 
			
		||||
 | 
			
		||||
    <instrumentation
 | 
			
		||||
        android:name="com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner"
 | 
			
		||||
        android:targetPackage="com.google.mediapipe.tasks.vision.gesturerecognizertest" />
 | 
			
		||||
 | 
			
		||||
</manifest>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
# Copyright 2022 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.
 | 
			
		||||
 | 
			
		||||
package(default_visibility = ["//mediapipe/tasks:internal"])
 | 
			
		||||
 | 
			
		||||
licenses(["notice"])
 | 
			
		||||
 | 
			
		||||
# TODO: Enable this in OSS
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,495 @@
 | 
			
		|||
// Copyright 2022 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.
 | 
			
		||||
 | 
			
		||||
package com.google.mediapipe.tasks.vision.gesturerecognizer;
 | 
			
		||||
 | 
			
		||||
import static com.google.common.truth.Truth.assertThat;
 | 
			
		||||
import static org.junit.Assert.assertThrows;
 | 
			
		||||
 | 
			
		||||
import android.content.res.AssetManager;
 | 
			
		||||
import android.graphics.BitmapFactory;
 | 
			
		||||
import androidx.test.core.app.ApplicationProvider;
 | 
			
		||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
 | 
			
		||||
import com.google.common.truth.Correspondence;
 | 
			
		||||
import com.google.mediapipe.formats.proto.ClassificationProto;
 | 
			
		||||
import com.google.mediapipe.framework.MediaPipeException;
 | 
			
		||||
import com.google.mediapipe.framework.image.BitmapImageBuilder;
 | 
			
		||||
import com.google.mediapipe.framework.image.Image;
 | 
			
		||||
import com.google.mediapipe.tasks.components.containers.Category;
 | 
			
		||||
import com.google.mediapipe.tasks.components.containers.Landmark;
 | 
			
		||||
import com.google.mediapipe.tasks.components.containers.proto.LandmarksDetectionResultProto.LandmarksDetectionResult;
 | 
			
		||||
import com.google.mediapipe.tasks.core.BaseOptions;
 | 
			
		||||
import com.google.mediapipe.tasks.vision.core.RunningMode;
 | 
			
		||||
import com.google.mediapipe.tasks.vision.gesturerecognizer.GestureRecognizer.GestureRecognizerOptions;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.junit.runner.RunWith;
 | 
			
		||||
import org.junit.runners.Suite;
 | 
			
		||||
import org.junit.runners.Suite.SuiteClasses;
 | 
			
		||||
 | 
			
		||||
/** Test for {@link GestureRecognizer}. */
 | 
			
		||||
@RunWith(Suite.class)
 | 
			
		||||
@SuiteClasses({GestureRecognizerTest.General.class, GestureRecognizerTest.RunningModeTest.class})
 | 
			
		||||
public class GestureRecognizerTest {
 | 
			
		||||
  private static final String HAND_DETECTOR_MODEL_FILE = "palm_detection_full.tflite";
 | 
			
		||||
  private static final String HAND_LANDMARKER_MODEL_FILE = "hand_landmark_full.tflite";
 | 
			
		||||
  private static final String GESTURE_RECOGNIZER_MODEL_FILE =
 | 
			
		||||
      "cg_classifier_screen3d_landmark_features_nn_2022_08_04_base_simple_model.tflite";
 | 
			
		||||
  private static final String TWO_HANDS_IMAGE = "right_hands.jpg";
 | 
			
		||||
  private static final String THUMB_UP_IMAGE = "thumb_up.jpg";
 | 
			
		||||
  private static final String NO_HANDS_IMAGE = "cats_and_dogs.jpg";
 | 
			
		||||
  private static final String THUMB_UP_LANDMARKS = "thumb_up_landmarks.pb";
 | 
			
		||||
  private static final String TAG = "Gesture Recognizer Test";
 | 
			
		||||
  private static final String THUMB_UP_LABEL = "Thumb_Up";
 | 
			
		||||
  private static final int THUMB_UP_INDEX = 5;
 | 
			
		||||
  private static final float LANDMARKS_ERROR_TOLERANCE = 0.03f;
 | 
			
		||||
  private static final int IMAGE_WIDTH = 382;
 | 
			
		||||
  private static final int IMAGE_HEIGHT = 406;
 | 
			
		||||
 | 
			
		||||
  @RunWith(AndroidJUnit4.class)
 | 
			
		||||
  public static final class General extends GestureRecognizerTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void recognize_successWithValidModels() throws Exception {
 | 
			
		||||
      GestureRecognizerOptions options =
 | 
			
		||||
          GestureRecognizerOptions.builder()
 | 
			
		||||
              .setBaseOptions(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsHandDetector(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsHandLandmarker(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
              .build();
 | 
			
		||||
      GestureRecognizer gestureRecognizer =
 | 
			
		||||
          GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options);
 | 
			
		||||
      GestureRecognitionResult actualResult =
 | 
			
		||||
          gestureRecognizer.recognize(getImageFromAsset(THUMB_UP_IMAGE));
 | 
			
		||||
      GestureRecognitionResult expectedResult =
 | 
			
		||||
          getExpectedGestureRecognitionResult(THUMB_UP_LANDMARKS, THUMB_UP_LABEL, THUMB_UP_INDEX);
 | 
			
		||||
      assertActualResultApproximatelyEqualsToExpectedResult(actualResult, expectedResult);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void recognize_successWithEmptyResult() throws Exception {
 | 
			
		||||
      GestureRecognizerOptions options =
 | 
			
		||||
          GestureRecognizerOptions.builder()
 | 
			
		||||
              .setBaseOptions(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsHandDetector(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsHandLandmarker(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
              .build();
 | 
			
		||||
      GestureRecognizer gestureRecognizer =
 | 
			
		||||
          GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options);
 | 
			
		||||
      GestureRecognitionResult actualResult =
 | 
			
		||||
          gestureRecognizer.recognize(getImageFromAsset(NO_HANDS_IMAGE));
 | 
			
		||||
      assertThat(actualResult.landmarks()).isEmpty();
 | 
			
		||||
      assertThat(actualResult.worldLandmarks()).isEmpty();
 | 
			
		||||
      assertThat(actualResult.handednesses()).isEmpty();
 | 
			
		||||
      assertThat(actualResult.gestures()).isEmpty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void recognize_successWithMinGestureConfidence() throws Exception {
 | 
			
		||||
      GestureRecognizerOptions options =
 | 
			
		||||
          GestureRecognizerOptions.builder()
 | 
			
		||||
              .setBaseOptions(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsHandDetector(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsHandLandmarker(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
              // TODO update the confidence to be in range [0,1] after embedding model
 | 
			
		||||
              // and scoring calculator is integrated.
 | 
			
		||||
              .setMinGestureConfidence(3.0f)
 | 
			
		||||
              .build();
 | 
			
		||||
      GestureRecognizer gestureRecognizer =
 | 
			
		||||
          GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options);
 | 
			
		||||
      GestureRecognitionResult actualResult =
 | 
			
		||||
          gestureRecognizer.recognize(getImageFromAsset(THUMB_UP_IMAGE));
 | 
			
		||||
      GestureRecognitionResult expectedResult =
 | 
			
		||||
          getExpectedGestureRecognitionResult(THUMB_UP_LANDMARKS, THUMB_UP_LABEL, THUMB_UP_INDEX);
 | 
			
		||||
      // Only contains one top scoring gesture.
 | 
			
		||||
      assertThat(actualResult.gestures().get(0)).hasSize(1);
 | 
			
		||||
      assertActualGestureEqualExpectedGesture(
 | 
			
		||||
          actualResult.gestures().get(0).get(0), expectedResult.gestures().get(0).get(0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void recognize_successWithNumHands() throws Exception {
 | 
			
		||||
      GestureRecognizerOptions options =
 | 
			
		||||
          GestureRecognizerOptions.builder()
 | 
			
		||||
              .setBaseOptions(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsHandDetector(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsHandLandmarker(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
              .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                  BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
              .setNumHands(2)
 | 
			
		||||
              .build();
 | 
			
		||||
      GestureRecognizer gestureRecognizer =
 | 
			
		||||
          GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options);
 | 
			
		||||
      GestureRecognitionResult actualResult =
 | 
			
		||||
          gestureRecognizer.recognize(getImageFromAsset(TWO_HANDS_IMAGE));
 | 
			
		||||
      assertThat(actualResult.handednesses()).hasSize(2);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @RunWith(AndroidJUnit4.class)
 | 
			
		||||
  public static final class RunningModeTest extends GestureRecognizerTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    public void create_failsWithIllegalResultListenerInNonLiveStreamMode() throws Exception {
 | 
			
		||||
      for (RunningMode mode : new RunningMode[] {RunningMode.IMAGE, RunningMode.VIDEO}) {
 | 
			
		||||
        IllegalArgumentException exception =
 | 
			
		||||
            assertThrows(
 | 
			
		||||
                IllegalArgumentException.class,
 | 
			
		||||
                () ->
 | 
			
		||||
                    GestureRecognizerOptions.builder()
 | 
			
		||||
                        .setBaseOptions(
 | 
			
		||||
                            BaseOptions.builder()
 | 
			
		||||
                                .setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE)
 | 
			
		||||
                                .build())
 | 
			
		||||
                        .setBaseOptionsHandDetector(
 | 
			
		||||
                            BaseOptions.builder()
 | 
			
		||||
                                .setModelAssetPath(HAND_DETECTOR_MODEL_FILE)
 | 
			
		||||
                                .build())
 | 
			
		||||
                        .setBaseOptionsHandLandmarker(
 | 
			
		||||
                            BaseOptions.builder()
 | 
			
		||||
                                .setModelAssetPath(HAND_LANDMARKER_MODEL_FILE)
 | 
			
		||||
                                .build())
 | 
			
		||||
                        .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                            BaseOptions.builder()
 | 
			
		||||
                                .setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE)
 | 
			
		||||
                                .build())
 | 
			
		||||
                        .setRunningMode(mode)
 | 
			
		||||
                        .setResultListener((gestureRecognitionResult, inputImage) -> {})
 | 
			
		||||
                        .build());
 | 
			
		||||
        assertThat(exception)
 | 
			
		||||
            .hasMessageThat()
 | 
			
		||||
            .contains("a user-defined result listener shouldn't be provided");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void create_failsWithMissingResultListenerInLiveSteamMode() throws Exception {
 | 
			
		||||
    IllegalArgumentException exception =
 | 
			
		||||
        assertThrows(
 | 
			
		||||
            IllegalArgumentException.class,
 | 
			
		||||
            () ->
 | 
			
		||||
                GestureRecognizerOptions.builder()
 | 
			
		||||
                    .setBaseOptions(
 | 
			
		||||
                        BaseOptions.builder()
 | 
			
		||||
                            .setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE)
 | 
			
		||||
                            .build())
 | 
			
		||||
                    .setBaseOptionsHandDetector(
 | 
			
		||||
                        BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
                    .setBaseOptionsHandLandmarker(
 | 
			
		||||
                        BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
                    .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                        BaseOptions.builder()
 | 
			
		||||
                            .setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE)
 | 
			
		||||
                            .build())
 | 
			
		||||
                    .setRunningMode(RunningMode.LIVE_STREAM)
 | 
			
		||||
                    .build());
 | 
			
		||||
    assertThat(exception)
 | 
			
		||||
        .hasMessageThat()
 | 
			
		||||
        .contains("a user-defined result listener must be provided");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void recognize_failsWithCallingWrongApiInImageMode() throws Exception {
 | 
			
		||||
    GestureRecognizerOptions options =
 | 
			
		||||
        GestureRecognizerOptions.builder()
 | 
			
		||||
            .setBaseOptions(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandDetector(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandLandmarker(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setRunningMode(RunningMode.IMAGE)
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
    GestureRecognizer gestureRecognizer =
 | 
			
		||||
        GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options);
 | 
			
		||||
    MediaPipeException exception =
 | 
			
		||||
        assertThrows(
 | 
			
		||||
            MediaPipeException.class,
 | 
			
		||||
            () -> gestureRecognizer.recognizeForVideo(getImageFromAsset(THUMB_UP_IMAGE), 0));
 | 
			
		||||
    assertThat(exception).hasMessageThat().contains("not initialized with the video mode");
 | 
			
		||||
    exception =
 | 
			
		||||
        assertThrows(
 | 
			
		||||
            MediaPipeException.class,
 | 
			
		||||
            () -> gestureRecognizer.recognizeAsync(getImageFromAsset(THUMB_UP_IMAGE), 0));
 | 
			
		||||
    assertThat(exception).hasMessageThat().contains("not initialized with the live stream mode");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void recognize_failsWithCallingWrongApiInVideoMode() throws Exception {
 | 
			
		||||
    GestureRecognizerOptions options =
 | 
			
		||||
        GestureRecognizerOptions.builder()
 | 
			
		||||
            .setBaseOptions(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandDetector(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandLandmarker(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setRunningMode(RunningMode.VIDEO)
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
    GestureRecognizer gestureRecognizer =
 | 
			
		||||
        GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options);
 | 
			
		||||
    MediaPipeException exception =
 | 
			
		||||
        assertThrows(
 | 
			
		||||
            MediaPipeException.class,
 | 
			
		||||
            () -> gestureRecognizer.recognize(getImageFromAsset(THUMB_UP_IMAGE)));
 | 
			
		||||
    assertThat(exception).hasMessageThat().contains("not initialized with the image mode");
 | 
			
		||||
    exception =
 | 
			
		||||
        assertThrows(
 | 
			
		||||
            MediaPipeException.class,
 | 
			
		||||
            () -> gestureRecognizer.recognizeAsync(getImageFromAsset(THUMB_UP_IMAGE), 0));
 | 
			
		||||
    assertThat(exception).hasMessageThat().contains("not initialized with the live stream mode");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void recognize_failsWithCallingWrongApiInLiveSteamMode() throws Exception {
 | 
			
		||||
    GestureRecognizerOptions options =
 | 
			
		||||
        GestureRecognizerOptions.builder()
 | 
			
		||||
            .setBaseOptions(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandDetector(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandLandmarker(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setRunningMode(RunningMode.LIVE_STREAM)
 | 
			
		||||
            .setResultListener((gestureRecognitionResult, inputImage) -> {})
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
    GestureRecognizer gestureRecognizer =
 | 
			
		||||
        GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options);
 | 
			
		||||
    MediaPipeException exception =
 | 
			
		||||
        assertThrows(
 | 
			
		||||
            MediaPipeException.class,
 | 
			
		||||
            () -> gestureRecognizer.recognize(getImageFromAsset(THUMB_UP_IMAGE)));
 | 
			
		||||
    assertThat(exception).hasMessageThat().contains("not initialized with the image mode");
 | 
			
		||||
    exception =
 | 
			
		||||
        assertThrows(
 | 
			
		||||
            MediaPipeException.class,
 | 
			
		||||
            () -> gestureRecognizer.recognizeForVideo(getImageFromAsset(THUMB_UP_IMAGE), 0));
 | 
			
		||||
    assertThat(exception).hasMessageThat().contains("not initialized with the video mode");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void recognize_successWithImageMode() throws Exception {
 | 
			
		||||
    GestureRecognizerOptions options =
 | 
			
		||||
        GestureRecognizerOptions.builder()
 | 
			
		||||
            .setBaseOptions(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandDetector(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandLandmarker(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setRunningMode(RunningMode.IMAGE)
 | 
			
		||||
            .build();
 | 
			
		||||
 | 
			
		||||
    GestureRecognizer gestureRecognizer =
 | 
			
		||||
        GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options);
 | 
			
		||||
    GestureRecognitionResult actualResult =
 | 
			
		||||
        gestureRecognizer.recognize(getImageFromAsset(THUMB_UP_IMAGE));
 | 
			
		||||
    GestureRecognitionResult expectedResult =
 | 
			
		||||
        getExpectedGestureRecognitionResult(THUMB_UP_LANDMARKS, THUMB_UP_LABEL, THUMB_UP_INDEX);
 | 
			
		||||
    assertActualResultApproximatelyEqualsToExpectedResult(actualResult, expectedResult);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void recognize_successWithVideoMode() throws Exception {
 | 
			
		||||
    GestureRecognizerOptions options =
 | 
			
		||||
        GestureRecognizerOptions.builder()
 | 
			
		||||
            .setBaseOptions(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandDetector(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandLandmarker(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setRunningMode(RunningMode.VIDEO)
 | 
			
		||||
            .build();
 | 
			
		||||
    GestureRecognizer gestureRecognizer =
 | 
			
		||||
        GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options);
 | 
			
		||||
    GestureRecognitionResult expectedResult =
 | 
			
		||||
        getExpectedGestureRecognitionResult(THUMB_UP_LANDMARKS, THUMB_UP_LABEL, THUMB_UP_INDEX);
 | 
			
		||||
    for (int i = 0; i < 3; i++) {
 | 
			
		||||
      GestureRecognitionResult actualResult =
 | 
			
		||||
          gestureRecognizer.recognizeForVideo(getImageFromAsset(THUMB_UP_IMAGE), i);
 | 
			
		||||
      assertActualResultApproximatelyEqualsToExpectedResult(actualResult, expectedResult);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void recognize_failsWithOutOfOrderInputTimestamps() throws Exception {
 | 
			
		||||
    Image image = getImageFromAsset(THUMB_UP_IMAGE);
 | 
			
		||||
    GestureRecognitionResult expectedResult =
 | 
			
		||||
        getExpectedGestureRecognitionResult(THUMB_UP_LANDMARKS, THUMB_UP_LABEL, THUMB_UP_INDEX);
 | 
			
		||||
    GestureRecognizerOptions options =
 | 
			
		||||
        GestureRecognizerOptions.builder()
 | 
			
		||||
            .setBaseOptions(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandDetector(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandLandmarker(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setRunningMode(RunningMode.LIVE_STREAM)
 | 
			
		||||
            .setResultListener(
 | 
			
		||||
                (actualResult, inputImage) -> {
 | 
			
		||||
                  assertActualResultApproximatelyEqualsToExpectedResult(
 | 
			
		||||
                      actualResult, expectedResult);
 | 
			
		||||
                  assertImageSizeIsExpected(inputImage);
 | 
			
		||||
                })
 | 
			
		||||
            .build();
 | 
			
		||||
    try (GestureRecognizer gestureRecognizer =
 | 
			
		||||
        GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options)) {
 | 
			
		||||
      gestureRecognizer.recognizeAsync(image, 1);
 | 
			
		||||
      MediaPipeException exception =
 | 
			
		||||
          assertThrows(MediaPipeException.class, () -> gestureRecognizer.recognizeAsync(image, 0));
 | 
			
		||||
      assertThat(exception)
 | 
			
		||||
          .hasMessageThat()
 | 
			
		||||
          .contains("having a smaller timestamp than the processed timestamp");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Test
 | 
			
		||||
  public void recognize_successWithLiveSteamMode() throws Exception {
 | 
			
		||||
    Image image = getImageFromAsset(THUMB_UP_IMAGE);
 | 
			
		||||
    GestureRecognitionResult expectedResult =
 | 
			
		||||
        getExpectedGestureRecognitionResult(THUMB_UP_LANDMARKS, THUMB_UP_LABEL, THUMB_UP_INDEX);
 | 
			
		||||
    GestureRecognizerOptions options =
 | 
			
		||||
        GestureRecognizerOptions.builder()
 | 
			
		||||
            .setBaseOptions(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandDetector(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_DETECTOR_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsHandLandmarker(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(HAND_LANDMARKER_MODEL_FILE).build())
 | 
			
		||||
            .setBaseOptionsGestureRecognizer(
 | 
			
		||||
                BaseOptions.builder().setModelAssetPath(GESTURE_RECOGNIZER_MODEL_FILE).build())
 | 
			
		||||
            .setRunningMode(RunningMode.LIVE_STREAM)
 | 
			
		||||
            .setResultListener(
 | 
			
		||||
                (actualResult, inputImage) -> {
 | 
			
		||||
                  assertActualResultApproximatelyEqualsToExpectedResult(
 | 
			
		||||
                      actualResult, expectedResult);
 | 
			
		||||
                  assertImageSizeIsExpected(inputImage);
 | 
			
		||||
                })
 | 
			
		||||
            .build();
 | 
			
		||||
    try (GestureRecognizer gestureRecognizer =
 | 
			
		||||
        GestureRecognizer.createFromOptions(ApplicationProvider.getApplicationContext(), options)) {
 | 
			
		||||
      for (int i = 0; i < 3; i++) {
 | 
			
		||||
        gestureRecognizer.recognizeAsync(image, i);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static Image getImageFromAsset(String filePath) throws Exception {
 | 
			
		||||
    AssetManager assetManager = ApplicationProvider.getApplicationContext().getAssets();
 | 
			
		||||
    InputStream istr = assetManager.open(filePath);
 | 
			
		||||
    return new BitmapImageBuilder(BitmapFactory.decodeStream(istr)).build();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static GestureRecognitionResult getExpectedGestureRecognitionResult(
 | 
			
		||||
      String filePath, String gestureLabel, int gestureIndex) throws Exception {
 | 
			
		||||
    AssetManager assetManager = ApplicationProvider.getApplicationContext().getAssets();
 | 
			
		||||
    InputStream istr = assetManager.open(filePath);
 | 
			
		||||
    LandmarksDetectionResult landmarksDetectionResultProto =
 | 
			
		||||
        LandmarksDetectionResult.parser().parseFrom(istr);
 | 
			
		||||
    ClassificationProto.ClassificationList gesturesProto =
 | 
			
		||||
        ClassificationProto.ClassificationList.newBuilder()
 | 
			
		||||
            .addClassification(
 | 
			
		||||
                ClassificationProto.Classification.newBuilder()
 | 
			
		||||
                    .setLabel(gestureLabel)
 | 
			
		||||
                    .setIndex(gestureIndex))
 | 
			
		||||
            .build();
 | 
			
		||||
    return GestureRecognitionResult.create(
 | 
			
		||||
        Arrays.asList(landmarksDetectionResultProto.getLandmarks()),
 | 
			
		||||
        Arrays.asList(landmarksDetectionResultProto.getWorldLandmarks()),
 | 
			
		||||
        Arrays.asList(landmarksDetectionResultProto.getClassifications()),
 | 
			
		||||
        Arrays.asList(gesturesProto),
 | 
			
		||||
        /*timestampMs=*/ 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static void assertActualResultApproximatelyEqualsToExpectedResult(
 | 
			
		||||
      GestureRecognitionResult actualResult, GestureRecognitionResult expectedResult) {
 | 
			
		||||
    // Expects to have the same number of hands detected.
 | 
			
		||||
    assertThat(actualResult.landmarks()).hasSize(expectedResult.landmarks().size());
 | 
			
		||||
    assertThat(actualResult.worldLandmarks()).hasSize(expectedResult.worldLandmarks().size());
 | 
			
		||||
    assertThat(actualResult.handednesses()).hasSize(expectedResult.handednesses().size());
 | 
			
		||||
    assertThat(actualResult.gestures()).hasSize(expectedResult.gestures().size());
 | 
			
		||||
 | 
			
		||||
    // Actual landmarks match expected landmarks.
 | 
			
		||||
    assertThat(actualResult.landmarks().get(0))
 | 
			
		||||
        .comparingElementsUsing(
 | 
			
		||||
            Correspondence.from(
 | 
			
		||||
                (Correspondence.BinaryPredicate<Landmark, Landmark>)
 | 
			
		||||
                    (actual, expected) -> {
 | 
			
		||||
                      return Correspondence.tolerance(LANDMARKS_ERROR_TOLERANCE)
 | 
			
		||||
                              .compare(actual.x(), expected.x())
 | 
			
		||||
                          && Correspondence.tolerance(LANDMARKS_ERROR_TOLERANCE)
 | 
			
		||||
                              .compare(actual.y(), expected.y());
 | 
			
		||||
                    },
 | 
			
		||||
                "landmarks approximately equal to"))
 | 
			
		||||
        .containsExactlyElementsIn(expectedResult.landmarks().get(0));
 | 
			
		||||
 | 
			
		||||
    // Actual handedness matches expected handedness.
 | 
			
		||||
    Category actualTopHandedness = actualResult.handednesses().get(0).get(0);
 | 
			
		||||
    Category expectedTopHandedness = expectedResult.handednesses().get(0).get(0);
 | 
			
		||||
    assertThat(actualTopHandedness.index()).isEqualTo(expectedTopHandedness.index());
 | 
			
		||||
    assertThat(actualTopHandedness.categoryName()).isEqualTo(expectedTopHandedness.categoryName());
 | 
			
		||||
 | 
			
		||||
    // Actual gesture with top score matches expected gesture.
 | 
			
		||||
    Category actualTopGesture = actualResult.gestures().get(0).get(0);
 | 
			
		||||
    Category expectedTopGesture = expectedResult.gestures().get(0).get(0);
 | 
			
		||||
    assertActualGestureEqualExpectedGesture(actualTopGesture, expectedTopGesture);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static void assertActualGestureEqualExpectedGesture(
 | 
			
		||||
      Category actualGesture, Category expectedGesture) {
 | 
			
		||||
    assertThat(actualGesture.index()).isEqualTo(actualGesture.index());
 | 
			
		||||
    assertThat(expectedGesture.categoryName()).isEqualTo(expectedGesture.categoryName());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private static void assertImageSizeIsExpected(Image inputImage) {
 | 
			
		||||
    assertThat(inputImage).isNotNull();
 | 
			
		||||
    assertThat(inputImage.getWidth()).isEqualTo(IMAGE_WIDTH);
 | 
			
		||||
    assertThat(inputImage.getHeight()).isEqualTo(IMAGE_HEIGHT);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user