Python 矩形的屏幕截图颜色平均

Python 矩形的屏幕截图颜色平均,python,c,optimization,screenshot,python-imaging-library,Python,C,Optimization,Screenshot,Python Imaging Library,我编写了一个快速python脚本来返回屏幕周边矩形的平均颜色。(这里的最终目标是让我的显示器周围有一个发光的效果,就像电影一样,但更有趣,因为我自己做的) 我目前使用的是获取屏幕位图(“屏幕截图”),获取每个像素值,以及RGB十六进制转换 简化版: step = 1 width = 5 height = 5 b = autopy.bitmap.capture_screen() for block in border_block(width, height): # for each recta

我编写了一个快速python脚本来返回屏幕周边矩形的平均颜色。(这里的最终目标是让我的显示器周围有一个发光的效果,就像电影一样,但更有趣,因为我自己做的)

我目前使用的是获取屏幕位图(“屏幕截图”),获取每个像素值,以及RGB十六进制转换

简化版:

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))
然后,我使用
matplotlib
:显示块(配置为5x5块,步骤=1)

问题在于实现速度——因为这是一个块中每个像素的循环(2560*1600分辨率/5=320*512块=163840像素/块),以及周长周围的每个块(16*163840=2621440循环)。总的来说,这需要2.814秒才能完成

如果我增加步长值,它会加快速度,但这还不够:(这是在边界周围使用更真实的15x10块)

这是因为屏幕截图本身大约需要0.070秒——这意味着我被限制在每秒12.8帧

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

  • 有没有更快的截图和平均屏幕区域的方法

    我不太担心准确性,但希望能够以大约30 FPS的速度返回这些值,最好更快(20-30 ms),以考虑串行传输开销。请记住,我的屏幕分辨率是2560*1600

    我听说过,但还没有时间研究
    ImageGrab
    函数的速度,但它看起来很有前途

  • 我可以直接从GPU读取像素值吗

  • 另一个想法-检测电影上/下边缘的最佳方法是什么?(如果纵横比是宽屏的,屏幕截图的顶部/底部有黑色条,一些矩形是黑色的)


使用PIL的抓斗()

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

PIL-调整大小:(克里斯托弗)

注意:这是对上述结果的改进,但我们仍然限制为9 FPS,或3 FPS,具有完全抗锯齿


PIL-最近然后调整大小:(标记赎金)

结果:

Step  Time(s)
1     0.333048412226
2     0.16206895716
5     0.117172371393
10    0.102383282629
15    0.101844097599
20    0.101229094581
50    0.100824552193
比使用顶部的
autopy
手动循环快得多,但我们仍然限制在~9 FPS(以10为“步长”)

注意:这不包括所需的RGB到十六进制的转换



有人能想出一个更快的方法吗?例如,拍摄部分屏幕截图?我应该用C写点什么吗?

快速的成功方法是对5x5图像使用
调整大小
操作(在PIL中)(可以使用简单的插值来提高速度),而不是平均区域,例如:

myimg = ImageGrab.grab()
resized = myimg.resize((5, 5), Image.NEAREST) 
这将产生与自己进行平均工作大致相同的效果


虽然不能确定PIL的ImageGrab的速度(以及它与autopy相比的速度),但很容易尝试找到它。

使用Python图像库。从(图像模块中的):

getcolors

im.getcolors()=>一个(计数、颜色)元组列表或无元组列表

im.getcolors(maxcolors)=>一个(count,color)元组列表或无元组列表

(1.1.5中新增)返回(计数,颜色)元组的未排序列表,其中计数是相应颜色在图像中出现的次数

图像模块还包含一个crop()方法,可用于将每个矩形插入getcolors()。你可以很容易地从中得出加权平均值

它应该比在python中手动运行循环快得多。我不确定它是否足够快,可以实时使用,但你会得到惊人的速度提升。您还可以每秒拍摄几次屏幕截图,因为可能以60 fps和10 fps的速度向LED发送信号不会特别明显。不要将其视为“限制为12.8 FPS”,而应将其视为“每5帧只能更新一次LED”,这应该不是一个明显的区别


编辑:如果你真的对这里的进一步优化感兴趣,我想你会发现这很有帮助。

要加快调整大小的操作,你可以分两步来完成。对第一个使用“最近”以尽可能快的方式减少像素数,然后使用“反别名”将这些像素合并到具有代表性的样本中。这相当于您之前使用PIL函数试验的步长

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

我更新了我的问题-看起来像
ImageGrab.grab()
平均需要0.116秒,大约是
autopy.bitmap.capture\u screen()的1.6倍。调整大小稍微快一点,不知道为什么。使用
Image.NEAREST
根本不进行任何平均-它只是从多个像素中选择一个像素。无法保证它所选择的像素将代表该区域。@Markransem很好-我已经用另一种调整大小的方法更新了我的问题
timit
sI已经更新了我的问题-看起来像
ImageGrab.grab()
平均需要0.109s-仅9 FPS!我意识到9fps和30fps之间的视觉差异将是最小的,但是优化代码是一个有趣的挑战,对吗?有趣,根本不是我所期望的。我已经链接到一些似乎表现更好的东西,尽管我自己还没有测试过。感谢链接Robert,我将尝试一下gtk!我将在这里发回我的结果。它看起来是关闭的,我将稍后再试一次。
调整大小
中使用的大多数模式都会产生很差的结果,因为它们没有采样足够的像素,这就是为什么它们的速度如此接近。最近值取1,双线性取2x2,双三次取3x3。这比ANTIALIAS将使用的170x160远得多。@MarkRansom啊,谢谢你的解释。有趣的是,可以看到使用autopy(1.35s)手动循环与PIL的ANTIALIAS(0.326s)之间的区别——改进了很多!就个人而言,如果PIL还没有这样做的话,这似乎是一个明显的并行化候选。如果
Step  Time(s)
1     0.333048412226
2     0.16206895716
5     0.117172371393
10    0.102383282629
15    0.101844097599
20    0.101229094581
50    0.100824552193
myimg = ImageGrab.grab()
resized = myimg.resize((5, 5), Image.NEAREST) 
PIL.ImageGrab.grab().resize((150, 100), PIL.Image.NEAREST).resize((15, 10), PIL.Image.ANTIALIAS)