def __init__(self, document_controller, panel_id, properties, debounce=True, sample=True): super().__init__(document_controller, panel_id, _("Histogram")) def calculate_region_data(display_data_and_metadata, region): if region is not None and display_data_and_metadata is not None: if display_data_and_metadata.is_data_1d and isinstance( region, Graphics.IntervalGraphic): interval = region.interval if 0 <= interval[0] < 1 and 0 < interval[1] <= 1: start, end = int( interval[0] * display_data_and_metadata.data_shape[0]), int( interval[1] * display_data_and_metadata.data_shape[0]) if end - start >= 1: cropped_data_and_metadata = Core.function_crop_interval( display_data_and_metadata, interval) if cropped_data_and_metadata: return cropped_data_and_metadata elif display_data_and_metadata.is_data_2d and isinstance( region, Graphics.RectangleTypeGraphic): cropped_data_and_metadata = Core.function_crop( display_data_and_metadata, region.bounds) if cropped_data_and_metadata: return cropped_data_and_metadata return display_data_and_metadata def calculate_region_data_func(display_data_and_metadata, region): return functools.partial(calculate_region_data, display_data_and_metadata, region) def calculate_histogram_widget_data(display_data_and_metadata_func, display_range): bins = 320 subsample = 0 # hard coded subsample size subsample_fraction = None # fraction of total pixels subsample_min = 1024 # minimum subsample size display_data_and_metadata = display_data_and_metadata_func() display_data = display_data_and_metadata.data if display_data_and_metadata else None if display_data is not None: total_pixels = numpy.product(display_data.shape, dtype=numpy.uint64) if not subsample and subsample_fraction: subsample = min( max(total_pixels * subsample_fraction, subsample_min), total_pixels) if subsample: factor = total_pixels / subsample data_sample = numpy.random.choice( display_data.reshape( numpy.product(display_data.shape, dtype=numpy.uint64)), subsample) else: factor = 1.0 data_sample = numpy.copy(display_data) if display_range is None or data_sample is None: return HistogramWidgetData() histogram_data = factor * numpy.histogram( data_sample, range=display_range, bins=bins)[0] histogram_max = numpy.max( histogram_data) # assumes that histogram_data is int if histogram_max > 0: histogram_data = histogram_data / float(histogram_max) return HistogramWidgetData(histogram_data, display_range) return HistogramWidgetData() def calculate_histogram_widget_data_func( display_data_and_metadata_model_func, display_range): return functools.partial(calculate_histogram_widget_data, display_data_and_metadata_model_func, display_range) display_item_stream = TargetDisplayItemStream(document_controller) display_data_channel_stream = StreamPropertyStream( display_item_stream, "display_data_channel") region_stream = TargetRegionStream(display_item_stream) def compare_data(a, b): return numpy.array_equal(a.data if a else None, b.data if b else None) display_data_and_metadata_stream = DisplayDataChannelTransientsStream( display_data_channel_stream, "display_data_and_metadata", cmp=compare_data) display_range_stream = DisplayDataChannelTransientsStream( display_data_channel_stream, "display_range") region_data_and_metadata_func_stream = Stream.CombineLatestStream( (display_data_and_metadata_stream, region_stream), calculate_region_data_func) histogram_widget_data_func_stream = Stream.CombineLatestStream( (region_data_and_metadata_func_stream, display_range_stream), calculate_histogram_widget_data_func) color_map_data_stream = StreamPropertyStream( display_data_channel_stream, "color_map_data", cmp=numpy.array_equal) if debounce: histogram_widget_data_func_stream = Stream.DebounceStream( histogram_widget_data_func_stream, 0.05, document_controller.event_loop) if sample: histogram_widget_data_func_stream = Stream.SampleStream( histogram_widget_data_func_stream, 0.5, document_controller.event_loop) def cursor_changed_fn(canvas_x: float, display_range) -> None: if not canvas_x: document_controller.cursor_changed(None) if display_item_stream and display_item_stream.value and canvas_x: if display_range is not None: # can be None with empty data displayed_intensity_calibration = display_item_stream.value.displayed_intensity_calibration adjusted_x = display_range[0] + canvas_x * ( display_range[1] - display_range[0]) adjusted_x = displayed_intensity_calibration.convert_to_calibrated_value_str( adjusted_x) document_controller.cursor_changed( [_('Intensity: ') + str(adjusted_x)]) else: document_controller.cursor_changed(None) self.__histogram_widget_data_model = Model.FuncStreamValueModel( histogram_widget_data_func_stream, document_controller.event_loop, value=HistogramWidgetData(), cmp=numpy.array_equal) self.__color_map_data_model = Model.StreamValueModel( color_map_data_stream, cmp=numpy.array_equal) self._histogram_widget = HistogramWidget( document_controller, display_item_stream, self.__histogram_widget_data_model, self.__color_map_data_model, cursor_changed_fn) def calculate_statistics(display_data_and_metadata_func, display_data_range, region, displayed_intensity_calibration): display_data_and_metadata = display_data_and_metadata_func() data = display_data_and_metadata.data if display_data_and_metadata else None data_range = display_data_range if data is not None and data.size > 0 and displayed_intensity_calibration: mean = numpy.mean(data) std = numpy.std(data) rms = numpy.sqrt(numpy.mean(numpy.square( numpy.absolute(data)))) sum_data = mean * functools.reduce( operator.mul, Image.dimensional_shape_from_shape_and_dtype( data.shape, data.dtype)) if region is None: data_min, data_max = data_range if data_range is not None else ( None, None) else: data_min, data_max = numpy.amin(data), numpy.amax(data) mean_str = displayed_intensity_calibration.convert_to_calibrated_value_str( mean) std_str = displayed_intensity_calibration.convert_to_calibrated_value_str( std) data_min_str = displayed_intensity_calibration.convert_to_calibrated_value_str( data_min) data_max_str = displayed_intensity_calibration.convert_to_calibrated_value_str( data_max) rms_str = displayed_intensity_calibration.convert_to_calibrated_value_str( rms) sum_data_str = displayed_intensity_calibration.convert_to_calibrated_value_str( sum_data) return { "mean": mean_str, "std": std_str, "min": data_min_str, "max": data_max_str, "rms": rms_str, "sum": sum_data_str } return dict() def calculate_statistics_func(display_data_and_metadata_model_func, display_data_range, region, displayed_intensity_calibration): return functools.partial(calculate_statistics, display_data_and_metadata_model_func, display_data_range, region, displayed_intensity_calibration) display_data_range_stream = DisplayDataChannelTransientsStream( display_data_channel_stream, "data_range") displayed_intensity_calibration_stream = StreamPropertyStream( display_item_stream, 'displayed_intensity_calibration') statistics_func_stream = Stream.CombineLatestStream( (region_data_and_metadata_func_stream, display_data_range_stream, region_stream, displayed_intensity_calibration_stream), calculate_statistics_func) if debounce: statistics_func_stream = Stream.DebounceStream( statistics_func_stream, 0.05, document_controller.event_loop) if sample: statistics_func_stream = Stream.SampleStream( statistics_func_stream, 0.5, document_controller.event_loop) self.__statistics_model = Model.FuncStreamValueModel( statistics_func_stream, document_controller.event_loop, value=dict(), cmp=numpy.array_equal) self._statistics_widget = StatisticsWidget(self.ui, self.__statistics_model) # create the main column with the histogram and the statistics section column = self.ui.create_column_widget( properties={"height": 80 + 18 * 3 + 12}) column.add(self._histogram_widget) column.add_spacing(6) column.add(self._statistics_widget) column.add_spacing(6) column.add_stretch() # this is necessary to make the panel happy self.widget = column
def __init__(self, stream, property_name, cmp=None): super().__init__( stream, lambda x: Stream.PropertyChangedEventStream(x, property_name, cmp))
def test_refcounts(self) -> None: with event_loop_context() as event_loop: # map stream, value stream stream = Stream.MapStream(Stream.ValueStream(0), lambda x: x) stream_ref = weakref.ref(stream) del stream self.assertIsNone(stream_ref()) # combine stream stream2 = Stream.CombineLatestStream[typing.Any, typing.Any]( [Stream.ValueStream(0), Stream.ValueStream(0)]) stream_ref2 = weakref.ref(stream2) del stream2 self.assertIsNone(stream_ref2()) # debounce stream3 = Stream.DebounceStream(Stream.ValueStream(0), 0.0, event_loop) stream_ref3 = weakref.ref(stream3) del stream3 self.assertIsNone(stream_ref3()) # sample stream4 = Stream.SampleStream(Stream.ValueStream(0), 0.0, event_loop) stream_ref4 = weakref.ref(stream4) del stream4 self.assertIsNone(stream_ref4()) # property changed event stream stream5 = Stream.PropertyChangedEventStream[typing.Any]( Model.PropertyModel(0), "value") stream_ref5 = weakref.ref(stream5) del stream5 self.assertIsNone(stream_ref5()) # optional stream stream6 = Stream.OptionalStream(Stream.ValueStream(0), lambda x: True) stream_ref6 = weakref.ref(stream6) del stream6 self.assertIsNone(stream_ref6()) # value stream action action = Stream.ValueStreamAction(Stream.ValueStream(0), lambda x: None) action_ref = weakref.ref(action) del action self.assertIsNone(action_ref()) # value change stream stream7 = Stream.ValueChangeStream(Stream.ValueStream(0)) stream_ref7 = weakref.ref(stream7) del stream7 self.assertIsNone(stream_ref7())