Lựa chọn phân loại tư thế

Với API Phát hiện tư thế của Bộ công cụ học máy, bạn có thể rút ra các diễn giải có ý nghĩa về một tư thế bằng cách kiểm tra vị trí tương đối của nhiều bộ phận trên cơ thể. Trang này đưa ra một vài ví dụ.

Phân loại tư thế và tính số lần lặp lại bằng thuật toán k-NN

Một trong những ứng dụng phổ biến nhất của tính năng phát hiện tư thế là theo dõi hoạt động thể dục. Việc xây dựng một thuật toán phân loại tư thế nhận dạng các tư thế thể dục cụ thể và đếm số lần lặp lại có thể là một nhiệm vụ khó khăn đối với nhà phát triển.

Trong phần này, chúng tôi mô tả cách chúng tôi tạo một tư thế tuỳ chỉnh thuật toán phân loại bằng MediaPipe Colab và minh hoạ một thuật toán phân loại hoạt động trong ứng dụng mẫu Bộ công cụ học máy của chúng tôi.

Nếu bạn chưa quen với Google Colaboratory, vui lòng tham khảo hướng dẫn giới thiệu.

Để nhận biết tư thế, chúng ta sử dụng thuật toán hàng lân cận k (k-NN) vì nó rất đơn giản và dễ bắt đầu. Thuật toán này xác định lớp của đối tượng dựa trên mẫu gần nhất trong tập huấn luyện.

Hãy làm theo các bước sau để xây dựng và huấn luyện trình nhận dạng:

1. Thu thập các mẫu hình ảnh

Chúng tôi đã thu thập các mẫu hình ảnh của các bài tập mục tiêu từ nhiều nguồn. T4 chọn vài trăm hình ảnh cho mỗi bài tập, chẳng hạn như "lên" và "giảm" vị trí chống đẩy. Điều quan trọng là bạn phải thu thập các mẫu bao gồm nhiều góc máy ảnh, điều kiện môi trường, hình dáng cơ thể và các biến thể bài tập thể dục.

Hình 1. Các tư thế chống đẩy lên và xuống

2. Chạy tính năng phát hiện tư thế trên hình ảnh mẫu

Thao tác này sẽ tạo ra một tập hợp các điểm mốc tư thế dùng để huấn luyện. Chúng ta không quan tâm đến việc phát hiện tư thế vì chúng ta sẽ huấn luyện mô hình của riêng mình ở bước tiếp theo.

Thuật toán k-NN mà chúng tôi đã chọn để phân loại tư thế tuỳ chỉnh yêu cầu một vectơ đặc trưng đại diện cho mỗi mẫu và một chỉ số để tính toán khoảng cách giữa hai vectơ nhằm tìm mục tiêu gần nhất với mẫu tư thế. Điều này có nghĩa là chúng ta phải chuyển đổi các mốc tư thế vừa nhận được.

Để chuyển đổi các điểm đánh dấu tư thế thành vectơ đặc điểm, chúng ta sử dụng khoảng cách giữa các cặp trong danh sách các khớp tư thế được xác định trước, chẳng hạn như khoảng cách giữa cổ tay và vai, mắt cá chân và hông, cổ tay trái và phải. Vì tỷ lệ hình ảnh có thể khác nhau, nên chúng tôi đã chuẩn hoá các tư thế để có cùng kích thước phần thân và hướng phần thân dọc trước khi chuyển đổi các điểm đánh dấu.

3. Huấn luyện mô hình và đếm số lần lặp lại

Chúng tôi đã dùng MediaPipe Colab để truy cập vào mã cho thuật toán phân loại và huấn luyện mô hình.

Để đếm số lần lặp lại, chúng tôi đã sử dụng một thuật toán Colab khác để theo dõi ngưỡng xác suất của một vị trí tư thế mục tiêu. Ví dụ:

  • Khi xác suất của lớp tư thế "xuống" vượt qua ngưỡng nhất định lần đầu tiên, thuật toán sẽ đánh dấu rằng lớp tư thế "xuống" đã được nhập.
  • Khi xác suất giảm xuống dưới ngưỡng, thuật toán sẽ đánh dấu rằng lớp tư thế "xuống" đã thoát và tăng bộ đếm.
Hình 2. Ví dụ về việc đếm số lần lặp lại

4. Tích hợp với ứng dụng bắt đầu nhanh của Bộ công cụ học máy

Colab ở trên tạo ra một tệp CSV mà bạn có thể điền sẵn tất cả mẫu tư thế. Trong phần này, bạn sẽ tìm hiểu cách tích hợp tệp CSV của mình với Ứng dụng bắt đầu nhanh của Android với Bộ công cụ học máy để xem cách phân loại tư thế tuỳ chỉnh theo thời gian thực.

Thử phân loại tư thế bằng các mẫu đi kèm trong ứng dụng làm quen nhanh

Thêm tệp CSV của riêng bạn

  • Thêm tệp CSV vào thư mục tài sản của ứng dụng.
  • Trong PoseClassifierProcessor, hãy cập nhật các biến POSE_SAMPLES_FILEPOSE_CLASSES để khớp với tệp CSV và các mẫu tư thế.
  • Tạo bản dựng và chạy ứng dụng

Xin lưu ý rằng tính năng phân loại có thể không hoạt động hiệu quả nếu không có đủ mẫu. Thông thường, bạn cần khoảng 100 mẫu cho mỗi lớp tư thế.

Để tìm hiểu thêm và dùng thử tính năng này, hãy xem MediaPipe Colabhướng dẫn phân loại MediaPipe.

Nhận dạng các cử chỉ đơn giản bằng cách tính toán khoảng cách đến địa danh

Khi hai hoặc nhiều mốc gần nhau, chúng có thể được dùng để nhận dạng cử chỉ. Ví dụ: khi điểm đánh dấu cho một hoặc nhiều ngón tay trên bàn tay gần với điểm đánh dấu cho mũi, bạn có thể suy luận rằng người dùng rất có thể đang chạm vào khuôn mặt của họ.

Hình 3. Diễn giải tư thế

Nhận biết tư thế yoga bằng kỹ thuật phỏng đoán góc

Bạn có thể xác định tư thế yoga bằng cách tính toán góc của nhiều khớp. Để ví dụ: Hình 2 bên dưới minh hoạ tư thế yoga của Warrior II. Các góc gần đúng giúp xác định tư thế này được viết trong:

Hình 4. Chia một tư thế thành các góc

Tư thế này có thể được mô tả là sự kết hợp sau đây của các góc phần cơ thể gần đúng:

  • Góc 90 độ ở cả hai vai
  • 180 độ ở cả hai khuỷu tay
  • Góc 90 độ ở chân trước và eo
  • Góc 180 độ ở đầu gối sau
  • Góc 135 độ ở eo

Bạn có thể sử dụng các điểm mốc đã đặt để tính các góc này. Ví dụ: góc ở chân trước bên phải và eo là góc giữa đường thẳng tính từ bên phải từ vai sang hông phải và đường thẳng từ hông phải sang đầu gối phải.

Sau khi tính toán tất cả các góc cần thiết để xác định tư thế, bạn có thể kiểm tra xem có khớp hay không. Trong trường hợp này, bạn đã nhận dạng được tư thế.

Đoạn mã dưới đây minh hoạ cách sử dụng toạ độ X và Y để tính toán góc giữa hai phần cơ thể. Phương pháp phân loại này có một số hạn chế. Khi chỉ kiểm tra X và Y, các góc được tính toán sẽ thay đổi theo góc giữa đối tượng và máy ảnh. Bạn sẽ nhận được kết quả tốt nhất bằng hình ảnh ngang, thẳng về phía trước. Bạn cũng có thể thử mở rộng thuật toán này bằng cách sử dụng toạ độ Z và xem liệu thuật toán này có hoạt động hiệu quả hơn cho trường hợp sử dụng của bạn hay không.

Tính toán góc địa danh trên Android

Phương thức sau đây tính toán góc giữa bất kỳ ba điểm đánh dấu nào. Hàm này đảm bảo góc được trả về nằm trong khoảng từ 0 đến 180 độ.

Kotlin

fun getAngle(firstPoint: PoseLandmark, midPoint: PoseLandmark, lastPoint: PoseLandmark): Double {
        var result = Math.toDegrees(atan2(lastPoint.getPosition().y - midPoint.getPosition().y,
                lastPoint.getPosition().x - midPoint.getPosition().x)
                - atan2(firstPoint.getPosition().y - midPoint.getPosition().y,
                firstPoint.getPosition().x - midPoint.getPosition().x))
        result = Math.abs(result) // Angle should never be negative
        if (result > 180) {
            result = 360.0 - result // Always get the acute representation of the angle
        }
        return result
    }

Java

static double getAngle(PoseLandmark firstPoint, PoseLandmark midPoint, PoseLandmark lastPoint) {
  double result =
        Math.toDegrees(
            atan2(lastPoint.getPosition().y - midPoint.getPosition().y,
                      lastPoint.getPosition().x - midPoint.getPosition().x)
                - atan2(firstPoint.getPosition().y - midPoint.getPosition().y,
                      firstPoint.getPosition().x - midPoint.getPosition().x));
  result = Math.abs(result); // Angle should never be negative
  if (result > 180) {
      result = (360.0 - result); // Always get the acute representation of the angle
  }
  return result;
}

Sau đây là cách tính góc ở hông phải:

Kotlin

val rightHipAngle = getAngle(
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_SHOULDER),
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_HIP),
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_KNEE))

Java

double rightHipAngle = getAngle(
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_SHOULDER),
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_HIP),
                pose.getPoseLandmark(PoseLandmark.Type.RIGHT_KNEE));

Tính toán các góc mốc trên iOS

Phương thức sau đây tính toán góc giữa bất kỳ ba điểm đánh dấu nào. Đảm bảo góc được trả về nằm trong khoảng 0 và 180 độ.

Swift

func angle(
      firstLandmark: PoseLandmark,
      midLandmark: PoseLandmark,
      lastLandmark: PoseLandmark
  ) -> CGFloat {
      let radians: CGFloat =
          atan2(lastLandmark.position.y - midLandmark.position.y,
                    lastLandmark.position.x - midLandmark.position.x) -
            atan2(firstLandmark.position.y - midLandmark.position.y,
                    firstLandmark.position.x - midLandmark.position.x)
      var degrees = radians * 180.0 / .pi
      degrees = abs(degrees) // Angle should never be negative
      if degrees > 180.0 {
          degrees = 360.0 - degrees // Always get the acute representation of the angle
      }
      return degrees
  }

Objective-C

(CGFloat)angleFromFirstLandmark:(MLKPoseLandmark *)firstLandmark
                      midLandmark:(MLKPoseLandmark *)midLandmark
                     lastLandmark:(MLKPoseLandmark *)lastLandmark {
    CGFloat radians = atan2(lastLandmark.position.y - midLandmark.position.y,
                            lastLandmark.position.x - midLandmark.position.x) -
                      atan2(firstLandmark.position.y - midLandmark.position.y,
                            firstLandmark.position.x - midLandmark.position.x);
    CGFloat degrees = radians * 180.0 / M_PI;
    degrees = fabs(degrees); // Angle should never be negative
    if (degrees > 180.0) {
        degrees = 360.0 - degrees; // Always get the acute representation of the angle
    }
    return degrees;
}

Sau đây là cách tính góc ở hông phải:

Swift

let rightHipAngle = angle(
      firstLandmark: pose.landmark(ofType: .rightShoulder),
      midLandmark: pose.landmark(ofType: .rightHip),
      lastLandmark: pose.landmark(ofType: .rightKnee))

Objective-C

CGFloat rightHipAngle =
    [self angleFromFirstLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightShoulder]
                     midLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightHip]
                    lastLandmark:[pose landmarkOfType:MLKPoseLandmarkTypeRightKnee]];