API Reference
SMLMDriftCorrection.AbstractAlignTransformSMLMDriftCorrection.AffineTransform2DSMLMDriftCorrection.AffineTransform3DSMLMDriftCorrection.IntraLegendreSMLMDriftCorrection.LegendrePoly1DSMLMDriftCorrection.LegendrePolynomialSMLMDriftCorrection.ShiftTransformSMLMDriftCorrection.align_smldSMLMDriftCorrection.applydriftSMLMDriftCorrection.correctdriftSMLMDriftCorrection.crosscorr2DSMLMDriftCorrection.drift_trajectorySMLMDriftCorrection.driftcorrectSMLMDriftCorrection.entropy_HDSMLMDriftCorrection.filter_emittersSMLMDriftCorrection.findshiftSMLMDriftCorrection.histimage2DSMLMDriftCorrection.position_frame_correlationSMLMDriftCorrection.ub_entropySMLMDriftCorrection.AlignConfigSMLMDriftCorrection.AlignInfoSMLMDriftCorrection.DriftConfigSMLMDriftCorrection.DriftInfo
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: OptionalDriftConfigstruct (alternative to keyword arguments)
Keyword Arguments
quality=:singlepass: Quality tier (:fft,:singlepass,:iterative)degree=2: Polynomial degree for intra-dataset drift modeldataset_mode=:registered: selects the inter-dataset algorithm (quality=:fftignores 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 framesn_chunks=0: Alternative to chunk_frames - specify number of chunks per datasetmaxn=100: Maximum number of neighbors for entropy calculationmax_iterations=10: Iterations per attempt for:iterativemode; a run that has not converged is retried from its current model up to 3 timesconvergence_tol=0.001: Convergence tolerance (μm) for:iterativemodewarm_start=nothing: Previous model for warm starting optimizationverbose=0: Verbosity level (0=quiet, 1=info, 2=debug)auto_roi=false: Set totruefor 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 sizingroi_safety_factor=4.0: Safety multiplier for required localizationsshift_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 belowconvergence_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 secondsbackend: Computation backend (:cpu)iterations: Number of iterations completedconverged: Whether convergence was achievedentropy: Final entropy value; scales with the number of localizations. Divide bylength(smld.emitters)for a size-independent numberhistory: Entropy after the singlepass start and after each iteration (:iterativeonly; empty for:fftand:singlepass)residual_correlation: Position-frame correlation diagnostic on corrected data; seeposition_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)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 (:registeredor:continuous)chunk_frames=info.chunk_frames: Frames per chunk for continuous moden_chunks=info.n_chunks: Chunks per dataset for continuous modemax_iterations=10: Additional iterations to runconvergence_tol=0.001: Convergence tolerance (μm)maxn=100: Maximum neighbors for entropy calculationverbose=0: Verbosity level
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 calculationhistbinsize=0.05: Histogram bin size (μm) for cross-correlationverbose=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 forsmlds[i](shifts[1]= zeros)info.transforms[i]: diagnostic transform (ShiftTransform for:shift; a similarity fitAffineTransform2Dfor:affine).info.diagnostic:nothingfor:shift; for:affinea NamedTuple withaffine_coeffs[i] = (a, b, c, d, e, f)andglobal_shifts[i]— the exact per-dataset correction applied (summed across internal passes). The similarity stored ininfo.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 appliedSMLMDriftCorrection.AbstractAlignTransform — Type
AbstractAlignTransformAbstract supertype for alignment transforms: ShiftTransform, AffineTransform2D, and AffineTransform3D.
SMLMDriftCorrection.ShiftTransform — Type
ShiftTransform <: AbstractAlignTransformPure translation alignment (current default behavior).
SMLMDriftCorrection.AffineTransform2D — Type
AffineTransform2D <: AbstractAlignTransform2D similarity transform: rotation + uniform scale + translation. Parameters: [θ_rot, scale, tx, ty] Transform: [x'; y'] = s * R(θ) * [x; y] + [tx; ty]
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.
SMLMDriftCorrection.AffineTransform3D — Type
AffineTransform3D <: AbstractAlignTransform3D similarity transform: Euler rotation + uniform scale + translation. Parameters: [θx, θy, θz, scale, tx, ty, tz]
See Configuration for full documentation of AlignConfig and AlignInfo.
Utility Functions
SMLMDriftCorrection.filter_emitters — Function
filter_emitters(smld, keep) -> smldSelect 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) # 1SMLMDriftCorrection.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 modeldataset: 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 modelsdataset: 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)")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 meanmode::Symbol=:intra::intraruns per-dataset (residual vs frame);:interruns 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 correctionDrift Models
SMLMDriftCorrection.LegendrePolynomial — Type
LegendrePolynomialCombined 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.
SMLMDriftCorrection.IntraLegendre — Type
IntraLegendreIntra-dataset Legendre drift model - wraps one LegendrePoly1D per spatial dimension.
SMLMDriftCorrection.LegendrePoly1D — Type
LegendrePoly1DUnivariate 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.
Drift Application
SMLMDriftCorrection.applydrift — Function
Apply drift to coordinate using Legendre polynomial model. Uses P1 through Pdegree (no constant P_0 term).
Apply drift to simulated data.
applydrift(smld, driftmodel) -> smld
Applies a drift model to SMLM data (for simulation/testing).
SMLMDriftCorrection.correctdrift — Function
Correct drift from coordinate using Legendre polynomial model. Uses P1 through Pdegree (no constant P_0 term).
Apply drift correction to drifted data.
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.
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).
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")
Entropy upper bound based on maxn nearest neighbors of each localization (3D).
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).
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.
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.
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.