level1match - match detected particles of both cameras

Motivation

The particle detection of each camera is completely separate, so the particles observed by each camera must be combined. This particle combination allows for the particle position to be determined in a three-dimensional reference coordinate system. As a side effect, this constrains the observation volume by discarding particles outside of the intersection of their observation volumes, i.e. observed by only one camera. We use a right-handed reference coordinate system (\(x\),\(y\),\(z\)) with \(z\) pointing to the ground to define the position of particles in the observation volume (Fig. 1). In the absence of an absolute reference, we attach the coordinate system to the leader camera (i.e., (\(x_\textrm{L}\),\(y_\textrm{L}\),\(z_\textrm{L}\)) = (\(x\),\(y\),\(z\))) such that \(x = X_\textrm{L}\) and \(z = Y_\textrm{L}\), where \(X_\textrm{L}\) and \(Y_\textrm{L}\) are the particle positions in the two dimensional leader images. Note that small letters describe the three dimensional coordinate system and capital letters describe the two dimensional position on the images of the individual camera images. The missing dimension \(y\) is obtained from the follower camera with \(y = -X_\textrm{F}\) where \(X_\textrm{F}\) the horizontal position in the follower image.

The matching of the particles from both cameras is based on the comparison of two variables: The vertical position of the particles and their vertical extent. Due to measurement uncertainties, the agreement of these variables cannot be perfect and they are treated probabilistically. That is, it is assumed that the difference in vertical extent \(\Delta h\) (vertical position \(\Delta z\)) between the two cameras follows a normally distributed probability density function (PDF) with mean zero and standard deviation 1.7 px (1.2 px), based on an analysis of manually matched particle pairs. To determine the probability (of e.g., measuring a certain vertical extent), the PDF is integrated over an interval of ±0.5 px representing the discrete 1 px steps.

This process requires matching the observations of both cameras in time. The internal clocks of the cameras (“capture time”) can deviate by more than 1 frame per 10 minutes. The time assigned by the computers (“record time”) is sometimes, but not always, distorted by computer load. Therefore, the continuous frame index (“capture id”) is used for matching, but this requires determining the index offset between both cameras at the start of each measurement (typically 10 minutes). For this, the algorithm uses pairs of frames with observed particles that are less than 1 ms (i.e. less than 1/4 of the measurement resolution) apart in record time assuming that the lag due to computer load is only sporadically increased. This allows the algorithm to identify the most common capture id offset of the frame pairs. We found that this method gives stable results for a subset of 500 frames. Similar to \(h\) and \(z\), the capture id offset \(\Delta i\) is used as the mean of a normal distribution with a standard deviation value of 0.01, which ensures that only particles observed at the same time are matched. During MOSAiC, the data acquisition computer CPUs turned out to be too slow to keep up with processing during heavy snowfall. With the additional impact of a bug in the data acquisition code and drifting computer clocks when the network connection to the ship’s reference clock were interrupted, the particle matching for the MOSAiC data set often requires manual adjustment. These problems have been resolved for later campaigns so that matching now works fully automatic.

The joint product of the probabilities from \(\Delta h\), \(\Delta z\), and \(\Delta i\) is considered a match score, which describes the quality of the particle match. Manual inspection revealed that the number of false matches increases strongly for match scores less than 0.001, which is used as a cut-off criterion. Assuming that the probabilities are correctly determined, this implies that 0.1% of particle matches are falsely rejected, resulting in a negligible bias.

For each particle, its three-dimensional position is provided and all per-particle variables from the detection are carried forward to the matched particle product level1match. The ratio of matched to observed particles from a single camera varies with the average particle size, since larger particles can be identified even when they are out of focus, and varies between approximately 10% and 90%. _[*]

VISSSlib.matching API

VISSSlib.matching.rotate_L2F(L_x, L_y, L_z, phi, theta, psi)

Rotate from leader to follower coordinate system.

Parameters

L_xfloat

Leader x coordinate (in common xyz)

L_yfloat

Leader y coordinate (in common xyz)

L_zfloat

Leader z coordinate (in common xyz)

phifloat

Follower roll in degrees

thetafloat

Follower pitch in degrees

psifloat

Follower yaw in degrees

Returns

tuple

Follower x, y, z coordinates

VISSSlib.matching.shiftRotate_L2F(L_x, L_y, L_z, phi, theta, psi, Olx, Ofy, Ofz)

Shift and rotate from leader to follower coordinate system.

Parameters

L_xfloat

Leader x coordinate (in common xyz)

L_yfloat

Leader y coordinate (in common xyz)

L_zfloat

Leader z coordinate (in common xyz)

phifloat

Follower roll in degrees

thetafloat

Follower pitch in degrees

psifloat

Follower yaw in degrees

Olxfloat

Leader shift in x direction

Ofyfloat

Follower shift in y direction

Ofzfloat

Follower shift in z direction

Returns

tuple

Follower x, y, z coordinates

VISSSlib.matching.rotate_F2L(F_xp, F_yp, F_zp, phi, theta, psi)

Rotate from follower to leader coordinate system.

Parameters

F_xpfloat

Follower x coordinate (in common xyz)

F_ypfloat

Follower y coordinate (in common xyz)

F_zpfloat

Follower z coordinate (in common xyz)

phifloat

Follower roll in degrees

thetafloat

Follower pitch in degrees

psifloat

Follower yaw in degrees

Returns

tuple

Leader x, y, z coordinates

VISSSlib.matching.shiftRotate_F2L(F_x, F_y, F_z, phi, theta, psi, Olx, Ofy, Ofz)

Shift and rotate from follower to leader coordinate system.

Parameters

F_xfloat

Follower x coordinate (in common xyz)

F_yfloat

Follower y coordinate (in common xyz)

F_zfloat

Follower z coordinate (in common xyz)

phifloat

Follower roll in degrees

thetafloat

Follower pitch in degrees

psifloat

Follower yaw in degrees

Olxfloat

Leader shift in x direction

Ofyfloat

Follower shift in y direction

Ofzfloat

Follower shift in z direction

Returns

tuple

Leader x, y, z coordinates

VISSSlib.matching.calc_L_z(L_x, F_yp, F_zp, phi, theta, psi)

Estimate z coordinate for the leader based on combined leader and follower measurements.

Parameters

L_xfloat

x measurement of leader

F_ypfloat

y measurement of follower without shift

F_zpfloat

z measurement of follower without shift

phifloat

Follower roll in degrees

thetafloat

Follower pitch in degrees

psifloat

Follower yaw in degrees

Returns

float

z coordinate as seen by leader ignoring offsets

VISSSlib.matching.calc_L_z_withOffsets(L_x, F_y, F_z, camera_phi=0, camera_theta=0, camera_psi=0, camera_Ofy=0, camera_Ofz=0, camera_Olx=0)

Estimate z coordinate for the leader based on combined leader and follower measurements.

Parameters

L_xfloat

x measurement of leader

F_yfloat

y measurement of follower

F_zfloat

z measurement of follower

camera_phifloat, optional

Follower roll in degrees (default is 0)

camera_thetafloat, optional

Follower pitch in degrees (default is 0)

camera_psifloat, optional

Follower yaw in degrees (default is 0)

camera_Ofyfloat, optional

Follower shift in y direction (default is 0)

camera_Ofzfloat, optional

Follower shift in z direction (default is 0)

camera_Olxfloat, optional

Leader shift in x direction (default is 0)

Returns

float

z coordinate as seen by leader

VISSSlib.matching.forward(x, L_x=None, F_y=None, F_z=None)

Forward model for pyOptimalEstimation.

Parameters

xpandas Series

State vector “phi”, “theta”, “psi”, “Ofy”, “Ofz”, “Olx”

L_xarray, optional

x coordinate as seen by the leader (default is None)

F_yarray, optional

y coordinate as seen by the follower (default is None)

F_zarray, optional

z coordinate as seen by the follower (default is None)

Returns

pandas Series

z coordinate as seen by leader

VISSSlib.matching.retrieveRotation(dat3, x_ap, x_cov_diag, y_cov_diag, config, verbose=False, maxIter=30)

Apply Optimal Estimation to retrieve rotation of cameras.

Parameters

dat3xarray Dataset

Input data containing particle information

x_apdict

A priori values for state variables

x_cov_diagarray-like

Diagonal elements of a priori covariance matrix

y_cov_diagarray-like

Diagonal elements of observation covariance matrix

configobject

Configuration object with camera settings

verbosebool, optional

Whether to print verbose output (default is False)

maxIterint, optional

Maximum number of iterations (default is 30)

Returns

tuple

(x_op, x_op_err, dgf_x) - Optimal state vector, errors, and goodness of fit

VISSSlib.matching.probability(x, mu, sigma, delta)

Calculate probability using normal distribution.

Parameters

xarray-like

Values to calculate probability for

mufloat

Mean of the distribution

sigmafloat

Standard deviation of the distribution

deltafloat

Width of integration interval

Returns

array-like

Probability values

VISSSlib.matching.step(x, mu, sigma)

Step function for probability calculation.

Parameters

xarray-like

Values to calculate probability for

mufloat

Mean value for comparison

sigmafloat

Threshold for comparison

Returns

array-like

Binary values (0 or 1) based on comparison

VISSSlib.matching.removeDoubleCounts(mPart, mProp, doubleCounts)

Remove duplicate particle matches.

Parameters

mPartarray-like

Particle match indices

mProparray-like

Match probabilities

doubleCountsarray-like

Indices of particles that appear multiple times

Returns

tuple

Updated mPart and mProp arrays with duplicates removed

VISSSlib.matching.doMatch(leader1D, follower1D, sigmaIn, mu, delta, config, rotate, ptpTime, minProp=1e-10, minNumber4Stats=10, maxMatches=100, indexOffset=0, testing=False)

Match particles between leader and follower cameras.

Parameters

leader1Dxarray Dataset

Leader camera particle data

follower1Dxarray Dataset

Follower camera particle data

sigmaIndict or str

Sigma values for matching criteria or ‘default’

mudict

Mean values for matching criteria

deltadict

Delta values for matching criteria

configobject

Configuration object with camera settings

rotatedict

Rotation parameters

ptpTimebool

Whether to use PTP time synchronization

minPropfloat, optional

Minimum required probability (default is 1e-10)

minNumber4Statsint, optional

Minimum number of samples for statistics (default is 10)

maxMatchesint, optional

Maximum number of matches to consider (default is 100)

indexOffsetint, optional

Offset for pairing indices (default is 0)

testingbool, optional

Whether to generate test plots (default is False)

Returns

tuple

(matchedDat, disputedPairs, new_sigma, new_mu) - Matched data, disputed pairs, updated sigma and mu values

VISSSlib.matching.get3DPosition(leaderDat, followerDat, config)

Get 3D positions from leader and follower data.

Parameters

leaderDatxarray Dataset

Leader camera particle data

followerDatxarray Dataset

Follower camera particle data

configobject

Configuration object with camera settings

Returns

tuple

(L_x, L_z, F_y, F_z) - Position coordinates

VISSSlib.matching.get3DCentroid(leaderDat, followerDat, config)

Get 3D centroids from leader and follower data.

Parameters

leaderDatxarray Dataset

Leader camera particle data

followerDatxarray Dataset

Follower camera particle data

configobject

Configuration object with camera settings

Returns

tuple

(L_x, L_z, F_y, F_z) - Centroid coordinates

VISSSlib.matching.addPosition(matchedDat, rotate, rotate_err, config)

Add position variable to match dataset based on retrieved rotation parameters.

Parameters

matchedDatxarray Dataset

Matched particle data

rotatedict

Rotation parameters

rotate_errdict

Rotation parameter errors

configobject

Configuration object with camera settings

Returns

xarray Dataset

Updated dataset with position information added

VISSSlib.matching.doMatchSlicer(leader1D, follower1D, sigma, mu, delta, config, rotate, ptpTime, minProp=1e-10, maxMatches=100, minNumber4Stats=10, chunckSize=700, testing=False)

Do matching with slicing to handle memory constraints.

Parameters

leader1Dxarray Dataset

Leader camera particle data

follower1Dxarray Dataset

Follower camera particle data

sigmadict

Sigma values for matching criteria

mudict

Mean values for matching criteria

deltadict

Delta values for matching criteria

configobject

Configuration object with camera settings

rotatedict

Rotation parameters

ptpTimebool

Whether to use PTP time synchronization

minPropfloat, optional

Minimum required probability (default is 1e-10)

maxMatchesint, optional

Maximum number of matches to consider (default is 100)

minNumber4Statsint, optional

Minimum number of samples for statistics (default is 10)

chunckSizeint, optional

Size of data chunks (default is 700)

testingbool, optional

Whether to generate test plots (default is False)

Returns

tuple

(matchedDat, disputedPairs, new_sigma, new_mu) - Matched data, disputed pairs, updated sigma and mu values

VISSSlib.matching.matchParticles(fnameLv1Detect, config, y_cov_diag=2.7224999999999997, version='0.0', chunckSize=1000, rotate='config', rotate_err='config', maxDiffMs='config', rotationOnly=False, nPoints=500, sigma='default', nSamples4rot=300, minSamples4rot=100, testing=False, minDMax4rot=0, singleParticleFramesOnly=False, doRot=False, writeNc=True, offsetsOnly=False, subset=None, maxIter=30, skipExisting=True)

Match particles between leader and follower cameras.

Parameters

fnameLv1Detectstr

Path to level1 detect file

configobject

Configuration object with camera settings

y_cov_diagfloat, optional

Observation covariance diagonal (default is 1.65**2)

versionstr, optional

Version string (default is __version__)

chunckSizeint, optional

Size of data chunks (default is 1000)

rotatedict or str, optional

Initial rotation parameters or “config” (default is “config”)

rotate_errdict or str, optional

Initial rotation parameter errors or “config” (default is “config”)

maxDiffMsfloat or str, optional

Maximum time difference in milliseconds or “config” (default is “config”)

rotationOnlybool, optional

Whether to only estimate rotation (default is False)

nPointsint, optional

Number of points for estimation (default is 500)

sigmadict or str, optional

Sigma values for matching criteria or “default” (default is “default”)

nSamples4rotint, optional

Number of samples for rotation estimation (default is 300)

minSamples4rotint, optional

Minimum samples for rotation estimation (default is 100)

testingbool, optional

Whether to generate test plots (default is False)

minDMax4rotfloat, optional

Minimum DMax threshold for rotation estimation (default is 0)

singleParticleFramesOnlybool, optional

Whether to use only single particle frames (default is False)

doRotbool, optional

Whether to perform rotation estimation (default is False)

writeNcbool, optional

Whether to write NetCDF files (default is True)

offsetsOnlybool, optional

Whether to only estimate offsets (default is False)

subsettuple, optional

Subset of particles to process (default is None)

maxIterint, optional

Maximum iterations for optimization (default is 30)

skipExistingbool, optional

Whether to skip existing files (default is True)

Returns

tuple

(fname1Match, matchedDat, rotate_final, rotate_err_final, nLeader, nFollower, nMatched, errors) - Results of matching

VISSSlib.matching.createMetaRotation(case, config, skipExisting=True, version='0.0', y_cov_diag=2.7224999999999997, chunckSize=1000, rotate='config', rotate_err='config', maxDiffMs='config', nPoints=500, sigma='default', minDMax4rot=10, nSamples4rot=300, minSamples4rot=50, testing=False, completeDaysOnly=True, writeNc=True, stopOnFailure=False, maxAgeDaysPrevFile=1, doPlots=True)

Create meta rotation data for camera alignment.

Parameters

casestr

Case identifier

configobject

Configuration object with camera settings

skipExistingbool, optional

Whether to skip existing files (default is True)

versionstr, optional

Version string (default is __version__)

y_cov_diagfloat, optional

Observation covariance diagonal (default is 1.65**2)

chunckSizeint, optional

Size of data chunks (default is 1000)

rotatedict or str, optional

Initial rotation parameters or “config” (default is “config”)

rotate_errdict or str, optional

Initial rotation parameter errors or “config” (default is “config”)

maxDiffMsfloat or str, optional

Maximum time difference in milliseconds or “config” (default is “config”)

nPointsint, optional

Number of points for estimation (default is 500)

sigmadict or str, optional

Sigma values for matching criteria or “default” (default is “default”)

minDMax4rotfloat, optional

Minimum DMax threshold for rotation estimation (default is 10)

nSamples4rotint, optional

Number of samples for rotation estimation (default is 300)

minSamples4rotint, optional

Minimum samples for rotation estimation (default is 50)

testingbool, optional

Whether to generate test plots (default is False)

completeDaysOnlybool, optional

Whether to process only complete days (default is True)

writeNcbool, optional

Whether to write NetCDF files (default is True)

stopOnFailurebool, optional

Whether to stop on failure (default is False)

maxAgeDaysPrevFileint, optional

Maximum age of previous file in days (default is 1)

Returns

tuple

(metaRotation, fnameMetaRotation) - Meta rotation data and file name

VISSSlib.matching.manualRotationEstimate(cases, settings, nPoints=1000, iterations=4, minSamples4rot=90)

Estimate camera rotation parameters through iterative particle matching.

This function processes multiple cases to estimate optimal camera rotation parameters by iteratively refining the rotation estimate using particle matching. Each case undergoes up to 4 iterations of matching with progressively refined parameters.

Parameters

caseslist of str

List of case identifiers to process in format [“YYYYMMDD-HHMMSS”]

settingsdict or str

Configuration settings or path to settings file

nPointsint, optional

Number of points to use in matching (default=1000)

iterationsint, optional

Number of iterations (default=4)

Returns

str

yaml Dictionary with case names as keys and rotation parameters as values Format: {case: {“transformation”: dict, “transformation_err”: dict}}

Notes

The function performs these steps for each case: 1. Load level1 detection data 2. Calculate minimum particle size (minSize) for filtering 3. Run up to 4 iterations of particle matching:

  • 1st iteration: Full parameter set with strict filters

  • Subsequent iterations: Relaxed parameters using previous rotation estimate

  1. Validate results at each iteration

  2. Store final rotation parameters if all validations pass