pythonpycairo

PyCairo and modifying pixel data


I'm using PyCairo to draw some vector images programmatically from a Python script. It works fine. But now I'd like to access the pixel data and do some further processing on them at the pixel level (things like blur or other raster effects) and then continue using that image surface with PyCairo to draw some more vector shapes.

I found the get_data() method in cairo.ImageSurface class, but I'm not sure how to use it, because the documentation is very cryptic about it. It just says that it returns something called a "Python buffer", but there are no code examples of how this can actually be used in a real aplication.

Can anyone provide an example code of how to get the grip of those pixels in that "Python buffer" thingamajig? (preferably without the need of copying the entire image back and forth from/to PyCairo surfaces).


Solution

  • The data is the raw pixel data. It's a memoryview representing the underlying ImageSurface. The data has a different interpretation depending on the format.

    Let's consider only the RGB24 pixel format for simplicity sake. Each pixel is stored as four bytes. Red, green, and blue respectively. The fourth byte is ignored, and is only there for performance reasons.

    The pixels are then stored row by row, the first row coming first and the second row coming after and so on and so forth.

    The might be additional padding at the end of the row as well, therefore the stride of the data is a crucial property. To get the byte index of a specific row y we thus need to compute y * stride. To this we add a x coordinate times the pixel byte width 4.

    This is all illustrated in the following small python program that draws a white rectangle on a black background.

    import cairo
    width, height = 100, 100
    surface = cairo.ImageSurface(cairo.Format.RGB24, width, height)
    data = surface.get_data()
    for y in range(50 - 20, 50 + 20):
        for x in range(50 - 20, 50 + 20):
            index = y * surface.get_stride() + x * 4
            data[index] = 255  # red
            data[index + 1] = 255  # green
            data[index + 2] = 255  # blue
    surface.write_to_png("im.png")