Captura de pantalla en color de los rectangularjs.

Escribí una secuencia de comandos de python para devolver el color promedio de los rectangularjs que rodean el perímetro de mi pantalla. (El objective final aquí es tener tiras de LED RGB alrededor de mi monitor, para un efecto shiny durante las películas, como este (youtube) , pero más divertido porque lo estoy haciendo yo mismo).

Actualmente estoy usando Autopy para obtener la pantalla como bitmap (“captura de pantalla“), obtener cada valor de píxel y las conversiones RGB HEX.

Versión simplificada:

step = 1 width = 5 height = 5 b = autopy.bitmap.capture_screen() for block in border_block(width, height): # for each rectangle around the perimeter of my screen R,G,B = 0,0,0 count = 0 for x in xrange(block.x_min, block.x_max, step): for y in xrange(block.y_min, block.y_max, step): r,g,b = autopy.color.hex_to_rgb(image.get_color(x, y)) R += r; G += g; B += b count += 1 block.colour = "#{:06x}".format(autopy.color.rgb_to_hex(R/count,G/count,B/count)) 

Luego matplotlib los bloques utilizando matplotlib : (esto está configurado como bloques 5×5, paso = 1)

Captura de pantalla de 5x5

El problema es la velocidad de implementación, ya que se trata de un bucle para cada píxel en un bloque (resolución 2560 * 1600/5 = 320 * 512 = 163,840 píxeles por bloque) y cada bloque alrededor del perímetro (16 * 163,840 = 2,621,440 bucles ). En general, esto tomó 2.814s para completar.

Si incremento el valor del paso, se acelera, pero no lo suficiente: (esto está usando bloques 15×10 más realistas que rodean el borde)

 Step Time (s) 1 1.35099983215 2 0.431000232697 5 0.137000083923 10 0.0980000495911 15 0.095999956131 20 0.0839998722076 50 0.0759999752045 

Esto se debe a que la captura de pantalla en sí toma aproximadamente 0.070 s, lo que significa que estoy limitado a 12.8 FPS.

 >>> timeit.Timer("autopy.bitmap.capture_screen()", "import autopy").timeit(100)/100 0.06874468830306966 

Preguntas:

  • ¿Existe un método más rápido para tomar una captura de pantalla y promediar regiones de la pantalla?

    No estoy demasiado preocupado por la precisión, pero me gustaría poder devolver estos valores a aproximadamente 30 FPS, idealmente más rápido (20-30 ms) para permitir la sobrecarga de transmisión en serie. ¡Tenga en cuenta que mi resolución de pantalla es 2560 * 1600!

    He oído hablar de Python Imaging Library (PIL) , pero aún no he tenido tiempo de analizar la velocidad de la función ImageGrab , pero parece prometedor.

  • ¿Puedo leer los valores de píxeles directamente desde la GPU?

  • Otro pensamiento: ¿cuál es la mejor manera de detectar el borde superior / inferior de una película? (Si la relación de aspecto es de pantalla ancha, hay barras negras en la parte superior / inferior de la captura de pantalla, y algunos rectangularjs son negros).


Usando la pinza de PIL () :

 >>> timeit.Timer("ImageGrab.grab()", "from PIL import ImageGrab").timeit(100)/100 0.1099840205312789 

PIL – redimensionar: (ChristopheD)

 >>> timeit.Timer("PIL.ImageGrab.grab().resize((15, 10), PIL.Image.NEAREST)", "import PIL").timeit(100)/100 0.1028043677442085 >>> timeit.Timer("PIL.ImageGrab.grab().resize((15, 10), PIL.Image.ANTIALIAS)", "import PIL").timeit(100)/100 0.3267692217886088 

Nota: esta es una mejora con respecto a los resultados obtenidos anteriormente, pero aún estamos limitados a 9 FPS o 3 FPS con anti-aliasing completo.


PIL: el tamaño más cercano y luego el tamaño: (Mark Ransom)

 >>> for step in [1,2,5,10,15,20,50]: print step, timeit.Timer("PIL.ImageGrab.grab().resize(("+str(2560/step)+", "+str(1600/step)+"), PIL.Image.NEAREST).resize((15, 10), PIL.Image.ANTIALIAS)", "import PIL.ImageGrab").timeit(100)/100 

Resultados:

 Step Time(s) 1 0.333048412226 2 0.16206895716 5 0.117172371393 10 0.102383282629 15 0.101844097599 20 0.101229094581 50 0.100824552193 

Mucho más rápido que el bucle manual con autopy en la parte superior, pero aún estamos limitados a ~ 9 FPS (en un “paso” de 10).

Nota: Esto no incluye la conversión RGB a HEX requerida


¿Puede alguien encontrar un método más rápido, es decir, tomar una captura de pantalla parcial? ¿Debo escribir algo en C?

Utilice la biblioteca de imágenes de Python. Desde los documentos (en el módulo Imagen):

obtener colores

im.getcolors () => una lista de tuplas (contar, color) o Ninguna

im.getcolors (maxcolors) => una lista de tuplas (recuento, color) o Ninguna

(Nuevo en 1.1.5) Devuelve una lista sin clasificar de tuplas (recuento, color), donde el recuento es el número de veces que aparece el color correspondiente en la imagen.

El módulo de imagen también contiene un método de recorte () que puede usar para hacer que cada rectángulo se conecte en getcolors (). Puede tomar un promedio ponderado de eso fácilmente.

Debería ser mucho más rápido que ejecutar el bucle manualmente en Python. No estoy seguro de si es lo suficientemente rápido para usar en tiempo real, pero obtendrás un aumento de velocidad dramático. También puede tomar la captura de pantalla varias veces por segundo, ya que lo más probable es que el envío de señales a los LED a 60 fps frente a 10 fps no sea particularmente notable. No lo vea como “limitado a 12.8 FPS”, mire como “solo se pueden actualizar los LED una vez cada 5 cuadros”, lo que no debería ser una diferencia notable.

EDITAR: Si está realmente interesado en una mayor optimización aquí, creo que encontrará la forma más rápida de hacer una captura de pantalla con python en Windows bastante útil.

Una ganancia rápida podría ser usar una operación de cambio de resize (en PIL) (puede usar una interpolación simple para la velocidad) a una imagen de 5×5 en lugar de promediar las regiones, por ejemplo:

 myimg = ImageGrab.grab() resized = myimg.resize((5, 5), Image.NEAREST) 

Esto debería producir aproximadamente el mismo efecto que el trabajo de promediado.

Sin embargo, no estoy realmente seguro de la velocidad de ImageGrab de PIL (y de cómo se compara con la autopy ), pero es bastante fácil intentarlo y descubrirlo.

Para acelerar una operación de cambio de tamaño, puede hacerlo en dos pasos. Use NEAREST para el primero para reducir el número de píxeles de la manera más rápida posible, luego ANTIALIAS para fusionarlos en una muestra representativa. Es equivalente al tamaño de paso con el que experimentaste anteriormente, hecho con las funciones PIL.

 PIL.ImageGrab.grab().resize((150, 100), PIL.Image.NEAREST).resize((15, 10), PIL.Image.ANTIALIAS)