python-imaging-library - python3 - pip install pillow
¿Cómo se dibujan polígonos transparentes con Python? (6)
Estoy usando PIL (Biblioteca de imágenes de Python). Me gustaría dibujar polígonos transparentes. Parece que especificar un color de relleno que incluya un nivel alfa no funciona. Son sus soluciones?
Si no se puede hacer usando PIL, estoy dispuesto a usar algo más.
Si hay más de una solución, se debe tener en cuenta el rendimiento. El dibujo debe ser lo más rápido posible.
Estoy usando cairo + pycairo para esto, y funciona bien. Y puede compartir datos de imagen entre PIL y Cairo, utilizando la interfaz de búfer de python, si hay una operación en pil que no se puede hacer en el Cairo.
El módulo de imagen de PIL proporciona un método de combinación.
Crea una segunda imagen del mismo tamaño que la primera, con un fondo negro. Dibuja tu polígono en él (con color completo). Luego llama a Image.blend pasando las dos imágenes y un nivel alfa. Devuelve una tercera imagen, que debería tener un polígono semitransparente.
No he medido el rendimiento (¡ni siquiera lo he intentado!), Así que no puedo comentar sobre su idoneidad. Le sugiero que elabore su presupuesto de rendimiento y luego mida para ver si es lo suficientemente rápido para sus propósitos.
Lo que tuve que hacer cuando uso PIL para dibujar imágenes transparentes es crear una capa de color, una capa de opacidad con el polígono dibujado sobre ella, y componerlas con la capa base de la siguiente manera:
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)
Cuando utilicé Image.Blend, tuve problemas con comportamientos de trazado extraños en los polígonos dibujados.
El único problema con este enfoque es que el rendimiento es abismal al dibujar una gran cantidad de polígonos de tamaño razonable. Una solución mucho más rápida sería algo así como dibujar "manualmente" el polígono en una representación de matriz numpy de la imagen.
Tuve que dibujar un polígono externo con un contorno y restar polígonos internos (una operación común en GIS). Funciona como un amuleto usando el color (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
Por lo que he encontrado, no se puede hacer directamente con PIL. Aquí hay una solución con PyCairo. Cairo también es utilizado por Mozilla, GTX +, Mono, Inkscape y WebKit, así que creo que es seguro usarlo en términos de soporte futuro. También se puede hacer con aggdraw, un complemento opcional para PIL. Vea mi fuente en la lista para más detalles. Python versión 2.7.3 es usada.
Fuente: http://livingcode.org/2008/12/14/drawing-with-opacity.1.html
Archivo auxiliar: 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
Archivo principal: 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()
Esto es para Pillow, una horquilla más mantenida de PIL. http://pillow.readthedocs.org/
Si desea dibujar polígonos que sean transparentes, relativos entre sí, la Imagen base debe ser de tipo RGB, no RGBA, y el ImageDraw debe ser del tipo RGBA. Ejemplo:
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'')
Esto dibujará dos triángulos superpuestos con sus dos colores mezclados. Esto es mucho más rápido que tener que componer múltiples ''capas'' para cada polígono.