我有一个PyQt4 GUI,我需要将一堆Numpy数组保存为* .tif图像。这似乎需要花费大量时间,导致我的GUI无法响应多达几分钟,具体取决于堆栈中的图像数量。
大部分处理发生在图像的循环中:
for i in range(0, np.shape(dataStack)[2]):
print('Saving slice ' + str(i + 1))
#Save tumor stack
im1 = Image.fromarray(tumorStack[:,:,i]*255)
im1.save(saveLocationStr + 'Slice' + str(i+1) + '.tif')
#Set up correct number of subplots for review plot.
if T0 is not None:
plt.subplot(141)
else:
plt.subplot(131)
#Plot current brain slice in 1st position
plt.imshow(dataStack[:,:,i], cmap=mpl.cm.bone)
plt.axis('off')
plt.title(patient + '\n' + date + '\n' + 'Slice ' + str(i+1) + ' of ' + str(int(np.shape(dataStack)[2])))
#Select correct next subplot
if T0 is not None:
plt.subplot(142)
else:
plt.subplot(132)
#Get a masked copy of the tumorStack
tempTumorStack = copy.deepcopy(tumorStack[:,:,i])
tempTumorStack = np.ma.masked_where(tempTumorStack == 0, tempTumorStack)
#Plot masked tumor stack over brain data
plt.imshow(dataStack[:,:,i], cmap=mpl.cm.bone)
plt.imshow(tempTumorStack, cmap=mpl.cm.jet_r, interpolation='nearest')
plt.axis('off')
plt.title(modality + ' Region')
#Get the auto-zoomed region and plot it
(x, y) = tumorStack[:,:,i].nonzero()
if( int(np.shape(x)[0]) == 0 or int(np.shape(y)[0]) == 0):
if T0 is not None:
plt.subplot(143)
plt.imshow(T0[:,:,i], cmap=mpl.cm.bone)
plt.axis('off')
plt.title('T0')
#Plot autozoomed with perimiter over brain data
plt.subplot(144)
plt.imshow(np.zeros(np.shape(dataStack[:,:,i])), cmap=mpl.cm.bone)
plt.title('Perimiter of \n' + modality + ' + T0 for SA')
plt.axis('off')
else:
plt.subplot(133)
plt.imshow(np.zeros(np.shape(dataStack[:,:,i])), cmap=mpl.cm.bone)
plt.title('Perimiter of \n' + modality + ' for SA')
plt.axis('off')
else:
minX = np.min(x)
minY = np.min(y)
maxX = np.max(x)
maxY = np.max(y)
zoomedXmin = minX - (minX * .10)
zoomedXmax = (maxX * .10) + maxX
zoomedYmin = minY - (minY * .10)
zoomedYmax = (maxY * .10) + maxY
widthOf = zoomedXmax - zoomedXmin
heigthOf = zoomedYmax - zoomedYmin
#Get perimiter of tumor for autozoomed region
#Can do n=8 if we want
#tempTumorStack = bwperim(tempTumorStack,n=8)
tempTumorStack = mahotas.labeled.borders(tempTumorStack)
tempTumorStack = np.where(tempTumorStack == np.max(tempTumorStack), 1, np.nan)
#Plot T0 then auto-zoomed if user wanted T0
if T0 is not None:
plt.subplot(143)
plt.imshow(T0[:,:,i], cmap=mpl.cm.bone)
plt.axis('off')
plt.title('T0')
#Plot autozoomed with perimiter over brain data
plt.subplot(144)
plt.imshow(dataStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax), i ], cmap=mpl.cm.bone)
plt.imshow(tempTumorStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax) ], cmap=mpl.cm.jet_r)
#plt.xlim(minX, maxX-minX)
#plt.ylim(minY, maxY-minY)
plt.title('Perimiter of \n' + modality + ' + T0 for SA')
plt.axis('off')
#Just plot autozoomed
else:
plt.subplot(133)
plt.imshow(dataStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax), i ], cmap=mpl.cm.bone)
plt.imshow(tempTumorStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax) ], cmap=mpl.cm.jet_r)
plt.title('Perimiter of \n' + modality + ' for SA')
plt.axis('off')
#Finish setting up plot to specs, render, and save it
plt.subplots_adjust(wspace=.5)
plt.axis('off')
plt.tick_params(bottom='off', top='off', left='off', right='off')
plt.draw()
plt.savefig(saveLocationStr + 'MRI_Comparison\\Slice' + str(i+1) + '.png', dpi=200)
我想我会将昂贵的工作外包给PyQt4工作者QThread,所以按照http://diotavelli.net/PyQtWiki/Threading,_Signals_and_Slots的例子我创建了一个继承自QtCore.QThread的工作类,在此方法的run方法中调用了上述所有代码class,并创建并启动()编辑此类的实例,其中* .tif堆栈保存需要在主窗口中进行。
GUI仍像以前一样没有响应,但线程似乎成功执行了代码。我担心上面代码中使用的某个包可能没有正确地释放GIL(这里引用http://www.riverbankcomputing.com/pipermail/pyqt/2011-August/030470.html)。有没有办法手动确保这有效?
我已经注释掉并用time.sleep延迟替换了* .tif堆栈保存代码,并且GUI仍然没有响应,所以也许我正在实现QThread错误?
我错过了别的什么吗?我提供了足够的信息吗?
编辑:
我在主窗口中创建线程:
# create instance of saveImageStackWorker(QThread) thread class
self.saveThread = brain_io.saveImageStackWorker()
self.saveThread.populate(self.tumorBrain.pixData, self.tumorBrain.tumor, saveLocationStr, self.measurer, self.tumorBrain.patient, dateForPlots, imageModality, T0pass)
这是我定义的整个工作线程类,其中函数saveImageStack(args)与上面提供的* .tif保存代码相同:
class saveImageStackWorker(QThread):
"""
"""
def __init__(self, parent=None):
QThread.__init__(self, parent)
def __del__(self):
self.wait()
def populate(self, dataStack, tumorStack, saveLocationStr, measurer, patient, date, modality, T0):
self.dataStack = dataStack
self.tumorStack = tumorStack
self.saveLocationStr = saveLocationStr
self.measurer = measurer
self.patient = patient
self.date = date
self.modality = modality
self.T0 = T0
self.start()
def run(self):
self.saveImageStack(self.dataStack, self.tumorStack, self.saveLocationStr, self.measurer, self.patient, self.date, self.modality, self.T0)
def saveImageStack(self, dataStack, tumorStack, saveLocationStr, measurer, patient, date, modality, T0): #, dateStr, measurer,, saveLocationStr):
"""
Input:
dataStack:
numpy array of the brain image data.
tumorStack:
numpy binary array of tumor data.
modality:
the modality of the image.
T0:
numpy binary array of T0, if you do not
wish to show T0 (i.e. for flair or something)
leave as default None.
Output:
None
Description:
Saves the image stack of tumor and the review plots
to the output directory.
"""
print('Saving image stack from within worker thread...')
font = {'size' : 10}
matplotlib.rc('font', **font)
np.seterr(all='ignore')
warnings.simplefilter('ignore')
for i in range(0, np.shape(dataStack)[2]):
print('Saving slice ' + str(i + 1))
#Save tumor stack
im1 = Image.fromarray(tumorStack[:,:,i]*255)
im1.save(saveLocationStr + 'Slice' + str(i+1) + '.tif')
#Set up correct number of subplots for review plot.
if T0 is not None:
plt.subplot(141)
else:
plt.subplot(131)
#Plot current brain slice in 1st position
plt.imshow(dataStack[:,:,i], cmap=mpl.cm.bone)
plt.axis('off')
plt.title(patient + '\n' + date + '\n' + 'Slice ' + str(i+1) + ' of ' + str(int(np.shape(dataStack)[2])))
#Select correct next subplot
if T0 is not None:
plt.subplot(142)
else:
plt.subplot(132)
#Get a masked copy of the tumorStack
tempTumorStack = copy.deepcopy(tumorStack[:,:,i])
tempTumorStack = np.ma.masked_where(tempTumorStack == 0, tempTumorStack)
#Plot masked tumor stack over brain data
plt.imshow(dataStack[:,:,i], cmap=mpl.cm.bone)
plt.imshow(tempTumorStack, cmap=mpl.cm.jet_r, interpolation='nearest')
plt.axis('off')
plt.title(modality + ' Region')
#Get the auto-zoomed region and plot it
(x, y) = tumorStack[:,:,i].nonzero()
if( int(np.shape(x)[0]) == 0 or int(np.shape(y)[0]) == 0):
if T0 is not None:
plt.subplot(143)
plt.imshow(T0[:,:,i], cmap=mpl.cm.bone)
plt.axis('off')
plt.title('T0')
#Plot autozoomed with perimiter over brain data
plt.subplot(144)
plt.imshow(np.zeros(np.shape(dataStack[:,:,i])), cmap=mpl.cm.bone)
plt.title('Perimiter of \n' + modality + ' + T0 for SA')
plt.axis('off')
else:
plt.subplot(133)
plt.imshow(np.zeros(np.shape(dataStack[:,:,i])), cmap=mpl.cm.bone)
plt.title('Perimiter of \n' + modality + ' for SA')
plt.axis('off')
else:
minX = np.min(x)
minY = np.min(y)
maxX = np.max(x)
maxY = np.max(y)
zoomedXmin = minX - (minX * .10)
zoomedXmax = (maxX * .10) + maxX
zoomedYmin = minY - (minY * .10)
zoomedYmax = (maxY * .10) + maxY
widthOf = zoomedXmax - zoomedXmin
heigthOf = zoomedYmax - zoomedYmin
#Get perimiter of tumor for autozoomed region
#Can do n=8 if we want
#tempTumorStack = bwperim(tempTumorStack,n=8)
tempTumorStack = mahotas.labeled.borders(tempTumorStack)
tempTumorStack = np.where(tempTumorStack == np.max(tempTumorStack), 1, np.nan)
#Plot T0 then auto-zoomed if user wanted T0
if T0 is not None:
plt.subplot(143)
plt.imshow(T0[:,:,i], cmap=mpl.cm.bone)
plt.axis('off')
plt.title('T0')
#Plot autozoomed with perimiter over brain data
plt.subplot(144)
plt.imshow(dataStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax), i ], cmap=mpl.cm.bone)
plt.imshow(tempTumorStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax) ], cmap=mpl.cm.jet_r)
#plt.xlim(minX, maxX-minX)
#plt.ylim(minY, maxY-minY)
plt.title('Perimiter of \n' + modality + ' + T0 for SA')
plt.axis('off')
#Just plot autozoomed
else:
plt.subplot(133)
plt.imshow(dataStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax), i ], cmap=mpl.cm.bone)
plt.imshow(tempTumorStack[int(zoomedXmin):int(zoomedXmax), int(zoomedYmin):int(zoomedYmax) ], cmap=mpl.cm.jet_r)
plt.title('Perimiter of \n' + modality + ' for SA')
plt.axis('off')
#Finish setting up plot to specs, render, and save it
plt.subplots_adjust(wspace=.5)
plt.axis('off')
plt.tick_params(bottom='off', top='off', left='off', right='off')
plt.draw()
plt.savefig(saveLocationStr + 'MRI_Comparison\\Slice' + str(i+1) + '.png', dpi=200)
编辑#2: 在启动线程的主窗口中添加方法的代码 -
def uploadAndSave(self):
[db, unused] = initGUI.getDbDataBetter()
self.db = db
subtypeID = str(self.dbImage.image_subtype_id.toString())
query = QtSql.QSqlQuery("SELECT subtype_name FROM image_subtypes where "
+ "id = " + subtypeID, self.db)
query.next()
imageModality = str(query.value(0).toString())
dateForPlots = str(self.dbImage.date_of_image.toString()).replace('T',' ')
date = dateForPlots.replace('-','').replace(':','.')
basePath = 'S:\Lab_KSwanson\MRI Project\Test measurements\\' + self.tumorBrain.patient + '\\' + self.measurer + '\\' + date + '\\'
print('Saving images...')
seriesDescription = str(self.dbImage.series_description.toString())
saveLocationStr = brain_io.createOutputFilepath(basePath, seriesDescription, imageModality)
if imageModality.upper().find('T1') < 0:
T0pass = None
else:
T0pass = self.tumorBrain.T0
operation_time = datetime.datetime.now().isoformat().replace('T',' ')[0:19]
# create instance of saveImageStackWorker(QThread) thread class
self.saveThread = brain_io.saveImageStackWorker()
self.saveThread.populate(self.tumorBrain.pixData, self.tumorBrain.tumor, saveLocationStr, self.measurer, self.tumorBrain.patient, dateForPlots, imageModality, T0pass)
# brain_io.saveImageStack(self.tumorBrain.pixData, self.tumorBrain.tumor, saveLocationStr, self.measurer, self.tumorBrain.patient, dateForPlots, imageModality, T0pass)
self.tumorBrain.save(saveLocationStr + date + '.dat')
[db, unused] = initGUI.getDbDataBetter()
query = QtSql.QSqlQuery('SELECT file_path FROM measurements m WHERE m.user_id = ' + self.userIDStr + ' AND m.image_id = '
+ str(self.dbImage.id.toString()) + ' AND m.status = "R"', db)
#If there was a rejected measurement, this will return True
remeasure = query.next()
print('Computing volume, surface area, etc...')
T1 = algorithms.vtk_stats(self.tumorBrain.tumor,
spacing=(self.tumorBrain.xSpacing,
self.tumorBrain.ySpacing,
np.mean(self.tumorBrain.SliceThickness)))
T0 = algorithms.vtk_stats(self.tumorBrain.T0,
spacing=(self.tumorBrain.xSpacing,
self.tumorBrain.ySpacing,
np.mean(self.tumorBrain.SliceThickness)))
mass = tvtk.MassProperties(input=T1.output)
T0mass = tvtk.MassProperties(input=T0.output)
#mySA = algorithms.calculateSurfaceArea(self.tumorBrain.tumor,
# self.tumorBrain.xSpacing,
# self.tumorBrain.ySpacing,
# self.tumorBrain.SliceThickness)
#mySAT0 = algorithms.calculateSurfaceArea(self.tumorBrain.T0,
# self.tumorBrain.xSpacing,
# self.tumorBrain.ySpacing,
# self.tumorBrain.SliceThickness)
#print('mysa = ' + str(mySA))
#area = 0
#for i in range(0, int(self.tumorBrain.tumor.shape[2])):
# tumor_filt = self.tumorBrain.tumor[:,:,i]
# currThreshold = self.thresholdList[i]
# tumor_filt = np.where(tumor_filt > currThreshold, 1, 0)
# area = area + np.sum(np.sum(tumor_filt))
#myVolumeT1 = np.sum(self.tumorBrain.xSpacing**2 * area * self.tumorBrain.SliceThickness)
myVolumeT1 = mass.volume
#T0sum = np.sum(np.sum(np.sum(self.tumorBrain.T0)))
#myVolumeT0 = np.sum(self.tumorBrain.xSpacing**2 * T0sum * self.tumorBrain.SliceThickness)
myVolumeT0 = T0mass.volume
myVolume_T0_T1 = myVolumeT1 + myVolumeT0
T0_radius = ((3.0*(myVolumeT0))/(4.0*math.pi))**(1.0/3.0)
T0_T1_radius = ((3.0*(myVolume_T0_T1))/(4.0*math.pi))**(1.0/3.0)
#print('volume vtk = ' + str(mass.volume))
#print('my volume = ' + str(myVolume_T0_T1))
#print('my radius = ' + str(T0_T1_radius))
if mass.volume + T0mass.volume == 0 or mass.surface_area == 0:
circularity = 0
else:
circularity = ((math.pi)**(1.0/3.0))*((6.0*(myVolume_T0_T1))**(2.0/3.0) / mass.surface_area)
print('SA = ' + str(mass.surface_area))
print('T0 SA = ' + str(T0mass.surface_area))
print('Volume = ' + str(myVolume_T0_T1))
print('T0 Volume = ' + str(myVolumeT0))
print('Radius = ' + str(T0_T1_radius))
print('T0 Radius = ' + str(T0_radius))
print('Circularity = ' + str(circularity))
# Ask to see rendering
msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Question, QtCore.QString('Render'), QtCore.QString('Show Tumor Rendering?'), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
ret = msgBox.exec_()
if ret == QtGui.QMessageBox.Yes:
algorithms.render_surface(T1, T0)
query = QtSql.QSqlQuery('select max(group_id) from ' +
'measurement_audit_log where measurement_type_id = '
+ "'1'", db)
query.next()
group_id = str(query.value(0).toInt()[0] + 1)
# Check for a measurement assignment.
osUserName = os.environ.get("USERNAME").lower()
query = QtSql.QSqlQuery('SELECT id from users where pathology_user_name = "' + osUserName + '"')
query.next()
user_id = str(query.value(0).toInt()[0])
query = QtSql.QSqlQuery('select id from ' +
'measurement_assignments_audit ' +
'where image_id = ' + str(self.dbImage.id.toString()) + ' ' +
'and measurer_id = ' + user_id + ' ' +
'and status = "P"')
assignment = query.next()
date_of_completion = operation_time[0:10]
if not assignment:
# Create a new assignment
newAssignmentQuery = ('insert into measurement_assignments_audit ' +
'(measurement_assignment_id, assigner_id, measurer_id, image_id, patient_id, date_of_assignment, ' +
'comments, date_of_completion, priority, measurement_group_id, operation_user_id, operation_code, ' +
'operation_time, status) values (0, ' + user_id + ', ' + user_id + ', ' + str(self.dbImage.id.toString()) + ', ' +
str(self.dbImage.patient_id.toString()) + ', "' + date_of_completion + '", ' + '"Self-assigned through brainsegment", "' +
date_of_completion + '", 2, ' + group_id + ', ' + user_id + ', "I", "' + operation_time + '", "P")')
query = QtSql.QSqlQuery(newAssignmentQuery)
else:
# Update the assignment
updateAssignmentQuery = ('update measurement_assignments_audit set date_of_completion = "' + date_of_completion + '", ' +
'measurement_group_id = ' + group_id + ' where id = ' + str(query.value(0).toInt()[0]))
query = QtSql.QSqlQuery(updateAssignmentQuery)
brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 1, 1, T0_T1_radius, saveLocationStr + 'MRI_Comparison', operation_time, str(self.dbImage.id.toString()), group_id)
brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 2, 2, myVolume_T0_T1, saveLocationStr + 'MRI_Comparison', operation_time, str(self.dbImage.id.toString()), group_id)
brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 7, 3, mass.surface_area, saveLocationStr + 'MRI_Comparison', operation_time, str(self.dbImage.id.toString()),group_id)
brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 11, 11, circularity, saveLocationStr + 'MRI_Comparison', operation_time, str(self.dbImage.id.toString()),group_id)
if T0pass is not None:
query = QtSql.QSqlQuery('SELECT image_file_path from images i where i.id = ' + str(self.dbImage.id.toString()), db)
query.next()
#print('SELECT i.image_file_path from images i where i.id = ' + str(self.dbImage.id.toString()))
image_file_path = str(query.value(0).toString()).replace('\\','\\\\')
query = QtSql.QSqlQuery('SELECT id from images i where i.image_file_path = "' + image_file_path + '" and i.image_subtype_id in (14, 15, 20, 21) ', db)
#print('SELECT id from images i where i.image_file_path = "' + image_file_path + '" and i.image_subtype_id in (14, 15, 20, 21) ')
query.next()
T0idStr = str(query.value(0).toString())
T0radius = ((3.0*T0mass.volume)/(4.0*math.pi))**(1.0/3.0)
brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 1, 1, T0_radius, saveLocationStr + 'MRI_Comparison', operation_time, T0idStr, group_id)
brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 2, 2, myVolumeT0, saveLocationStr + 'MRI_Comparison', operation_time, T0idStr, group_id)
brain_io.uploadMeasurement(self.dbImage, self.db, self.version, self.tumorBrain.patient, remeasure, 7, 3, T0mass.surface_area, saveLocationStr + 'MRI_Comparison', operation_time, T0idStr, group_id)
答案 0 :(得分:4)
请务必使用matplotlib.use('Agg')在后端生成图像 在pyplot之前调用matplotlib.use()有一些排序要求。
http://matplotlib.org/faq/howto_faq.html
在不显示窗口的情况下生成图像 最简单的方法是使用非交互式后端(请参阅什么是后端?),例如Agg(用于PNG),PDF,SVG或PS。在你的图形生成脚本中,只需在导入pylab或pyplot之前调用matplotlib.use()指令:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
plt.plot([1,2,3])
plt.savefig('myfig')
答案 1 :(得分:1)
如果处理图片需要很长时间,则应使用multiprocessing.Pool
。
为此,您应该为每个图像创建一个包含数据的列表(或其他可迭代的)。你应该根据循环中包含的处理代码创建一个函数。
接下来,您使用池的map_async
方法将该函数应用于所有图像。这将在不同的进程中执行,因此它不应该打扰GUI。作为奖励,它将使用您机器中的所有CPU核心来完成工作。