def test_fixpixels_multiple_ext(niriprim2): regions = [ '430:437, 513:533', # vertical region '1 / 450,521', # single pixel '2/429:439, 136:140', # horizontal region ] ad = niriprim2.fixPixels(regions=';'.join(regions), debug=DEBUG)[0] # for all extensions sy, sx = cartesian_regions_to_slices(regions[0]) assert_almost_equal(ad[0].data[sy, sx].min(), 18.555, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 42.888, decimal=2) assert_almost_equal(ad[1].data[sy, sx].min(), 18.555, decimal=2) assert_almost_equal(ad[1].data[sy, sx].max(), 42.888, decimal=2) # only ext 1 sy, sx = cartesian_regions_to_slices(regions[1][3:]) assert_almost_equal(ad[0].data[sy, sx].min(), 24.5, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 24.5, decimal=2) assert_almost_equal(ad[1].data[sy, sx].min(), 2733, decimal=2) assert_almost_equal(ad[1].data[sy, sx].max(), 2733, decimal=2) # only ext 2 sy, sx = cartesian_regions_to_slices(regions[2][2:]) assert_almost_equal(ad[0].data[sy, sx].min(), -125, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 21293, decimal=2) assert_almost_equal(ad[1].data[sy, sx].min(), 37.166, decimal=2) assert_almost_equal(ad[1].data[sy, sx].max(), 60.333, decimal=2)
def test_fixpixels_median(niriprim): regions = [ '450,521', # single pixel ] ad = niriprim.fixPixels(regions=';'.join(regions), use_local_median=True, debug=DEBUG)[0] sy, sx = cartesian_regions_to_slices(regions[0]) assert_almost_equal(ad[0].data[sy, sx].min(), 28, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 28, decimal=2)
def test_fixpixels(niriprim): regions = [ '430:437,513:533', # vertical region '450,521', # single pixel '429:439,136:140', # horizontal region ] ad = niriprim.fixPixels(regions=';'.join(regions), debug=DEBUG)[0] sy, sx = cartesian_regions_to_slices(regions[0]) assert_almost_equal(ad[0].data[sy, sx].min(), 18.555, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 42.888, decimal=2) sy, sx = cartesian_regions_to_slices(regions[1]) assert_almost_equal(ad[0].data[sy, sx].min(), 24.5, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 24.5, decimal=2) sy, sx = cartesian_regions_to_slices(regions[2]) assert_almost_equal(ad[0].data[sy, sx].min(), 37.166, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 60.333, decimal=2)
def test_fixpixels_with_file(niriprim, tmp_path): regions = [ '450,521', # single pixel '429:439,136:140', # horizontal region ] regions_file = str(tmp_path / 'regions.txt') with open(regions_file, mode='w') as f: f.write('\n'.join(regions)) ad = niriprim.fixPixels(regions='430:437,513:533', # vertical region regions_file=regions_file, debug=DEBUG)[0] sy, sx = cartesian_regions_to_slices('430:437,513:533') assert_almost_equal(ad[0].data[sy, sx].min(), 18.555, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 42.888, decimal=2) sy, sx = cartesian_regions_to_slices(regions[0]) assert_almost_equal(ad[0].data[sy, sx].min(), 24.5, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 24.5, decimal=2) sy, sx = cartesian_regions_to_slices(regions[1]) assert_almost_equal(ad[0].data[sy, sx].min(), 37.166, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 60.333, decimal=2)
def test_fixpixels_specify_axis(niriprim): regions = [ '430:437,513:533', # vertical region ] with pytest.raises(ValueError): ad = niriprim.fixPixels(regions=';'.join(regions), axis=0)[0] with pytest.raises(ValueError): ad = niriprim.fixPixels(regions=';'.join(regions), axis=3)[0] ad = niriprim.fixPixels(regions=';'.join(regions), axis=2, debug=DEBUG)[0] sy, sx = cartesian_regions_to_slices(regions[0]) assert_almost_equal(ad[0].data[sy, sx].min(), 17.636, decimal=2) assert_almost_equal(ad[0].data[sy, sx].max(), 38.863, decimal=2)
def __init__(self, visualizer, fitting_parameters, domain, x, y, weights=None, min_order=1, max_order=10, xlabel='x', ylabel='y', plot_width=600, plot_height=400, plot_residuals=True, enable_user_masking=True): """ Panel for visualizing a 1-D fit, perhaps in a tab Parameters ---------- visualizer : :class:`~geminidr.interactive.fit.fit1d.Fit1DVisualizer` visualizer to associate with fitting_parameters : dict parameters for this fit domain : list of pixel coordinates Used for new fit_1D fitter x : :class:`~numpy.ndarray` X coordinate values y : :class:`~numpy.ndarray` Y coordinate values min_order : int minimum order in UI max_order : int maximum order in UI xlabel : str label for X axis ylabel : str label for Y axis plot_width : int width of plot area in pixels plot_height : int height of plot area in pixels plot_residuals : bool True if we want the lower plot showing the differential between the fit and the data grow_slider : bool True if we want the slider for modifying growth radius """ # Just to get the doc later self.visualizer = visualizer self.info_div = Div() # Make a listener to update the info panel with the RMS on a fit def update_info(info_div, f): info_div.update(text='<b>RMS:</b> {rms:.4f}'.format(rms=f.rms)) listeners = [ lambda f: update_info(self.info_div, f), ] self.fitting_parameters = fitting_parameters self.fit = InteractiveModel1D(fitting_parameters, domain, x, y, weights, listeners=listeners) fit = self.fit order_slider = interactive.build_text_slider("Order", fit.order, 1, min_order, max_order, fit, "order", fit.perform_fit, throttled=True) self.sigma_upper_slider = interactive.build_text_slider( "Sigma (Upper)", fitting_parameters["sigma_upper"], 0.01, 1, 10, fitting_parameters, "sigma_upper", self.sigma_slider_handler, throttled=True) self.sigma_lower_slider = interactive.build_text_slider( "Sigma (Lower)", fitting_parameters["sigma_lower"], 0.01, 1, 10, fitting_parameters, "sigma_lower", self.sigma_slider_handler, throttled=True) sigma_button = bm.CheckboxGroup( labels=['Sigma clip'], active=[0] if self.fit.sigma_clip else []) sigma_button.on_change('active', self.sigma_button_handler) controls_column = [ order_slider, row(self.sigma_upper_slider, sigma_button) ] controls_column.append(self.sigma_lower_slider) controls_column.append( interactive.build_text_slider("Max iterations", fitting_parameters["niter"], 1, 0, 10, fitting_parameters, "niter", fit.perform_fit)) controls_column.append( interactive.build_text_slider("Grow", fitting_parameters["grow"], 1, 0, 10, fitting_parameters, "grow", fit.perform_fit)) mask_button = bm.Button(label="Mask", align='center', button_type='primary', width_policy='min') mask_button.on_click(self.mask_button_handler) unmask_button = bm.Button(label="Unmask", align='center', button_type='primary', width_policy='min') unmask_button.on_click(self.unmask_button_handler) controller_div = Div() self.info_div = Div() if enable_user_masking: controls = column(*controls_column, row(mask_button, unmask_button), controller_div, self.info_div) else: controls = column(*controls_column, controller_div, self.info_div) # Now the figures x_range = None y_range = None try: if self.fit.data and 'x' in self.fit.data.data and len( self.fit.data.data['x']) >= 2: x_min = min(self.fit.data.data['x']) x_max = max(self.fit.data.data['x']) x_pad = (x_max - x_min) * 0.1 x_range = Range1d(x_min - x_pad, x_max + x_pad) if self.fit.data and 'y' in self.fit.data.data and len( self.fit.data.data['y']) >= 2: y_min = min(self.fit.data.data['y']) y_max = max(self.fit.data.data['y']) y_pad = (y_max - y_min) * 0.1 y_range = Range1d(y_min - y_pad, y_max + y_pad) except: pass # ok, we don't *need* ranges... if enable_user_masking: tools = "pan,wheel_zoom,box_zoom,reset,lasso_select,box_select,tap" else: tools = "pan,wheel_zoom,box_zoom,reset" p_main = figure(plot_width=plot_width, plot_height=plot_height, title='Fit', x_axis_label=xlabel, y_axis_label=ylabel, tools=tools, output_backend="webgl", x_range=x_range, y_range=y_range) p_main.height_policy = 'fixed' p_main.width_policy = 'fit' class Fit1DRegionListener(GIRegionListener): """ Wrapper class so we can just detect when a bands are finished. We don't want to do an expensive recalc as a user is dragging a band around. """ def __init__(self, fn): """ Create a band listener that just updates on `finished` Parameters ---------- fn : function function to call when band is finished. """ self.fn = fn def adjust_region(self, region_id, start, stop): pass def delete_region(self, region_id): self.fn() def finish_regions(self): self.fn() self.band_model = GIRegionModel() def update_regions(): self.fit.model.regions = self.band_model.build_regions() self.band_model.add_listener(Fit1DRegionListener(update_regions)) self.band_model.add_listener( Fit1DRegionListener(self.band_model_handler)) connect_figure_extras(p_main, None, self.band_model) Controller(p_main, None, self.band_model, controller_div, mask_handlers=(self.mask_button_handler, self.unmask_button_handler)) fig_column = [p_main] if plot_residuals: x_range = None try: if self.fit.data and 'x' in self.fit.data.data and len( self.fit.data.data['x']) >= 2: x_min = min(self.fit.data.data['x']) x_max = max(self.fit.data.data['x']) x_pad = (x_max - x_min) * 0.1 x_range = Range1d(x_min - x_pad, x_max + x_pad) except: pass # ok, we don't *need* ranges... p_resid = figure(plot_width=plot_width, plot_height=plot_height // 2, title='Fit Residuals', x_axis_label=xlabel, y_axis_label='delta' + ylabel, tools="pan,box_zoom,reset", output_backend="webgl", x_range=x_range, y_range=None) p_resid.height_policy = 'fixed' p_resid.width_policy = 'fit' connect_figure_extras(p_resid, None, self.band_model) fig_column.append(p_resid) # Initalizing this will cause the residuals to be calculated self.fit.data.data['residuals'] = np.zeros_like(self.fit.x) p_resid.scatter(x='x', y='residuals', source=self.fit.data, size=5, **self.fit.mask_rendering_kwargs()) # Initializing regions here ensures the listeners are notified of the region(s) if "regions" in fitting_parameters and fitting_parameters[ "regions"] is not None: region_tuples = cartesian_regions_to_slices( fitting_parameters["regions"]) self.band_model.load_from_tuples(region_tuples) self.scatter = p_main.scatter(x='x', y='y', source=self.fit.data, size=5, **self.fit.mask_rendering_kwargs()) self.fit.add_listener(self.model_change_handler) # TODO refactor? this is dupe from band_model_handler # hacking it in here so I can account for the initial # state of the band model (which used to be always empty) x_data = self.fit.data.data['x'] for i in np.arange(len(x_data)): if self.band_model.contains(x_data[i]): self.fit.band_mask[i] = 0 else: self.fit.band_mask[i] = 1 self.fit.perform_fit() self.line = p_main.line(x='xlinspace', y='model', source=self.fit.evaluation, line_width=3, color='black') region_editor = RegionEditor(self.band_model) fig_column.append(region_editor.get_widget()) col = column(*fig_column) col.sizing_mode = 'scale_width' self.component = row(controls, col)