def _on_ensemble_test(self, event=None): from PYME.recipes.base import ModuleCollection from PYME.IO.FileUtils import nameUtils from nep_fitting.recipe_modules import nep_fits from nep_fitting import reports from PYME.IO.ragged import RaggedCache import webbrowser rec = ModuleCollection() rec.add_module( nep_fits.TestEnsembleParameters( rec, inputName='line_profiles', fit_type=list(profile_fitters.ensemble_fitters.keys())[0], hold_ensemble_parameter_constant=False, outputName='output')) # populate namespace with current profiles rec.namespace['line_profiles'] = RaggedCache( self._line_profile_handler.get_line_profiles()) if not rec.configure_traits(view=rec.pipeline_view, kind='modal'): return # handle cancel res = rec.execute() # generate report context = { #'ensemble_parameters': res.mdh['TestEnsembleParameters.EnsembleTestValues'], # note this is a dict 'results': res, 'filename': self._dsviewer.image.filename, 'fittype': res.mdh['TestEnsembleParameters.FitType'], 'img_schematic': reports.img_as_strb64( reports.get_schematic( res.mdh['TestEnsembleParameters.FitType'])) } handler_names = self._line_profile_handler.get_image_names() if (len(handler_names) == 1) and (handler_names[0] == self.image_name): # if there's only a single image, include it in the report context['img_data'] = reports.img_as_strb64( self._dsviewer.view.GrabPNGToBuffer()) fdialog = wx.FileDialog( None, 'Save report as ...', wildcard='html (*.html)|*.html', style=wx.FD_SAVE, defaultDir=nameUtils.genShiftFieldDirectoryPath()) succ = fdialog.ShowModal() if (succ == wx.ID_OK): fpath = os.path.splitext(fdialog.GetPath())[0] + '.html' reports.generate_and_save(fpath, context, template_name='ensemble_test.html') webbrowser.open('file://' + fpath, 2)
def OnGenShiftmapQuad(self, event): from PYME.Analysis.points import twoColour, twoColourPlot from PYME.IO.MetaDataHandler import get_camera_roi_origin pipeline = self.visFr.pipeline vs = pipeline.mdh.voxelsize_nm roi_x0, roi_y0 = get_camera_roi_origin(pipeline.mdh) x0 = (roi_x0)*vs[0] y0 = (roi_y0)*vs[1] lx = len(pipeline.filter['x']) bbox = None#[0,(pipeline.mdh['Camera.ROIWidth'] + 1)*vs[0], 0,(pipeline.mdh['Camera.ROIHeight'] + 1)*vs[1]] dx, dy, spx, spy, good = twoColour.genShiftVectorFieldQ(pipeline.filter['x']+.1*np.random.randn(lx) + x0, pipeline.filter['y']+.1*np.random.randn(lx) + y0, pipeline.filter['fitResults_dx'], pipeline.filter['fitResults_dy'], pipeline.filter['fitError_dx'], pipeline.filter['fitError_dy'], bbox=bbox) #twoColourPlot.PlotShiftField(dx, dy, spx, spy) twoColourPlot.PlotShiftField2(spx, spy, pipeline.mdh['Splitter.Channel0ROI'][2:], voxelsize=vs) twoColourPlot.PlotShiftResiduals(pipeline['x'][good] + x0, pipeline['y'][good] + y0, pipeline['fitResults_dx'][good], pipeline['fitResults_dy'][good], spx, spy) from six.moves import cPickle defFile = os.path.splitext(os.path.split(self.visFr.GetTitle())[-1])[0] + '.sf' fdialog = wx.FileDialog(None, 'Save shift field as ...', wildcard='Shift Field file (*.sf)|*.sf', style=wx.FD_SAVE, defaultDir = nameUtils.genShiftFieldDirectoryPath(), defaultFile=defFile) succ = fdialog.ShowModal() if (succ == wx.ID_OK): fpath = fdialog.GetPath() #save as a pickle containing the data and voxelsize fid = open(fpath, 'wb') cPickle.dump((spx, spy), fid, 2) fid.close()
def OnSetShiftField(self, event): fdialog = wx.FileDialog(None, 'Select shift field', wildcard='*.sf', style=wx.FD_OPEN, defaultDir = nameUtils.genShiftFieldDirectoryPath()) succ = fdialog.ShowModal() if (succ == wx.ID_OK): sfname = fdialog.GetPath() self.SetShiftField(sfname)
def OnGenShiftmapQuadz(self, event): from PYME.Analysis.points import twoColour, twoColourPlot pipeline = self.visFr.pipeline vs = [ pipeline.mdh['voxelsize.x'] * 1e3, pipeline.mdh['voxelsize.y'] * 1e3, 200. ] z0 = (pipeline['z'] * pipeline['A']).sum() / pipeline['A'].sum() lx = len(pipeline.filter['x']) bbox = None #[0,(pipeline.mdh['Camera.ROIWidth'] + 1)*vs[0], 0,(pipeline.mdh['Camera.ROIHeight'] + 1)*vs[1]] dx, dy, spx, spy, good = twoColour.genShiftVectorFieldQz( pipeline.filter['x'] + .1 * pylab.randn(lx), pipeline.filter['y'] + .1 * pylab.randn(lx), pipeline.filter['z'] - z0, pipeline.filter['fitResults_dx'], pipeline.filter['fitResults_dy'], pipeline.filter['fitError_dx'], pipeline.filter['fitError_dy'], bbox=bbox) #twoColourPlot.PlotShiftField(dx, dy, spx, spy) twoColourPlot.PlotShiftField2(spx, spy, pipeline.mdh['Splitter.Channel0ROI'][2:], voxelsize=vs) twoColourPlot.PlotShiftResiduals(pipeline['x'][good], pipeline['y'][good], pipeline['fitResults_dx'][good], pipeline['fitResults_dy'][good], spx, spy, pipeline.filter['z'][good] - z0) from six.moves import cPickle defFile = os.path.splitext(os.path.split( self.visFr.GetTitle())[-1])[0] + '.sf' fdialog = wx.FileDialog( None, 'Save shift field as ...', wildcard='Shift Field file (*.sf)|*.sf', style=wx.FD_SAVE, defaultDir=nameUtils.genShiftFieldDirectoryPath(), defaultFile=defFile) succ = fdialog.ShowModal() if (succ == wx.ID_OK): fpath = fdialog.GetPath() #save as a pickle containing the data and voxelsize fid = open(fpath, 'wb') cPickle.dump((spx, spy), fid, 2) fid.close()
def OnCalibrateShifts(self, event): """ Generates multiview shiftmaps on bead-data. Only beads which show up in all channels are used to generate the shiftmap. Parameters ---------- searchRadius: Radius within which to clump bead localizations that appear in all channels. Units of pixels. Notes ----- """ from PYME.recipes.multiview import CalibrateShifts # recipe = self.pipeline.recipe # # hold off auto-running the recipe until we configure things # recipe.trait_set(execute_on_invalidation=False) # try: # calibration_module = CalibrateShifts(recipe, input_name=self.pipeline.selectedDataSourceKey, # output_name='shiftmap') # # recipe.add_module(calibration_module) # if not recipe.configure_traits(view=recipe.pipeline_view, kind='modal'): # return # # recipe.execute() # sm = recipe.namespace['shiftmap'] # finally: # make sure that we configure the pipeline recipe as it was # recipe.trait_set(execute_on_invalidation=True) calibration_module = CalibrateShifts() if not calibration_module.configure_traits(kind='modal'): return sm = calibration_module.apply_simple(self.pipeline.selectedDataSource) # save the file defFile = os.path.splitext(os.path.split(self.pipeline.filename)[-1])[0] + 'MultiView.sf' fdialog = wx.FileDialog(None, 'Save shift field as ...', wildcard='Shift Field file (*.sf)|*.sf', style=wx.FD_SAVE, defaultDir=nameUtils.genShiftFieldDirectoryPath(), defaultFile=defFile) succ = fdialog.ShowModal() if (succ == wx.ID_OK): fpath = fdialog.GetPath() sm.to_hdf(fpath, tablename='shift_map', metadata=sm.mdh)
def OnShiftCorrectFolded(self, event=None): """ Applies chromatic shift correction to folded localization data that was acquired with an image splitting device, but localized without splitter awareness. See recipes.localizations.MultiviewShiftCorrect. Parameters ---------- None Notes ----- """ from PYME.recipes.multiview import ShiftCorrect pipeline = self.pipeline recipe = self.pipeline.recipe if 'FIXMESiftmap' in pipeline.mdh.keys( ): # load shiftmaps from metadata, if present fpath = pipeline.mdh['FIXMEShiftmap'] #FIXME: break this for now else: fdialog = wx.FileDialog( None, 'Load shift field', wildcard='Shift Field file (*.sf)|*.sf', style=wx.FD_OPEN, defaultDir=nameUtils.genShiftFieldDirectoryPath()) succ = fdialog.ShowModal() if (succ == wx.ID_OK): fpath = fdialog.GetPath() else: raise RuntimeError( 'Shiftmaps not found in metadata and could not be loaded from file' ) recipe.add_module( ShiftCorrect(recipe, input_name=pipeline.selectedDataSourceKey, shift_map_path=fpath, output_name='shift_corrected')) recipe.execute() self.pipeline.selectDataSource('shift_corrected')
def OnSetShiftField(self, event=None): from PYME.IO.FileUtils import nameUtils fdialog = wx.FileDialog( None, 'Please select shift field to use ...', wildcard='Shift fields|*.sf', style=wx.FD_OPEN, defaultDir=nameUtils.genShiftFieldDirectoryPath()) succ = fdialog.ShowModal() if (succ == wx.ID_OK): #self.ds = example.CDataStack(fdialog.GetPath().encode()) #self.ds = sfFilename = fdialog.GetPath() self.image.mdh.setEntry('chroma.ShiftFilename', sfFilename) dx, dy = np.load(sfFilename) self.image.mdh.setEntry('chroma.dx', dx) self.image.mdh.setEntry('chroma.dy', dy) #self.md.setEntry('PSFFile', psfFilename) #self.stShiftFieldName.SetLabel('Shifts: %s' % os.path.split(sfFilename)[1]) #self.stShiftFieldName.SetForegroundColour(wx.Colour(0, 128, 0)) return True else: return False
def OnCalibrateAstigmatism(self, event): #TODO - move all non-GUI logic for this out of this file? from PYME.recipes.measurement import FitPoints from PYME.IO.FileUtils import nameUtils import matplotlib.pyplot as plt import mpld3 import json from PYME.Analysis.PSFEst import extractImages import wx from PYME.Analysis.points.astigmatism import astigTools # query user for type of calibration # NB - GPU fit is not enabled here because it exits on number of iterations, which is not necessarily convergence for very bright beads! ftypes = ['BeadConvolvedAstigGaussFit', 'AstigGaussFitFR'] # , 'AstigGaussGPUFitFR'] fitType_dlg = wx.SingleChoiceDialog(self.dsviewer, 'Fit-type selection', 'Fit-type selection', ftypes) fitType_dlg.ShowModal() fitMod = ftypes[fitType_dlg.GetSelection()] if (fitMod == 'BeadConvolvedAstigGaussFit') and ( 'Bead.Diameter' not in self.image.mdh.keys()): beadDiam_dlg = wx.NumberEntryDialog(None, 'Bead diameter in nm', 'diameter [nm]', 'diameter [nm]', 100, 1, 9e9) beadDiam_dlg.ShowModal() beadDiam = float(beadDiam_dlg.GetValue()) # store this in metadata self.image.mdh['Analysis.Bead.Diameter'] = beadDiam ps = self.image.pixelSize obj_positions = {} obj_positions['x'] = ps * self.image.data.shape[0] * 0.5 * np.ones( self.image.data.shape[2]) obj_positions['y'] = ps * self.image.data.shape[1] * 0.5 * np.ones( self.image.data.shape[2]) obj_positions['t'] = np.arange(self.image.data.shape[2]) z = np.arange( self.image.data.shape[2]) * self.image.mdh['voxelsize.z'] * 1.e3 obj_positions['z'] = z - z.mean() ptFitter = FitPoints() ptFitter.trait_set(roiHalfSize=11) ptFitter.trait_set(fitModule=fitMod) namespace = {'input': self.image, 'objPositions': obj_positions} results = [] for chanNum in range(self.image.data.shape[3]): # get z centers dx, dy, dz = extractImages.getIntCenter(self.image.data[:, :, :, chanNum]) ptFitter.trait_set(channel=chanNum) ptFitter.execute(namespace) res = namespace['fitResults'] dsigma = abs(res['fitResults_sigmax']) - abs( res['fitResults_sigmay']) valid = ((res['fitError_sigmax'] > 0) * (res['fitError_sigmax'] < 50) * (res['fitError_sigmay'] < 50) * (res['fitResults_A'] > 0) > 0) results.append({ 'sigmax': abs(res['fitResults_sigmax'][valid]).tolist(), 'error_sigmax': abs(res['fitError_sigmax'][valid]).tolist(), 'sigmay': abs(res['fitResults_sigmay'][valid]).tolist(), 'error_sigmay': abs(res['fitError_sigmay'][valid]).tolist(), 'dsigma': dsigma[valid].tolist(), 'z': obj_positions['z'][valid].tolist(), 'zCenter': obj_positions['z'][int(dz)] }) #generate new tab to show results use_web_view = True if not '_astig_view' in dir(self): try: self._astig_view = wx.html2.WebView.New(self.dsviewer) self.dsviewer.AddPage(self._astig_view, True, 'Astigmatic calibration') except NotImplementedError: use_web_view = False # find reasonable z range for each channel, inject 'zRange' into the results. FIXME - injection is bad results = astigTools.find_and_add_zRange(results) #do plotting plt.ioff() f = plt.figure(figsize=(10, 4)) colors = iter( plt.cm.Dark2(np.linspace(0, 1, 2 * self.image.data.shape[3]))) plt.subplot(121) for i, res in enumerate(results): nextColor1 = next(colors) nextColor2 = next(colors) lbz = np.absolute(res['z'] - res['zRange'][0]).argmin() ubz = np.absolute(res['z'] - res['zRange'][1]).argmin() plt.plot(res['z'], res['sigmax'], ':', c=nextColor1) # , label='x - %d' % i) plt.plot(res['z'], res['sigmay'], ':', c=nextColor2) # , label='y - %d' % i) plt.plot(res['z'][lbz:ubz], res['sigmax'][lbz:ubz], label='x - %d' % i, c=nextColor1) plt.plot(res['z'][lbz:ubz], res['sigmay'][lbz:ubz], label='y - %d' % i, c=nextColor2) #plt.ylim(-200, 400) plt.grid() plt.xlabel('z position [nm]') plt.ylabel('Sigma [nm]') plt.legend() plt.subplot(122) colors = iter(plt.cm.Dark2(np.linspace(0, 1, self.image.data.shape[3]))) for i, res in enumerate(results): nextColor = next(colors) lbz = np.absolute(res['z'] - res['zRange'][0]).argmin() ubz = np.absolute(res['z'] - res['zRange'][1]).argmin() plt.plot(res['z'], res['dsigma'], ':', lw=2, c=nextColor) # , label='Chan %d' % i) plt.plot(res['z'][lbz:ubz], res['dsigma'][lbz:ubz], lw=2, label='Chan %d' % i, c=nextColor) plt.grid() plt.xlabel('z position [nm]') plt.ylabel('Sigma x - Sigma y [nm]') plt.legend() plt.tight_layout() plt.ion() #dat = {'z' : objPositions['z'][valid].tolist(), 'sigmax' : res['fitResults_sigmax'][valid].tolist(), # 'sigmay' : res['fitResults_sigmay'][valid].tolist(), 'dsigma' : dsigma[valid].tolist()} if use_web_view: fig = mpld3.fig_to_html(f) data = json.dumps(results) template = env.get_template('astigCal.html') html = template.render(astigplot=fig, data=data) #print html self._astig_view.SetPage(html, '') else: plt.show() fdialog = wx.FileDialog( None, 'Save Astigmatism Calibration as ...', wildcard='Astigmatism Map (*.am)|*.am', style=wx.FD_SAVE, defaultDir=nameUtils.genShiftFieldDirectoryPath( )) #, defaultFile=defFile) succ = fdialog.ShowModal() if (succ == wx.ID_OK): fpath = fdialog.GetPath() fid = open(fpath, 'w', encoding='utf8') json.dump(results, fid, indent=4, sort_keys=True) fid.close() return results
def OnMapAstigmaticZ(self, event=None, useMD = True): """ Uses sigmax and sigmay values from astigmatic fits to look up a z-position using calibration curves. See Analysis.points.astigmatism.astigTools.lookup_astig_z Parameters ---------- None Notes ----- """ from PYME.recipes.multiview import MapAstigZ #from PYME.IO import unifiedIO pipeline = self.pipeline # FIXME - Rename metadata key to be more reasonable stigLoc = pipeline.mdh.getOrDefault('Analysis.AstigmatismMapID', None) if (not stigLoc is None) and useMD: #s = unifiedIO.read(stigLoc) #astig_calibrations = json.loads(s) pathToMap = stigLoc else: fdialog = wx.FileDialog(None, 'Load Astigmatism Calibration', wildcard='Astigmatism map (*.am)|*.am', style=wx.FD_OPEN, defaultDir=nameUtils.genShiftFieldDirectoryPath()) succ = fdialog.ShowModal() if (succ == wx.ID_OK): fpath = fdialog.GetPath() fdialog.Destroy() # load json #with open(fpath, 'r') as fid: # astig_calibrations = json.load(fid) pathToMap = fpath else: fdialog.Destroy() logger.info('User canceled astigmatic calibration selection') return recipe = self.pipeline.recipe # # hold off auto-running the recipe until we configure things # recipe.trait_set(execute_on_invalidation=False) # try: # mapping_module = MapAstigZ(recipe, input_name=self.pipeline.selectedDataSourceKey, # astigmatism_calibration_location=pathToMap, output_name='z_mapped') # # recipe.add_module(mapping_module) # if not recipe.configure_traits(view=recipe.pipeline_view, kind='modal'): # return # # # FIXME - figure out why configuring just the new module doesn't give us an OK button # # if not mapping_module.configure_traits(view=mapping_module.pipeline_view): # # return #handle cancel # # recipe.add_module(mapping_module) # # recipe.execute() # finally: # make sure that we configure the pipeline recipe as it was # recipe.trait_set(execute_on_invalidation=True) mapping_module = MapAstigZ(recipe, input_name=self.pipeline.selectedDataSourceKey, astigmatism_calibration_location=pathToMap, output_name='z_mapped') if mapping_module.configure_traits(kind='modal'): recipe.add_modules_and_execute([mapping_module,]) # keep a reference for debugging self._amm = mapping_module self.pipeline.selectDataSource('z_mapped') self.visFr.RefreshView() #TODO - is this needed?
def _on_fit(self, event=None): from PYME.recipes.base import ModuleCollection from nep_fitting.recipe_modules import nep_fits from PYME.IO.ragged import RaggedCache from PYME.IO.FileUtils import nameUtils import webbrowser from nep_fitting import reports rec = ModuleCollection() rec.add_module( nep_fits.FitProfiles( rec, inputName='line_profiles', fit_type=list(profile_fitters.non_ensemble_fitters.keys())[0], outputName='output')) # populate namespace with current profiles rec.namespace['line_profiles'] = RaggedCache( self._line_profile_handler.get_line_profiles()) if not rec.configure_traits(view=rec.pipeline_view, kind='modal'): return # handle cancel res = rec.execute() fdialog = wx.FileDialog( None, 'Save results as ...', wildcard='hdf (*.hdf)|*.hdf', style=wx.FD_SAVE, defaultDir=nameUtils.genShiftFieldDirectoryPath( )) # , defaultFile=defFile) succ = fdialog.ShowModal() if (succ == wx.ID_OK): base_path = os.path.splitext(fdialog.GetPath())[0] res.to_hdf( base_path + '.hdf', tablename='profile_fits' ) # table name changed to avoid conflicts with standard fit data fitter = rec.modules[ 0].fitter # TODO - move plot_results out from class so we don't have to hack like this profile_dir = base_path + '/' os.mkdir(profile_dir) fitter.plot_results(profile_dir) htmlfn = base_path + '.html' context = { 'results': res, 'filename': self._dsviewer.image.filename, 'fittype': res.mdh['FitProfiles.FitType'], 'img_schematic': reports.img_as_strb64( reports.get_schematic(res.mdh['FitProfiles.FitType'])) } handler_names = self._line_profile_handler.get_image_names() if (len(handler_names) == 1) and (handler_names[0] == self.image_name): # if there's only a single image, include it in the report context['img_data'] = reports.img_as_strb64( self._dsviewer.view.GrabPNGToBuffer()) reports.generate_and_save(htmlfn, context, template_name='single_data.html') webbrowser.open('file://' + htmlfn, 2)
def OnCalibrateMultiview(self, event): """ CalibrateMultiview loops over all channels described in the metadata and runs CalibrateAstigmatism on each one. Results are stored in a library of dictionaries and are saved into a jason astigmatism map file (.am) Args: event: GUI event Returns: nothing """ # first make sure the user is calling the right function try: chanSizeX = self.image.mdh['Multiview.ROISize'][0] except KeyError: raise KeyError( 'You are not looking at multiview data, or the metadata is incomplete' ) if len(self.PSFLocs) > 0: print( 'Place cursor on bead in first multiview channel and press Calibrate Multiview Astigmatism' ) self.OnClearTags(event) return from PYME.Analysis.PSFEst import extractImages from PYME.DSView.modules import psfTools import scipy.interpolate as terp import numpy as np from PYME.IO.FileUtils import nameUtils import os import json zrange = np.nan * np.ones(2) rsx, rsy, rsz = [int(s) for s in self.tPSFROI.GetValue().split(',')] # astigDat = [] astigLib = {} for ii in range(self.numChan): # grab PSFs, currently relying on user to pick psf in first channel xmin, xmax = [(self.do.xp - rsx + ii * chanSizeX), (self.do.xp + rsx + ii * chanSizeX + 1)] # dz returns offset in units of frames, dx dy are in pixels dx, dy, dz = extractImages.getIntCenter( self.image.data[xmin:xmax, (self.do.yp - rsy):(self.do.yp + rsy + 1), :, 0]) self.PSFLocs.append((self.do.xp + dx, self.do.yp + dy, dz)) self.view.psfROIs = self.PSFLocs self.view.Refresh() psfROISize = [int(s) for s in self.tPSFROI.GetValue().split(',')] psfBlur = [float(s) for s in self.tPSFBlur.GetValue().split(',')] psf = extractImages.getPSF3D(self.image.data[:, :, :, 0], [self.PSFLocs[ii]], psfROISize, psfBlur) if self.cbBackgroundCorrect.GetValue(): #widefield image - do special background subtraction psf = extractImages.backgroundCorrectPSFWF(psf) from PYME.DSView.dsviewer import ImageStack, ViewIm3D im = ImageStack(data=psf, mdh=self.image.mdh, titleStub='Extracted PSF, Chan %i' % ii) im.defaultExt = '*.psf' #we want to save as PSF by default ViewIm3D(im, mode='psf', parent=wx.GetTopLevelParent(self.dsviewer)) calibrater = psfTools.PSFTools(self.dsviewer, im) astigLib['PSF%i' % ii] = (calibrater.OnCalibrateAstigmatism( event, plotIt=False)) # the next line is only ok if each frame is at a different z position, which it should be astigLib['PSF%i' % ii]['z'] += dz * self.image.mdh['voxelsize.z'] * 1.e3 # -- spline interpolate -- # find region of dsigma which is monotonic dsig = terp.UnivariateSpline( astigLib['PSF%i' % ii]['z'], astigLib['PSF%i' % ii]['dsigma']) #, s=1.5*len(astigDat[ii]['z'])) # mask where the sign is the same as the center zvec = np.linspace(astigLib['PSF%i' % ii]['z'].min(), astigLib['PSF%i' % ii]['z'].max(), 1000) sgn = np.sign(np.diff(dsig(zvec))) halfway = len(sgn) / 2 notmask = sgn != sgn[halfway] # find z range for spline generation lowerZ = zvec[np.where(notmask[:halfway])[0].max()] upperZ = zvec[(len(sgn) / 2 + np.where(notmask[halfway:])[0].min() - 1)] astigLib['PSF%i' % ii]['zrange'] = [lowerZ, upperZ] zrange = [ np.nanmin([lowerZ, zrange[0]]), np.nanmax([upperZ, zrange[1]]) ] #lowsubZ , upsubZ = np.absolute(astigDat[ii]['z'] - zvec[lowerZ]), np.absolute(astigDat[ii]['z'] - zvec[upperZ]) #lowZLoc = np.argmin(lowsubZ) #upZLoc = np.argmin(upsubZ) # #astigLib['sigxTerp%i' % ii] = terp.UnivariateSpline(astigLib['PSF%i' % ii]['z'], astigLib['PSF%i' % ii]['sigmax'], # bbox=[lowerZ, upperZ]) #astigLib['sigyTerp%i' % ii] = terp.UnivariateSpline(astigLib['PSF%i' % ii]['z'], astigLib['PSF%i' % ii]['sigmay'], # bbox=[lowerZ, upperZ]) astigLib['PSF%i' % ii]['z'] = astigLib['PSF%i' % ii]['z'].tolist() astigLib['zRange'] = np.round(zrange).tolist() astigLib['numChan'] = self.numChan psfTools.plotAstigCalibration(astigLib) # save to json file #defFile = os.path.splitext(os.path.split(self.visFr.GetTitle())[-1])[0] + '.am' fdialog = wx.FileDialog( None, 'Save Astigmatism Calibration as ...', wildcard='AstigMAPism file (*.am)|*.am', style=wx.FD_SAVE, defaultDir=nameUtils.genShiftFieldDirectoryPath( )) #, defaultFile=defFile) succ = fdialog.ShowModal() if (succ == wx.ID_OK): fpath = fdialog.GetPath() fid = open(fpath, 'wb') json.dump(astigLib, fid) fid.close()