出于想要通过学习和创造一改自己最近颓丧的状态,也出于自己的专业兴趣,决定做点自己的小项目
学校教的课程还都只停留在理论,在毫无实践经验的情况下,我抱着试试看的心态尝试完全通过chatgpt的指导来一步步探索,发现chatgpt意外的强大。题外话有点多了2333
一、图像的截取
想要利用计算机视觉实时操作,对图像的截取肯定是第一步
import pyautogui
# 截取屏幕
screenshot = pyautogui.screenshot()
# 将截图保存为图像
screenshot.save("screenshot.png")
以上为利用pyautogui库的自带函数对整个屏幕进行截图
而实际操作中往往只需要对所需窗口进行截图:
import win32gui
import win32con
import pyautogui
import cv2
from PIL import ImageGrab
# 获取窗口句柄
def get_window_handle(window_name):
handle = win32gui.FindWindow(None, window_name)
if handle == 0:
raise Exception(f"Window '{window_name}' not found!")
return handle
# 获取窗口的边界框(左, 上, 右, 下)
def get_window_rect(handle):
rect = win32gui.GetWindowRect(handle)
# rect 包括 (left, top, right, bottom) 四个值
return rect
# 截取窗口图像
def capture_window(window_name):
# 获取窗口句柄
handle = get_window_handle(window_name)
# 获取窗口边界框
rect = get_window_rect(handle)
# 调整窗口边界(某些窗口可能有标题栏和边框,需要裁剪)
left, top, right, bottom = rect
width = right - left
height = bottom - top
# 截取该区域的图像
screenshot = ImageGrab.grab(bbox=(left, top, right, bottom))
# 将PIL图像转换为OpenCV格式
screenshot_cv = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
return screenshot_cv
二、对地图中标志物的识别
所测试的游戏是早就过气了的pokemmo(叠个甲 所做仅用于个人的学习和探索,今后成果也不会用于盈利和对游戏性的破坏,选择这款游戏单纯是因为游戏操作与地图简单)
游戏流程中肯定需要判定目标地点并进行移动,那么首先就需要对目标地点进行识别
我选择了最简单好做的方法:识别特征标志物
若识别到地图中存在该标志物,便返回其相对于人物的位置,若识别不到便输出错误信息
那么该如何判定标志物是否存在呢?
使用特征匹配算法将地图中的特征与目标点的特征进行匹配。
import cv2
# 加载目标点和地图的图像
marked_img = cv2.imread('target.jpg', 0)
map_img = cv2.imread('map.jpg', 0)
# 使用模板匹配来查找标志物在地图中的位置
result = cv2.matchTemplate(map_img, marker_img, cv2.TM_CCOEFF_NORMED)
对图像判定预测概率。如果预测结果的最高概率低于某个设定的阈值,可以认为该图像上不存在该标志物。
#计算最大概率和最小概率
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
threshold=‘预先设定好的阈值’
if max_val < threshold:
return "Unknown"
else:
return "Finded"
需要反复测试来寻找最佳阈值。
在识别到后,通过比较坐标来找到目标点相对人物方位,如果xy距离都小于一定值,则直接判定为到达出“OK”
由于在该游戏中人物位于窗口中心,所以直接用窗口中心的坐标。
if max_val >= threshold:
# 获取标志物左上角的位置
top_left = max_loc
bottom_right = (top_left[0] + marker_img.shape[1], top_left[1] + marker_img.shape[0])
# 标志物的中心位置
marker_center = (top_left[0] + marker_img.shape[1] // 2, top_left[1] + marker_img.shape[0] // 2)
# 计算标志物相对于地图中心的位置
dx = marker_center[0] - map_center[0]
dy = marker_center[1] - map_center[1]
# 判断标志物位于地图中心的哪个方位
if abs(dx) <= 50 and abs(dy) <= 50: # 如果标志物接近中心
print("OK")
cv2.putText(map_img, "OK", (map_center[0] - 50, map_center[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
else:
if dx > 0:
x_pos = "右"
else:
x_pos = "左"
if dy > 0:
y_pos = "下"
else:
y_pos = "上"
position = f"标志物位于地图中心的{x_pos}{y_pos}方位"
print(position)
cv2.putText(map_img, position, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
else:
print("标志物未找到")
Day1阶段性成果:
测试所用地图:Straton City
目标标志物:
道馆大门
以下为代码
import time
import win32gui
import win32con
import pyautogui
import cv2
import numpy as np
from PIL import ImageGrab
import win32gui
def capture_window(window_name):
# 获取窗口句柄s
hwnd = win32gui.FindWindow(None, window_name)
if hwnd == 0:
print(f"未找到窗口: {window_name}")
return None
# 获取窗口的设备上下文
left, top, right, bottom = win32gui.GetWindowRect(hwnd)
width = right - left
height = bottom - top
# 获取截图ds
screenshot = ImageGrab.grab(bbox=(left, top, right, bottom))
# 将PIL图像转换为OpenCV格式
screenshot_cv = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
return screenshot_cv
# 测试 - 传入游戏窗口名称(替换为你游戏窗口的名称)
time.sleep(3)
window_name = "PokeMМO" # 示例:替换为游戏窗口的名称
image = capture_window(window_name)
dg1d = cv2.imread(r'C:\Users\28788\Desktop\TASK2\daoguan1door\dg1d.png')
while True:
map_img=image = capture_window(window_name)
marker_img=dg1d
if map_img is None or marker_img is None:
print("地图或标志物图片读取失败")
exit()
# 使用模板匹配来查找标志物在地图中的位置
result = cv2.matchTemplate(map_img, marker_img, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
# 定义匹配成功的阈值
threshold = 0.55
# 获取地图的尺寸
map_h, map_w = map_img.shape[:2]
# 地图的中心位置
map_center = (map_w // 2, map_h // 2)
if max_val >= threshold:
# 获取标志物左上角的位置
top_left = max_loc
bottom_right = (top_left[0] + marker_img.shape[1], top_left[1] + marker_img.shape[0])
# 绘制矩形框标注标志物
cv2.rectangle(map_img, top_left, bottom_right, (0, 255, 0), 2)
# 标志物的中心位置
marker_center = (top_left[0] + marker_img.shape[1] // 2, top_left[1] + marker_img.shape[0] // 2)
# 计算标志物相对于地图中心的位置
dx = marker_center[0] - map_center[0]
dy = marker_center[1] - map_center[1]
# 判断标志物位于地图中心的哪个方位
if abs(dx) <= 50 and abs(dy) <= 50: # 如果标志物接近中心
print("OK")
cv2.putText(map_img, "OK", (map_center[0] - 50, map_center[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
else:
if dx > 0:
x_pos = "右"
else:
x_pos = "左"
if dy > 0:
y_pos = "下"
else:
y_pos = "上"
position = f"标志物位于地图中心的{x_pos}{y_pos}方位"
print(position)
cv2.putText(map_img, position, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
else:
print("标志物未找到")
# 在窗口中显示结果
#cv2.imshow('Map with Marker Detection', map_img)
time.sleep(1)
# 显示图像
#cv2.imshow("Window Screenshot", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
测试结果:
在该地图中,0.55-0.6的threshold值表现较为优秀
结果可以说是非常成功,识别准确的有点让我吃惊