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