find-a-different-card/main.py

240 lines
9.3 KiB
Python

import pygetwindow as gw # Get size & coords of specific window with It's handle(hwnd), not title
from pywinauto import Desktop # Get size & coords of specific window with It's title *USE THIS!!!*
from mss import mss # Capture windows with given coords
import cv2 # You know what It is
import numpy as np # sure
import time
from skimage.metrics import structural_similarity as ssim
def resize_with_ratio(image, scale=1.5, inter=cv2.INTER_LINEAR):
# 이미지 크기 가져오기
(h, w) = image.shape[:2]
# 너비와 높이를 1.5배로 확대
new_size = (int(w * scale), int(h * scale))
# 리사이즈 적용
resized = cv2.resize(image, new_size, interpolation=inter)
return resized
def pad_to_match_size(card1, card2):
# 카드 1과 카드 2의 크기 확인
h1, w1 = card1.shape[:2]
h2, w2 = card2.shape[:2]
# 두 카드의 최대 너비와 높이 계산
max_h = max(h1, h2)
max_w = max(w1, w2)
# 카드 1에 패딩 추가 (상하좌우에 패딩을 균일하게 추가)
pad_top_1 = (max_h - h1) // 2
pad_bottom_1 = max_h - h1 - pad_top_1
pad_left_1 = (max_w - w1) // 2
pad_right_1 = max_w - w1 - pad_left_1
padded_card1 = cv2.copyMakeBorder(card1, pad_top_1, pad_bottom_1, pad_left_1, pad_right_1, cv2.BORDER_CONSTANT, value=[0, 0, 0])
# 카드 2에 패딩 추가
pad_top_2 = (max_h - h2) // 2
pad_bottom_2 = max_h - h2 - pad_top_2
pad_left_2 = (max_w - w2) // 2
pad_right_2 = max_w - w2 - pad_left_2
padded_card2 = cv2.copyMakeBorder(card2, pad_top_2, pad_bottom_2, pad_left_2, pad_right_2, cv2.BORDER_CONSTANT, value=[0, 0, 0])
return padded_card1, padded_card2
# def compare_cards(card1, card2): #============================================= L2 norm
# resized_card1, resized_card2 = pad_to_match_size(card1, card2)
# return cv2.norm(resized_card1, resized_card2, cv2.NORM_L2)
# def compare_cards(card1, card2): #============================================= ORB
# orb = cv2.ORB_create()
# # 카드에서 특징점 검출
# kp1, des1 = orb.detectAndCompute(card1, None)
# kp2, des2 = orb.detectAndCompute(card2, None)
# # BFMatcher로 특징점 매칭
# bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# matches = bf.match(des1, des2)
# # 매칭 결과를 거리 기준으로 정렬
# matches = sorted(matches, key=lambda x: x.distance)
# # 거리 합산으로 차이 계산 (거리가 클수록 더 많은 차이)
# return sum([match.distance for match in matches])
# def compare_cards(card1, card2): #============================================= 정적배경차분
# resized_card1, resized_card2 = pad_to_match_size(card1, card2)
# # 이미지 차이 계산
# diff = cv2.absdiff(resized_card1, resized_card2)
# # 차이가 나는 픽셀의 합계를 계산
# difference_score = np.sum(diff)
# return difference_score
# def compare_cards(card1, card2): #============================================= Canny edge detection
# resized_card1, resized_card2 = pad_to_match_size(card1, card2)
# # Canny Edge Detection 적용
# edges1 = cv2.Canny(resized_card1, 30, 150)
# edges2 = cv2.Canny(resized_card2, 30, 150)
# # 이미지 차이 계산 (엣지 차이)
# diff = cv2.absdiff(edges1, edges2)
# # 차이가 나는 픽셀의 합계를 계산
# difference_score = np.sum(diff)
# return difference_score
# def compare_cards(card1, card2): #============================================= 히스토그램 비교
# resized_card1, resized_card2 = pad_to_match_size(card1, card2)
# # 히스토그램 계산
# hist1 = cv2.calcHist([resized_card1], [0], None, [256], [0, 256])
# hist2 = cv2.calcHist([resized_card2], [0], None, [256], [0, 256])
# # 히스토그램을 정규화
# hist1 = cv2.normalize(hist1, hist1)
# hist2 = cv2.normalize(hist2, hist2)
# # 히스토그램 차이 계산 (Chi-Square 비교)
# score = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CHISQR)
# return score
# def compare_cards(card1, card2): #============================================= 템플릿 매칭
# resized_card1, resized_card2 = pad_to_match_size(card1, card2)
# # 템플릿 매칭
# result = cv2.matchTemplate(resized_card1, resized_card2, cv2.TM_CCOEFF_NORMED)
# # 매칭 값 (1에 가까울수록 유사, 0에 가까울수록 차이)
# min_val, max_val, _, _ = cv2.minMaxLoc(result)
# return max_val
# def compare_cards(card1, card2): #============================================= 로그적용 정적배경차분
# resized_card1, resized_card2 = pad_to_match_size(card1, card2)
# # LoG 필터 적용
# log_card1 = cv2.Laplacian(cv2.GaussianBlur(resized_card1, (3, 3), 0), cv2.CV_64F)
# log_card2 = cv2.Laplacian(cv2.GaussianBlur(resized_card2, (3, 3), 0), cv2.CV_64F)
# # log_card1 = cv2.Laplacian(resized_card1, cv2.CV_64F)
# # log_card2 = cv2.Laplacian(resized_card2, cv2.CV_64F)
# # 이미지 차이 계산
# diff = cv2.absdiff(log_card1, log_card2)
# # 차이 합계 계산
# difference_score = np.sum(diff)
# return difference_score
def compare_cards(card1, card2): #============================================= SSIM
resized_card1, resized_card2 = pad_to_match_size(card1, card2)
# SSIM 비교 (grayscale로 변환한 후)
ssim_score, _ = ssim(resized_card1, resized_card2, full=True)
return ssim_score
target_window = "S22 Ultra"
windows = Desktop(backend="uia").windows()
selected_window = None
for w in windows:
if target_window.lower() in w.window_text().lower(): # 대소문자 구분 없이 비교
selected_window = w
break
if selected_window != None:
rect = selected_window.rectangle()
monitor_coords = {"top": rect.top, "left": rect.left, "width": rect.width(), "height": rect.height()}
with mss() as sct:
while True:
rect = selected_window.rectangle()
monitor_coords = {"top": rect.top, "left": rect.left, "width": rect.width(), "height": rect.height()}
sct_img = sct.grab(monitor_coords)
img = np.array(sct_img)
# Now we're gonna do some CV things
# Preprocessing
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
_, thresh = cv2.threshold(blur, 245, 255, cv2.THRESH_BINARY_INV)
kernel = np.ones((3, 3), np.uint8)
thresh = cv2.erode(thresh, kernel, iterations=1)
_, thresh = cv2.threshold(thresh, 100, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# Find contour
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.imshow("Preprocessed", thresh)
cards = []
for i, cnt in enumerate(contours):
x, y, w, h = cv2.boundingRect(cnt)
if 60 < w < 200 and 100 < h < 300: # 카드 크기
aspect_ratio = float(w) / h
if 0.5 < aspect_ratio < 1.0: # 카드 비율
card_gray = gray[y:y+h, x:x+w]
card_resized = resize_with_ratio(card_gray, 2)
_, card_thresh = cv2.threshold(card_resized, 130, 255, cv2.THRESH_BINARY_INV)
card_kernel = np.ones((1, 1), np.uint8) # 커널 크기로 글자 두께 조절
card_eroded = cv2.erode(card_thresh, card_kernel, iterations=1)
cv2.imshow("card", card_eroded)
cards.append((card_eroded, cnt)) # 카드 이미지와 윤곽선 정보 함께 저장
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
# 차이 계산
differences = []
if len(cards) > 1:
for i in range(len(cards)):
min_diff = float('inf') # 비교 시 최소값을 찾기 위한 초기값 설정
card_image_1, _ = cards[i] # 기준 카드 이미지
for j in range(len(cards)):
if i != j: # 자기 자신과는 비교하지 않음
card_image_2, _ = cards[j] # 비교할 다른 카드 이미지
diff = compare_cards(card_image_1, card_image_2) # 두 카드 이미지 비교
if diff < min_diff:
min_diff = diff # 최소 차이값 업데이트
differences.append((i, min_diff))
# 차이비교
if differences:
max_different_card = max(differences, key=lambda x: x[1]) # 차이가 가장 큰 카드 (간혹 방식에 따라 반대일 수 있음)
min_different_card = min(differences, key=lambda x: x[1]) # 차이가 가장 작은 카드
# 가장 큰 차이가 있는 카드의 윤곽선
_, cnt_max = cards[max_different_card[0]]
x_max, y_max, w_max, h_max = cv2.boundingRect(cnt_max)
cv2.rectangle(img, (x_max, y_max), (x_max + w_max, y_max + h_max), (0, 0, 255), 2) # 빨간색
cv2.putText(img, f'Conf: {max_different_card}%', (x_max, y_max - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 0), 2)
# 가장 작은 차이가 있는 카드의 윤곽선
_, cnt_min = cards[min_different_card[0]]
x_min, y_min, w_min, h_min = cv2.boundingRect(cnt_min)
cv2.rectangle(img, (x_min, y_min), (x_min + w_min, y_min + h_min), (255, 0, 0), 2) # 파란색
cv2.putText(img, f'Conf: {min_different_card}%', (x_min, y_min - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 0), 2)
print("\rtotal cards: {}".format(len(cards)), end="")
cv2.imshow("Capturing target", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()