我正在使用OS X 10.8.2在Python中構建自動化遊戲機器人程序,並且在研究Python GUI自動化的過程中發現了autopy。鼠標操作的API是偉大的,但似乎屏幕捕獲方法依賴於不推薦的OpenGL方法...Python在OS X中獲取屏幕像素值
是否有任何有效的方法獲取OS X中的像素的顏色值?我現在能想到的唯一方法是使用os.system("screencapture foo.png")
,但這個過程似乎沒有必要開銷,因爲我將很快進行輪詢。
我正在使用OS X 10.8.2在Python中構建自動化遊戲機器人程序,並且在研究Python GUI自動化的過程中發現了autopy。鼠標操作的API是偉大的,但似乎屏幕捕獲方法依賴於不推薦的OpenGL方法...Python在OS X中獲取屏幕像素值
是否有任何有效的方法獲取OS X中的像素的顏色值?我現在能想到的唯一方法是使用os.system("screencapture foo.png")
,但這個過程似乎沒有必要開銷,因爲我將很快進行輪詢。
小的改進,但使用TIFF壓縮選項screencapture
是有點快:
$ time screencapture -t png /tmp/test.png
real 0m0.235s
user 0m0.191s
sys 0m0.016s
$ time screencapture -t tiff /tmp/test.tiff
real 0m0.079s
user 0m0.028s
sys 0m0.026s
這確實有很大的開銷,就像你說的(子進程創建,寫作/從光盤讀取,壓縮/解壓縮)。
相反,您可以使用PyObjC來使用CGWindowListCreateImage
來捕獲屏幕。我發現,花了大約70毫秒(〜14fps)捕捉1680×1050像素的屏幕,並且在內存
一些隨機的筆記訪問的值:
Quartz.CoreGraphics
模塊是最慢的部分,約1第二。導入大部分PyObjC模塊也是如此。在這種情況下不太可能出現問題,但對於短暫的過程,您可能會更好地在ObjC中編寫工具。指定較小的區域會更快一些,但不是很大(對於100x100像素塊,約爲40毫秒,對於1680x1050約爲70毫秒) 。大部分時間似乎只花在CGDataProviderCopyData
調用 - 我想知道是否有方法直接訪問數據,因爲我們不需要修改它?ScreenPixel.pixel
功能非常快,但訪問大量的像素仍然很慢(因爲0.01ms * 1650*1050
大約17秒) - 如果您需要訪問大量的像素,可能會更快,他們都在一起去。下面的代碼:
import time
import struct
import Quartz.CoreGraphics as CG
class ScreenPixel(object):
"""Captures the screen using CoreGraphics, and provides access to
the pixel values.
"""
def capture(self, region = None):
"""region should be a CGRect, something like:
>>> import Quartz.CoreGraphics as CG
>>> region = CG.CGRectMake(0, 0, 100, 100)
>>> sp = ScreenPixel()
>>> sp.capture(region=region)
The default region is CG.CGRectInfinite (captures the full screen)
"""
if region is None:
region = CG.CGRectInfinite
else:
# TODO: Odd widths cause the image to warp. This is likely
# caused by offset calculation in ScreenPixel.pixel, and
# could could modified to allow odd-widths
if region.size.width % 2 > 0:
emsg = "Capture region width should be even (was %s)" % (
region.size.width)
raise ValueError(emsg)
# Create screenshot as CGImage
image = CG.CGWindowListCreateImage(
region,
CG.kCGWindowListOptionOnScreenOnly,
CG.kCGNullWindowID,
CG.kCGWindowImageDefault)
# Intermediate step, get pixel data as CGDataProvider
prov = CG.CGImageGetDataProvider(image)
# Copy data out of CGDataProvider, becomes string of bytes
self._data = CG.CGDataProviderCopyData(prov)
# Get width/height of image
self.width = CG.CGImageGetWidth(image)
self.height = CG.CGImageGetHeight(image)
def pixel(self, x, y):
"""Get pixel value at given (x,y) screen coordinates
Must call capture first.
"""
# Pixel data is unsigned char (8bit unsigned integer),
# and there are for (blue,green,red,alpha)
data_format = "BBBB"
# Calculate offset, based on
# http://www.markj.net/iphone-uiimage-pixel-color/
offset = 4 * ((self.width*int(round(y))) + int(round(x)))
# Unpack data from string into Python'y integers
b, g, r, a = struct.unpack_from(data_format, self._data, offset=offset)
# Return BGRA as RGBA
return (r, g, b, a)
if __name__ == '__main__':
# Timer helper-function
import contextlib
@contextlib.contextmanager
def timer(msg):
start = time.time()
yield
end = time.time()
print "%s: %.02fms" % (msg, (end-start)*1000)
# Example usage
sp = ScreenPixel()
with timer("Capture"):
# Take screenshot (takes about 70ms for me)
sp.capture()
with timer("Query"):
# Get pixel value (takes about 0.01ms)
print sp.width, sp.height
print sp.pixel(0, 0)
# To verify screen-cap code is correct, save all pixels to PNG,
# using http://the.taoofmac.com/space/projects/PNGCanvas
from pngcanvas import PNGCanvas
c = PNGCanvas(sp.width, sp.height)
for x in range(sp.width):
for y in range(sp.height):
c.point(x, y, color = sp.pixel(x, y))
with open("test.png", "wb") as f:
f.write(c.dump())
我碰到這個職位來到同時尋找解決的辦法得到的截屏,用於實時處理的Mac OS X。我曾嘗試使用PIL中的ImageGrab,正如其他一些帖子中所建議的,但無法獲得足夠快的數據(僅約0.5 fps)。
答案https://stackoverflow.com/a/13024603/3322123在這篇文章中使用PyObjC救了我一天!謝謝@dbr!
但是,我的任務需要獲取所有像素值而不是單個像素,並且還要通過@dbr評論第三個註釋,我在該類中添加了一個新方法以獲取完整圖像,以防萬一別的可能需要它。
圖像數據以尺寸爲(高度,寬度,3)的numpy數組形式返回,可以直接用於numpy或opencv等後期處理...從中獲取單個像素值也變得非常簡單numpy索引。我測試了1600 x 1000屏幕截圖的代碼 - 使用capture()獲取數據約30 ms並將其轉換爲np數組getimage()在我的Macbook上只需要約50 ms。所以現在我有> 10 fps甚至更小的區域更快。
import numpy as np
def getimage(self):
imgdata=np.fromstring(self._data,dtype=np.uint8).reshape(len(self._data)/4,4)
return imgdata[:self.width*self.height,:-1].reshape(self.height,self.width,3)
注意我扔掉BGRA 4通道的「alpha」通道。
寧靜的話題;你在製作一個機器人遊戲? – tMC
'autopy.color.hex_to_rgb(autopy.screen.get_color(1,1))'? – tMC
Bejeweled Blitz,它是一個AI項目課程。 如果您查看已棄用函數的源代碼,autopy中的所有screengrab函數都會返回黑色。 – itsachen