Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions nion/data/Core.py
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,44 @@ def calculate_data() -> _ImageDataType:
return DataAndMetadata.new_data_and_metadata(data=calculate_data(), intensity_calibration=data_and_metadata.intensity_calibration, dimensional_calibrations=data_and_metadata.dimensional_calibrations)


def function_gaussian_window(data_shape: DataAndMetadata.ShapeType, sigma: float) -> DataAndMetadata.DataAndMetadata:
sigma = sigma if sigma > 0.0 else 1.0 # clamp sigma to avoid divide by zero
if len(data_shape) == 1:
w = data_shape[0]
return DataAndMetadata.new_data_and_metadata(scipy.signal.windows.gaussian(w, std=sigma))
elif len(data_shape) == 2:
# uses circularly rotated approach of generating 2D filter from 1D
h, w = data_shape
y, x = numpy.meshgrid(numpy.arange(0, h) - (h - 1) / 2, numpy.arange(0, w) - (w - 1) / 2, indexing='ij')
r_squared = y * y + x * x
return DataAndMetadata.new_data_and_metadata(numpy.exp(-0.5 * r_squared / (sigma * sigma)))
raise ValueError("Window input data must be 1D or 2D")


def function_hamming_window(data_shape: DataAndMetadata.ShapeType) -> DataAndMetadata.DataAndMetadata:
if len(data_shape) == 1:
return DataAndMetadata.new_data_and_metadata(scipy.signal.windows.hamming(data_shape[0]))
elif len(data_shape) == 2:
# uses outer product approach of generating 2D filter from 1D
h, w = data_shape
w0 = numpy.reshape(scipy.signal.windows.hamming(w), (1, w))
w1 = numpy.reshape(scipy.signal.windows.hamming(h), (h, 1))
return DataAndMetadata.new_data_and_metadata(w0 * w1)
raise ValueError("Window input data must be 1D or 2D")


def function_hann_window(data_shape: DataAndMetadata.ShapeType) -> DataAndMetadata.DataAndMetadata:
if len(data_shape) == 1:
return DataAndMetadata.new_data_and_metadata(scipy.signal.windows.hann(data_shape[0]))
elif len(data_shape) == 2:
# uses outer product approach of generating 2D filter from 1D
h, w = data_shape
w0 = numpy.reshape(scipy.signal.windows.hann(w), (1, w))
w1 = numpy.reshape(scipy.signal.windows.hann(h), (h, 1))
return DataAndMetadata.new_data_and_metadata(w0 * w1)
raise ValueError("Window input data must be 1D or 2D")


def function_transpose_flip(data_and_metadata_in: _DataAndMetadataLike, transpose: bool = False, flip_v: bool = False, flip_h: bool = False) -> DataAndMetadata.DataAndMetadata:
data_and_metadata = DataAndMetadata.promote_ndarray(data_and_metadata_in)

Expand Down
13 changes: 12 additions & 1 deletion nion/data/test/Core_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io
import logging
import math
import os
import typing
import unittest

Expand Down Expand Up @@ -1429,6 +1428,18 @@ def test_warp_rgba(self) -> None:
dst = Core.function_warp(src, coords)
self._validate_warp_shape(src, dst, coords, is_channel_data=True)

def test_gaussian_window(self) -> None:
sigma = 8.0
size = 17 # use an odd size so the center is well-defined
reference_data = scipy.signal.windows.gaussian(size, std=sigma)
data_1d = Core.function_gaussian_window((size,), sigma).data
data_2d = Core.function_gaussian_window((size, size), sigma).data
for i in range(size):
self.assertAlmostEqual(reference_data[i], data_1d[i])
# check that center column and center row are close
self.assertAlmostEqual(reference_data[i], data_2d[i, size//2])
self.assertAlmostEqual(reference_data[i], data_2d[size//2, i])


if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG)
Expand Down
25 changes: 23 additions & 2 deletions nion/data/xdata_1_0.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,11 +257,32 @@ def median_filter(data_and_metadata: _DataAndMetadataLike, size: int) -> DataAnd
def uniform_filter(data_and_metadata: _DataAndMetadataLike, size: int) -> DataAndMetadata.DataAndMetadata:
return Core.function_uniform_filter(data_and_metadata, size)

def transpose_flip(data_and_metadata: _DataAndMetadataLike, transpose: bool=False, flip_v: bool=False, flip_h: bool=False) -> DataAndMetadata.DataAndMetadata:
return Core.function_transpose_flip(data_and_metadata, transpose, flip_v, flip_h)
# windows

def gaussian_window(data_shape: DataAndMetadata.Shape2dType, sigma: float) -> DataAndMetadata.DataAndMetadata:
return Core.function_gaussian_window(data_shape, sigma)

def hamming_window(data_shape: DataAndMetadata.Shape2dType) -> DataAndMetadata.DataAndMetadata:
return Core.function_hamming_window(data_shape)

def hann_window(data_shape: DataAndMetadata.Shape2dType) -> DataAndMetadata.DataAndMetadata:
return Core.function_hann_window(data_shape)

# scalar functions

def sum_scalar(data_and_metadata_in: _DataAndMetadataLike) -> DataAndMetadata.ScalarAndMetadata:
data_and_metadata = DataAndMetadata.promote_ndarray(data_and_metadata_in)
return DataAndMetadata.ScalarAndMetadata.from_value(numpy.sum(data_and_metadata), data_and_metadata.intensity_calibration)

def mean_scalar(data_and_metadata_in: _DataAndMetadataLike) -> DataAndMetadata.ScalarAndMetadata:
data_and_metadata = DataAndMetadata.promote_ndarray(data_and_metadata_in)
return DataAndMetadata.ScalarAndMetadata.from_value(numpy.average(data_and_metadata), data_and_metadata.intensity_calibration)

# miscellaneous

def transpose_flip(data_and_metadata: _DataAndMetadataLike, transpose: bool=False, flip_v: bool=False, flip_h: bool=False) -> DataAndMetadata.DataAndMetadata:
return Core.function_transpose_flip(data_and_metadata, transpose, flip_v, flip_h)

def histogram(data_and_metadata: _DataAndMetadataLike, bins: int) -> DataAndMetadata.DataAndMetadata:
return Core.function_histogram(data_and_metadata, bins)

Expand Down
Loading