携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情
项目需求
相信大家不少的人曾经都玩过找不同游戏的,今天想趁热打铁,做一个找不同游戏的“辅助”来进行自动找不同。先来介绍找不同游戏的操作逻辑:
1. 登录游戏网页端,全屏显示便于清晰查看不同点;
2. 画面中显示两张相似的图像,这两张图像只存在部分地方存在差异;
3. 点击存在差异的地方即认可为完成了“找不同”任务
介绍完游戏的操作逻辑,我们根据操作逻辑进行“自动找不同”程序设计实现上述逻辑:
1. 截取当前游戏画面(固定);
2. 分别锚定两张相似图像并切割下来(由于游戏画面固定,出现图像的位置也会固定);
3. 对两张相似的图像进行处理得到不同位置的位置,并输出中心点坐标。
需求分析
实现“找不同”一件很难的事情,程序设计的前两个需求并不是很难,通过前面两个辅助的制作,可以通过pyautogui.screenshot() 和 pymouse 可以实现屏幕画面的截取和鼠标的移动点击;通过OpenCvd的切割可以实现相似图的切割。较为难实现的是找到不同的位置,本文也将重点分析如何找到不同!
网上流传较为广的是一种遍历两张图像的像素,当像素不一样的时候输出此时的位置。这里我就不阐述它的逻辑了。我将按照如下的几种方案实现找不同任务:
1. 对裁剪下来的两张图像进行灰度化后做差得到结果进行二值化;
2. 使用图像匹配法完成“找不同”
3. 遍历像素实现
实施验证
今日时间有限,先做个“丐版”的找不同。从技术实现的角度有限解决核心部分,实现“有无”的难题。我们提前将两张相似的图像截取下来:
使用作差法实现:
import cv2
img_1 = cv2.imread("1.png") # Slicing to crop the image
img_2 = cv2.imread("2.png")
img_1 = cv2.cvtColor(img_1,cv2.COLOR_BGR2GRAY)
img_2 = cv2.cvtColor(img_2,cv2.COLOR_BGR2GRAY)
img3 = img_1 - img_2
ret, img3 = cv2.threshold(img3, 50, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("a", img3)
cv2.waitKey(0)
使用遍历像素实现:
import cv2
img_1 = cv2.imread("1.png") # Slicing to crop the image
img_2 = cv2.imread("2.png")
img_11 = cv2.cvtColor(img_1, cv2.COLOR_BGR2GRAY)
img_22 = cv2.cvtColor(img_2, cv2.COLOR_BGR2GRAY)
width,height = img_11.shape
for i in range(width):
for j in range(height):
x = img_11[i][j]
y = img_22[i][j]
if abs(x - y) > 30:
img_1[i][j] = [0, 0, 0]
cv2.imshow("result", img_1)
cv2.waitKey(0)
图像匹配法
图像匹配法是借用某位大神的代码,结果供大家参考一下:
import cv2
import numpy as np
from matplotlib import pyplot as plt
import argparse
def matchAB(fileA, fileB):
# 读取图像数据
imgA = cv2.imread(fileA)
imgB = cv2.imread(fileB)
# 转换成灰色
grayA = cv2.cvtColor(imgA, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imgB, cv2.COLOR_BGR2GRAY)
# 获取图片A的大小
height, width = grayA.shape
# 取局部图像,寻找匹配位置
result_window = np.zeros((height, width), dtype=imgA.dtype)
for start_y in range(0, height - 100, 10):
for start_x in range(0, width - 100, 10):
window = grayA[start_y:start_y + 100, start_x:start_x + 100]
match = cv2.matchTemplate(grayB, window, cv2.TM_CCOEFF_NORMED)
_, _, _, max_loc = cv2.minMaxLoc(match)
matched_window = grayB[max_loc[1]:max_loc[1] + 100, max_loc[0]:max_loc[0] + 100]
result = cv2.absdiff(window, matched_window)
result_window[start_y:start_y + 100, start_x:start_x + 100] = result
# 用四边形圈出不同部分
_, result_window_bin = cv2.threshold(result_window, 30, 255, cv2.THRESH_BINARY)
_, contours, _ = cv2.findContours(result_window_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
imgC = imgA.copy()
for contour in contours:
min = np.nanmin(contour, 0)
max = np.nanmax(contour, 0)
loc1 = (min[0][0], min[0][1])
loc2 = (max[0][0], max[0][1])
cv2.rectangle(imgC, loc1, loc2, 0, 2)
plt.subplot(1, 3, 1), plt.imshow(cv2.cvtColor(imgA, cv2.COLOR_BGR2RGB)), plt.title('A'), plt.xticks([]), plt.yticks(
[])
plt.subplot(1, 3, 2), plt.imshow(cv2.cvtColor(imgB, cv2.COLOR_BGR2RGB)), plt.title('B'), plt.xticks([]), plt.yticks(
[])
plt.subplot(1, 3, 3), plt.imshow(cv2.cvtColor(imgC, cv2.COLOR_BGR2RGB)), plt.title('Answer'), plt.xticks(
[]), plt.yticks([])
plt.show()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'--source_image',
type=str,
default='1.png',
help='source image'
)
parser.add_argument(
'--target_image',
type=str,
default='2.png',
help='target image'
)
FLAGS, unparsed = parser.parse_known_args()
matchAB(FLAGS.source_image, FLAGS.target_image)