API Reference

RegisterMismatch module

RegisterMismatchModule

The major types and functions exported are:

  • mismatch and mismatch!: compute the mismatch between two images
  • mismatch_apertures and mismatch_apertures!: compute apertured mismatch between two images
  • mismatch0: simple direct mismatch calculation with no shift
  • nanpad: pad the smaller image with NaNs
  • highpass: highpass filter an image
  • correctbias!: replace corrupted mismatch data (due to camera bias inhomogeneity) with imputed data
  • truncatenoise!: threshold mismatch computation to prevent problems from roundoff
  • aperture_grid: create a regular grid of apertures
  • allocate_mmarrays: create storage for output of mismatch_apertures!
  • CMStorage: a type that facilitates re-use of intermediate storage during registration computations

Core mismatch functions

RegisterMismatchCommon.mismatchFunction
mismatch(fixed, moving, maxshift; normalization=:intensity) -> MismatchArray

Compute the mismatch between fixed and moving for all integer shifts up to maxshift.

Returns a MismatchArray of NumDenom values indexed from -maxshift to +maxshift along each dimension. normalization may be :intensity (default) or :pixels.

Note

This function dispatches to a concrete implementation that must be provided by a downstream package such as RegisterMismatch (CPU) or RegisterMismatchCuda (GPU).

mm = mismatch([T], fixed, moving, maxshift; normalization=:intensity) -> MismatchArray

Compute the mismatch between fixed and moving as a function of translations up to size maxshift. Returns a MismatchArray indexed from -maxshift[d]:maxshift[d] in each dimension.

The optional type parameter T sets the element type (default: Float32 for integer or fixed-point images, eltype(fixed) otherwise). The normalization keyword controls the denominator: :intensity (default) normalizes by local image intensity; :pixels normalizes by pixel count.

fixed and moving must have the same size; pad with NaNs as needed (see nanpad).

Examples

julia> fixed = zeros(Float32, 10, 10); fixed[3:7, 3:7] .= 1;

julia> moving = circshift(fixed, (2, 1));

julia> mm = mismatch(fixed, moving, (3, 3));

julia> size(mm)
(7, 7)
RegisterMismatch.mismatch!Function
mismatch!(mm, cms, moving; normalization=:intensity) -> mm

Compute the mismatch as a function of shift, storing the result in mm. The fixed image must have been loaded into cms via fillfixed! before calling this function. cms is a CMStorage object.

See also mismatch for a higher-level interface that handles setup automatically.

RegisterMismatchCommon.mismatch_aperturesFunction
mms = mismatch_apertures([T], fixed, moving, gridsize, maxshift; normalization=:pixels, flags=FFTW.MEASURE, kwargs...)
mms = mismatch_apertures([T], fixed, moving, aperture_centers, aperture_width, maxshift; kwargs...)

Compute the mismatch between fixed and moving over a grid of aperture positions. Returns an array of MismatchArrays with the same shape as aperture_centers.

The first form divides the image into a gridsize regular grid, inferring aperture centers and widths automatically. The second form accepts explicit aperture_centers and aperture_width. In both cases, the mismatch within each aperture is computed for translations up to maxshift.

fixed and moving must have the same size; pad with NaNs as needed (see nanpad). The optional type parameter T sets the element type of the output (default: Float32 for integer or fixed-point images, eltype(fixed) otherwise).

The aperture_centers can be a vector-of-tuples, vector-of-vectors, or matrix (each point as a column); for rectangular grids use aperture_grid.

Examples

julia> using FFTW

julia> fixed = ones(Float32, 20, 20); moving = ones(Float32, 20, 20);

julia> mms = mismatch_apertures(fixed, moving, (2, 2), (3, 3); flags=FFTW.ESTIMATE);

julia> size(mms)
(2, 2)

julia> size(mms[1, 1])
(7, 7)
RegisterMismatch.mismatch_apertures!Function
mms = mismatch_apertures!(mms, fixed, moving, aperture_centers, cms; normalization=:pixels) -> mms

Compute the mismatch between fixed and moving over apertures at positions aperture_centers, storing results in mms. Working storage and parameters are provided by cms, a CMStorage object. mms must be an array of MismatchArrays with length equal to the number of aperture centers.

See also mismatch_apertures for a higher-level interface, and allocate_mmarrays for allocating mms.

RegisterMismatchCommon.mismatch0Function
mm0 = mismatch0(fixed, moving; normalization=:intensity) -> NumDenom{Float64}

Compute the "as-is" mismatch between fixed and moving at zero shift.

normalization may be :intensity (default, normalizes by vf² + vm²) or :pixels (normalizes by the count of finite pixel pairs). Returns a NumDenom{Float64}; the ratio mm0.num / mm0.denom gives the normalized mismatch.

See also: mismatch0(mms).

mm0 = mismatch0(mms::AbstractArray{<:MismatchArray}) -> NumDenom

Extract and sum the zero-shift NumDenom from each element of an aperture-wise array-of-MismatchArrays, returning a single NumDenom representing the overall as-is mismatch.

See also: mismatch0(fixed, moving).

Low-level reusable workflow

RegisterMismatch.CMStorageType
CMStorage{T}(undef, aperture_width, maxshift; flags=FFTW.ESTIMATE, timelimit=Inf, display=true) -> CMStorage{T}

Pre-allocate FFT working storage for mismatch computations over apertures of size aperture_width with translations up to maxshift. The element type T (e.g., Float32 or Float64) must be specified explicitly.

flags is an FFTW planning flag: FFTW.ESTIMATE (default, instant) or FFTW.MEASURE / FFTW.PATIENT (slower to plan but faster per call — worthwhile when the same aperture size is reused many times). timelimit caps planning time in seconds. Set display=false to suppress the planning progress message printed when flags != FFTW.ESTIMATE.

The typical low-level workflow is:

  1. Construct CMStorage once.
  2. Call fillfixed! to load the fixed image.
  3. Call mismatch! for each moving image.

Examples

julia> cms = CMStorage{Float32}(undef, (10, 10), (3, 3));

julia> eltype(cms)
Float32

julia> ndims(cms)
2
RegisterMismatch.fillfixed!Function
fillfixed!(cms::CMStorage, fixed) -> cms

Load the fixed image into cms, preparing it for mismatch computations. Call this once before calling mismatch! one or more times with different moving images.

This is the setup step performed internally by mismatch and mismatch_apertures. Use it directly when computing multiple mismatches against the same fixed image with different moving images.

Preprocessing

RegisterCore.highpassFunction
datahp = highpass([T], data, sigma)
highpass!(out, data, sigma)
highpass!(data, sigma)

Return (or write in-place) a high-pass–filtered version of data with negative values clamped to zero. The high-pass is computed by subtracting a Gaussian-smoothed copy of data (via ImageFiltering.jl), which gracefully handles NaN values.

sigma must be an iterable (e.g., a tuple or vector) with one width per dimension of data. To skip filtering along a particular axis, set the corresponding entry to Inf (the input is then returned as-is, converted to Array{T}, with no subtraction or clamping applied).

For highpass, the optional first argument T sets the element type of the output:

  • highpass(T, data, sigma) — allocates an output of element type T
  • highpass(data, sigma)T defaults to eltype(data) for AbstractFloat inputs, or Float32 for Integer/FixedPoint inputs

For highpass!, the output element type is eltype(out):

  • highpass!(out, data, sigma) — writes result into out; out and data may be distinct arrays (useful for pre-allocated buffers in hot loops)
  • highpass!(data, sigma) — filters data in-place

See also PreprocessSNF for a combined shot-noise–filtering preprocessor.

RegisterCore.highpass!Function
highpass!(out, data, sigma)
highpass!(data, sigma)

In-place variant of highpass. See that function for full documentation.

RegisterMismatchCommon.nanpadFunction
fixedpad, movingpad = nanpad(fixed, moving)

Pad fixed and/or moving with NaN so that both outputs have the same size.

If fixed and moving already have the same size they are returned unchanged. Otherwise each is expanded along every dimension to max(size(fixed, d), size(moving, d)), padding out-of-bounds positions with NaN.

The element type of the outputs is promote_type(eltype(fixed), eltype(moving)). For non-floating-point inputs this promotes to at least Float32 (the smallest float type that can represent NaN).

Post-processing

RegisterMismatchCommon.correctbias!Function
correctbias!(mm::MismatchArray[, w]) -> mm
correctbias!(mms::AbstractArray{<:MismatchArray}) -> mms

Replace "suspect" mismatch entries with values imputed from their neighbors.

Camera pixel-to-pixel bias and CMOS row/column noise create a spurious incentive to avoid shifts, because mm[i,j,...] is unreliable whenever i or j is zero (along the first two spatial dimensions). Suspect entries are identified via weight array w (zero = suspect; default computed by correctbias_weight), and replaced by a weighted average of adjacent non-suspect values.

Both forms modify their first argument in place and return it.

RegisterMismatchCommon.truncatenoise!Function
truncatenoise!(mm::AbstractArray{NumDenom{T}}, thresh) -> mm
truncatenoise!(mms::AbstractArray{<:MismatchArray}, thresh) -> mms

Zero out entries whose denom is ≤ thresh, replacing them with NumDenom(0, 0).

Both forms modify their first argument in place and return it.

Utilities

RegisterMismatchCommon.register_translateFunction
shift = register_translate(fixed, moving, maxshift[, thresh]) -> CartesianIndex

Compute the integer-valued translation that best aligns fixed and moving. All shifts up to maxshift (in each dimension) are considered.

thresh sets the minimum denom value for a mismatch entry to be considered reliable. Entries with denom ≤ thresh are excluded from the search. The default is 0.25 * maximum(denom), i.e., entries whose denominator falls below 25% of the peak denominator are excluded.

Returns a CartesianIndex of the best integer shift.

Note

Requires a concrete mismatch implementation to be loaded, e.g. from RegisterMismatch (CPU) or RegisterMismatchCuda (GPU).

RegisterMismatchCommon.aperture_gridFunction
ag = aperture_grid(ssize, gridsize) -> Array{NTuple{N,Float64},N}

Construct a uniformly-spaced grid of aperture centers for an image of spatial size ssize.

The returned array has size gridsize and element type NTuple{N,Float64}. Along each dimension, centers are linearly spaced between 1 and ssize[d] (inclusive). When gridsize[d] == 1, the single center is placed at the midpoint (ssize[d] + 1) / 2.

Examples

julia> ag = aperture_grid((256, 256), (4, 4));

julia> size(ag)
(4, 4)

julia> ag[1, 1]
(1.0, 1.0)

julia> ag[4, 4]
(256.0, 256.0)
RegisterMismatchCommon.allocate_mmarraysFunction
mms = allocate_mmarrays(T, gridsize::NTuple, maxshift) -> Array{MismatchArray{NumDenom{T},N},N}
mms = allocate_mmarrays(T, aperture_centers, maxshift) -> Array{MismatchArray{NumDenom{T},N}}

Allocate an array of MismatchArray{NumDenom{T}} objects, each with half-size maxshift.

gridsize form (recommended for regular grids): mms is an N-dimensional array of size gridsize.

aperture_centers form: mms matches the shape of aperture_centers. Centers may be provided as:

  • An array of tuples or AbstractVectors: mms has the same shape.
  • An AbstractMatrix{<:Real} where each column encodes one point: mms has shape size(aperture_centers)[2:end], i.e., one entry per column.
RegisterMismatchCommon.aperture_rangeFunction

rng = aperture_range(center, width) returns a tuple of UnitRange{Int}s that, for dimension d, is centered on center[d] and has width width[d].

RegisterMismatchCommon.each_pointFunction
iter = each_point(points)

Return an iterator over the points in points.

points may be:

  • An AbstractArray of tuples or AbstractVectors: each element is yielded as-is.
  • An AbstractArray{<:Real} where points are laid out along the first dimension (e.g., columns of a matrix): each point is yielded as a Vector.
RegisterMismatchCommon.set_FFTPRODFunction
set_FFTPROD(v)

Set the global list of prime factors used when selecting FFT-friendly array sizes. The default is [2, 3], meaning padded sizes are products of powers of 2 and 3.

This setting affects padsize and padranges.

Example

set_FFTPROD([2, 3, 5])  # allow sizes of the form 2^a * 3^b * 5^c

Types and helpers (RegisterCore)

RegisterCore.MismatchArrayType
MismatchArray{ND,N,A}

A CenterIndexedArray whose elements are NumDenom pairs, representing packed numerator/denominator mismatch data over a range of shifts.

The axes are centered at zero, so axes(D, k) runs from -maxshift(D)[k] to +maxshift(D)[k]; an index value of (i, j, ...) corresponds to a shift of (i, j, ...) pixels.

D = MismatchArray(num::AbstractArray, denom::AbstractArray)

Pack same-size arrays num and denom into a MismatchArray with centered axes of half-size size(num) .÷ 2. Element type is NumDenom{promote_type(eltype(num), eltype(denom))}.

D = MismatchArray(T, dims::Dims)
D = MismatchArray(T, dims::Integer...)

Allocate an uninitialized MismatchArray with element type NumDenom{T} and the given dimensions. Useful for pre-allocating output before filling with copyto!.

RegisterCore.NumDenomType
x = NumDenom(num, denom)

A packed (num, denom) pair with element type T. Access fields as x.num and x.denom. Arguments of type Gray are automatically unwrapped; mismatched numeric types are promoted.

NumDenom objects act as 2-vectors under arithmetic — addition and scalar multiplication operate component-wise on both fields:

nd1 + nd2  →  NumDenom(nd1.num + nd2.num, nd1.denom + nd2.denom)
nd1 - nd2  →  NumDenom(nd1.num - nd2.num, nd1.denom - nd2.denom)
c * nd     →  NumDenom(c*nd.num, c*nd.denom)
nd / c     →  NumDenom(nd.num/c, nd.denom/c)

This vector-space algebra (rather than ratio algebra) is intentional: it allows Interpolations.jl to interpolate numerator and denominator jointly without recomputing interpolation weights, enabling efficient aperture-windowed mismatch computations.

As a consequence, convert(Float64, ::NumDenom) is deliberately not defined, because the component-wise arithmetic breaks the ratio interpretation. Use ratio to convert to a scalar ratio.

RegisterCore.separateFunction
num, denom = separate(data::AbstractArray{NumDenom{T}})
num, denom = separate(mm::MismatchArray)
nums, denoms = separate(mma::AbstractArray{<:MismatchArray})

Split packed NumDenom data into separate numerator and denominator arrays.

  • For a plain AbstractArray{NumDenom{T}}, returns a pair of Array{T} with the same size and linear indexing as data.
  • For a MismatchArray, returns a pair of CenterIndexedArray{T}, preserving the centered axes (so index ranges correspond to shift values).
  • For an array of MismatchArrays, returns a pair of Array{CenterIndexedArray{T}}.
RegisterCore.ratioFunction
r = ratio(nd::NumDenom, thresh; fillval=NaN)
r = ratio(r::Real, thresh; fillval=NaN)

Return the ratio nd.num/nd.denom, or fillval (converted to the ratio type) when nd.denom < thresh. Setting thresh = 0 always returns the ratio.

The second form accepts a plain Real and returns it unchanged, regardless of thresh and fillvalthresh and fillval are silently ignored. This allows callers to handle both NumDenom and pre-computed ratio arrays uniformly without branching on the element type.

RegisterCore.maxshiftFunction
mxs = maxshift(D::MismatchArray)

Return the half-size of the MismatchArray D as a tuple of integers — i.e., the maximum shift (in pixels, per dimension) that was searched when computing the mismatch. Equivalent to D.halfsize.

RegisterCore.mismatcharraysFunction
mms = mismatcharrays(nums, denom::AbstractArray{<:Number})
mms = mismatcharrays(nums, denoms::AbstractArray{<:AbstractArray})

Pack array-of-arrays (num, denom) pairs into an Array{MismatchArray}.

The first form uses the same denom array for every entry in nums. The second form pairs each nums[i] with the corresponding denoms[i]; nums and denoms must have the same size.

Returns an Array{MismatchArray} with the same shape as nums.

Indexing helpers (RegisterCore)

RegisterCore.argmin_mismatchFunction
index = argmin_mismatch(numdenom::MismatchArray, thresh::Real)
index = argmin_mismatch(r::CenterIndexedArray{<:Number})

Return the CartesianIndex of the minimum mismatch, excluding edge points.

The first form operates on a MismatchArray: it computes num/denom at each interior point and finds the minimum among points where denom > thresh. The second form operates on a pre-computed ratio array r of plain numbers and finds the interior minimum unconditionally.

"Edge points" are the outermost index value on each side of every dimension; they are always excluded from consideration.

If no valid point is found (e.g., all denom ≤ thresh), returns a zero CartesianIndex — check for this case before using the result as an array index.

RegisterCore.paddedviewFunction
Apad = paddedview(A::SubArray)

Return a SubArray of A's parent that extends to the full parent size along every dimension of A that was indexed by a UnitRange. Dimensions indexed by a scalar are still dropped (as usual for SubArray), and dimensions indexed by a Slice are unchanged.

Only Slice, Real, and UnitRange index types are supported; any other index type throws an error.

See also trimmedview.

RegisterCore.trimmedviewFunction
B = trimmedview(Bpad::AbstractArray, A::SubArray)

Return a view B of Bpad with axes(B) == axes(A).

Bpad must be an AbstractArray with the same size as paddedview(A) — typically it is the result of an operation applied to paddedview(A). Dimensions of A that were indexed by a scalar are skipped (they are dropped in A); all other dimensions are sliced with the original index range from A.

See also paddedview.

RegisterCore.ColonFunType
ColonFun()(i::Int) -> Colon()

A callable singleton that returns Colon() for any integer argument. Used in place of Colon() directly where type-stable, composable slicing is needed (e.g., constructing index tuples programmatically).

Preprocessing (RegisterCore)

RegisterCore.PreprocessSNFType
pp = PreprocessSNF(bias, sigmalp, sigmahp)

Construct a shot-noise–filtered preprocessor. Call it as pp(img) to preprocess an image. All arguments are stored as Float32 (or Vector{Float32}), so Float64 inputs are silently converted.

The "SNF" name stands for "shot-noise filtered": this preprocessor is designed for photon-counting (Poisson) data, where the variance equals the mean.

Processing steps:

  1. Subtract bias and clamp to zero: A′ = max(0, img - bias)
  2. Square-root transform to stabilize variance: A″ = √A′
  3. High-pass filter with Gaussian width sigmahp (subtracts a low-pass Gaussian)
  4. Low-pass filter with Gaussian width sigmalp

sigmalp and sigmahp must each be a vector of length ndims(img). Pass sigmalp = zeros(ndims(img)) to skip low-pass filtering; pass sigmahp = fill(Inf, ndims(img)) to skip high-pass filtering.

When called on a SubArray, pp extends the view to the full parent (via paddedview) before processing, then trims the result back to the original index range (via trimmedview). This preserves any padding context around the sub-region.

If ImageMetadata is loaded, pp also accepts ImageMeta arrays and propagates image properties to the output.

Type aliases (RegisterMismatchCommon)

RegisterMismatchCommon.DimsLikeType
const DimsLike = Union{AbstractVector{Int}, Dims}

Type alias accepted wherever dimension sizes can be passed.

Note

Prefer Dims tuples (e.g. (128, 128)) over AbstractVector{Int} where possible; tuple inputs carry dimensionality in the type, which enables more precise dispatch and avoids runtime length checks.

RegisterMismatchCommon.WidthLikeType
const WidthLike = Union{AbstractVector, Tuple}

Type alias for values that specify aperture widths — accepted as either a Tuple or an AbstractVector.

Internal helpers

RegisterMismatchCommon.checksize_maxshiftFunction
checksize_maxshift(A, maxshift)

Validate that array A has the size expected for a mismatch array with the given maxshift. Checks that size(A, d) == 2maxshift[d] + 1 for every dimension d.

Throws an ErrorException on failure; returns nothing on success.

RegisterMismatchCommon.default_aperture_widthFunction
aperturesize = default_aperture_width(img, gridsize[, overlap]) -> NTuple{N,Float64}

Compute the aperture width for a regularly-spaced grid of aperture centers with size gridsize over image img.

overlap is a DimsLike giving the number of pixels by which adjacent apertures overlap along each spatial dimension; it defaults to zero in every dimension. For non-negative overlap, the collection of apertures provides full coverage of the image.

Returns a Tuple of Float64 widths, one per spatial dimension.

RegisterMismatchCommon.padrangesFunction
padranges(blocksize, maxshift) -> Vector{UnitRange{Int}}

Compute padded index ranges for an FFT-based cross-correlation.

For each dimension d, returns a UnitRange{Int} that covers the block 1:blocksize[d] and is extended symmetrically by maxshift[d], then rounded up to an FFT-friendly length via padsize. Ranges start at 1 - maxshift[d] to accommodate negative shifts.

RegisterMismatchCommon.padsizeFunction
padsize(blocksize, maxshift, dim) -> Int
padsize(blocksize::Dims, maxshift::Dims) -> Dims

Compute the padded FFT size.

The single-dimension form returns the smallest integer ≥ blocksize + 2maxshift that is efficient for FFT computation: a power of 2 along dimension 1, and a product of FFTPROD primes along other dimensions. When maxshift == 0, the input size is returned unchanged (that dimension will not be transformed).

The multi-dimension form applies padsize independently to each dimension and returns a tuple of padded sizes.

RegisterMismatchCommon.tovecFunction
tovec(v) -> AbstractVector

Convert v to an AbstractVector, returning v unchanged if it is already an AbstractVector (no copy), or converting a Tuple to a Vector.

RegisterMismatchCommonModule

RegisterMismatchCommon provides shared types, utilities, and aperture-based workflow helpers for image-registration mismatch computation. Concrete mismatch implementations are supplied by downstream packages: RegisterMismatch (CPU/FFTW) and RegisterMismatchCuda (GPU/CUFFT).

Main entry points:

Aperture workflow helpers: aperture_grid, allocate_mmarrays, default_aperture_width, aperture_range.

Post-processing: correctbias!, truncatenoise!, mismatch0.

RegisterCoreModule

Core types and utilities for image registration mismatch computations.

Types

  • NumDenom: a packed (numerator, denominator) pair; supports vector-space arithmetic for use with Interpolations.jl
  • MismatchArray: a CenterIndexedArray of NumDenom elements representing mismatch over a range of shifts
  • PreprocessSNF: shot-noise–filtering image preprocessor (bias subtract, square-root transform, band-pass filter)
  • ColonFun: callable singleton returning Colon(), for type-stable slicing

Functions

  • separate: unpack a NumDenom or MismatchArray into (num, denom) arrays
  • ratio: convert NumDenom to a scalar ratio, with threshold masking
  • maxshift: return the maximum-shift half-size of a MismatchArray
  • mismatcharrays: pack array-of-arrays pairs into an array of MismatchArrays
  • argmin_mismatch: find the shift index of the minimum mismatch
  • highpass / highpass!: high-pass filter an image (Gaussian-based, NaN-safe)
  • paddedview / trimmedview: extend/trim a SubArray to/from its parent