如何绘制透明多边形?

Posted

技术标签:

【中文标题】如何绘制透明多边形?【英文标题】:How to draw transparent polygons? 【发布时间】:2010-09-26 11:01:48 【问题描述】:

我正在使用 PIL(Python 图像库)。我想画透明的多边形。似乎指定包含 alpha 级别的填充颜色不起作用。他们的解决方法是什么?

如果使用 PIL 无法完成,我愿意使用其他东西。

如果有多个解决方案,则应考虑性能。绘图需要尽可能快。

【问题讨论】:

【参考方案1】:

为此,您可以像这样使用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()

【讨论】:

【参考方案2】:

这是针对 Pillow 的,它是 PIL 的一个更维护的分支。 http://pillow.readthedocs.org/

如果你想绘制透明的、相对于彼此的多边形,则基础 Image 必须是 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')

这将绘制两个重叠的三角形,它们的两种颜色混合。这比为每个多边形合成多个“层”要快得多。

【讨论】:

谢谢,这很有用。我不知道你从哪里得到这些信息 - 我没有看到它记录在案。除了你提到的那些之外,我在使用 RGB/RGBA 组合时遇到了一些奇怪的行为...... 为什么写del drw而不写del img? 像魅力一样工作! (可惜您需要一直向下滚动才能获得最佳答案:)) 我支持“你在哪里找到该文档”;) 谢谢。不知何故,很难找到这个答案的解决方案,它适用于任何形状的多边形,而大多数解决方案都显示矩形​​区域。【参考方案3】:

据我发现,它不能直接用 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()

【讨论】:

【参考方案4】:

我必须用轮廓绘制外部多边形,并减去内部多边形(GIS 中的常见操作)。使用颜色(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

【讨论】:

这与透明度无关。【参考方案5】:

使用 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 数组表示上绘制多边形。

【讨论】:

【参考方案6】:

PIL 的 Image 模块提供了一种混合方法。

创建与第一个大小相同的第二个图像,背景为黑色。在上面画出你的多边形(全彩色)。然后调用 Image.blend 传递两个图像和一个 alpha 级别。它返回第三张图像,上面应该有一个半透明的多边形。

我没有测量性能(嘿,我什至没有尝试过!)所以我无法评论它的适用性。我建议您计算出您的性能预算,然后对其进行衡量,看看它是否足够快以满足您的目的。

【讨论】:

【参考方案7】:

我为此使用cairo + pycairo,效果很好。 并且你可以在PIL和cairo之间共享图像数据,使用python缓冲区接口,如果pil中有无法在cairo中完成的操作。

【讨论】:

以上是关于如何绘制透明多边形?的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV:如何混合多个不同颜色的半透明多边形?

OpenGL:渲染具有大量纹理透明度的模型,没有绘制顺序?

使用 MapKit 框架在谷歌地图上绘制多边形

DirectX 图像纹理四边形在透明的地方显示底层控件颜色

所有多边形都变得透明,如何修复?

安卓怎么设计六边形的游戏背景