MediaPipe로 커스텀 객체 인식 웹 앱 만들기

1. 시작하기 전에

MediaPipe 솔루션을 사용하면 머신러닝(ML) 솔루션을 앱에 적용할 수 있습니다. 이 솔루션으로 제공되는 프레임워크를 통해 사용자에게 즉각적이고, 매력적이고, 유용한 출력을 제공하는 사전 빌드된 처리 파이프라인을 구성할 수 있습니다. 또한 Model Maker를 사용해서 솔루션을 맞춤설정하여 기본 모델을 업데이트할 수도 있습니다.

객체 인식은 MediaPipe 솔루션이 제공하는 여러 ML 버전 태스크 중 하나입니다. MediaPipe 태스크는 Android, Python, 웹용으로 제공됩니다.

이 Codelab에서는 이미지 및 라이브 웹캠 동영상에서 개를 인식하기 위해 웹 앱에 객체 인식을 추가합니다.

학습할 내용

빌드할 항목

  • 개가 있는지 인식하는 웹 앱. 또한 MediaPipe Model Maker를 사용해서 원하는 종류의 객체를 인식하도록 모델을 맞춤설정할 수도 있습니다.

필요한 항목

  • CodePen 계정
  • 웹브라우저 지원 기기
  • JavaScript, CSS, HTML에 대한 기본 지식

2. 설정

이 Codelab에서는 브라우저에서 코드를 작성하고 빌드 결과를 확인할 수 있게 해주는 소셜 개발 환경인 CodePen에서 코드를 실행합니다.

시작하려면 다음 단계를 수행합니다.

  1. CodePen 계정에서 CodePen으로 이동합니다. 이 코드를 시작 지점으로 사용해서 자체 객체 인식기를 만들 수 있습니다.
  2. 탐색 메뉴의 CodePen 아래에서 포크를 클릭하여 시작 코드를 복사합니다.

포크 버튼이 있는 CodePen의 탐색 메뉴

  1. JS 탭에서 f079bd83ad4547c9.png 펼치기 화살표를 클릭한 후 JavaScript 편집기 최대화를 선택합니다. 이 Codelab에 대해 JS 탭에서만 작업을 편집하기 때문에 HTML 또는 CSS 탭을 볼 필요가 없습니다.

시작 앱 검토

  1. 미리보기 창에 두 개의 개 이미지와 웹캠 실행 옵션이 있습니다. 이 튜토리얼에서 사용하는 모델은 2개 이미지에 표시된 세 마리의 개를 이용해서 학습되었습니다.

시작 코드에서 웹 앱 미리보기

  1. JS 탭에서 코드 전반에 걸쳐 여러 주석이 있는 것을 볼 수 있습니다. 예를 들어 15행에서 다음 주석을 찾을 수 있습니다.
// Import the required package.

이러한 주석은 코드 스니펫을 삽입할 위치를 알려줍니다.

3. MediaPipe 태스크-비전 패키지 가져오기와 필요한 변수 추가

  1. JS 탭에서 MediaPipe tasks-vision 패키지를 가져옵니다.
// Import the required package.
​​import { ObjectDetector, FilesetResolver, Detection } from "https://cdn.skypack.dev/@mediapipe/tasks-vision@latest";

이 코드는 Skypack 콘텐츠 전송 네트워크(CDN)를 사용하여 패키지를 가져옵니다. CodePen과 함께 Skypack을 사용하는 방법에 대한 자세한 내용은 Skypack + CodePen을 참조하세요.

프로젝트에서 npm이나 패키지 관리자 또는 원하는 CDN과 함께 Node.js를 사용할 수 있습니다. 설치해야 하는 필수 패키지에 대한 자세한 내용은 JavaScript 패키지를 참조하세요.

  1. 객체 인식기 및 실행 모드에 대해 변수를 선언합니다.
// Create required variables.
let objectDetector: ObjectDetector;
let runningMode = "IMAGE";

runningMode 변수는 이미지에서 객체를 인식할 경우 "IMAGE" 값으로 설정되고, 동영상에서 객체를 인식할 경우 "VIDEO"로 설정되는 문자열입니다.

4. 객체 인식기 초기화

  • 객체 인식기를 초기화하려면 JS 탭에서 관련 주석 다음에 아래 코드를 추가합니다.
// Initialize the object detector.
async function initializeObjectDetector() {
  const visionFilesetResolver = await FilesetResolver.forVisionTasks(
    "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
  );
  objectDetector = await ObjectDetector.createFromOptions(visionFilesetResolver, {
    baseOptions: {
      modelAssetPath: "https://storage.googleapis.com/mediapipe-assets/dogs.tflite"
    },
    scoreThreshold: 0.3,
    runningMode: runningMode
  });
}
initializeObjectDetector();

FilesetResolver.forVisionTasks() 메서드는 태스크에 대한 WebAssembly(Wasm) 바이너리 위치를 지정합니다.

ObjectDetector.createFromOptions() 메서드는 객체 인식기를 인스턴스화합니다. 인식에 사용되는 모델의 경로를 제공해야 합니다. 여기에서 개 인식 모델은 Cloud Storage에 호스팅됩니다.

scoreThreshold 속성은 0.3 값으로 설정됩니다. 즉, 모델이 신뢰도 30% 이상으로 인식된 모든 객체의 결과를 반환합니다. 앱의 요구에 따라 이 임곗값을 조정할 수 있습니다.

runningMode 속성은 ObjectDetector 객체를 초기화할 때 설정됩니다. 이 옵션과 다른 옵션은 필요에 따라 나중에 변경할 수 있습니다.

5. 이미지에서 예측 실행

  • 이미지에 대해 예측을 실행하려면 handleClick() 함수로 이동한 후 함수 본문에 다음 코드를 추가합니다.
// Verify object detector is initialized and choose the correct running mode.
if (!objectDetector) {
    alert("Object Detector still loading. Please try again");
    return;
  }

  if (runningMode === "VIDEO") {
    runningMode = "IMAGE";
    await objectDetector.setOptions({ runningMode: runningMode });
  }

이 코드는 객체 인식기가 초기화되는지 여부를 결정하고 실행 중인 모드가 이미지용으로 설정되었는지 확인합니다.

객체 인식

  • 이미지에서 객체를 인식하려면 handleClick() 함수 본문에 다음 코드를 추가합니다.
// Run object detection.
  const detections = objectDetector.detect(event.target);

다음 코드 스니펫에는 이 태스크의 출력 데이터 예시가 포함됩니다.

ObjectDetectionResult:
 Detection #0:
  Box: (x: 355, y: 133, w: 190, h: 206)
  Categories:
   index       : 17
   score       : 0.73828
   class name  : aci
 Detection #1:
  Box: (x: 103, y: 15, w: 138, h: 369)
  Categories:
   index       : 17
   score       : 0.73047
   class name  : tikka

예측 처리 및 표시

  1. handleClick() 함수 본문 끝에서 displayImageDetections() 함수를 호출합니다.
// Call the displayImageDetections() function.
displayImageDetections(detections, event.target);
  1. displayImageDetections() 함수 본문에서 객체 인식 결과를 표시하도록 다음 코드를 추가합니다.
// Display object detection results.

  const ratio = resultElement.height / resultElement.naturalHeight;

  for (const detection of detections) {
    // Description text
    const p = document.createElement("p");
    p.setAttribute("class", "info");
    p.innerText =
      detection.categories[0].categoryName +
      " - with " +
      Math.round(parseFloat(detection.categories[0].score) * 100) +
      "% confidence.";
    // Positioned at the top-left of the bounding box.
    // Height is that of the text.
    // Width subtracts text padding in CSS so that it fits perfectly.
    p.style =
      "left: " +
      detection.boundingBox.originX * ratio +
      "px;" +
      "top: " +
      detection.boundingBox.originY * ratio +
      "px; " +
      "width: " +
      (detection.boundingBox.width * ratio - 10) +
      "px;";
    const highlighter = document.createElement("div");
    highlighter.setAttribute("class", "highlighter");
    highlighter.style =
      "left: " +
      detection.boundingBox.originX * ratio +
      "px;" +
      "top: " +
      detection.boundingBox.originY * ratio +
      "px;" +
      "width: " +
      detection.boundingBox.width * ratio +
      "px;" +
      "height: " +
      detection.boundingBox.height * ratio +
      "px;";

    resultElement.parentNode.appendChild(highlighter);
    resultElement.parentNode.appendChild(p);
  }

이 함수는 이미지에서 인식된 객체 주위에 경계 상자를 표시합니다. 이전 강조 표시를 없애고 인식된 각 객체를 강조 표시하는 <p> 태그를 만들고 표시합니다.

앱 테스트

CodePen에서 코드를 변경하면 저장 시 미리보기 창이 자동으로 새로고침됩니다. 자동저장이 사용 설정된 경우 앱이 이미 새로고침되었을 가능성이 있지만 다시 새로고침하는 것이 좋습니다.

앱을 테스트하려면 다음 단계를 수행합니다.

  1. 미리보기 창에서 각 이미지를 클릭하여 예측을 확인합니다. 경계 상자에 모델 신뢰도 수준과 함께 개 이름이 표시됩니다.
  2. 경계 상자가 없으면 Chrome DevTools를 연 후 콘솔 패널에서 오류를 확인하거나 이전 단계를 검토하여 누락된 것이 없는지 확인합니다.

이미지에 인식된 개 위로 경계 상자가 표시된 웹 앱 미리보기

6. 라이브 웹캠 동영상에서 예측 실행

객체 인식

  • 라이브 웹캠 동영상에서 객체를 인식하려면 predictWebcam() 함수로 이동한 후 다음 코드를 함수 본문에 추가합니다.
// Run video object detection.
  // If image mode is initialized, create a classifier with video runningMode.
  if (runningMode === "IMAGE") {
    runningMode = "VIDEO";
    await objectDetector.setOptions({ runningMode: runningMode });
  }
  let startTimeMs = performance.now();

  // Detect objects with the detectForVideo() method.
  const detections = await objectDetector.detectForVideo(video, startTimeMs);

  displayVideoDetections(detections);

스트리밍 데이터 또는 완전한 동영상에서 추론을 실행하는지 여부에 관계없이 동영상에 대한 객체 인식에는 동일한 메서드가 사용됩니다. detectForVideo() 메서드는 사진에 사용되는 detect() 메서드와 비슷하지만 현재 프레임과 연관된 타임스탬프에 대한 추가 매개변수가 포함되어 있습니다. 함수가 실시간으로 인식을 수행하므로 현재 시간을 타임스탬프로 전달합니다.

예측 처리 및 표시

  • 인식 결과를 처리하고 표시하려면 displayVideoDetections() 함수로 이동한 후 다음 코드를 함수 본문에 추가합니다.
//  Display video object detection results.
  for (let child of children) {
    liveView.removeChild(child);
  }
  children.splice(0);

  // Iterate through predictions and draw them to the live view.
  for (const detection of result.detections) {
    const p = document.createElement("p");
    p.innerText =
      detection.categories[0].categoryName +
      " - with " +
      Math.round(parseFloat(detection.categories[0].score) * 100) +
      "% confidence.";
    p.style =
      "left: " +
      (video.offsetWidth -
        detection.boundingBox.width -
        detection.boundingBox.originX) +
      "px;" +
      "top: " +
      detection.boundingBox.originY +
      "px; " +
      "width: " +
      (detection.boundingBox.width - 10) +
      "px;";

    const highlighter = document.createElement("div");
    highlighter.setAttribute("class", "highlighter");
    highlighter.style =
      "left: " +
      (video.offsetWidth -
        detection.boundingBox.width -
        detection.boundingBox.originX) +
      "px;" +
      "top: " +
      detection.boundingBox.originY +
      "px;" +
      "width: " +
      (detection.boundingBox.width - 10) +
      "px;" +
      "height: " +
      detection.boundingBox.height +
      "px;";

    liveView.appendChild(highlighter);
    liveView.appendChild(p);

    // Store drawn objects in memory so that they're queued to delete at next call.
    children.push(highlighter);
    children.push(p);
  }

이 코드는 이전 강조 표시를 없애고 인식된 각 객체를 강조 표시하는 <p> 태그를 만들고 표시합니다.

앱 테스트

라이브 객체 인식을 테스트하려면 모델이 학습된 개들 중 하나의 이미지를 준비하는 것이 좋습니다.

앱을 테스트하려면 다음 단계를 수행합니다.

  1. 개 사진 중 하나를 휴대폰에 다운로드합니다.
  2. 미리보기 창에서 웹캠 사용 설정을 클릭합니다.
  3. 브라우저에 웹캠 액세스 권한을 부여해달라는 대화상자가 표시되면 권한을 부여합니다.
  4. 웹캠 앞에 휴대폰 개 사진을 비춥니다. 경계 상자에 모델 신뢰도 수준과 함께 개 이름이 표시됩니다.
  5. 경계 상자가 없으면 Chrome DevTools를 연 후 콘솔 패널에서 오류를 확인하거나 이전 단계를 검토하여 누락된 것이 없는지 확인합니다.

라이브 웹캠에서 개 이미지 위에 표시된 경계 상자

7. 축하합니다

수고하셨습니다. 이미지에서 객체를 인식하는 웹 앱을 빌드했습니다. 자세한 내용은 CodePen에서 완료된 앱 버전을 참조하세요.

자세히 알아보기