簡體   English   中英

PIL:生成垂直梯度圖像

[英]PIL: Generating Vertical Gradient Image

在 Android 中,我使用以下代碼生成了我需要的漸變背景:

<gradient
    android:angle="90"
    android:startColor="#40000000"
    android:endColor="#00000000"
    android:type="linear" />

背景從上到下從淺到深。 我想知道如何使用 PIL 在 Python 中做同樣的事情,因為我需要對另一個用 Python 編寫的程序產生相同的效果。

這里展示了繪制多色矩形水平和垂直漸變的方法。

from 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

    @staticmethod
    def from_point(other):
        return Point(other.x, other.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)

    @staticmethod
    def from_points(p1, p2):
        return Rect(p1.x, p1.y, p2.x, p2.y)

    def __str__(self):
        return 'Rect({:d}, {:d}, {:d}, {:d})'.format(self.min.x, self.min.y,
                                                     self.max.x, self.max.x)
    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)

color_palette = [BLUE, GREEN, RED]
region = Rect(0, 0, 730, 350)
imgx, imgy = region.max.x+1, region.max.y+1
image = Image.new("RGB", (imgx, imgy), 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 顏色空間中的中間顏色,但也可以使用其他顏色空間— 例如,將我對問題Range values 的答案的結果與偽顏色進行比較。

這可以很容易地擴展為生成 RGBA (RGB+Alpha) 模式圖像。

這是詳細說明的技術。 你需要 2 層相互疊加,每種顏色一層。 然后,您使頂層的透明度增加,底層的透明度降低。 對於額外的作業,您可以將透明度更改為升序對數刻度而不是線性。 玩得開心。

如果您只需要兩種顏色,這可以非常簡單地完成:

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):
        for x in range(width):
            mask_data.append(int(255 * (y / height)))
    mask.putdata(mask_data)
    base.paste(top, (0, 0), mask)
    return base

這會在每種顏色中創建一個圖層,然后創建一個透明度根據y位置而變化的蒙版。 第 10 行中的y / height ,水平梯度的x / width ,或另一個梯度的xy任何函數。

對@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

這種靈活性的代價是必須逐個像素地設置顏色而不是使用線條。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM