🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
http://[blog.csdn.net/pipisorry/article/details/46564967](http://blog.csdn.net/pipisorry/article/details/46564967) 找茬游戏地址[[美女大家来找茬](http://www.3366.com/swf.html?gid=72567&type=0&needframe=0&open=1&FlashParam=&IFrameName=&basedirexp=&adid=3366_1_15168&apiflag=1&NeedOutLink=0)] ### 游戏窗口探查 下载安装PyWin32库(对windows接口的Python封装)[http://sourceforge.net/projects/pywin32/](http://sourceforge.net/projects/pywin32/),但不能直接点Download图标,不然下下来是一个Readme.txt,点“[Browse All Files](http://sourceforge.net/projects/pywin32/files/ "Browse All Files")”寻找需要的版本。 使用spy++找到窗口句柄(或者找到窗口类名lpClassName和窗口名lpWindowName)[[python指定窗口截图-spy++用法](http://blog.csdn.net/pipisorry/article/details/46559139)] ~~~ hwnd = win32gui.FindWindow("MozillaWindowClass", "游戏全屏 - Mozilla Firefox") if not hwnd: print(RED, 'window not found!', DEFAULT) else: print(hwnd) # hwnd = 918912 #或者直接使用句柄 ~~~ **Note**: 1. **FindWindow函数:** FindWindow这个函数检索处理顶级窗口的类名和窗口名称匹配指定的字符串。这个函数不搜索子窗口。 函数功能:该函数获得一个顶层窗口的句柄,该窗口的类名和窗口名与给定的字符串相匹配。这个函数不查找子窗口。在查找时不区分大小写。 函数型:HWND FindWindow(LPCTSTR IpClassName,LPCTSTR IpWindowName); 参数:     IpClassName :指向一个指定了类名的空结束字符串,或一个标识类名字符串的成员的指针。如果该参数为一个成员,则它必须为前次调用theGlobafAddAtom函数产生的全局成员。该成员为16位,必须位于IpClassName的低16位,高位必须为0。     IpWindowName:指向一个指定了窗口名(窗口标题)的空结束字符串。如果该参数为空,则为所有窗口全匹配。     返回值:如果函数成功,返回值为具有指定类名和窗口名的窗口句柄;如果函数失败,返回值为NULL。 [[FindWindow用法](http://blog.csdn.net/coolszy/article/details/5523486)] [[findwindow](http://baike.baidu.com/view/373605.htm)-baike.baidu] 2. **句柄** 直接使用句柄可能的问题: 1)firefox中每个页面tab都是同一个句柄,程序中找到的句柄对应当前tab,如果当前tab不是找茬游戏就不对了 2)使用窗口类名lpClassName和窗口名lpWindowName时如果当前tab不是找茬游戏会找不到相应句柄 上面问题一个有效的解决方法是将找茬游戏单独在新窗口中打开 也可以使用spy++反向从句柄找到句柄对应窗口,检查是否正确。 ### 游戏图片提取 提取图片采用了截屏的方式,找到窗口后将窗口提到最前,再作窗口截屏。 下载安装PIL图形处理库[[linux和windows下安装python拓展包-...PIL、pythonqt...](http://blog.csdn.net/pipisorry/article/details/39902327)] ~~~ win32gui.ShowWindow(hwnd, win32con.SW_MAXIMIZE) # 强行显示界面后才好截图,SW_RESTORE初始大小 win32gui.SetForegroundWindow(hwnd) # 将窗口提到最前 # 裁剪得到全图 game_rect = win32gui.GetWindowRect(hwnd) # src_image = ImageGrab.grab(game_rect) src_image = ImageGrab.grab((game_rect[0] + 20, game_rect[1] + 176, game_rect[2] - 523, game_rect[1] + 176 + 514)) # src_image.show() width, hight = src_image.size # 分别裁剪左右内容图片 left_box = (0, 0, width // 2 + 1, hight) right_box = (width // 2 + 1, 0, width, hight) image_left = src_image.crop(left_box) image_right = src_image.crop(right_box) # image_left.show() # image_right.show() ~~~ **Note**: 1.**win32gui.GetWindowRect函数** ```l,t,r,b ``=``win32gui.GetWindowRect(``self``.hwnd)`    #返回图形左、上、右、下边界(或者说是左上、右下的坐标点) 2.**ImageGrab.grab函数**:ImageGrab是PIL的一个模块,用于图像的抓取。不带参数的ImageGrab.grab()进行全屏截屏,返回一个Image对象,也可使用一个元组作为参数指定要截取的范围(左上与右下两点的坐标),这两种截屏都是不带鼠标指针的。 上面用到的坐标都为为了演示代码简单填的,实际上可使用了变量参数,而且要区分分辨率什么的。 还有一个ImageGrab.grabclipboard()可从系统剪贴板采集图像。 3. 得到Image图像后可用**show()方法**,使用系统默认的图像查看工具打开,方便调试。 也可以用save(filename)保存成文件,对应的可以Image.open(filename)打开获得。 4. **crop(box)方法**:参数为左上和右下标点坐标(或者说是左、上、右、下边界) grab得到了一个包含左右图片的Image对象后,用crop(box)方法可裁剪得到其中指定的区域,分别拿到左右两个游戏图片。 **对比获得两图内容不同的区域** 把两图裁剪成N个小图片分别对比,左右统一区域对应的小图片不相等则为“茬”区,问题是怎么判断两个图片内容不一致? **Image.histogram()函数:**用于得到图像的颜色直方图。直方图可以表示一张图片中各种亮度(或颜色)的数量,两张自然图片的直方图基本是不一样的,除非两图对称、颜色一致但排列不一,但就算如此,将两图继续分割下去,其子图的直方图也会不一样。直方图就是一种图形到数值的转换,对比两图的颜色数值就可知是否存在差异。一张用RBG颜色格式的图像,histogram()函数将返回一个长度为768的数组,第0-255表示红色的0-255,第256-511表色绿色的0-255,第512-767表色蓝色的0-255,**数值表示该颜色像素的个数**。因此,histogram()列表所有成员之和等于改图像的像素值 x 3。 **用来获得两图比较的数值差的函数** ~~~ def channel_compare(cha_a, cha_b): ''' 比较两个颜色通道的差异值并返回 ''' sum_a = sum([i * v for i, v in enumerate(cha_a)]) sum_b = sum([i * v for i, v in enumerate(cha_b)]) # red_a = 0 # red_b = 0 # for i in range(0, 256): # red_a += histogram_a[i + 0] * i # red_b += histogram_b[i + 0] * i if sum_a + sum_b > 0: diff_channel = abs(sum_a - sum_b) * 10000 / max(sum_a, sum_b) return diff_channel def image_compare(image_a, image_b): ''' 返回两图的差异值, 返回两图红绿蓝差值万分比之和 ''' histogram_a = image_a.histogram() histogram_b = image_b.histogram() if len(histogram_a) != 768 or len(histogram_b) != 768: print(RED, "get histogram error", DEFAULT) return None print([i * v for i, v in enumerate(histogram_a)][0:10]) diff_red = channel_compare(histogram_a[:256], histogram_b[:256]) diff_green = channel_compare(histogram_a[256:512], histogram_b[256:512]) diff_blue = channel_compare(histogram_a[512:768], histogram_b[512:768]) return diff_red, diff_green, diff_blue ~~~ **Note**:将函数返回的红绿蓝差值相加,如果超过了预定定的阀值2000,则表示该区域不同。这个计算方式有点“土”,但对这次要解决的问题很有效,就没再继续改进。 **把两图裁剪成N个小图片分别对比** ~~~ # 将左右大图裁剪成多个小图分别进行对比 result = zeros((width // 10, hight // 10)) for col in range(0, width // 10): for row in range(0, hight // 10): clip_box = (col * 10, row * 10, (col + 1) * 10, (row + 1) * 10) clip_image_left = image_left.crop(clip_box) clip_image_right = image_right.crop(clip_box) clip_diff = image_compare(clip_image_left, clip_image_right) if sum(clip_diff) > 2000: result[row][col] = 1 ~~~ **Note**:大图是780*520,分隔成10x10的小块,定义一个78*52的二位数组存储结果,分别比较后将差值大于阀值的数组区域标记为1. **在游戏上标记两边不同的区域** 可以使用PyWin32的函数,获得游戏窗口句柄后直接在上面绘制,但要熟悉Windows编程,解决游戏自身重绘后将我的标记擦除的问题。 也可以使用Qt。下面用Qt创建了一个和游戏大小一样透明的QWidget窗口,叠加在游戏窗口上,用遮罩来绘制标记。标记数据已记录在result数组中,在指定的位置绘制一个方格则表示该区域左右不同,要注意两个方格间的边界不要绘制,避免格子太多干扰了游戏。除标记外,还绘制了两个按钮来触发对比与擦除。 **Note**: 1. 这里没有替换变量,清楚算法就行。 2. The **QPixmap **class is an off-screen image representation that canbe used as a paint device. from:[http://blog.csdn.net/pipisorry/article/details/46564967](http://blog.csdn.net/pipisorry/article/details/46564967) ref:[PIL Document](http://www.pythonware.com/library/pil/handbook/index.htm)