본문 바로가기

AI & 딥러닝/프로젝트

google Mediapipe(hand detection) + React.js (feat. AI 수화 번역 실시간 채팅 서비스 [1])

반응형

 

 

제 블로그에서 가장 인기 좋은 글은 아래의 'AI 수화번역 프로젝트' 인데요 🙌
 
[지난 글] https://eonhwa-theme.tistory.com/45

 

[딥러닝프로젝트_AI수화번역기 ]  최종결과물

그동안 이것저것 할게 많아서 프로젝트는 11월에 끝났는데 정리를 못했다. 최종적으로 2021 혁신성장 청년인재 집중 양성 사업 "인공지능 개발자 양성 과정 최종 프로젝트" 에서 부산대표 1등을

eonhwa-theme.tistory.com

 
첫 직장을 가기 전에 했던 프로젝트이니 지금은 4년이 지난 결과물입니다 ...ㅎ하하 
그동안 직장생활을 살면서 코드를 볼 겨를이 없었는데요. 사실 웹쪽은 부트스트랩 템플릿으로 빠르게 만들었고 ai 가 더 주였기 때문에 (현재는 프론트엔드 개발자) 더욱 신경을 쓸 시간이 없었기도 했습니다.. 
도움을 달라는 댓글들이 올라올 때마다 저도 기억이 가물가물한 코드를 선뜻 드리기는 너무 무책임하고, 그때 내가 고생하면서 했던 결과를 의미없게 드리고 싶지 않았습니다 ㅠㅠ 그리고 구현에만 신경 쓴 코드라 부끄럽네요 (게으른 탓에..죄송합니다..)
 
이번에는 react 를 공부도 해볼겸 나의 오래된 프로젝트를 다듬어 완성도 있게 리팩토링 해 볼려고 합니다.
물론 그 과정은 블로그에 세세하게 적어서 도움이 되는 한 열심히 작성할 겁니다.
기존의 기획의도도 조금 변경해서 서비스를 출시까지 해볼려고 합니다. (할 수 있겠지...? )
 
1차적인 목표는 '빠른 초기 제품 제작' 입니다. 언제나 그렇듯 개인 프로젝트는 시간이 딜레이 되고 오래 질질 끌수록 완성을 못하고 흐지부지 되는 경우가 많은....윽
더욱이나 하나가 안풀리면 끝까지 물어지는 성격이라 진도가 너무 안나가고 금방 지쳐버려서 ... 하면서 수정사항 혹은 개선점은 틈틈히 메모로만!! 적어두고 완성을 빠르게 하는 걸로 목표를 잡아야겠어요 
수정하면서 디벨롭하는 재미도 있으니.. 욕심내지 말자.
 
이전의 단방향에서만 끝났던 화상캠에 조금  더. 업그레이드를 해서 zoom 형식으로 양방향 화상 채팅으로 만들어 볼려고 합니다.
 
그리하여 업그레이드 된 실시간 수화 감지 시스템을 개발하면서 겪은 과정을 공유하고자 합니다. 이번편은 MediaPipe Hands와 React, TypeScript를 활용하여 웹캠을 통한 실시간 손동작 감지 및 시각화를 구현했습니다.

 
 
 
 
 


일단 먼저, 화면단의 구현부터 시작해봅시다.
React.js 로 프론트엔드 쪽을 구현하고 Mediapipe 를 연동하여 손 동작 인식 프로토타입까지 구축을 해봅시다.
 

기술 스택

Frontend : React.js + TypeScript
   - WebRTC : 화상 채팅 구현
   - Socket.IO : Signaling 서버 구축
AI 모델
   - Mediapipe + 딥러닝(문장 번역 모델)

 

여기서 MediaPipe는 Google에서 개발한 크로스 플랫폼 머신러닝 솔루션 프레임워크입니다.
MediaPipe에는 얼굴 감지, 포즈 추정, 손 인식, 객체 감지 등과 같은 일부 사전 훈련된 ML 솔루션이 제공됩니다.

 
`MediaPipe Hands` 는 21개의 3D 랜드마크 포인트로 손을 감지하며,  각 손가락 관절과 손바닥의 위치를 추적하고
최대 2개의 손을 동시에 감지 가능합니다.
해당 프로젝트의 실시간 웹캠 스트림에서 손 동작을 감지하고 시작화하는데 사용합니다.

 
 
당시에도 니콜라스 유튜브보고 많이 도움받았는데 react 로 구현하는 영상도 있네요!??!
https://www.youtube.com/watch?v=f7uBsb-0sGQ

MediaPipe 공식 문서는 CDN 방식을 주로 설명하고 있지만, TypeScript/React 프로젝트에서는 npm 패키지 방식이 더 많이 사용 되는 것 같습니다. 
공홈의 demo 코드처럼 이전프로젝트 당시에도 openCV 로 웹캠을 구현하고 cdn 방식으로 구현했었는데,  npm 패키지 사용이 더 TypeScript와의 호환성이 좋고, 모듈 관리가 편리한 것 같습니다.
 

 
 
 
 
 
 


1.  react 앱 생성하기

[참고 react 공식 홈] https://react.dev/learn/creating-a-react-app

 

Creating a React App – React

The library for web and native user interfaces

react.dev

 

1️⃣ 먼저, 리액트프로젝트를 생성해줍니다.

npx create-react-app 프로젝트명
cd 프로젝트명
npm start
  • Install "react": "^19.0.0"
  • node v22.14.0 (npm v10.9.2)

 
 
 

2.  react 웹캠 구현 + mediapipe 

1️⃣ React에서 웹캠을 처리하기 위한 패키지를 설치한다.

[참고 react-webcam npm 패키지] https://www.npmjs.com/package/react-webcam

 

react-webcam

React webcam component. Latest version: 7.2.0, last published: a year ago. Start using react-webcam in your project by running `npm i react-webcam`. There are 323 other projects in the npm registry using react-webcam.

www.npmjs.com

 

Install

# with npm
npm install react-webcam

# with yarn
yarn add react-webcam

 
 
 

2️⃣ MediaPipe 관련 패키지를 설치한다.

npm i @mediapipe/hands @mediapipe/camera_utils @mediapipe/drawing_utils

 

`@mediapipe/hands`는 `@mediapipe/camera_utils`필수입니다.
`@mediapipe/drawing_utils`은 캔버스에 그리기를 쉽게 만드는 것이므로 필요에 따라 넣습니다.
 

import Webcam from "react-webcam";          // 웹캠 접근 및 제어
import { Camera } from "@mediapipe/camera_utils";  // MediaPipe 카메라 유틸리티
import { Hands, Results } from "@mediapipe/hands"; // 손 감지 ML 모델

 
 

 ❇️ 주요 상태 관리

import React, { useRef, useEffect } from 'react';
import styled from 'styled-components';

//styled-components를 사용한 스타일링

const CameraDetection = () => {
    const webcamRef = useRef<Webcam>(null); // 웹캠 참조
    const canvasRef = useRef<HTMLCanvasElement>(null); // 캔버스 참조
    const resultsRef = useRef<Results>(null); // 손 감지 결과 저장
    
    
    ...
}
  

export default CameraDetection;

 
 

 ❇️ 손 감지 결과 처리

const onResults = useCallback((results: Results) => {
  resultsRef.current = results;  // 결과 저장
  const canvasCtx = canvasRef.current!.getContext("2d")!;
  🔥 drawCanvas(canvasCtx, results);   // 캔버스에 결과 시각화
}, []);



// 🔥drawCanvas 는 파일로 별도 분리

 

🔥 MediaPipe Hands를 사용하여 손 인식 결과를 캔버스에 시각적으로 표시하는 함수

drawCanvas.ts 파일은 MediaPipe Hands를 사용하여 손 인식 결과를 캔버스에 시각적으로 표시하는 함수

import { drawConnectors, drawLandmarks } from "@mediapipe/drawing_utils";
import { HAND_CONNECTIONS, Results } from "@mediapipe/hands";

export const drawCanvas = (ctx: CanvasRenderingContext2D, results: Results) => {
  const width = ctx.canvas.width;
  const height = ctx.canvas.height;

  ctx.save();
  ctx.clearRect(0, 0, width, height);
  // canvas의 좌우 반전 : 사용자가 자연스럽게 손동작을 확인할 수 있도록 거울 효과를 추가
  ctx.scale(-1, 1);
  ctx.translate(-width, 0);
  // capture image 그리기
  ctx.drawImage(results.image, 0, 0, width, height);
  // 손의 묘사
  if (results.multiHandLandmarks) {
    // 골격 묘사
    for (const landmarks of results.multiHandLandmarks) {
      // 손가락 관절을 연결하는 선 그리기 (초록색)
      drawConnectors(ctx, landmarks, HAND_CONNECTIONS, {
        color: "#00FF00",
        lineWidth: 5,
      });
      // 각 랜드마크 포인트 그리기 (빨간색)
      drawLandmarks(ctx, landmarks, {
        color: "#FF0000",
        lineWidth: 1,
        radius: 5,
      });
    }
  }
  ctx.restore();
};

 

 
❇️ MediaPipe Hands 초기화

useEffect(() => {
  const hands = new Hands({
    locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`,
  });

  hands.setOptions({
    maxNumHands: 2,              // 최대 2개의 손 감지
    modelComplexity: 1,          // 모델 복잡도 (1: 높음)
    minDetectionConfidence: 0.5,  // 최소 감지 신뢰도
    minTrackingConfidence: 0.5   // 최소 추적 신뢰도
  });

    hands.onResults(onResults);

    // MediaPipe Camera 설정
    if (typeof webcamRef.current !== "undefined" && webcamRef.current !== null) {
      const camera = new Camera(webcamRef.current.video!, {
        onFrame: async () => {
          await hands.send({ image: webcamRef.current!.video! });
        },
        width: 1920, // 증가된 해상도
        height: 1080, // 증가된 해상도
      });
      camera.start();
    }
  }, [onResults]);

 

❇️  화면에 그리기

return (
    <div className={styles.container}>
      {/* capture */}
      <Webcam
        audio={false}
        style={{ visibility: "hidden" }}
        width={1920} // 증가된 해상도
        height={1080} // 증가된 해상도
        ref={webcamRef}
        screenshotFormat="image/jpeg"
        videoConstraints={videoConstraints}
      />
      {/* draw */}
      <canvas
        ref={canvasRef}
        className={styles.canvas}
        width={1920} // 캔버스 해상도도 맞춤
        height={1080} // 캔버스 해상도도 맞춤
      />
     ...
    </div>
  );

 

 

❇️  실행 화면

화질도 좋고 ~ 웹캠으로 MediaPipe Hands와 React, TypeScript를 활용하여 구현을 했습니다.

 
 

 
 


 
퇴근하고 또 다시 컴퓨터 앞에 앉아서 코드를 작성하기 쉽지 않다..새해에 다짐한거 이제야 블로그로 작성하게 되었네요... 

 
 
다음은 webRTC 를 이용해서 양방향 커뮤니케이션이 가능하게 구현해볼려고 합니다. 
 
 
 
 


+ 추가 참고사이트
https://github.com/ayushgdev/MediaPipeCodeSamples/blob/main/React/src/MPHands.tsx

 

MediaPipeCodeSamples/React/src/MPHands.tsx at main · ayushgdev/MediaPipeCodeSamples

This repository contains mediapipe solutions sample codes for getting started - ayushgdev/MediaPipeCodeSamples

github.com

 

728x90
반응형