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
Validate results at each iteration
Store final rotation parameters if all validations pass