【问题标题】:Convert PIL Image to Cairo ImageSurface将 PIL Image 转换为 Cairo ImageSurface
【发布时间】:2011-09-30 12:11:45
【问题描述】:

我正在尝试从 PIL 图像创建 cairo ImageSurface,我目前的代码是:

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.

我真的不明白为什么会这样,也许我对图像格式了解不够。

我使用的是 cairo 1.10。

【问题讨论】:

  • 问题是我拥有的图像是 jpg 而 cairo 只能从 png 文件加载。
  • 如果只是这样!使用 gimp 转换图像?
  • 我的意思是该程序的目的是处理单个图像但图像未知,我所知道的只是它将是一个 jpg。

标签: python python-imaging-library cairo


【解决方案1】:

Cairo 的 create_for_data() 想要一个可写的缓冲区对象(字符串可以用作缓冲区对象,但它不可写),并且它只支持每像素数据 32 位(RGBA,或 RGB 后跟一个未使用的字节) .另一方面,PIL 提供 24bpp RGB 只读缓冲区对象。

我建议你告诉 PIL 添加一个 alpha 通道,然后将 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)

【讨论】:

  • 这几乎可以工作,只是颜色被弄乱了,我怀疑这与 cairo 中 RGBA 像素值的排序有关,这取决于我所在机器的字节序。我试过只是颠倒numpy数组中像素值的顺序,但这似乎不起作用。有什么建议吗?
  • 设法通过将 numpy 数组中的像素从 RGBA 重新排序为 BGRA 来使其工作。
  • 开罗网站上也有一个食谱解释了如何做到这一点:cairographics.org/pythoncairopil
  • create_for_data 已在最新版本的 pycairo 中被删除,因此上面的代码将失败并出现“未实现”错误。
  • @totaam 它出现在我的 Debian Bullseye 系统上,pycairo 版本 1.16.2。
【解决方案2】:

如果出现以下情况,接受的版本将无法正常工作:

  • 您的图片有颜色
  • 您的图片不透明
  • 您的图像处于不同于 RGB(A) 的模式

在 cairo 中,图像颜色的值预先乘以 alpha 的值,并使用本机 CPU 字节序存储为 32 位字。这意味着 PIL 图像:

r1 g1 b1 a1 r2 g2 b2 a2 ...

在小端CPU中存储在cairo中:

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

在大端 CPU 中为:

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

在这里,我用 cairo 进行预乘。我也尝试使用 NumPy 进行预乘,但结果较慢。这个函数在我的电脑(Mac OS X,2.13GHz Intel Core 2 Duo)中需要大约1s来转换6000x6000像素的图像,5ms来转换500x500像素的图像。

【讨论】:

    猜你喜欢
    • 2011-04-13
    • 2020-12-08
    • 1970-01-01
    • 2017-12-20
    • 1970-01-01
    • 1970-01-01
    • 2016-04-14
    • 2015-04-30
    相关资源
    最近更新 更多