web-dev-qa-db-fra.com

Matplotlib: traçage de nombreux segments de ligne déconnectés de différentes couleurs

J'ai un ensemble d'enregistrements de données comme celui-ci:

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

Dans tout enregistrement, les deux premières valeurs sont les points d'extrémité d'un segment de ligne, la troisième valeur est le couleur de ce segment de ligne. Plus précisement, (sn, tn) sont les coordonnées x-y du premier point final, (un, vn) sont les coordonnées x-y du deuxième point de terminaison. De plus, couleur est un rgb avec une valeur alpha.

En général, deux segments de ligne quelconques sont déconnectés (ce qui signifie que leurs extrémités ne coïncident pas nécessairement).

Comment tracer ces données en utilisant matplotlib avec un seul appel plot (ou aussi peu que possible) car il pourrait y avoir potentiellement des milliers d'enregistrements.

Tentatives

Préparer les données dans une grande liste et appeler plot par contre est beaucoup trop lent. Par exemple, le code suivant n'a pas pu se terminer dans un délai raisonnable:

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()

J'ai pu accélérer le rendu du tracé en utilisant l'astuce d'insertion None comme suit:

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)

Cela s'exécute en moins d'une seconde sur ma machine. Je dois encore comprendre comment intégrer les valeurs de couleur (RVB avec canal alpha).

43
Rabih Kodeih

OK, j'ai fini par pixelliser les lignes d'une image PIL avant de la convertir en un tableau 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()

C'est de loin la solution la plus rapide et elle correspond parfaitement à mes besoins en temps réel. Une mise en garde est cependant que les lignes sont tracées sans anti-aliasing.

6
Rabih Kodeih

utilisez 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)

voici la sortie:

enter image description here

78
HYRY

la fonction plot permet de tracer plusieurs lignes en un seul appel, si vos données sont juste dans une liste, décompressez-les simplement en les passant à 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>]

enter image description here

9
zhangxaochen

J'ai essayé quelques bons moteurs de rendu 2D disponibles sur Python 3, tout en recherchant une solution rapide pour une étape de sortie en Deep Learning et GAN orienté image.

En utilisant le benchmark suivant: Temps pour rendre 99 lignes dans une image hors écran 256x256 (ou quoi que ce soit plus efficace) avec et sans anti-alias.

Les résultats, par ordre d'efficacité sur mon vieux portable x301:

  • PyGtk2: ~ 2500 FPS, (Python 2, GTK 2, je ne sais pas comment obtenir AA)
  • PyQt5: ~ 1200 FPS, ~ 350 avec Antialias
  • PyQt4: ~ 1100 FPS, ~ 380 avec AA
  • Le Caire: ~ 750 FPS, ~ 250 avec AA (seulement légèrement plus rapide avec 'FAST' AA)
  • PIL: ~ 600 FPS

La ligne de base est une boucle qui prend ~ 0,1 ms (10 000 FPS) pour récupérer des nombres aléatoires et appeler les primitives.

Code de base pour 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')

Et voici pour 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')

Et voici Python3-Cairo pour l'exhaustivité:

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')
1
gatopeich