Ключевые точки руки: структура данных

ml5.js предоставляет удобный интерфейс для работы с моделями машинного обучения в браузере, включая модели для отслеживания человеческой руки в режиме реального времени. Одной из таких моделей является Handpose, которая позволяет получать координаты ключевых точек руки для дальнейшей обработки в интерактивных приложениях, играх или визуализациях.

Основные концепции Handpose

Модель Handpose распознаёт 21 ключевую точку руки. Каждая точка соответствует определённой анатомической позиции: запястье, фаланги пальцев и кончики пальцев. Все точки имеют координаты в 2D-пространстве (x, y) и глубину (z), что позволяет использовать их для трёхмерной визуализации или анализа движения.

Ключевые моменты структуры данных:

  • points — массив из 21 объекта.

  • каждый объект содержит координаты:

    {
      x: Number, // горизонтальная координата
      y: Number, // вертикальная координата
      z: Number  // глубина относительно камеры
    }
  • индексация точек соответствует следующей схеме:

    • 0 — запястье
    • 1–4 — большой палец (от основания к кончику)
    • 5–8 — указательный палец
    • 9–12 — средний палец
    • 13–16 — безымянный палец
    • 17–20 — мизинец

Структура данных для одной руки

Когда модель обнаруживает руку на изображении, она возвращает объект hand, содержащий следующие поля:

{
  landmarks: [ {x, y, z}, ... ], // массив ключевых точек (21 элемент)
  annotations: {                  // более удобные группы по пальцам
    thumb: [ {x, y, z}, ... ],
    indexFinger: [ {x, y, z}, ... ],
    middleFinger: [ {x, y, z}, ... ],
    ringFinger: [ {x, y, z}, ... ],
    pinky: [ {x, y, z}, ... ],
    palmBase: [ {x, y, z} ]       // точка запястья
  }
}

landmarks используется для универсальной обработки, где важны все координаты подряд. annotations упрощает работу с отдельными пальцами, позволяя обращаться к каждой фаланге напрямую.

Примеры использования координат

Определение угла пальца: используя координаты трёх точек, можно вычислить угол между фалангами для анализа жестов.

function angleBetweenPoints(A, B, C) {
  const AB = {x: B.x - A.x, y: B.y - A.y};
  const BC = {x: C.x - B.x, y: C.y - B.y};
  const dot = AB.x * BC.x + AB.y * BC.y;
  const magAB = Math.sqrt(AB.x**2 + AB.y**2);
  const magBC = Math.sqrt(BC.x**2 + BC.y**2);
  return Math.acos(dot / (magAB * magBC));
}

// Использование:
const thumbAngle = angleBetweenPoints(
  hand.landmarks[0],
  hand.landmarks[1],
  hand.landmarks[2]
);

Рисование контуров руки: можно соединять точки линиями для визуализации.

function drawHand(ctx, hand) {
  const fingers = ['thumb', 'indexFinger', 'middleFinger', 'ringFinger', 'pinky'];
  ctx.strokeStyle = 'blue';
  ctx.lineWidth = 2;
  
  fingers.forEach(finger => {
    const points = hand.annotations[finger];
    for (let i = 0; i < points.length - 1; i++) {
      ctx.beginPath();
      ctx.moveTo(points[i].x, points[i].y);
      ctx.lineTo(points[i + 1].x, points[i + 1].y);
      ctx.stroke();
    }
  });
}

Структура данных для нескольких рук

Модель Handpose поддерживает отслеживание одной руки на данный момент. Если требуется мульти-хенд (несколько рук), можно обрабатывать каждый объект hand отдельно, помещая их в массив:

const hands = [hand1, hand2, ...];
hands.forEach(hand => {
  // работа с hand.landmarks или hand.annotations
});

Для каждого элемента массива структура данных идентична: landmarks и annotations.

Практическая оптимизация работы с данными

  • Кэширование координат: если обработка кадра требует сложных вычислений, координаты текущего кадра можно сохранять, чтобы избежать повторного вызова модели.
  • Интерполяция движения: при низком FPS можно сглаживать движения пальцев с помощью линейной интерполяции координат между кадрами.
  • Фильтрация шума: z-координата иногда нестабильна; медианный фильтр или усреднение нескольких последних кадров повышает стабильность жестов.

Взаимодействие с другими библиотеками

Ключевые точки руки в ml5.js можно использовать с p5.js для визуализации и с TensorFlow.js для построения собственных моделей распознавания жестов. Например, массив landmarks легко превращается в вектор признаков для классификатора:

const features = hand.landmarks.flatMap(point => [point.x, point.y, point.z]);
// features — массив длиной 63 (21 точка * 3 координаты)

Эти данные можно подавать на вход модели для классификации различных жестов или действий.