Skip to content

Commit 1d1ca55

Browse files
authored
Basic OPR support (#108)
- implement basic OPR support - prototype affine transform estimation algorithm - auto estimate photon counts - add macro to attempt all scan transforms
1 parent 6117219 commit 1d1ca55

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+1262
-844
lines changed

src/ptychodus/api/geometry.py

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ class AffineTransform:
1616
a11: float
1717
a12: float
1818

19-
def __call__(self, x: float, y: float) -> tuple[float, float]:
20-
xp = self.a00 * x + self.a01 * y + self.a02
21-
yp = self.a10 * x + self.a11 * y + self.a12
22-
return xp, yp
19+
def __call__(self, y: float, x: float) -> tuple[float, float]:
20+
yp = self.a00 * y + self.a01 * x + self.a02
21+
xp = self.a10 * y + self.a11 * x + self.a12
22+
return yp, xp
2323

2424

2525
@dataclass(frozen=True)
@@ -37,9 +37,6 @@ def copy(self) -> PixelGeometry:
3737
height_m=float(self.height_m),
3838
)
3939

40-
def __repr__(self) -> str:
41-
return f'{type(self).__name__}({self.width_m}, {self.height_m})'
42-
4340

4441
@dataclass(frozen=True)
4542
class ImageExtent:
@@ -62,18 +59,12 @@ def __eq__(self, other: object) -> bool:
6259

6360
return False
6461

65-
def __repr__(self) -> str:
66-
return f'{type(self).__name__}({self.width_px}, {self.height_px})'
67-
6862

6963
@dataclass(frozen=True)
7064
class Point2D:
7165
x: float
7266
y: float
7367

74-
def __repr__(self) -> str:
75-
return f'{type(self).__name__}({self.x}, {self.y})'
76-
7768

7869
@dataclass(frozen=True)
7970
class Line2D:
@@ -86,9 +77,6 @@ def lerp(self, alpha: float) -> Point2D:
8677
y = beta * self.begin.y + alpha * self.end.y
8778
return Point2D(x, y)
8879

89-
def __repr__(self) -> str:
90-
return f'{type(self).__name__}({self.begin}, {self.end})'
91-
9280

9381
@dataclass(frozen=True)
9482
class Box2D:
@@ -113,9 +101,6 @@ def y_begin(self) -> float:
113101
def y_end(self) -> float:
114102
return self.y + self.height
115103

116-
def __repr__(self) -> str:
117-
return f'{type(self).__name__}({self.x}, {self.y}, {self.width}, {self.height})'
118-
119104

120105
class Interval(Generic[T]):
121106
def __init__(self, lower: T, upper: T) -> None:

src/ptychodus/api/object.py

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@
33
from collections.abc import Sequence
44
from dataclasses import dataclass
55
from pathlib import Path
6-
from typing import Any, TypeAlias
76

87
import numpy
9-
import numpy.typing
108

119
from .geometry import ImageExtent, PixelGeometry
1210
from .scan import ScanPoint
13-
14-
ObjectArrayType: TypeAlias = numpy.typing.NDArray[numpy.complexfloating[Any, Any]]
11+
from .typing import ComplexArrayType
1512

1613

1714
@dataclass(frozen=True)
@@ -25,9 +22,6 @@ def copy(self) -> ObjectCenter:
2522
position_y_m=float(self.position_y_m),
2623
)
2724

28-
def __repr__(self) -> str:
29-
return f'{type(self).__name__}({self.position_x_m}, {self.position_y_m})'
30-
3125

3226
@dataclass(frozen=True)
3327
class ObjectPoint:
@@ -112,13 +106,13 @@ def get_object_geometry(self) -> ObjectGeometry:
112106
class Object:
113107
def __init__(
114108
self,
115-
array: ObjectArrayType | None,
109+
array: ComplexArrayType | None,
116110
pixel_geometry: PixelGeometry | None,
117111
center: ObjectCenter | None,
118112
layer_distance_m: Sequence[float] = [],
119113
) -> None:
120114
if array is None:
121-
self._array: ObjectArrayType = numpy.zeros((1, 0, 0), dtype=complex)
115+
self._array: ComplexArrayType = numpy.zeros((1, 0, 0), dtype=complex)
122116
elif numpy.iscomplexobj(array):
123117
match array.ndim:
124118
case 2:
@@ -148,7 +142,7 @@ def copy(self) -> Object:
148142
layer_distance_m=list(self._layer_distance_m),
149143
)
150144

151-
def get_array(self) -> ObjectArrayType:
145+
def get_array(self) -> ComplexArrayType:
152146
return self._array
153147

154148
@property
@@ -171,40 +165,35 @@ def height_px(self) -> int:
171165
def num_layers(self) -> int:
172166
return self._array.shape[-3]
173167

174-
def get_pixel_geometry(self) -> PixelGeometry | None:
168+
def get_pixel_geometry(self) -> PixelGeometry:
169+
if self._pixel_geometry is None:
170+
raise ValueError('Missing object pixel geometry!')
171+
175172
return self._pixel_geometry
176173

177-
def get_center(self) -> ObjectCenter | None:
174+
def get_center(self) -> ObjectCenter:
175+
if self._center is None:
176+
raise ValueError('Missing object center!')
177+
178178
return self._center
179179

180180
def get_geometry(self) -> ObjectGeometry:
181-
pixel_width_m = 0.0
182-
pixel_height_m = 0.0
183-
184-
if self._pixel_geometry is not None:
185-
pixel_width_m = self._pixel_geometry.width_m
186-
pixel_height_m = self._pixel_geometry.height_m
187-
188-
center_x_m = 0.0
189-
center_y_m = 0.0
190-
191-
if self._center is not None:
192-
center_x_m = self._center.position_x_m
193-
center_y_m = self._center.position_y_m
181+
pixel_geometry = self.get_pixel_geometry()
182+
center = self.get_center()
194183

195184
return ObjectGeometry(
196185
width_px=self.width_px,
197186
height_px=self.height_px,
198-
pixel_width_m=pixel_width_m,
199-
pixel_height_m=pixel_height_m,
200-
center_x_m=center_x_m,
201-
center_y_m=center_y_m,
187+
pixel_width_m=pixel_geometry.width_m,
188+
pixel_height_m=pixel_geometry.height_m,
189+
center_x_m=center.position_x_m,
190+
center_y_m=center.position_y_m,
202191
)
203192

204-
def get_layer(self, number: int) -> ObjectArrayType:
193+
def get_layer(self, number: int) -> ComplexArrayType:
205194
return self._array[number, :, :]
206195

207-
def get_layers_flattened(self) -> ObjectArrayType:
196+
def get_layers_flattened(self) -> ComplexArrayType:
208197
return numpy.prod(self._array, axis=-3)
209198

210199
@property

src/ptychodus/api/observer.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def _update(self, observable: Observable) -> None:
2222
class Observable:
2323
def __init__(self) -> None:
2424
self._observer_list: list[Observer] = list()
25+
self._block_notifications = False
26+
self._pending_notify = False
2527

2628
def add_observer(self, observer: Observer) -> None:
2729
if observer not in self._observer_list:
@@ -33,7 +35,18 @@ def remove_observer(self, observer: Observer) -> None:
3335
except ValueError:
3436
pass
3537

38+
def block_notifications(self, block: bool) -> None:
39+
self._block_notifications = block
40+
41+
if self._pending_notify:
42+
self.notify_observers()
43+
self._pending_notify = False
44+
3645
def notify_observers(self) -> None:
46+
if self._block_notifications:
47+
self._pending_notify = True
48+
return
49+
3750
for observer in self._observer_list:
3851
observer._update(self)
3952

src/ptychodus/api/patterns.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from .geometry import ImageExtent, PixelGeometry
1212
from .tree import SimpleTreeNode
1313

14-
BooleanArrayType: TypeAlias = numpy.typing.NDArray[numpy.bool_]
1514
PatternDataType: TypeAlias = numpy.typing.NDArray[numpy.integer[Any]]
1615
PatternIndexesType: TypeAlias = numpy.typing.NDArray[numpy.integer[Any]]
1716

0 commit comments

Comments
 (0)