Как рисовать прозрачные полигоны?

Я использую PIL (библиотека изображений Python). Я хотел бы рисовать прозрачные полигоны. Кажется, что указание цвета заливки, включающего альфа-уровень, не работает. Их обходные пути?

Если это невозможно сделать с помощью PIL, я готов использовать что-то еще.

Если имеется более одного решения, следует учитывать производительность. Рисунок должен выполняться как можно быстрее.


person carrier    schedule 11.12.2008    source источник


Ответы (7)


Это для Pillow, более поддерживаемого форка PIL. http://pillow.readthedocs.org/

Если вы хотите рисовать многоугольники, которые прозрачны друг относительно друга, базовое изображение должно иметь тип RGB, а не RGBA, а ImageDraw должен быть типа RGBA. Пример:

from PIL import Image, ImageDraw

img = Image.new('RGB', (100, 100))
drw = ImageDraw.Draw(img, 'RGBA')
drw.polygon([(50, 0), (100, 100), (0, 100)], (255, 0, 0, 125))
drw.polygon([(50,100), (100, 0), (0, 0)], (0, 255, 0, 125))
del drw

img.save('out.png', 'PNG')

Это нарисует два перекрывающихся треугольника со смешиванием двух цветов. Это намного быстрее, чем создавать несколько «слоев» для каждого полигона.

person Matt    schedule 13.02.2014
comment
Спасибо, это очень полезно. Я понятия не имею, откуда вы взяли эту информацию - я не вижу ее в документах. У меня появилось странное поведение при использовании комбинаций RGB/RGBA, отличных от тех, которые вы упомянули... - person jwd; 24.09.2015
comment
Почему вы пишете del drw, а не del img? - person Marcus Johansson; 24.05.2016
comment
Работает как шарм! (Жаль, что вам нужно прокрутить вниз, чтобы получить лучший ответ :)) - person Anton vBR; 10.07.2017
comment
Я второй, где вы нашли эту документацию;) - person WestCoastProjects; 22.04.2020
comment
Благодарю вас. Почему-то было трудно найти решение для этого ответа, который работает на многоугольнике любой формы, в то время как большинство решений показывали прямоугольную область. - person sandeepsign; 06.05.2021

Что мне нужно было сделать при использовании PIL для рисования прозрачных изображений, так это создать цветной слой, слой непрозрачности с нарисованным на нем многоугольником и скомпоновать их с базовым слоем следующим образом:

color_layer = Image.new('RGBA', base_layer.size, fill_rgb)
alpha_mask = Image.new('L', base_layer.size, 0)
alpha_mask_draw = ImageDraw.Draw(alpha_mask)
alpha_mask_draw.polygon(self.outline, fill=fill_alpha)
base_layer = Image.composite(color_layer, base_layer, alpha_mask)

При использовании Image.Blend у меня были проблемы со странным поведением контуров нарисованных полигонов.

Единственная проблема с этим подходом заключается в том, что производительность ужасна при рисовании большого количества полигонов разумного размера. Гораздо более быстрым решением было бы что-то вроде «ручного» рисования многоугольника в представлении изображения в виде массива numpy.

person akdom    schedule 11.01.2009

Модуль изображения PIL предоставляет метод смешивания.

Создайте второе изображение того же размера, что и первое, с черным фоном. Нарисуйте на нем свой многоугольник (полным цветом). Затем вызовите Image.blend, передав два изображения и альфа-уровень. Он возвращает третье изображение, на котором должен быть полупрозрачный многоугольник.

Я не измерял производительность (эй, я даже не пробовал!), поэтому я не могу комментировать его пригодность. Я предлагаю вам рассчитать свой бюджет производительности, а затем измерить его, чтобы увидеть, достаточно ли он быстр для ваших целей.

person Oddthinking    schedule 22.12.2008

Я использую для этого cairo + pycairo, и это работает хорошо. И вы можете обмениваться данными изображения между PIL и cairo, используя интерфейс буфера python, если в pil есть операция, которую нельзя выполнить в cairo.

person Antonin ENFRUN    schedule 18.12.2008

Из того, что я обнаружил, это нельзя сделать напрямую с помощью PIL. Вот решение с PyCairo. Cairo также используется Mozilla, GTX+, Mono, Inkscape и WebKit, поэтому я думаю, что его безопасно использовать с точки зрения будущей поддержки. Это также можно сделать с помощью aggdraw, дополнительного дополнения к PIL. См. мой указанный источник для более подробной информации. Используется Python версии 2.7.3.

Источник: http://livingcode.org/2008/12/14/drawing-with-opacity.1.html

Вспомогательный файл: random_polys_util.py

    MIN_ALPHA = 50
    MAX_ALPHA = 100

    WIDTH = 500
    HEIGHT = 250

    #
    #   Utilities
    #
    def hex2tuple(hex_color):
        return tuple([int(hex_color[i:i+2], 16) for i in range(1,9,2)])

    def tuple2hex(tuple_color):
        return "#%0.2X%0.2X%0.2X%0.2X" % tuple_color

    def ints2floats(tuple_color):
        return tuple([c / 255.0 for c in tuple_color])

    def inc_point(p, dp):
        return (p[0] + dp[0]) % WIDTH, (p[1] + dp[1]) % HEIGHT

    def inc_triangle(t, dt):
        return tuple([inc_point(t[i], dt[i]) for i in range(3)])

    def inc_color(c, dc):
        new_c = [(c[i] + dc[i]) % 256 for i in range(3)]
        new_a = (c[3] + dc[3]) % MAX_ALPHA
        if new_a < MIN_ALPHA: new_a += MIN_ALPHA
        new_c.append(new_a)
        return tuple(new_c)

    def draw_all(draw_fn):
        triangle = start_t
        color = start_c
        for i in range(50):
            triangle = inc_triangle(triangle, dt)
            color = inc_color(color, dc)
            draw_fn(triangle, color)

    #
    #   Starting and incrementing values
    #
    start_c = hex2tuple('E6A20644')
    start_t = (127, 132), (341, 171), (434, 125)
    dt = (107, 23), (47, 73), (13, 97)
    dc = 61, 113, 109, 41

Основной файл: random_polys.py

from random_polys_util import *

def cairo_poly(pts, clr):
    ctx.set_source_rgba(*ints2floats(clr))
    ctx.move_to(*pts[-1])
    for pt in pts:
        ctx.line_to(*pt)
    ctx.close_path()
    ctx.fill()

def cairo_main():
    # Setup Cairo
    import cairo
    global ctx
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
    ctx = cairo.Context(surface)
    # fill background white
    cairo_poly(((0,0),(WIDTH,0),(WIDTH,HEIGHT),(0,HEIGHT)),(255,255,255,255))
    draw_all(cairo_poly)
    surface.write_to_png('cairo_example.png')

def main():
    cairo_main()

if __name__ == "__main__":
    main()
person Matt Campbell    schedule 11.05.2012

Мне пришлось нарисовать внешний многоугольник с контуром и вычесть внутренние многоугольники (обычная операция в ГИС). Работает как шарм, используя цвет (255,255,255,0).

image = Image.new("RGBA", (100,100))
drawing = ImageDraw.Draw(i)
for index, p in enumerate(polygons):
    if index == 0:
        options = { 'fill': "#AA5544",
                    'outline': "#993300"}
    else:
        options = {'fill': (255,255,255,0)}
    drawing.polygon( p, **options )

buf= StringIO.StringIO()
i.save(buf, format= 'PNG')
# do something with buf
person ivy    schedule 13.03.2012
comment
Это не имеет ничего общего с прозрачностью. - person Junuxx; 21.05.2012

Для этого вы можете использовать Shapely и OpenCV следующим образом:

import cv2
import numpy as np
from shapely.geometry import Polygon

alpha = 0.5 # that's your transparency factor
path = 'path_to_image.jpg'
image = cv2.imread(path)
(H, W) = image.shape[:2]

xmin = 0
ymin = 0 
xmax = int(W / 2)
ymax = int(H / 2)

polygon = Polygon([(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)])
int_coords = lambda x: np.array(x).round().astype(np.int32)
exterior = [int_coords(polygon.exterior.coords)]

overlay = image.copy()
cv2.fillPoly(overlay, exterior, color=(255, 255, 0))
cv2.addWeighted(overlay, alpha, image, 1 - alpha, 0, image)
cv2.imshow("Polygon", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
person tsveti_iko    schedule 28.05.2020