简体   繁体   English

Matplotlib:用不同的颜色绘制许多断开的线段

[英]Matplotlib: Plotting numerous disconnected line segments with different colors

I have a set of data records like this: 我有一组这样的数据记录:

(s1, t1), (u1, v1), color1
(s2, t2), (u2, v2), color2
.
.
.
(sN, tN), (uN, vN), colorN

In any record, the first two values are the end-points of a line segment, the third value is the color of that line segment. 在任何记录中,前两个值是线段的终点 ,第三个值是该线段的颜色 More specifically, (sn, tn) are the xy coordinates of the first end-point, (un, vn) are the xy coordinates of the second-endpoint. 更具体地, (sn, tn)是第一端点的xy坐标, (un, vn)是第二端点的xy坐标。 Also, color is an rgb with alpha value. 此外, 颜色是具有alpha值的rgb。

In general, any two line segments are disconnected (meaning that their end-points do not necessarily coincide). 通常,任何两个线段都是断开的 (意味着它们的端点不一定重合)。

How to plot this data using matplotlib with a single plot call (or as few as possible) as there could be potentially thousands of records. 如何使用matplotlib使用单个plot调用(或尽可能少)绘制此数据,因为可能存在数千条记录。

Attempts 尝试

Preparing the data in one big list and calling plot against it is way too slow. 准备一个大清单中的数据并调用对它的plot太慢了。 For example the following code couldn't finish in a reasonable amount of time: 例如,以下代码无法在合理的时间内完成:

import numpy as np
import matplotlib.pyplot as plt

data = []
for _ in xrange(60000):
    data.append((np.random.rand(), np.random.rand()))
    data.append((np.random.rand(), np.random.rand()))
    data.append('r')

print 'now plotting...' # from now on, takes too long
plt.plot(*data)
print 'done'
#plt.show()

I was able to speed-up the plot rendering by using the None insertion trick as follows: 我能够使用None插入技巧加速绘图渲染,如下所示:

import numpy as np
import matplotlib.pyplot as plt
from timeit import timeit

N = 60000
_s = np.random.rand(N)
_t = np.random.rand(N)
_u = np.random.rand(N)
_v = np.random.rand(N)
x = []
y = []
for s, t, u, v in zip(_s, _t, _u, _v):
    x.append(s)
    x.append(u)
    x.append(None)
    y.append(t)
    y.append(v)
    y.append(None)
print timeit(lambda:plt.plot(x, y), number=1)

This executes in under a second on my machine. 这在我的机器上执行一秒钟。 I still have to figure out how to embed the color values (RGB with alpha channel). 我仍然需要弄清楚如何嵌入颜色值(带alpha通道的RGB)。

use LineCollection : 使用LineCollection

import numpy as np
import pylab as pl
from matplotlib import collections  as mc

lines = [[(0, 1), (1, 1)], [(2, 3), (3, 3)], [(1, 2), (1, 3)]]
c = np.array([(1, 0, 0, 1), (0, 1, 0, 1), (0, 0, 1, 1)])

lc = mc.LineCollection(lines, colors=c, linewidths=2)
fig, ax = pl.subplots()
ax.add_collection(lc)
ax.autoscale()
ax.margins(0.1)

here is the output: 这是输出:

在此输入图像描述

function plot allows to draw multiple lines in one call, if your data is just in a list, just unpack it when passing it to plot : 函数plot允许在一次调用中绘制多行,如果您的数据只在列表中,只需将其传递给plot时将其解压缩:

In [315]: data=[(1, 1), (2, 3), 'r', #assuming points are (1,2) (1,3) actually and,
                                     #here they are in form of (x1, x2), (y1, y2)
     ...: (2, 2), (4, 5), 'g',
     ...: (5, 5), (6, 7), 'b',]

In [316]: plot(*data)
Out[316]: 
[<matplotlib.lines.Line2D at 0x8752870>,
 <matplotlib.lines.Line2D at 0x8752a30>,
 <matplotlib.lines.Line2D at 0x8752db0>]

在此输入图像描述

OK, I ended up rasterising the lines on a PIL image before converting it to a numpy array: 好吧,我最终光栅化PIL图像上的线条,然后将其转换为numpy数组:

from PIL import Image
from PIL import ImageDraw
import random as rnd
import numpy as np
import matplotlib.pyplot as plt

N = 60000
s = (500, 500)

im = Image.new('RGBA', s, (255,255,255,255))
draw = ImageDraw.Draw(im)

for i in range(N):
    x1 = rnd.random() * s[0]
    y1 = rnd.random() * s[1]
    x2 = rnd.random() * s[0]
    y2 = rnd.random() * s[1]
    alpha = rnd.random()
    color  = (int(rnd.random() * 256), int(rnd.random() * 256), int(rnd.random() * 256), int(alpha * 256)) 
    draw.line(((x1,y1),(x2,y2)), fill=color, width=1)

plt.imshow(np.asarray(im),
           origin='lower')
plt.show()

This is by far the fastest solution and it fits my real-time needs perfectly. 这是迄今为止最快的解决方案,它完全符合我的实时需求。 One caveat though is the lines are drawn without anti-aliasing. 但需要注意的是,绘制的线条没有抗锯齿。

I have tried a good few 2D rendering engines available on Python 3, while looking for a fast solution for an output stage in image-oriented Deep Learning & GAN. 我在Python 3上尝试了很多2D渲染引擎,同时在面向图像的Deep Learning和GAN中寻找输出阶段的快速解决方案。

Using the following benchmark: Time to render 99 lines into a 256x256 off-screen image (or whatever is more effective) with and without anti-alias. 使用以下基准:使用和不使用抗锯齿将99行渲染为256x256离屏图像(或任何更有效的图像)的时间。

The results, in order of efficiency on my oldish x301 laptop: 结果,按照我老式的x301笔记本电脑的效率顺序:

  • PyGtk2: ~2500 FPS, (Python 2, GTK 2, not sure how to get AA) PyGtk2:~2500 FPS,(Python 2,GTK 2,不知道如何获得AA)
  • PyQt5: ~1200 FPS, ~350 with Antialias PyQt5:〜1200 FPS,带有Antialias的~350
  • PyQt4: ~1100 FPS, ~380 with AA PyQt4:~1100 FPS,~380 AA
  • Cairo: ~750 FPS, ~250 with AA (only slightly faster with 'FAST' AA) 开罗:约750 FPS,约250加AA(只有'FAST'AA略快)
  • PIL: ~600 FPS PIL:约600 FPS

The baseline is a loop which takes ~0.1 ms (10,000 FPS) retrieving random numbers and calling the primitives. 基线是一个循环,需要~0.1 ms(10,000 FPS)检索随机数并调用基元。

Basic code for PyGtk2: PyGtk2的基本代码:

from gtk import gdk
import random

WIDTH = 256
def r255(): return int(256.0*random.random())

cmap = gdk.Colormap(gdk.visual_get_best_with_depth(24), True)
black = cmap.alloc_color('black')
white = cmap.alloc_color('white')
pixmap = gdk.Pixmap(None, WIDTH, WIDTH, 24)
pixmap.set_colormap(cmap)
gc = pixmap.new_gc(black, line_width=2)
pixmap.draw_rectangle(gc, True, -1, -1, WIDTH+2, WIDTH+2);
gc.set_foreground(white)
for n in range(99):
    pixmap.draw_line(gc, r255(), r255(), r255(), r255())

gdk.Pixbuf(gdk.COLORSPACE_RGB, False, 8, WIDTH, WIDTH
    ).get_from_drawable(pixmap, cmap, 0,0, 0,0, WIDTH, WIDTH
        ).save('Gdk2-lines.png','png')

And here is for PyQt5: 这是PyQt5:

from PyQt5.QtCore import Qt
from PyQt5.QtGui import *
import random

WIDTH = 256.0
def r255(): return WIDTH*random.random()

image = QImage(WIDTH, WIDTH, QImage.Format_RGB16)
painter = QPainter()
image.fill(Qt.black)
painter.begin(image)
painter.setPen(QPen(Qt.white, 2))
#painter.setRenderHint(QPainter.Antialiasing)
for n in range(99):
    painter.drawLine(WIDTH*r0to1(),WIDTH*r0to1(),WIDTH*r0to1(),WIDTH*r0to1())    
painter.end()
image.save('Qt5-lines.png', 'png')

And here is Python3-Cairo for completeness: 这里是Python3-Cairo的完整性:

import cairo
from random import random as r0to1

WIDTH, HEIGHT = 256, 256

surface = cairo.ImageSurface(cairo.FORMAT_A8, WIDTH, HEIGHT)
ctx = cairo.Context(surface)
ctx.scale(WIDTH, HEIGHT)  # Normalizing the canvas
ctx.set_line_width(0.01)
ctx.set_source_rgb(1.0, 1.0, 1.0)
ctx.set_antialias(cairo.ANTIALIAS_NONE)
#ctx.set_antialias(cairo.ANTIALIAS_FAST)

ctx.set_operator(cairo.OPERATOR_CLEAR)
ctx.paint()
ctx.set_operator(cairo.OPERATOR_SOURCE)
for n in range(99):
    ctx.move_to(r0to1(), r0to1())
    ctx.line_to(r0to1(), r0to1())
    ctx.stroke()

surface.write_to_png('Cairo-lines.png')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM