- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
以下脚本来自http://projectproto.blogspot.co.uk/2014/07/opencv-python-2048-game-solver.html
import cv2
import numpy as np
import win32api, win32gui, win32ui, win32con, win32com.client
from PIL import Image, ImageFont, ImageDraw, ImageOps
# create training model based on the given TTF font file
# http://projectproto.blogspot.com/2014/07/opencv-python-digit-recognition.html
def createDigitsModel(fontfile, digitheight):
font = ImageFont.truetype(fontfile, digitheight)
samples = np.empty((0,digitheight*(digitheight/2)))
responses = []
for n in range(10):
pil_im = Image.new("RGB", (digitheight, digitheight*2))
ImageDraw.Draw(pil_im).text((0, 0), str(n), font=font)
pil_im = pil_im.crop(pil_im.getbbox())
pil_im = ImageOps.invert(pil_im)
#pil_im.save(str(n) + ".png")
# convert to cv image
cv_image = cv2.cvtColor(np.array( pil_im ), cv2.COLOR_RGBA2BGRA)
gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray,(5,5),0)
thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)
roi = cv2.resize(thresh,(digitheight,digitheight/2))
responses.append( n )
sample = roi.reshape((1,digitheight*(digitheight/2)))
samples = np.append(samples,sample,0)
samples = np.array(samples,np.float32)
responses = np.array(responses,np.float32)
model = cv2.KNearest()
model.train(samples,responses)
return model
class Board(object):
UP, DOWN, LEFT, RIGHT = 1, 2, 3, 4
FONT = "font/ClearSans-Bold.ttf"
def __init__(self, clientwindowtitle):
self.hwnd = self.getClientWindow(clientwindowtitle)
if not self.hwnd:
return
self.hwndDC = win32gui.GetWindowDC(self.hwnd)
self.mfcDC = win32ui.CreateDCFromHandle(self.hwndDC)
self.saveDC = self.mfcDC.CreateCompatibleDC()
self.cl, self.ct, right, bot = win32gui.GetClientRect(self.hwnd)
self.cw, self.ch = right-self.cl, bot-self.ct
self.cl += win32api.GetSystemMetrics(win32con.SM_CXSIZEFRAME)
self.ct += win32api.GetSystemMetrics(win32con.SM_CYSIZEFRAME)
self.ct += win32api.GetSystemMetrics(win32con.SM_CYCAPTION)
self.ch += win32api.GetSystemMetrics(win32con.SM_CYSIZEFRAME)*2
self.saveBitMap = win32ui.CreateBitmap()
self.saveBitMap.CreateCompatibleBitmap(self.mfcDC, self.cw, self.ch)
self.saveDC.SelectObject(self.saveBitMap)
self.tiles, self.tileheight, self.contour = self.findTiles(self.getClientFrame())
if not len(self.tiles):
return
self.digitheight = self.tileheight / 2
self.digitsmodel = createDigitsModel(self.FONT, self.digitheight)
self.update()
def getClientWindow(self, windowtitle):
toplist, winlist = [], []
def enum_cb(hwnd, results):
winlist.append((hwnd, win32gui.GetWindowText(hwnd)))
win32gui.EnumWindows(enum_cb, toplist)
window = [(hwnd, title) for hwnd, title in winlist if windowtitle.lower() in title.lower()]
if not len(window):
return 0
return window[0][0]
def getClientFrame(self):
self.saveDC.BitBlt((0, 0), (self.cw, self.ch),
self.mfcDC, (self.cl, self.ct), win32con.SRCCOPY)
bmpinfo = self.saveBitMap.GetInfo()
bmpstr = self.saveBitMap.GetBitmapBits(True)
pil_img = Image.frombuffer( 'RGB',
(bmpinfo['bmWidth'], bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRX', 0, 1)
array = np.array( pil_img )
cvimage = cv2.cvtColor(array, cv2.COLOR_RGBA2BGRA)
return cvimage
def findTiles(self, cvframe):
tiles, avgh = [], 0
gray = cv2.cvtColor(cvframe,cv2.COLOR_BGRA2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,1,1,11,2)
contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
def findBoard(contours): # get largest square
ww, sqcnt = 10, None
for cnt in contours:
x,y,w,h = cv2.boundingRect(cnt)
if w>ww and abs(w-h)<w/10:
ww = w
sqcnt = cnt
return sqcnt
board = findBoard(contours)
if board==None:
print 'board not found!'
return tiles, avgh, board
bx,by,bw,bh = cv2.boundingRect(board)
#cv2.rectangle(cvframe,(bx,by),(bx+bw,by+bh),(0,255,0),2)
#cv2.imshow('board',cvframe)
#cv2.waitKey(0)
#cv2.destroyWindow( 'board' )
maxh = bh/4
minh = (maxh*4)/5
count = 0
for contour in contours:
x,y,w,h = cv2.boundingRect(contour)
if y>by and w>minh and w<maxh and h>minh and h<maxh:
avgh += h
count += 1
if not count:
print 'no tile found!'
return tiles, avgh, board
avgh = avgh / count
margin = (bh-avgh*4)/5
for row in range(4):
for col in range(4):
x0 = bx + avgh*col + margin*(col+1)
x1 = x0 + avgh
y0 = by + avgh*row + margin*(row+1)
y1 = y0 + avgh
tiles.append([x0, y0, x1, y1])
#cv2.rectangle(cvframe,(x0,y0),(x1,y1),(0,255,0),2)
#cv2.imshow('tiles',cvframe)
#cv2.waitKey(0)
#cv2.destroyWindow( 'tiles' )
return tiles, avgh, board
def getTileThreshold(self, tileimage):
gray = cv2.cvtColor(tileimage,cv2.COLOR_BGR2GRAY)
row, col = gray.shape
tmp = gray.copy().reshape(1, row*col)
counts = np.bincount(tmp[0])
sort = np.sort(counts)
modes, freqs = [], []
for i in range(len(sort)):
freq = sort[-1-i]
if freq < 4:
break
mode = np.where(counts==freq)[0][0]
modes.append(mode)
freqs.append(freq)
bg, fg = modes[0], modes[0]
for i in range(len(modes)):
fg = modes[i]
#if abs(bg-fg)>=48:
if abs(bg-fg)>32 and abs(fg-150)>4: # 150?!
break
#print bg, fg
if bg>fg: # needs dark background ?
tmp = 255 - tmp
bg, fg = 255-bg, 255-fg
tmp = tmp.reshape(row, col)
ret, thresh = cv2.threshold(tmp,(bg+fg)/2,255,cv2.THRESH_BINARY)
return thresh
def getTileNumbers(self, cvframe):
numbers = []
outframe = np.zeros(cvframe.shape,np.uint8)
def guessNumber(digits):
for i in range(1,16):
nn = 2**i
ss = str(nn)
dd = [int(c) for c in ss]
if set(digits) == set(dd):
return nn
return 0
for tile in self.tiles:
x0,y0,x1,y1 = tile
tileimage = cvframe[y0:y1,x0:x1]
cv2.rectangle(cvframe,(x0,y0),(x1,y1),(0,255,0),2)
cv2.rectangle(outframe,(x0,y0),(x1,y1),(0,255,0),1)
thresh = self.getTileThreshold(tileimage)
contours,hierarchy = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
dh = self.digitheight
digits = []
for cnt in contours:
x,y,w,h = cv2.boundingRect(cnt)
if h>w and h>(dh*1)/5 and h<(dh*6)/5:
cv2.rectangle(cvframe,(x0+x,y0+y),(x0+x+w,y0+y+h),(0,0,255),1)
roi = thresh[y:y+h,x:x+w]
roi = cv2.resize(roi,(dh,dh/2))
roi = roi.reshape((1,dh*(dh/2)))
roi = np.float32(roi)
retval, results, neigh_resp, dists = self.digitsmodel.find_nearest(roi, k=1)
digit = int((results[0][0]))
string = str(digit)
digits.append(digit)
cv2.putText(outframe,string,(x0+x,y0+y+h),0,float(h)/24,(0,255,0))
numbers.append(guessNumber(digits))
return numbers, outframe
def getWindowHandle(self):
return self.hwnd
def getBoardContour(self):
return self.contour
def update(self):
frame = self.getClientFrame()
self.tilenumbers, outframe = self.getTileNumbers(frame)
return self.tilenumbers, frame, outframe
def copyTileNumbers(self):
return self.tilenumbers[:]
def getCell(self, tiles, x, y):
return tiles[(y*4)+x]
def setCell(self, tiles, x, y, v):
tiles[(y*4)+x] = v
return tiles
def getCol(self, tiles, x):
return [self.getCell(tiles, x, i) for i in range(4)]
def setCol(self, tiles, x, col):
for i in range(4):
self.setCell(tiles, x, i, col[i])
return tiles
def getLine(self, tiles, y):
return [self.getCell(tiles, i, y) for i in range(4)]
def setLine(self, tiles, y, line):
for i in range(4):
self.setCell(tiles, i, y, line[i])
return tiles
def validMove(self, tilenumbers, direction):
if direction == self.UP or direction == self.DOWN:
for x in range(4):
col = self.getCol(tilenumbers, x)
for y in range(4):
if(y < 4-1 and col[y] == col[y+1] and col[y]!=0):
return True
if(direction == self.DOWN and y > 0 and col[y] == 0 and col[y-1]!=0):
return True
if(direction == self.UP and y < 4-1 and col[y] == 0 and col[y+1]!=0):
return True
if direction == self.LEFT or direction == self.RIGHT:
for y in range(4):
line = self.getLine(tilenumbers, y)
for x in range(4):
if(x < 4-1 and line[x] == line[x+1] and line[x]!=0):
return True
if(direction == self.RIGHT and x > 0 and line[x] == 0 and line[x-1]!=0):
return True
if(direction == self.LEFT and x < 4-1 and line[x] == 0 and line[x+1]!=0):
return True
return False
def moveTileNumbers(self, tilenumbers, direction):
def collapseline(line, direction):
if (direction==self.LEFT or direction==self.UP):
inc = 1
rg = xrange(0, 4-1, inc)
else:
inc = -1
rg = xrange(4-1, 0, inc)
pts = 0
for i in rg:
if line[i] == 0:
continue
if line[i] == line[i+inc]:
v = line[i]*2
line[i] = v
line[i+inc] = 0
pts += v
return line, pts
def moveline(line, directsion):
nl = [c for c in line if c != 0]
if directsion==self.UP or directsion==self.LEFT:
return nl + [0] * (4 - len(nl))
return [0] * (4 - len(nl)) + nl
score = 0
if direction==self.LEFT or direction==self.RIGHT:
for i in range(4):
origin = self.getLine(tilenumbers, i)
line = moveline(origin, direction)
collapsed, pts = collapseline(line, direction)
new = moveline(collapsed, direction)
tilenumbers = self.setLine(tilenumbers, i, new)
score += pts
elif direction==self.UP or direction==self.DOWN:
for i in range(4):
origin = self.getCol(tilenumbers, i)
line = moveline(origin, direction)
collapsed, pts = collapseline(line, direction)
new = moveline(collapsed, direction)
tilenumbers = self.setCol(tilenumbers, i, new)
score += pts
return score, tilenumbers
# AI based on "term2048-AI"
# https://github.com/Nicola17/term2048-AI
class AI(object):
def __init__(self, board):
self.board = board
def nextMove(self):
tilenumbers = self.board.copyTileNumbers()
m, s = self.nextMoveRecur(tilenumbers[:],3,3)
return m
def nextMoveRecur(self, tilenumbers, depth, maxDepth, base=0.9):
bestMove, bestScore = 0, -1
for m in range(1,5):
if(self.board.validMove(tilenumbers, m)):
score, newtiles = self.board.moveTileNumbers(tilenumbers[:], m)
score, critical = self.evaluate(newtiles)
newtiles = self.board.setCell(newtiles,critical[0],critical[1],2)
if depth != 0:
my_m,my_s = self.nextMoveRecur(newtiles[:],depth-1,maxDepth)
score += my_s*pow(base,maxDepth-depth+1)
if(score > bestScore):
bestMove = m
bestScore = score
return bestMove, bestScore
def evaluate(self, tilenumbers, commonRatio=0.25):
maxVal = 0.
criticalTile = (-1, -1)
for i in range(8):
linearWeightedVal = 0
invert = False if i<4 else True
weight = 1.
ctile = (-1,-1)
cond = i%4
for y in range(4):
for x in range(4):
if cond==0:
b_x = 4-1-x if invert else x
b_y = y
elif cond==1:
b_x = x
b_y = 4-1-y if invert else y
elif cond==2:
b_x = 4-1-x if invert else x
b_y = 4-1-y
elif cond==3:
b_x = 4-1-x
b_y = 4-1-y if invert else y
currVal=self.board.getCell(tilenumbers,b_x,b_y)
if(currVal == 0 and ctile == (-1,-1)):
ctile = (b_x,b_y)
linearWeightedVal += currVal*weight
weight *= commonRatio
invert = not invert
if linearWeightedVal > maxVal:
maxVal = linearWeightedVal
criticalTile = ctile
return maxVal, criticalTile
def solveBoard(self, moveinterval=500):
boardHWND = self.board.getWindowHandle()
if not boardHWND:
return False
bx, by, bw, bh = cv2.boundingRect(self.board.getBoardContour())
x0, x1, y0, y1 = bx, bx+bw, by, by+bh
win32gui.SetForegroundWindow(boardHWND)
shell = win32com.client.Dispatch('WScript.Shell')
print 'Set the focus to the Game Window, and the press this arrow key:'
keymove = ['UP', 'DOWN', 'LEFT', 'RIGHT']
delay = moveinterval / 3 # milliseconds delay to cancel board animation effect
prev_numbers = []
while True:
numbers, inframe, outframe = self.board.update()
if numbers != prev_numbers:
cv2.waitKey(delay)
numbers, inframe, outframe = self.board.update()
if numbers == prev_numbers: # recheck if has changed
continue
prev_numbers = numbers
move = ai.nextMove()
if move:
key = keymove[move-1]
shell.SendKeys('{%s}'%key)
print key
cv2.waitKey(delay)
cv2.imshow('CV copy',inframe[y0:y1,x0:x1])
cv2.imshow('CV out', outframe[y0:y1,x0:x1])
cv2.waitKey(delay)
cv2.destroyWindow( 'CV copy' )
cv2.destroyWindow( 'CV out' )
# http://gabrielecirulli.github.io/2048/
# http://ov3y.github.io/2048-AI/
board = Board("2048 - Google Chrome")
#board = Board("2048 - Mozilla Firefox")
ai = AI(board)
ai.solveBoard(360)
print 'stopped.'
我用示例 URL 打开 Google Chrome http://ov3y.github.io/2048-AI/打开,运行脚本出现如下错误:
20.py:109: FutureWarning: comparison to `None` will result in an elementwise object comparison in the future.
if board==None:
no tile found!
Set the focus to the Game Window, and the press this arrow key:
然后什么也没有,它只是坐在那里。所以我最关心的部分是 no tile found!
。取消注释行:
#cv2.rectangle(cvframe,(bx,by),(bx+bw,by+bh),(0,255,0),2)
#cv2.imshow('board',cvframe)
#cv2.waitKey(0)
#cv2.destroyWindow( 'board' )
在屏幕上显示以下窗口:
谁能解释为什么 OpenCV 无法检测到网格,或者如何进行调试?
最佳答案
很可能这不是检测网格的问题,而是捕获浏览器窗口的问题 - 您试图在空图像上查找网格,这当然会失败。首先确保您已正确抓取 firefox/chrome/opera 屏幕窗口 - 在函数 getClientFrame(self)
中放置此代码:
cv2.imshow('browser window', cvimage)
cv2.waitKey(10000)
就在最后的 return cvimage
之前。它应该向您显示浏览器窗口 10 秒钟。如果不是,那么它将 100% 确定问题出在捕获浏览器窗口,而不是检测网格。要检查捕获浏览器窗口有什么问题,请使用 win32api.GetLastError()
函数(您可以检查错误代码 here )。
当然有可能我错了,这是检测网格的问题 - 如果是这样,请提供示例图像(只需保存我提供的代码显示的图像)以便我们进行测试。
\\编辑:
我刚刚注意到你帖子的第二部分 - 所以很可能我错了,但你仍然可以测试它。看起来您正在捕获一个 chrome 窗口和其他窗口的一部分 - 尝试让您的浏览器窗口全屏显示。
仔细查看您的图像后,我发现了一件奇怪的事情 - 捕获的图像有垂直线并且宽度(右侧没有重复部分)小于原始窗口(但高度似乎没问题)。宽度似乎是原始宽度的 75%,所以我猜想 PIL 将每 4 个字节视为一个像素,但每个像素应该只使用 3 个字节。很难对其进行测试,因为在我的系统(win 8.1 64 位)上它运行良好。可能的解决方案(我无法测试它们,所以您需要检查哪一个可以工作..抱歉 :) ):
尝试改变这一行:
pil_img = Image.frombuffer( 'RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
像这样:pil_img = Image.frombuffer( 'RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGR', 0, 1)通常,您需要将第五个参数的值从 BGRX
更改为其他值 - 最有可能更改为“BGR”,完整的选项列表为 here .如果它不起作用,请尝试使用不同的第一个和第五个参数值。
关于Python/OpenCV - 不检测网格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28880784/
我正在处理一组标记为 160 个组的 173k 点。我想通过合并最接近的(到 9 或 10 个组)来减少组/集群的数量。我搜索过 sklearn 或类似的库,但没有成功。 我猜它只是通过 knn 聚类
我有一个扁平数字列表,这些数字逻辑上以 3 为一组,其中每个三元组是 (number, __ignored, flag[0 or 1]),例如: [7,56,1, 8,0,0, 2,0,0, 6,1,
我正在使用 pipenv 来管理我的包。我想编写一个 python 脚本来调用另一个使用不同虚拟环境(VE)的 python 脚本。 如何运行使用 VE1 的 python 脚本 1 并调用另一个 p
假设我有一个文件 script.py 位于 path = "foo/bar/script.py"。我正在寻找一种在 Python 中通过函数 execute_script() 从我的主要 Python
这听起来像是谜语或笑话,但实际上我还没有找到这个问题的答案。 问题到底是什么? 我想运行 2 个脚本。在第一个脚本中,我调用另一个脚本,但我希望它们继续并行,而不是在两个单独的线程中。主要是我不希望第
我有一个带有 python 2.5.5 的软件。我想发送一个命令,该命令将在 python 2.7.5 中启动一个脚本,然后继续执行该脚本。 我试过用 #!python2.7.5 和http://re
我在 python 命令行(使用 python 2.7)中,并尝试运行 Python 脚本。我的操作系统是 Windows 7。我已将我的目录设置为包含我所有脚本的文件夹,使用: os.chdir("
剧透:部分解决(见最后)。 以下是使用 Python 嵌入的代码示例: #include int main(int argc, char** argv) { Py_SetPythonHome
假设我有以下列表,对应于及时的股票价格: prices = [1, 3, 7, 10, 9, 8, 5, 3, 6, 8, 12, 9, 6, 10, 13, 8, 4, 11] 我想确定以下总体上最
所以我试图在选择某个单选按钮时更改此框架的背景。 我的框架位于一个类中,并且单选按钮的功能位于该类之外。 (这样我就可以在所有其他框架上调用它们。) 问题是每当我选择单选按钮时都会出现以下错误: co
我正在尝试将字符串与 python 中的正则表达式进行比较,如下所示, #!/usr/bin/env python3 import re str1 = "Expecting property name
考虑以下原型(prototype) Boost.Python 模块,该模块从单独的 C++ 头文件中引入类“D”。 /* file: a/b.cpp */ BOOST_PYTHON_MODULE(c)
如何编写一个程序来“识别函数调用的行号?” python 检查模块提供了定位行号的选项,但是, def di(): return inspect.currentframe().f_back.f_l
我已经使用 macports 安装了 Python 2.7,并且由于我的 $PATH 变量,这就是我输入 $ python 时得到的变量。然而,virtualenv 默认使用 Python 2.6,除
我只想问如何加快 python 上的 re.search 速度。 我有一个很长的字符串行,长度为 176861(即带有一些符号的字母数字字符),我使用此函数测试了该行以进行研究: def getExe
list1= [u'%app%%General%%Council%', u'%people%', u'%people%%Regional%%Council%%Mandate%', u'%ppp%%Ge
这个问题在这里已经有了答案: Is it Pythonic to use list comprehensions for just side effects? (7 个答案) 关闭 4 个月前。 告
我想用 Python 将两个列表组合成一个列表,方法如下: a = [1,1,1,2,2,2,3,3,3,3] b= ["Sun", "is", "bright", "June","and" ,"Ju
我正在运行带有最新 Boost 发行版 (1.55.0) 的 Mac OS X 10.8.4 (Darwin 12.4.0)。我正在按照说明 here构建包含在我的发行版中的教程 Boost-Pyth
学习 Python,我正在尝试制作一个没有任何第 3 方库的网络抓取工具,这样过程对我来说并没有简化,而且我知道我在做什么。我浏览了一些在线资源,但所有这些都让我对某些事情感到困惑。 html 看起来
我是一名优秀的程序员,十分优秀!