Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
bb3fa39
fix(serialize): round-trip tuple and list subclasses
gvarnavi Aug 14, 2026
b0dff8f
refactor(direct-ptycho): extract DirectPtychographyBase, and fix thre…
gvarnavi Aug 14, 2026
f3482a1
feat(direct-ptycho): add ShadowMontagePtychography, with tests for bo…
gvarnavi Aug 14, 2026
c8b12c1
refactor(direct-ptycho): derive object sampling from the field of view
gvarnavi Aug 14, 2026
bab7926
feat(direct-ptycho): default the montage to nearest-neighbour deposition
gvarnavi Aug 14, 2026
b5a877b
feat(direct-ptycho): position-dependent defocus for tilted samples
gvarnavi Aug 15, 2026
60fe1a3
feat(direct-ptycho): real-space SSB/OBF/MF via a truncated stencil
gvarnavi Aug 15, 2026
073fa37
feat(direct-ptycho): DirectPtychography.from_dataset3d for ungridded …
gvarnavi Aug 15, 2026
d3407d8
fix(direct-ptycho): fill regridding holes with the occupied mean, not…
gvarnavi Aug 15, 2026
5739d2f
feat(direct-ptycho): expose regridding diagnostics on from_dataset3d
gvarnavi Aug 15, 2026
2881b87
fix(direct-ptycho): warn that regridded ungridded scans cannot be ups…
gvarnavi Aug 15, 2026
345bfbd
feat(direct-ptycho): upsample_positions, so ungridded scans can unfold
gvarnavi Aug 15, 2026
5e60d42
refactor(direct-ptycho): a finer scan_sampling is the whole story
gvarnavi Aug 15, 2026
3294590
fix(direct-ptycho): make the ungridded and montage paths work on MPS
gvarnavi Aug 15, 2026
e57c81c
fix(imaging): correct the sub-pixel cross-correlation refinement
gvarnavi Aug 16, 2026
a888ca8
feat(direct-ptycho): pin the canvas to a window of the specimen
gvarnavi Aug 16, 2026
bd935c8
fix(direct-ptycho): score the whole patch, not its densest spots
gvarnavi Aug 16, 2026
1cfe888
feat(direct-ptycho): let the hyperparameter searches take a loss
gvarnavi Aug 16, 2026
772f5f7
feat(direct-ptycho): expose the map behind a defocus-gradient fit
gvarnavi Aug 16, 2026
0d435d0
feat(direct-ptycho): take a wavelength, not only an electron energy
gvarnavi Aug 20, 2026
271e854
feat(direct-ptycho): reconstruct from an empirical complex probe
gvarnavi Aug 20, 2026
529055d
perf(direct-ptycho): evaluate the montage convolutions by FFT
gvarnavi Aug 20, 2026
6d3c56c
fix(direct-ptycho): sample an empirical probe without interpolating
gvarnavi Aug 20, 2026
67ce1e9
fix(direct-ptycho): support rotation with an empirical probe
gvarnavi Aug 20, 2026
67b474f
feat(direct-ptycho): state the bright-field mask, and pin a wrapped c…
gvarnavi Aug 20, 2026
28cf4a8
feat(direct-ptycho): run iCoM on the montage, and with any probe
gvarnavi Aug 20, 2026
f285ae1
fix(direct-ptycho): warn when a wrapped canvas is smaller than the scan
gvarnavi Aug 20, 2026
a905cdf
feat(direct-ptycho): a truncated iCoM stencil is riCOM
gvarnavi Aug 20, 2026
bcab3f6
perf(direct-ptycho): sum the detector before convolving for riCOM
gvarnavi Aug 20, 2026
6504fcd
refactor(direct-ptycho): rename ShadowMontagePtychography to DirectPt…
gvarnavi Aug 21, 2026
921a100
docs(direct-ptycho): describe the class that exists now, not the one …
gvarnavi Aug 21, 2026
9f6bfb3
cleaning up docstrings
gvarnavi Aug 30, 2026
e908a24
Merge remote-tracking branch 'origin/dev' into diffractive_imaging
gvarnavi Aug 30, 2026
a9f5001
fix(diffractive-imaging): define OptimizationParameter once
gvarnavi Aug 30, 2026
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
4 changes: 3 additions & 1 deletion src/quantem/core/io/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,9 @@ def _serialize_container(

# Handle list/tuple containers
if isinstance(value, (list, tuple)):
group.attrs["_container_type"] = type(value).__name__
# normalize subclasses (torch.Size, namedtuples, ...) to their base container,
# since _deserialize_container only knows how to rebuild list/tuple
group.attrs["_container_type"] = "tuple" if isinstance(value, tuple) else "list"
# Fast-path: homogeneous numeric scalars → single ndarray
try:
is_all_numeric = len(value) > 0 and all(
Expand Down
37 changes: 26 additions & 11 deletions src/quantem/core/utils/imaging_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ def dft_upsample(
Manuel Guizar-Sicairos, Samuel T. Thurman, and James R. Fienup, "Efficient subpixel
image registration algorithms," Opt. Lett. 33, 156-158 (2008).
http://www.sciencedirect.com/science/article/pii/S0045790612000778

Evaluates the inverse transform of ``F`` on a ``(2*du+1)`` square grid of spacing
``1/up``, centered on ``shift``, where ``du = ceil(1.5 * up)``. The center tap is
therefore index ``du``, not ``up``.

Parameters
----------
F : ndarray
Fourier-domain array, in FFT (unshifted) order.
up : int
Upsampling factor. The output samples ``shift + arange(-du, du+1) / up``.
shift : tuple of float
Position, in pixels of the *real-space* image, to center the fine grid on.
"""
if device == "gpu":
import cupy as cp # type: ignore
Expand All @@ -33,17 +46,17 @@ def dft_upsample(

M, N = F.shape
du = np.ceil(1.5 * up).astype(int)
row = np.arange(-du, du + 1)
col = np.arange(-du, du + 1)
r_shift = shift[0] - M // 2
c_shift = shift[1] - N // 2
span = np.arange(-du, du + 1)

kern_row = np.exp(
-2j * np.pi / (M * up) * np.outer(row, xp.fft.ifftshift(xp.arange(M)) - M // 2 + r_shift)
)
kern_col = np.exp(
-2j * np.pi / (N * up) * np.outer(xp.fft.ifftshift(xp.arange(N)) - N // 2 + c_shift, col)
)
# frequency of FFT bin m, i.e. `ifftshift` of the centered ramp
freq_row = xp.fft.ifftshift(xp.arange(M)) - M // 2
freq_col = xp.fft.ifftshift(xp.arange(N)) - N // 2

# The offset re-centers the *output* sampling positions -- `shift + span/up` -- rather
# than translating the input frequencies, and the sign is the inverse transform's, so
# that this agrees with `ifft2` where the two grids coincide.
kern_row = xp.exp(2j * np.pi / (M * up) * np.outer(span + up * shift[0], freq_row))
kern_col = xp.exp(2j * np.pi / (N * up) * np.outer(freq_col, span + up * shift[1]))
return xp.real(kern_row @ F @ kern_col)


Expand Down Expand Up @@ -142,7 +155,9 @@ def parabolic_peak(v):
except (IndexError, ValueError):
dxf = dyf = 0.0

shifts = np.array([x0, y0]) + (np.array(peak) - upsample_factor) / upsample_factor
# the fine grid is centered on its middle tap, `ceil(1.5 * upsample_factor)`
center = np.ceil(1.5 * upsample_factor).astype(int)
shifts = np.array([x0, y0]) + (np.array(peak) - center) / upsample_factor
shifts += np.array([dxf, dyf]) / upsample_factor

shifts = (shifts + 0.5 * np.array(cc.shape)) % cc.shape - 0.5 * np.array(cc.shape)
Expand Down
13 changes: 13 additions & 0 deletions src/quantem/diffractive_imaging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,25 @@
from quantem.diffractive_imaging.complex_probe import (
real_space_probe as real_space_probe,
fourier_space_probe as fourier_space_probe,
FourierProbe as FourierProbe,
)

from quantem.diffractive_imaging.direct_ptychography import (
DirectPtychography as DirectPtychography,
)

from quantem.diffractive_imaging.direct_ptychography_base import (
OptimizationParameter as OptimizationParameter,
)

from quantem.diffractive_imaging.direct_ptychography_montage import (
DirectPtychographyMontage as DirectPtychographyMontage,
)

from quantem.diffractive_imaging.direct_ptycho_utils import (
estimate_frame_drift as estimate_frame_drift,
)

from quantem.diffractive_imaging.origin_models import (
CenterOfMassOriginModel as CenterOfMassOriginModel,
)
Loading