Скриншот Усреднение цвета прямоугольников

Я написал быстрый скрипт на Python, чтобы вернуть средний цвет прямоугольников, окружающих периметр моего экрана. (Конечная цель здесь иметь RGB-светодиодывокруг моего монитора для эффекта свечения во время просмотра фильмов — как это (youtube), но веселее, потому что я делаю это сам).

Я В настоящее время я использую autopyдля получения экрана в виде растрового изображения («скриншот»), получения значения каждого пикселя и преобразования RGB HEX

Упрощенная версия:

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)

5x5 Screenshot

Проблема заключается в скорости реализации, потому что это цикл для каждого пикселя в блоке (разрешение 2560 * 1600/ 5 = 320 * 512 блоков = 163 840 пикселей на блок), и каждый блок по периметру (16 * 163 840 = 2 621 440 циклов). В целом на это ушло 2,814 с.

Если я увеличу step, это ускоряется, но недостаточно: (здесь используются более реалистичные блоки 15x10, окружающие границу)

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

Это потому, что сам скриншот занимает примерно 0,070 с - это означает, что я ограничен 12,8 FPS.

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

Вопросы:

  • Есть ли более быстрый способ сделать снимок экрана и усреднить области экрана?

    Я не слишком беспокоюсь о точности, но хотел бы иметь возможность возвращать эти значения примерно со скоростью 30 кадров в секунду, в идеале быстрее (20–30 мс), чтобы обеспечить накладные расходы на последовательную передачу. Имейте в виду, что у меня разрешение экрана 2560*1600!

    Я слышал о Python Imaging Library (PIL), но еще не успел изучить скорость работы функции ImageGrab, но она выглядит многообещающе.

  • Можно ли считывать значения пикселей непосредственно с графического процессора?

  • Еще одна мысль: как лучше всего определить верхний/нижний край фильма? (Если соотношение сторон широкоэкранное, вверху/внизу скриншота есть черные полосы, а некоторые прямоугольники черные).


Использование PIL's grab():

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

PIL - resize:(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

Примечание. Это улучшение по сравнению с результатами, полученными выше, но мы по-прежнему ограничены 9 кадрами в секунду, или 3 FPS с полным сглаживанием.


PIL - ближайший затем изменить размер:(Марк Рэнсом)

>>> 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

Результаты:

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 в HEX.


Может ли кто-нибудь предложить более быстрый способ — например, сделать частичный снимок экрана? Должен ли я написать что-нибудь на C?

9
задан Alex L 19 May 2012 в 02:29
поделиться