Python Matplotlib:使用不同颜色打印多条断开连接的线段

Python Matplotlib:使用不同颜色打印多条断开连接的线段,python,matplotlib,plot,line,Python,Matplotlib,Plot,Line,我有一组数据记录,如下所示: (s1, t1), (u1, v1), color1 (s2, t2), (u2, v2), color2 . . . (sN, tN), (uN, vN), colorN 在任何记录中,前两个值是线段的端点,第三个值是该线段的颜色。更具体地说,(sn,tn)是第一个端点的x-y坐标,(un,vn)是第二个端点的x-y坐标。另外,color是带有alpha值的rgb 通常,任何两条线段都是断开的(即它们的端点不一定重合) 如何使用matplotlib和单个plot

我有一组数据记录,如下所示:

(s1, t1), (u1, v1), color1
(s2, t2), (u2, v2), color2
.
.
.
(sN, tN), (uN, vN), colorN
在任何记录中,前两个值是线段的端点,第三个值是该线段的颜色。更具体地说,
(sn,tn)
是第一个端点的x-y坐标,
(un,vn)
是第二个端点的x-y坐标。另外,color是带有alpha值的rgb

通常,任何两条线段都是断开的(即它们的端点不一定重合)

如何使用matplotlib和单个
plot
调用(或尽可能少的调用)绘制此数据,因为可能有数千条记录

尝试 在一个大列表中准备数据,并对其调用
plot
,速度太慢了。例如,以下代码无法在合理的时间内完成:

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()
我能够通过使用无插入技巧加快绘图渲染速度,如下所示:

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)

这在我的机器上执行不到一秒钟。我仍然需要弄清楚如何嵌入颜色值(带有alpha通道的RGB)

函数
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>]
In[315]:data=[(1,1),(2,3),'r',#假设点实际上是(1,2)(1,3)和,
#这里它们的形式是(x1,x2),(y1,y2)
…:(2,2),(4,5),‘g’,
…:(5,5),(6,7),‘b’,]
在[316]中:绘图(*数据)
Out[316]:
[,
,
]

好的,在将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()
这是迄今为止最快的解决方案,完全符合我的实时需求。但需要注意的是,绘制的线条没有抗锯齿。

使用:

以下是输出:


我尝试了Python3上可用的一些2D渲染引擎,同时在面向图像的深度学习&GAN中寻找输出阶段的快速解决方案

使用以下基准:使用或不使用反别名将99行渲染为256x256屏幕外图像(或更有效的图像)的时间

按照我老式x301笔记本电脑的效率顺序,结果如下:

  • PyGtk2:~2500 FPS,(Python 2,GTK 2,不确定如何获得AA)
  • PyQt5:~1200 FPS,~350带抗锯齿
  • PyQt4:~1100 FPS,~380带AA
  • 开罗:~750 FPS,使用AA时约250 FPS(使用“快速”AA时仅稍微快一点)
  • PIL:~600 FPS
基线是一个循环,检索随机数并调用原语需要约0.1 ms(10000 FPS)

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')
以下是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')
为了完整起见,这里是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')

@用户698585你试过了吗?看我贴的照片。我假设你的
(s1,t1),(u1,v1)
(x1,x2),(y1,y2)
,否则你应该把
(x1,y1),(x2,y2)
压缩到
(x1,x2),(y1,y2)
对不起,问题不够清楚,请检查更新。无论如何,我明白你的意思。您可能希望更新您的答案,使其与问题中明确说明的内容保持一致。我已经在我的实际数据集上进行了尝试。这仍然太慢,如果您有60000个(实际记录数)段,那么您正在调用一个具有~60000个参数的函数!!inefficient@user698585这就是你所要求的(通过一次绘图调用)。。。没有人说在一个数字上画60000条线是有效的,无论是一次调用还是for循环中的调用。实际上循环会比你做的更快,这是我的第一次尝试。我试过了,它仍然不够快,不能画60000条线,对吗?@张Xaochen:当然!事实上,我想给这个问题添加一个类似的更新,但我决定不这么做,完全是因为懒惰。难以置信的回答!明亮的回答得很好,这是我发现的唯一一个使用matplotlib可以有效处理大量行的解决方案。对于大约2000行,它是60毫秒,而不是其他技术的1.6秒。@zhangxaochen也许硬件速度更快了,但现在是。大约2秒钟内62k行。使用
nan
而不是
None
是否有任何区别,它会产生相同的绘图,但我可以使用
numpy.tile
numpy。重复
来构建
x
y
,而不是附加到列表中。你是否也知道你是否可以用这个方法嵌入颜色(不像
linecollection
方法)?出于懒惰,我建议
rgba=(np.random.randint(256)表示范围内(4))
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')