정보/R&E

OpenCV-python를 이용하여 Unrailed! 윈도우 창 캡쳐하기

MinseobKim 2021. 7. 14. 23:20

이전 글 : 2021 정보 R&E 주제 소개 https://minseob.tistory.com/6

 

게임을 플레이하는 ai를 만들려면 가장 먼저 게임 화면을 실시간으로 캡처해서 ai가 인식할 수 있도록 해야 한다.

이를 수행해주는 것이 바로 OpenCV이다.

 

OpenCV는 Open Source Computer Vision의 약자로 실시간으로 이미지를 프로세싱하는 라이브러리이다.

 

파이썬 파일은 window화면을 캡처해주는 class가 담긴 window.py와 main.py 두 개를 만든다.

Unrailed! 창을 캡처하기 위해서는 OpenCV 뿐만 아니라 win32gui, pyautogui, numpy 라이브러리가 필요하다.

 

이들 라이브러리를 설치한 후 import 해준다.

import win32gui
import numpy as np
import cv2
import pyautogui

 

그다음 window.py에서 WindowCapture라는 클래스를 선언한다.

생성자는 캡처할 윈도우 창의 이름(window_name)과 1초에 캡처할 이미지수(capture_rate)를 전달받는다.

class WindowCapture:
    def __init__(self,window_name,capture_rate):
        self.window_name = window_name
        self.wait_time = 1/capture_rate
        self.frame=self.screenshot()

WindowCapture클래스 안의 screenshot메서드에서는 handle을 이용해 각 window창들을 고유한 번호로 찾는다.

만약 윈도우창을 찾지 못할 경우 예외처리도 해준다.

hwnd = win32gui.FindWindow(None, self.window_name)
        if not hwnd:
            raise Exception('Window not found: ' + self.window_name)

 그다음 hwnd의 윈도우의 작업 영역 크기를 win32gui.GetClientRect(hwnd) 함수를 통해 제공받는다.

*GetClientRect() : 윈도우의 작업 영역 크기를 계산해주는 함수다. left, top은 항상 0이면서 나머지 두 좌표는 right, bot이다. (곧 그리기 영역의 크기가 된다.)

 

그리고 win32gui.ClientToScreen(hwnd, (left, top)) 함수를 통해 게임 클라이언트의 (0,0) 좌표를 화면상의 좌표로 변환하여 x, y에 저장한다.

*ClientToScreen(): 클라이언트 영역의 좌표를 화면 좌표로  변환하는 함수이다.

left, top, right, bot = win32gui.GetClientRect(hwnd)
x, y = win32gui.ClientToScreen(hwnd, (left, top))

마지막으로 screenshot메서드 안에서 pyautogui.screenshot()으로 화면 스크린샷 이미지 객체를 반환하는데

이때 region변수에 시작 좌표와 끝 좌표를 넣어 어느 영역을 캡처할지 인자를 넘겨준다.

그리고 OpenCV에서는 일반적인 window화면처럼 RGB 색상 체계가 아닌 BGR색상 체계를 사용하므로

cv2.cvtColor()으로 변환해준다.

 

이를 종합하면 다음과 같은 return문이 나온다.

return cv2.cvtColor(
            np.asarray(
                pyautogui.screenshot(
                    region=(x, y,
                            *win32gui.ClientToScreen(hwnd, (right - x, bot - y))))), cv2.COLOR_RGB2BGR)

 

이를 종합하면 window.py 소스코드는 다음과 같다.

import win32gui
import numpy as np
import cv2
import pyautogui

class WindowCapture:
    def __init__(self,window_name,capture_rate):
        self.window_name = window_name
        self.wait_time = 1/capture_rate

        self.frame=self.screenshot()
    def screenshot(self):
        hwnd = win32gui.FindWindow(None, self.window_name)
        if not hwnd:
            raise Exception('Window not found: ' + self.window_name)

        left, top, right, bot = win32gui.GetClientRect(hwnd)
        x, y = win32gui.ClientToScreen(hwnd, (left, top))
        return cv2.cvtColor(
            np.asarray(
                pyautogui.screenshot(
                    region=(x, y,
                            *win32gui.ClientToScreen(hwnd, (right - x, bot - y))))), cv2.COLOR_RGB2BGR)

 

다음으로는 main.py 소스코드를 살펴보자.

 

만들었던 window.py의 클래스의 객체를 만들어야 하므로 window를 import 해주고

화면 캡처에 쓰일 opencv, win32gui, time 라이브러리도 import 한다.

import win32gui
import numpy as np
import cv2
import pyautogui

esc키의 아스키코드값, 프레임률 등을 저장할 상수를 선언한다.

ESC_KEY=27
FRAME_RATE = 120
SLEEP_TIME = 1/FRAME_RATE

capture에 WindowCapture클래스의 인스턴스를 저장한다.

이때 캡처할 window창의 이름인 "Unrailed!"와 FRAME_RATE를 넘겨준다.

capture = window.WindowCapture("Unrailed!",FRAME_RATE)

무한 반복문 안에서 capture.screenshot() 메서드를 실행하고 반환 값을 frame변수에 저장한다.

그 후 OpenCV의 imshow() 함수로 캡처한 이미지를 출력한다.

 

time()으로 시간 값을 저장받아 SLEEP_TIME만큼 딜레이를 준다.

 

또한 esc키가 눌리면 창이 닫히도록 key변수에 cv2.waitKey(1) & 0xFF 를 저장한다.

이때 waitKey()과 0xFF를 비트AND연산시키는 이유는 waitKey()의 반환 값의 하위 8비트 만을 저장하기 위해서이다.

만약 key에 esc키의 아스키코드가 입력되면 while문을 빠져나가 프로그램이 종료되게 된다.

 

이를 종합하면 main.py 소스코드는 다음과 같다.

import window
import cv2
import time
import win32gui

ESC_KEY=27
FRAME_RATE = 120
SLEEP_TIME = 1/FRAME_RATE

capture = window.WindowCapture("Unrailed!",FRAME_RATE)

while True:
    start=time.time()
    frame = capture.screenshot()
    cv2.imshow("frame1",frame)
    delta= time.time()-start
    if delta <SLEEP_TIME:
        time.sleep(SLEEP_TIME-delta)
    key= cv2.waitKey(1) & 0xFF
    if key== ESC_KEY:
        break

 

이제 만든 소스코드의 실행결과를 살펴봐야 한다.

Unrailed! 창을 연 상태로 main.py를 실행하면 위 사진과 같이 "frame1"이라는 이름의 창이 생성된다.

(오른쪽 위의 화면이 실제 게임 화면, 오른쪽 아래의 화면이 게임 화면을 캡처하여 출력한 창이다.)

 

위 gif처럼 실시간으로 Unrailed! 화면을 캡처하여 출력해준다.

 

'정보 > R&E' 카테고리의 다른 글

Rainbow DQN 강화학습 알고리즘 적용하기  (0) 2022.03.25
OpenCV image를 grid-based tiles로 변환하기  (0) 2021.08.30
2021 정보 R&E 주제 소개  (0) 2021.07.13