Python Получить значение пикселя экрана в OS X

Я нахожусь в процессе создания автоматизированного игрового бота в Python на OS X 10.8.2 и в процессе исследования автоматизации Python GUI я обнаружил autopy. API манипуляции с мышью великолепен, но кажется, что методы захвата экрана полагаются на устаревшие методы OpenGL ...

Есть ли эффективные способы получения значения цвета пикселя в OS X? Единственный способ, которым я могу думать сейчас, это использоватьos.system("screencapture foo.png") но у процесса, кажется, нет ненужных накладных расходов, так как я буду опрашивать очень быстро.

 tMC19 окт. 2012 г., 19:09
autopy.color.hex_to_rgb(autopy.screen.get_color(1, 1)) ?
 itsachen19 окт. 2012 г., 21:03
Bejeweled Blitz, это для класса проекта AI. Все функции screengrab в autopy возвращают черный цвет, если вы посмотрите на исходные коды устаревших функций.
 tMC19 окт. 2012 г., 19:04
скорее не по теме; для какой игры вы строите бота?

Ответы на вопрос(2)

Решение Вопроса

но с использованием опции сжатия TIFF дляscreencapture немного быстрее:

$ 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

Это, как вы говорите, требует много времени (создание подпроцесса, запись / чтение с диска, сжатие / распаковка).

Вместо этого вы можете использовать PyObjC для захвата экрана, используяCGWindowListCreateImage, Я обнаружил, что для захвата экрана с разрешением 1680x1050 пикселей требуется около 70 мс (~ 14 кадров в секунду), и значения доступны в памяти

Несколько случайных заметок:

ИмпортQuartz.CoreGraphics Модуль самая медленная часть, около 1 секунды. То же самое верно для импорта большинства модулей PyObjC. Вряд ли это имеет значение в этом случае, но для недолговечных процессов лучше написать инструмент в ObjCЗадание области меньшего размера выполняется немного быстрее, но не так сильно (~ 40 мс для блока размером 100x100 пикселей, ~ 70 мс для 1680x1050). Кажется, что большую часть времени проводят только вCGDataProviderCopyData вызов - интересно, есть ли способ получить доступ к данным напрямую, так как нам не нужно их изменять?ScreenPixel.pixel функция довольно быстрая, но доступ к большому количеству пикселей все еще медленный (так как0.01ms * 1650*1050 около 17 секунд) - если вам нужно получить доступ к большому количеству пикселей, возможно, быстрееstruct.unpack_from их все за один раз.

Вот код:

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())
 Jesse Aldridge31 июл. 2013 г., 20:25
Хорошо, спасибо. -
 dbr31 июл. 2013 г., 11:17
@JesseAldridgeИзменение размера полученного CGimage, как это должно быть довольно быстро
 Jesse Aldridge30 июл. 2013 г., 23:51
Знаете ли вы, есть ли простой способ получить уменьшенный скриншот? Как флаг CoreGraphics или что-то? Это было бы полезно для таких вещей, как поиск местоположения спрайта на экране.
 dbr23 окт. 2012 г., 10:27
Написал блог-пост с немного более сложным описанием кода
 itsachen24 окт. 2012 г., 00:24
Отличный обходной путь! Отличная запись в блоге тоже.

когда искал решение для получения скриншота в Mac OS X, используемого для обработки в реальном времени. Я пытался использовать ImageGrab из PIL, как это предлагалось в некоторых других публикациях, но не смог получить данные достаточно быстро (всего с 0,5 кадра в секунду).

Ответhttps://stackoverflow.com/a/13024603/3322123 в этом посте использовать PyObjC спас мой день! Спасибо @dbr!

Однако моя задача состоит в том, чтобы получить все значения пикселей, а не только один пиксель, а также прокомментировать третью заметку с помощью @dbr. Я добавил новый метод в этот класс, чтобы получить полное изображение на случай, если кому-то еще это понадобится ,

Данные изображения возвращаются в виде массива NumPy с размером (высота, ширина, 3), который может быть непосредственно использован для последующей обработки в NUMPY или OpenCV и т. Д. Получение отдельных значений пикселей также становится довольно тривиальным с использованием индексирования NUMPY.

Я протестировал код со снимком экрана 1600 x 1000 - получение данных с помощью capture () заняло ~ 30 мс, а преобразование его в массив np getimage () занимает всего ~ 50 мс на моем Macbook. Так что теперь у меня> 10 кадров в секунду и даже быстрее для небольших регионов.

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)

Обратите внимание, что я выбрасываю «альфа» канал из канала BGRA 4.

Ваш ответ на вопрос