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ể diễn giải tư thế có ý nghĩa bằng cách kiểm tra vị trí tương đối của nhiều bộ phận cơ thể. Trang này trình bày một số ví dụ.

Phân loại tư thế và đếm 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 thể chất. Việc xây dựng một thuật toán phân loại tư thế giúp 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 công việc khó khăn đối với các nhà phát triển.

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

Nếu bạn chưa hiểu rõ về Google Colaboratory, vui lòng xem hướng dẫn giới thiệu.

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

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

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

Chúng tôi đã thu thập mẫu hình ảnh của các bài tập mục tiêu từ nhiều nguồn. Chúng tôi chọn vài trăm hình ảnh cho mỗi bài tập, chẳng hạn như vị trí "lên" và "xuống" cho chống đẩy. Điều quan trọng là 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ể tập thể dục.

Hình 1. Vị trí 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 tạo ra một tập hợp các điểm mốc về tư thế được dùng để huấn luyện. Chúng tôi không quan tâm đến việc phát hiện tư thế vì sẽ huấn luyện mô hình của riêng mình trong 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 biểu diễn vectơ tính năng cho từng mẫu và một chỉ số để tính 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 điểm mốc về tư thế vừa nhận được.

Để chuyển đổi các điểm mốc về tư thế thành vectơ đặc điểm, chúng tôi sử dụng khoảng cách theo cặp giữa các 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ũng như 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 thân người và hướng dọc thân người trước khi chuyển đổi các điểm mốc.

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

Chúng tôi đã sử 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.

Để tính 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 một ngưỡng nhất định lần đầu tiên, thuật toán sẽ đánh dấu là 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" đã bị thoát và tăng bộ đếm.
Hình 2. Ví dụ về cách đếm lặp lại

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

Colab ở trên tạo một tệp CSV mà bạn có thể điền sẵn bằng tất cả các mẫu tư thế của mình. Trong phần này, bạn sẽ tìm hiểu cách tích hợp tệp CSV với ứng dụng hướng dẫn nhanh về Bộ công cụ học máy Android để 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 được gói trong ứng dụng khởi động nhanh

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

  • Thêm tệp CSV vào thư mục thành phầ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à mẫu đặt ra.
  • Tạo bản dựng và chạy ứng dụng

Lưu ý rằng tính năng phân loại có thể sẽ không hoạt động chính xác 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à tự mình trải nghiệm, 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 điểm mốc

Khi 2 hoặc nhiều mốc ở gần nhau, bạn có thể dùng các điểm mốc này để nhận dạng cử chỉ. Ví dụ: khi mốc của một hoặc nhiều ngón tay trên bàn tay gần với mốc cho mũi, bạn có thể dự đoán rằng người dùng có nhiều khả năng đang chạm vào mặt mình nhất.

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

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

Bạn có thể xác định một tư thế yoga bằng cách tính toán góc của các khớp. Ví dụ: Hình 2 bên dưới cho thấy tư thế yoga Warrior II. Các góc gần đúng xác định tư thế này được viết bằng:

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

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

  • Góc 90 độ ở cả hai vai
  • 180 độ ở cả hai cù tay
  • Góc 90 độ ở chân trước và thắt lưng
  • Góc 180 độ ở đầu gối sau
  • Góc 135 độ ở thắt lưng

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

Khi bạn đã 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 không, trong trường hợp đó bạn đã nhận ra 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 bộ phận cơ thể. Phương pháp phân loại này có một số hạn chế. Bằng cách chỉ kiểm tra X và Y, các góc được tính toán sẽ thay đổi tuỳ 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 với hình ảnh trực tiếp về một cấp độ. Bạn cũng có thể thử mở rộng thuật toán này bằng cách tận dụng toạ độ Z để xem liệu toạ độ này có hoạt động tốt hơn cho trường hợp sử dụng của bạn hay không.

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

Phương thức sau đây sẽ tính toán góc giữa 3 điểm đánh dấu bất kỳ. Đả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;
}

Dưới đâ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 sẽ tính toán góc giữa 3 điểm đánh dấu bất kỳ. Đảm bảo góc được trả về nằm trong khoảng từ 0 đến 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;
}

Dưới đâ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]];