1
+ import sys
2
+ import h5py
3
+ from pathlib import Path
4
+ import os .path
5
+ import pyqtgraph as pg
6
+ from pyqtgraph .Qt import QtCore , QtGui , QtWidgets #, QColorDialog
7
+ import numpy as np
8
+ import matplotlib .pyplot as plt
9
+ import pickle
10
+ import time
11
+ from lmfit .models import GaussianModel
12
+ import customplotting .mscope as cpm
13
+ # local modules
14
+
15
+ pg .mkQApp ()
16
+ pg .setConfigOption ('background' , 'w' )
17
+ pg .setConfigOption ('imageAxisOrder' , 'row-major' )
18
+
19
+ base_path = Path (__file__ ).parent
20
+ file_path = (base_path / "flim_plot_gui.ui" ).resolve ()
21
+
22
+ uiFile = file_path
23
+
24
+ WindowTemplate , TemplateBaseClass = pg .Qt .loadUiType (uiFile )
25
+
26
+ class MainWindow (TemplateBaseClass ):
27
+
28
+ def __init__ (self ):
29
+ super (TemplateBaseClass , self ).__init__ ()
30
+
31
+ # Create the main window
32
+ self .ui = WindowTemplate ()
33
+ self .ui .setupUi (self )
34
+
35
+ #set up ui signals
36
+ self .ui .load_scan_pushButton .clicked .connect (self .open_pkl_file )
37
+ self .ui .plot_intensity_sums_pushButton .clicked .connect (self .plot_intensity_sums )
38
+ self .ui .plot_raw_hist_data_pushButton .clicked .connect (self .plot_raw_scan )
39
+ self .ui .save_intensities_image_pushButton .clicked .connect (self .save_intensities_image )
40
+ self .ui .save_intensities_array_pushButton .clicked .connect (self .save_intensities_array )
41
+ self .ui .compare_checkBox .stateChanged .connect (self .switch_compare )
42
+ self .ui .intensity_sums_viewBox .roi .sigRegionChanged .connect (self .line_profile_update_plot )
43
+ self .ui .import_pkl_pushButton .clicked .connect (self .import_pkl_to_convert )
44
+ self .ui .pkl_to_h5_pushButton .clicked .connect (self .pkl_to_h5 )
45
+
46
+ self .show ()
47
+
48
+ def open_pkl_file (self ):
49
+ """ Open FLIM scan file """
50
+ try :
51
+ self .filename = QtWidgets .QFileDialog .getOpenFileName (self )
52
+ self .pkl_file = pickle .load (open (self .filename [0 ], 'rb' ))
53
+ except Exception as err :
54
+ print (format (err ))
55
+
56
+ def import_pkl_to_convert (self ):
57
+ """ Open pkl file to convert to h5 """
58
+ try :
59
+ self .pkl_to_convert = QtWidgets .QFileDialog .getOpenFileName (self )
60
+ self .ui .result_textBrowser .append ("Done Loading - .pkl to convert" )
61
+ except :
62
+ pass
63
+
64
+ def plot_intensity_sums (self ):
65
+ try :
66
+ data = self .pkl_file
67
+ self .numb_pixels_X = int ((data ['Scan Parameters' ]['X scan size (um)' ])/ (data ['Scan Parameters' ]['X step size (um)' ]))
68
+ self .numb_pixels_Y = int ((data ['Scan Parameters' ]['Y scan size (um)' ])/ (data ['Scan Parameters' ]['Y step size (um)' ]))
69
+ self .x_step_size = float (data ['Scan Parameters' ]['X step size (um)' ])
70
+ self .x_scan_size = float (data ['Scan Parameters' ]['X scan size (um)' ])
71
+ self .y_step_size = float (data ['Scan Parameters' ]['Y step size (um)' ])
72
+ self .y_scan_size = float (data ['Scan Parameters' ]['Y scan size (um)' ])
73
+
74
+ hist_data = data ["Histogram data" ]
75
+ hist_data = np .reshape (hist_data , newshape = (hist_data .shape [0 ], self .numb_pixels_X * self .numb_pixels_Y ))
76
+ self .intensity_sums = np .sum (hist_data , axis = 0 ) #sum intensities for each pixel
77
+ self .intensity_sums = np .reshape (self .intensity_sums , newshape = (self .numb_pixels_X , self .numb_pixels_Y ))
78
+ self .ui .intensity_sums_viewBox .view .invertY (False ) # stop y axis invert
79
+ self .ui .intensity_sums_viewBox .setImage (self .intensity_sums , scale =
80
+ (data ['Scan Parameters' ]['X step size (um)' ],
81
+ data ['Scan Parameters' ]['Y step size (um)' ]))
82
+ self .ui .intensity_sums_viewBox .roi .setSize ([self .x_scan_size , self .y_step_size ]) #line roi
83
+ scale = pg .ScaleBar (size = 1 ,suffix = 'um' )
84
+ scale .setParentItem (self .ui .intensity_sums_viewBox .view )
85
+ scale .anchor ((1 , 1 ), (1 , 1 ), offset = (- 30 , - 30 ))
86
+ except Exception as err :
87
+ print (format (err ))
88
+
89
+ def line_profile_update_plot (self ):
90
+ """ Handle line profile for intensity sum viewbox """
91
+ if hasattr (self , "intensity_sums" ):
92
+ roiPlot = self .ui .intensity_sums_viewBox .getRoiPlot ()
93
+ roiPlot .clear ()
94
+ roi = self .ui .intensity_sums_viewBox .roi
95
+
96
+ image = self .ui .intensity_sums_viewBox .getProcessedImage ()
97
+
98
+ # Extract image data from ROI
99
+ axes = (self .ui .intensity_sums_viewBox .axes ['x' ], self .ui .intensity_sums_viewBox .axes ['y' ])
100
+ data , coords = roi .getArrayRegion (image .view (np .ndarray ), self .ui .intensity_sums_viewBox .imageItem , axes , returnMappedCoords = True )
101
+
102
+ #calculate sums along columns in region
103
+ sums_to_plot = np .sum (data , axis = 0 )
104
+
105
+ #get scan x-coordinates in region
106
+ x_values = coords [1 ][0 ]
107
+
108
+ try :
109
+ roiPlot .plot (x_values , sums_to_plot )
110
+ except :
111
+ pass
112
+
113
+ def plot_raw_scan (self ):
114
+ try :
115
+ data = self .pkl_file
116
+ self .numb_pixels_X = int ((data ['Scan Parameters' ]['X scan size (um)' ])/ (data ['Scan Parameters' ]['X step size (um)' ]))
117
+ self .numb_pixels_Y = int ((data ['Scan Parameters' ]['Y scan size (um)' ])/ (data ['Scan Parameters' ]['Y step size (um)' ]))
118
+ self .x_step_size = float (data ['Scan Parameters' ]['X step size (um)' ])
119
+ self .x_scan_size = float (data ['Scan Parameters' ]['X scan size (um)' ])
120
+ self .y_step_size = float (data ['Scan Parameters' ]['Y step size (um)' ])
121
+ self .y_scan_size = float (data ['Scan Parameters' ]['Y scan size (um)' ])
122
+ # TODO test line scan plots
123
+ hist_data = data ['Histogram data' ]
124
+
125
+ self .hist_image = np .reshape (hist_data , newshape = (hist_data .shape [0 ],self .numb_pixels_X ,self .numb_pixels_Y ))
126
+ time_data = data ['Time data' ]
127
+ self .times = time_data [:, 0 , 0 ]* 1e-3
128
+ self .ui .raw_hist_data_viewBox .view .invertY (False ) # stops y-axis invert
129
+ self .ui .raw_hist_data_viewBox .setImage (self .hist_image , scale =
130
+ (data ['Scan Parameters' ]['X step size (um)' ],
131
+ data ['Scan Parameters' ]['Y step size (um)' ]), xvals = self .times )
132
+ self .ui .raw_hist_data_viewBox .roi .setSize ([self .x_scan_size , self .y_scan_size ])
133
+ # if self.ui.compare_checkBox.isChecked():
134
+ # self.ui.imv2.setImage(self.hist_image, scale= (data['Scan Parameters']['X step size (um)'],
135
+ # data['Scan Parameters']['Y step size (um)']), xvals=self.times)
136
+ self .switch_compare ()
137
+ self .ui .raw_hist_data_viewBox .ui .roiBtn .clicked .connect (self .switch_compare )
138
+ scale = pg .ScaleBar (size = 1 ,suffix = 'um' )
139
+ scale .setParentItem (self .ui .raw_hist_data_viewBox .view )
140
+ scale .anchor ((1 , 1 ), (1 , 1 ), offset = (- 30 , - 30 ))
141
+
142
+ except Exception as err :
143
+ print (format (err ))
144
+
145
+ def switch_compare (self ):
146
+ """
147
+ Handles compare checkbox. If checked, show second ROI on raw histogram data that user can use for comparison to first ROI.
148
+ """
149
+ if self .ui .compare_checkBox .isChecked () and hasattr (self , "hist_image" ):
150
+ if not hasattr (self , "roi2" ): #create roi if doesn't exist yet
151
+ self .roi2 = pg .ROI (pos = [0 ,0 ], size = [int (self .x_scan_size / 2 ), int (self .y_scan_size / 2 )], movable = True , pen = 'r' )
152
+ self .roi2 .addScaleHandle ([1 , 1 ], [0 , 0 ])
153
+ self .roi2 .addRotateHandle ([0 , 0 ], [1 , 1 ])
154
+ self .roi2 .sigRegionChanged .connect (self .update_roi2_plot )
155
+ self .ui .raw_hist_data_viewBox .addItem (self .roi2 )
156
+ self .update_roi2_plot ()
157
+ self .roi2 .hide ()
158
+ self .roi2_plot .hide ()
159
+ if self .ui .raw_hist_data_viewBox .ui .roiBtn .isChecked ():
160
+ self .roi2 .show ()
161
+ self .roi2_plot .show ()
162
+ else :
163
+ self .roi2 .hide ()
164
+ self .roi2_plot .hide ()
165
+ else : #if not checked, hide roi
166
+ if hasattr (self , "roi2" ):
167
+ self .roi2 .hide ()
168
+ self .roi2_plot .hide ()
169
+
170
+ def update_roi2_plot (self ):
171
+ """ Update plot corresponding to second roi """
172
+ #Adapted from pyqtgraph imageview sourcecode
173
+
174
+ image = self .ui .raw_hist_data_viewBox .getProcessedImage ()
175
+
176
+ # Extract image data from ROI
177
+ axes = (self .ui .raw_hist_data_viewBox .axes ['x' ], self .ui .raw_hist_data_viewBox .axes ['y' ])
178
+ data , coords = self .roi2 .getArrayRegion (image .view (np .ndarray ), self .ui .raw_hist_data_viewBox .imageItem , axes , returnMappedCoords = True )
179
+ if data is None :
180
+ return
181
+
182
+ # Average data within entire ROI for each frame
183
+ data = data .mean (axis = max (axes )).mean (axis = min (axes ))
184
+ xvals = self .ui .raw_hist_data_viewBox .tVals
185
+ if hasattr (self , "roi2_plot" ):
186
+ self .roi2_plot .clear ()
187
+ self .roi2_plot = self .ui .raw_hist_data_viewBox .getRoiPlot ().plot (xvals , data , pen = 'r' )
188
+
189
+ def save_intensities_image (self ):
190
+ try :
191
+ filename_ext = os .path .basename (self .filename [0 ])
192
+ filename = os .path .splitext (filename_ext )[0 ] #get filename without extension
193
+ save_to = os .getcwd () + "\\ " + filename + "_intensity_sums.png"
194
+ cpm .plot_confocal (self .intensity_sums , stepsize = np .abs (self .pkl_file ['Scan Parameters' ]['X step size (um)' ]))
195
+ cpm .plt .savefig (save_to , bbox_inches = 'tight' , dpi = 300 )
196
+ except :
197
+ pass
198
+
199
+ def save_intensities_array (self ):
200
+ try :
201
+ filename_ext = os .path .basename (self .filename [0 ])
202
+ filename = os .path .splitext (filename_ext )[0 ] #get filename without extension
203
+ save_to = os .getcwd () + "\\ " + filename + "_intensity_sums.txt"
204
+ np .savetxt (save_to , self .intensity_sums .T , fmt = '%f' ) #save transposed intensity sums, as original array handles x in cols and y in rows
205
+ except :
206
+ pass
207
+
208
+ def pkl_to_h5 (self ):
209
+ #Convert scan .pkl file into h5
210
+ try :
211
+ folder = os .path .dirname (self .pkl_to_convert [0 ])
212
+ filename_ext = os .path .basename (self .pkl_to_convert [0 ])
213
+ filename = os .path .splitext (filename_ext )[0 ] #get filename without extension
214
+ pkl_file = pickle .load (open (self .pkl_to_convert [0 ], 'rb' ))
215
+
216
+ h5_filename = folder + "/" + filename + ".h5"
217
+ h5_file = h5py .File (h5_filename , "w" )
218
+ self .traverse_dict_into_h5 (pkl_file , h5_file )
219
+ except Exception as err :
220
+ print (format (err ))
221
+
222
+ def traverse_dict_into_h5 (self , dictionary , h5_output ):
223
+ #Create an h5 file using .pkl with scan data and params
224
+ for key in dictionary :
225
+ if type (dictionary [key ]) == dict : #if subdictionary, create a group
226
+ group = h5_output .create_group (key )
227
+ previous_dict = dictionary [key ]
228
+ self .traverse_dict_into_h5 (dictionary [key ], group ) #traverse subdictionary
229
+ else :
230
+ if key == "Histogram data" or key == "Time data" :
231
+ h5_output .create_dataset (key , data = dictionary [key ])
232
+ else :
233
+ h5_output .attrs [key ] = dictionary [key ] #if not dataset, create attribute
234
+
235
+ def close_application (self ):
236
+ choice = QtGui .QMessageBox .question (self , 'EXIT!' ,
237
+ "Do you want to exit the app?" ,
238
+ QtGui .QMessageBox .Yes | QtGui .QMessageBox .No )
239
+ if choice == QtGui .QMessageBox .Yes :
240
+ sys .exit ()
241
+ else :
242
+ pass
243
+
244
+ """Run the Main Window"""
245
+ def run ():
246
+ win = MainWindow ()
247
+ QtGui .QApplication .instance ().exec_ ()
248
+ return win
249
+
250
+ #Uncomment below if you want to run this as standalone
251
+ #run()
0 commit comments