matplotlib.pyplot.tripcolor 如何用随机 RGB 颜色填充三角形?

matplotlib.pyplot.tripcolor how to fill triangles with random RGB colors?

假设我有一堆三角形,我知道如何使用 matplotlib.pyplot.tripcolor 绘制它们,我想知道如何使用整个 RGB 颜色中完全随机的 RGB 颜色填充单个三角形 space(所有 16777216 种颜色)与 x、y 无关,如何完成?

我有这个: source

import matplotlib.pyplot as plt
import numpy as np
from scipy.spatial import Delaunay
from random import random, randbytes

plt.style.use('_mpl-gallery-nogrid')

pts = np.zeros((360,2))
pts[:,0] = np.random.randint(0,1920,360)
pts[:,1] = np.random.randint(0,1080,360)
tri = Delaunay(pts)
plt.xlim(0, 1920)
plt.ylim(0, 1080)
centers = np.sum(pts[tri.simplices], axis=1, dtype='int')/3.0
colors = np.array([ (x-960)**2 + (y-540)**2 for x,y in centers])
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()
l = centers.shape[0]

colors = np.random.random(size=l)
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

他们工作正常,但两个示例的颜色变化太小。

第一个生成如下内容:

颜色一点都不随机。

第二个生成这个:

第二张图片颜色变化不大。

我尝试了很多方法,都失败了:

colors = np.random.random((l, 3))
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [tuple(randbytes(3)) for i in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [tuple(randbytes(4)) for i in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [tuple([random() for i in range(3)]) for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [tuple([random() for i in range(4)]) for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [randbytes(3).hex() for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = [randbytes(4).hex() for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = ['#'+randbytes(3).hex() for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

colors = ['#'+randbytes(4).hex() for j in range(l)]
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

前七次加注ValueError: Collections can only map rank 1 arrays.

最后两个加注TypeError: Image data of dtype <U6 cannot be converted to float.

如何正确地做到这一点? facecolors 到底应该放什么?官方文档对它应该是什么非常模糊。


为了完整起见,我的意图是:

from PIL import Image

fig = plt.figure(frameon=False, figsize=(19.2,10.8), dpi=100)
ax = fig.add_subplot(111)
ax.set_axis_off()
...
fig.canvas.draw()
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.axis('scaled')
plt.box(False)
Image.frombytes('RGB', fig.canvas.get_width_height(), fig.canvas.tostring_rgb())

我得到了这个:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon
from PIL import Image
from scipy.spatial import Delaunay
from random import random, randbytes

plt.style.use('_mpl-gallery-nogrid')

points = np.zeros((360,2))
points[:,0] = np.random.randint(0,1920,360)
points[:,1] = np.random.randint(0,1080,360)
triangles = points[Delaunay(points).simplices]

fig = plt.figure(frameon=False, figsize=(19.2,10.8), dpi=100)
ax = fig.add_subplot(111)
ax.set_axis_off()
for triangle in triangles:
    ax.add_patch(Polygon(triangle, edgecolor='#c0c0c0', facecolor='#'+randbytes(3).hex(), fill=True))

plt.xlim(0, 1920)
plt.ylim(0, 1080)
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.axis('scaled')
plt.box(False)
fig.canvas.draw()
Image.frombytes('RGB', fig.canvas.get_width_height(), fig.canvas.tostring_rgb()).show()

但是我的代码明显比tripcolor效率低很多。

解决此问题的一种方法是创建一个生成随机颜色的颜色图,独立于您分配给 facecolorsC 的值。例如:

import matplotlib.pyplot as plt
from matplotlib.colors import Colormap
import numpy as np
from scipy.spatial import Delaunay

class RandomColors(Colormap):
    def __init__(self):
        pass
    def __call__(self, X, alpha=None, bytes=False):
        # randomly generate an RGBA [N x 4] matrix of colors
        # where N is the number of elements of X
        # X is what we assigne to `facecolors` or `C`
        X = np.atleast_1d(X)
        col = np.random.random((X.shape[0], 4))
        # set alpha=1
        col[:, -1] = 1
        return col


plt.figure()
pts = np.zeros((360,2))
pts[:,0] = np.random.randint(0,1920,360)
pts[:,1] = np.random.randint(0,1080,360)
tri = Delaunay(pts)
plt.xlim(0, 1920)
plt.ylim(0, 1080)
centers = np.sum(pts[tri.simplices], axis=1, dtype='int')/3.0

# Don't really care about what's in this array: our custom colormap
# is going to ignore it!
colors = np.ones(centers.shape[0])
# instantiate the colormap
cmap = RandomColors()
plt.tripcolor(pts[:,0], pts[:,1], tri.simplices.copy(), facecolors=colors, cmap=cmap, edgecolors='k')
plt.gca().set_aspect('equal')
plt.show()

避免一起使用 tripcolor 可能是最简单的方法,除非您需要它的某些特定功能?您可以根据 Delauny 三角剖分创建自己的 PolyCollection,这在格式方面更加灵活。

from matplotlib.collections import PolyCollection
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
from copy import copy

n_points = 360
pts = np.random.randint(0, 1920, (n_points, 2)).astype(np.float32)

tri = Delaunay(pts)

vertices = np.stack((
    tri.points[tri.simplices, 0], # x
    tri.points[tri.simplices, 1], # y
), axis=-1)


collection = PolyCollection(vertices, edgecolor="k")
collection.set_facecolor(np.random.rand(len(vertices), 3))

fig, ax = plt.subplots(figsize=(8,8), facecolor="w")

ax.add_collection(copy(collection))
ax.autoscale_view()