python实现连连看辅助(图像识别)
个人兴趣,用python实现连连看的辅助程序,总结实现过程及知识点。
总体思路
1、获取连连看程序的窗口并前置
2、游戏界面截图,将每个一小图标切图,并形成由小图标组成的二维列表
3、对图片的二维列表遍历,将二维列表转换成由数字组成的二维数组,图片相同的数值相同。
4、遍历二维数组,找到可消除的对象,实现算法:
- 两个图标相邻。(一条线连接)
- 两个图标同行,同列,且中间的图标全部为空(数值为0)(一条线连接)
- 两条线连接,转弯一次,路径上所有图标为空。(二条线连接)
- 三条线连接,转弯二次,路径上所有图标为空。(三条线连接)
- 分别点击两个图标,并将对应的二维数据值置为0
实现过程中遇到的问题
图片切割
im=image.crop((left,top,right,bottom)) //image.crop参数为一个列表或元组,顺序为(left,top,right,bottom)
找到游戏运行窗口
hdwd=win32gui.FindWindow(0,wdname) #设置为最前显示 win32gui.SetForegroundWindow(hdwd)
窗口不要点击最小化,点击后无法弹出来。
- 图片缩放并转为灰度
img1=im1.resize((20,20),Image.ANTIALIAS).convert('L')
Image.ANTIALIAS为抗锯齿的选项,图片无毛边。
- 获取图片每个点的RGB值
pi1=list(img1.getdata())
列表每个元素为一个三位数的值,分别代表该点的RGB值。列表pi1共400个元素。(因为图片为20*20)
- 鼠标点击消除
PyMouse.click()该方法默认双击,改为PyMouse.press()或PyMouse.release()
- 判断图片相似
- 汉明距离,平均哈希
defcompare_img(self,im1,im2): img1=im1.resize((20,20),Image.ANTIALIAS).convert('L') img2=im2.resize((20,20),Image.ANTIALIAS).convert('L') pi1=list(img1.getdata()) pi2=list(img2.getdata()) avg1=sum(pi1)/len(pi1) avg2=sum(pi2)/len(pi2) hash1="".join(map(lambdap:"1"ifp>avg1else"0",pi1)) hash2="".join(map(lambdap:"1"ifp>avg2else"0",pi2)) match=0 foriinrange(len(hash1)): ifhash1[i]!=hash2[i]: match+=1 #match=sum(map(operator.ne,hash1,hash2)) #match值越小,相似度越高 returnmatch
- 计算直方图
fromPILimportImage #将图片转化为RGB defmake_regalur_image(img,size=(8,8)): gray_image=img.resize(size).convert('RGB') returngray_image #计算直方图 defhist_similar(lh,rh): assertlen(lh)==len(rh) hist=sum(1-(0ifl==relsefloat(abs(l-r))/max(l,r))forl,rinzip(lh,rh))/len(lh) returnhist #计算相似度 defcalc_similar(li,ri): calc_sim=hist_similar(li.histogram(),ri.histogram()) returncalc_sim if__name__=='__main__': image1=Image.open('1-10.jpg') image1=make_regalur_image(image1) image2=Image.open('2-11.jpg') image2=make_regalur_image(image2) print("图片间的相似度为",calc_similar(image1,image2)) #值在[0,1]之间,数值越大,相似度越高
- 图片余弦相似度
fromPILimportImage fromnumpyimportaverage,dot,linalg #对图片进行统一化处理 defget_thum(image,size=(64,64),greyscale=False): #利用image对图像大小重新设置,Image.ANTIALIAS为高质量的 image=image.resize(size,Image.ANTIALIAS) ifgreyscale: #将图片转换为L模式,其为灰度图,其每个像素用8个bit表示 image=image.convert('L') returnimage #计算图片的余弦距离 defimage_similarity_vectors_via_numpy(image1,image2): image1=get_thum(image1) image2=get_thum(image2) images=[image1,image2] vectors=[] norms=[] forimageinimages: vector=[] forpixel_tupleinimage.getdata(): vector.append(average(pixel_tuple)) vectors.append(vector) #linalg=linear(线性)+algebra(代数),norm则表示范数 #求图片的范数?? norms.append(linalg.norm(vector,2)) a,b=vectors a_norm,b_norm=norms #dot返回的是点积,对二维数组(矩阵)进行计算 res=dot(a/a_norm,b/b_norm) returnres if__name__=='__main__': image1=Image.open('1-9.jpg') image2=Image.open('8-6.jpg') cosin=image_similarity_vectors_via_numpy(image1,image2) print('图片余弦相似度',cosin) #值在[0,1]之间,数值越大,相似度越高,计算量较大,效率较低
完整代码
importwin32gui importtime fromPILimportImageGrab,Image importnumpyasnp frompymouseimportPyMouse classGameAuxiliaries(object): def__init__(self): self.wdname=r'宠物连连看经典版2,宠物连连看经典版2小游戏,4399小游戏www.4399.com-GoogleChrome' #self.wdname=r'main.swf-PotPlayer' self.image_list={} self.m=PyMouse() deffind_game_wd(self,wdname): #取得窗口句柄 hdwd=win32gui.FindWindow(0,wdname) #设置为最前显示 win32gui.SetForegroundWindow(hdwd) time.sleep(1) defget_img(self): image=ImageGrab.grab((417,289,884,600)) #image=ImageGrab.grab((417,257,885,569)) image.save('1.jpg','JPEG') forxinrange(1,9): self.image_list[x]={} foryinrange(1,13): top=(x-1)*38+(x-2) left=(y-1)*38+(y-2) right=y*38+(y-1) bottom=x*38+(x-1) iftop<0: top=0 ifleft<0: left=0 im_temp=image.crop((left,top,right,bottom)) im=im_temp.crop((1,1,37,37)) im.save('{}-{}.jpg'.format(x,y)) self.image_list[x][y]=im #判断两个图片是否相同。汉明距离,平均哈希 defcompare_img(self,im1,im2): img1=im1.resize((20,20),Image.ANTIALIAS).convert('L') img2=im2.resize((20,20),Image.ANTIALIAS).convert('L') pi1=list(img1.getdata()) pi2=list(img2.getdata()) avg1=sum(pi1)/len(pi1) avg2=sum(pi2)/len(pi2) hash1="".join(map(lambdap:"1"ifp>avg1else"0",pi1)) hash2="".join(map(lambdap:"1"ifp>avg2else"0",pi2)) match=0 foriinrange(len(hash1)): ifhash1[i]!=hash2[i]: match+=1 #match=sum(map(operator.ne,hash1,hash2)) #match值越小,相似度越高 returnmatch #将图片矩阵转换成数字矩阵 defcreate_array(self): array=np.zeros((10,14),dtype=np.int32) img_type_list=[] forrowinrange(1,len(self.image_list)+1): forcolinrange(1,len(self.image_list[1])+1): #im=Image.open('{}-{}.jpg'.format(row,col)) im=self.image_list[row][col] forimginimg_type_list: match=self.compare_img(im,img) #match=test2.image_similarity_vectors_via_numpy(im,img) ifmatch<15: array[row][col]=img_type_list.index(img)+1 break else: img_type_list.append(im) array[row][col]=len(img_type_list) returnarray defrow_zero(self,x1,y1,x2,y2,array): '''相同的图片中间图标全为空''' ifx1==x2: min_y=min(y1,y2) max_y=max(y1,y2) ifmax_y-min_y==1: returnTrue foryinrange(min_y+1,max_y): ifarray[x1][y]!=0: returnFalse returnTrue else: returnFalse defcol_zero(self,x1,y1,x2,y2,array): '''相同的图片同列''' ify1==y2: min_x=min(x1,x2) max_x=max(x1,x2) ifmax_x-min_x==1: returnTrue forxinrange(min_x+1,max_x): ifarray[x][y1]!=0: returnFalse returnTrue else: returnFalse deftwo_line(self,x1,y1,x2,y2,array): '''两条线相连,转弯一次''' forrowinrange(1,9): forcolinrange(1,13): ifrow==x1andcol==y2andarray[row][col]==0andself.row_zero(x1,y1,row,col,array)andself.col_zero(x2,y2,row,col,array): returnTrue ifrow==x2andcol==y1andarray[row][col]==0andself.row_zero(x2,y2,row,col,array)andself.col_zero(x1,y1,row,col,array): returnTrue returnFalse defthree_line(self,x1,y1,x2,y2,array): '''三条线相连,转弯两次''' forrow1inrange(10): forcol1inrange(14): forrow2inrange(10): forcol2inrange(14): ifarray[row1][col1]==array[row2][col2]==0andself.row_zero(x1,y1,row1,col1,array)andself.row_zero(x2,y2,row2,col2,array)andself.col_zero(row1,col1,row2,col2,array): returnTrue ifarray[row1][col1]==array[row2][col2]==0andself.col_zero(x1,y1,row1,col1,array)andself.col_zero(x2,y2,row2,col2,array)andself.row_zero(row1,col1,row2,col2,array): returnTrue ifarray[row1][col1]==array[row2][col2]==0andself.row_zero(x2,y2,row1,col1,array)andself.row_zero(x1,y1,row2,col2,array)andself.col_zero(row1,col1,row2,col2,array): returnTrue ifarray[row1][col1]==array[row2][col2]==0andself.col_zero(x2,y2,row1,col1,array)andself.col_zero(x1,y1,row2,col2,array)andself.row_zero(row1,col1,row2,col2,array): returnTrue returnFalse defmouse_click(self,x,y): top=(x-1)*38+(x-2) left=(y-1)*38+(y-2) right=y*38+(y-1) bottom=x*38+(x-1) iftop<0: top=0 ifleft<0: left=0 self.m.press(int(417+(left+right)/2),int(289+(top+bottom)/2)) deffind_same_img(self,array): forx1inrange(1,9): fory1inrange(1,13): ifarray[x1][y1]==0: continue forx2inrange(1,9): fory2inrange(1,13): ifx1==x2andy1==y2: continue ifarray[x2][y2]==0: continue ifarray[x1][y1]!=array[x2][y2]: continue ifarray[x1][y1]==array[x2][y2]and(self.row_zero(x1,y1,x2,y2,array)orself.col_zero(x1,y1,x2,y2,array)orself.two_line(x1,y1,x2,y2,array)orself.three_line(x1,y1,x2,y2,array)): print("可消除!x{}y{}和x{}y{}".format(x1,y1,x2,y2)) self.mouse_click(x1,y1) time.sleep(0.1) self.mouse_click(x2,y2) time.sleep(0.1) array[x1][y1]=array[x2][y2]=0 defrun(self): #找到游戏运行窗口 self.find_game_wd(self.wdname) #截图,切割成小图标 self.get_img() #将图片矩阵转换成数字矩阵 array=self.create_array() print(array) #遍历矩阵,找到可消除项,点击消除 foriinrange(10): self.find_same_img(array) print(array) if__name__=='__main__': ga=GameAuxiliaries() ga.run()
总结
该程序其实未能完全实现辅助功能,主要是因为图片切割时未找到更好的规则,造成图片识别困难,缩放比例和判断阀值未找到一个平衡点,阀值太大,则将不同的图标识别为相同,阀值太小,相同的图标又判断为不一样。
更多关于python游戏的精彩文章请点击查看以下专题:
python俄罗斯方块游戏集合
python经典小游戏汇总
python微信跳一跳游戏集合
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。