개요

  • 하나의 파일, 하나의 프로젝트 내에서 함수의 개수가 점점 많아지고 유사한 기능이 점점 많아지다보면, 함수명을 짓는게 힘듦.
  • 특히 상속 받은 함수, 클래스 내 고유한 함수를 구분해주어야 하는 등 속성 측면에서도 함수명을 보다 구체적이고 구분 되도록 지어야 함.
  • 따라서 이번 프로젝트 (hand-pose-detection, face-landmarks-detection, pose-detection) 에서는 함수를 구분하기 위해 ①상속 받은 함수는 부모 클래스와 동일한 함수명 ②static 키워드 ③_함수명 을 가장 많이 사용하였음
  • 이하에서는 위 방법 중 ②, ③ 에 대해 소개함. 

 


 

1. static 

 

정적 메서드와 정적 프로퍼티

 

ko.javascript.info

  • 핵심 코드
static detect(imageData) {
  if (!this._detector) {
      this._createDetector().then(detector => {
          this._detector = detector;
          console.log("model loading success!, detector: ", this._detector);
      })
  }
  //...//
}
  • 의미, 기능
    • 정적 메서드
    • 클래스의 prototype이 아닌 함수 자체에 메서드를 설정 → 양자는 메서드 접근 방법에서 차이가 있음
const HandposeDetector = require('./detector-handpose');

// prototype에 메서드를 설정 : 생성자 호출 -> 메서드 호출  
const handposeDetector = new HandposeDetector();
handposeDetector.detect(imageData);

// 함수 자체에 메서드를 설정 : 클래스명.메서드명으로 메서드 호출
HandposeDetector.detect(imageData);
  • HandposeDetector.detect(imageData) 이 코드 같은 메서드 접근 방법에서도 알 수 있듯, 정적 메서드는 프로퍼티 형태로 직접 할당하는 것처럼 메서드를 정의
    (마치 프로퍼티에 접근하는 것처럼 메서드를 호출)
  • 정적 메서드도 상속됨. 따라서 자식클래스명.함수명 형태로 호출 가능
  • 사용 이유 : 데이터를 클래스 수준에 저장하고 싶을 때
    • 프로젝트에 적용
    • HandposeDetector, FaceDetector, PoseDetector 같은 커스텀 detector를 만들 때, Detector 클래스를 상속 받아 만들었음
    • 그래서 상속 받은 함수와 커스텀 detector 만의 함수를 구분 지을 필요 有, 따라서 초기에 static으로 구분
  • 정적 프로퍼티도 static 프로퍼티 정의 형태로 사용하면 됨

 


 

2. _함수명

  • 예시 코드
_createDetector() {
  const model = SupportedModels.MediaPipeHands;
  const detector = createDetector(model, {
      runtime: "tfjs",
      modelType: "lite",
      maxHands: 2, // or 2~10.
      flipHorizontal: false,
      staticImageMode: false,
      detectorModelUrl:
          "/static/tensorflow-models/tfjs-model_handpose_3d_detector_lite_1/model.json",
      landmarkModelUrl:
          "/static/tensorflow-models/tfjs-model_handpose_3d_landmark_lite_1/model.json",
  });
  return detector;
}
  • HandposeDetector, FaceDetector, PoseDetector 같은 커스텀 detector를 만들 때, Detector 클래스를 상속 받아 만들었음
  • 그래서 상속 받은 함수와 커스텀 detector 만의 함수를 구분 지을 필요 有
  • 코딩 초기 당시 static 키워드를 활용하여 구분하였음. 그러나 이 방법은 중간 중간 해당 함수 호출할 때, 클래스명.함수명 으로 호출해야하여 번거롭다는 단점
  • 따라서 사수께서 추천해주신 방법은 _함수명() 임. 위처럼 함수를 정의하고 호출할 때는 this._createDetector() 로 호출하면 됨

개요

Tensorflow-models에 속해 있는 모델들의 전형적인 사용 구조를 설명하려고 함. 미래의 나를 위해... 이하 모든 글과 코드는 근본적으로 Tensorflow-models 공식 깃헙에 출처가 있음.

 


 

1. model loading : createDetector

_createDetector() {
  const model = SupportedModels.MediaPipeHands;
  const detector = createDetector(model, {
      runtime: "tfjs",
      modelType: "lite",
      maxHands: 2, // or 2~10.
      flipHorizontal: false,
      staticImageMode: false,
      detectorModelUrl:
          "/static/tensorflow-models/tfjs-model_handpose_3d_detector_lite_1/model.json",
      landmarkModelUrl:
          "/static/tensorflow-models/tfjs-model_handpose_3d_landmark_lite_1/model.json",
  });

  console.log("model loading success!, detector: ", this._detector);
  return detector;
}

 


 

2. detect

  • detector.estimateHands(video or image)
  • estimateFaces 등 다양한 시리즈 있음
detect(imageData) {
  if (!this._detector) {
      this._createDetector().then(detector => {
          this._detector = detector;
          console.log("model loading success!, detector: ", this._detector);
      })
  }

  return new Promise((resolve, reject) => {   
      this._detector.estimateHands(imageData).then(result => {
          this._result = result;
          console.log(`this._result: `, this._result);

          this._result.forEach((res) => {
              console.log(`${res.handedness} hand keypoints:`);
              res.keypoints.forEach((keypoint, i) => {
                  let x = keypoint.x - 320;
                  let y = keypoint.y - 240;
                  console.log(`Keypoint ${i}: [${x}, ${y}]`);
              })
          })
          resolve(this._result);
      }).catch(e => {
          reject(e);
      });
  })
}

 


 

3. draw : draw(canvas) 등 매개 변수는 다양할 수 있음

  • drawKeypoint
  • drawPath
draw(canvas) {
    if (!canvas || !this.isExistContent(this._result)) return canvas;
    const ctx = canvas.getContext("2d");
    if (this._result) {
        this._result.forEach( (res) => {
            ctx.fillStyle = res.handedness === "Left" ? "Red" : "Blue";
            ctx.strokeStyle = "White";
            ctx.lineWidth = 2;
            res.keypoints.forEach(keypoint => {
                this._drawKeypoint(ctx, keypoint);
            });
            Object.keys(FINGER_INDICES).forEach(finger => {
                const points = FINGER_INDICES[finger].map(idx => res.keypoints[idx]);
                this._drawPath(ctx, points, false);
            });
        });
    }
    return canvas;
}

_drawKeypoint(ctx, keypoint) {
    ctx.beginPath();
    ctx.arc(keypoint.x - 2, keypoint.y - 2, 3, 0, 2 * Math.PI);
    ctx.fill();
}

_drawPath(ctx, points, closePath) {
    const region = new Path2D();
    region.moveTo(points[0].x, points[0].y);
    points.slice(1).forEach(point => region.lineTo(point.x, point.y));
    if (closePath) {
        region.closePath();
    }
    ctx.stroke(region);
}

 

 

문제 인식

      • 기능별로 브랜치를 만들어서 작업해야 한다고 하심 ( ex. handpose / face / pose | 각 경우에서 구체적으로 model / detect / draw )
        • 공통 사용할 npm 모듈 설치하여 feature/tfjs → 그 브랜치에서 feature/tfjs-handpose, feature/tfjs-face, feature/tfjs-pose로 뻗어나가기

브랜치 예시

  • 그러나 이미 작업 중인 파일이 있다면, 브랜치 생성하여 checkout 해도 계속 수정된 파일이 남아있다는 문제 + commit은 부담스러움
  • git stash를 사용하면 됨!

 


의미 (용도)

 

  • 임시 저장소
  • commit은 부담스럽고 checkout 해서 다른 브랜치에서 새로 작업하고 싶을 때, stash라는 임시 저장소에 저장하였다가 불러와서 사용하면 됨

 

사용법

  • stash 생성(임시 저장) : git stash (save)
    • save 적어도 되고 안 적어도 됨
    • 위 명령어 사용하여 stash 생성하고, 작업할 브랜치로 checkout 하면 됨
    • Node.js의 경우 package.json (내가 설치, 삭제한 npm 모듈 기록)까지 임시 저장됨
  • stash 저장 목록 확인 : git stash list
    • 임시 저장한 파일을 현재 내가 있는 브랜치로 불러오는 것임
    • stash 이름을 생략하면 가장 최근에 저장된 stash가 나옴 → LIFO: 후입선출, 스택의 자료 구조
  • stash 삭제: git stash drop (@stash{0})
    • stash 이름을 생략하면 가장 최근에 저장된 stash가 삭제됨

 

개요

회사에서 처음으로 프로젝트를 부여받았다. 손 이미지 좌표를 예측하는 AI를 개발하는 것이었고, 예측 과정은 다음과 같다. 

  1. 웹캠으로 손 영상이 들어옴
  2. 프레임 단위로 캡쳐된 이미지를 텐서* 형태로 변환
    * 텐서
     - 데이터가 3차 이상으로 들어있는 것; 3차원인 경우 x(row), y(column), z(depth)로 구성됨
     - 3차원 이상의 정보를 담을 수 있어 일반 테이블 구조보다 많은 정보를 담을 수 있음. 그러나 데이터 해석 시 데이터 구조에 대한 기초 지식이 요구됨
  3. 텐서 형태로 인식된 손의 좌표(x, y, z(깊이) 축)를 예측

위와 같은 과정을 수행하기 위해 차장님께 TensorflowHandpose, Handpose detection 라이브러리를 사용하면 된다고 전달받았다. 그러나  모르는 것 투성이었으므로 꼬리 문제 풀듯이 꼬리 개념으로 정리하고자 한다.

 


 

TensorFlow 란?

텐서플로(TensorFlow)는 구글(Google)에서 만든 데이터 흐름 프로그래밍을 위한 아파치 2.0 오픈소스 소프트웨어 라이브러리이다. 특히 머신러닝, 딥러닝 프로그램을 쉽게 구현할 수 있도록 해준다. 기본적으로 C++로 구현되어 있으며, Python, Java, Go 등 다양한 언어를 지원한다. 그러나 대부분의 편한 기능들이 Python 라이브러리로 구현되어 있어 언어로 개발하는 것이 편하다. 텐서플로의 라이브러리들은 깃헙에 정리 되어 있다. 이번 프로젝트를 수행하기 위해 나는 그중에서도 tfjs-models 라이브러리handpose, hand-pose-detection 패키지를 참고해야 했다. 그러나 각 패키지의 차이가 무엇인지, 둘 중 무엇을 더 중점적으로 사용해야 하는지 의문이 있었다.

 

Handpose 라이브러리 vs Handpose detection 패키지의 차이점
  Handpose Handpose detection
사용 예시  3D 손 부위 key points(landmarks) 감지를 위한 Media pipe hands 모델 구현  - Google's Real-time Hand pose Detection with Media pipe 모델 구현 
- TensorFlow Lite Micro Speech 예제에서 사용된 모델을 구현 
특징  - 손바닥, 손가락, 손목을 합친 21개의 key points 감지 
- 이미지 또는 비디오에서 손 추적 가능 
- 웹 어플리케이션에서 복잡한 3D key points 감지 기능이 필요한 경우 사용 
- 임베디드 시스템(마이크로 컨트롤러)에서 음성 명령과 함께 실시간으로 손 동작을 감지하는데에 사용하기 좋음

 

위와 같은 특징들로 인해 나는 프로젝트에서 Handpose 패키지를 중점적으로 사용하되, 웹캠 구현 등 기타  사항에서는 Handpose detection 패키지를 적극적으로 활용하기로 결정했다!

 


 

Human pose Estimation 이란?

현재 내가 하고 있는 프로젝트 내용, 텐서플로의 Handpose 및 Handpose detection 패키지는 모두 Human pose Estimation에 속한다.  이에 대한 이론적인 내용은 방대하므로 별도의 글에서 보다 구체적으로 정리하도록 하고, 이 글에서는 의미와 프로젝트와 관련된 내용만 간략하게 정리하고 넘어가고자 한다. 

 

의미

Human pose Estimation(& Tracking)은 Semantic key points*를 검출하고 ②key points 사이의 관련성을 찾고 ③지속적으로 추적하는 컴퓨터 비전**의 과제이다. 나는 Human pose Estimation 중에서도 첫 번째로 손 위치 추적에 대한 프로젝트를 부여받은 것이다. 명칭에서도 알 수 있듯 Human pose Estimation은 Pose Estimation의 한 종류이다. Pose Estimation(포즈 추정)은 컴퓨터 비전에서 유명한 과제 중 하나이다. 
  * Semantic key points : 의미 있는 점; 즉 사람 신체의 오른쪽 어깨, 왼쪽 무릎 등을 예로 들 수 있음
  **  컴퓨터 비전 (vision) : 사람의 시각을 모방하여 기계가 컴퓨터 영상을 처리할 수 있도록 도와줌

 

원리

대부분의 Pose Estimator는 2개 과정으로 위치를 추정한다. 

  • bounding box로 사람 또는 물체를 탐색한다.
  • 각 박스에서 탐색 대상의 key points를 찾아낸다. 사람의 경우 key points는 팔꿈치, 무릎, 손목 등이 될 수 있다. Single-pose estimation 의 경우 주어진 영상에서 하나의 물체의 pose를 추정하는 것이 목적이고, Multi-pose estimation의 경우 여러 물체를 탐지하는 것이 목적이 된다. 

 

Handpose 패키지를 이용한 AI 개발이 속하는 method의 범위

딥러닝의 빠른 발전으로 딥러닝 기반 Pose Estimation이 고전 방식에 비해 우월한 성능을 보이고 있으며 그 종류가 다양하다. 이번 프로젝트는 Node.js 기반 서버에서 동작하는 자사 제품에 추가될 기능이므로, 내 생각에는 Handpose 패키지를 이용한 프로젝트는 PoseNet 모델을 이용한 것이라고 볼 수 있을 것 같다. 

PoseNet
  - 경량으로 모바일 또는 웹 브라우저에서 작동하기 위한 모델 
  - TensorFlow.js 를 기반으로 구축된 사전 학습 모델

 

 


 

 

참고 자료

TensorFlow 란?

https://ko.wikipedia.org/wiki/%ED%85%90%EC%84%9C%ED%94%8C%EB%A1%9C

 

텐서플로 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 텐서플로(TensorFlow) 또는 텐서플로우는 다양한 작업에대해 데이터 흐름 프로그래밍을 위한 오픈소스 소프트웨어 라이브러리이다. 심볼릭 수학 라이브러리이자,

ko.wikipedia.org

https://excelsior-cjh.tistory.com/148

 

[러닝 텐서플로]Chap01 - 텐서플로 란?

Chap01 - 개요1.1 텐서플로 란? 텐서플로(TensorFlow)는 구글(Google)에서 만든, 딥러닝 프로그램을 쉽게 구현할 수 있도록 다양한 기능을 제공해주는 라이브러리다. 텐서플로 자체는 기본적으로 C++로 구

excelsior-cjh.tistory.com

 

Human pose Estimation 이란?

https://supermemi.tistory.com/entry/Human-Pose-Estimation-%EC%9D%B4%EB%9E%80-2022

 

Human Pose Estimation 이란? (2022)

Human Pose Estimation Ultimate Overview in 2022 Human Pose Estimation with Deep Learning - Ultimate Overview in 2022 - viso.ai Pose Estimation is a computer vision technique to predict and track the location of a person or object. List of use cases and arc

supermemi.tistory.com

https://viso.ai/deep-learning/pose-estimation-ultimate-overview/

 

Human Pose Estimation with Deep Learning - Ultimate Overview in 2023 - viso.ai

Pose Estimation is a computer vision technique to predict and track the location of a person or object. List of use cases and architectures.

viso.ai

 

+ Recent posts