将 ARGB32 (py)cairo 表面转换为 PIL(low) 图像 - 反色?
Converting an ARGB32 (py)cairo surface to a PIL(low) image - inverse colors?
我 认为 正在做一些琐碎的标准任务:我正在将 (py)cairo
表面转换为 PIL
(low) 图像。原来的cairo
面使用ARGB
模式。目标 PIL
图像使用 RGBA
,即我想保留所有颜色和 alpha 通道。然而,在转换过程中事情变得非常奇怪:似乎 cairo
在内部将其数据存储为 BGRA
,因此我实际上需要在转换过程中交换颜色通道,请参见此处:
import cairo
import gi
gi.require_version('Rsvg', '2.0')
from gi.repository import Rsvg
from PIL import Image
w, h = 600, 600
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h,)
ctx = cairo.Context(surface)
ctx.set_source_rgba(1.0, 1.0, 1.0, 1.0) # explicitly draw white background
ctx.rectangle(0, 0, w, h)
ctx.fill()
# tested with https://raw.githubusercontent.com/pleiszenburg/abgleich/v0.0.7/src/abgleich/share/icon.svg
layout = Rsvg.Handle.new_from_file('icon.svg')
layout.render_cairo(ctx)
# EXPORT TEST #1: cairo
surface.write_to_png('export_cairo.png') # ok, looks as expected
pil = Image.frombuffer(mode = 'RGBA', size = (w, h), data = surface.get_data(),)
b, g, r, a = pil.split() # Color swap, part 1: Splitting the channels
pil = Image.merge('RGBA', (r, g, b, a)) # Color swap, part 2: Rearranging the channels
# EXPORT TEST #2: PIL
pil.save('export_pil.png') # ok, looks as expected IF COLORS ARE REARRANGED AS ABOVE
上面的测试用的是rsvg
,但是用cairo
画几条彩线也可以重现。
我是不是误会了什么,或者这实际上是正确的方法吗?
来自开罗文档 (https://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t):
CAIRO_FORMAT_ARGB32
each pixel is a 32-bit quantity, with alpha in the upper 8 bits, then red, then green, then blue. The 32-bit quantities are stored native-endian. Pre-multiplied alpha is used. (That is, 50% transparent red is 0x80800000, not 0x80ff0000.) (Since 1.0)
所以,在小字节序上,我认为这实际上就是 PIL 所说的 BGRA。
与您的问题没有直接关系,但这是 Pre-multiplied alpha。
根据 https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-modes,唯一具有预乘 alpha 的模式是 'RGBa'
。
or is this actually the right way to do it?
不知道“正确”是什么意思。但是,我的评论是:必须有一些方法可以在不通过中间图像的情况下做到这一点。
由于 Pillow 不支持 cairo 的图像模式,也许您可以改用 numpy-y 来进行转换。例如,pycairo 的测试套件包含以下内容:https://github.com/dalembertian/pycairo/blob/22d29e0820d0dcbe070a6eb6f8f302e8c41b71a7/test/isurface_get_data.py#L37-L42
buf = surface.get_data()
a = numpy.ndarray (shape=(w,h,4), dtype=numpy.uint8, buffer=buf)
# draw a vertical line
a[:,40,0] = 255 # byte 0 is blue on little-endian systems
a[:,40,1] = 0
a[:,40,2] = 0
因此,要从(在 Pillow-speak 中)BGRa 转换为 RGBa,您可以这样做来交换红色和蓝色通道(其中 a
是一个类似于上面的缓冲区) :
(a[:,:,0], a[:,:,2]) = (a[:,:,2], a[:,:,0])
如果这真的比您通过中间过程的方法更好Image
...好吧,我不知道。您必须判断执行此操作的最佳方法是什么。至少你现在应该能够解释为什么它是必要的(cairo 和 PIL 都没有支持通用的图像格式)
我 认为 正在做一些琐碎的标准任务:我正在将 (py)cairo
表面转换为 PIL
(low) 图像。原来的cairo
面使用ARGB
模式。目标 PIL
图像使用 RGBA
,即我想保留所有颜色和 alpha 通道。然而,在转换过程中事情变得非常奇怪:似乎 cairo
在内部将其数据存储为 BGRA
,因此我实际上需要在转换过程中交换颜色通道,请参见此处:
import cairo
import gi
gi.require_version('Rsvg', '2.0')
from gi.repository import Rsvg
from PIL import Image
w, h = 600, 600
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h,)
ctx = cairo.Context(surface)
ctx.set_source_rgba(1.0, 1.0, 1.0, 1.0) # explicitly draw white background
ctx.rectangle(0, 0, w, h)
ctx.fill()
# tested with https://raw.githubusercontent.com/pleiszenburg/abgleich/v0.0.7/src/abgleich/share/icon.svg
layout = Rsvg.Handle.new_from_file('icon.svg')
layout.render_cairo(ctx)
# EXPORT TEST #1: cairo
surface.write_to_png('export_cairo.png') # ok, looks as expected
pil = Image.frombuffer(mode = 'RGBA', size = (w, h), data = surface.get_data(),)
b, g, r, a = pil.split() # Color swap, part 1: Splitting the channels
pil = Image.merge('RGBA', (r, g, b, a)) # Color swap, part 2: Rearranging the channels
# EXPORT TEST #2: PIL
pil.save('export_pil.png') # ok, looks as expected IF COLORS ARE REARRANGED AS ABOVE
上面的测试用的是rsvg
,但是用cairo
画几条彩线也可以重现。
我是不是误会了什么,或者这实际上是正确的方法吗?
来自开罗文档 (https://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t):
CAIRO_FORMAT_ARGB32
each pixel is a 32-bit quantity, with alpha in the upper 8 bits, then red, then green, then blue. The 32-bit quantities are stored native-endian. Pre-multiplied alpha is used. (That is, 50% transparent red is 0x80800000, not 0x80ff0000.) (Since 1.0)
所以,在小字节序上,我认为这实际上就是 PIL 所说的 BGRA。
与您的问题没有直接关系,但这是 Pre-multiplied alpha。
根据 https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-modes,唯一具有预乘 alpha 的模式是 'RGBa'
。
or is this actually the right way to do it?
不知道“正确”是什么意思。但是,我的评论是:必须有一些方法可以在不通过中间图像的情况下做到这一点。
由于 Pillow 不支持 cairo 的图像模式,也许您可以改用 numpy-y 来进行转换。例如,pycairo 的测试套件包含以下内容:https://github.com/dalembertian/pycairo/blob/22d29e0820d0dcbe070a6eb6f8f302e8c41b71a7/test/isurface_get_data.py#L37-L42
buf = surface.get_data()
a = numpy.ndarray (shape=(w,h,4), dtype=numpy.uint8, buffer=buf)
# draw a vertical line
a[:,40,0] = 255 # byte 0 is blue on little-endian systems
a[:,40,1] = 0
a[:,40,2] = 0
因此,要从(在 Pillow-speak 中)BGRa 转换为 RGBa,您可以这样做来交换红色和蓝色通道(其中 a
是一个类似于上面的缓冲区) :
(a[:,:,0], a[:,:,2]) = (a[:,:,2], a[:,:,0])
如果这真的比您通过中间过程的方法更好Image
...好吧,我不知道。您必须判断执行此操作的最佳方法是什么。至少你现在应该能够解释为什么它是必要的(cairo 和 PIL 都没有支持通用的图像格式)