Note that there are some explanatory texts on larger screens.

plurals
  1. POScreenshot colour averaging of rectangles
    primarykey
    data
    text
    <p>I wrote a quick python script to return the average colour of rectangles surrounding the perimeter of my screen. (The end goal here is to have <a href="http://www.sparkfun.com/products/10312" rel="nofollow noreferrer">RGB LED strips</a> surrounding my monitor, for a glowing effect during movies - like <a href="http://www.youtube.com/watch?v=mXBqg3Zeg0k" rel="nofollow noreferrer">this (youtube)</a>, but more fun because I'm making it myself).</p> <p>I'm currently using <a href="http://pypi.python.org/pypi/autopy/0.51" rel="nofollow noreferrer">autopy</a> for getting the screen as a bitmap ("screenshot"), getting each pixel value, and the RGB &lt;-> HEX conversions. </p> <p>Simplified version:</p> <pre><code>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)) </code></pre> <p>I then display the blocks using <code>matplotlib</code>: (this is configured as 5x5 blocks, step = 1)</p> <p><img src="https://i.stack.imgur.com/xVvkj.jpg" alt="5x5 Screenshot"></p> <p>The problem is the speed of implementation - because this is looping for each pixel in a block (2560*1600 resolution/5 = 320*512 block = 163,840 pixels per block), and each block around the perimeter (16*163,840 = 2,621,440 loops). Overall, this took took 2.814s to complete. </p> <p>If I increase the step value, it speeds up, but not enough: (this is using a more realistic 15x10 blocks surrounding the border)</p> <pre><code>Step Time (s) 1 1.35099983215 2 0.431000232697 5 0.137000083923 10 0.0980000495911 15 0.095999956131 20 0.0839998722076 50 0.0759999752045 </code></pre> <p>That's because the screenshot itself takes approx 0.070s - this means that I'm limited to 12.8 FPS. </p> <pre><code>&gt;&gt;&gt; timeit.Timer("autopy.bitmap.capture_screen()", "import autopy").timeit(100)/100 0.06874468830306966 </code></pre> <p><strong>Questions:</strong></p> <ul> <li><p>Is there a faster method of taking a screenshot and averaging regions of the screen? </p> <p>I'm not too worried about accuracy, but would like to be able to return these values at approx 30 FPS, ideally faster (20-30 ms) to allow for serial transmission overhead. Bear in mind my screen resolution is 2560*1600! </p> <p>I've heard about <a href="http://www.pythonware.com/products/pil/" rel="nofollow noreferrer">Python Imaging Library (PIL)</a>, but haven't had time to look into the speed of the <code>ImageGrab</code> function yet, but it looks promising. </p></li> <li><p>Can I read pixel values directly from the GPU? </p></li> <li><p>Another thought - what's the best way to detect the top/bottom edge of a movie? (If aspect ratio is widescreen, there are black bars at top/bottom of screenshot, and some rectangles are black).</p></li> </ul> <hr> <p><strong>Using PIL's grab()</strong>:</p> <pre><code>&gt;&gt;&gt; timeit.Timer("ImageGrab.grab()", "from PIL import ImageGrab").timeit(100)/100 0.1099840205312789 </code></pre> <hr> <p><strong>PIL - resize:</strong> (ChristopheD)</p> <pre><code>&gt;&gt;&gt; timeit.Timer("PIL.ImageGrab.grab().resize((15, 10), PIL.Image.NEAREST)", "import PIL").timeit(100)/100 0.1028043677442085 &gt;&gt;&gt; timeit.Timer("PIL.ImageGrab.grab().resize((15, 10), PIL.Image.ANTIALIAS)", "import PIL").timeit(100)/100 0.3267692217886088 </code></pre> <p>Note: This is an improvement over the results obtained above, but we're still limited to 9 FPS, or 3 FPS with full anti-aliasing. </p> <hr> <p><strong>PIL - nearest then resize:</strong> (Mark Ransom) </p> <pre><code>&gt;&gt;&gt; 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 </code></pre> <p>Results:</p> <pre><code>Step Time(s) 1 0.333048412226 2 0.16206895716 5 0.117172371393 10 0.102383282629 15 0.101844097599 20 0.101229094581 50 0.100824552193 </code></pre> <p>Much faster than manually looping with <code>autopy</code> at top, but we're still limited to ~9 FPS (at a 'step' of 10). </p> <p>Note: This doesn't include the RGB to HEX conversion required</p> <hr> <p>Can anyone come up with a faster method - i.e. taking a partial screenshot? Should I write something in C?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload