Skip to content

Commit

Permalink
Fix #1203. Eliminate use of data sampling to calculate complex displa…
Browse files Browse the repository at this point in the history
…y limits.

The sample size was 200 and in no way could be representative of an
arbitrary complex valued image displayed in log-absolute. This was probably
done for perceived performance; but modern processors make the performance
issue minimal. So do not sample; just use the full range until a good
general sampling solution is implemented.
  • Loading branch information
cmeyer committed Nov 26, 2024
1 parent 2d73324 commit b67e070
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 55 deletions.
58 changes: 4 additions & 54 deletions nion/swift/model/DisplayItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,23 +177,12 @@ def remove_index(self, remove_index: int) -> None:
self.changed_event.fire()


def calculate_display_range(display_limits: DisplayLimitsType,
data_range: typing.Optional[typing.Tuple[float, float]],
data_sample: typing.Optional[_ImageDataType],
xdata: typing.Optional[DataAndMetadata.DataAndMetadata],
complex_display_type: typing.Optional[str]) -> typing.Optional[typing.Tuple[float, float]]:
def calculate_display_range(display_limits: DisplayLimitsType, data_range: typing.Optional[typing.Tuple[float, float]]) -> typing.Optional[typing.Tuple[float, float]]:
if display_limits is not None:
assert data_range is not None
display_limit_low = display_limits[0] if display_limits[0] is not None else data_range[0]
display_limit_high = display_limits[1] if display_limits[1] is not None else data_range[1]
return display_limit_low, display_limit_high
if xdata and xdata.is_data_complex_type and complex_display_type is None: # log absolute
if data_sample is not None:
assert data_range is not None
fraction = 0.05
display_limit_low = data_sample[int(data_sample.shape[0] * fraction)]
display_limit_high = data_range[1]
return display_limit_low, display_limit_high
return data_range


Expand Down Expand Up @@ -577,11 +566,8 @@ class DisplayRangeProcessor(ProcessorBase):
def __init__(self, *,
element_data: typing.Union[typing.Optional[DataAndMetadata._DataAndMetadataLike], ProcessorConnection] = None,
display_limits: typing.Union[typing.Optional[DisplayLimitsType], ProcessorConnection] = None,
data_range: typing.Union[typing.Optional[typing.Tuple[float, float]], ProcessorConnection] = None,
data_sample: typing.Union[typing.Optional[_ImageDataType], ProcessorConnection] = None,
complex_display_type: typing.Union[typing.Optional[str], ProcessorConnection] = None) -> None:
super().__init__(element_data=element_data, display_limits=display_limits, data_range=data_range,
data_sample=data_sample, complex_display_type=complex_display_type)
data_range: typing.Union[typing.Optional[typing.Tuple[float, float]], ProcessorConnection] = None) -> None:
super().__init__(element_data=element_data, display_limits=display_limits, data_range=data_range)

def _execute(self) -> None:
element_data_and_metadata = self._get_data_and_metadata_like("element_data")
Expand All @@ -592,38 +578,13 @@ def _execute(self) -> None:
display_range = display_limits[0], display_limits[1]
else:
data_range = typing.cast(typing.Optional[typing.Tuple[float, float]], self._get_parameter("data_range"))
data_sample = typing.cast(typing.Optional[_ImageDataType], self._get_parameter("data_sample"))
complex_display_type = self._get_optional_string("complex_display_type")
display_range = calculate_display_range(display_limits, data_range, data_sample, element_data_and_metadata, complex_display_type)
display_range = calculate_display_range(display_limits, data_range)
# double check for cases where one or the other display limit is specified and the other is calculated.
if display_range is not None and display_range[0] is not None and display_range[1] is not None:
display_range = min(display_range[0], display_range[1]), max(display_range[0], display_range[1])
self.set_result("display_range", display_range)


class DataSampleProcessor(ProcessorBase):
def __init__(self, *,
data: typing.Union[typing.Optional[DataAndMetadata._DataAndMetadataLike], ProcessorConnection] = None,
display_data: typing.Union[typing.Optional[DataAndMetadata._DataAndMetadataLike], ProcessorConnection] = None) -> None:
super().__init__(data=data, display_data=display_data)

def _execute(self) -> None:
data_and_metadata = self._get_data_and_metadata_like("data")
display_data_and_metadata = self._get_data_and_metadata_like("display_data")
display_data = display_data_and_metadata.data if display_data_and_metadata else None
data_sample: typing.Optional[_ImageDataType] = None
if display_data is not None and display_data.shape and data_and_metadata:
data_shape = data_and_metadata.data_shape
data_dtype = data_and_metadata.data_dtype
if Image.is_shape_and_dtype_rgb_type(data_shape, data_dtype):
data_sample = None
elif Image.is_shape_and_dtype_complex_type(data_shape, data_dtype):
data_sample = numpy.sort(numpy.random.choice(display_data.reshape(numpy.prod(display_data.shape, dtype=numpy.uint64)), 200))
else:
data_sample = None
self.set_result("data_sample", data_sample)


class DisplayRGBProcessor(ProcessorBase):
def __init__(self, *,
adjusted_data: typing.Union[typing.Optional[DataAndMetadata._DataAndMetadataLike], ProcessorConnection] = None,
Expand Down Expand Up @@ -798,17 +759,10 @@ def __init__(self, data_and_metadata: typing.Optional[DataAndMetadata.DataAndMet
display_data=ProcessorConnection(self.__display_data_processor, "data", "display_data"),
)

self.__data_sample_processor = DataSampleProcessor(
data=data_and_metadata,
display_data=ProcessorConnection(self.__display_data_processor, "data", "display_data"),
)

self.__display_range_processor = DisplayRangeProcessor(
element_data=ProcessorConnection(self.__element_data_processor, "data", "element_data"),
display_limits=display_limits,
complex_display_type=complex_display_type,
data_range=ProcessorConnection(self.__data_range_processor, "data_range"),
data_sample=ProcessorConnection(self.__data_sample_processor, "data_sample"),
)

self.__normalized_data_processor = NormalizedDataProcessor(
Expand Down Expand Up @@ -875,10 +829,6 @@ def display_data_and_metadata(self) -> typing.Optional[DataAndMetadata.DataAndMe
def data_range(self) -> typing.Optional[typing.Tuple[float, float]]:
return typing.cast(typing.Optional[typing.Tuple[float, float]], self.__data_range_processor.get_result("data_range"))

@property
def data_sample(self) -> typing.Optional[_ImageDataType]:
return typing.cast(typing.Optional[_ImageDataType], self.__data_sample_processor.get_result("data_sample"))

@property
def display_range(self) -> typing.Optional[typing.Tuple[float, float]]:
return typing.cast(typing.Optional[typing.Tuple[float, float]], self.__display_range_processor.get_result("display_range"))
Expand Down
2 changes: 1 addition & 1 deletion nion/swift/test/Display_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ def test_reset_display_limits_on_complex_data_gives_reasonable_results(self):
display_data_channel.reset_display_limits()
# the display limit should never be less than the display data minimum
display_range = display_data_channel.get_latest_computed_display_values().display_range
self.assertLess(numpy.amin(display_data_channel.get_latest_computed_display_values().display_data_and_metadata.data), display_range[0])
self.assertLessEqual(numpy.amin(display_data_channel.get_latest_computed_display_values().display_data_and_metadata.data), display_range[0])
self.assertAlmostEqual(numpy.amax(
display_data_channel.get_latest_computed_display_values().display_data_and_metadata.data), display_range[1])

Expand Down

0 comments on commit b67e070

Please sign in to comment.