起因是我想做个自动走迷宫的外挂(其实是想做点实践),所以我需要在游戏中捕捉画面并自动寻路,然后再控制自动移动,此为第一部分:捕捉画面。
1.取得图像迷宫
2.处理图像
3.图像分割
4.生成数组
首先我们得捕捉屏幕画面,即获得迷宫图像,这里我是在steam上面找了一个迷宫小游戏作为捕捉对象
然后写个捕捉屏幕画面的函数
def VideoCapture():
imm = ImageGrab.grab() # 获得当前屏幕
imm = cv2.cvtColor(np.array(imm), cv2.COLOR_RGB2GRAY) # 转为opencv的灰度图
imm = imm[35:1040, 450:1470] # 1080*1100 裁剪图像,这是根据游戏画面调整的数值
imm = cv2.resize(imm, (250, 250)) # 19 * 19 缩小图像
return imm
然后如果显示图像就能看到:
接着我们需要对图像进行处理,我们可以看到这个迷宫有三个对象:从上到下依次是巢,心,球。而我们是要操控球吃完所有的心再返回巢。所以 巢和心的是需要搜索路径的终点,球是起点,但是在此之前,我们需要一个完整的迷宫地图,所以我们要先找到这三个对象并涂白:
所以我们要进行模板匹配:
def match(target, template, color):
global object
# 获得模板图片的高宽尺寸
theight, twidth = template.shape[:2]
# 执行模板匹配,采用的匹配方式cv2.TM_SQDIFF_NORMED
result = cv2.matchTemplate(target, template, cv2.TM_SQDIFF_NORMED)
# 归一化处理
cv2.normalize(result, result, 0, 1, cv2.NORM_MINMAX, -1)
# 寻找矩阵(一维数组当做向量,用Mat定义)中的最大值和最小值的匹配结果及其位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
# 匹配值转换为字符串
# 对于cv2.TM_SQDIFF及cv2.TM_SQDIFF_NORMED方法min_val越趋近与0匹配度越好,匹配位置取min_loc
# 对于其他方法max_val越趋近于1匹配度越好,匹配位置取max_loc
strmin_val = str(min_val)
# 绘制矩形边框,将匹配区域涂成白色
# min_loc:矩形定点
# (min_loc[0]+twidth,min_loc[1]+theight):矩形的宽高
# color:矩形的边框颜色;2:矩形边框宽度
cv2.rectangle(target, min_loc, (min_loc[0] + twidth, min_loc[1] + theight), color, -1)
# 返回操作后图像,以及对象的四点坐标
return [target, (int(min_loc[0]), int(min_loc[1]), int(min_loc[0] + twidth), int(min_loc[1] + theight))]
执行后效果如图所示:
然后我们要把图像转化为标准的迷宫图像,黑白块的大小要相等,这样才方便转化成数组形式,所以我们对图像进行二值化后白色为可走趋于,黑色为障碍,接着通过腐蚀操作增大黑色趋于使之与白色块大小接近。
def pic(imm):
ret, binary = cv2.threshold(imm, 200, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)# 二值化
k = np.ones((7, 7), np.uint8)#定义核
binary = cv2.erode(binary, k)# 腐蚀
return binary
然后大致数出可以划分为19*19的色块,计算出单个色块的长度,然后定义每个色块的近中心点(因为坐标只能整数而计算中心点会有小数,一般略微靠近左上角比较准确),取其近中心点的颜色为整个色块的颜色转化为数组中:
def getBoard(imm):
board = [[0 for w in range(19)] for h in range(19)]#创建数组
for h in range(19):
for w in range(19):
if imm[13 * h + 5, 13 * w + 5] == np.uint8(255):#坐标转换
board[h][w] = 255
return board
然后我们把得到的数组以图像的形式打印出来看看:
对比一下原图像,已经实现了我们的目的。
最后我们再把其他信息获取一下,比如对象位置转化为数组坐标:
def getBoardIndex(cor):
index = (int((cor[1] - 5) / 13), int((cor[0] - 5) / 13))
return index
然后我们也方便日后的寻路算法应用(这么小,很快!)
最后的最后给个运行代码:
startFlag = False
while True:
imm = VideoCapture()
if startFlag:
imm, ball = match(imm, cv2.imread("ball.png", 0), (255, 255, 255))
imm, home = match(imm, cv2.imread("home.png", 0), (255, 255, 255))
imm, heart = match(imm, cv2.imread("heart.png", 0), (255, 255, 255))
imm = pic(imm)
board = getBoard(imm)
cv2.imwrite("board.png", np.array(board, dtype=np.uint8))
ball = getBoardIndex(getPointCor(ball))
heart = getBoardIndex(getPointCor(heart))
home = getBoardIndex(getPointCor(home))
print(ball, heart, home)
key = cv2.waitKey(1)
if key == ord('q'):
break
elif key == ord('g'):
startFlag = True
cv2.imshow("im", imm)
key = cv2.waitKey(1)
if key == ord('q'):
break
elif key == ord('g'):
startFlag = True
cv2.imshow("im", imm)