2010-11-19 9 views
6

Tôi muốn tạo một hoạt ảnh với matplotlib để giám sát sự hội tụ của một thuật toán phân cụm. Nó sẽ vẽ một phân tán dữ liệu của tôi khi được gọi là lần đầu tiên và vẽ các dấu ba chấm lỗi mỗi khi ô được cập nhật. Tôi đang cố gắng sử dụng canvas_copy_from_bbox()restore_region() để lưu phân tán và sau đó vẽ một tập hợp các dấu ba chấm mới bất cứ khi nào tôi cập nhật cốt truyện. Tuy nhiên, mã chỉ vẽ các hình elip mới lên trên các hình elip cũ, mà không xóa ô trước đó trước. Tôi nghi ngờ, bằng cách nào đó cách tiếp cận này không hoạt động tốt với các lệnh Ellipse()add_path(), nhưng tôi không biết cách khắc phục điều này.Xóa nền trong matplotlib bằng cách sử dụng wxPython

Đây là mã:

import wx 
import math 
from math import pi 
from matplotlib.patches import Ellipse 
from matplotlib.figure import Figure 
from matplotlib.backends.backend_wxagg import \ 
    FigureCanvasWxAgg as FigureCanvas 

TIMER_ID = wx.NewId() 


class _MonitorPlot(wx.Frame): 
    def __init__(self, data, scale=1): 
     self.scale = scale 
     wx.Frame.__init__(self, None, wx.ID_ANY, 
          title="FlowVB Progress Monitor", size=(800, 600)) 
     self.fig = Figure((8, 6), 100) 
     self.canvas = FigureCanvas(self, wx.ID_ANY, self.fig) 
     self.ax = self.fig.add_subplot(111) 

     x_lims = [data[:, 0].min(), data[:, 0].max()] 
     y_lims = [data[:, 1].min(), data[:, 1].max()] 

     self.ax.set_xlim(x_lims) 
     self.ax.set_ylim(y_lims) 
     self.ax.set_autoscale_on(False) 

     self.l_data = self.ax.plot(data[:, 0], data[:, 1], color='blue', 
           linestyle='', marker='o') 

     self.canvas.draw() 
     self.bg = self.canvas.copy_from_bbox(self.ax.bbox) 

     self.Bind(wx.EVT_IDLE, self._onIdle) 

    def update_plot(self, pos, cov): 
     self.canvas.restore_region(self.bg) 

     for k in range(pos.shape[0]): 
      l_center, = self.ax.plot(pos[k, 0], pos[k, 1], 
            color='red', marker='+') 

      U, s, Vh = np.linalg.svd(cov[k, :, :]) 
      orient = math.atan2(U[1, 0], U[0, 0]) * 180/pi 
      ellipsePlot = Ellipse(xy=pos[k, :], width=2.0 * math.sqrt(s[0]), 
            height=2.0 * math.sqrt(s[1]), 
            angle=orient, facecolor='none', 
            edgecolor='red') 
      self.ax.add_patch(ellipsePlot) 

     self.canvas.draw() 
     self.canvas.blit(self.ax.bbox) 

Trả lời

5

gì đang xảy ra là bạn đang thêm các bản vá lỗi mới để cốt truyện mỗi lần, và sau đó vẽ tất cả trong số họ khi bạn gọi self.canvas.draw().

Việc sửa chữa nhanh nhất là chỉ cần gọi self.canvas.draw_artist(ellipsePlot) sau khi thêm mỗi miếng vá và loại bỏ các cuộc gọi đến self.canvas.draw()

Là một đơn giản, độc lập Ví dụ:

# Animates 3 ellipses overlain on a scatterplot 
import matplotlib.pyplot as plt 
from matplotlib.patches import Ellipse 
import numpy as np 

num = 10 
x = np.random.random(num) 
y = np.random.random(num) 

plt.ion() 
fig = plt.figure() 
ax = fig.add_subplot(111) 
line = ax.plot(x, y, 'bo') 

fig.canvas.draw() 
bg = fig.canvas.copy_from_bbox(ax.bbox) 

# Pseudo-main loop 
for i in range(100): 
    fig.canvas.restore_region(bg) 

    # Make a new ellipse each time... (inefficient!) 
    for i in range(3): 
     width, height, angle = np.random.random(3) 
     angle *= 180 
     ellip = Ellipse(xy=(0.5, 0.5), width=width, height=height, 
       facecolor='red', angle=angle, alpha=0.5) 
     ax.add_patch(ellip) 
     ax.draw_artist(ellip) 

    fig.canvas.blit(ax.bbox) 

Tuy nhiên, điều này có lẽ sẽ gây tiêu hao bộ nhớ các vấn đề theo thời gian, vì đối tượng trục sẽ theo dõi tất cả các nghệ sĩ được thêm vào nó. Nếu trục của bạn không treo trong một thời gian dài, điều này có thể không đáng kể, nhưng ít nhất bạn nên biết rằng nó sẽ gây ra rò rỉ bộ nhớ. Một cách xung quanh điều này là để loại bỏ các nghệ sĩ ellipsis từ các trục bằng cách gọi ax.remove(ellipsePlot) cho mỗi hình elip sau khi vẽ chúng. Tuy nhiên, điều này vẫn còn hơi không hiệu quả, khi bạn liên tục tạo và phá hủy các nghệ sĩ hình elip, khi bạn chỉ có thể cập nhật chúng. (Tạo và phá hủy chúng không có nhiều chi phí, mặc dù, nó chủ yếu là một vấn đề phong cách ...)

Nếu số lượng dấu ba chấm ở cùng một thời gian thì tốt hơn và dễ dàng cập nhật các thuộc tính của mỗi đối tượng nghệ sĩ hình elip thay vì tạo và thêm đối tượng mới. Điều này sẽ tránh được loại bỏ hình elip "cũ" khỏi trục, vì chỉ số bạn cần sẽ tồn tại.

Là một độc lập, ví dụ đơn giản này:

# Animates 3 ellipses overlain on a scatterplot 
import matplotlib.pyplot as plt 
from matplotlib.patches import Ellipse 
import numpy as np 

num = 10 
x = np.random.random(num) 
y = np.random.random(num) 

plt.ion() 
fig = plt.figure() 
ax = fig.add_subplot(111) 
line = ax.plot(x, y, 'bo') 

fig.canvas.draw() 
bg = fig.canvas.copy_from_bbox(ax.bbox) 

# Make and add the ellipses the first time (won't ever be drawn) 
ellipses = [] 
for i in range(3): 
    ellip = Ellipse(xy=(0.5, 0.5), width=1, height=1, 
      facecolor='red', alpha=0.5) 
    ax.add_patch(ellip) 
    ellipses.append(ellip) 

# Pseudo-main loop 
for i in range(100): 
    fig.canvas.restore_region(bg) 

    # Update the ellipse artists... 
    for ellip in ellipses: 
     ellip.width, ellip.height, ellip.angle = np.random.random(3) 
     ellip.angle *= 180 
     ax.draw_artist(ellip) 

    fig.canvas.blit(ax.bbox) 
+1

Thay vì 'fig.canvas.blit (ax.bbox) 'người ta cũng có thể xem xét' fig.canvas.update()'. Theo [blog này] (http://bastibe.de/2013-05-30-speeding-up-matplotlib.html) nó cũng nhanh như nhau. Đối với tôi, nó giải quyết được vấn đề tràn bộ nhớ. –

+1

@LucM - Đối với những gì nó có giá trị, điều đó phụ thuộc rất nhiều vào chương trình phụ trợ. 'fig.canvas.blit (updated_region)' sẽ nhanh hơn và hoạt động hoàn hảo với 'TkAgg' và một số phần phụ trợ phổ biến khác. Tuy nhiên, trên các chương trình phụ trợ khác, 'fig.canvas.update()' (làm mới toàn bộ khung hình) nhanh hơn vì việc đổ một vùng phụ không được hỗ trợ đầy đủ. Bất kể, nó là tốt để biết về cả hai. –

+0

Điều đó sẽ thực sự hữu ích nếu tôi chuyển đổi phụ trợ. Tôi đã có một tràn bộ nhớ với blit khi sử dụng PyQt. –