Skip to content
Merged
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
19 changes: 18 additions & 1 deletion nion/data/Core.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ def function_make_elliptical_mask(data_shape: DataAndMetadata.ShapeType, center:
bounds = Geometry.FloatRect.from_center_and_size(center_point, size_size)
if bounds.height <= 0 or bounds.width <= 0:
return DataAndMetadata.new_data_and_metadata(data=mask)
a, b = bounds.center.y, bounds.center.x
a, b = bounds.center.y - 0.5, bounds.center.x - 0.5
# work around incomplete numpy typing
y: _ImageDataType
x: _ImageDataType
Expand All @@ -696,6 +696,23 @@ def function_make_elliptical_mask(data_shape: DataAndMetadata.ShapeType, center:
mask[mask_eq] = 1
return DataAndMetadata.new_data_and_metadata(data=mask)

def function_make_rectangular_mask(data_shape: DataAndMetadata.ShapeType, center: Geometry.FloatPoint, size: Geometry.FloatSize, rotation: float) -> DataAndMetadata.DataAndMetadata:
data_size = Geometry.IntSize.make(typing.cast(Geometry.SizeIntTuple, data_shape))
data_rect = Geometry.FloatRect(origin=Geometry.FloatPoint(), size=Geometry.FloatSize.make(typing.cast(Geometry.SizeFloatTuple, data_size)))
center_point = Geometry.map_point(center, Geometry.FloatRect.unit_rect(), data_rect)
size_size = Geometry.map_size(size, Geometry.FloatRect.unit_rect(), data_rect)
mask = numpy.zeros(data_shape)
bounds = Geometry.FloatRect.from_center_and_size(center_point, size_size)
a, b = bounds.top + bounds.height * 0.5 - 0.5, bounds.left + bounds.width * 0.5 - 0.5
y, x = numpy.ogrid[-a:data_shape[0] - a, -b:data_shape[1] - b] # type: ignore
if rotation == 0.0:
mask_eq = (numpy.fabs(x) / (bounds.width / 2) <= 1) & (numpy.fabs(y) / (bounds.height / 2) <= 1)
else:
angle_sin = math.sin(rotation)
angle_cos = math.cos(rotation)
mask_eq = (numpy.fabs(x*angle_cos - y*angle_sin) / (bounds.width / 2) <= 1) & (numpy.fabs(y*angle_cos + x*angle_sin) / (bounds.height / 2) <= 1)
mask[mask_eq] = 1
return DataAndMetadata.new_data_and_metadata(data=mask)

def function_fourier_mask(data_and_metadata_in: _DataAndMetadataIndeterminateSizeLike, mask_data_and_metadata_in: _DataAndMetadataIndeterminateSizeLike) -> DataAndMetadata.DataAndMetadata:
data_and_metadata_c = DataAndMetadata.promote_indeterminate_array(data_and_metadata_in)
Expand Down
265 changes: 265 additions & 0 deletions nion/data/test/Core_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from nion.data import Core
from nion.data import DataAndMetadata
from nion.data.DataAndMetadata import _ImageDataType
from nion.utils import Geometry


class TestCore(unittest.TestCase):
Expand Down Expand Up @@ -1054,6 +1055,270 @@ def test_element_data_returns_ndarray(self) -> None:
# test directly its type
self.assertIsInstance(element.data, numpy.ndarray)

def test_elliptical_mask_generation(self) -> None:
bounds = Geometry.FloatRect.make(((0.2, 0.2), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertEqual(mask.data[200, 200], 0) # top left
self.assertEqual(mask.data[200, 299], 0) # bottom left
self.assertEqual(mask.data[299, 299], 0) # bottom right
self.assertEqual(mask.data[299, 200], 0) # top right

self.assertEqual(mask.data[249, 200], 1) # center top
self.assertEqual(mask.data[249, 199], 0) # center top
self.assertEqual(mask.data[299, 249], 1) # center right
self.assertEqual(mask.data[300, 249], 0) # center right
self.assertEqual(mask.data[249, 299], 1) # center bottom
self.assertEqual(mask.data[249, 300], 0) # center bottom
self.assertEqual(mask.data[200, 249], 1) # center left
self.assertEqual(mask.data[199, 249], 0) # center left

def test_elliptical_mask_generation_out_of_bounds_top_left(self) -> None:
bounds = Geometry.FloatRect.make(((-0.05, -0.05), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertEqual(mask.data[49, 49], 0) # bottom right

self.assertEqual(mask.data[49, 0], 1) # center right
self.assertEqual(mask.data[50, 0], 0) # center right
self.assertEqual(mask.data[0, 49], 1) # center bottom
self.assertEqual(mask.data[0, 50], 0) # center bottom

def test_elliptical_mask_generation_out_of_bounds_center_top(self) -> None:
bounds = Geometry.FloatRect.make(((0.45, -0.05), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertEqual(mask.data[450, 49], 0) # bottom left
self.assertEqual(mask.data[549, 49], 0) # bottom right

self.assertEqual(mask.data[549, 0], 1) # center right
self.assertEqual(mask.data[550, 0], 0) # center right
self.assertEqual(mask.data[500, 49], 1) # center bottom
self.assertEqual(mask.data[500, 50], 0) # center bottom
self.assertEqual(mask.data[450, 0], 1) # center left
self.assertEqual(mask.data[449, 0], 0) # center left

def test_elliptical_mask_generation_out_of_bounds_top_right(self) -> None:
bounds = Geometry.FloatRect.make(((0.95, -0.05), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertEqual(mask.data[950, 49], 0) # bottom left

self.assertEqual(mask.data[999, 49], 1) # center bottom
self.assertEqual(mask.data[999, 50], 0) # center bottom
self.assertEqual(mask.data[950, 0], 1) # center left
self.assertEqual(mask.data[949, 0], 0) # center left

def test_elliptical_mask_generation_out_of_bounds_center_right(self) -> None:
bounds = Geometry.FloatRect.make(((0.95, 0.45), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertEqual(mask.data[950, 450], 0) # top left
self.assertEqual(mask.data[950, 549], 0) # bottom left

self.assertEqual(mask.data[999, 450], 1) # center top
self.assertEqual(mask.data[999, 449], 0) # center top
self.assertEqual(mask.data[999, 549], 1) # center bottom
self.assertEqual(mask.data[999, 550], 0) # center bottom
self.assertEqual(mask.data[950, 500], 1) # center left
self.assertEqual(mask.data[949, 550], 0) # center left

def test_elliptical_mask_generation_out_of_bounds_bottom_right(self) -> None:
bounds = Geometry.FloatRect.make(((0.95, 0.95), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertEqual(mask.data[950, 950], 0) # top left

self.assertEqual(mask.data[999, 950], 1) # center top
self.assertEqual(mask.data[999, 949], 0) # center top
self.assertEqual(mask.data[950, 999], 1) # center left
self.assertEqual(mask.data[949, 999], 0) # center left

def test_elliptical_mask_generation_out_of_bound_center_bottom(self) -> None:
bounds = Geometry.FloatRect.make(((0.45, 0.95), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertEqual(mask.data[450, 950], 0) # top left
self.assertEqual(mask.data[549, 950], 0) # top right

self.assertEqual(mask.data[500, 950], 1) # center top
self.assertEqual(mask.data[500, 949], 0) # center top
self.assertEqual(mask.data[549, 999], 1) # center right
self.assertEqual(mask.data[550, 999], 0) # center right
self.assertEqual(mask.data[450, 999], 1) # center left
self.assertEqual(mask.data[449, 999], 0) # center left

def test_elliptical_mask_generation_out_of_bounds_bottom_left(self) -> None:
bounds = Geometry.FloatRect.make(((-0.05, 0.95), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertEqual(mask.data[49, 950], 0) # top right

self.assertEqual(mask.data[0, 950], 1) # center top
self.assertEqual(mask.data[0, 949], 0) # center top
self.assertEqual(mask.data[49, 999], 1) # center right
self.assertEqual(mask.data[50, 999], 0) # center right

def test_elliptical_mask_generation_out_of_bounds_center_left(self) -> None:
bounds = Geometry.FloatRect.make(((-0.05, 0.45), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertEqual(mask.data[49, 549], 0) # bottom right
self.assertEqual(mask.data[49, 450], 0) # top right

self.assertEqual(mask.data[0, 450], 1) # center top
self.assertEqual(mask.data[0, 449], 0) # center top
self.assertEqual(mask.data[49, 500], 1) # center right
self.assertEqual(mask.data[50, 500], 0) # center right
self.assertEqual(mask.data[0, 549], 1) # center bottom
self.assertEqual(mask.data[0, 550], 0) # center bottom

def test_elliptical_mask_generation_out_of_bounds_completely(self) -> None:
bounds = Geometry.FloatRect.make(((1.1, 1.1), (0.1, 0.1)))
mask_xdata = Core.function_make_elliptical_mask((1000, 1000), bounds.center.as_tuple(), bounds.size.as_tuple(), 0)
mask = mask_xdata.data
self.assertTrue(numpy.all(mask == 0))

def test_rectangular_mask_generation(self) -> None:
bounds = Geometry.FloatRect.make(((0.2, 0.2), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size,0)
mask = mask_xdata.data
self.assertEqual(mask.data[200, 200], 1) # top left
self.assertEqual(mask.data[199, 199], 0) # top left
self.assertEqual(mask.data[200, 299], 1) # bottom left
self.assertEqual(mask.data[199, 300], 0) # bottom left
self.assertEqual(mask.data[299, 299], 1) # bottom right
self.assertEqual(mask.data[300, 300], 0) # bottom right
self.assertEqual(mask.data[299, 200], 1) # top right
self.assertEqual(mask.data[300, 199], 0) # top right

self.assertEqual(mask.data[249, 200], 1) # center top
self.assertEqual(mask.data[249, 199], 0) # center top
self.assertEqual(mask.data[299, 249], 1) # center right
self.assertEqual(mask.data[300, 249], 0) # center right
self.assertEqual(mask.data[249, 299], 1) # center bottom
self.assertEqual(mask.data[249, 300], 0) # center bottom
self.assertEqual(mask.data[200, 249], 1) # center left
self.assertEqual(mask.data[199, 249], 0) # center left

def test_rectangular_mask_generation_out_of_bounds_top_left(self) -> None:
bounds = Geometry.FloatRect.make(((-0.05, -0.05), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size, 0)
mask = mask_xdata.data
self.assertEqual(mask.data[49, 49], 1) # bottom right
self.assertEqual(mask.data[50, 50], 0) # bottom right

self.assertEqual(mask.data[49, 0], 1) # center right
self.assertEqual(mask.data[50, 0], 0) # center right
self.assertEqual(mask.data[0, 49], 1) # center bottom
self.assertEqual(mask.data[0, 50], 0) # center bottom

def test_rectangular_mask_generation_out_of_bounds_center_top(self) -> None:
bounds = Geometry.FloatRect.make(((0.45, -0.05), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size, 0)
mask = mask_xdata.data
self.assertEqual(mask.data[450, 49], 1) # bottom left
self.assertEqual(mask.data[449, 50], 0) # bottom left
self.assertEqual(mask.data[549, 49], 1) # bottom right
self.assertEqual(mask.data[550, 50], 0) # bottom right

self.assertEqual(mask.data[549, 0], 1) # center right
self.assertEqual(mask.data[550, 0], 0) # center right
self.assertEqual(mask.data[500, 49], 1) # center bottom
self.assertEqual(mask.data[500, 50], 0) # center bottom
self.assertEqual(mask.data[450, 0], 1) # center left
self.assertEqual(mask.data[449, 0], 0) # center left

def test_rectangular_mask_generation_out_of_bounds_top_right(self) -> None:
bounds = Geometry.FloatRect.make(((0.95, -0.05), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size,0)
mask = mask_xdata.data
self.assertEqual(mask.data[950, 49], 1) # bottom left
self.assertEqual(mask.data[949, 50], 0) # bottom left

self.assertEqual(mask.data[999, 49], 1) # center bottom
self.assertEqual(mask.data[999, 50], 0) # center bottom
self.assertEqual(mask.data[950, 0], 1) # center left
self.assertEqual(mask.data[949, 0], 0) # center left

def test_rectangular_mask_generation_out_of_bounds_center_right(self) -> None:
bounds = Geometry.FloatRect.make(((0.95, 0.45), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size, 0)
mask = mask_xdata.data
self.assertEqual(mask.data[950, 450], 1) # top left
self.assertEqual(mask.data[949, 449], 0) # top left
self.assertEqual(mask.data[950, 549], 1) # bottom left
self.assertEqual(mask.data[950, 550], 0) # bottom left

self.assertEqual(mask.data[999, 450], 1) # center top
self.assertEqual(mask.data[999, 449], 0) # center top
self.assertEqual(mask.data[999, 549], 1) # center bottom
self.assertEqual(mask.data[999, 550], 0) # center bottom
self.assertEqual(mask.data[950, 500], 1) # center left
self.assertEqual(mask.data[949, 500], 0) # center left

def test_rectangular_mask_generation_out_of_bounds_bottom_right(self) -> None:
bounds = Geometry.FloatRect.make(((0.95, 0.95), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size,0)
mask = mask_xdata.data
self.assertEqual(mask.data[950, 950], 1) # top left
self.assertEqual(mask.data[949, 949], 0) # top left

self.assertEqual(mask.data[999, 950], 1) # center top
self.assertEqual(mask.data[999, 949], 0) # center top
self.assertEqual(mask.data[950, 999], 1) # center left
self.assertEqual(mask.data[949, 999], 0) # center left

def test_rectangular_mask_generation_out_of_bound_center_bottom(self) -> None:
bounds = Geometry.FloatRect.make(((0.45, 0.95), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size,0)
mask = mask_xdata.data
self.assertEqual(mask.data[450, 950], 1) # top left
self.assertEqual(mask.data[449, 949], 0) # top left
self.assertEqual(mask.data[549, 950], 1) # top right
self.assertEqual(mask.data[550, 949], 0) # top right

self.assertEqual(mask.data[500, 950], 1) # center top
self.assertEqual(mask.data[500, 949], 0) # center top
self.assertEqual(mask.data[549, 999], 1) # center right
self.assertEqual(mask.data[550, 999], 0) # center right
self.assertEqual(mask.data[450, 999], 1) # center left
self.assertEqual(mask.data[449, 999], 0) # center left

def test_rectangular_mask_generation_out_of_bounds_bottom_left(self) -> None:
bounds = Geometry.FloatRect.make(((-0.05, 0.95), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size,0)
mask = mask_xdata.data
self.assertEqual(mask.data[49, 950], 1) # top right
self.assertEqual(mask.data[50, 949], 0) # top right

self.assertEqual(mask.data[0, 950], 1) # center top
self.assertEqual(mask.data[0, 949], 0) # center top
self.assertEqual(mask.data[49, 999], 1) # center right
self.assertEqual(mask.data[50, 999], 0) # center right

def test_rectangular_mask_generation_out_of_bounds_center_left(self) -> None:
bounds = Geometry.FloatRect.make(((-0.05, 0.45), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size,0)
mask = mask_xdata.data
self.assertEqual(mask.data[49, 549], 1) # bottom right
self.assertEqual(mask.data[50, 550], 0) # bottom right
self.assertEqual(mask.data[49, 450], 1) # top right
self.assertEqual(mask.data[50, 449], 0) # top right

self.assertEqual(mask.data[0, 450], 1) # center top
self.assertEqual(mask.data[0, 449], 0) # center top
self.assertEqual(mask.data[49, 500], 1) # center right
self.assertEqual(mask.data[50, 500], 0) # center right
self.assertEqual(mask.data[0, 549], 1) # center bottom
self.assertEqual(mask.data[0, 550], 0) # center bottom

def test_rectangular_mask_generation_out_of_bounds_completely(self) -> None:
bounds = Geometry.FloatRect.make(((1.1, 1.1), (0.1, 0.1)))
mask_xdata = Core.function_make_rectangular_mask((1000, 1000), bounds.center, bounds.size,0)
mask = mask_xdata.data
self.assertTrue(numpy.all(mask == 0))


if __name__ == '__main__':
logging.getLogger().setLevel(logging.DEBUG)
Expand Down