API Reference

Main Interface

SMLMDriftCorrection.driftcorrect — Function
driftcorrect(smld; kwargs...) -> (corrected_smld, info)
driftcorrect(smld, config::DriftConfig) -> (corrected_smld, info)

Main interface for drift correction. Uses Legendre polynomial model with entropy-based cost function and adaptive KDTree neighbor building.

Arguments

  • smld: SMLD structure containing (X, Y) or (X, Y, Z) localization coordinates (μm)
  • config: Optional DriftConfig struct (alternative to keyword arguments)

Keyword Arguments

  • quality=:singlepass: Quality tier (:fft, :singlepass, :iterative)
  • degree=2: Polynomial degree for intra-dataset drift model
  • dataset_mode=:registered: selects the inter-dataset algorithm (quality=:fft ignores it):
    • :registered: datasets are independent acquisitions (CC-primary inter alignment)
    • :continuous: one acquisition split into files or chunks (consecutive-chunk CC)
  • chunk_frames=0: For continuous mode, split each dataset into chunks of this many frames
  • n_chunks=0: Alternative to chunk_frames - specify number of chunks per dataset
  • maxn=100: Maximum number of neighbors for entropy calculation
  • max_iterations=10: Iterations per attempt for :iterative mode; a run that has not converged is retried from its current model up to 3 times
  • convergence_tol=0.001: Convergence tolerance (μm) for :iterative mode
  • warm_start=nothing: Previous model for warm starting optimization
  • verbose=0: Verbosity level (0=quiet, 1=info, 2=debug)
  • auto_roi=false: Set to true for faster (but slightly less accurate) estimation using a dense ROI subset
  • σ_loc=0.010: Typical localization precision (μm) for ROI sizing
  • σ_target=0.001: Target drift precision (μm) for ROI sizing
  • roi_safety_factor=4.0: Safety multiplier for required localizations
  • shift_scale=1.0: Has no effect: no dataset mode reads it (registered mode stopped using it as an L2 inter-shift prior in 0.2.7). Kept so existing calls and configs still construct; a non-default value logs a warning.

Quality Tiers

  • :fft: Fast cross-correlation only (~10x faster, less accurate)
  • :singlepass: One pass of intra then inter correction (default). Intra is threaded per dataset; registered inter is threaded per dataset (continuous CC seeding is sequential, its refine threaded then sequential)
  • :iterative: Iterates intra↔inter until both inter and intra parameter changes fall below convergence_tol, or the entropy cost plateaus (relative improvement < 1e-4, both dataset modes)

Returns

Tuple (corrected_smld, info) where info::DriftInfo contains:

  • model: Fitted drift model (LegendrePolynomial)
  • elapsed_s: Wall time in seconds
  • backend: Computation backend (:cpu)
  • iterations: Number of iterations completed
  • converged: Whether convergence was achieved
  • entropy: Final entropy value; scales with the number of localizations. Divide by length(smld.emitters) for a size-independent number
  • history: Entropy after the singlepass start and after each iteration (:iterative only; empty for :fft and :singlepass)
  • residual_correlation: Position-frame correlation diagnostic on corrected data; see position_frame_correlation

Example

# Basic usage
(smld_corrected, info) = driftcorrect(smld)

# Using DriftConfig
config = DriftConfig(; quality=:iterative, degree=3, verbose=1)
(smld_corrected, info) = driftcorrect(smld, config)

# Fast FFT-only mode
(smld_corrected, info) = driftcorrect(smld; quality=:fft)

# Warm start from previous result
(smld1, info1) = driftcorrect(smld1; degree=2)
(smld2, info2) = driftcorrect(smld2; warm_start=info1.model)

# Extract drift trajectory for plotting
traj = drift_trajectory(info.model)
source
driftcorrect(smld, info::DriftInfo; kwargs...) -> (corrected_smld, info)

Continue drift correction from a previous result using the model from info. Pass the original, uncorrected smld. Continuation reuses the original run's dataset_mode and chunking, including for chunked continuous results. Corrections preserve the input's dataset and frame numbering.

Keyword Arguments

  • dataset_mode=info.dataset_mode: Dataset mode (:registered or :continuous)
  • chunk_frames=info.chunk_frames: Frames per chunk for continuous mode
  • n_chunks=info.n_chunks: Chunks per dataset for continuous mode
  • max_iterations=10: Additional iterations to run
  • convergence_tol=0.001: Convergence tolerance (μm)
  • maxn=100: Maximum neighbors for entropy calculation
  • verbose=0: Verbosity level
source

See Configuration for full documentation of DriftConfig and DriftInfo.

SMLD Alignment

SMLMDriftCorrection.align_smld — Function
align_smld(smlds; kwargs...) -> (Vector{<:SMLD}, AlignInfo)
align_smld(smlds, config::AlignConfig) -> (Vector{<:SMLD}, AlignInfo)

Align a vector of independent SMLD structures to a common reference (the first). Each SMLD is assumed to be an independent acquisition of the same FOV/structure.

Arguments

  • smlds: Vector of SMLD structures (>= 2 required)

Keyword Arguments

  • method=:entropy: :entropy (CC initial guess + entropy refinement) or :fft (CC only)
  • transform=:shift: :shift (translation only) or :affine (rotation + scale + translation)
  • maxn=100: Maximum neighbors for entropy calculation
  • histbinsize=0.05: Histogram bin size (μm) for cross-correlation
  • verbose=0: Verbosity: 0=quiet, 1=info

transform=:affine is 2D only (3D data throws) and ignores method: it always runs the shift-field cross-correlation method and needs about 200 localizations per tile (min_locs=200).

Returns

Tuple (aligned_smlds, info::AlignInfo) where:

  • aligned_smlds: Vector of aligned SMLDs (first is unchanged)
  • info.shifts[i]: translation component for smlds[i] (shifts[1] = zeros)
  • info.transforms[i]: diagnostic transform (ShiftTransform for :shift; a similarity fit AffineTransform2D for :affine).
  • info.diagnostic: nothing for :shift; for :affine a NamedTuple with affine_coeffs[i] = (a, b, c, d, e, f) and global_shifts[i] — the exact per-dataset correction applied (summed across internal passes). The similarity stored in info.transforms[i] is only a best-fit approximation of this general affine.

Example

# Shift-only (default)
(aligned, info) = align_smld(smlds)
info.shifts  # [zeros(2), [dx2, dy2], [dx3, dy3], ...]

# Affine (rotation + scale + translation)
(aligned, info) = align_smld(smlds; transform=:affine)
info.transforms[2]        # AffineTransform2D(θ, scale, tx, ty) — similarity approximation
info.diagnostic.affine_coeffs[2]  # (a, b, c, d, e, f) — actual correction applied
source
SMLMDriftCorrection.AffineTransform2D — Type
AffineTransform2D <: AbstractAlignTransform

2D similarity transform: rotation + uniform scale + translation. Parameters: [θ_rot, scale, tx, ty] Transform: [x'; y'] = s * R(θ) * [x; y] + [tx; ty]

align_smld(..., transform=:affine) stores an approximation

align_smld with transform=:affine applies a general 2D affine correction recovered from a grid of local CC shifts (independent x/y scale + shear is possible), and then reports a best-fit similarity in info.transforms[i]. The exact per-dataset 6-parameter affine that was applied is available in info.diagnostic.affine_coeffs[i] as (a, b, c, d, e, f), representing the correction Δx = a·x + b·y + c, Δy = d·x + e·y + f.

source

See Configuration for full documentation of AlignConfig and AlignInfo.

Utility Functions

SMLMDriftCorrection.filter_emitters — Function
filter_emitters(smld, keep) -> smld

Select the emitters indexed by keep from the SMLD structure, returning a new SMLD with only those emitters. keep may be a single positive integer, a range, a vector of integers, or a BitVector mask.

Example

smld_sub = filter_emitters(smld, [1])
length(smld_sub.emitters)  # 1
source
SMLMDriftCorrection.drift_trajectory — Function
drift_trajectory(model; dataset=nothing, frames=nothing, cumulative=false)

Extract drift trajectory from a drift model for plotting.

Arguments

  • model: Polynomial or LegendrePolynomial drift model
  • dataset: specific dataset to extract (default: all datasets)
  • frames: frame range to evaluate (default: 1:n_frames for each dataset)
  • cumulative: if true, chain datasets end-to-end showing total accumulated drift. Useful for registered acquisitions where you want to visualize what drift would look like without stage registration. Default: false.

With cumulative=false each point is the total drift of its dataset at that frame, inter[ds] + intra[ds](frame): the inter-dataset shift is included, so the extrema of x/y mix the inter shift with the intra-dataset drift. For the intra-dataset drift alone, subtract model.inter[ds].dm (or evaluate model.intra[ds]).

Returns

NamedTuple with fields ready for plotting:

  • frames: frame numbers (global if multiple datasets)
  • x: x-drift values (μm)
  • y: y-drift values (μm)
  • z: z-drift values (μm) - only present for 3D models
  • dataset: dataset index for each point (useful for coloring)

Example

(smld_corrected, info) = driftcorrect(smld; dataset_mode=:registered)
traj = drift_trajectory(info.model)  # Each dataset relative to ds1

# Cumulative view - chains datasets end-to-end
traj_cumul = drift_trajectory(info.model; cumulative=true)
plot(traj_cumul.frames, traj_cumul.x, label="X drift (cumulative)")
source
SMLMDriftCorrection.position_frame_correlation — Function
position_frame_correlation(smld::SMLD; K::Int=20, mode::Symbol=:intra)

Diagnose residual drift by correlating local position residuals with frame number (intra mode) or dataset index (inter mode).

For each localization, the K nearest spatial neighbors are found and a residual is computed as pos_i - mean(neighbor_positions). If drift correction is effective, these residuals should be uncorrelated with frame number (intra mode) or dataset index (inter mode). A nonzero correlation indicates remaining systematic drift.

Arguments

  • smld::SMLD: localization dataset (2D or 3D, single or multi-dataset)
  • K::Int=20: number of spatial neighbors used to estimate the local mean
  • mode::Symbol=:intra: :intra runs per-dataset (residual vs frame); :inter runs across all datasets combined (residual vs dataset index)

Returns

For mode = :intra, a NamedTuple:

(per_dataset = [(dataset, n_locs, corr_x, corr_y, corr_z,
                 residuals_x, residuals_y, residuals_z, frames), ...],
 summary = (mean_abs_corr_x, mean_abs_corr_y, mean_abs_corr_z),
 mode = :intra, K = K)

For mode = :inter, a NamedTuple:

(corr_x, corr_y, corr_z,
 residuals_x, residuals_y, residuals_z,
 dataset_indices,
 mode = :inter, K = K)

The corr_z and residuals_z fields are nothing for 2D data.

Example

(smld_corrected, info) = driftcorrect(smld)
diag = position_frame_correlation(smld_corrected; K=20, mode=:intra)
diag.summary.mean_abs_corr_x  # small values indicate good correction
source

Drift Models

SMLMDriftCorrection.LegendrePolynomial — Type
LegendrePolynomial

Combined intra + inter Legendre drift model. Contains one IntraLegendre per dataset plus inter-dataset shifts. The n_frames field stores the valid frame range for drift evaluation.

source
SMLMDriftCorrection.LegendrePoly1D — Type
LegendrePoly1D

Univariate Legendre polynomial drift model for a single spatial dimension. Coefficients are for P1(t), P2(t), ..., P_degree(t) where t is normalized to [-1, 1].

Note: P_0 (constant) is NOT included because the inter-dataset shift already handles global offsets. This matches the convention for standard Polynomial1D which uses t^1, t^2, ... (no constant term).

The key advantage over standard polynomials: orthogonal basis functions mean each coefficient captures independent variation, leading to better-conditioned optimization.

source

Drift Application

SMLMDriftCorrection.applydrift — Function

Apply drift to coordinate using Legendre polynomial model. Uses P1 through Pdegree (no constant P_0 term).

source

Apply drift to simulated data.

source

applydrift(smld, driftmodel) -> smld

Applies a drift model to SMLM data (for simulation/testing).

source

Entropy Functions

SMLMDriftCorrection.entropy_HD — Function

H_i(D) is the entropy of the distribution p(r), where D = {d(1),...,d(L)} is the drift at all frames (1 to L). The quantity below is Gaussian Mixture Model single components summed over all localizations provided (total Shannon entropy, not per-point).

σx and σy are localization uncertainties.

Scale note

entropy_HD returns a sum of size ~N, while the neighbor divergence term used in entropy1_*/ub_entropy is averaged per-localization (out / N). The formula entropy_HD - out/N is therefore dominated by the H(D) term as N grows — by design, since entropy_HD is θ-independent and is only a constant offset for the optimizer. If you want a dataset-size-independent number for diagnostics, divide by length(σ_x).

source
SMLMDriftCorrection.ub_entropy — Function

Entropy upper bound based on maxn nearest neighbors of each localization (2D).

Returns entropy_HD(σ_x, σ_y) - out/N, where the first term is a sum over N localizations and the second is a per-localization average. The result scales with N through the entropy_HD term; divide by length(x) for per-locus bits.

Arguments

  • x, y: Vectors of localization positions
  • σx, σy: Vectors of localization uncertainties
  • maxn: Maximum number of neighbors considered (default 200)
  • divmethod: Divergence method ("KL", "Symmetric", "Bhattacharyya", "Mahalanobis")
source

Entropy upper bound based on maxn nearest neighbors of each localization (3D).

source

Matrix interface for ub_entropy - extracts vectors and calls optimized version. Expects r and σ to be N×K matrices (points as rows, dimensions as columns).

source

Cross-Correlation

SMLMDriftCorrection.findshift — Function

Perform a cross-correlation between images representing localizations in two SMLD structures and compute the shift between the two original images. histbinsize is the size of the bins in the histogram image in the same units as the localization coordinates. maxcount, when given, clips every histogram bin to at most that many localizations before correlating, so the CC measures shared occupancy and one bright aggregate (thousands of localizations in a bin) cannot outweigh the structure; nothing (default) correlates raw counts. It must be a positive integer.

source
SMLMDriftCorrection.histimage2D — Function

Produce a histogram image from the localization coordinates x and y. x and y are in arbitrary units. ROI is [xmin, xmax, ymin, ymax] of the Region Of Interest in the same units as x and y. If not provided, these values are estimated from the coordinate data. histbinsize is the size of the bins of each coordinate in the same units.

source
SMLMDriftCorrection.crosscorr2D — Function

Compute the cross-correlation between two 2D images with zero-padding.

Zero-padding to 2x size eliminates cyclic wrap-around artifacts that cause false peaks at large shifts.

source