Te recomendamos que revises esta resolución en un ambiente controlado antes de enviarlo a producción, un saludo.
Solución:
Una pequeña mejora, pero usando la opción de compresión TIFF para screencapture
es un poco más rápido:
$ 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
Esto tiene mucha sobrecarga, como usted dice (la creación de subprocesos, escritura / lectura de disco, compresión / descompresión).
En su lugar, puede usar PyObjC para capturar la pantalla usando CGWindowListCreateImage
. Descubrí que tomó alrededor de 70 ms (~ 14 fps) capturar una pantalla de 1680×1050 píxeles y tener los valores accesibles en la memoria
Algunas notas al azar:
- Importando el
Quartz.CoreGraphics
El módulo es la parte más lenta, aproximadamente 1 segundo. Lo mismo es true para importar la mayoría de los módulos PyObjC. Es poco probable que importe en este caso, pero para procesos de corta duración, es mejor escribir la herramienta en ObjC. - Especificar una región más pequeña es un poco más rápido, pero no enormemente (~ 40ms para un bloque de 100x100px, ~ 70ms para 1680×1050). La mayor parte del tiempo parece dedicarse solo al
CGDataProviderCopyData
llamar – Me pregunto si hay una manera de acceder a los datos directamente, ya que no necesitamos modificarlos. - los
ScreenPixel.pixel
La función es bastante rápida, pero el acceso a una gran cantidad de píxeles sigue siendo lento (ya que0.01ms * 1650*1050
es de unos 17 segundos): si necesita acceder a muchos píxeles, probablemente sea más rápidostruct.unpack_from
todos a la vez.
Aquí está el código:
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())
Encontré esta publicación mientras buscaba una solución para obtener una captura de pantalla en Mac OS X utilizada para el procesamiento en tiempo real. Intenté usar ImageGrab de PIL como se sugiere en algunas otras publicaciones, pero no pude obtener los datos lo suficientemente rápido (con solo alrededor de 0.5 fps).
¡La respuesta https://stackoverflow.com/a/13024603/3322123 en esta publicación para usar PyObjC me salvó el día! ¡Gracias @dbr!
Sin embargo, mi tarea requiere obtener todos los valores de píxeles en lugar de un solo píxel, y también para comentar la tercera nota de @dbr, agregué un nuevo método en esta clase para obtener una imagen completa, en caso de que alguien más pudiera necesitarlo. .
Los datos de la imagen se devuelven como un número array con una dimensión de (altura, ancho, 3), que se puede usar directamente para el posprocesamiento en numpy o opencv, etc. Obtener valores de píxeles individuales también se vuelve bastante trivial usando la indexación de numpy.
Probé el código con una captura de pantalla de 1600 x 1000: obtener los datos usando capture () tomó ~ 30 ms y convertirlos a un np array getimage () toma solo ~ 50 ms en mi Macbook. Así que ahora tengo> 10 fps e incluso más rápido para regiones más pequeñas.
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)
tenga en cuenta que desecho el canal “alfa” del canal BGRA 4.
Todo esto fue tan útil que tuve que volver para comentar / sin embargo, no tengo la reputación … Sin embargo, tengo un código de muestra de una combinación de las respuestas anteriores para una captura de pantalla / guardado rápido como un rayo gracias a @dbr y @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)
Aquí puedes ver las reseñas y valoraciones de los usuarios
Si tienes algún recelo y disposición de modernizar nuestro tutorial eres capaz de dejar una anotación y con gusto lo interpretaremos.