PIL:生成垂直渐变图像
PIL: Generating Vertical Gradient Image
在Android中,我用下面的代码生成了我需要的渐变背景:
<gradient
android:angle="90"
android:startColor="#40000000"
android:endColor="#00000000"
android:type="linear" />
背景从上到下从亮到相对暗。我想知道如何在 Python 中使用 PIL 执行相同的操作,因为我需要在另一个用 Python.
编写的程序上实现相同的效果
这是详细说明的技术。你需要两层叠在一起,每种颜色一层。然后你让顶层的透明度增加,底层的透明度降低。对于额外的作业,您可以将透明度更改为升序的对数刻度而不是线性的。玩得开心。
这里展示了绘制多色矩形水平和垂直渐变的方法。
rom PIL import Image, ImageDraw
BLACK, DARKGRAY, GRAY = ((0,0,0), (63,63,63), (127,127,127))
LIGHTGRAY, WHITE = ((191,191,191), (255,255,255))
BLUE, GREEN, RED = ((0, 0, 255), (0, 255, 0), (255, 0, 0))
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
class Rect(object):
def __init__(self, x1, y1, x2, y2):
minx, maxx = (x1,x2) if x1 < x2 else (x2,x1)
miny, maxy = (y1,y2) if y1 < y2 else (y2,y1)
self.min = Point(minx, miny)
self.max = Point(maxx, maxy)
width = property(lambda self: self.max.x - self.min.x)
height = property(lambda self: self.max.y - self.min.y)
def gradient_color(minval, maxval, val, color_palette):
""" Computes intermediate RGB color of a value in the range of minval
to maxval (inclusive) based on a color_palette representing the range.
"""
max_index = len(color_palette)-1
delta = maxval - minval
if delta == 0:
delta = 1
v = float(val-minval) / delta * max_index
i1, i2 = int(v), min(int(v)+1, max_index)
(r1, g1, b1), (r2, g2, b2) = color_palette[i1], color_palette[i2]
f = v - i1
return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))
def horz_gradient(draw, rect, color_func, color_palette):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
width = float(rect.width) # Cache.
for x in range(rect.min.x, rect.max.x+1):
f = (x - rect.min.x) / width
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
draw.line([(x, rect.min.y), (x, rect.max.y)], fill=color)
def vert_gradient(draw, rect, color_func, color_palette):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
height = float(rect.height) # Cache.
for y in range(rect.min.y, rect.max.y+1):
f = (y - rect.min.y) / height
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
draw.line([(rect.min.x, y), (rect.max.x, y)], fill=color)
if __name__ == '__main__':
# Draw a three color vertical gradient.
color_palette = [BLUE, GREEN, RED]
region = Rect(0, 0, 730, 350)
width, height = region.max.x+1, region.max.y+1
image = Image.new("RGB", (width, height), WHITE)
draw = ImageDraw.Draw(image)
vert_gradient(draw, region, gradient_color, color_palette)
image.show()
#image.save("vert_gradient.png", "PNG")
#print('image saved')
这是它生成并显示的图像:
这将计算 RGB color space, but other colorspaces could be used — for examples compare results of my answers to the question Range values to pseudocolor 中的中间色。
这可以轻松扩展以生成 RGBA (RGB+Alpha) 模式图像。
如果你只需要两种颜色,这可以很简单地完成:
def generate_gradient(
colour1: str, colour2: str, width: int, height: int) -> Image:
"""Generate a vertical gradient."""
base = Image.new('RGB', (width, height), colour1)
top = Image.new('RGB', (width, height), colour2)
mask = Image.new('L', (width, height))
mask_data = []
for y in range(height):
mask_data.extend([int(255 * (y / height))] * width)
mask.putdata(mask_data)
base.paste(top, (0, 0), mask)
return base
这会为每种颜色创建一个图层,然后创建一个透明度根据 y
位置而变化的蒙版。您可以将第 10 行中的 y / height
替换为 x / width
以获得水平渐变,或者将 x
和 y
的任何函数替换为另一个渐变。
对@martineau 的代码进行一些修改,此函数以度数(不仅是垂直或水平)处理梯度方向:
from PIL import Image
import math
BLACK, DARKGRAY, GRAY = ((0,0,0), (63,63,63), (127,127,127))
LIGHTGRAY, WHITE = ((191,191,191), (255,255,255))
BLUE, GREEN, RED = ((0, 0, 255), (0, 255, 0), (255, 0, 0))
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
def rot_x(self, degrees):
radians = math.radians(degrees)
return self.x * math.cos(radians) + self.y * math.sin(radians)
class Rect(object):
def __init__(self, x1, y1, x2, y2):
minx, maxx = (x1,x2) if x1 < x2 else (x2,x1)
miny, maxy = (y1,y2) if y1 < y2 else (y2,y1)
self.min = Point(minx, miny)
self.max = Point(maxx, maxy)
def min_max_rot_x(self, degrees):
first = True
for x in [self.min.x, self.max.x]:
for y in [self.min.y, self.max.y]:
p = Point(x, y)
rot_d = p.rot_x(degrees)
if first:
min_d = rot_d
max_d = rot_d
else:
min_d = min(min_d, rot_d)
max_d = max(max_d, rot_d)
first = False
return min_d, max_d
width = property(lambda self: self.max.x - self.min.x)
height = property(lambda self: self.max.y - self.min.y)
def gradient_color(minval, maxval, val, color_palette):
""" Computes intermediate RGB color of a value in the range of minval
to maxval (inclusive) based on a color_palette representing the range.
"""
max_index = len(color_palette)-1
delta = maxval - minval
if delta == 0:
delta = 1
v = float(val-minval) / delta * max_index
i1, i2 = int(v), min(int(v)+1, max_index)
(r1, g1, b1), (r2, g2, b2) = color_palette[i1], color_palette[i2]
f = v - i1
return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))
def degrees_gradient(im, rect, color_func, color_palette, degrees):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
min_d, max_d = rect.min_max_rot_x(degrees)
range_d = max_d - min_d
for x in range(rect.min.x, rect.max.x + 1):
for y in range(rect.min.y, rect.max.y+1):
p = Point(x, y)
f = (p.rot_x(degrees) - min_d) / range_d
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
im.putpixel((x, y), color)
def gradient_image(color_palette, degrees):
region = Rect(0, 0, 600, 400)
width, height = region.max.x+1, region.max.y+1
image = Image.new("RGB", (width, height), WHITE)
degrees_gradient(image, region, gradient_color, color_palette, -degrees)
return image
这种灵活性的代价是必须逐个像素地设置颜色,而不是使用线条。
在Android中,我用下面的代码生成了我需要的渐变背景:
<gradient
android:angle="90"
android:startColor="#40000000"
android:endColor="#00000000"
android:type="linear" />
背景从上到下从亮到相对暗。我想知道如何在 Python 中使用 PIL 执行相同的操作,因为我需要在另一个用 Python.
编写的程序上实现相同的效果这是详细说明的技术。你需要两层叠在一起,每种颜色一层。然后你让顶层的透明度增加,底层的透明度降低。对于额外的作业,您可以将透明度更改为升序的对数刻度而不是线性的。玩得开心。
这里展示了绘制多色矩形水平和垂直渐变的方法。
rom PIL import Image, ImageDraw
BLACK, DARKGRAY, GRAY = ((0,0,0), (63,63,63), (127,127,127))
LIGHTGRAY, WHITE = ((191,191,191), (255,255,255))
BLUE, GREEN, RED = ((0, 0, 255), (0, 255, 0), (255, 0, 0))
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
class Rect(object):
def __init__(self, x1, y1, x2, y2):
minx, maxx = (x1,x2) if x1 < x2 else (x2,x1)
miny, maxy = (y1,y2) if y1 < y2 else (y2,y1)
self.min = Point(minx, miny)
self.max = Point(maxx, maxy)
width = property(lambda self: self.max.x - self.min.x)
height = property(lambda self: self.max.y - self.min.y)
def gradient_color(minval, maxval, val, color_palette):
""" Computes intermediate RGB color of a value in the range of minval
to maxval (inclusive) based on a color_palette representing the range.
"""
max_index = len(color_palette)-1
delta = maxval - minval
if delta == 0:
delta = 1
v = float(val-minval) / delta * max_index
i1, i2 = int(v), min(int(v)+1, max_index)
(r1, g1, b1), (r2, g2, b2) = color_palette[i1], color_palette[i2]
f = v - i1
return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))
def horz_gradient(draw, rect, color_func, color_palette):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
width = float(rect.width) # Cache.
for x in range(rect.min.x, rect.max.x+1):
f = (x - rect.min.x) / width
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
draw.line([(x, rect.min.y), (x, rect.max.y)], fill=color)
def vert_gradient(draw, rect, color_func, color_palette):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
height = float(rect.height) # Cache.
for y in range(rect.min.y, rect.max.y+1):
f = (y - rect.min.y) / height
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
draw.line([(rect.min.x, y), (rect.max.x, y)], fill=color)
if __name__ == '__main__':
# Draw a three color vertical gradient.
color_palette = [BLUE, GREEN, RED]
region = Rect(0, 0, 730, 350)
width, height = region.max.x+1, region.max.y+1
image = Image.new("RGB", (width, height), WHITE)
draw = ImageDraw.Draw(image)
vert_gradient(draw, region, gradient_color, color_palette)
image.show()
#image.save("vert_gradient.png", "PNG")
#print('image saved')
这是它生成并显示的图像:
这将计算 RGB color space, but other colorspaces could be used — for examples compare results of my answers to the question Range values to pseudocolor 中的中间色。
这可以轻松扩展以生成 RGBA (RGB+Alpha) 模式图像。
如果你只需要两种颜色,这可以很简单地完成:
def generate_gradient(
colour1: str, colour2: str, width: int, height: int) -> Image:
"""Generate a vertical gradient."""
base = Image.new('RGB', (width, height), colour1)
top = Image.new('RGB', (width, height), colour2)
mask = Image.new('L', (width, height))
mask_data = []
for y in range(height):
mask_data.extend([int(255 * (y / height))] * width)
mask.putdata(mask_data)
base.paste(top, (0, 0), mask)
return base
这会为每种颜色创建一个图层,然后创建一个透明度根据 y
位置而变化的蒙版。您可以将第 10 行中的 y / height
替换为 x / width
以获得水平渐变,或者将 x
和 y
的任何函数替换为另一个渐变。
对@martineau 的代码进行一些修改,此函数以度数(不仅是垂直或水平)处理梯度方向:
from PIL import Image
import math
BLACK, DARKGRAY, GRAY = ((0,0,0), (63,63,63), (127,127,127))
LIGHTGRAY, WHITE = ((191,191,191), (255,255,255))
BLUE, GREEN, RED = ((0, 0, 255), (0, 255, 0), (255, 0, 0))
class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y
def rot_x(self, degrees):
radians = math.radians(degrees)
return self.x * math.cos(radians) + self.y * math.sin(radians)
class Rect(object):
def __init__(self, x1, y1, x2, y2):
minx, maxx = (x1,x2) if x1 < x2 else (x2,x1)
miny, maxy = (y1,y2) if y1 < y2 else (y2,y1)
self.min = Point(minx, miny)
self.max = Point(maxx, maxy)
def min_max_rot_x(self, degrees):
first = True
for x in [self.min.x, self.max.x]:
for y in [self.min.y, self.max.y]:
p = Point(x, y)
rot_d = p.rot_x(degrees)
if first:
min_d = rot_d
max_d = rot_d
else:
min_d = min(min_d, rot_d)
max_d = max(max_d, rot_d)
first = False
return min_d, max_d
width = property(lambda self: self.max.x - self.min.x)
height = property(lambda self: self.max.y - self.min.y)
def gradient_color(minval, maxval, val, color_palette):
""" Computes intermediate RGB color of a value in the range of minval
to maxval (inclusive) based on a color_palette representing the range.
"""
max_index = len(color_palette)-1
delta = maxval - minval
if delta == 0:
delta = 1
v = float(val-minval) / delta * max_index
i1, i2 = int(v), min(int(v)+1, max_index)
(r1, g1, b1), (r2, g2, b2) = color_palette[i1], color_palette[i2]
f = v - i1
return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))
def degrees_gradient(im, rect, color_func, color_palette, degrees):
minval, maxval = 1, len(color_palette)
delta = maxval - minval
min_d, max_d = rect.min_max_rot_x(degrees)
range_d = max_d - min_d
for x in range(rect.min.x, rect.max.x + 1):
for y in range(rect.min.y, rect.max.y+1):
p = Point(x, y)
f = (p.rot_x(degrees) - min_d) / range_d
val = minval + f * delta
color = color_func(minval, maxval, val, color_palette)
im.putpixel((x, y), color)
def gradient_image(color_palette, degrees):
region = Rect(0, 0, 600, 400)
width, height = region.max.x+1, region.max.y+1
image = Image.new("RGB", (width, height), WHITE)
degrees_gradient(image, region, gradient_color, color_palette, -degrees)
return image
这种灵活性的代价是必须逐个像素地设置颜色,而不是使用线条。