Отслеживание рук в Apple Vision Pro: ладонь к лицу или нет?

Вопросы и ответы

Я пытаюсь создать функцию для Apple Vision Pro, которая определяет, направлена ли моя ладонь ко мне (в сторону устройства) или нет. Вот высокоуровневый обзор проблемы:

  1. Определите вектор направления для ладони
    Я создал вектор направления для ладони, ссылаясь на ее направление вперед (оси Z в ее матрице трансформации). Это дает мне направление, в котором направлена ладонь.

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

  3. Выполните произведение скалярного
    Затем я использовал скалярное произведение между вектором направления ладони и вектором направления головы. Если скалярное произведение больше 0, ладонь, как правило, направлена на голову. Регулируя порог (например, 0.2), я мог точно настроить, насколько прямо ладонь должна быть направлена на голову, чтобы это считалось «направленной».

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

К сожалению, я не получаю ожидаемого результата.

Вот мое решение на Swift:

func isPalmFacingDevice(palmPos: simd_float4x4, facePos: simd_float4x4) -> Bool {
        // Извлечение позиций из матриц 4x4
        let palmPos3x3 = simd_make_float3(palmPos.columns.3.x,
                                          palmPos.columns.3.y,
                                          palmPos.columns.3.z)

        let headPos3x3 = simd_make_float3(facePos.columns.3.x,
                                          facePos.columns.3.y,
                                          facePos.columns.3.z)

        // Рассчитайте направления вперед из матриц 4x4
        // В матрице 4x4 направление вперед обычно находится в отрицательной оси Z (третьем столбце)
        let palmForwardDirection = simd_make_float3(palmPos.columns.2.x,
                                                    palmPos.columns.2.y,
                                                    palmPos.columns.2.z)

        let headForwardDirection = simd_make_float3(facePos.columns.2.x,
                                                    facePos.columns.2.y,
                                                    facePos.columns.2.z)

        // Нормализуйте оба векторa направления, чтобы сделать их единичными векторами
        let normalizedPalmDirection = simd_normalize(palmForwardDirection)
        let normalizedHeadDirection = simd_normalize(headForwardDirection)

        // Выполните скалярное произведение между нормализованными направлениями вперед
        let dotProduct = simd_dot(normalizedPalmDirection, normalizedHeadDirection)

        // Если скалярное произведение близко к 1, ладонь направлена на голову (в том же направлении)
        print("Скалярное произведение: \(dotProduct)")

        return dotProduct > 0
    }

Кроме того, вот как я извлекаю координаты ладони и головы:

for await anchorUpdate in handTracking.anchorUpdates {
                    let anchor = anchorUpdate.anchor

                    switch anchor.chirality {
                    case .left:
                        if let handSkeleton = anchor.handSkeleton {
                            let palmPos = handSkeleton.joint(.middleFingerKnuckle).anchorFromJointTransform
                            let headPos = worldTracking.queryDeviceAnchor(atTimestamp: CACurrentMediaTime())

                            if let headPos = await fetchHeadPositionWithRetry() {
                                print("Ладонь направлена на моё лицо: \(isPalmFacingDevice(palmPos: palmPos, facePos: headPos))")
                                print(headPos)
                            } else {
                                print("Не удалось получить позицию головы.")
                            }

                        }

Моя проблема заключается в том, что я, по сути, только получаю скалярное произведение ~0.9, как показано в этих логах:

Скалярное произведение: 0.9786667
Ладонь направлена на моё лицо: true

Ответ

Ваш вопрос о том, как определить, направлена ли ладонь к лицу при использовании Apple Vision Pro — это интересная задача, и я постараюсь помочь вам улучшить вашу реализацию.

Общий обзор проблемы

Вы уже определили основные шаги, которые нужно предпринять для решения этой задачи:

  1. Определите вектор направления ладони.
  2. Получите вектор направления головы.
  3. Выполните скалярное произведение этих двух векторов, чтобы понять, направлены ли они в одном направлении.

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

Решение проблемы

1. Определение векторов направления

Вы выделили векторы направления ладони и головы правильно, используя третий столбец матрицы преобразования. Это стандартный подход.

2. Нормализация векторов

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

3. Скалярное произведение

Ваш текущий расчет скалярного произведения выглядит рационально. Однако, как вы заметили, даже при высоком значении скалярного произведения (близком к 1) вы можете получить результаты, которые не соответствуют вашим ожиданиям. Это может быть связано с тем, что скалярное произведение измеряет лишь общее направление, а не угол между векторами.

4. Анализ угла между векторами

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

let angle = acos(dotProduct)
let degrees = angle * (180 / .pi) // Преобразование радиан в градусы

Вы можете задать порог значения угла, например 30 градусов, чтобы считать ладонь "направленной" на лицо. Ваша функция может выглядеть следующим образом:

func isPalmFacingDevice(palmPos: simd_float4x4, facePos: simd_float4x4) -> Bool {
    // Извлечение положений и векторов
    let palmForwardDirection = simd_make_float3(palmPos.columns.2.x, palmPos.columns.2.y, palmPos.columns.2.z)
    let headForwardDirection = simd_make_float3(facePos.columns.2.x, facePos.columns.2.y, facePos.columns.2.z)

    // Нормализация векторов
    let normalizedPalmDirection = simd_normalize(palmForwardDirection)
    let normalizedHeadDirection = simd_normalize(headForwardDirection)

    // Скалярное произведение
    let dotProduct = simd_dot(normalizedPalmDirection, normalizedHeadDirection)

    // Проверка угла
    if dotProduct > 0.2 {  // Порог для "ближайшего направления"
        let angle = acos(dotProduct)
        let degrees = angle * (180 / .pi)

        print("Angle: \(degrees) degrees")

        // Установите нужный вам порог угла
        return degrees < 30 // Например, 30 градусов
    }
    return false
}

5. Получение положений пальцев и головы

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

Заключение

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

Надеюсь, что это описание и предложения помогут вам улучшить вашу реализацию! Если у вас возникнут дополнительные вопросы, не стесняйтесь обращаться.

Оцените материал
Добавить комментарий

Капча загружается...