Руководство по распознаванию жестов для iOS

Задача «Распознаватель жестов MediaPipe» позволяет распознавать жесты рук в реальном времени и предоставляет результаты распознанных жестов рук и ориентиры обнаруженных рук. В этих инструкциях показано, как использовать Распознаватель жестов с приложениями iOS.

Вы можете увидеть эту задачу в действии, просмотрев веб-демо. Дополнительные сведения о возможностях, моделях и параметрах конфигурации этой задачи см. в разделе Обзор .

Пример кода

Пример кода задач MediaPipe — это базовая реализация приложения распознавания жестов для iOS. В примере используется камера на физическом устройстве iOS для постоянного обнаружения жестов рук, а также могут использоваться изображения и видео из галереи устройства для статического обнаружения жестов.

Вы можете использовать это приложение в качестве отправной точки для своего собственного приложения для iOS или обращаться к нему при изменении существующего приложения. Пример кода Распознавателя жестов размещен на GitHub .

Загрузите код

Следующие инструкции показывают, как создать локальную копию кода примера с помощью инструмента командной строки git .

Чтобы загрузить пример кода:

  1. Клонируйте репозиторий git, используя следующую команду:

    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. При желании настройте свой экземпляр git на использование разреженной проверки, чтобы у вас были только файлы для примера приложения Распознаватель жестов:

    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/gesture_recognizer/ios/
    

После создания локальной версии примера кода вы можете установить библиотеку задач MediaPipe, открыть проект с помощью Xcode и запустить приложение. Инструкции см. в Руководстве по установке для iOS .

Ключевые компоненты

Следующие файлы содержат ключевой код для примера приложения Распознаватель жестов:

  • GestureRecouncerService.swift : инициализирует Распознаватель жестов, обрабатывает выбор модели и выполняет логический вывод на входных данных.
  • CameraViewController.swift : реализует пользовательский интерфейс для режима ввода изображения с камеры в реальном времени и визуализирует результаты.
  • MediaLibraryViewController.swift : реализует пользовательский интерфейс для режима ввода неподвижных изображений и видеофайлов и визуализирует результаты.

Настраивать

В этом разделе описаны ключевые шаги по настройке среды разработки и проектов кода для использования Распознаватель жестов. Общие сведения о настройке среды разработки для использования задач MediaPipe, включая требования к версии платформы, см. в руководстве по настройке для iOS .

Зависимости

Распознаватель жестов использует библиотеку MediaPipeTasksVision , которую необходимо установить с помощью CocoaPods. Библиотека совместима с приложениями Swift и Objective-C и не требует дополнительной настройки для конкретного языка.

Инструкции по установке CocoaPods на macOS см. в руководстве по установке CocoaPods . Инструкции о том, как создать Podfile с необходимыми модулями для вашего приложения, см. в разделе Использование CocoaPods .

Добавьте модуль MediaPipeTasksVision в Podfile используя следующий код:

target 'MyGestureRecognizerApp' do
  use_frameworks!
  pod 'MediaPipeTasksVision'
end

Если ваше приложение включает в себя цели модульного тестирования, обратитесь к Руководству по настройке для iOS для получения дополнительной информации о настройке вашего Podfile .

Модель

Для задачи Распознавателя жестов MediaPipe требуется обученная модель, совместимая с этой задачей. Дополнительные сведения о доступных обученных моделях для Распознаватель жестов см. в разделе «Модели» обзора задач.

Выберите и загрузите модель и добавьте ее в каталог проекта с помощью Xcode. Инструкции по добавлению файлов в проект Xcode см. в разделе Управление файлами и папками в проекте Xcode .

Используйте свойство BaseOptions.modelAssetPath , чтобы указать путь к модели в вашем пакете приложений. Пример кода см. в следующем разделе.

Создать задачу

Вы можете создать задачу «Распознаватель жестов», вызвав один из ее инициализаторов. Инициализатор GestureRecognizer(options:) принимает значения для параметров конфигурации.

Если вам не нужен Распознаватель жестов, инициализированный с настраиваемыми параметрами конфигурации, вы можете использовать инициализатор GestureRecognizer(modelPath:) для создания Распознаватель жестов с параметрами по умолчанию. Дополнительные сведения о параметрах конфигурации см. в разделе Обзор конфигурации .

Задача «Распознаватель жестов» поддерживает три типа входных данных: неподвижные изображения, видеофайлы и прямые видеопотоки. По умолчанию GestureRecognizer(modelPath:) инициализирует задачу для неподвижных изображений. Если вы хотите, чтобы ваша задача была инициализирована для обработки видеофайлов или прямых видеопотоков, используйте GestureRecognizer(options:) чтобы указать режим работы видео или прямой трансляции. Для режима прямой трансляции также требуется дополнительный параметр gestureRecognizerLiveStreamDelegate , который позволяет распознавателю жестов асинхронно доставлять делегату результаты распознавания жестов.

Выберите вкладку, соответствующую вашему режиму работы, чтобы узнать, как создать задачу и выполнить вывод.

Быстрый

Изображение

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "gesture_recognizer",
                                      ofType: "task")

let options = GestureRecognizerOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .image
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

let gestureRecognizer = try GestureRecognizer(options: options)
    

Видео

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "gesture_recognizer",
                                      ofType: "task")

let options = GestureRecognizerOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .video
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

let gestureRecognizer = try GestureRecognizer(options: options)
    

Прямая трансляция

import MediaPipeTasksVision

// Class that conforms to the `GestureRecognizerLiveStreamDelegate` protocol and
// implements the method that the gesture recognizer calls once it finishes
// performing recognizing hand gestures in each input frame.
class GestureRecognizerResultProcessor: NSObject, GestureRecognizerLiveStreamDelegate {

  func gestureRecognizer(
    _ gestureRecognizer: GestureRecognizer,
    didFinishRecognition result: GestureRecognizerResult?,
    timestampInMilliseconds: Int,
    error: Error?) {

    // Process the gesture recognizer result or errors here.

  }
}

let modelPath = Bundle.main.path(
  forResource: "gesture_recognizer",
  ofType: "task")

let options = GestureRecognizerOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .liveStream
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

// Assign an object of the class to the `gestureRecognizerLiveStreamDelegate`
// property.
let processor = GestureRecognizerResultProcessor()
options.gestureRecognizerLiveStreamDelegate = processor

let gestureRecognizer = try GestureRecognizer(options: options)
    

Цель-C

Изображение

@import MediaPipeTasksVision;

NSString *modelPath =
  [[NSBundle mainBundle] pathForResource:@"gesture_recognizer"
                                  ofType:@"task"];

MPPGestureRecognizerOptions *options =
  [[MPPGestureRecognizerOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeImage;
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

MPPGestureRecognizer *gestureRecognizer =
      [[MPPGestureRecognizer alloc] initWithOptions:options error:nil];
    

Видео

@import MediaPipeTasksVision;

NSString *modelPath =
  [[NSBundle mainBundle] pathForResource:@"gesture_recognizer"
                                  ofType:@"task"];

MPPGestureRecognizerOptions *options =
  [[MPPGestureRecognizerOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeVideo;
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

MPPGestureRecognizer *gestureRecognizer =
      [[MPPGestureRecognizer alloc] initWithOptions:options error:nil];
    

Прямая трансляция

@import MediaPipeTasksVision;

// Class that conforms to the `MPPGestureRecognizerLiveStreamDelegate` protocol
// and implements the method that the gesture recognizer calls once it finishes
// performing gesture recognition on each input frame.

@interface APPGestureRecognizerResultProcessor : NSObject 

@end

@implementation APPGestureRecognizerResultProcessor

-   (void)gestureRecognizer:(MPPGestureRecognizer *)gestureRecognizer
    didFinishRecognitionWithResult:(MPPGestureRecognizerResult *)gestureRecognizerResult
           timestampInMilliseconds:(NSInteger)timestampInMilliseconds
                             error:(NSError *)error {

    // Process the gesture recognizer result or errors here.

}

@end

NSString *modelPath =
  [[NSBundle mainBundle] pathForResource:@"gesture_recognizer"
                                  ofType:@"task"];

MPPGestureRecognizerOptions *options =
  [[MPPGestureRecognizerOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeLiveStream;
options.minHandDetectionConfidence = minHandDetectionConfidence
options.minHandPresenceConfidence = minHandPresenceConfidence
options.minTrackingConfidence = minHandTrackingConfidence
options.numHands = numHands

// Assign an object of the class to the `gestureRecognizerLiveStreamDelegate`
// property.
APPGestureRecognizerResultProcessor *processor =
  [APPGestureRecognizerResultProcessor new];
options.gestureRecognizerLiveStreamDelegate = processor;

MPPGestureRecognizer *gestureRecognizer =
      [[MPPGestureRecognizer alloc] initWithOptions:options error:nil];
    

Варианты конфигурации

Эта задача имеет следующие параметры конфигурации для приложений iOS:

Название опции Описание Диапазон значений Значение по умолчанию
runningMode Устанавливает режим выполнения задачи. Есть три режима:

ИЗОБРАЖЕНИЕ: Режим для ввода одного изображения.

ВИДЕО: Режим декодированных кадров видео.

LIVE_STREAM: режим прямой трансляции входных данных, например, с камеры. В этом режиме необходимо вызвать resultListener, чтобы настроить прослушиватель на асинхронное получение результатов. В этом режиме gestureRecognizerLiveStreamDelegate необходимо задать экземпляр класса, реализующего GestureRecognizerLiveStreamDelegate , чтобы получать результаты асинхронного распознавания жестов.
{ RunningMode.image, RunningMode.video, RunningMode.liveStream } RunningMode.image
num_hands Максимальное количество рук может быть обнаружено с помощью GestureRecognizer . Any integer > 0 1
min_hand_detection_confidence Минимальный показатель достоверности, позволяющий считать обнаружение рук успешным в модели обнаружения ладоней. 0.0 - 1.0 0.5
min_hand_presence_confidence Минимальный показатель достоверности оценки присутствия руки в модели обнаружения ориентиров рук. В режиме «Видео» и режиме прямой трансляции Распознаватель жестов, если показатель достоверности присутствия руки по модели ориентира руки ниже этого порога, активируется модель обнаружения ладони. В противном случае для определения местоположения руки (рук) для последующего обнаружения ориентиров используется упрощенный алгоритм отслеживания рук. 0.0 - 1.0 0.5
min_tracking_confidence Минимальный показатель достоверности, позволяющий считать отслеживание рук успешным. Это порог IoU ограничивающей рамки между руками в текущем кадре и последнем кадре. В режиме «Видео» и «Потоковый режим» Распознаватель жестов, если отслеживание не удается, Распознаватель жестов запускает обнаружение рук. В противном случае обнаружение руки пропускается. 0.0 - 1.0 0.5
canned_gestures_classifier_options Параметры настройки поведения классификатора стандартных жестов. Стандартные жесты: ["None", "Closed_Fist", "Open_Palm", "Pointing_Up", "Thumb_Down", "Thumb_Up", "Victory", "ILoveYou"]
  • Язык отображаемых имен: языковой стандарт, используемый для отображаемых имен, указанных в метаданных модели TFLite, если таковые имеются.
  • Максимальное количество результатов: максимальное количество возвращаемых результатов классификации с наибольшим количеством баллов. Если < 0, будут возвращены все доступные результаты.
  • Порог оценки: балл, ниже которого результаты отклоняются. Если установлено значение 0, будут возвращены все доступные результаты.
  • Список разрешенных категорий: список разрешенных названий категорий. Если поле не пусто, результаты классификации, категории которых нет в этом наборе, будут отфильтрованы. Взаимоисключающее со списком запретов.
  • Список запрещенных категорий: список запрещенных имен категорий. Если не пусто, результаты классификации, категория которых находится в этом наборе, будут отфильтрованы. Взаимоисключающее со списком разрешений.
    • Локаль отображаемых имен: any string
    • Максимальное количество результатов: any integer
    • Порог оценки: 0.0-1.0
    • Список разрешенных категорий: vector of strings
    • Список запрещенных категорий: vector of strings
    • Язык отображения отображаемых имен: "en"
    • Максимальное количество результатов: -1
    • Порог оценки: 0
    • Белый список категорий: пусто
    • Список запрещенных категорий: пустой
    custom_gestures_classifier_options Параметры настройки поведения классификатора пользовательских жестов.
  • Язык отображаемых имен: языковой стандарт, используемый для отображаемых имен, указанных в метаданных модели TFLite, если таковые имеются.
  • Максимальное количество результатов: максимальное количество возвращаемых результатов классификации с наибольшим количеством баллов. Если < 0, будут возвращены все доступные результаты.
  • Порог оценки: балл, ниже которого результаты отклоняются. Если установлено значение 0, будут возвращены все доступные результаты.
  • Список разрешенных категорий: список разрешенных названий категорий. Если поле не пусто, результаты классификации, категории которых нет в этом наборе, будут отфильтрованы. Взаимоисключающие со списком запретов.
  • Список запрещенных категорий: список запрещенных имен категорий. Если не пусто, результаты классификации, категория которых находится в этом наборе, будут отфильтрованы. Взаимоисключающее со списком разрешений.
    • Локаль отображаемых имен: any string
    • Максимальное количество результатов: any integer
    • Порог оценки: 0.0-1.0
    • Список разрешенных категорий: vector of strings
    • Список запрещенных категорий: vector of strings
    • Язык отображения отображаемых имен: "en"
    • Максимальное количество результатов: -1
    • Порог оценки: 0
    • Белый список категорий: пусто
    • Список запрещенных категорий: пустой
    result_listener Настраивает прослушиватель результатов на асинхронное получение результатов классификации, когда распознаватель жестов находится в режиме прямого потока. Может использоваться только в том случае, если для режима работы установлено значение LIVE_STREAM ResultListener Н/Д Н/Д

    Если в качестве рабочего режима установлена ​​прямая трансляция, распознавателю жестов требуется дополнительный параметр gestureRecognizerLiveStreamDelegate , который позволяет распознавателю жестов асинхронно доставлять результаты распознавания жестов. Делегат должен реализовать метод gestureRecognizer(_:didFinishRecognition:timestampInMilliseconds:error:) , который Распознаватель жестов вызывает после обработки результатов выполнения распознавания жестов в каждом кадре.

    Название опции Описание Диапазон значений Значение по умолчанию
    gestureRecognizerLiveStreamDelegate Позволяет распознавателю жестов асинхронно получать результаты распознавания жестов в режиме прямой трансляции. Класс, экземпляру которого присвоено это свойство, должен реализовать метод gestureRecognizer(_:didFinishRecognition:timestampInMilliseconds:error:) . Непригодный Не установлено

    Подготовьте данные

    Вам необходимо преобразовать входное изображение или кадр в объект MPImage перед передачей его в распознаватель жестов. MPImage поддерживает различные типы форматов изображений iOS и может использовать их в любом рабочем режиме для вывода. Для получения дополнительной информации о MPImage обратитесь к MPImage API .

    Выберите формат изображения iOS в зависимости от вашего варианта использования и режима работы, который требуется вашему приложению. MPImage принимает форматы изображений iOS UIImage , CVPixelBuffer и CMSampleBuffer .

    UIImage

    Формат UIImage хорошо подходит для следующих режимов работы:

    • Изображения: изображения из пакета приложения, пользовательской галереи или файловой системы, отформатированные как изображения UIImage можно преобразовать в объект MPImage .

    • Видео: используйте AVAssetImageGenerator для извлечения видеокадров в формат CGImage , а затем преобразуйте их в изображения UIImage .

    Быстрый

    // Load an image on the user's device as an iOS `UIImage` object.
    
    // Convert the `UIImage` object to a MediaPipe's Image object having the default
    // orientation `UIImage.Orientation.up`.
    let image = try MPImage(uiImage: image)
        

    Цель-C

    // Load an image on the user's device as an iOS `UIImage` object.
    
    // Convert the `UIImage` object to a MediaPipe's Image object having the default
    // orientation `UIImageOrientationUp`.
    MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
        

    В примере инициализируется MPImage с ориентацией UIImage.Orientation.Up по умолчанию. Вы можете инициализировать MPImage любым из поддерживаемых значений UIImage.Orientation . Распознаватель жестов не поддерживает зеркальные ориентации, такие как .upMirrored , .downMirrored , .leftMirrored , .rightMirrored .

    Для получения дополнительной информации о UIImage обратитесь к документации UIImage Apple Developer Documentation .

    CVPixelBuffer

    Формат CVPixelBuffer хорошо подходит для приложений, генерирующих кадры и использующих для обработки платформу iOS CoreImage .

    Формат CVPixelBuffer хорошо подходит для следующих режимов работы:

    • Изображения: приложения, которые генерируют изображения CVPixelBuffer после некоторой обработки с использованием платформы iOS CoreImage , могут быть отправлены в Распознаватель жестов в режиме работы изображения.

    • Видео: видеокадры можно конвертировать в формат CVPixelBuffer для обработки, а затем отправлять в Распознаватель жестов в видеорежиме.

    • прямая трансляция: приложения, использующие камеру iOS для создания кадров, могут быть преобразованы в формат CVPixelBuffer для обработки перед отправкой в ​​Распознаватель жестов в режиме прямой трансляции.

    Быстрый

    // Obtain a CVPixelBuffer.
    
    // Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the default
    // orientation `UIImage.Orientation.up`.
    let image = try MPImage(pixelBuffer: pixelBuffer)
        

    Цель-C

    // Obtain a CVPixelBuffer.
    
    // Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the
    // default orientation `UIImageOrientationUp`.
    MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
        

    Дополнительные сведения о CVPixelBuffer см. в документации разработчика Apple CVPixelBuffer .

    CMSampleBuffer

    Формат CMSampleBuffer хранит образцы мультимедиа единого типа и хорошо подходит для режима прямой трансляции. Живые кадры с камер iOS асинхронно доставляются в формате CMSampleBuffer с помощью iOS AVCaptureVideoDataOutput .

    Быстрый

    // Obtain a CMSampleBuffer.
    
    // Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the default
    // orientation `UIImage.Orientation.up`.
    let image = try MPImage(sampleBuffer: sampleBuffer)
        

    Цель-C

    // Obtain a `CMSampleBuffer`.
    
    // Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the
    // default orientation `UIImageOrientationUp`.
    MPImage *image = [[MPPImage alloc] initWithSampleBuffer:sampleBuffer error:nil];
        

    Дополнительные сведения о CMSampleBuffer см. в документации CMSampleBuffer Apple для разработчиков .

    Запустить задачу

    Чтобы запустить Распознаватель жестов, используйте метод recognize() специфичный для назначенного режима работы:

    • Неподвижное изображение: recognize(image:)
    • Видео: recognize(videoFrame:timestampInMilliseconds:)
    • Прямая трансляция: recognizeAsync(image:timestampInMilliseconds:)

    В следующих примерах кода показаны основные примеры запуска Распознаватель жестов в различных режимах работы:

    Быстрый

    Изображение

    let result = try gestureRecognizer.recognize(image: image)
        

    Видео

    let result = try gestureRecognizer.recognize(
      videoFrame: image,
      timestampInMilliseconds: timestamp)
        

    Прямая трансляция

    try gestureRecognizer.recognizeAsync(
      image: image,
      timestampInMilliseconds: timestamp)
        

    Цель-C

    Изображение

      MPPGestureRecognizerResult *result =
        [gestureRecognizer recognizeImage:mppImage
                                    error:nil];
        

    Видео

    MPPGestureRecognizerResult *result =
      [gestureRecognizer recognizeVideoFrame:image
                     timestampInMilliseconds:timestamp
                                       error:nil];
        

    Прямая трансляция

    BOOL success =
      [gestureRecognizer recognizeAsyncImage:image
                     timestampInMilliseconds:timestamp
                                       error:nil];
        

    Пример кода позволяет пользователю переключаться между режимами обработки, которые могут не потребоваться для вашего варианта использования.

    Обратите внимание на следующее:

    • При работе в режиме видео или в режиме прямой трансляции вы также должны предоставить метку времени входного кадра задаче Распознавателя жестов.

    • При работе в режиме изображения или видео задача «Распознаватель жестов» блокирует текущий поток до тех пор, пока не завершит обработку входного изображения или кадра. Чтобы избежать блокировки текущего потока, выполните обработку в фоновом потоке с помощью платформ iOS Dispatch или NSOperation .

    • При работе в режиме прямой трансляции задача «Распознаватель жестов» возвращается немедленно и не блокирует текущий поток. Он вызывает gestureRecognizer(_:didFinishRecognition:timestampInMilliseconds:error:) с результатом распознавания жестов после обработки каждого входного кадра. Распознаватель жестов вызывает этот метод асинхронно в выделенной последовательной очереди отправки. Для отображения результатов в пользовательском интерфейсе отправьте результаты в основную очередь после обработки результатов. Если функция recognizeAsync вызывается, когда задача Распознаватель жестов занята обработкой другого кадра, Распознаватель жестов игнорирует новый входной кадр.

    Обработка и отображение результатов

    После выполнения вывода задача Распознавателя жестов возвращает GestureRecognizerResult , который содержит ориентиры рук в координатах изображения, ориентиры рук в мировых координатах, категории рук (левая/правая) и жесты рук обнаруженных рук.

    Ниже показан пример выходных данных этой задачи:

    Полученный результат GestureRecognizerResult содержит четыре компонента, каждый из которых представляет собой массив, где каждый элемент содержит обнаруженный результат одной обнаруженной руки.

    • Рукава

      Handedness показывает, являются ли обнаруженные руки левыми или правыми.

    • Жесты

      Распознанные категории жестов обнаруженных рук.

    • Достопримечательности

      Имеется 21 ручной ориентир, каждый из которых состоит из координат x , y и z . Координаты x и y нормализуются на [0,0, 1,0] по ширине и высоте изображения соответственно. Координата z представляет глубину ориентира, при этом глубина на запястье является началом координат. Чем меньше значение, тем ближе ориентир к камере. Величина z использует примерно тот же масштаб, что и x .

    • Достопримечательности мира

      21 ориентир также представлен в мировых координатах. Каждый ориентир состоит из x , y и z , представляющих реальные трехмерные координаты в метрах с началом координат в геометрическом центре руки.

    GestureRecognizerResult:
      Handedness:
        Categories #0:
          index        : 0
          score        : 0.98396
          categoryName : Left
      Gestures:
        Categories #0:
          score        : 0.76893
          categoryName : Thumb_Up
      Landmarks:
        Landmark #0:
          x            : 0.638852
          y            : 0.671197
          z            : -3.41E-7
        Landmark #1:
          x            : 0.634599
          y            : 0.536441
          z            : -0.06984
        ... (21 landmarks for a hand)
      WorldLandmarks:
        Landmark #0:
          x            : 0.067485
          y            : 0.031084
          z            : 0.055223
        Landmark #1:
          x            : 0.063209
          y            : -0.00382
          z            : 0.020920
        ... (21 world landmarks for a hand)
    

    На следующих изображениях показана визуализация результатов задачи: