memthick#

class cryocat.analysis.memthick.MeshNormalVoter(points, initial_normals, radius_hit, batch_size=2000, logger=None)#

Bases: object

Class for refining normals and separating bilayer surfaces.

This class handles the refinement of surface normal vectors using a voting-based approach, and separates the bilayer into inner and outer surfaces based on normal directions.

build_adjacency(max_neighbors=100)#

Build weighted adjacency graph for normal propagation.

Parameters:
max_neighborsint

Maximum number of neighbors per point

Returns:
graphscipy.sparse.csr_matrix

Weighted adjacency graph

log(message)#

Log message using the logger if available, otherwise print

orient_normals()#

Orient normals consistently using minimum spanning tree.

Returns:
refined_normalsndarray

Consistently oriented normal vectors

process()#

Run full processing pipeline.

Returns:
surface1_maskndarray

Boolean mask for first surface

surface2_maskndarray

Boolean mask for second surface

refine_normals()#

Refine normal directions using weighted average of neighbor normals.

Returns:
refined_normalsndarray

Refined normal vectors

separate_bilayer()#

Separate bilayer surfaces using marching cubes orientation.

Since marching cubes gives outward-pointing normals, we can use a simple dot product test with a direction vector to separate surfaces.

Returns:
surface1_maskndarray

Boolean mask for first surface

surface2_maskndarray

Boolean mask for second surface

cryocat.analysis.memthick.create_vertex_volume(aligned_vertices, membrane_mask_shape)#

Create binary volume marking vertex positions.

Parameters:
aligned_verticesnp.ndarray

2D array (N, 3) of vertex coordinates in voxel units

membrane_mask_shapetuple

Shape (z, y, x) of the target volume

Returns:
np.ndarray

3D binary volume with 1 at vertex positions, 0 elsewhere

cryocat.analysis.memthick.extract_and_validate_surface(segmentation, membrane_mask, mesh_sampling, logger=None)#

Extract and validate surface points using marching cubes.

Parameters:
segmentationnp.ndarray

3D segmentation volume (passed to extract_surface_points)

membrane_masknp.ndarray

3D binary mask for membrane of interest

mesh_samplingint

Step size for marching cubes algorithm

loggerlogging.Logger, optional

Logger instance

Returns:
aligned_verticesnp.ndarray or None

2D array (N, 3) of validated surface vertices

aligned_normalsnp.ndarray or None

2D array (N, 3) of corresponding normal vectors

vertex_volumenp.ndarray or None

3D binary volume marking vertex positions

Notes

Returns (None, None, None) if no valid surface points found.

cryocat.analysis.memthick.extract_intensity_profile(thickness_df, tomo, voxel_size=None, intensity_extension_voxels=10, intensity_normalize_method='zscore', logger=None)#

Extract intensity profiles between paired membrane points with physical units.

This function computes intensity profiles along lines connecting matched surface points from thickness measurements. It extracts intensity values from the tomogram along vectors extending beyond the matched points, providing data for quality assessment and filtering of thickness measurements.

The function performs the following steps:

  1. Input validation: Checks for required columns and valid coordinate pairs

  2. Tomogram normalization: Applies specified normalization method

  3. Profile extraction: Computes intensity values along each line using linear interpolation

  4. Coordinate scaling: Converts voxel coordinates to physical units

  5. Extension: Extends profiles beyond matched points for better analysis

Intensity profiles are essential for validating thickness measurements by detecting characteristic bilayer features (dual minima, central maximum).

Parameters:
thickness_dfpd.DataFrame

DataFrame containing paired point coordinates for thickness measurements. Must have columns [‘x1_voxel’, ‘y1_voxel’, ‘z1_voxel’, ‘x2_voxel’, ‘y2_voxel’, ‘z2_voxel’] with coordinates in voxel units. Invalid or NaN coordinate pairs are automatically filtered out.

tomonp.ndarray

3D tomogram array with shape (Z, Y, X) in ZYX order. The tomogram should have the same dimensions as the segmentation used for thickness measurement. Intensity values are extracted from this volume.

voxel_sizefloat, optional

Voxel size in nanometers for physical coordinate scaling. If None, only voxel coordinates are returned. If provided, physical coordinates in nanometers are added to each profile dictionary.

intensity_extension_voxelsint, default 10

Number of voxels to extend beyond the midpoint in each direction. Larger values provide more context for intensity analysis but may include irrelevant regions. Typical values range from 10-20 voxels.

intensity_normalize_method{‘zscore’, ‘minmax’, ‘percentile’, ‘none’}, default ‘zscore’

Normalization method to apply to tomogram before extraction: - ‘zscore’: Standardize to zero mean and unit variance - ‘minmax’: Scale to range [0, 1] - ‘percentile’: Clip to 1st-99th percentile range - ‘none’: Use original intensity values

loggerlogging.Logger, optional

Logger instance for status messages

Returns:
List[Dict]

List of profile dictionaries, one for each valid coordinate pair. Each dictionary contains:

Core profile data: - ‘profile’: np.ndarray of intensity values along the line - ‘p1’: np.ndarray, coordinates of first point (voxel units) - ‘p2’: np.ndarray, coordinates of second point (voxel units) - ‘midpoint’: np.ndarray, midpoint coordinates (voxel units) - ‘start’: np.ndarray, extended start coordinates (voxel units) - ‘end’: np.ndarray, extended end coordinates (voxel units)

Physical coordinates (if voxel_size provided): - ‘p1_nm’: np.ndarray, first point in nanometers - ‘p2_nm’: np.ndarray, second point in nanometers - ‘midpoint_nm’: np.ndarray, midpoint in nanometers - ‘start_nm’: np.ndarray, extended start in nanometers - ‘end_nm’: np.ndarray, extended end in nanometers - ‘voxel_size’: float, voxel size used for scaling

Raises:
ValueError

If required columns are missing from thickness_df. If tomo is not a 3D array. If voxel_size is negative.

IndexError

If any coordinate pairs are outside tomogram bounds.

Notes

  • Profiles extend beyond the matched points by intensity_extension_voxels in both directions

  • Invalid or NaN coordinate pairs are automatically filtered out

  • Interpolation is performed using scipy.ndimage.map_coordinates with linear interpolation

  • Physical coordinates are computed by multiplying voxel coordinates by voxel_size

  • The function handles variable line lengths automatically

  • Processing time scales with the number of coordinate pairs and extension distance

Examples

Extract profiles with physical units as a standalone function:

>>> profiles = extract_intensity_profile(
...     thickness_df=df,
...     tomo=tomogram_array,
...     voxel_size=0.788,
...     intensity_extension_voxels=10,
...     intensity_normalize_method='zscore'
... )
>>> print(f"Profile has {len(profiles[0]['profile'])} intensity points")
>>> print(f"Physical distance: {np.linalg.norm(profiles[0]['p2_nm'] - profiles[0]['p1_nm']):.2f} nm")
cryocat.analysis.memthick.extract_surface_points(segmentation, membrane_mask, mesh_sampling=1, logger=None)#

Extract surface points and normals using marching cubes algorithm.

This function processes a binary membrane mask to identify surface voxels and compute their normal vectors. It uses the marching cubes algorithm from scikit-image to extract the surface mesh, then validates each vertex to ensure it lies on the true segmentation boundary.

The function performs the following steps: 1. Applies marching cubes to extract surface mesh 2. Converts vertex coordinates to integer voxel positions 3. Validates each vertex using is_surface_point() function 4. Removes duplicate vertices at the same position 5. Returns validated vertices with corresponding normal vectors

Parameters:
segmentationnp.ndarray

3D segmentation volume (unused, kept for interface compatibility)

membrane_masknp.ndarray

3D binary mask for membrane of interest

mesh_samplingint, default 1

Step size for marching cubes algorithm. Larger values reduce computational cost but may miss fine surface details. - 1: Full resolution (most accurate, slowest) - 2: Half resolution (2x faster, some detail loss) - 4: Quarter resolution (4x faster, significant detail loss)

loggerlogging.Logger, optional

Logger instance

Returns:
aligned_verticesnp.ndarray or None

2D array (N, 3) of surface vertex coordinates in voxel units

aligned_normalsnp.ndarray or None

2D array (N, 3) of surface normal vectors

Notes

Only returns vertices that pass surface validation (is_surface_point). Returns (None, None) if extraction fails or no valid points found.

cryocat.analysis.memthick.filter_intensity_profiles(profiles, thickness_df, intensity_min_snr=0.2, intensity_central_max_required=True, intensity_extension_range=(-10, 10), intensity_margin_factor=0.1, require_both_minima_in_region=True, smooth_sigma=0.0, edge_fraction=0.2, logger=None)#

Filter intensity profiles using quality criteria and geometric constraints.

This function applies a two-pass filtering approach to validate intensity profiles and filter out low-quality thickness measurements. It uses both signal quality metrics and geometric constraints to ensure only reliable bilayer measurements are retained.

Two-Pass Filtering Strategy:

  1. Pass 1 - Characterization: Identifies candidate profiles using loose criteria to establish dataset characteristics and statistics. This pass is used for analysis only, not for filtering.

  2. Pass 2 - Quality Filtering: Applies strict criteria to filter profiles based on: - Dual minima detection (representing lipid headgroups) - Central maximum requirement (representing lipid tails) - Signal-to-noise ratio validation - Geometric position constraints

Quality Criteria:

  • Dual Minima: Must detect at least two minima in the profile

  • Central Maximum: Must have a maximum between the two minima

  • Signal Quality: Minima must meet minimum SNR requirements

  • Position Validation: Minima must be positioned between matched points

  • Geometric Constraints: Optional margin for position flexibility

Parameters:
profilesList[Dict]

List of intensity profile dictionaries from extract_intensity_profile(). Each profile should contain ‘profile’, ‘p1’, ‘p2’, ‘start’, ‘end’, and ‘midpoint’ keys with appropriate coordinate data.

thickness_dfpd.DataFrame

DataFrame containing thickness measurement metadata. Must have the same number of rows as profiles. Used for coordinate validation and result mapping.

intensity_min_snrOptional[float], default 0.2

Minimum signal-to-noise ratio for minima prominence. - If None: SNR filtering is disabled (position-based filtering only) - If float: Minima must have prominence >= intensity_min_snr × baseline noise - Typical values: 1.0-3.0 (higher = stricter quality requirements) - Lower values may include noisy profiles, higher values may exclude valid ones

intensity_central_max_requiredbool, default True

Whether to require a central maximum between the two detected minima. This validates the bilayer structure where lipid tails create a central intensity peak between the headgroup minima.

intensity_extension_rangeTuple[float, float], default (-10, 10)

Distance range in voxel units to analyze around the profile midpoint. Profiles are analyzed within this range for feature detection. - First value: Minimum distance from midpoint - Second value: Maximum distance from midpoint - Typical range: (-8, 8) to (-15, 15) voxels

intensity_margin_factorfloat, default 0.1

Allowed margin for minima detection outside the measurement region as a fraction of the measurement span. - 0.0: Minima must be exactly between matched points (strictest) - 0.1: 10% margin allowed (recommended for most cases) - 0.3: 30% margin allowed (most permissive) - Higher values accommodate measurement uncertainty

require_both_minima_in_regionbool, default True

If True, both minima must be within the extended region for a profile to pass filtering. If False, only one minimum is required. - True: Ensures complete bilayer detection (recommended) - False: More permissive, may include partial bilayer profiles

smooth_sigmafloat, default 0.0

Gaussian smoothing parameter for intensity profiles. - 0.0: No smoothing (preserves original profile features) - 0.5-1.0: Light smoothing (reduces noise) - 1.5-2.0: Moderate smoothing (may blur features) - Higher values: Heavy smoothing (may lose important features)

edge_fractionfloat, default 0.2

Fraction of profile edges used for baseline noise calculation. - 0.1: Use 10% of profile edges for baseline (narrow baseline) - 0.2: Use 20% of profile edges for baseline (recommended) - 0.3: Use 30% of profile edges for baseline (wide baseline) - Used to calculate signal-to-noise ratios

loggerlogging.Logger, optional

Logger instance for status messages. If None, prints to stdout. Used to report filtering progress and results.

Returns:
Dict

Comprehensive filtering results containing:

Core Results: - ‘pass1_candidates’: List of Pass 1 characterization results - ‘final_results’: List of Pass 2 filtering results - ‘filtered_profiles’: List of profiles that passed filtering - ‘filtered_thickness_df’: DataFrame of filtered thickness data

Statistics and Analysis: - ‘statistics’: Comprehensive filtering statistics - ‘dataset_characteristics’: Dataset-wide feature characteristics - ‘parameters’: User-specified filtering parameters

Statistics Details: - ‘total_profiles’: Total number of profiles analyzed - ‘profiles_passed’: Number of profiles passing all criteria - ‘pass_rate’: Fraction of profiles passing filters - ‘failure_analysis’: Breakdown of failure reasons - ‘quality_metrics’: Statistical analysis of passed profiles

Dataset Characteristics: - ‘median_separation’: Typical distance between minima - ‘median_prominence_snr’: Typical signal quality - ‘n_candidates’: Number of Pass 1 candidates

Raises:
ValueError

If profiles and thickness_df have incompatible lengths. If intensity_extension_range is invalid (min >= max). If intensity_margin_factor is negative.

RuntimeError

If profile processing fails unexpectedly.

Notes

Filtering Logic: - Profiles are processed individually with comprehensive feature detection - Minima detection uses scipy.signal.find_peaks with prominence filtering - Position validation ensures minima fall within measurement constraints - SNR calculation uses edge regions for baseline noise estimation

Performance Considerations: - Processing time scales with number of profiles and profile length - Memory usage scales with profile count and feature data storage - Smoothing increases computation time but may improve feature detection

Quality Trade-offs: - Stricter SNR requirements improve quality but reduce coverage - Larger position margins increase coverage but may include lower quality data - Central maximum requirement ensures bilayer structure but may exclude valid cases

Examples

Basic filtering with default parameters:

>>> from memthick import filter_intensity_profiles
>>> import pandas as pd
>>>
>>> # Filter profiles with standard criteria
>>> results = filter_intensity_profiles(
...     profiles=extracted_profiles,
...     thickness_df=thickness_data,
...     intensity_min_snr=0.2,
...     intensity_central_max_required=True,
...     intensity_extension_range=(-10, 10)
... )
>>>
>>> print(f"Profiles passed: {results['statistics']['profiles_passed']}")
>>> print(f"Pass rate: {results['statistics']['pass_rate']:.1%}")

Custom filtering parameters:

>>> # More permissive filtering
>>> results_permissive = filter_intensity_profiles(
...     profiles=extracted_profiles,
...     thickness_df=thickness_data,
...     intensity_min_snr=None,  # Disable SNR filtering
...     intensity_central_max_required=False,  # Don't require central maximum
...     intensity_margin_factor=0.2,  # 20% position margin
...     require_both_minima_in_region=False  # Only one minimum required
... )
>>> # Stricter filtering
>>> results_strict = filter_intensity_profiles(
...     profiles=extracted_profiles,
...     thickness_df=thickness_data,
...     intensity_min_snr=2.0,  # High SNR requirement
...     intensity_central_max_required=True,
...     intensity_margin_factor=0.0,  # Exact position requirement
...     smooth_sigma=0.5  # Light smoothing
... )

Analyzing failure reasons:

>>> failure_analysis = results['statistics']['failure_analysis']
>>> for reason, count in failure_analysis.items():
...     print(f"{reason}: {count} profiles")

Accessing quality metrics:

>>> quality_metrics = results['statistics']['quality_metrics']
>>> snr_stats = quality_metrics['prominence_snr_stats']
>>> print(f"Mean SNR: {snr_stats['mean']:.1f}x")
>>> print(f"Median SNR: {snr_stats['median']:.1f}x")
cryocat.analysis.memthick.find_all_possible_matches_kernel(points, normals, surface1_mask, surface2_mask, match_distances, match_indices, match_counts, max_thickness_voxels, max_angle_cos, max_matches_per_point)#

CUDA kernel to find all possible matches for each surface1 point.

Parameters:
pointsndarray

Vertex coordinates

normalsndarray

Normal vectors

surface1_maskndarray

Boolean mask for first surface

surface2_maskndarray

Boolean mask for second surface

match_distancesndarray

Output array for match distances

match_indicesndarray

Output array for match indices

match_countsndarray

Output array for match counts

max_thickness_voxelsfloat

Maximum thickness in voxel units

max_angle_cosfloat

Cosine of maximum angle for cone search

max_matches_per_pointint

Maximum number of matches per point

cryocat.analysis.memthick.find_matches_parallel(points, normals, source_mask, target_mask, target_indices, max_thickness_voxels, max_angle_cos, match_distances, match_indices, match_counts)#

Parallelized function to find matches between points on different surfaces.

Parameters:
pointsndarray

Point coordinates

normalsndarray

Normal vectors

source_maskndarray

Mask for source points

target_maskndarray

Mask for target points

target_indicesndarray

Indices of target points

max_thickness_voxelsfloat

Maximum thickness in voxel units

max_angle_cosfloat

Cosine of maximum angle

match_distancesndarray

Output array for match distances

match_indicesndarray

Output array for match indices

match_countsndarray

Output array for match counts

cryocat.analysis.memthick.find_surface_neighbors(vertex, segmentation, include_edges=True)#

Find valid surface neighbors around a vertex.

Parameters:
vertexarray-like

Coordinates [x, y, z] of the central vertex

segmentationnp.ndarray

3D binary segmentation volume

include_edgesbool, default True

Whether to include edge neighbors (12) in addition to face neighbors (6)

Returns:
list

List of [x, y, z] coordinates for valid surface neighbors

cryocat.analysis.memthick.generate_matching_statistics(thickness_results, valid_mask, point_pairs, points, surface1_mask, surface2_mask, voxel_size)#

Generate comprehensive statistics for thickness measurements.

Parameters:
thickness_resultsnp.ndarray

1D array of thickness measurements in physical units (nm)

valid_masknp.ndarray

1D boolean array indicating valid measurements

point_pairsnp.ndarray

1D array of indices for paired points

pointsnp.ndarray

2D array (N, 3) of vertex coordinates in voxel units

surface1_masknp.ndarray

1D boolean array for surface 1 points

surface2_masknp.ndarray

1D boolean array for surface 2 points

voxel_sizefloat

Voxel size in nanometers for coordinate scaling

Returns:
dict

Statistics dictionary containing: - total_points, surface1_points, surface2_points, valid_measurements - coverage_percentage - thickness statistics (mean, std, median, min, max, percentiles) - thickness_histogram with counts and bin_edges - spatial_distribution with mean and std coordinates

cryocat.analysis.memthick.generate_thickness_volume(points, thickness_results, valid_mask, segmentation, voxel_size, point_pairs)#

Create 3D volume where voxel values represent membrane thickness.

Parameters:
pointsnp.ndarray

2D array (N, 3) of point coordinates in voxel space

thickness_resultsnp.ndarray

1D array of thickness measurements in nanometers

valid_masknp.ndarray

1D boolean array indicating valid measurements

segmentationnp.ndarray

3D reference segmentation for volume dimensions

voxel_sizefloat

Voxel size in nanometers (unused but kept for consistency)

point_pairsnp.ndarray

1D array of paired point indices

Returns:
np.ndarray

3D float32 volume with thickness values in nm (NaN for unmeasured voxels)

Notes

Sets thickness values at both surface points of each valid measurement.

cryocat.analysis.memthick.get_neighbor_surface_points(vertex, segmentation, include_edges=True)#

Find neighboring surface points around a vertex.

Parameters:
vertexarray-like

Coordinates [x, y, z] of the central vertex

segmentationnp.ndarray

3D binary segmentation volume

include_edgesbool, default True

Whether to include 12 edge neighbors in addition to 6 face neighbors

Returns:
list

List of neighboring points that pass surface validation

cryocat.analysis.memthick.int_profiles_extract_clean(thickness_csv, tomo_path, output_dir, intensity_min_snr=0.2, intensity_central_max_required=True, intensity_extension_voxels=10, intensity_extension_range=(-10, 10), intensity_normalize_method='zscore', save_cleaned_df=True, save_profiles=True, save_statistics=True, intensity_margin_factor=0.1, intensity_require_both_minima=True, intensity_smooth_sigma=0.0, intensity_edge_fraction=0.2, logger=None)#

Workflow for extracting intensity profiles, filtering, and saving results.

Parameters:
thickness_csvUnion[str, Path]

Path to thickness CSV file

tomo_pathUnion[str, Path]

Path to tomogram file

output_dirUnion[str, Path]

Directory to save results

intensity_min_snrOptional[float], default 0.2

Minimum SNR for minima prominence. If None, SNR filtering is disabled.

intensity_central_max_requiredbool

Whether to require central maximum

intensity_extension_voxelsint

Extension distance for profile extraction

intensity_extension_rangeTuple[float, float]

Range for filtering analysis

intensity_normalize_methodLiteral

Tomogram normalization method

save_cleaned_dfbool

Whether to save cleaned DataFrame

save_profilesbool

Whether to save intensity profiles

save_statisticsbool

Whether to save statistics

intensity_margin_factorfloat, default 0.1

Allowed margin for minima detection outside measurement region (0.0=exact, 0.3=30% margin)

intensity_require_both_minimabool, default True

Whether both minima must be in extended region

intensity_smooth_sigmafloat, default 0.0

Gaussian smoothing parameter (0=no smoothing)

intensity_edge_fractionfloat, default 0.2

Fraction of profile edges for baseline calculation

loggerlogging.Logger, optional

Logger instance for status messages

Returns:
Dict

Complete analysis results

cryocat.analysis.memthick.interpolate_between_points(p1, p2, segmentation, num_points=1)#

Interpolate points between two coordinates with surface validation.

Parameters:
p1, p2array-like

Start and end point coordinates

segmentationnp.ndarray

3D binary segmentation volume for validation

num_pointsint, default 1

Number of points to interpolate

Returns:
list

List of interpolated points that pass surface validation

cryocat.analysis.memthick.interpolate_between_vertices(v1, v2, segmentation, num_points=1)#

Interpolate points between two vertices on the surface.

Parameters:
v1, v2array-like

Start and end vertex coordinates

segmentationnp.ndarray

3D binary segmentation volume for validation

num_pointsint, default 1

Number of points to interpolate between vertices

Returns:
list

List of interpolated points that pass surface validation

cryocat.analysis.memthick.interpolate_surface_if_requested(aligned_vertices, aligned_normals, membrane_mask, interpolate, interpolation_points, logger=None)#

Conditionally interpolate surface points for denser coverage.

Parameters:
aligned_verticesnp.ndarray

2D array (N, 3) of surface vertex coordinates

aligned_normalsnp.ndarray

2D array (N, 3) of normal vectors

membrane_masknp.ndarray

3D binary segmentation mask for validation

interpolatebool

Whether to perform interpolation

interpolation_pointsint

Number of points to interpolate between vertices

loggerlogging.Logger, optional

Logger instance

Returns:
verticesnp.ndarray

2D array of (possibly modified) vertex coordinates

normalsnp.ndarray

2D array of (possibly modified) normal vectors

Notes

Returns original arrays unchanged if interpolate=False.

cryocat.analysis.memthick.interpolate_surface_points(vertices, normals, segmentation, interpolation_points=1, include_edges=True, logger=None)#

Increase surface point density through interpolation and neighbor addition.

This function enhances surface coverage by adding interpolated points between consecutive vertices and including neighboring surface points around each vertex. The resulting denser surface representation improves thickness measurement accuracy and coverage.

The function performs two types of point addition:

  1. Interpolation: Adds points between consecutive vertices along the surface using linear interpolation

  2. Neighbor addition: Includes valid surface neighbors around each vertex (6 face neighbors + 12 edge neighbors if include_edges=True)

All new points are validated using is_surface_point() to ensure they lie on the true segmentation boundary.

Parameters:
verticesnp.ndarray

2D array (N, 3) of original vertex coordinates

normalsnp.ndarray

2D array (N, 3) of corresponding normal vectors

segmentationnp.ndarray

3D binary segmentation volume for validation

interpolation_pointsint, default 1

Number of points to interpolate between consecutive vertices. - 0: No interpolation (only neighbor addition) - 1: One point between each pair of vertices (recommended) - 2: Two points between each pair of vertices - Higher values increase density but may create redundant points

include_edgesbool, default True

Whether to include edge neighbors around each vertex

loggerlogging.Logger, optional

Logger instance

Returns:
dense_verticesnp.ndarray

2D array of densified vertex coordinates

dense_normalsnp.ndarray

2D array of corresponding normal vectors

Notes

  • Processing time scales with N * (1 + interpolation_points + neighbor_count)

  • Memory usage scales with the final number of vertices M

  • All new points are validated using is_surface_point() function

  • Duplicate positions are automatically removed

  • Interpolated normals are linearly interpolated and re-normalized

  • Neighbor points inherit the normal vector of their source vertex

  • The function maintains the order: original vertices, then interpolated, then neighbor points

cryocat.analysis.memthick.is_surface_point(point, segmentation)#

Validate if a point is truly on the segmentation surface boundary.

This function determines whether a given voxel position lies on the surface of a binary segmentation by checking if it is inside the segmentation and has at least one immediate neighbor outside it.

The function uses a 6-connected neighborhood check (face neighbors only) to determine surface membership. A point is considered on the surface if it satisfies both conditions: 1. The point itself is inside the segmentation (True/1) 2. At least one of its 6 face neighbors is outside the segmentation (False/0)

Parameters:
pointarray-like

Coordinates [x, y, z] of the point to check

segmentationnp.ndarray

3D binary segmentation volume where True/1 indicates inside the segmentation and False/0 indicates outside. Shape should be (Z, Y, X) in voxel coordinates.

Returns:
bool

True if the point is on the surface boundary, False otherwise. A point is on the surface if it’s inside the segmentation and has at least one face neighbor outside the segmentation.

Raises:
IndexError

If the point coordinates are outside the segmentation array bounds.

ValueError

If the segmentation array is not 3D or contains non-boolean values.

Notes

  • Uses 6-connected neighborhood (face neighbors only, not edge or corner)

  • Face neighbors are at positions: [x±1, y, z], [x, y±1, z], [x, y, z±1]

  • Edge and corner neighbors are not considered for surface detection

  • This conservative approach ensures only true boundary points are identified

  • Processing time is O(1) per point (constant time)

cryocat.analysis.memthick.main()#

Entry point for command line execution.

This function: 1) Parses CLI arguments 2) Validates inputs per selected mode 3) Dispatches to the requested workflow (full, surface, thickness, intensity)

Exit codes are communicated via printed error messages and early returns.

cryocat.analysis.memthick.measure_membrane_thickness(segmentation_path, input_csv, output_csv=None, output_dir=None, max_thickness=8.0, max_angle=1.0, save_thickness_mrc=False, direction='1to2', use_gpu=True, num_cpu_threads=None, logger=None)#

Measure membrane thickness between separated surface points.

Parameters:
segmentation_pathstr

Path to original MRC segmentation file (for metadata)

input_csvstr

Path to CSV file with vertices, normals, and surface assignments

output_csvstr, optional

Path for thickness results CSV (auto-generated if None)

output_dirstr, optional

Output directory (defaults to input CSV directory)

max_thicknessfloat, default 8.0

Maximum allowed thickness in nanometers

max_anglefloat, default 1.0

Maximum angle for cone search in degrees

save_thickness_mrcbool, default False

Whether to save thickness volume as MRC file

directionstr, default “1to2”

Measurement direction: “1to2” (surface1→surface2) or “2to1”

use_gpubool, default True

Whether to use GPU acceleration if available

num_cpu_threadsint, optional

Number of CPU threads (if using CPU implementation)

loggerlogging.Logger, optional

Logger instance

Returns:
output_csvstr or None

Path to thickness measurements CSV file

stats_filestr or None

Path to statistics log file

Notes

Output CSV contains only valid thickness measurements with complete information about both matched surface points. Falls back to CPU if GPU not available.

cryocat.analysis.memthick.measure_thickness_cpu(points, normals, surface1_mask, surface2_mask, voxel_size, max_thickness_nm=8.0, max_angle_degrees=1.0, direction='1to2', num_threads=None, logger=None, max_matches_per_point=25)#

CPU-based thickness measurement with parallelization.

cryocat.analysis.memthick.measure_thickness_gpu(points, normals, surface1_mask, surface2_mask, voxel_size, max_thickness_nm=8.0, max_angle_degrees=1.0, direction='1to2', logger=None)#

GPU-accelerated membrane thickness measurement with one-to-one point matching.

This function measures membrane thickness between two separated surfaces using CUDA-accelerated nearest neighbor search. It ensures each surface point is measured exactly once by implementing a one-to-one matching algorithm that prioritizes the shortest valid distances.

The function performs the following steps:

  1. GPU preparation: Transfers data to GPU memory and configures CUDA grid

  2. Parallel search: Uses CUDA kernel to find all possible matches for each source point within geometric constraints

  3. CPU post-processing: Processes matches on CPU to ensure one-to-one assignment and converts results to physical units

  4. Validation: Applies geometric constraints (max thickness, cone angle)

Geometric constraints ensure measurement quality: - Maximum thickness: Limits search to reasonable membrane thicknesses - Cone angle: Restricts search to a cone along the normal direction - Surface separation: Ensures points are on different surfaces

Parameters:
pointsndarray

Unscaled voxel coordinates

normalsndarray

Normal vectors

surface1_mask, surface2_maskndarray

Boolean masks for each surface

voxel_sizefloat

Voxel size in nm or angstroms

max_thickness_nmfloat

Maximum thickness in nm (will be converted to voxels internally)

max_angle_degreesfloat

Maximum angle for cone search

directionstr

Direction of thickness measurement, either “1to2” (surface1 to surface2) or “2to1” (surface2 to surface1)

loggerlogging.Logger, optional

Logger instance

Returns:
thickness_resultsnp.ndarray

1D array of length N containing thickness measurements in nanometers. Only valid measurements have non-zero values; invalid measurements are set to 0.0.

valid_masknp.ndarray

1D boolean array of length N indicating which measurements are valid. True values indicate successful thickness measurements.

point_pairsnp.ndarray

1D array of length N containing indices of matched points for each source point. For valid measurements, this gives the index of the corresponding point on the opposite surface.

Raises:
RuntimeError

If CUDA is not available or GPU memory allocation fails.

ValueError

If input arrays have incompatible shapes or invalid parameters.

Notes

  • GPU Requirements: Requires CUDA-capable GPU with sufficient memory

  • Performance: Significantly faster than CPU implementation for large surfaces (typically 10-100x speedup)

  • Memory: GPU memory usage scales with N * max_matches_per_point

  • Fallback: Automatically falls back to CPU if CUDA unavailable

  • Constraints: Maximum 25 potential matches per point to manage memory

  • Accuracy: Results are identical to CPU implementation but faster

cryocat.analysis.memthick.normalize_tomogram(tomo, method='zscore', logger=None)#

Normalize tomogram intensity values using various strategies.

Parameters:
tomonp.ndarray

3D tomogram array

method{‘zscore’, ‘minmax’, ‘percentile’, ‘none’}, default ‘zscore’

Normalization method

loggerlogging.Logger, optional

Logger instance for status messages

Returns:
np.ndarray

Normalized tomogram

cryocat.analysis.memthick.parse_arguments()#

Build and parse command line arguments for the CLI.

The CLI supports four modes: - full: complete pipeline with optional intensity profiling - surface: surface extraction only - thickness: thickness measurement only - intensity: intensity profiling only

Returns:
argparse.Namespace

Parsed CLI arguments containing all options required by the pipeline and its sub-commands.

Examples

Typical invocations:

  • Full pipeline with intensity profiling: python memthick_250814.py seg.mrc –mode full –extract_intensity –tomo_path tomo.mrc –membrane_labels NE:1,ER:2

  • Surface extraction only: python memthick_250814.py seg.mrc –mode surface –mesh_sampling 2

  • Thickness only: python memthick_250814.py seg.mrc –mode thickness –input_csv vertices.csv

  • Intensity only: python memthick_250814.py seg.mrc –mode intensity –thickness_csv thickness.csv –tomo_path tomo.mrc

cryocat.analysis.memthick.print_summary(results, logger=None)#

Print summary of filtering results.

cryocat.analysis.memthick.process_matches_cpu2cpu(flat_matches, n_points, voxel_size)#

Process matches on CPU to ensure one-to-one matching and convert to physical units.

Parameters:
flat_matcheslist

List of tuples (distance, source_idx, target_idx)

n_pointsint

Total number of points

voxel_sizefloat

Voxel size for scaling

Returns:
thickness_resultsndarray

Thickness measurements in physical units

valid_maskndarray

Boolean mask for valid measurements

point_pairsndarray

Indices of paired points

cryocat.analysis.memthick.process_matches_gpu2cpu(match_distances, match_indices, match_counts, n_points, max_matches_per_point, voxel_size)#

Process matches on CPU to ensure one-to-one matching and convert to physical units.

Parameters:
match_distancesndarray

Match distances in voxel units

match_indicesndarray

Match indices

match_countsndarray

Match counts

n_pointsint

Number of points

max_matches_per_pointint

Maximum number of matches per point

voxel_sizefloat

Voxel size for scaling

Returns:
thickness_resultsndarray

Thickness measurements in physical units

valid_maskndarray

Boolean mask for valid measurements

point_pairsndarray

Indices of paired points

cryocat.analysis.memthick.process_membrane_segmentation(segmentation_path, output_dir=None, config_path=None, membrane_labels=None, mesh_sampling=1, interpolate=True, interpolation_points=1, refine_normals=True, radius_hit=10.0, flip_normals=True, batch_size=2000, save_vertices_mrc=False, save_vertices_xyz=False, logger=None)#

Process membrane segmentation to extract and refine surface points.

Parameters:
segmentation_pathstr

Path to input MRC segmentation file

output_dirstr, optional

Output directory (defaults to segmentation file directory)

config_pathstr, optional

Path to YAML configuration file with membrane labels

membrane_labelsdict, optional

Mapping of membrane names to label values {name: int}

mesh_samplingint, default 1

Step size for marching cubes algorithm

interpolatebool, default True

Whether to interpolate surface points for denser coverage

interpolation_pointsint, default 1

Number of points to interpolate between vertices

refine_normalsbool, default True

Whether to refine normals and separate surfaces

radius_hitfloat, default 10.0

Search radius for normal refinement (voxel units)

flip_normalsbool, default True

Whether to flip normals inward after refinement

batch_sizeint, default 2000

Batch size for normal refinement processing

save_vertices_mrcbool, default False

Whether to save vertex positions as MRC files

save_vertices_xyzbool, default False

Whether to save coordinates as XYZ point clouds

loggerlogging.Logger, optional

Logger instance

Returns:
dict or None

Mapping of membrane names to output CSV file paths Returns None if processing fails

Notes

Processes each membrane label separately through the full pipeline: 1. Surface extraction and validation 2. Optional point interpolation 3. Optional normal refinement and surface separation 4. Output file generation

cryocat.analysis.memthick.read_segmentation(segmentation_path, logger=None)#

Read segmentation data from MRC file and extract metadata.

Parameters:
segmentation_pathstr

Path to the MRC segmentation file

loggerlogging.Logger, optional

Logger instance for status messages

Returns:
segmentationnp.ndarray or None

3D segmentation data array (ZYX order)

voxel_sizefloat or None

Voxel size in nanometers

origintuple or None

Origin coordinates (x, y, z) in nanometers

shapetuple or None

Shape of the array (ZYX order)

Notes

Returns (None, None, None) if file reading fails. Voxel size is converted from angstroms to nanometers by dividing by 10.

cryocat.analysis.memthick.read_tomo(tomo_path, logger=None)#

Read tomogram data from MRC file and extract metadata.

Parameters:
tomo_pathstr

Path to the MRC tomogram file

loggerlogging.Logger, optional

Logger instance for status messages

Returns:
tomonp.ndarray or None

3D tomogram data array (ZYX order)

voxel_sizefloat or None

Voxel size in nanometers

shapetuple or None

Shape of the tomogram array (ZYX order)

cryocat.analysis.memthick.refine_mesh_normals(vertices, initial_normals, radius_hit=10.0, batch_size=2000, flip_normals=True, logger=None)#

Refine surface normals and separate bilayer surfaces.

This function improves the quality of surface normal vectors and separates the bilayer membrane into inner and outer surfaces. It uses a voting-based approach to refine normals by averaging neighboring normals with distance-weighted contributions.

The function performs three main operations:

  1. Normal refinement: Uses weighted averaging of neighbor normals within a specified radius to smooth and improve normal quality

  2. Normal orientation: Ensures consistent normal orientation across the surface using minimum spanning tree propagation

  3. Surface separation: Separates the bilayer into two surfaces based on normal direction analysis using PCA

Parameters:
verticesnp.ndarray

2D array (N, 3) of surface vertex coordinates

initial_normalsnp.ndarray

2D array (N, 3) of initial normal vectors from marching cubes

radius_hitfloat, default 10.0

Search radius for finding neighbor points in voxel units. Larger values include more neighbors but increase computation time. Typical values range from 5-20 voxels depending on surface density.

batch_sizeint, default 2000

Number of vertices to process in each batch. Larger batches are more memory-efficient but may cause memory issues with very large surfaces. Adjust based on available RAM.

flip_normalsbool, default True

Whether to flip refined normals to point inward toward the membrane interior. This is typically desired for bilayer analysis as it ensures consistent orientation relative to the membrane center.

loggerlogging.Logger, optional

Logger instance

Returns:
refined_normalsnp.ndarray

2D array of shape (N, 3) containing refined normal vectors. All vectors are unit-normalized and consistently oriented. If flip_normals=True, normals point inward toward membrane center.

surface1_masknp.ndarray

1D boolean array of length N indicating membership in the first surface. True values indicate vertices assigned to surface 1. Returns zero array if surface separation fails.

surface2_masknp.ndarray

1D boolean array for second surface assignment

Notes

  • Processing time scales with N * (average_neighbors_per_point)

  • Memory usage scales with N and the number of neighbor connections

  • Normal refinement uses Gaussian weighting based on distance

  • Surface separation requires sufficient surface coverage to work reliably

  • The function automatically falls back to original normals if refinement fails

  • Batch processing helps manage memory for large surfaces

cryocat.analysis.memthick.refine_normals_and_separate_surfaces(aligned_vertices, aligned_normals, refine_normals, radius_hit, batch_size, flip_normals, logger=None)#

Conditionally refine normals and separate bilayer surfaces.

Parameters:
aligned_verticesnp.ndarray

2D array (N, 3) of surface vertex coordinates

aligned_normalsnp.ndarray

2D array (N, 3) of initial normal vectors

refine_normalsbool

Whether to perform normal refinement

radius_hitfloat

Search radius for neighbor finding (voxel units)

batch_sizeint

Batch size for processing

flip_normalsbool

Whether to flip refined normals inward

loggerlogging.Logger, optional

Logger instance

Returns:
refined_normalsnp.ndarray

2D array of (possibly refined) normal vectors

surface1_masknp.ndarray

1D boolean array for surface 1 assignment

surface2_masknp.ndarray

1D boolean array for surface 2 assignment

Notes

Always initializes surface masks to False arrays. Only modifies them if refinement succeeds.

cryocat.analysis.memthick.run_full_pipeline(segmentation_path, output_dir=None, config_path=None, membrane_labels=None, mesh_sampling=1, interpolate=True, interpolation_points=1, refine_normals=True, radius_hit=10.0, flip_normals=True, max_thickness=8.0, max_angle=1.0, save_vertices_mrc=False, save_vertices_xyz=False, save_thickness_mrc=False, direction='1to2', batch_size=2000, use_gpu=True, num_cpu_threads=None, extract_intensity_profiles=True, tomo_path=None, intensity_extension_voxels=10, intensity_extension_range=(-10, 10), intensity_normalize_method='zscore', intensity_min_snr=0.2, intensity_central_max_required=True, intensity_save_profiles=True, intensity_save_statistics=True, intensity_tolerance=0.01, intensity_margin_factor=0.1, intensity_require_both_minima=True, intensity_smooth_sigma=0.0, intensity_edge_fraction=0.2)#

Execute complete membrane thickness analysis pipeline with optional intensity profiling.

This is the main entry point for comprehensive membrane analysis. The function orchestrates the entire workflow from surface extraction to thickness measurement and optional intensity profile analysis. It processes each membrane type separately and provides comprehensive output files for further analysis.

The pipeline consists of three main stages:

  1. Surface Processing (process_membrane_segmentation):

    • Extract surface points using marching cubes algorithm

    • Optionally interpolate points for denser coverage

    • Refine normal vectors using neighbor averaging

    • Separate bilayer into inner/outer surfaces

  2. Thickness Measurement (measure_membrane_thickness):

    • Match points between separated surfaces

    • Apply geometric constraints (max thickness, cone angle)

    • Generate thickness measurements with GPU acceleration

    • Create comprehensive output files

  3. Intensity Profiling (optional, int_profiles_extract_clean):

    • Extract intensity profiles from tomogram

    • Filter profiles using quality criteria

    • Validate thickness measurements

    • Generate quality metrics and statistics

Parameters:
segmentation_pathstr

Path to input MRC segmentation file

output_dirstr, optional

Output directory (defaults to segmentation file directory)

config_pathstr, optional

Path to YAML configuration file specifying membrane labels. Format: {“membrane_name”: label_value}. If None, uses membrane_labels parameter or defaults to {“membrane”: 1}.

membrane_labelsdict, optional

Direct specification of membrane labels as {name: value} pairs. Example: {“plasma_membrane”: 1, “nuclear_envelope”: 2}. Overrides config_path if both are provided.

**Surface Processing Parameters:**
mesh_samplingint, default 1

Step size for marching cubes algorithm. Larger values reduce computation time but may miss fine surface details. - 1: Full resolution (most accurate, slowest) - 2: Half resolution (2x faster, some detail loss) - 4: Quarter resolution (4x faster, significant detail loss)

interpolatebool, default True

Whether to interpolate surface points for denser coverage. Increases the number of surface points for better thickness measurement accuracy.

interpolation_pointsint, default 1

Number of points to interpolate between consecutive vertices. Higher values increase surface density but may create redundant points.

refine_normalsbool, default True

Whether to refine normal vectors using neighbor averaging. Improves normal quality and surface separation reliability.

radius_hitfloat, default 10.0

Search radius for normal refinement in voxel units. Larger values include more neighbors but increase computation time.

flip_normalsbool, default True

Whether to flip refined normals to point inward toward the membrane interior. Typically desired for bilayer analysis.

**Thickness Measurement Parameters:**
max_thicknessfloat, default 8.0

Maximum allowed thickness in nanometers. Typical membrane thicknesses range from 4-8 nm. Larger values may include invalid measurements.

max_anglefloat, default 1.0

Maximum cone search angle in degrees. Restricts search to a cone along the normal direction. Smaller angles provide more precise measurements.

directionstr, default “1to2”

Thickness measurement direction: - “1to2”: Measure from surface 1 to surface 2 - “2to1”: Measure from surface 2 to surface 1

save_vertices_mrcbool, default False

Whether to save vertex positions as MRC files for visualization.

save_vertices_xyzbool, default False

Whether to save coordinates as XYZ point clouds for external tools.

save_thickness_mrcbool, default False

Whether to save thickness volume as MRC file where voxel values represent thickness measurements.

**Performance Parameters:**
batch_sizeint, default 2000

Processing batch size for normal refinement. Larger batches are more memory-efficient but may cause memory issues.

use_gpubool, default True

Whether to use GPU acceleration for thickness measurement. Automatically falls back to CPU if CUDA unavailable.

num_cpu_threadsint, optional

Number of CPU threads for parallel processing. If None, uses all available cores.

**Intensity Profiling Parameters:**
extract_intensity_profilesbool, default True

Whether to perform intensity profile analysis after thickness measurement. Requires tomo_path to be specified.

tomo_pathstr, optional

Path to tomogram file for intensity profiling. Must have compatible dimensions and voxel size with the segmentation.

intensity_extension_voxelsint, default 10

Number of voxels to extend intensity profiles beyond matched points. Larger values provide more context for analysis.

intensity_extension_rangetuple, default (-10, 10)

Range for intensity profile filtering analysis in voxel units. Profiles are analyzed within this range around the midpoint.

intensity_normalize_methodstr, default ‘zscore’

Tomogram normalization method for intensity extraction:

  • ‘zscore’: Standardize to zero mean and unit variance

  • ‘minmax’: Scale to range [0, 1]

  • ‘percentile’: Clip to 1st-99th percentile range

  • ‘none’: Use original intensity values

intensity_min_snrOptional[float], default 0.2

Minimum signal-to-noise ratio for minima prominence. If None, SNR filtering is disabled (position-based filtering only). Higher values require more prominent bilayer features.

intensity_central_max_requiredbool, default True

Whether to require a central maximum between minima in intensity profiles. This validates bilayer structure.

intensity_tolerancefloat, default 0.01

Tolerance for segmentation-tomogram compatibility check in nm. Files must have matching dimensions and voxel sizes within this tolerance.

intensity_margin_factorfloat, default 0.1

Allowed margin for minima detection outside measurement region as fraction of measurement span. 0.0 = exact, 0.3 = 30% margin.

intensity_require_both_minimabool, default True

Whether both minima must be within the extended region for a profile to pass filtering.

intensity_smooth_sigmafloat, default 0.0

Gaussian smoothing parameter for intensity profiles. 0 = no smoothing, higher values smooth profiles.

intensity_edge_fractionfloat, default 0.2

Fraction of profile edges used for baseline noise calculation. Used to determine signal-to-noise ratios.

Returns:
dict or None

Results dictionary with membrane names as keys and dictionaries containing analysis results. Returns None if pipeline fails.

Each membrane result dictionary contains:

  • ‘input_csv’: Path to vertices/normals CSV file

  • ‘thickness_csv’: Path to thickness measurements CSV file

  • ‘stats_file’: Path to thickness statistics log file

  • ‘intensity_results’: Dictionary with intensity analysis results (only if extract_intensity_profiles=True)

Intensity results include:

  • ‘status’: “completed”, “skipped”, or “failed”

  • ‘analysis_results’: Complete intensity analysis results

  • ‘profiles_extracted’: Number of profiles extracted

  • ‘profiles_filtered’: Number of profiles passing quality filters

  • ‘pass_rate’: Fraction of profiles passing filters

Raises:
FileNotFoundError

If segmentation_path or tomo_path (if specified) don’t exist.

ValueError

If required parameters are invalid or incompatible. If intensity profiling is requested without tomo_path.

RuntimeError

If any pipeline stage fails unexpectedly.

Notes

Performance Considerations:

  • GPU acceleration provides 10-100x speedup for thickness measurement

  • Memory usage scales with surface complexity and batch size

  • Processing time scales with surface size and interpolation settings

Output Files:

  • *_vertices_normals.csv: Surface point coordinates and normals

  • *_thickness.csv: Thickness measurements with point pairs

  • *_thickness_stats.log: Comprehensive measurement statistics

  • *_int_profiles.pkl: Intensity profiles (if enabled)

  • *_thickness_cleaned.csv: Filtered thickness data (if enabled)

Quality Control:

  • Surface validation ensures only boundary points are used

  • Geometric constraints filter invalid thickness measurements

  • Intensity profiling provides automated quality assessment

  • Comprehensive logging tracks all processing steps

Examples

Basic pipeline without intensity profiling:

>>> from memthick import run_full_pipeline
>>>
>>> results = run_full_pipeline(
...     segmentation_path="membrane_seg.mrc",
...     output_dir="analysis_results",
...     membrane_labels={"plasma_membrane": 1, "nuclear_envelope": 2},
...     interpolate=True,
...     refine_normals=True,
...     max_thickness=6.0,
...     use_gpu=True
... )
>>>
>>> for membrane_name, result in results.items():
...     print(f"{membrane_name}: {result['thickness_csv']}")

Complete pipeline with intensity profiling:

>>> results_with_intensity = run_full_pipeline(
...     segmentation_path="membrane_seg.mrc",
...     output_dir="analysis_with_intensity",
...     extract_intensity_profiles=True,
...     tomo_path="tomogram.mrc",
...     intensity_min_snr=0.2,
...     intensity_central_max_required=True,
...     intensity_margin_factor=0.1
... )
>>>
>>> # Check intensity profiling results
>>> for membrane_name, result in results_with_intensity.items():
...     if 'intensity_results' in result:
...         int_results = result['intensity_results']
...         print(f"{membrane_name}: {int_results['profiles_filtered']} profiles passed")

Custom surface processing parameters:

>>> results_custom = run_full_pipeline(
...     segmentation_path="membrane_seg.mrc",
...     mesh_sampling=2,  # Faster processing
...     interpolate=False,  # No interpolation
...     radius_hit=5.0,  # Smaller search radius
...     batch_size=1000,  # Smaller batches
...     save_vertices_mrc=True,  # Save vertex files
...     save_thickness_mrc=True   # Save thickness volume
... )

Intensity profiling requires compatible tomogram file with matching dimensions and voxel size to the segmentation.

cryocat.analysis.memthick.save_int_results(results, thickness_csv, output_dir, profiles, save_cleaned_df=True, save_profiles=True, save_statistics=True, logger=None)#

Save files related to intensity profiles pre- and post-filtering with feature data.

Parameters:
resultsDict

Results from filter_intensity_profiles containing filtered data

thickness_csvPath

Original thickness file path (for naming)

output_dirUnion[str, Path]

Directory to save results

profilesList[Dict]

Original extracted intensity profiles from extract_intensity_profile(). Each dict contains ‘profile’, ‘p1’, ‘p2’, ‘start’, ‘end’, ‘midpoint’

save_cleaned_dfbool, default True

Whether to save cleaned thickness DataFrame as “*_thickness_cleaned.csv”

save_profilesbool, default True

Whether to save intensity profiles (both original and cleaned)

save_statisticsbool, default True

Whether to save filtering statistics as “*_filtering_stats.txt”

loggerlogging.Logger, optional

Logger instance for status messages

Returns:
Dict[str, Path]

Dictionary mapping file types to saved paths: - ‘thickness_cleaned’: Path to cleaned thickness CSV - ‘profiles_original’: Path to original intensity profiles with features - ‘profiles_cleaned’: Path to cleaned intensity profiles with features - ‘statistics’: Path to statistics file

Notes

  • Follows naming convention: base_name + “_thickness_cleaned.csv”

  • Intensity profiles saved as pickle files with feature data merged in

  • Each profile now includes ‘features’ dict with extracted minima/maxima data

  • Base name automatically extracted by removing common suffixes

cryocat.analysis.memthick.save_matching_statistics(stats, output_path, logger=None)#

Save thickness analysis statistics to formatted text file.

Parameters:
statsdict

Statistics dictionary from generate_matching_statistics()

output_pathstr

Path where statistics file will be saved

loggerlogging.Logger, optional

Logger instance for status messages

cryocat.analysis.memthick.save_thickness_volume(thickness_volume, output_path, voxel_size, origin=(0, 0, 0))#

Save thickness volume as MRC file with proper metadata.

Parameters:
thickness_volumenp.ndarray

3D volume with thickness values in nanometers

output_pathstr

Path for output MRC file

voxel_sizefloat

Voxel size in nanometers

origintuple, default (0, 0, 0)

Origin coordinates (x, y, z) in nanometers

Notes

NaN values are converted to 0 for visualization compatibility. Voxel size is converted to angstroms for MRC format.

cryocat.analysis.memthick.save_vertices_mrc_helper(vertex_volume, output_path, voxel_size, origin)#

Helper function to save vertex volume as MRC file.

Parameters:
vertex_volumenp.ndarray

3D binary volume data

output_pathstr

Path for output MRC file

voxel_sizefloat

Voxel size in nanometers

origintuple

Origin coordinates (x, y, z) in nanometers

cryocat.analysis.memthick.setup_logger(output_dir, name='MembraneThickness')#

Set up logger for the analysis with both file and console handlers.

Parameters:
output_dirstr

Directory where log file will be saved

namestr, default “MembraneThickness”

Name of the logger

Returns:
logging.Logger

Configured logger instance with file and console handlers

cryocat.analysis.memthick.update_vertex_volume_after_interpolation(aligned_vertices, membrane_mask, logger=None)#

Rebuild vertex volume to include interpolated points.

Parameters:
aligned_verticesnp.ndarray

2D array (N, 3) of all vertex coordinates (including interpolated)

membrane_masknp.ndarray

3D reference mask for volume shape

loggerlogging.Logger, optional

Logger instance

Returns:
np.ndarray

3D binary volume with 1 at all vertex positions

Notes

Recreates the entire vertex volume from current vertex list. Uses tqdm progress bar for large vertex sets.

cryocat.analysis.memthick.validate_seg_tomo_compatibility(segmentation_path, tomo_path, tolerance=0.01, logger=None)#

Validate compatibility between segmentation and tomogram files.

Checks dimensions and voxel size compatibility between a segmentation MRC file and its corresponding tomogram for intensity profile analysis.

Parameters:
segmentation_pathstr

Path to the MRC segmentation file

tomo_pathstr

Path to the MRC tomogram file

tolerancefloat, default 0.01

Tolerance for voxel size comparison (in nanometers)

loggerlogging.Logger, optional

Logger instance for status messages

Returns:
compatiblebool

True if files are compatible for intensity analysis

detailsdict

Dictionary containing compatibility details: - ‘segmentation_shape’: tuple of segmentation dimensions (ZYX) - ‘tomogram_shape’: tuple of tomogram dimensions (ZYX) - ‘segmentation_voxel_size’: float, voxel size in nm - ‘tomogram_voxel_size’: float, voxel size in nm - ‘dimensions_match’: bool, whether shapes are identical - ‘voxel_sizes_match’: bool, whether voxel sizes are within tolerance - ‘error’: str, error message if validation failed

cryocat.analysis.memthick.verify_and_save_outputs(aligned_vertices, aligned_normals, vertex_volume, surface1_mask, surface2_mask, membrane_name, base_name, output_dir, voxel_size, origin, save_vertices_mrc=False, save_vertices_xyz=False, logger=None)#

Save surface analysis outputs in multiple formats.

Parameters:
aligned_verticesnp.ndarray

2D array (N, 3) of vertex coordinates in voxel units (ZYX order)

aligned_normalsnp.ndarray

2D array (N, 3) of normal vectors (ZYX order)

vertex_volumenp.ndarray

3D binary volume marking vertex positions

surface1_masknp.ndarray

1D boolean array for surface 1 assignment

surface2_masknp.ndarray

1D boolean array for surface 2 assignment

membrane_namestr

Name identifier for this membrane

base_namestr

Base filename for outputs

output_dirstr

Output directory path

voxel_sizefloat

Voxel size in nanometers

origintuple

Origin coordinates (x, y, z) in nanometers

save_vertices_mrcbool, default False

Whether to save vertex volume as MRC file

save_vertices_xyzbool, default False

Whether to save coordinates as XYZ point cloud

loggerlogging.Logger, optional

Logger instance

Returns:
bool

True if all requested outputs saved successfully