这是我尝试创建一些可能适合您的用例的东西。它的功能有限——具体来说,只支持线性渐变和径向渐变——而且线性渐变本身也是有限的。但是,首先,让我们看一个示例输出:
基本上有两种方法
linear_gradient(i, poly, p1, p2, c1, c2)
和
radial_gradient(i, poly, p, c1, c2)
它们都得到一个枕头Image对象i,一个描述多边形poly的顶点列表,渐变的开始和停止颜色c1和c2,以及两个点p1和p2 描述线性渐变的方向(从一个顶点到第二个顶点)或单个点 p 描述径向渐变的中心。
在这两种方法中,初始多边形都是在最终图像大小的空白画布上绘制的,仅使用 Alpha 通道。
对于线性渐变,计算p1 和p2 之间的角度。绘制的多边形旋转该角度,并裁剪以获得适当线性渐变所需的尺寸。那是由np.linspace 创建的。梯度以已知角度旋转,但方向相反,最后平移以适合实际多边形。将渐变图像粘贴在中间多边形图像上,得到具有线性渐变的多边形,并将结果粘贴到实际图像上。
线性渐变的限制:您最好选择“在相对侧”的多边形的两个顶点,或者更好:使得多边形内的所有点都在这两个点所跨越的虚拟空间内。否则,当前的实现可能会失败,例如选择两个相邻顶点时。
径向渐变方法的工作方式略有不同。确定从p 到所有多边形顶点的最大距离。然后,对于实际图像大小的中间图像中的所有点,计算到p的距离,并通过计算的最大距离进行归一化。对于多边形内的所有点,我们得到了[0.0 ... 1.0] 范围内的值。这些值用于计算从c1 到c2 的适当颜色。至于线性梯度,则将梯度图像粘贴在中间多边形图像上,并将结果粘贴到实际图像上。
希望代码使用 cmets 是不言自明的。但如果有问题,请不要犹豫!
这是完整的代码:
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image, ImageDraw
# Draw polygon with linear gradient from point 1 to point 2 and ranging
# from color 1 to color 2 on given image
def linear_gradient(i, poly, p1, p2, c1, c2):
# Draw initial polygon, alpha channel only, on an empty canvas of image size
ii = Image.new('RGBA', i.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(ii)
draw.polygon(poly, fill=(0, 0, 0, 255), outline=None)
# Calculate angle between point 1 and 2
p1 = np.array(p1)
p2 = np.array(p2)
angle = np.arctan2(p2[1] - p1[1], p2[0] - p1[0]) / np.pi * 180
# Rotate and crop shape
temp = ii.rotate(angle, expand=True)
temp = temp.crop(temp.getbbox())
wt, ht = temp.size
# Create gradient from color 1 to 2 of appropriate size
gradient = np.linspace(c1, c2, wt, True).astype(np.uint8)
gradient = np.tile(gradient, [2 * h, 1, 1])
gradient = Image.fromarray(gradient)
# Paste gradient on blank canvas of sufficient size
temp = Image.new('RGBA', (max(i.size[0], gradient.size[0]),
max(i.size[1], gradient.size[1])), (0, 0, 0, 0))
temp.paste(gradient)
gradient = temp
# Rotate and translate gradient appropriately
x = np.sin(angle * np.pi / 180) * ht
y = np.cos(angle * np.pi / 180) * ht
gradient = gradient.rotate(-angle, center=(0, 0),
translate=(p1[0] + x, p1[1] - y))
# Paste gradient on temporary image
ii.paste(gradient.crop((0, 0, ii.size[0], ii.size[1])), mask=ii)
# Paste temporary image on actual image
i.paste(ii, mask=ii)
return i
# Draw polygon with radial gradient from point to the polygon border
# ranging from color 1 to color 2 on given image
def radial_gradient(i, poly, p, c1, c2):
# Draw initial polygon, alpha channel only, on an empty canvas of image size
ii = Image.new('RGBA', i.size, (0, 0, 0, 0))
draw = ImageDraw.Draw(ii)
draw.polygon(poly, fill=(0, 0, 0, 255), outline=None)
# Use polygon vertex with highest distance to given point as end of gradient
p = np.array(p)
max_dist = max([np.linalg.norm(np.array(v) - p) for v in poly])
# Calculate color values (gradient) for the whole canvas
x, y = np.meshgrid(np.arange(i.size[0]), np.arange(i.size[1]))
c = np.linalg.norm(np.stack((x, y), axis=2) - p, axis=2) / max_dist
c = np.tile(np.expand_dims(c, axis=2), [1, 1, 3])
c = (c1 * (1 - c) + c2 * c).astype(np.uint8)
c = Image.fromarray(c)
# Paste gradient on temporary image
ii.paste(c, mask=ii)
# Paste temporary image on actual image
i.paste(ii, mask=ii)
return i
# Create blank canvas with zero alpha channel
w, h = (800, 600)
image = Image.new('RGBA', (w, h), (0, 0, 0, 0))
# Draw first polygon with radial gradient
polygon = [(100, 200), (320, 130), (460, 300), (700, 500), (350, 550), (200, 400)]
point = (350, 350)
color1 = (255, 0, 0)
color2 = (0, 255, 0)
image = radial_gradient(image, polygon, point, color1, color2)
# Draw second polygon with linear gradient
polygon = [(500, 50), (650, 250), (775, 150), (700, 25)]
point1 = (700, 25)
point2 = (650, 250)
color1 = (255, 255, 0)
color2 = (0, 0, 255)
image = linear_gradient(image, polygon, point1, point2, color1, color2)
# Draw third polygon with linear gradient
polygon = [(50, 550), (200, 575), (200, 500), (100, 300), (25, 450)]
point1 = (100, 300)
point2 = (200, 575)
color1 = (255, 255, 255)
color2 = (255, 128, 0)
image = linear_gradient(image, polygon, point1, point2, color1, color2)
# Save image
image.save('image.png')
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.9.1
Matplotlib: 3.4.0
NumPy: 1.20.2
Pillow: 8.1.2
----------------------------------------