[Flutter] Tensorflow 모델을 활용한 Hand gesture recognition 제작

박예승·2024년 10월 19일

이 글에서는 내가 그로스로그 모임에서 추진하려는 프로젝트를 진행하기 전 먼저 기능을 사용해보는 것이 좋을 것 같아 시작하게 된 개인 프로젝트에서 Tensorflow를 사용한 경험과, 이후 해결 해야할 문제에 대해 정리해보려한다.

Tensorflow란?

Tensorflow는 수치 계산, 대규모 머신 러닝, 딥러닝, 기타 통계 및 예측 분석 워크로드를 위한 오픈 소스 라이브러리이다. 이를 통해 개발자들이 머신 러닝 모델을 구현하는 것을 더 빠르고 쉽게 만드는 기술이다.

프로젝트 진행 과정

프로젝트의 목적은 손을 모을 때와 폈을 때 0~100 사이의 실수값이 변수에 담기도록 하는 것이다. 간단하게 프로젝트를 살펴보면 다음과 같이 구성되어있다.

  • tflite Model: assets/models/hand_landmark.tflite에 위치한 학습되어있는 모델을 사용해 hand landmark를 추적
  • IsolateUtils: 반복되는 추론 작업을 처리하기 위해 메모리 공간 분리
  • TensorflowProvider: IsolateUtils 클래스를 활용한 추론 및 데이터 가공 작업 (추후 domain/use_case로 분리 할 것)
  • CameraProvider: 내장 카메라를 사용하여 이미지를 프레임당 callback하는 함수를 호출 Ui Boundary에서 TensorflowProvider로 CameraImage 객체 전달

위와 같이 개발된 프로젝트를 기반으로 TensorflowProvider에서 추론을 통해 가공되지않은 데이터를 전달받으면 데이터를 다음과 같이 가공한다.

4, 8, 12, 16, 20 번 landmark의 좌표값을 가져와 해당 좌표들이 가깝게 위치할 경우 손을 모았다고 판단, 떨어져 있을 때 손을 폈다고 판단해 0~100 사이의 실수값을 반환하는 로직을 만들었다.

double _calculateHandFoldPercentage(List<Offset> points) {
    if (state.initialDistance == 0.0) {
      state = state.copyWith(initialDistance: (points[1] - points[0]).distance);
    }

    final indicesToCheck = [4, 8, 12, 16, 20];

    final currentDistance = (points[1] - points[0]).distance;

    final distanceFactor = state.initialDistance / currentDistance;

    double weightedDistanceSum = 0.0;
    double weightSum = 0.0;
    double interpolationFactor = 0.5;

    for (int i = 0; i < indicesToCheck.length - 1; i++) {
      int index1 = indicesToCheck[i];
      int index2 = indicesToCheck[i + 1];

      double segmentDistance = (points[index1] - points[index2]).distance * distanceFactor;
      double weight = 1.0 / (1.0 + interpolationFactor * segmentDistance);

      weightedDistanceSum += segmentDistance * weight;
      weightSum += weight;
    }

    double averageWeightedDistance = weightedDistanceSum / weightSum;

    double maxDistance = 500.0;
    double foldPercentage = (1 - (averageWeightedDistance / maxDistance)) * 100.0;
    return foldPercentage.clamp(0.0, 100.0);
  }

해당 로직으로 가공을 진행한 이후 로그를 출력하면 아래와 같이 출력된다.

프로젝트 진행 도중 마주한 문제점

프로젝트를 진행하며 문제점이 몇가지 있었다.

  1. 거리에 따라 224*224 픽셀의 이미지에서 손이 차지하는 비율의 차이가 있고, landmark 사이의 좌표값이 멀거나 가까워지게 된다.
  2. 위 문제를 해결하기 위해 0번 1번 landmark의 거리를 보간으로 사용하였지만 손의 각도가 달라지면 이 값도 신뢰도가 떨어진다.
  3. 손의 떨림으로 인해 1.0 이상의 유의미한 값의 변화가 지속적으로 있다.

위와 같은 문제를 해결하기 위해 Mediapipe 모델을 직접 학습시켜, 데이터 가공을 내부에서 처리하거나 처리하기에 쉬운 다른 제스쳐를 고안해보는 방법을 생각해야 되겠다.


Hand-Gesture-Recognition Project: https://github.com/Yeseung0610/Hand-Gesture-Recognition
Google Mediapipe: https://github.com/google-ai-edge/mediapipe

0개의 댓글