포시코딩

디아블로4 경매 사이트 만들기 (5) - OpenCV.js Template Matching 본문

개인프로젝트/OCR

디아블로4 경매 사이트 만들기 (5) - OpenCV.js Template Matching

포시 2023. 6. 27. 02:32
728x90

개요

https://4sii.tistory.com/607

 

디아블로4 경매 사이트 만들기 (4) - Vue에서 Drag&Drop, Crop, Tesseract 사용하기

개요 지금까지 React를 사용했지만 더 다양한 언어를 경험하고자 Vue를 사용하기로 결정했다. 마침 비교적 최근에 Vue 3.0 업데이트가 있어서 기존과 다른 부분이 생겼는데 아래 링크를 통해 확인

4sii.tistory.com

위 글에서 이어진다.

지금까지 업로드한 사진에 대해 특정 부분을 crop하고 해당 부분에서 텍스트 인식하는 방법까지 알아봤다.

다만, 텍스트 전체를 인식하기엔 인식률이 떨어져서 

전처리 작업으로 보통 OpenCV를 통해 그레이스케일 작업을 거치기 때문에 

나도 마찬가지로 그레이스케일을 진행하기로 결정했다.

 

그레이스케일(GrayScale)이란?

8비트의 R.G.B(3채널)의 이미지를 1채널로 변화시키는 것을 의미

다시 말해, 색상 정보를 갖지 않고 0 ~ 255 밝기의 차이로 이미지를 변환시키는 것을 말하는데

3채널의 이미지가 1채널의 이미지로 변환이 되면 컴퓨터가 처리해야 할 계산이 줄어들기 때문에 

이미지, 영상을 처리하는 데 있어 그레이스케일을 활용하는 것이 좋다.

 

이진화(Binarization)란?

그레이스케일이 3채널의 이미지를 1채널로 바꾸는 거라면

이진화는 1채널의 이미지 0 ~ 255를 0 or 1로 바꿔주는 것이다.

쉽게 말해, 특정값(임계값)을 흑 또는 백으로 구분 짓는 것

보통 이미지나 영상에 등장하는 사물의 특징을 추출하기 위한 최적의 임계값을 찾기 위해 이진화를 진행한다.

 

블러링(Bluring)

추가로 블러링(또는 스무딩. Smoothing)을 진행하여 노이즈를 제거할 수 있지만 

나는 게임에서 찍은 스크린샷을 대상으로 진행할 것이기 때문에

제거할 노이즈가 따로 없기에 진행하지 않았다.

 

OpenCV란?

Open Source Computer Vision Library의 약자로

이미지, 영상 처리에 사용할 수 있는 오픈소스 라이브러리다.

이미지 처리, 객체 감지, 얼굴 인식, 동작 인식, 패턴 인식, 영상 분석 등의 작업에 활용되며

실시간 처리에 중점을 두고 설계되서 빠른 속도와 효율성을 자랑한다.

C++ 기반 언어지만 최근에는 파이썬의 대중화로 파이썬에서도 많이 사용되며

내 경우엔 OpenCV.js를 통해 javascript에서 사용할 것이다. 

 

OpenCV.js

https://docs.opencv.org/3.4/index.html

 

OpenCV: OpenCV modules

OpenCV  3.4.20-pre Open Source Computer Vision

docs.opencv.org

세팅

https://docs.opencv.org/3.4/d0/d84/tutorial_js_usage.html

 

OpenCV: Using OpenCV.js

Steps In this tutorial, you will learn how to include and start to use opencv.js inside a web page. You can get a copy of opencv.js from opencv-{VERSION_NUMBER}-docs.zip in each release, or simply download the prebuilt script from the online documentations

docs.opencv.org

우선 opencv.js를 파일로 받아야 한다.

https://docs.opencv.org/3.4.0/opencv.js

위 링크로 들어가면 검은 바탕에 흰색 글씨로 수 많은 코드가 나열될텐데 

우클릭 후 다른 이름으로 저장하기를 통해 내 프로젝트 폴더 내에 위치시키면 된다. 

이후 같은 위치에 index.html을 만들어 예제에 나온 그대로 진행해준다.

 

index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Hello OpenCV.js</title>
</head>
<body>
  <h2>Hello OpenCV.js</h2>
  <p id="status">OpenCV.js is loading...</p>
  <div style="display: flex;">
    <div class="inputoutput">
      <img id="imageSrc" alt="No Image" />
      <div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>
    </div>
    <div class="inputoutput">
      <canvas id="canvasOutput"></canvas>
      <div class="caption">canvasOutput</div>
    </div>
  </div>
  <script type="text/javascript">
    let imgElement = document.getElementById('imageSrc');
    let inputElement = document.getElementById('fileInput');
    inputElement.addEventListener('change', (e) => {
      imgElement.src = URL.createObjectURL(e.target.files[0]);
    }, false);
    imgElement.onload = function () {
      let mat = cv.imread(imgElement);
      cv.imshow('canvasOutput', mat);
      mat.delete();
    };
    var Module = {
      onRuntimeInitialized() {
        document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
      }
    };
  </script>
  <script async src="opencv.js" type="text/javascript"></script>
</body>
</html>

위 예제를 따라적고 실행해서 아무 이미지나 업로드 해보면 위 사진처럼 그대로 캔버스에 찍혀 나오게 된다.

둘 다 같다고 생각할 수 있지만 위에는 <img> 태그, 아래는 <canvas> 태그로 

opencv의 결과물은 이제부터 항상 canvas를 통해 출력될 것이다. 

 

이렇게 기본 베이스 준비가 끝났다. 

다른 처리 방법에 따라 여기서 코드를 추가하는 방식으로 진행된다.

 

그레이스케일

https://docs.opencv.org/3.4/df/d24/tutorial_js_image_display.html

 

OpenCV: Getting Started with Images

Goals Learn how to read an image and how to display it in a web. Read an image OpenCV.js saves images as cv.Mat type. We use HTML canvas element to transfer cv.Mat to the web or in reverse. The ImageData interface can represent or set the underlying pixel

docs.opencv.org

이번엔 그레이스케일 처리를 진행해보자

마찬가지로 위 예제를 따라하면 되는데 변수명이 달라지기도 하기 때문에

바로 따라하기 좋게 수정한 코드를 공유한다.

 

index.html

<script type="text/javascript">
  let imgElement = document.getElementById('imageSrc');
  let inputElement = document.getElementById('fileInput');
  inputElement.addEventListener('change', (e) => {
    imgElement.src = URL.createObjectURL(e.target.files[0]);
  }, false);

  imgElement.onload = function () {
    // let mat = cv.imread(imgElement);
    // cv.imshow('canvasOutput', mat);
    // mat.delete();
    let src = cv.imread(imgElement);
    let dst = new cv.Mat();
    cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);
    cv.imshow('canvasOutput', dst);
    src.delete();
    dst.delete();
  };

  var Module = {
    onRuntimeInitialized() {
      document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
    }
  };
</script>

여기서 src는 'source'로 원본 이미지 출처를,

dst는 'destination으로 목적지'를 의미한다.

 

cv.imread를 통해 원본 이미지를 src에 담고 cv.Mat()으로 결과를 담을 객체 dst를 만든 다음

cv.cvtColor를 통해 cv.COLOR_RGBA2GRAY 알고리즘을 적용한 결과를 dst에 담는다. 

 

이렇게 만들어진 결과를 cv.imshow를 통해 canvas에 뿌려주게 되는 과정이다. 

(여기서 imshow는 imageshow라는 뜻)

추가로, 공식 문서에서 처리를 완료한 후 과정에 쓰였던 객체들을 항상 delete() 해주는 것을 강조했다.

 

그렇게 나온 결과에 대해 테스트해보면 위와 같다.

 

여기까지 진행하고나서 나는 opencv의 다른 기능들에 대해서 궁금해졌고

모든 기능들을 확인하는 과정에서 흥미로운 기능을 찾을 수 있었다. 

 

템플릿 매칭

https://docs.opencv.org/3.4/d8/dd1/tutorial_js_template_matching.html

 

OpenCV: Template Matching

Goals Theory Template Matching is a method for searching and finding the location of a template image in a larger image. OpenCV comes with a function cv.matchTemplate() for this purpose. It simply slides the template image over the input image (as in 2D co

docs.opencv.org

템플릿 매칭은 큰 이미지에서 템플릿 이미지의 위치를 검색하여 찾는 방법이다. 

나는 이 기능을 활용하면 굳이 사용자가 직접 스크린샷에서 아이템의 상세 내용 부분을 crop하지 않아도 될 것 같다는 생각을 했다.

(템플릿 매칭의 경우 적용하기에 맞는 기능인지 먼저 테스트해야해서 공식 문서상의 테스트 코드를 통해 진행했다.)

 

(희귀)아이템은 노란색 테두리로 이루어져 있기에 미리 위 이미지와 같이 편집한 이미지를 템플릿으로 사용

 

그 결과 위와 같이 원하는 위치가 빨간 테두리로 잡히는걸 확인할 수 있었다.

이제 매칭된 이미지 내용을 tesseract를 통해 텍스트 인식하면 crop할 필요도 없어질 것이다.

다만, 추가로 확인하는 과정에서 문제가 여럿 발생했는데, 

 

마법 아이템에 대해 희귀 테두리로 매칭시킨 결과

첫번째, 등급이 다른 아이템일 경우 테두리 색이 달라져 제대로 위치를 잡지 못한다. 

하지만 이 문제는 보통 거래 되는 아이템은 희귀 아이템일 것이기 때문에 고려하지 않아도 될듯하다.

 

원본 이미지의 40% 사이즈와 비교한 결과

두번째, 비교대상 이미지의 크기가 다를 경우. 즉, 비교 템플릿 이미지와 원본 이미지에서 테두리 크기가 매칭되지 않을 경우인데

이 경우도 보통 게임 내 스크린샷 기능으로 생성된 이미지 파일 원본을 그대로 사용할 것이기 때문에 고려하지 않아도 될 것으로 보인다.

만약 고려한다면 opencv의 image pyramids 기능을 사용하면 될 것으로 보임

https://docs.opencv.org/3.4/d5/d0f/tutorial_js_pyramids.html

 

OpenCV: Image Pyramids

Goal Theory Normally, we used to work with an image of constant size. But on some occasions, we need to work with (the same) images in different resolution. For example, while searching for something in an image, like face, we are not sure at what size the

docs.opencv.org

 

세번째는 오류 상황이지만 사용성을 고려했을 때 문제 되지 않을 부분인 첫번째, 두번째와 다르게 

실제로 문제가 되는 부분인데 템플릿 테두리 이미지의 크기에 맞춰서 매칭되기 때문에 

비교 대상 아이템의 옵션 내용이 길면 나머지 옵션이 테두리를 벗어나 측정되지 않을 상황이 생긴다.

 

위 문제를 해결하기 위해 두가지 정도 테스트해봤다.

 

1. 테두리 이미지의 세로 크기를 최대한 늘리기

(진행중)

 

2. cv 함수 옵션에서 세로 크기 옵션 조정하기

//let point = new cv.Point(maxPoint.x + templ.cols, maxPoint.y + templ.rows);
let point = new cv.Point(maxPoint.x + templ.cols, {원본 이미지의 높이});

기존의 코드가 템플릿 이미지의 세로만큼 테두리 크기를 잡았다면, 

매칭이 시작되는 위치부터 이미지 하단까지 잡아주게 세팅되게끔 수정했다.

코드는 다음과 같다.

// html
<img id="templImageSrc" src="./contours.png" style="display: none;"/>

// script
let src = cv.imread(imgElement);
let testImgElement = document.getElementById('templImageSrc');
let templ = cv.imread(testImgElement);
let dst = new cv.Mat();
let mask = new cv.Mat();
cv.matchTemplate(src, templ, dst, cv.TM_CCOEFF, mask);
let result = cv.minMaxLoc(dst, mask);
let maxPoint = result.maxLoc;
let color = new cv.Scalar(255, 0, 0, 255);
// let point = new cv.Point(maxPoint.x + templ.cols, maxPoint.y + templ.rows);
let point = new cv.Point(maxPoint.x + templ.cols, imgElement.height);  // imgElement.height 대신 src.rows도 가능
cv.rectangle(src, maxPoint, point, color, 2, cv.LINE_8, 0);
cv.imshow('canvasOutput', src);
src.delete(); dst.delete(); mask.delete();

템플릿 이미지를 폴더 내 위치에서 javascript로 바로 불러와지지 않아 일단 html에 안보이게 세팅한 후 가져오는 방법을 택했다.

기존의 maxPoint.y + templ.rows 대신 원본 이미지의 높이를 뜻하는 imgElement.height (or src.rows)로 옵션을 수정하니

원하는 결과를 얻을 수 있었다. 

이제 해당 테두리 내의 이미지 데이터를 따로 잘라내어 tesseract로 텍스트 인식하기만 성공하면 될듯..!

 

+ 추가

맨 처음 아이디어가 떠올라 느긋하게 작업하던 중에 역시 비슷한 생각으로 거래 사이트를 만든 사람이 생겼네요. 

https://d4.tradurs.com/

 

거래 목록 - 디아블로® IV | 트레이더스

트레이더스에서 디아블로® IV에 사용하는 다양한 아이템을 거래해 보세요

d4.tradurs.com

찾아보니 디아2 레저렉션 관련 사이트를 운영중인 곳에서 만든듯한데 

굉장히 잘만든데다 귀찮은 작업까지 세세하게 세팅해놔서 옵션을 상세히 조절할 수가 있더군여

엄청난 노가다를 할 생각에 opencv 기능을 찾게된 저로썬 대단하단 생각이 먼저 드네요.

 

실제로 발견한 이후에 해당 사이트를 통해 거래를 몇번 해봤고 예전에 poe 거래 사이트 느낌이 나서 사용감이 굉장히 좋았습니다. 

디아에선 따로 애드온 사용을 허용하지 않기에 부득이하게 불편한 점이 몇가지 있었지만

업데이트 주기로 봤을 때 디아4가 꾸준히 흥행한다면 편의성 개선이 계속 될 것 같습니다. 

아이템 이미지 분석 기능이 추가됐지만 아직까진 원활하게 작동되진 않는 모양이다.

애초에 제 프로젝트는 이정도의 퀄리티를 갖춰야지 하고 시작한게 아니었지만

그래도 이정도 완성도를 따라가야지 하고 생각하면 굉장히 아득하네요.

 

하지만 주눅들지 않고 계속 처음 구상했던 결과물이 나올때까진 힘써볼 생각입니다. 

적어도 백엔드를 건들여보긴 해야죠

 

다음 포스팅에선 crop 기능 대신 opencv 템플릿 매칭으로 얻게된 이미지를 통해 텍스트 인식을 하는 과정까지 진행해볼 예정입니다. 

해당 기능이 완료되면 비로소 백엔드 작업에 들어갈듯!

 

Github

https://github.com/cchoseonghun/Practice/tree/main/opencv

 

GitHub - cchoseonghun/Practice

Contribute to cchoseonghun/Practice development by creating an account on GitHub.

github.com

 

참고 사이트

https://mooonstar.tistory.com/entry/Computer-VisionOpenCV%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A0%84%EC%B2%98%EB%A6%AC%EC%9D%B4%EC%A7%84%ED%99%94-%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%8A%A4%EC%BC%80%EC%9D%BC-%EB%B8%94%EB%9F%AC%EB%A7%81

 

[Computer Vision]OpenCV를 이용한 이미지 전처리(이진화, 그레이스케일, 블러링)

⚡️ OpenCV(Open Source Computer Vision) 개요 오픈 소스 컴퓨터 비전 라이브러리 중 하나로 실시간으로 이미지, 영상 프로세싱에 중점을 둔 라이브러리이다. 과거에는 C언어만을 지원하였지만, 현재 C++,

mooonstar.tistory.com

https://studium-anywhere.tistory.com/22

 

[OpenCV]OpenCV란 무엇인가? 그리고 설치

1. OpenCV OpenCV는 Open Source Computer Vision의 약자로 영상 처리에 사용할 수 있는 오픈 소스 라이브러리 입니다. 컴퓨터가 사람의 눈처럼 인식할 수 있게 처리해주는 역할을 하기도 하며, 우리가 많이

studium-anywhere.tistory.com

 

728x90