matplotlib Python中颜色线性插值的最简单示例

j8ag8udp  于 2023-03-03  发布在  Python
关注(0)|答案(3)|浏览(272)

我正在寻找一种基于附近对象的颜色来给对象着色的方法,从我所读到的来看,线性插值似乎是要做的事情。
我已经浏览了SO上很多(所有?)关于Python中颜色的线性插值的帖子,但还没有能够使任何一个与我的代码一起工作。
现在我想尽可能的简单,如果我有一个从紫色到白色的渐变和两个对象,我该怎么告诉python:如果对象1是紫色,使对象2是浅紫色?
查看this post,函数lerp定义如下:

def lerp(color1, color2, frac):
    return color1 * (1 - frac) + color2 * frac

This post有很多建议,但似乎没有解决,虽然它确实包含了很多关于加法和减法着色方法的有趣信息。我知道着色是复杂的,我不想做一个多余的职位,所以我希望真正强调,我正在寻找最简单,最基本的方法来做一些非常简单的事情。例如,如果一个对象紧挨着一个深紫色的对象,则使该对象具有较浅的紫色阴影。
在本例中,我创建了3个多边形,并将它们全部着色为洋红色:

import matplotlib.pyplot as plt
from shapely.geometry import Point, LineString

# functions for plotting
def plot_coords(coords, color):
    pts = list(coords)
    x, y = zip(*pts)
    # print(color)
    plt.plot(x,y, color='k', linewidth=1)
    plt.fill_between(x, y, facecolor=color)

def plot_polys(polys, color):
    for poly, color in zip(polys, color):
        plot_coords(poly.exterior.coords, color)

x = 0
y = 0

# make some points
points = [Point(x, y),
          Point((x + 2), y),
          Point((x - 2), y)]

#buffer points by 2 to make circles
circles = []
for point in points:
    circles.append(point.buffer(2))

# plot 3 magenta circles
plot_polys(circles, "m"*3)

如何使用上面的lerp函数(或其他函数)来给圆圈渐变着色?
注意:我知道我可以使用matplotlib中预先制作的调色板,但我的长期目标是理解控制颜色变化的算法。
谢谢你的帮忙!

tcomlyy6

tcomlyy61#

在下面,我不使用圆或多边形,只是简单的曲线,但我相信你会得到我的观点。
我的三分,就是...
1.如果我们可以对颜色分量使用真实的,则颜色插值以直接的方式工作,因此我们使用matplotlib.colors.to_rgbRGB空间中定义我们的颜色。
1.插值必须在两种颜色的每个单独分量上进行。
1.最后,我们可以使用西内斯的时移作为参数,为每条曲线指定不同的颜色。
我还添加了一条细黑线来“关闭”绘图,只是为了美观。
如果你能适应你的问题,好的,否则就问吧!

import numpy as np
import matplotlib.pyplot as plt

# point no. 1
from matplotlib.colors import to_rgb
mgt, wht = to_rgb('magenta'), to_rgb('white')

# point no.2
def mix(col0, col1, ratio):
    return tuple(cmp0*ratio+cmp1*(1-ratio)
        for cmp0, cmp1 in zip(col0, col1))

t = np.linspace(0, 2*np.pi, 629)
# point no. 3, the phase shifts but also the interpolation ratios 
for phase_shift in np.linspace(0, 1, 31):
    plt.plot(t, np.sin(t-phase_shift), color=mix(wht, mgt, phase_shift))
plt.plot(t, np.sin(t-phase_shift), color='k', lw=0.4)
plt.show()

PS如果在生产中检查0 ≤ ratio ≤ 1会更好,不是吗?

未测试

def lerp(color1, color2, frac):
    from matplotlib.colors import to rgb
    c1, c2 = (to_rgb(color) for color in (color1, color2))
    return tuple(x1*(1-frac) + x2*frac for x1, x2 in zip(c1, c2))
...
plot_polys(circles, [lerp("m", "white", frac) for frac in (0, 0.4, 0.8)])
r55awzrz

r55awzrz2#

这很有趣。我过去对渐变和颜色混合的感觉和你完全一样。这是一个一开始看起来很容易理解的主题,但是你越挖掘就越困惑。这促使我写了一个叫做colorir的库。它的目标是简化Python中使用颜色的工作流程。它的主要卖点之一是制作渐变和插值颜色是多么容易。
贝娄遵循你的代码的工作版本,我只改变了两行,使它的工作!我还删除了你的评论,以突出我所做的更改。

import matplotlib.pyplot as plt
from shapely.geometry import Point, LineString
from colorir import Grad

def plot_coords(coords, color):
    pts = list(coords)
    x, y = zip(*pts)
    plt.plot(x,y, color='k', linewidth=1)
    plt.fill_between(x, y, facecolor=color)

def plot_polys(polys, color):
    for poly, color in zip(polys, color):
        plot_coords(poly.exterior.coords, color)

x = 0
y = 0

points = [Point((x - 2), y),  # Points were ordered according to their x values
          Point(x, y),
          Point((x + 2), y)]

circles = []
for point in points:
    circles.append(point.buffer(2))

# Create a gradient from magenta to white and sample three colors
colors = Grad(["ff00ff", "ffffff"]).n_colors(3)
plot_polys(circles, colors)
plt.show()

绘图:x1c 0d1x
既然你说你想要一些关于插值如何工作的直觉,我将给予我所能解释colorir在table底下做什么。
首先,您需要了解插值的含义。
简单地说,插值是在坐标系中找到两点中间点的过程。这意味着如果我有一个点(1,2,3)和一个点(0,0,0),中途(0.5)这些点的插值是(0.5,1,1.5),我们也可以在0 - 1区间内插值其他值,如0.3,0.7等。
颜色插值的问题特别来自RGB不是一个感知均匀的颜色系统。本质上,这意味着两个RGB点的中间点插值不是大多数人认为的那些颜色的完美混合。你可以在这里阅读更多关于这个主题的内容。
我们如何解决这个问题呢?最简单的方法是首先将RGB值转换为在感知上一致的颜色系统,然后才在新的坐标系统中插值点。
这就是colorir在创建Grad对象时所做的事情:
1.解释输入颜色的值(在我们的例子中,十六进制rgb值"ff00ff"-洋红色和"ffffff"-白色)
1.将这些颜色转换为感知上统一的颜色坐标系(默认为CIELuv,但也可以使用其他颜色空间)
1.使用方法Grad.n_colors(),根据需要从渐变中采样任意数量的颜色
1.将采样颜色转换回RGB或其他所需格式
这个过程的第三步实际上实现了一个与你在问题中提到的公式非常相似的公式,类似于(伪代码):

function lerp(color1, color2, percentage):
    return [
               (color2[0] - color1[0]) * percentage,
               (color2[1] - color1[1]) * percentage,
               (color2[2] - color1[2]) * percentage
           ]

关于如何使用colorir,请参考colorir's docs和其他问题。
另外,请随意查看colorir's source code中的数学运算!

9fkzdhlc

9fkzdhlc3#

下面是我编写的一些代码,尝试执行类似的操作
正如你所看到的,我在每个颜色值rgb(而不是a)之间进行了单独的比较,然后将它们加回一起
我做这个相当快,可能还可以改进。

# what I used to but let's go with yours
# def lerp(a, b, p):
#     return a + ((b - a) * p)
def lerp(color1, color2, frac):
    return color1 * (1 - frac) + color2 * frac

def lerpH(c1, c1, p):
    r = lerp(int(c1[:2], 16), int(c2[:2], 16), p)
    g = lerp(int(c1[2:4], 16), int(c2[2:4], 16), p)
    b = lerp(int(c1[4:6], 16), int(c2[4:6] ,16), p)
    t = lambda _x: str(hex(int(_x)))[2:]
    return f'{t(r)}{t(g)}{t(b)}'

color = lerpH("a480ff", "80bdff", .5)

# untested
iterations = 5
startcolor = "a480ff" 
# startcolor = str(hex(0xa480ff))[2:]  # for hex()
endcolor = "80bdff"
# endcolor = str(hex(0x80bdff))[2:]  # for hex()
colors = []

for x in range(0, iterations):
    colors[x] = lerpH(startcolor, endcolor, float(x)/iterations)
    #colors[x] = int(colors[x], 16)  # this is to convert back to hex

相关问题