OpenCV를 활용한 화면 감지 프로그램
메이플엔 물약 봉인 몬스터, 일명 물봉몹이 존재한다
체력이 일정 수치 이하로 떨어지면 자동으로 포션을 먹는데,
해당 몬스터는 포션 사용 금지 + 체비뎀 10%를 넣기때문에 사냥터에서의 잠수를 막는다.
그렇기에 물봉몹이 등장하면 이를 감지하여 알람을 울려주는 프로그램을 만들어봤다.
어떤 언어를 사용할까 고민하다가... 학부시절 배운 OpenCV가 생각나 이를 활용해보기로 결정
def capture_screen():
screenshot = pyautogui.screenshot()
screen = np.array(screenshot)
screen = cv2.cvtColor(screen, cv2.COLOR_RGB2BGR)
return screen
화면 캡쳐 함수.
pyautogui가 제공하는 screenshot() 함수로 스크린샷을 찍고
이를 numpy 배열로 변환
게임 이미지는 RGB이지만 Opencv는 BGR을 사용하므로 변환해서 리턴해준다.
def is_image_on_screen(template, screen, threshold):
result = cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED)
_, max_val, _, _ = cv2.minMaxLoc(result)
return max_val > threshold
특정 이미지가 화면에 존재하는지 확인하는 함수
threshold는 임계값으로, 높을수록 더 정확한 이미지를 요구한다.
cv2.TM_CCOEFF_NORMED는 이미지 매칭 메서드로, 정규화된 상관 계수를 사용한다.
스킬 아이콘으로 사용 여부를 판단하기로 했기에,
밝기 변화에 민감한 해당 메서드를 사용했다.
https://deep-learning-study.tistory.com/242
[파이썬 OpenCV] 탬플릿 매칭으로 원하는 영상 찾기 - cv2.matchTemplate
탬플릿 매칭 - Template matching 템플릿 매칭은 입력 영상에서 (작은 크기의) 템플릿 영상과 일치하는 부분을 찾는 기법입니다. 템플릿은 찻을 대상이 되는 작은 영상을 의미합니다. 커널이라는 용어
deep-learning-study.tistory.com
더 자세한 설명은 여기
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
_, max_val, _, _ = cv2.minMaxLoc(result)
return max_val > threshold
minMaxLoc는 최소값과 최대값, 그 위치를 리턴하는 함수로,
가장 높은 값만 필요하니 나머진 언더바로 처리했다.
해당 최대값이 임계값보다 높은지 아닌지로 특정 이미지가 존재하는지를 판단한다.
다음은 물봉몹 이미지.
처음엔 실제 gif를 프레임별로 나눈 뒤 배경을 제거하였는데
생각보다 적중률이 떨어져서 그냥 실제 인게임 사진을 찍었다.
이런 느낌으로 10장정도 찍었다
def load_images(image_paths):
images = {}
for i, path in enumerate(image_paths, start=1):
image = load_image(path)
images[f"img{i}"] = image
return images
monster_image_paths = ["image/img1.png", "image/img2.png", "image/img3.png",
"image/img4.png", "image/img5.png", "image/img6.png",
"image/img7.png", "image/img8.png", "image/img9.png",
"image/img10.png"]
monster_images = load_images(monster_image_paths)
이미지 로드해주고
def detect_monster(images, screen):
for img_name, template in images.items():
try:
image_found = is_image_on_screen(template, screen, 0.5)
if image_found:
print(f"{img_name}이(가) 존재합니다.")
play_music("music/monster_appear.mp3")
return True
else:
print("모든 이미지가 화면에 존재하지 않습니다.")
pygame.mixer.music.stop()
return False
10장의 사진에 대해 위에서 만든 이미지 대조 함수를 호출한다.
만약 true값이 나오면 바로 break하게 하였음
def play_music(sound_path):
pygame.mixer.music.stop()
pygame.mixer.music.load(sound_path)
pygame.mixer.music.play()
경고음은 pygame으로 mp3 파일 재생하면 간단히 해결
그리고 스킬 사용시 3분 쿨타임이 지나면 알람이 울리는 기능을 추가하였다.
처음엔 스킬 키 입력을 트리거로 사용하려했는데
마우스가 게임에 포커스된 채론 키보드 입력을 인식 못하더라.
찾으면 방법이 있겠다만 그냥 화면 인식으로 뽕 뽑기로 했다.
처음엔 스킬 이펙트를 감지하려 했는데
감지 능력이 묘하게 불안불안해서...
그냥 스킬 아이콘을 감지하게 하고 임계값을 올리니 훨씬 안정적이었다.
def detect_attack_skill(screen):
global is_attack_trigger, wtime
if is_attack_trigger:
is_attack = is_image_on_screen(w_icon_template, screen, 0.9)
if is_attack:
# 이후 3분동안 감지하지 않음
is_attack_trigger = False
wtime = time.time()
play_music("music/skill_use.mp3")
print("W 감지")
else:
if time.time() - wtime > 180:
is_attack_trigger = True
play_music("music/skill_cool_done.mp3")
print("w 쿨타임 완료")
몬스터는 동적으로 움지이기에 임계값을 0.5로 하였는데,
스킬 아이콘은 정확히 구분해야하므로 0.9로 하였다.
그리고 스킬을 사용을 감지하고 이후 3분동안은
스킬 감지를 진행하지 않도록 하였다.
while True:
# 화면 캡처후 특정 이미지 존재 여부 검사
screen = capture_screen()
# 몬스터 감지 및 소리 재생
if detect_monster(monster_images, screen):
continue # Skip the rest of the loop if a monster is found
# 공격 스킬 사용 감지
detect_attack_skill(screen)
time.sleep(0.5)
이제 반복문으로 매 초마다 화면을 감지하게 해주면 끝!
시범 영상
정상적으로 잘 작동한다!
https://github.com/gillyongs/Game-screen-detection/tree/main
GitHub - gillyongs/Game-screen-detection
Contribute to gillyongs/Game-screen-detection development by creating an account on GitHub.
github.com
깃허브.