[英]Python Get Screen Pixel Value in OS X
我正在 OS X 10.8.2 上用 Python 構建一個自動化游戲機器人,在研究 Python GUI 自動化的過程中我發現了 autopy。 鼠標操作 API 很棒,但屏幕捕獲方法似乎依賴於已棄用的 OpenGL 方法......
有什么有效的方法可以在 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
捕獲屏幕。 我發現捕獲一個 1680x1050 像素的屏幕大約需要 70 毫秒(~14fps),並且可以在內存中訪問這些值
一些隨機筆記:
Quartz.CoreGraphics
模塊是最慢的部分,大約 1 秒。 導入大多數 PyObjC 模塊也是如此。 在這種情況下不太重要,但對於短暫的流程,您最好用 ObjC 編寫工具CGDataProviderCopyData
調用上——我想知道是否有直接訪問數據的方法,因為我們不需要修改它?ScreenPixel.pixel
函數非常快,但訪問大量像素仍然很慢(因為0.01ms * 1650*1050
大約需要 17 秒) - 如果您需要訪問大量像素,可能更快地struct.unpack_from
them all in一去。這是代碼:
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)。
這篇文章中使用 PyObjC 的答案https://stackoverflow.com/a/13024603/3322123拯救了我! 謝謝@dbr!
但是,我的任務需要獲取所有像素值,而不僅僅是單個像素,並且還要評論@dbr 的第三個注釋,我在此類中添加了一個新方法來獲取完整圖像,以防其他人可能需要它.
圖像數據作為維度為(高度、寬度、3)的 numpy 數組返回,可以直接用於 numpy 或 opencv 等中的后處理……使用 numpy 索引從中獲取單個像素值也變得非常簡單。
我用 1600 x 1000 的屏幕截圖測試了代碼——在我的 Macbook 上使用 capture() 獲取數據花費了約 30 毫秒並將其轉換為 np 數組 getimage() 僅花費了約 50 毫秒。 所以現在我有 >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”通道。
這一切都非常有幫助,我不得不回來發表評論/但是我沒有聲譽..但是,我有一個示例代碼,上面的答案組合用於閃電般的快速屏幕捕獲/保存感謝@dbr 和@qqg!
import time
import numpy as np
from scipy.misc import imsave
import Quartz.CoreGraphics as CG
image = CG.CGWindowListCreateImage(CG.CGRectInfinite, CG.kCGWindowListOptionOnScreenOnly, CG.kCGNullWindowID, CG.kCGWindowImageDefault)
prov = CG.CGImageGetDataProvider(image)
_data = CG.CGDataProviderCopyData(prov)
width = CG.CGImageGetWidth(image)
height = CG.CGImageGetHeight(image)
imgdata=np.fromstring(_data,dtype=np.uint8).reshape(len(_data)/4,4)
numpy_img = imgdata[:width*height,:-1].reshape(height,width,3)
imsave('test_fast.png', numpy_img)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.