Synthetic Data & Noise

Phantoms are functions $x \mapsto \sigma(x)$ that do not depend on a mesh. They are put on a discretization by conductivity (cell averages for piecewise constants). This makes it easy to simulate data on a finer mesh than the reconstruction mesh, with the same physical electrodes (transfer_electrodes), and so avoid the inverse crime:

coarse, fine = FerriteDiscretization(grid_coarse), FerriteDiscretization(grid_fine)
els  = angular_electrodes(coarse, 16; coverage = 0.5)
fm   = ForwardModel(coarse, CompleteElectrodeModel(els, 0.1))
fm_f = ForwardModel(fine, CompleteElectrodeModel(transfer_electrodes(coarse, els, fine), 0.1))

phantom = random_inclusions(rng; count = 1:3)            # or gaussian_random_field, image_phantom
currents = trigonometric_patterns(fm, 7)
noise = RelativeGaussianNoise(0.01)
sim = simulate_data(fine, fm_f, phantom, currents; noise, rng)

data = AdjointStateObjective(fm, currents, sim.data)
res = minimize(RegularizedObjective(data, 1e-4 => TotalVariationRegularizer(coarse; ε = 1e-2)),
               ones(ndofs_σ(coarse)), GaussNewton(); lower = 0.05,
               ftarget = discrepancy_target(data, noise))   # discrepancy principle

The discrepancy target only accounts for the instrument noise. When the modelling error (for example of a coarse reconstruction mesh) is larger than the noise, the data have to be explained beyond what the model can represent, and the reconstruction deteriorates. Compare the model error norm(sim.clean - forward_neumann(fm, conductivity(coarse, phantom), currents)[1]) with the noise level before choosing the target.

Noise models

ModularEIT.GaussianNoise — Type
GaussianNoise(s)

Additive white Gaussian noise with standard deviation s: a scalar, a vector with one standard deviation per measurement channel (data row), e.g. from an instrument specification, or a matrix with one per data entry (e.g. the noise of rotated data, see pattern_svd).

source
ModularEIT.RelativeGaussianNoise — Type
RelativeGaussianNoise(δ)

Additive Gaussian noise relative to the signal level: for every pattern (column) fᵢ with m entries, ηᵢ ~ N(0, (δ ‖fᵢ‖ / √m)² I), so that ‖ηᵢ‖ ≈ δ ‖fᵢ‖ (e.g. δ = 0.01 for 1 % noise).

source
ModularEIT.SourceMeterNoise — Type
SourceMeterNoise(source, meter)

Errors of the current source and of the voltmeter (or, for voltage-driven data, of the voltage source and the ammeter), each a GaussianNoise standard deviation (scalar or per channel): the applied inputs are inputs + source ε (projected back to zero net current for current patterns), the measurements F(applied) + meter ε. The reconstruction is given the nominal inputs. Needs the forward model, so it is applied by simulate_data.

source
ModularEIT.discrepancy_target — Function
discrepancy_target(obj::AdjointStateObjective, noise; τ = 1.1)

Target value of the objective for the discrepancy principle: τ² E[J(σ_true)], the expected misfit of exact data corrupted by noise, taken in the objective's metric (the whitening of its misfit and, for voltages, the removal of the weighted mean). Pass it as ftarget to minimize to stop when the data are explained to the noise level (τ slightly above 1). Relative noise levels are evaluated on the objective's (noisy) data.

source
discrepancy_target(obj::ApproximationErrorObjective; τ = 1.1)

τ² m / 2 for m residuals: the expected misfit of the whitened residual.

source
ModularEIT.perturb_boundary_operator — Function
perturb_boundary_operator(R, s; rng = Random.default_rng())

Operator-level noise for a discrete boundary operator (e.g. an estimated Neumann-to-Dirichlet matrix): R + s (Ê + Êᵀ)/2 with i.i.d. standard normal Ê. Symmetry is preserved, positive semidefiniteness is not (for large s).

source

Modelling errors

ModularEIT.perturb_contact_impedance — Function
perturb_contact_impedance(model::CompleteElectrodeModel, s; rng = Random.default_rng())

Complete electrode model with log-normally perturbed contact impedances zₗ exp(s εₗ), a modelling error for data simulation (the reconstruction keeps the nominal model).

source
ModularEIT.electrode_angles — Function
electrode_angles(L; offset = 0, jitter = 0, rng = Random.default_rng())

Centre angles offset + 2π(ℓ-1)/L + jitter εₗ of L electrodes, with Gaussian position errors of standard deviation jitter (radians); pass them as angles to angular_electrodes.

source
ModularEIT.transfer_electrodes — Function
transfer_electrodes(src, electrodes, dst)

The electrodes of src as electrodes of the discretization dst of the same domain (e.g. a finer mesh for simulated data). Back end contract.

source

Approximation-error model

The modelling error of the reconstruction model (coarser mesh, pixels, …) as a Gaussian, from samples simulated with both models (Kaipio–Somersalo), with a leave-one-out variance floor for the directions the samples do not span:

R = reduce(hcat, [residual!(zeros(m), obj_i, θ_i) for (obj_i, θ_i) in samples])  # noise-free fine data
ae = ApproximationError(R; noise = η)
aobj = ApproximationErrorObjective(obj, ae)
res = minimize(aobj, θ0, GaussNewton(; linear_solver = :cg); ftarget = discrepancy_target(aobj))
ModularEIT.ApproximationError — Type
ApproximationError(R; noise, floor = :leave_one_out, rtol = 1e-10)

Gaussian model N(μ, Γ) of the modelling error from samples: the columns of R are residuals of the reconstruction model at the true parameters of sample conductivities, for noise-free data of the accurate model (e.g. a finer mesh). noise is the standard deviation η of the measurement noise per residual entry. Stores the mean μ and the low-rank factor of the whitening C^{-1/2}, C = (η² + ν²) I + Γ̂ (see whiten), with the sample covariance Γ̂ and a variance floor ν² for the directions the samples do not span: estimated by leave-one-out (floor = :leave_one_out: the mean squared part of a sample outside the span of the other samples, per remaining dimension; conservative, since the span of the other samples is itself perturbed), or given as a number (floor = 0: none). Singular values below rtol times the largest are dropped.

source
ModularEIT.ApproximationErrorObjective — Type
ApproximationErrorObjective(obj, ae)

The least-squares objective obj with the modelling error of ae accounted for: residual C^{-1/2}(r - μ), so that the misfit of the true conductivity is of the size of the noise again. Values, gradients, Jacobians (explicit and matrix-free, jacobian_operator), column norms and Gram matrices, so that all Gauss–Newton variants apply. Stop by the discrepancy principle with discrepancy_target(obj; τ) (the whitened residual has unit variance per entry).

source
ModularEIT.discrepancy_target — Method
discrepancy_target(obj::ApproximationErrorObjective; τ = 1.1)

τ² m / 2 for m residuals: the expected misfit of the whitened residual.

source

Phantoms

ModularEIT.InclusionPhantom — Type
InclusionPhantom(background, inclusions)

Piecewise constant conductivity: background outside all inclusions, the value of the last inclusion containing x otherwise (later inclusions are painted on top). Callable: ph(x).

source
ModularEIT.random_inclusions — Function
random_inclusions(rng = Random.default_rng(); count = 1:3, domain = :disk, center = (0, 0),
                  radius = 1, bbox = (-1, 1, -1, 1), sizes = (0.1, 0.35), values = (0.2, 5.0),
                  background = 1, shapes = (:circle, :ellipse, :polygon), margin = 0.05,
                  separation = 0.05, maxtries = 1000)

Random InclusionPhantom: rand(rng, count) non-overlapping inclusions of random shape (circles, ellipses with random orientation and aspect ratio, star-shaped polygons with 3–7 vertices) inside the domain (:disk with center and radius, or :box with bbox = (xmin, xmax, ymin, ymax)). Sizes (bounding radii) are drawn from sizes relative to the domain half-width, values log-uniformly from values. Every inclusion keeps the distance margin from the boundary and separation from the others; an inclusion that cannot be placed in maxtries attempts is dropped.

source
ModularEIT.PixelFunction — Type
PixelFunction(values; bbox = (-1, 1, -1, 1), interpolation = :nearest)

Function on the rectangle bbox = (xmin, xmax, ymin, ymax) given by pixel values (row 1 at the top, as for to_image (Ferrite back end)), evaluated by :nearest pixel or :bilinear interpolation between pixel centres. Points outside the box take the value of the nearest border pixel.

source
ModularEIT.image_phantom — Function
image_phantom(img, σmin, σmax; bbox = (-1, 1, -1, 1), interpolation = :nearest)

Conductivity from a grayscale image with intensities in [0, 1], mapped affinely to [σmin, σmax] (0 < σmin < σmax, so the forward problem stays elliptic), as a PixelFunction on bbox.

source
ModularEIT.gaussian_random_field — Function
gaussian_random_field(rng = Random.default_rng(); bbox = (-1, 1, -1, 1), ℓ = 0.2, order = 2,
                      pixel = ℓ / 10, pad = 3ℓ)

Sample of a Gaussian random field with covariance (1 - ℓ²Δ)^(-order) (Matérn type: length scale ℓ, smoothness ν = order - 1 in 2D; order = 2 is the Whittle field, order = 1.5 the exponential covariance), scaled to unit marginal variance on average over bbox. Sampled on a pixel grid (size pixel) over bbox enlarged by pad in the cosine basis of the Neumann Laplacian, which keeps the reflecting boundary away from the box. Returns a bilinear PixelFunction; combine with lognormal_phantom or levelset_phantom.

source
ModularEIT.levelset_phantom — Function
levelset_phantom(field; level = 0, inside = 2, outside = 1)

Two-phase conductivity: inside where f(x) > level, outside elsewhere. For a Gaussian random field this gives random inclusions with smooth, irregular boundaries.

source

Simulation

ModularEIT.conductivity — Function
conductivity(par::AbstractParametrization, θ)

Conductivity coefficients σ = P θ of the parameters θ.

source
conductivity(disc, phantom; method = :l2, quadrature_order = 4)
conductivity(disc, σ::AbstractVector)

Coefficients of a conductivity given as a function x ↦ σ(x) (a phantom, e.g. InclusionPhantom, PixelFunction, or any callable) in the σ space of disc: the L² projection (method = :l2, cell averages for piecewise constants, sampled with a rule of quadrature_order) or nodal interpolation (:interpolate). Coefficient vectors are passed through.

source
ModularEIT.simulate_data — Function
simulate_data(disc, fm, conductivity, inputs; mode = :neumann, noise = nothing,
              rng = Random.default_rng(), solver = DirectSolver(), quadrature_order = 4)

Simulated measurements for the forward model fm on disc: conductivity is a phantom (projected with conductivity) or a coefficient vector; inputs are current patterns (mode = :neumann, data = voltages) or voltage patterns (mode = :dirichlet, data = currents). noise: nothing, an additive AbstractNoiseModel or SourceMeterNoise.

Returns a named tuple (data, clean, σ, inputs, applied): noisy and exact data, the conductivity coefficients, the nominal inputs (to give to the reconstruction) and the inputs actually applied (different under source noise).

To avoid the inverse crime, simulate on a finer (or different) mesh than the reconstruction mesh: electrode patterns and electrode voltages do not depend on the mesh.

source

Image corruption

ModularEIT.corrupt_image — Function
corrupt_image(img; rng = Random.default_rng(), steps = 1, spatial_noise = 0.05,
              spectral_noise = 0.01, damping = 1e-3, exponent = 1)

Synthetic degradation of an image for training denoisers (wiki: Spectral Image Corruption). Repeats steps times: add white noise (spatial_noise), transform to the orthonormal DCT-II basis, add frequency-dependent noise spectral_noise (1 + |ω|²) ξ, damp by exp(-damping |ω|^(2 exponent)), transform back. |ω|² are the eigenvalues of the discrete Neumann Laplacian of the pixel grid, scaled to ≈ k² + l² for low frequencies (k, l), so damping alone (exponent = 1) is the discrete heat semigroup: it preserves the mean and satisfies the maximum principle.

source

Wiki articles

Theory behind this page in the theory wiki:

Index