Skip to content

Commit cdfac00

Browse files
committed
Add scan and tilt plots
1 parent 2c2df83 commit cdfac00

3 files changed

Lines changed: 75 additions & 13 deletions

File tree

phaser/engines/common/output.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ def output_images(state: ReconsState, out_dir: Path, options: SaveOptions):
1717
if ty not in _SAVE_FUNCS:
1818
raise ValueError(f"Unknown image type '{ty}'")
1919

20+
ext = options.plot_ext if ty in _PLOT_FUNCS else 'tiff'
2021
try:
2122
out_name = options.img_fmt.format(
22-
type=ty, iter=state.iter,
23+
type=ty, iter=state.iter, ext=ext
2324
)
2425
out_path = out_dir / out_name
2526
except KeyError as e:
@@ -175,6 +176,60 @@ def _save_object_mag(state: ReconsState, out_path: Path, options: SaveOptions, s
175176
w.write(obj_mag, **write_opts)
176177

177178

179+
def _plot_scan(state: ReconsState, out_path: Path, options: SaveOptions):
180+
from matplotlib import pyplot
181+
fig, ax = pyplot.subplots(figsize=(4, 4), dpi=options.plot_dpi, constrained_layout=True)
182+
183+
ax.set_aspect(1.)
184+
[l, r, b, t] = state.object.sampling.mpl_extent()
185+
ax.set_xlim(l, r)
186+
ax.set_ylim(b, t)
187+
188+
scan = to_numpy(state.scan)
189+
i = numpy.arange(scan[..., 0].size)
190+
ax.scatter(scan[..., 1].ravel(), scan[..., 0].ravel(), c=i, s=0.2, cmap='plasma')
191+
192+
fig.savefig(out_path)
193+
pyplot.close(fig)
194+
195+
196+
def _plot_tilt(state: ReconsState, out_path: Path, options: SaveOptions):
197+
from matplotlib import pyplot
198+
199+
fig, ax = pyplot.subplots(figsize=(4, 4), dpi=options.plot_dpi, constrained_layout=True)
200+
201+
ax.set_aspect(1.)
202+
[l, r, b, t] = state.object.sampling.mpl_extent()
203+
ax.set_xlim(l, r)
204+
ax.set_ylim(b, t)
205+
206+
scan = to_numpy(state.scan)
207+
tilt = to_numpy(state.tilt)
208+
tilt = tilt[..., 1] + tilt[..., 0]*1.j
209+
max_tilt = max(numpy.max(numpy.abs(tilt)), 1.0) # at least 1 mrad
210+
c = colorize_complex(tilt / max_tilt, amp=True, rescale=False)
211+
ax.scatter(scan[..., 1].ravel(), scan[..., 0].ravel(), c=c, s=0.2)
212+
213+
fig.draw_without_rendering()
214+
trans = ax.transAxes + fig.transFigure.inverted()
215+
legend_ax_max = trans.transform([0.95, 0.02])
216+
legend_ax_size = (0.1, 0.1)
217+
legend_ax = fig.add_axes((legend_ax_max[0] - legend_ax_size[0], legend_ax_max[1], *legend_ax_size), projection='polar')
218+
219+
legend_ax.set_rmax(max_tilt) # type: ignore
220+
legend_ax.set_theta_direction(-1) # type: ignore
221+
legend_ax.set_axis_off()
222+
223+
thetas = numpy.linspace(0., 2*numpy.pi, 70)
224+
rs = numpy.concatenate([[0.0], numpy.geomspace(0.1, 1.0, 30)])
225+
rr, tt = numpy.meshgrid(rs, thetas, indexing='ij')
226+
c2 = colorize_complex(rr * numpy.exp(1.j * tt), rescale=False)
227+
legend_ax.pcolormesh(tt, rr * max_tilt, c2)
228+
legend_ax.text(-numpy.pi/2., max_tilt * 1.05, f"{max_tilt:.1f} mrad", ha='center', va='bottom', size='small')
229+
230+
fig.savefig(out_path)
231+
pyplot.close(fig)
232+
178233

179234
_SAVE_FUNCS: t.Dict[str, t.Callable[[ReconsState, Path, SaveOptions], t.Any]] = {
180235
'probe': _save_probe,
@@ -185,4 +240,8 @@ def _save_object_mag(state: ReconsState, out_path: Path, options: SaveOptions, s
185240
'object_phase_sum': partial(_save_object_phase, stack=False),
186241
'object_mag_stack': partial(_save_object_mag, stack=True),
187242
'object_mag_sum': partial(_save_object_mag, stack=False),
188-
}
243+
'scan': _plot_scan,
244+
'tilt': _plot_tilt,
245+
}
246+
# save functions with special handling of file extensions
247+
_PLOT_FUNCS: t.Set[str] = {'scan', 'tilt'}

phaser/plan.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
'probe', 'probe_mag', 'probe_recip', 'probe_recip_mag',
1313
'object_phase_stack', 'object_phase_sum',
1414
'object_mag_stack', 'object_mag_sum',
15+
'scan', 'tilt',
1516
]
1617

1718

@@ -27,11 +28,15 @@ class InitPlan(Dataclass, kw_only=True):
2728
class SaveOptions(Dataclass, kw_only=True):
2829
images: t.Tuple[SaveType, ...] = ('probe', 'object_phase_stack')
2930
crop_roi: bool = True
30-
unwrap_phase: bool = True
31+
unwrap_phase: bool = False
3132
img_dtype: t.Literal['float', '8bit', '16bit', '32bit'] = '16bit'
3233

34+
plot_ext: str = "svg"
35+
"""Extension to use for matplotlib savefig"""
36+
plot_dpi: int = 300
37+
3338
out_dir: str = "{name}"
34-
img_fmt: str = "{type}_iter{iter.total_iter}.tiff"
39+
img_fmt: str = "{type}_iter{iter.total_iter}.{ext}"
3540
hdf5_fmt: str = "iter{iter.total_iter}.h5"
3641

3742

phaser/utils/image.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,21 @@ def remove_linear_ramp(
5454
return output
5555

5656

57-
def colorize_complex(vals: ArrayLike, magnitude_only=False) -> NDArray[numpy.floating]:
57+
def colorize_complex(vals: ArrayLike, amp: bool = False, rescale: bool = True) -> NDArray[numpy.floating]:
5858
"""Colorize a ndarray of complex values as rgb."""
5959
from matplotlib.colors import hsv_to_rgb
6060
xp = get_array_module(vals)
6161

6262
vals = xp.asarray(vals, dtype=numpy.complexfloating)
63-
mag = abs2(vals)
64-
arg = xp.angle(vals)
65-
max_mag = xp.max(mag)
6663

67-
if magnitude_only:
68-
return mag
64+
v = xp.abs(vals) if amp else abs2(vals)
65+
if rescale:
66+
v /= xp.max(v)
67+
arg = xp.angle(vals)
6968

7069
h = (arg + numpy.pi) / (2*numpy.pi)
71-
s = 0.85 * xp.ones_like(mag)
72-
v = mag / max_mag
73-
return hsv_to_rgb(to_numpy(xp.stack((h, s, v), axis=-1)))
70+
s = 0.85 * xp.ones_like(v)
71+
return xp.clip(hsv_to_rgb(to_numpy(xp.stack((h, s, v), axis=-1))), 0.0, 1.0)
7472

7573

7674
def scale_to_integral_type(

0 commit comments

Comments
 (0)