摘要:这篇文章介绍微信小程序“大家来找茬”怎么使用程序自动“找茬”,使用到的工具主要是Python3和adb工具。


作者:yooongchun
微信公众号: yooongchun小屋


腾讯官方出了一个小程序的找茬游戏,如下示意:

很多时候“眼疾手快”比不过别人,只好寻找一种便捷的玩法:程序自动实现!
这里使用的是Python3

  • 第一步:获取手机截图
os.system("adb.exe exec-out screencap -p >screenshot.png")

上面的命令获得的截图在windows系统上会出错,这是由于windows默认使用的换行符为\r\n 而Andriod系统使用的是Linux内核,其换行表示为\n ,在手机端把二进制数据流传输给电脑时,windows会自动把\n 替换为\r\n 因而为了正确显示,还需要一个转换,我们编写Python的转换代码如下:

# 转换图片格式
# adb 工具直接截图保存到电脑的二进制数据流在windows下"\n" 会被解析为"\r\n",
# 这是由于Linux系统下和Windows系统下表示的不同造成的,而Andriod使用的是Linux内核
def convert_img(path):with open(path, "br") as f:bys = f.read()bys_ = bys.replace(b"\r\n", b"\n")  # 二进制流中的"\r\n" 替换为"\n"with open(path, "bw") as f:f.write(bys_)
  • 第二步:图片裁剪
    获得的图片有多余的部分,需要进行裁剪,使用Python的opencv库,代码如下:

# 裁剪图片
def crop_image(im, box=(0.20, 0.93, 0.05, 0.95), gap=38, dis=2):''':param path: 图片路径:param box: 裁剪的参数:比例:param gap: 中间裁除区域:param dis: 偏移距离:return: 返回裁剪出来的区域'''h, w = im.shape[0], im.shape[1]region = im[int(h * box[2]):int(h * box[3]), int(w * box[0]):int(w * box[1])]rh, rw = region.shape[0], region.shape[1]region_1 = region[0 + dis: int(rh / 2) - gap + dis, 0: rw]region_2 = region[rh - int(rh / 2) + gap: rh, 0:rw]return region_1, region_2, region
  • 第三步:图片差异对比
    图片差异对比这就很好理解了,把两张图片叠到一起,相减,剩下的就是不同的地方了,当然,这里有几个细节需要注意:原图的截取,上面从手机获取的截图有很多非目标区域,因而我们需要定义截图区域,这就是我们程序中需要给出的box参数:
    box=(0.2,0.93,0.05,0.95)
    这里,参数依次代表:
    开始截取的列=0.2*图片宽,停止截取的列=0.93*图片宽
    开始截取的行=0.05*图片高,开始截取的行=0.95*图片高
    然后,仔细观察你会发现中间还有一块多余的区域,把上下两张图分开只需要给出中间区域要截除的像素值,这也就是我们程序运行的第二个参数:
    gap=38
    这里代表把第一次截图得到的图片二分后分别截去38像素的高度。
    这时,还有一个问题要注意的是,我们截图参数是根据肉眼分辨设置的,你截图的结果可能并不是严格的目标图片的开始行列,这时,得到的两张图片会存在很小的错位,为了微调这个错位,我们给出程序的第三个参数:
    dis=2
    这代表两张图片在进行相减作差的时候会微调两行。
    好了,得到差异图片后我们来看看效果

    哈,五个不同的地方,终于“原形毕露”!
# 查找不同返回差值图
def diff(img1, img2):diff = (img1 - img2)# 形态学开运算滤波kernel = np.ones((5, 5), np.uint8)opening = cv2.morphologyEx(diff, cv2.MORPH_OPEN, kernel)return opening

这时,你就可以看着这张差异图去“找茬”了。
当然,上面这张丑陋的差异图是不能忍受的,没事,我们接着改进。
找到了差异,如何“优雅”的展示差异呢?我的第一反应就是:在原图上画个圈出来,这样既直观又不失“优雅”。好吧,说干就干!
第一步,使用Opencv库检索差异图的轮廓。这里,值得一提的是在图片的右上角有个小程序的返回图标,这会干扰我们提取轮廓,因而需要先把这个图标去除。查找到轮廓之前需要把图片转换为二值图,然后运用形态学开运算去除噪声,这里涉及程序的第四个参数:滤波核尺寸:
filter_sz=25
最后查找外轮廓并根据轮廓周长保存前n个轮廓,这就是程序里的第五个参数:
num=5
然后检测轮廓的最小外接圆,找到圆心和半径,绘制到原图上,效果如下:

这么样,效果是不是更“优雅”一些了呢!


# 去除右上角的多余区域,即显示小程序返回及分享的灰色区域块
def dispose_region(img):h, w = img.shape[0], img.shape[1]img[0:int(0.056 * h), int(0.68 * w):w] = 0return img# 查找轮廓中心返回坐标值
def contour_pos(img, num=5, filter_size=5):''':param img: 查找的目标图,需为二值图:param num: 返回的轮廓数量,如果该值大于轮廓总数,则返回轮廓总数:return: 返回值为轮廓的最小外接圆的圆心坐标和半径,存放在一个list中'''position = []  # 保存返回值# 计算轮廓gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)cv2.namedWindow("binary", cv2.WINDOW_NORMAL)cv2.imshow("binary", binary)kernel = np.ones((filter_size, filter_size), np.uint8)opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)  # 开运算cv2.namedWindow("open", cv2.WINDOW_NORMAL)cv2.imshow("open", opening)image, contours, hierarchy = cv2.findContours(np.max(opening) - opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 根据轮廓周长大小决定返回的轮廓arclen = [cv2.arcLength(contour, True) for contour in contours]arc = arclen.copy()arc.sort(reverse=True)if len(arc) >= num:thresh = arc[num - 1]else:thresh = arc[len(arc) - 1]for index, contour in enumerate(contours):if cv2.arcLength(contour, True) < thresh:continue(x, y), radius = cv2.minEnclosingCircle(contour)center = (int(x), int(y))radius = int(radius)position.append({"center": center, "radius": radius})return position# 在原图上显示
def dip_diff(origin, region, region_1, region_2, dispose_img, position, box, setting_radius=40, gap=38, dis=2):for pos in position:center, radius = pos["center"], pos["radius"]if setting_radius is not None:radius = setting_radiuscv2.circle(region_2, center, radius, (0, 0, 255), 5)cv2.namedWindow("region2",cv2.WINDOW_NORMAL)cv2.imshow("region2",region_2)gray = cv2.cvtColor(dispose_img, cv2.COLOR_BGR2GRAY)binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)kernel = np.ones((15, 15), np.uint8)opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)  # 开运算merged = 255 - cv2.merge([opening, opening, opening])h, w = region_1.shape[0], region_1.shape[1]region[0:h, 0:w] *= mergedregion[0:h, 0:w] += region_1region[h + gap * 2 - dis:2 * h + gap * 2 - dis, 0:w] = region_2orih, oriw = origin.shape[0], origin.shape[1]origin[int(orih * box[2]):int(orih * box[3]), int(oriw * box[0]):int(oriw * box[1])] = regioncv2.namedWindow("show diff", cv2.WINDOW_NORMAL)cv2.imshow("show diff", origin)cv2.waitKey(0)
# 自动点击
def auto_click(origin, region_1, box, position, gap=38, dis=2):h, w = origin.shape[0], origin.shape[1]rh = region_1.shape[0]for pos in position:center, radius = pos["center"], pos["radius"]x = int(w * box[0] + center[0])y = int(h * box[2] + rh - dis + 2 * gap + center[1])os.system("adb.exe shell input tap %d %d" % (x, y))logging.info("tap:(%d,%d)" % (x, y))time.sleep(0.05)

最后贴上完整的代码:

"""
大家来找茬微信小程序腾讯官方版 自动找出图片差异"""
__author__ = "yooongchun"
__email__ = "yooongchun@foxmail.com"
__site__ = "www.yooongchun.com"import cv2
import numpy as np
import os
import time
import sys
import logging
import threadinglogging.basicConfig(level=logging.INFO)DEBUG = True  # 开启debug模式,供调试用,正常使用情况下请不要修改# 转换图片格式
# adb 工具直接截图保存到电脑的二进制数据流在windows下"\n" 会被解析为"\r\n",
# 这是由于Linux系统下和Windows系统下表示的不同造成的,而Andriod使用的是Linux内核
def convert_img(path):with open(path, "br") as f:bys = f.read()bys_ = bys.replace(b"\r\n", b"\n")  # 二进制流中的"\r\n" 替换为"\n"with open(path, "bw") as f:f.write(bys_)# 裁剪图片
def crop_image(im, box=(0.20, 0.93, 0.05, 0.95), gap=38, dis=2):''':param path: 图片路径:param box: 裁剪的参数:比例:param gap: 中间裁除区域:param dis: 偏移距离:return: 返回裁剪出来的区域'''h, w = im.shape[0], im.shape[1]region = im[int(h * box[2]):int(h * box[3]), int(w * box[0]):int(w * box[1])]rh, rw = region.shape[0], region.shape[1]region_1 = region[0 + dis: int(rh / 2) - gap + dis, 0: rw]region_2 = region[rh - int(rh / 2) + gap: rh, 0:rw]return region_1, region_2, region# 查找不同返回差值图
def diff(img1, img2):diff = (img1 - img2)# 形态学开运算滤波kernel = np.ones((5, 5), np.uint8)opening = cv2.morphologyEx(diff, cv2.MORPH_OPEN, kernel)return opening# 去除右上角的多余区域,即显示小程序返回及分享的灰色区域块
def dispose_region(img):h, w = img.shape[0], img.shape[1]img[0:int(0.056 * h), int(0.68 * w):w] = 0return img# 查找轮廓中心返回坐标值
def contour_pos(img, num=5, filter_size=5):''':param img: 查找的目标图,需为二值图:param num: 返回的轮廓数量,如果该值大于轮廓总数,则返回轮廓总数:return: 返回值为轮廓的最小外接圆的圆心坐标和半径,存放在一个list中'''position = []  # 保存返回值# 计算轮廓gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)cv2.namedWindow("binary", cv2.WINDOW_NORMAL)cv2.imshow("binary", binary)kernel = np.ones((filter_size, filter_size), np.uint8)opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)  # 开运算cv2.namedWindow("open", cv2.WINDOW_NORMAL)cv2.imshow("open", opening)image, contours, hierarchy = cv2.findContours(np.max(opening) - opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 根据轮廓周长大小决定返回的轮廓arclen = [cv2.arcLength(contour, True) for contour in contours]arc = arclen.copy()arc.sort(reverse=True)if len(arc) >= num:thresh = arc[num - 1]else:thresh = arc[len(arc) - 1]for index, contour in enumerate(contours):if cv2.arcLength(contour, True) < thresh:continue(x, y), radius = cv2.minEnclosingCircle(contour)center = (int(x), int(y))radius = int(radius)position.append({"center": center, "radius": radius})return position# 在原图上显示
def dip_diff(origin, region, region_1, region_2, dispose_img, position, box, setting_radius=40, gap=38, dis=2):for pos in position:center, radius = pos["center"], pos["radius"]if setting_radius is not None:radius = setting_radiuscv2.circle(region_2, center, radius, (0, 0, 255), 5)cv2.namedWindow("region2",cv2.WINDOW_NORMAL)cv2.imshow("region2",region_2)gray = cv2.cvtColor(dispose_img, cv2.COLOR_BGR2GRAY)binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)kernel = np.ones((15, 15), np.uint8)opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)  # 开运算merged = 255 - cv2.merge([opening, opening, opening])h, w = region_1.shape[0], region_1.shape[1]region[0:h, 0:w] *= mergedregion[0:h, 0:w] += region_1region[h + gap * 2 - dis:2 * h + gap * 2 - dis, 0:w] = region_2orih, oriw = origin.shape[0], origin.shape[1]origin[int(orih * box[2]):int(orih * box[3]), int(oriw * box[0]):int(oriw * box[1])] = regioncv2.namedWindow("show diff", cv2.WINDOW_NORMAL)cv2.imshow("show diff", origin)cv2.waitKey(0)# 在原图上绘制圆
def draw_circle(origin, region_1, position, box, gap=38, dis=2):h, w = origin.shape[0], origin.shape[1]rh = region_1.shape[0]for pos in position:center, radius = pos["center"], pos["radius"]radius = 40x = int(w * box[0] + center[0])y = int(h * box[2] + rh - dis + 2 * gap + center[1])cv2.circle(origin, (x, y), radius, (0, 0, 255), 3)cv2.namedWindow("origin with diff", cv2.WINDOW_NORMAL)cv2.imshow("origin with diff", origin)cv2.waitKey(0)# 自动点击
def auto_click(origin, region_1, box, position, gap=38, dis=2):h, w = origin.shape[0], origin.shape[1]rh = region_1.shape[0]for pos in position:center, radius = pos["center"], pos["radius"]x = int(w * box[0] + center[0])y = int(h * box[2] + rh - dis + 2 * gap + center[1])os.system("adb.exe shell input tap %d %d" % (x, y))logging.info("tap:(%d,%d)" % (x, y))time.sleep(0.05)# 主函数入口
def main(argv):# 参数列表,程序运行需要提供的参数# box = None  # 裁剪原始图像的参数,分别为宽和高的比例倍# gap = None  # 图像中间间隔的一半大小# dis = None  # 图像移位,微调系数# num = None  # 显示差异的数量# filter_sz = None  # 滤波核大小# auto_clicked=True# 仅有一个参数,则使用默认参数if len(argv) == 1:box = (0.20, 0.93, 0.05, 0.95)gap = 38dis = 2num = 5filter_sz = 13auto_clicked = "True"else:  # 多个参数时需要进行参数解析,参数使用等号分割try:# 设置参数para_pairs = {}paras = argv[1:]  # 参数for para in paras:para_pairs[para.split("=")[0]] = para.split("=")[1]# 参数配对if "gap" in para_pairs.keys():gap = int(para_pairs["gap"])else:gap = 38if "box" in para_pairs.keys():box = tuple([float(i) for i in para_pairs["box"][1:-1].split(",")])else:box = (0.20, 0.93, 0.05, 0.95)if "dis" in para_pairs.keys():dis = int(para_pairs["dis"])else:dis = 2if "num" in para_pairs.keys():num = int(para_pairs["num"])else:num = 5if "filter_sz" in para_pairs.keys():filter_sz = int(para_pairs["filter_sz"])else:filter_sz = 13if "auto_clicked" in para_pairs.keys():auto_clicked = para_pairs["auto_clicked"]else:auto_clicked = "True"except IOError:logging.info("参数出错,请重新输入!")returnst = time.time()try:os.system("adb.exe exec-out screencap -p >screenshot.png")convert_img("screenshot.png")except IOError:logging.info("从手机获取图片出错,请检查adb工具是否安装及手机是否正常连接!")returnlogging.info(">>>从手机截图用时:%0.2f 秒\n" % (time.time() - st))st = time.time()try:origin = cv2.imread("screenshot.png")  # 原始图像region_1, region_2, region = crop_image(origin, box=box, gap=gap, dis=dis)diff_img = diff(region_1, region_2)dis_img = dispose_region(diff_img)position = contour_pos(dis_img, num=num, filter_size=filter_sz)while len(position) < num and filter_sz > 3:filter_sz -= 1position = contour_pos(dis_img, num=num, filter_size=filter_sz)except IOError:logging.info("处理图片出错!")returntry:if auto_clicked is "True":threading.Thread(target=auto_click, args=(origin, region_1, box, position, gap, dis)).start()except IOError:logging.info(">>>尝试点击出错!")logging.info(">>>处理图片用时:%0.2f 秒\n" % (time.time() - st))try:dip_diff(origin, region, region_1, region_2, dis_img, position, box)# draw_circle(origin, region_1, position, box, gap=gap, dis=dis)except IOError:logging.info("重组显示出错!")returnif __name__ == "__main__":if not DEBUG:while True:main(sys.argv)else:box = (0.19, 0.95, 0.05, 0.95)gap = 38dis = 2num = 5filter_sz = 13origin = cv2.imread("c:/users/fanyu/desktop/adb/screenshot.png")  # 原始图像region_1, region_2, region = crop_image(origin, box=box, gap=gap, dis=dis)cv2.namedWindow("", cv2.WINDOW_NORMAL)cv2.imshow("", region_2)diff_img = diff(region_1, region_2)dis_img = dispose_region(diff_img)cv2.namedWindow(" ", cv2.WINDOW_NORMAL)cv2.imshow(" ", region_1)cv2.imshow("", dis_img)position = contour_pos(dis_img, num=num, filter_size=filter_sz)dip_diff(origin, region, region_1, region_2, dis_img, position, box)# draw_circle(origin, region_1, position, box, gap=gap, dis=dis)

另外,可到我的github下载完整版:
https://github.com/yooongchun/auto_find_difference

也可以到微信公众号查看完整的文章:yooongchun小屋

Python实现微信找茬小游戏自动进行相关推荐

  1. Python编写微信打飞机小游戏(八)

    如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢. Python编写微信打飞机小游戏(一) Python编写微信打飞机小游戏(二) Python编写微信打飞机小游戏(三 ...

  2. Python编写微信打飞机小游戏(五)

    如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢. Python编写微信打飞机小游戏(一) Python编写微信打飞机小游戏(二) Python编写微信打飞机小游戏(三 ...

  3. Python编写微信打飞机小游戏(九)

    如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢. Python编写微信打飞机小游戏(一) Python编写微信打飞机小游戏(二) Python编写微信打飞机小游戏(三 ...

  4. Python编写微信打飞机小游戏(三)

    如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢. Python编写微信打飞机小游戏(一) Python编写微信打飞机小游戏(二) Python编写微信打飞机小游戏(三 ...

  5. Python编写微信打飞机小游戏(十一)

    如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢. Python编写微信打飞机小游戏(一) Python编写微信打飞机小游戏(二) Python编写微信打飞机小游戏(三 ...

  6. flash找茬小游戏开发总结

    刚接触flash没多长时间,从最开始的花两三天做了一个建立在flash帧上面的找茬小游戏,到后来花了两个星期用as3写了一个找茬小游戏, 通过这段时间的学习,也感觉自己在一步一步的深入,到今天做完这个 ...

  7. Python编写微信打飞机小游戏(七)

    如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢. Python编写微信打飞机小游戏(一) Python编写微信打飞机小游戏(二) Python编写微信打飞机小游戏(三 ...

  8. Python编写微信打飞机小游戏(十)

    如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢. Python编写微信打飞机小游戏(一) Python编写微信打飞机小游戏(二) Python编写微信打飞机小游戏(三 ...

  9. Python编写微信打飞机小游戏(十二)

    如果觉得这篇文章对您有所启发,欢迎关注我的公众号,我会尽可能积极和大家交流,谢谢. Python编写微信打飞机小游戏(一) Python编写微信打飞机小游戏(二) Python编写微信打飞机小游戏(三 ...

最新文章

  1. oracle update批量修改sql语句编写
  2. 上海交通大学python教材答案-上海交通大学python期末考试样题加解析.doc
  3. 开学综合症有救了!17篇最新AI论文不容错过
  4. Java读取Excel文件并将之写入数据库操作
  5. 不定方程求解c语言_事业单位考试备考之数量关系:不定方程的求解
  6. 创建委托登录模块(用于JBoss EAP 6.1)
  7. JS ES6中的箭头函数(Arrow Functions)使用
  8. 模线性同余方程组求解
  9. 有关vi(vim)的常用命令
  10. MyBatis集合Spring(二)之SqlSession
  11. 小程序丨canvas内容自适应不同尺寸屏幕
  12. PRML 1.5 决策论
  13. android m4a转mp3格式转换,音频提取格式转换app
  14. 试述HDFS中的名称节点和数据节点的具体功能
  15. 开源项目之freepy自由输入法
  16. 在IE浏览器,icon字体图标不显示
  17. 苹果App Store简介
  18. 树莓派为什么涨价了?
  19. windows关闭端口
  20. 芯云融合,全栈可信:曙光超融合一体机重磅发布

热门文章

  1. 今天是我第一次开通本博客 自己爱好学习破解软件,OEM软件。易语言做软件等等,
  2. 代码质量管理平台SonarQube安装使用Centos7 三 Maven处理提交至远程仓库
  3. 局域网固定内网IP地址的方法(亲测有效)
  4. 紫林U盘解锁精灵V1.0.1
  5. XLD学习(Halcon)
  6. react hooks学习之useState set值后,获取值没有更新
  7. Latex Error: Misplaced alignment tab character
  8. 【opencv】Face Detection using Haar Cascades
  9. 【EMC专题】电快速瞬变脉冲群抗扰度测试
  10. 【uiautomation】微信群发消息,可发送文本 文件