2

アルファチャンネルを含む可能性のある画像をカイロに変換したいと考えています。

私が書いたコードは、完全に不透明な画像をグレースケールに変換しますが、画像にアルファ チャネルが含まれていると失敗します。

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 のバージョンを入手するにはどうすればよいですか?

[*] これらの値は、cairo では通常のように、アルファによって乗算された RGB 値を持つことに注意してください。

4

2 に答える 2

1

NumPyを使用して、最終的に元のアルファを尊重して画像を変換することができました。cairo メーリング リストで質問しましたが、入手した唯一の代替案には、私のバージョンと同じ問題がありました (つまり、元のアルファ チャネルを尊重していませんでした)。

これが私の解決策です:

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 に答える