2

我想将可能带有 alpha 通道的图像转换为 cairo。

我编写的代码将完全不透明的图像转换为灰度,但当图像包含 alpha 通道时失败:

import cairo
CAIRO_OPERATOR_HSL_LUMINOSITY = 28  # my py2cairo seems outdated

def convert_to_grayscale(img_in):
    img_out = img_in.create_similar(
        cairo.CONTENT_COLOR_ALPHA, img_in.get_width(), img_in.get_height())
    cr = cairo.Context(img_out)
    cr.set_source_rgba(1, 1, 1, 1)
    cr.paint()
    cr.set_source_surface(img_in)
    cr.set_operator(CAIRO_OPERATOR_HSL_LUMINOSITY)
    cr.paint()

    return img_out

包含 RGBA 值(20、30、40、255)的图像将(正确)转换为(28、28、28、255)。但是,如果图像不是完全不透明,结果将是错误的,例如,如果我用颜色 (10, 15, 20, 128) 转换图像,我会返回 (141, 141, 141, 25),当我期待 (14, 14, 14, 128)[*]。我怎样才能得到一个可以很好地处理半透明图像的 convert_to_grayscale 版本?

[*] 请注意,这些值的 RGB 值与它们的 alpha 值相乘,这在开罗很常见。

4

2 回答 2

2

我今天也尝试做同样的事情,并想出了一种不同的方法来做到这一点。我实际上并没有在 Python 中这样做,实际上我根本不懂 Python,所以我无法提供任何代码。但是,这就是我所做的:

  • 从原始图像创建表面
  • 为该表面创建图案
  • 使用 CAIRO_OPERATOR_HSL_SATURATION 应用 rgb 源(黑色),使用模式作为掩码

为了让事情更清楚,在 C 中它的意思是这样s的(新的 cairo_surface_tcr及其相关的 cairo_t 在哪里;假设您已经将原始图片放在那里):

cairo_pattern_t *pattern = cairo_pattern_create_for_surface (s);
cairo_rectangle (cr, 0, 0, width, height);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_set_operator (cr, CAIRO_OPERATOR_HSL_SATURATION);
cairo_mask (cr, pattern);
cairo_pattern_destroy (pattern);

在此处添加它以防万一/希望它可能对某些人有所帮助。

于 2012-11-23T21:49:59.677 回答
1

我终于设法使用 NumPy 转换了尊重原始 alpha 的图像。我在 cairo 邮件列表中询问过,但我得到的唯一替代方案与我的版本有相同的问题(即,它不尊重原始 alpha 通道)。

这是我的解决方案:

import cairo
import numpy
import sys


def convert_to_grayscale(img_in):
    """Convert an image to grayscale.

    Arguments:
        img_in: (cairo.ImageSurface) input image.

    Return:
        (cairo.ImageSurface) image in grayscale, in ARGB32 mode.

    Timing:
        ~100ms to convert an image of 800x800

    Examples:
        # returns a B&W image
        >>> convert_to_grayscale(cairo.ImageSurface.create_from_png('test.png'))
    """
    a = numpy.frombuffer(img_in.get_data(), numpy.uint8)
    w, h = img_in.get_width(), img_in.get_height()
    a.shape = (w, h, 4)

    assert sys.byteorder == 'little', (
        'The luminosity vector needs to be switched if we\'re in a big endian architecture. '
        'The alpha channel will be at position 0 instead of 3.')
    alpha = a[:, :, 3]
    alpha.shape = (w, h, 1)

    luminosity_float = numpy.sum(a * numpy.array([.114, .587, .299, 0]), axis=2)
    luminosity_int = numpy.array(luminosity_float, dtype=numpy.uint8)
    luminosity_int.shape = (w, h, 1)
    grayscale_gbra = numpy.concatenate((luminosity_int, luminosity_int, luminosity_int, alpha),
                                       axis=2)
    stride = cairo.ImageSurface.format_stride_for_width(cairo.FORMAT_ARGB32, w)
    assert stride == 4 * w, 'We need to modify the numpy code if the stride is different'
    img_out = cairo.ImageSurface.create_for_data(grayscale_gbra, cairo.FORMAT_ARGB32, w, h, stride)

    return img_out
于 2012-09-13T10:38:16.563 回答