2016-09-30 2 views
0

У меня есть программа с gui, реализованная с использованием PyQt4. Эта программа управляет профилировщиком луча, который возвращает мне четыре вектора из 7500 элементов. Первые два представляют собой векторы положения (X и Y), а два вторых являются векторами интенсивности для X и Y соответственно. То, что я хочу сделать, - создать сетку (XY) и рассчитать интенсивность как цвет на графике, как показано на следующем рисунке. Figure 1 - Side Gaussian function are my raw data and I want to plot like thisОптимизация цветного графика для 2D-сетки в pyqt

Я уже справился, как это сделать, но у меня проблемы с оптимизацией.

def emitGraph(self): 
    zz = zeros([len(self.slit_data_pos[self.slit][::100]), len(self.slit_data_pos[self.slit + 1][::100])]) 
    for i in xrange(len(self.slit_data_pos[self.slit][::100])): 
     for j in xrange(len(self.slit_data_pos[self.slit + 1][::100])): 
      zz[i,j] = self.mesh(i, j, self.slit_data_int[self.slit][::100], self.slit_data_int[self.slit + 1][::100]) 

    self.graph.emit(
     self.slit_data_pos[self.slit][::100], 
     self.slit_data_int[self.slit][::100], 
     self.slit_data_pos[self.slit + 1][::100], 
     self.slit_data_int[self.slit + 1][::100], 
     zz 
     ) 
    self.timerGraph = threading.Timer(0.2, self.emitGraph).start() 

def mesh(self, i, j, intx, inty): 
    norm_max = max(max(intx), max(inty)) 
    intx = [x/norm_max for x in intx] 
    inty = [y/norm_max for y in inty] 
    return intx[i]*inty[j] 

Как вы можете видеть в следующем коде у меня есть две петли, чтобы заполнить матрицу ZZ (первоначально 7500x7500). Теперь я использую только 75 точек для каждого вектора и отображаю его каждые 0,2 секунды с помощью таймера. Я должен был сделать это, потому что, если я использую все данные, потребуется возраст, чтобы заполнить матрицу zz, а затем построить.

Часть кода, где я сюжет это (в другом объекте/нить):

def graph_update(self, slit_samples_positionsX, slit_samples_intensitiesX, slit_samples_positionsY, slit_samples_intensitiesY, zz): 

    # self.matplotlibWidget.axis.plot(slit_samples_positionsX, slit_samples_intensitiesX) 
    # self.matplotlibWidget.axis.plot(slit_samples_positionsY, slit_samples_intensitiesY) 
    self.matplotlibWidget.axis.imshow(zz, cmap=cm.jet) 
    self.matplotlibWidget.canvas.draw() 

Мой вопрос, как я могу улучшить свои расчеты данных в целях повышения скорости разрешения и отображения моего графа? Или есть ли какой-нибудь другой способ быстрее создать такой сюжет?

Спасибо!

ответ

0

Вы создаете всю сетку каждые 0,2 секунды. Вы также создаете новый график каждые 0,2 секунды. Это замедляет ваше приложение. Создание нового таймера, каждый раз, когда вызывается emitGraph, может быть и проблема.

Чтобы улучшить вашу программу, идея состоит в том, чтобы создать сетку один раз и только обновить данные внутри. Также создайте сюжет только один раз, обновив его новыми данными. В принципе, numpy очень быстро работает в операциях массива, и печать данных на экране не должна быть проблемой. Глядя на ваш код, одна проблема может заключаться в том, что вы на самом деле много работаете со списками, а не с массивами numpy. Поэтому старайтесь избегать создания списка и for-loops. На самом деле, я думаю, вы можете полностью избавиться от функции mesh и тем самым сэкономить много времени.

Относительно участка, не назовите imshow() при каждом обновлении! Если, например, self.image ваш imshow-график, а затем просто обновить этот участок по телефону self.image.set_data(zz)

Если вы можете предоставить более подробную информацию о форме массивов, которые вы используете, и скажите нам, что self.slit, я, вероятно, может помочь вам Больше.

На этом этапе вы также можете посмотреть следующий код, который пытается имитировать профилировщик луча и работает очень быстро.

from PyQt4 import QtGui, QtCore 
import numpy as np 

from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas 
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar 
from matplotlib.figure import Figure 

import sys, time, random 


class WorkerObject(QtCore.QObject): 

    signalStatus = QtCore.pyqtSignal(object) 

    def __init__(self, parent=None): 
     super(self.__class__, self).__init__(parent) 

     self.data = np.zeros((600,800)) 
     self.x = np.arange(0,800) 
     self.y = np.arange(0,600) 
     self.Y, self.X = np.meshgrid(self.x, self.y) 
     self.f = lambda x, y, x0, y0, sigmax, sigmay : 0.96*np.exp(-((x-x0)/float(sigmax))**2)*np.exp(-((y-y0)/float(sigmay))**2) 


    @QtCore.pyqtSlot()   
    def startWork(self): 
     print "StartWork" 
     x0 = random.randint(250,550) 
     y0 = random.randint(250,350) 
     sigmax = random.randint(100,200) 
     sigmay = random.randint(100,200) 
     while 1 > 0: 
      x0, y0, sigmax, sigmay = self.doWork(x0, y0, sigmax, sigmay) 

    def doWork(self, x0, y0, sigmax, sigmay): 
     dx, dy, dsx, dsy = random.randint(0,1)*2-1, random.randint(0,1)*2-1, random.randint(0,1)*2-1, random.randint(0,1)*2-1 
     self.data[:,:] = (self.f(self.X, self.Y, x0+dx, y0+dy, sigmax+dsx, sigmay+dsy)+np.random.random((600,800))*0.04)*(1.+np.random.rand()*0.04-0.02) 
     self.signalStatus.emit(self.data) 
     time.sleep(0.04) 
     return x0+dx, y0+dy, sigmax+dsx, sigmay+dsy 



class App(QtGui.QMainWindow): 

    signalStatus = QtCore.pyqtSignal(object) 
    abortSignal = QtCore.pyqtSignal() 

    def __init__(self, parent=None): 
     super(App, self).__init__(parent) 

     self.button_start = QtGui.QPushButton('Start',self) 
     self.button_cancel = QtGui.QPushButton('Cancel', self) 
     self.label_status = QtGui.QLabel('', self) 

     self.mainbox = QtGui.QWidget(self) 
     self.layout = QtGui.QVBoxLayout() 
     self.mainbox.setLayout(self.layout) 
     self.setCentralWidget(self.mainbox) 
     self.layout.addWidget(self.button_start) 
     self.layout.addWidget(self.button_cancel) 
     self.layout.addWidget(self.label_status) 

     self.fig = Figure((6.0, 3.0), dpi=72) 
     self.canvas = FigureCanvas(self.fig) 
     self.canvas.setParent(self) 
     self.canvastoolbar = NavigationToolbar(self.canvas, self) 
     self.fig.patch.set_alpha(0.0) 
     self.ax = self.fig.add_subplot(111) 
     self.x = np.arange(0,800); self.y = np.arange(0,600) 
     self.im = self.ax.imshow(np.zeros((600, 800)), origin='upper', vmin=0, vmax=1) 
     self.pv, = self.ax.plot(np.zeros(600) ,self.y , color="white" , alpha=0.6, lw=2) 
     self.ph, = self.ax.plot(self.x ,np.zeros(800) , color="white" , alpha=0.6, lw=2) 
     self.ax.set_xlim([0,800]); self.ax.set_ylim([0,600]) 
     self.layout.addWidget(self.canvas) 
     self.layout.addWidget(self.canvastoolbar) 

     self.initWorker() 



    def initWorker(self): 
     self.worker = WorkerObject() 
     self.worker_thread = QtCore.QThread() 
     self._connectSignals() 
     self.worker.moveToThread(self.worker_thread) 
     self.worker_thread.start() 


    def _connectSignals(self): 
     self.button_start.clicked.connect(self.worker.startWork) 
     self.button_cancel.clicked.connect(self.forceWorkerQuit) 
     self.worker.signalStatus.connect(self.updateStatus) 


    def forceWorkerQuit(self): 
     print "calculation aborted" 
     if self.worker_thread.isRunning(): 
      self.worker_thread.terminate() 
     self.worker_thread.start() 


    @QtCore.pyqtSlot(object) 
    def updateStatus(self, obj): 
     self.im.set_data(obj) 
     argm = np.unravel_index(np.argmax(obj), (600,800)) 
     self.pv.set_data(obj[:,argm[0]]*250, self.y) 
     self.ph.set_data(self.x, obj[argm[1], :]*250) 
     self.fig.canvas.draw() 


if __name__=='__main__': 
    app = QtGui.QApplication(sys.argv) 
    thisapp = App() 
    thisapp.show() 
    sys.exit(app.exec_()) 

Редактировать (ответ на комментарий ниже, спрашивая, как рассчитать zz):

Если я правильно понимаю, ваш slit_data_int[0] является накопленная интенсивность в направлении X, slit_data_int[1] это один в Y направлении.Если zz[j,i] = slit_data_int[1][j]*slit_data_int[0][i] то это лучше вычисленная с помощью матричного умножения (np.outer) в NumPy:

zz[:,:] = np.outer(slit_data_int[1], slit_data_int[0])

+0

Большое спасибо! Он решил большинство моих проблем, однако у меня все еще есть некоторые проблемы с тем, как рассчитать данные zz. Профилятор луча возвращает данные с 4 щелей. Следовательно, это вектор, такой как np.array ([[slit1data], [slit2data], [slit3data], [slit4data]]). Slit 0 (ось X) и 1 (ось Y) - 25-миллиметровая щель, а 2 и 3 - 5-миллиметровые разрезы. Поэтому я просто использую self.slit для выбора той щели, которую я выбираю. Я не очень понимаю, как строить мои данные, потому что векторы положения не используются, так как я строю только матрицу интенсивности, – Eduardo

+0

Итак, что такое щели 2 и 3? Используются ли они вообще? Во всяком случае, перемещаются ли щели или как вы получите полную 2d информацию только из двух 1D-массивов? – ImportanceOfBeingErnest

+0

В моем Gui есть радиобутом, который меняет щели. Таким образом, его либо 0, либо 1 или 2 и 3. На самом деле это четыре 1D массива. Предположим, что это две пары, одна для оси X и другая для оси Y. Для первой пары мы имеем один массив позиции и один массив интенсивности, который показывает интенсивность для каждой точки в массиве позиций. То же самое касается Y. Итак, чтобы восстановить полные 2D-данные, нам нужно создать сетку, основанную на положениях X и Y, а затем создать матрицу для интенсивности, элементами которой являются произведения интенсивности проекции на ортогональную ось. Например: ZMatrix [i, j] = intx (i) * inty (j) – Eduardo

Смежные вопросы