3.1.1. fringes package

3.1.1.1. Submodules

3.1.1.2. fringes.filter module

fringes.filter.curvature(s: ndarray, center: bool = False, normalize: bool = False) ndarray[source]

Mean curvature map.

Computed by differentiating a slope map.

Parameters:
  • s (np.ndarray) – Slope map. It is reshaped to video-shape (frames \(T\), height \(Y\), width \(X\), color channels \(C\)) before processing.

  • center (bool, default=False) – If this flag is set to True, the curvature values get centered around zero using the median.

  • normalize (bool, default=False) – Flag indicating whether to use the arc-tangent function to non-linearly map the codomain of \(c\) from [-inf, inf] to [-1, 1].

Returns:

c – Curvature map.

Return type:

np.ndarray

fringes.filter.direct(b: ndarray) ndarray[source]

Direct illumination component.

Parameters:

b (np.ndarray) – Modulation.

Returns:

d – Direct illumination component.

Return type:

np.ndarray

References

fringes.filter.exposure(a: ndarray, Irec: ndarray, lessbits: bool = True) ndarray[source]

Exposure.

Parameters:
  • a (np.ndarray) – Brightness.

  • Irec (np.ndarray) – Fringe pattern sequence.

  • lessbits (bool, default=True) – \(Irec\) recorded by tha camera may contain fewer bits of information than its data type can hold, e.g. 12 bits for dtype \(uint16\). If this flag is activated, it looks for the maximal value in \(I\) and sets \(Imax\) to the same or next power of two which is divisible by two. Example: If \(I.max()\) is 3500, \(Imax\) is set to 4095 (the maximal value a 12bit camera can deliver).

Returns:

E – Exposure.

Return type:

np.ndarray

fringes.filter.indirect(a: ndarray, b: ndarray) ndarray[source]

Indirect (global) illumination component.

Parameters:
  • a (np.ndarray) – Brightness.

  • b (np.ndarray) – Modulation.

Returns:

g – Indirect (global) illumination component.

Return type:

np.ndarray

References

fringes.filter.visibility(a: ndarray, b: ndarray) ndarray[source]

Visibility.

Parameters:
  • a (np.ndarray) – Brightness.

  • b (np.ndarray) – Modulation

Returns:

V – Visibility.

Return type:

np.ndarray

3.1.1.3. fringes.fringes module

class fringes.fringes.Fringes(*args, X: int = 1920, Y: int = 1200, axes: int | Sequence[int] = (1, 0), K: int = 2, N: int | Sequence[int] = ((4, 4), (4, 4)), v: float | Sequence[float] | str = ((13.0, 7.0), (13.0, 7.0)), f: float | Sequence[float] = ((1.0, 1.0), (1.0, 1.0)), h: Sequence[int] | str = ((255, 255, 255),), p0: float = 3.141592653589793, g: float = 1.0, E: float = 0.5, V: float = 1.0, a: float = 1.0, bits: int = 8, dtype: str | dtype = 'uint8', grid: str = 'image', SDM: bool = False, WDM: bool = False, FDM: bool = False, static: bool = False, lmin: float = 8.0, reverse: bool = False, mode: str = 'fast')[source]

Bases: object

Configure, encode and decode fringe patterns using phase shifting algorithms.

Note

All parameters are implemented as properties (managed attributes) and are parsed when set. Note that some attributes have sub-dependencies, hence dependent attributes might change as well. Circular dependencies are resolved automatically.

Parameters:
  • *args (iterable) – Non-keyword arguments are explicitly discarded. Only keyword arguments are considered.

  • X (int, default=1920) – Width of fringe patterns (in pixel units).

  • Y (int, default=1200) – Height of fringe patterns (in pixel units).

  • axes (tuple, default=(1, 0)) – Axes along which to encode.

  • K (int, default=2) – Number of sets (number of fringe patterns with different spatial frequencies).

  • N (array_like, default=((4, 4), (4, 4))) – Number of (equally distributed) phase shifts.

  • v (array_like, default=((13.0, 7.0), (13.0, 7.0))) – Spatial frequencies (number of fringe periods across extended coding length \(Lext\)).

  • f (array_like, default=((1.0, 1.0), (1.0, 1.0))) – Temporal frequency (number of periods to shift over).

  • h (array_like, default=((255, 255, 255),)) – Hues i.e. colors of fringe patterns.

  • p0 (float, default=3.141592653589793) – Phase offset.

  • g (float, default=1.0) – Gamma correction factor used to compensate nonlinearities of the display response curve.

  • E (float, default=0.5) – Relative exposure, i.e. relative mean intensity ∈ [0, 1].

  • V (float, default=1.0) – Fringe visibility (fringe contrast) ∈ [0, 1].

  • a (float, default=1.0) – Factor for extending the coding length.

  • bits (int, default=8) – Number of bits.

  • dtype (str, default=uint8) – Data type which can hold 2**`bits` of information.

  • grid (str, default=image) – Coordinate system of the fringe patterns.

  • SDM (bool, default=False) – Spatial division multiplexing.

  • WDM (bool, default=False) – Wavelength division multiplexing.

  • FDM (bool, default=False) – Frequency division multiplexing.

  • static (bool, default=False) – Flag for creating static fringes (so they remain congruent when shifted).

  • lmin (float, default=8.0) – Minimum resolvable wavelength (in pixel units).

  • reverse (bool, default=False) – Flag for shifting fringes in reverse direction.

  • mode (str, default=fast) – Mode for coding.

encode()[source]

Encode fringe patterns. See \(encode\).

decode()[source]

Decode fringe patterns. See \(decode\).

property A: float

Offset.

property B: float

Amplitude.

property C: int

Number of color channels.

property D: int

Number of directions.

property DRQ

Dynamic range due to quantization noise.

It is a measure of how many points can be distinguished within the unambiguous measurement range [0, UMR).

property DRQdB

Dynamic range, in unit ‘dB’.

property E: float

Relative exposure, i.e. relative mean intensity ∈ [0, 1].

property FDM: bool

Frequency division multiplexing.

The directions ‘D’ and the sets K are multiplexed, resulting in a crossed fringe pattern if ‘D’ ≡ 2. It can only be activated if ‘D’ ∨ ‘K’ > 1 i.e. ‘D’ * ‘K’ > 1. The amplitude ‘b’ is reduced by the factor ‘D’ * ‘K’. Usually ‘f’ equals 1 and is essentially only changed if frequency division multiplexing (‘FDM’) is activated: Each set per direction receives an individual temporal frequency ‘f’, which is used in temporal demodulation to distinguish the individual sets. A minimal number of shifts Nmin ≥ ⌈ 2 * fmax + 1 ⌉ is required to satisfy the sampling theorem and N is updated automatically if necessary. If one wants a static pattern, i.e. one that remains congruent when shifted, set ‘static’ to True.

property H: int

Number of hues.

property Imax: int

Maximum gray value.

property K: int

Number of sets (number of fringe patterns with different spatial frequencies).

property L: ndarray

Coding lengths, i.e. lengths of fringe patterns for each direction (in pixel units).

property Lext: float

Extended coding length (in pixel units).

property Lmax: int

Maximum coding length (in pixel units).

property M: int

Number of averaged intensity samples.

property N: ndarray

Number of (equally distributed) phase shifts.

property SDM: bool

Spatial division multiplexing.

The directions ‘D’ are multiplexed, resulting in a crossed fringe pattern. The amplitude ‘B’ is halved. It can only be activated if we have two directions, i.e. ‘D’ ≡ 2. The number of frames ‘T’ is reduced by the factor 2.

property SQNR

Signal to quantization noise ratio.

It is a measure of how many points can be distinguished within the screen length [0, L).

property SQNRdB

Signal to quantization noise ratio, in unit ‘dB’.

property T: int

Number of frames.

property UMR: ndarray

Unambiguous measurement range (in pixel units).

The coding is only unique within the periodic interval [0, UMR).

The UMR is derived from ‘l’ and ‘v’:

  • If ‘l’ ∈ ℕ, UMR = lcm(‘l’), with lcm() being the least common multiple.

  • Else, if ‘v’ ∈ ℕ, UMR = ‘Lext’ / gcd(‘v’), with gcd() being the greatest common divisor.

  • Else, if ‘l’ ∨ ‘v’ ∈ ℚ, lcm() resp. gcd() are extended to rational numbers.

  • Else, if ‘l’ ∧ ‘v’ ∈ ℝ ∖ ℚ, UMR = prod(‘l’), with prod() being the product operator.

property V: float

Fringe visibility (fringe contrast) ∈ [0, 1].

Screens are extremely nonlinear near the minimum and maximum values; we can avoid them by setting V to e.g. 0.95.

property WDM: bool

Wavelength division multiplexing.

The shifts are multiplexed into the color channel, resulting in an RGB fringe pattern. It can only be activated if all shifts equal 3, i.e. ‘N’ ≡ 3. The number of frames ‘T’ is reduced by the factor 3.

property X: int

Width of fringe patterns (in pixel units).

property Y: int

Height of fringe patterns (in pixel units).

property a: float

Factor for extending the coding length.

property axes: ndarray

Axes along which to encode.

property bits: int

Number of bits.

property crt: ndarray

Coefficients for Chinese remainder theorem.

decode(I: ndarray, bmin: float = 0.0, Vmin: float = 0.0, unwrap: bool = True, verbose: bool | str = False, check_overexposure: bool = False, threads: int | None = None, spu_func: str = 'ski') decoded | decoded_verbose[source]

Decode fringe patterns.

Parameters:
  • I (np.ndarray) –

    Fringe pattern sequence. It is reshaped to \(vshape\) (frames \(T\), height \(Y\), width \(X\), color channels \(C\)) before processing.

    Note

    \(I\) must have been encoded with the same parameters set to the Fringes instance as the current one.

  • bmin (float, default=0) – Minimum modulation for measurement to be valid. Can accelerate decoding.

  • Vmin (float, default=0) – Minimum visibility for measurement to be valid. Can accelerate decoding.

  • unwrap (bool, default=True) – Flag for unwrapping.

  • verbose (bool or str, default=False) – Flag for returning intermediate and verbose results.

  • check_overexposure (bool, default=False) – Flag for checking if ‘I’ is overexposed. Logs a warning message if so.

  • threads (int, optional) – Number of threads to use. Default is all threads. If negative, this many threads will not be used.

  • spu_func ({'ski', 'cv2'}, default='ski') –

    Spatial unwrapping function to use.

Returns:

  • a (np.ndarray) – Brightness: average signal.

  • b (np.ndarray) – Modulation: amplitude of the cosine signal.

  • x (np.ndarray) – Registration: decoded screen coordinates.

  • p (np.ndarray, optional) – Local phase.

  • k (np.ndarray, optional) – Fringe order.

  • r (np.ndarray, optional) – Residuals from the optimization-based unwrapping process.

  • u (np.ndarray, optional) – Uncertainty of positional decoding in pixel units

Raises:

ValueError – If the number of frames of \(I\) and the attribute \(T\) of the \(Fringes\) instance don’t match. If \(WDM\) is active but \(I\) does not have 3 color channels.

References

Examples

>>> from fringes import Fringes
>>> f = Fringes()
>>> I = f.encode()
>>> Irec = I  # todo: replace this line with the recorded data (cf. example in 'record.py')
>>> a, b, x = f.decode(Irec)

Also return intermediate and verbose results:

>>> a, b, x, p, k, r, u = f.decode(Irec, verbose=True)
property dtype: dtype

Data type which can hold 2**`bits` of information.

encode() ndarray[source]

Encode fringe patterns.

Returns:

I – Fringe pattern sequence.

Return type:

np.ndarray

Notes

To receive the frames iteratively (i.e. in a lazy manner), simply iterate over the Fringes instance.

Examples

>>> from fringes import Fringes
>>> f = Fringes()

Encode the complete fringe pattern sequence.

>>> I = f.encode()

Call the instance to encode the complete fringe pattern sequence.

>>> I = f()

Create a generator to receive the frames iteratively, i.e. in a lazy manner.

>>> I = (frame for frame in f)
property eta: float

Spatial coding efficiency.

property f: ndarray

Temporal frequency (number of periods to shift over).

property g: float

Gamma correction factor used to compensate nonlinearities of the display response curve.

property gcd: ndarray

Greatest common divisor of moduli.

Must be smaller than noise.

property grid: str

Coordinate system of the fringe patterns.

property h: ndarray

Hues i.e. colors of fringe patterns.

Possible values are any sequence of RGB color triples ∈ [0, 255]. However, black (0, 0, 0) is not allowed.

The hue values can also be set by assigning any combination of the following characters as a string:

  • ‘r’: red

  • ‘g’: green

  • ‘b’: blue

  • ‘c’: cyan

  • ‘m’: magenta

  • ‘y’: yellow

  • ‘w’: white

Before decoding, repeating hues will be fused by averaging.

property l: ndarray

Wavelengths of fringe periods (in pixel units).

When Lext changes, ‘v’ is kept constant and only ‘l’ is changed.

property lmin: float

Minimum resolvable wavelength (in pixel units).

load(fname: str | None = None) None[source]

Load parameters from a config file to the \(Fringes\) instance.

Warning

The parameters are only loaded if the config file provides the section \(fringes\).

Parameters:

fname (str, optional) – File name of the file to load. Supported file formats are: *.json, *.yaml. If \(fname\) is not provided, the file \(.fringes.yaml\) within the user home directory is loaded.

Examples

>>> import os
>>> fname = os.path.join(os.getcwd(), "config.yaml")
>>> from fringes import Fringes
>>> f = Fringes()
>>> f.N = f.N + 1
>>> f.save(fname)
>>> f_ = Fringes()
>>> f_.load(fname)
>>> f_ == f
True
property m: ndarray

Moduli for Chinese remainder theorem.

property mode: str

Mode for coding.

The following values can be set:

  • ‘fast’

  • ‘precise’

mtf(v: Sequence[float] | None = None, PSF: float = 0.0) ndarray[source]

Modulation Transfer Function.

Normalized to values ∈ [0, 1].

Parameters:
  • v (array_like) – Spatial frequencies at which to determine the normalized modulation.

  • PSF (float, default=0) – Standard deviation of the point spread function.

Returns:

b – Relative modulation, at spatial frequencies ‘v’. ‘b’ is in the same shape as ‘v’.

Return type:

np.ndarray

Raises:

ValueError – If ‘v’ contains negative numbers.

Notes

  • If the private attribute \(_mtf\) of the Fringes instance is not None, the MTF is interpolated from previous measurements.

  • Else, if \(PSF\) is larger than zero, the MTF is computed from the optical transfer function of the optical system, i.e. as the magnitude of the Fourier-transformed ‘Point Spread Function’ (PSF).

  • Else, an approximation is used [1]_.

References

property p0: float

Phase offset.

It can be used to e.g. let the fringe patterns start (at the origin) with a gray value of zero.

reset() None[source]

Reset parameters of the \(Fringes\) instance to default values.

property reverse: bool

Flag for shifting fringes in reverse direction.

save(fname: str | None = None) None[source]

Save the parameters of the \(Fringes\) instance to a config file.

Within the file, the parameters are written to the section \(fringes\).

Parameters:

fname (str, optional) – File name of the file to save. Supported file formats are: *.json, *.yaml. If \(fname\) is not provided, the parameters are saved to the file \(.fringes.yaml\) within the user home directory.

Examples

>>> import os
>>> fname = os.path.join(os.getcwd(), "config.yaml")
>>> from fringes import Fringes
>>> f = Fringes()
>>> f.save(fname)
property shape: tuple[int, int, int, int]

Shape of fringe pattern sequence in video shape (frames, height, with, color channels).

property static: bool

Flag for creating static fringes (so they remain congruent when shifted).

uncertainty(ui: float | ndarray = np.float64(0.28867513459481287), b: float | ndarray | None = None, a: float | ndarray | None = None, K: float | None = None, dark_noise: float | None = None) ndarray[source]

Uncertainty of positional decoding, in pixel units.

Using inverse variance weighting in unwrapping and assuming no fringe order errors.

Parameters:
  • ui (float | np.ndarray, default=np.sqrt(1 / 12)) – Standard deviation of intensity noise. The default assumes only quantization noise. If np.ndarray, it must have image shape (Y, X, C).

  • b (float | np.ndarray, optional) – Modulation. If np.ndarray, it must have video shape (T, Y, X, C) with T = D * K.

  • a (float | np.ndarray, optional) – Offset (mean number of gray values). If np.ndarray, it must have video shape (T, Y, X, C) with T = D.

  • K (float, optional) – System gain of used camera, in units DN/electron.

  • dark_noise (float, optional) – Dark noise of used camera, in units electrons.

Returns:

u – Uncertainty of positional decoding, in pixel units.

Return type:

np.ndarray

Note

If \(a\), \(K\) and \(dark_noise\) are given, \(ui\) is ignored and calculated from them. In any case, if either \(ui\), \(b\) or \(a\) is a numpy.ndarray, they must be shape consistent and hence their shape must end in the same (Y, X, C) values (the function takes care of those who are in front).

Examples

>>> from fringes import Fringes
>>> f = Fringes()

Assuming only quantization noise and using \(f.B\) as \(b\) (both by default):

>>> u = f.uncertainty()

Setting \(ui\) and \(b\) with floats:

>>> u = f.uncertainty(ui=2.28, b=100)

Using decoded values (numpy.ndarrays) and camera parameters:

>>> I = f.encode()
>>> Irec = I  # todo: replace this line with recording data (cf. example in 'record.py')
>>> a, b, x = f.decode(Irec)
>>> u = f.uncertainty(b=b, a=a, K=0.038, dark_noise=13.7)
property uq

Positional uncertainty (standard deviation) due to quantization noise.

property uwr: str

Phase unwrapping method.

  • ‘temporal’: Temporal phase unwrapping.

  • ‘spatial’: Spatial phase unwrapping.

  • ‘ftm’: Fourier-transform method.

  • ‘none’: No unwrapping required.

property v: ndarray

Spatial frequencies (number of fringe periods across extended coding length \(Lext\)).

property vmax: float

Maximum resolvable spatial frequency.

property x: ndarray

Coordinate matrices of the coordinate system defined in \(grid\).

This is always a fleshed out representation.

‘axes’ defines the order in which the coordinate matrices appear.

An additional axis is added for the color channel, which is useful when running tests so that ‘x’ is shape consistent/broadcastable to ‘dec.x’ (the latter has a color channel).

property x0: float

Coordinate offset.

3.1.1.4. fringes.grid module

fringes.grid.cart(Y: int = 1024, X: int = 1024, a: float = 0)[source]

Cartesian coordinates.

fringes.grid.img(Y: int = 1024, X: int = 1024, a: float = 0)[source]

Image coordinates.

fringes.grid.inner_circ(Y: int = 1024, X: int = 1024)[source]

Boolean mask with True values inside inscribed circle.

fringes.grid.logpol(Y: int = 1024, X: int = 1024, a: float = 0)[source]

Log-polar coordinates.

fringes.grid.pol(Y: int = 1024, X: int = 1024, a: float = 0)[source]

Polar coordinates.

fringes.grid.rot(uu, vv, a)[source]

Rotate coordinate matrix.

3.1.1.5. fringes.gui module

3.1.1.6. fringes.util module

fringes.util.circular_distance(a: float | ndarray, b: float | ndarray, c: float) ndarray[source]

Circular distance.

Parameters:
  • a (float or np.ndarray) – Start points.

  • b (float or np.ndarray) – End points.

  • c (float) – Circumference (distance) after which wrapping occurs.

Returns:

d – Circular distance between a and b.

Return type:

float or np.ndarray

References

fringes.util.fade(I: ndarray, I0: float | ndarray = 0.0, T: float | ndarray = 1.0, M: int = 1, PSF: float = 0.0, K: float = 0.038, dark_noise: float = 13.7, dark_current: float = 14.2, bits: int = 8, rng: Generator | int | None = None, clip: bool = True)[source]

Fade image (sequence).

This includes the modulation transfer function (computed from the imaging system’s point spread function), and intensity noise added by the camera.

Parameters:
  • I (np.ndarray) – Image (sequence) with values within [0, 1].

  • I0 (float | np.ndarray, default=0.0) – Offset with values within [0, 2**`b`-1].

  • T (float | np.ndarray, default=1.0) – Transmission factor with values within [0, 1].

  • M (int, default=1) – Optical magnification >= 1.

  • PSF (float, default=0.0) – Standard deviation of the point spread function, in pixel units.

  • K (float, default=0.038 (@ \(bits\) = 8).) – System gain of the digital camera, in units of DN (digital numbers) / electron.

  • dark_noise (float, default=13.7) – Dark noise of the digital camera, in units of electrons.

  • dark_current (float, default=14.2) – Dark current of the digital camera, in units of electrons.

  • bits (int, default=8) – Number of bits to store information of one pixel. This is used to determine the output dtype. If e.g. bits equals 8, \(Imax\) will be 2 ** 8 - 1 = 255 and \(dtype\) will be \(uint8\). If e.g. bits equals 0, \(Imax\) will be 1 and \(dtype\) will be \(float\).

  • clip (bool, default=True) – If True (default), the output will be clipped after noise is applied. This is needed to maintain the proper image data range.

  • rng ({numpy.random.Generator, int}, optional) – Pseudo-random number generator. By default, a PCG64 generator is used (see numpy.random.default_rng()). If rng is an int, it is used to seed the generator.

Returns:

In – Faded image (sequence).

Return type:

np.ndarray

fringes.util.gamma_auto_correct(I: ndarray) ndarray[source]

Automatically estimate and apply the gamma correction factor.

Linearizes the display/camera response curve.

Parameters:

I (np.ndarray) – Recorded data.

Returns:

Ilin – Linearized data.

Return type:

np.ndarray

fringes.util.linear2srgb(I: ndarray) ndarray[source]

Convert from linear to sRGB color space.

Parameters:

I (np.ndarray) – Input image. Must be within [0, 1].

Returns:

I\(I\) in sRGB color space and within [0, 1].

Return type:

np.ndarray

fringes.util.unzip(I: ndarray, T: int) ndarray[source]

Unzip pattern sequences.

This applies for pattern sequences recorded with a line scan camera, where each frame has been displayed and captured as the object moved by one pixel.

Parameters:
  • I (np.ndarray) – Pattern sequence. It is reshaped to video-shape (frames \(T\), height \(Y\), width \(X\), color channels \(C\)) before processing.

  • T (int) – Number of frames of the pattern sequence.

Returns:

I – Unzipped pattern sequence.

Return type:

np.ndarray

Raises:

ValueError – If the number of frames of \(I\) and the number of frames of the pattern sequence \(T\) don’t match.

Examples

>>> from fringes import Fringes
>>> from fringes.util import unzip
>>> f = Fringes()
>>> I = f.encode()
>>> Irec = I.swapaxes(0, 1).reshape(-1, f.T, f.X, f.C)  # zip; this is how a line camera would record
>>> Irec = unzip(Irec, f.T)
fringes.util.vshape(data: ndarray, channels: set[int] = (1, 3, 4)) ndarray[source]

Standardizes the input data shape.

Transforms video data into the standardized shape (T, Y, X, C), where T is number of frames, Y is height, X is width, and C is number of color channels.

Inspired by scikit-video.

Parameters:
  • data (ndarray) – Input data in arbitrary shape.

  • channels (set of ints, default={1, 3, 4}) – Allowed number of color channels.

Returns:

videodata – Standardized version of data, in shape (T, Y, X, C), where T is number of frames, Y is height, X is width, and C is number of color channels.

Return type:

ndarray

Notes

Ensures that the array becomes 4-dimensional and that the length of the last dimension is in \(channels\). To do this, leading dimensions may be flattened.

Examples

>>> from fringes import vshape
>>> data = np.empty(shape=(100))
>>> videodata = vshape(data)
>>> videodata.shape
(100, 1, 1, 1)
>>> data = np.empty(shape=(1200, 1920))
>>> videodata = vshape(data)
>>> videodata.shape
(1, 1200, 1920, 1)
>>> data = np.empty(shape=(1200, 1920, 3))
>>> videodata = vshape(data)
>>> videodata.shape
(1, 1200, 1920, 3)
>>> data = np.empty(shape=(10, 1200, 1920))
>>> videodata = vshape(data)
>>> videodata.shape
(10, 1200, 1920, 1)
>>> data = np.empty(shape=(10, 1200, 1920, 3))
>>> videodata = vshape(data)
>>> videodata.shape
(10, 1200, 1920, 3)
>>> data = np.empty(shape=(2, 3, 4, 1200, 1920))
>>> videodata = vshape(data)
>>> videodata.shape
(24, 1200, 1920, 1)

3.1.1.7. Module contents

class fringes.Fringes(*args, X: int = 1920, Y: int = 1200, axes: int | Sequence[int] = (1, 0), K: int = 2, N: int | Sequence[int] = ((4, 4), (4, 4)), v: float | Sequence[float] | str = ((13.0, 7.0), (13.0, 7.0)), f: float | Sequence[float] = ((1.0, 1.0), (1.0, 1.0)), h: Sequence[int] | str = ((255, 255, 255),), p0: float = 3.141592653589793, g: float = 1.0, E: float = 0.5, V: float = 1.0, a: float = 1.0, bits: int = 8, dtype: str | dtype = 'uint8', grid: str = 'image', SDM: bool = False, WDM: bool = False, FDM: bool = False, static: bool = False, lmin: float = 8.0, reverse: bool = False, mode: str = 'fast')[source]

Bases: object

Configure, encode and decode fringe patterns using phase shifting algorithms.

Note

All parameters are implemented as properties (managed attributes) and are parsed when set. Note that some attributes have sub-dependencies, hence dependent attributes might change as well. Circular dependencies are resolved automatically.

Parameters:
  • *args (iterable) – Non-keyword arguments are explicitly discarded. Only keyword arguments are considered.

  • X (int, default=1920) – Width of fringe patterns (in pixel units).

  • Y (int, default=1200) – Height of fringe patterns (in pixel units).

  • axes (tuple, default=(1, 0)) – Axes along which to encode.

  • K (int, default=2) – Number of sets (number of fringe patterns with different spatial frequencies).

  • N (array_like, default=((4, 4), (4, 4))) – Number of (equally distributed) phase shifts.

  • v (array_like, default=((13.0, 7.0), (13.0, 7.0))) – Spatial frequencies (number of fringe periods across extended coding length \(Lext\)).

  • f (array_like, default=((1.0, 1.0), (1.0, 1.0))) – Temporal frequency (number of periods to shift over).

  • h (array_like, default=((255, 255, 255),)) – Hues i.e. colors of fringe patterns.

  • p0 (float, default=3.141592653589793) – Phase offset.

  • g (float, default=1.0) – Gamma correction factor used to compensate nonlinearities of the display response curve.

  • E (float, default=0.5) – Relative exposure, i.e. relative mean intensity ∈ [0, 1].

  • V (float, default=1.0) – Fringe visibility (fringe contrast) ∈ [0, 1].

  • a (float, default=1.0) – Factor for extending the coding length.

  • bits (int, default=8) – Number of bits.

  • dtype (str, default=uint8) – Data type which can hold 2**`bits` of information.

  • grid (str, default=image) – Coordinate system of the fringe patterns.

  • SDM (bool, default=False) – Spatial division multiplexing.

  • WDM (bool, default=False) – Wavelength division multiplexing.

  • FDM (bool, default=False) – Frequency division multiplexing.

  • static (bool, default=False) – Flag for creating static fringes (so they remain congruent when shifted).

  • lmin (float, default=8.0) – Minimum resolvable wavelength (in pixel units).

  • reverse (bool, default=False) – Flag for shifting fringes in reverse direction.

  • mode (str, default=fast) – Mode for coding.

encode()[source]

Encode fringe patterns. See \(encode\).

decode()[source]

Decode fringe patterns. See \(decode\).

property A: float

Offset.

property B: float

Amplitude.

property C: int

Number of color channels.

property D: int

Number of directions.

property DRQ

Dynamic range due to quantization noise.

It is a measure of how many points can be distinguished within the unambiguous measurement range [0, UMR).

property DRQdB

Dynamic range, in unit ‘dB’.

property E: float

Relative exposure, i.e. relative mean intensity ∈ [0, 1].

property FDM: bool

Frequency division multiplexing.

The directions ‘D’ and the sets K are multiplexed, resulting in a crossed fringe pattern if ‘D’ ≡ 2. It can only be activated if ‘D’ ∨ ‘K’ > 1 i.e. ‘D’ * ‘K’ > 1. The amplitude ‘b’ is reduced by the factor ‘D’ * ‘K’. Usually ‘f’ equals 1 and is essentially only changed if frequency division multiplexing (‘FDM’) is activated: Each set per direction receives an individual temporal frequency ‘f’, which is used in temporal demodulation to distinguish the individual sets. A minimal number of shifts Nmin ≥ ⌈ 2 * fmax + 1 ⌉ is required to satisfy the sampling theorem and N is updated automatically if necessary. If one wants a static pattern, i.e. one that remains congruent when shifted, set ‘static’ to True.

property H: int

Number of hues.

property Imax: int

Maximum gray value.

property K: int

Number of sets (number of fringe patterns with different spatial frequencies).

property L: ndarray

Coding lengths, i.e. lengths of fringe patterns for each direction (in pixel units).

property Lext: float

Extended coding length (in pixel units).

property Lmax: int

Maximum coding length (in pixel units).

property M: int

Number of averaged intensity samples.

property N: ndarray

Number of (equally distributed) phase shifts.

property SDM: bool

Spatial division multiplexing.

The directions ‘D’ are multiplexed, resulting in a crossed fringe pattern. The amplitude ‘B’ is halved. It can only be activated if we have two directions, i.e. ‘D’ ≡ 2. The number of frames ‘T’ is reduced by the factor 2.

property SQNR

Signal to quantization noise ratio.

It is a measure of how many points can be distinguished within the screen length [0, L).

property SQNRdB

Signal to quantization noise ratio, in unit ‘dB’.

property T: int

Number of frames.

property UMR: ndarray

Unambiguous measurement range (in pixel units).

The coding is only unique within the periodic interval [0, UMR).

The UMR is derived from ‘l’ and ‘v’:

  • If ‘l’ ∈ ℕ, UMR = lcm(‘l’), with lcm() being the least common multiple.

  • Else, if ‘v’ ∈ ℕ, UMR = ‘Lext’ / gcd(‘v’), with gcd() being the greatest common divisor.

  • Else, if ‘l’ ∨ ‘v’ ∈ ℚ, lcm() resp. gcd() are extended to rational numbers.

  • Else, if ‘l’ ∧ ‘v’ ∈ ℝ ∖ ℚ, UMR = prod(‘l’), with prod() being the product operator.

property V: float

Fringe visibility (fringe contrast) ∈ [0, 1].

Screens are extremely nonlinear near the minimum and maximum values; we can avoid them by setting V to e.g. 0.95.

property WDM: bool

Wavelength division multiplexing.

The shifts are multiplexed into the color channel, resulting in an RGB fringe pattern. It can only be activated if all shifts equal 3, i.e. ‘N’ ≡ 3. The number of frames ‘T’ is reduced by the factor 3.

property X: int

Width of fringe patterns (in pixel units).

property Y: int

Height of fringe patterns (in pixel units).

property a: float

Factor for extending the coding length.

property axes: ndarray

Axes along which to encode.

property bits: int

Number of bits.

property crt: ndarray

Coefficients for Chinese remainder theorem.

decode(I: ndarray, bmin: float = 0.0, Vmin: float = 0.0, unwrap: bool = True, verbose: bool | str = False, check_overexposure: bool = False, threads: int | None = None, spu_func: str = 'ski') decoded | decoded_verbose[source]

Decode fringe patterns.

Parameters:
  • I (np.ndarray) –

    Fringe pattern sequence. It is reshaped to \(vshape\) (frames \(T\), height \(Y\), width \(X\), color channels \(C\)) before processing.

    Note

    \(I\) must have been encoded with the same parameters set to the Fringes instance as the current one.

  • bmin (float, default=0) – Minimum modulation for measurement to be valid. Can accelerate decoding.

  • Vmin (float, default=0) – Minimum visibility for measurement to be valid. Can accelerate decoding.

  • unwrap (bool, default=True) – Flag for unwrapping.

  • verbose (bool or str, default=False) – Flag for returning intermediate and verbose results.

  • check_overexposure (bool, default=False) – Flag for checking if ‘I’ is overexposed. Logs a warning message if so.

  • threads (int, optional) – Number of threads to use. Default is all threads. If negative, this many threads will not be used.

  • spu_func ({'ski', 'cv2'}, default='ski') –

    Spatial unwrapping function to use.

Returns:

  • a (np.ndarray) – Brightness: average signal.

  • b (np.ndarray) – Modulation: amplitude of the cosine signal.

  • x (np.ndarray) – Registration: decoded screen coordinates.

  • p (np.ndarray, optional) – Local phase.

  • k (np.ndarray, optional) – Fringe order.

  • r (np.ndarray, optional) – Residuals from the optimization-based unwrapping process.

  • u (np.ndarray, optional) – Uncertainty of positional decoding in pixel units

Raises:

ValueError – If the number of frames of \(I\) and the attribute \(T\) of the \(Fringes\) instance don’t match. If \(WDM\) is active but \(I\) does not have 3 color channels.

References

Examples

>>> from fringes import Fringes
>>> f = Fringes()
>>> I = f.encode()
>>> Irec = I  # todo: replace this line with the recorded data (cf. example in 'record.py')
>>> a, b, x = f.decode(Irec)

Also return intermediate and verbose results:

>>> a, b, x, p, k, r, u = f.decode(Irec, verbose=True)
property dtype: dtype

Data type which can hold 2**`bits` of information.

encode() ndarray[source]

Encode fringe patterns.

Returns:

I – Fringe pattern sequence.

Return type:

np.ndarray

Notes

To receive the frames iteratively (i.e. in a lazy manner), simply iterate over the Fringes instance.

Examples

>>> from fringes import Fringes
>>> f = Fringes()

Encode the complete fringe pattern sequence.

>>> I = f.encode()

Call the instance to encode the complete fringe pattern sequence.

>>> I = f()

Create a generator to receive the frames iteratively, i.e. in a lazy manner.

>>> I = (frame for frame in f)
property eta: float

Spatial coding efficiency.

property f: ndarray

Temporal frequency (number of periods to shift over).

property g: float

Gamma correction factor used to compensate nonlinearities of the display response curve.

property gcd: ndarray

Greatest common divisor of moduli.

Must be smaller than noise.

property grid: str

Coordinate system of the fringe patterns.

property h: ndarray

Hues i.e. colors of fringe patterns.

Possible values are any sequence of RGB color triples ∈ [0, 255]. However, black (0, 0, 0) is not allowed.

The hue values can also be set by assigning any combination of the following characters as a string:

  • ‘r’: red

  • ‘g’: green

  • ‘b’: blue

  • ‘c’: cyan

  • ‘m’: magenta

  • ‘y’: yellow

  • ‘w’: white

Before decoding, repeating hues will be fused by averaging.

property l: ndarray

Wavelengths of fringe periods (in pixel units).

When Lext changes, ‘v’ is kept constant and only ‘l’ is changed.

property lmin: float

Minimum resolvable wavelength (in pixel units).

load(fname: str | None = None) None[source]

Load parameters from a config file to the \(Fringes\) instance.

Warning

The parameters are only loaded if the config file provides the section \(fringes\).

Parameters:

fname (str, optional) – File name of the file to load. Supported file formats are: *.json, *.yaml. If \(fname\) is not provided, the file \(.fringes.yaml\) within the user home directory is loaded.

Examples

>>> import os
>>> fname = os.path.join(os.getcwd(), "config.yaml")
>>> from fringes import Fringes
>>> f = Fringes()
>>> f.N = f.N + 1
>>> f.save(fname)
>>> f_ = Fringes()
>>> f_.load(fname)
>>> f_ == f
True
property m: ndarray

Moduli for Chinese remainder theorem.

property mode: str

Mode for coding.

The following values can be set:

  • ‘fast’

  • ‘precise’

mtf(v: Sequence[float] | None = None, PSF: float = 0.0) ndarray[source]

Modulation Transfer Function.

Normalized to values ∈ [0, 1].

Parameters:
  • v (array_like) – Spatial frequencies at which to determine the normalized modulation.

  • PSF (float, default=0) – Standard deviation of the point spread function.

Returns:

b – Relative modulation, at spatial frequencies ‘v’. ‘b’ is in the same shape as ‘v’.

Return type:

np.ndarray

Raises:

ValueError – If ‘v’ contains negative numbers.

Notes

  • If the private attribute \(_mtf\) of the Fringes instance is not None, the MTF is interpolated from previous measurements.

  • Else, if \(PSF\) is larger than zero, the MTF is computed from the optical transfer function of the optical system, i.e. as the magnitude of the Fourier-transformed ‘Point Spread Function’ (PSF).

  • Else, an approximation is used [1]_.

References

property p0: float

Phase offset.

It can be used to e.g. let the fringe patterns start (at the origin) with a gray value of zero.

reset() None[source]

Reset parameters of the \(Fringes\) instance to default values.

property reverse: bool

Flag for shifting fringes in reverse direction.

save(fname: str | None = None) None[source]

Save the parameters of the \(Fringes\) instance to a config file.

Within the file, the parameters are written to the section \(fringes\).

Parameters:

fname (str, optional) – File name of the file to save. Supported file formats are: *.json, *.yaml. If \(fname\) is not provided, the parameters are saved to the file \(.fringes.yaml\) within the user home directory.

Examples

>>> import os
>>> fname = os.path.join(os.getcwd(), "config.yaml")
>>> from fringes import Fringes
>>> f = Fringes()
>>> f.save(fname)
property shape: tuple[int, int, int, int]

Shape of fringe pattern sequence in video shape (frames, height, with, color channels).

property static: bool

Flag for creating static fringes (so they remain congruent when shifted).

uncertainty(ui: float | ndarray = np.float64(0.28867513459481287), b: float | ndarray | None = None, a: float | ndarray | None = None, K: float | None = None, dark_noise: float | None = None) ndarray[source]

Uncertainty of positional decoding, in pixel units.

Using inverse variance weighting in unwrapping and assuming no fringe order errors.

Parameters:
  • ui (float | np.ndarray, default=np.sqrt(1 / 12)) – Standard deviation of intensity noise. The default assumes only quantization noise. If np.ndarray, it must have image shape (Y, X, C).

  • b (float | np.ndarray, optional) – Modulation. If np.ndarray, it must have video shape (T, Y, X, C) with T = D * K.

  • a (float | np.ndarray, optional) – Offset (mean number of gray values). If np.ndarray, it must have video shape (T, Y, X, C) with T = D.

  • K (float, optional) – System gain of used camera, in units DN/electron.

  • dark_noise (float, optional) – Dark noise of used camera, in units electrons.

Returns:

u – Uncertainty of positional decoding, in pixel units.

Return type:

np.ndarray

Note

If \(a\), \(K\) and \(dark_noise\) are given, \(ui\) is ignored and calculated from them. In any case, if either \(ui\), \(b\) or \(a\) is a numpy.ndarray, they must be shape consistent and hence their shape must end in the same (Y, X, C) values (the function takes care of those who are in front).

Examples

>>> from fringes import Fringes
>>> f = Fringes()

Assuming only quantization noise and using \(f.B\) as \(b\) (both by default):

>>> u = f.uncertainty()

Setting \(ui\) and \(b\) with floats:

>>> u = f.uncertainty(ui=2.28, b=100)

Using decoded values (numpy.ndarrays) and camera parameters:

>>> I = f.encode()
>>> Irec = I  # todo: replace this line with recording data (cf. example in 'record.py')
>>> a, b, x = f.decode(Irec)
>>> u = f.uncertainty(b=b, a=a, K=0.038, dark_noise=13.7)
property uq

Positional uncertainty (standard deviation) due to quantization noise.

property uwr: str

Phase unwrapping method.

  • ‘temporal’: Temporal phase unwrapping.

  • ‘spatial’: Spatial phase unwrapping.

  • ‘ftm’: Fourier-transform method.

  • ‘none’: No unwrapping required.

property v: ndarray

Spatial frequencies (number of fringe periods across extended coding length \(Lext\)).

property vmax: float

Maximum resolvable spatial frequency.

property x: ndarray

Coordinate matrices of the coordinate system defined in \(grid\).

This is always a fleshed out representation.

‘axes’ defines the order in which the coordinate matrices appear.

An additional axis is added for the color channel, which is useful when running tests so that ‘x’ is shape consistent/broadcastable to ‘dec.x’ (the latter has a color channel).

property x0: float

Coordinate offset.

fringes.vshape(data: ndarray, channels: set[int] = (1, 3, 4)) ndarray[source]

Standardizes the input data shape.

Transforms video data into the standardized shape (T, Y, X, C), where T is number of frames, Y is height, X is width, and C is number of color channels.

Inspired by scikit-video.

Parameters:
  • data (ndarray) – Input data in arbitrary shape.

  • channels (set of ints, default={1, 3, 4}) – Allowed number of color channels.

Returns:

videodata – Standardized version of data, in shape (T, Y, X, C), where T is number of frames, Y is height, X is width, and C is number of color channels.

Return type:

ndarray

Notes

Ensures that the array becomes 4-dimensional and that the length of the last dimension is in \(channels\). To do this, leading dimensions may be flattened.

Examples

>>> from fringes import vshape
>>> data = np.empty(shape=(100))
>>> videodata = vshape(data)
>>> videodata.shape
(100, 1, 1, 1)
>>> data = np.empty(shape=(1200, 1920))
>>> videodata = vshape(data)
>>> videodata.shape
(1, 1200, 1920, 1)
>>> data = np.empty(shape=(1200, 1920, 3))
>>> videodata = vshape(data)
>>> videodata.shape
(1, 1200, 1920, 3)
>>> data = np.empty(shape=(10, 1200, 1920))
>>> videodata = vshape(data)
>>> videodata.shape
(10, 1200, 1920, 1)
>>> data = np.empty(shape=(10, 1200, 1920, 3))
>>> videodata = vshape(data)
>>> videodata.shape
(10, 1200, 1920, 3)
>>> data = np.empty(shape=(2, 3, 4, 1200, 1920))
>>> videodata = vshape(data)
>>> videodata.shape
(24, 1200, 1920, 1)