Преобразование изображения PIL в Cairo ImageSurface - PullRequest
5 голосов
/ 30 сентября 2011

Я пытаюсь создать Каир ImageSurface из изображения PIL, код, который у меня пока есть:

im = Image.open(filename)
imstr = im.tostring()
a = array.array('B', imstr)
height, width = im.size
stride = cairo.ImageSurface.format_stride_for_width(cairo.FORMAT_RGB24, width)
return cairo.ImageSurface.create_for_data(a, cairo.FORMAT_ARGB24, width, height, stride)

Но это дает мне

TypeError: buffer is not long enough.

Я не очень понимаю, почему это так, возможно, я недостаточно хорошо понимаю форматы изображений.

Я использую Каир 1.10.

Ответы [ 2 ]

5 голосов
/ 12 сентября 2012

Принятая версия не работает правильно, если:

  • Ваше изображение имеет цвета
  • Ваше изображение не непрозрачно
  • Ваше изображение в режиме, отличном от RGB (A)

В каирских цветах изображения их значение предварительно умножается на значение альфа, и они хранятся в виде 32-битного слова с использованием собственного порядкового номера ЦП. Это означает, что изображение PIL:

r1 g1 b1 a1 r2 g2 b2 a2 ...

хранится в Каире в процессоре с прямым порядком байтов как:

b1*a1 g1*a1 r1*a1 a1 b2*a2 g2*a2 r2*a2 a2 ...

и в процессоре с прямым порядком байтов как:

a1 r1*a1 b1*a1 g1*a1 a2 r2*a2 g2*a2 b2*a2 ...

Вот версия, которая корректно работает на машине с прямым порядком байтов без зависимости NumPy:

def pil2cairo(im):
    """Transform a PIL Image into a Cairo ImageSurface."""

    assert sys.byteorder == 'little', 'We don\'t support big endian'
    if im.mode != 'RGBA':
        im = im.convert('RGBA')

    s = im.tostring('raw', 'BGRA')
    a = array.array('B', s)
    dest = cairo.ImageSurface(cairo.FORMAT_ARGB32, im.size[0], im.size[1])
    ctx = cairo.Context(dest)
    non_premult_src_wo_alpha = cairo.ImageSurface.create_for_data(
        a, cairo.FORMAT_RGB24, im.size[0], im.size[1])
    non_premult_src_alpha = cairo.ImageSurface.create_for_data(
        a, cairo.FORMAT_ARGB32, im.size[0], im.size[1])
    ctx.set_source_surface(non_premult_src_wo_alpha)
    ctx.mask_surface(non_premult_src_alpha)
    return dest

Здесь я делаю предварительное умножение с помощью Каира. Я также попытался выполнить предварительное умножение с помощью NumPy, но результат оказался медленнее. Эта функция требует от моего компьютера (Mac OS X, Intel Core 2 Duo 2,13 ГГц) ~ 1 с для преобразования изображения 6000x6000 пикселей и 5 мс для преобразования изображения 500x500 пикселей.

5 голосов
/ 30 сентября 2011

Cairo create_for_data () хочет записываемый буферный объект (строка может использоваться как буферный объект, но он не доступен для записи), и он поддерживает только 32-битные данные на пиксель (RGBA или RGB, за которыми следует один неиспользуемый байт),PIL, с другой стороны, предоставляет 24-битный буферный объект RGB только для чтения.

Я предлагаю вам указать PIL добавить альфа-канал, а затем преобразовать буфер PIL в массив numpy, чтобы получить буфер записи для Cairo..

im = Image.open(filename)
im.putalpha(256) # create alpha channel
arr = numpy.array(im)
height, width, channels = arr.shape
surface = cairo.ImageSurface.create_for_data(arr, cairo.FORMAT_RGB24, width, height)
...