Partitioning Module

The partitioning module implements a novel radial cytoplasmic partitioning algorithm that divides the cytoplasm between nuclear envelope (NE) and plasma membrane (PM) into concentric shells. This enables standardized spatial analysis across cells of varying geometries and imaging modalities, addressing a key challenge in quantitative cell biology.

Overview

Radial partitioning normalizes the cytoplasmic space into radial shells from NE to PM, providing a unified framework for spatial quantification. This approach is particularly powerful for:

  • Cross-cell comparisons: Eliminating biases from variations in nuclear and cellular shapes

  • Multi-modal integration: Enabling direct comparison of organelle distributions across WFM, SIM, SXT, and cryo-ET

  • Radial Distribution Function (RDF) analysis: Quantifying organelle localization patterns with fine-grained spatial resolution

Key Features

  • Pure NE-PM Pair Method: Fast and interpretable partitioning based on shell point clouds generated from NE-PM pairs

  • Boundary Extraction: Automatic identification and extraction of cellular membrane structure boundary points using Gaussian smoothing

  • Spatial Pairing: NE-PM point mapping based on angular similarity for robust pair generation

  • Coordinate Extraction: Sampling partition coordinates for downstream RDF and interaction analysis

  • 3D Visualization: Multi-level visualization tools for partitioning results

Partitioning visualization showing original masks and partition results

Figure 1: Cellular radial partitioning visualization. Left: Original PM and NE masks; Middle and Right: Radial partitioning results with 8 concentric shells from NE to PM, showing color gradient representation of spatial zones.

Partitioning Class

class ipa.processing.partitioning.Partitioning(root_dir, n_slices=8, num_cores=None)[source]

Bases: object

This class provides a comprehensive toolkit for cellular partitioning analysis, including boundary extraction, partition generation, feature calculation, and visualization. Primarily used for analyzing organelle spatial distribution between nuclear envelope and plasma membrane.

Parameters

root_dirstr

Root directory path for result outputs

n_slicesint, default=8

Number of radial partitions, dividing nucleus-membrane distance into n_slices regions

num_coresint, optional

Number of CPU cores for parallel processing, defaults to 1

Attributes

root_dirstr

Output directory path

n_slicesint

Number of partitions

num_coresint

Number of CPU cores

load_mask_data(ne_path, pm_path)[source]

Load nuclear envelope (NE) and plasma membrane (PM) mask data from files

Supports multiple file formats including numpy arrays (.npy), TIFF images (.tif/.tiff), MRC format (.mrc), and other common biological imaging formats.

Parameters:
  • ne_path (str) – Path to nuclear envelope mask file

  • pm_path (str) – Path to plasma membrane mask file

Returns:

(ne_mask, pm_mask) - Binary mask arrays for nuclear envelope and plasma membrane

Return type:

tuple of numpy.ndarray

Raises:
  • FileNotFoundError – When specified file paths do not exist

  • ValueError – When file format is unsupported or data format is incorrect

Examples

>>> ne_mask, pm_mask = partitioner.load_mask_data("nuclear.npy", "membrane.npy")
>>> print(f"NE mask shape: {ne_mask.shape}, PM mask shape: {pm_mask.shape}")
static smooth_mask(mask: ndarray, sigma: float = 1.0, close_iter: int = 2) ndarray[source]

Smooth binary mask using Gaussian filtering and morphological closing.

Parameters:
  • mask (numpy.ndarray) – Input binary mask.

  • sigma (float) – Sigma for Gaussian filter. Higher values produce smoother boundaries.

  • close_iter (int) – Number of iterations for morphological closing to fill small gaps.

Returns:

Smoothed binary mask.

Return type:

numpy.ndarray

extract_ne_pm_edges(pm_mask, ne_mask, smooth_sigma=1.0, smooth_close_iter=2)[source]

Extract nuclear envelope and plasma membrane boundary points and cellular center from 3D mask data

Uses morphological operations to extract proper boundaries instead of EDT method which may produce incorrect boundary representations.

Parameters:
  • pm_mask (numpy.ndarray) – 3D binary mask of plasma membrane, shape (Z, Y, X)

  • ne_mask (numpy.ndarray) – 3D binary mask of nuclear envelope, shape (Z, Y, X)

  • smooth_sigma (float, optional) – Sigma for Gaussian smoothing. Set to 0 to disable smoothing.

  • smooth_close_iter (int, optional) – Iterations for morphological closing to fill small gaps.

Returns:

  • center (numpy.ndarray) – Cell center coordinates, shape (3,), order (Z, Y, X)

  • ne_edge (numpy.ndarray) – Nuclear envelope boundary point coordinates array, shape (N, 3)

  • pm_edge (numpy.ndarray) – Plasma membrane boundary point coordinates array, shape (M, 3)

static min_angle(ne_points, pm_point, center)[source]

Find nuclear envelope point with minimal angle to plasma membrane point relative to center

Calculates cosine angles between vectors to find the directionally closest nuclear envelope point. Used for establishing optimal NE-PM point pair matching.

Parameters:
  • ne_points (numpy.ndarray) – Nuclear envelope point coordinates array, shape (N, 3)

  • pm_point (numpy.ndarray) – Single plasma membrane point coordinates, shape (3,)

  • center (numpy.ndarray) – Cell center coordinates, shape (3,)

Returns:

Nuclear envelope point coordinates with minimal angle, shape (3,)

Return type:

numpy.ndarray

Notes

Uses cosine similarity to calculate vector angles: cos(angle) = dot product of vectors divided by product of their magnitudes Selects the point with maximum cosine value, i.e., minimal angle.

Examples

>>> closest_ne = Partitioning.min_angle(ne_points, pm_point, center)
>>> print(f"Closest NE point: {closest_ne}")
find_ne_pm_pairs(center, ne_edge, pm_edge, step=8, save_txt=False, dataid=None)[source]

Pair nuclear envelope and plasma membrane boundary points using GPU acceleration.

static points_along_vector(p1, p2, n_slices)[source]

Generate equally spaced division points between two points

Creates n_slices+1 equally spaced points along the vector from p1 to p2, including start and end points. These points are used to create boundary surfaces for radial partitions.

Parameters:
  • p1 (numpy.ndarray) – Starting point coordinates, shape (3,)

  • p2 (numpy.ndarray) – Ending point coordinates, shape (3,)

  • n_slices (int) – Number of division segments

Returns:

Equally spaced points array, shape (n_slices+1, 3)

Return type:

numpy.ndarray

Examples

>>> points = Partitioning.points_along_vector([0,0,0], [10,10,10], 4)
>>> print(f"Generated {len(points)} division points")
shell_partition(pairs, cell_mask=None)[source]

Generate shell partition points from paired NE-PM points with adaptive partitioning

Creates equally spaced partition points along the connection line for each NE-PM point pair, with adaptive subdivision for better handling of irregular cell shapes.

Parameters:
  • pairs (numpy.ndarray) – Paired points array, shape (K, 6)

  • cell_mask (numpy.ndarray, optional) – Binary mask indicating the active cell regions, shape (H, W, D)

Returns:

Shell points list, each element is a coordinate array of all points in one shell

Return type:

list of numpy.ndarray

Notes

Optimized version with adaptive partitioning for irregular cell shapes

create_nepm_radial_partitions_with_edt(ne_edge, pm_edge, shape, n_slices=None, pm_mask=None, ne_mask=None)[source]

Optimized version of radial partition generation

Parameters:
  • ne_edge (numpy.ndarray) – Nuclear envelope boundary coordinates array, shape (N, 3)

  • pm_edge (numpy.ndarray) – Plasma membrane boundary coordinates array, shape (M, 3)

  • shape (tuple) – Shape of original image volume (Z, Y, X)

  • n_slices (int, optional) – Number of radial partitions, defaults to self.n_slices

  • pm_mask (numpy.ndarray, optional) – Original plasma membrane mask for improved accuracy

  • ne_mask (numpy.ndarray, optional) – Original nuclear envelope mask for improved accuracy

Returns:

3D partition mask array where each voxel value indicates partition ID (0=background)

Return type:

numpy.ndarray

Notes

Uses local EDT-guided pair matching for better handling of irregular cell shapes

shell_partition_optimized(pairs, cell_mask=None)[source]

Optimized version of shell partition generation with parallel processing support

extract_partition_coordinates(partition_mask, sampling_density=0.1)[source]

Extract coordinate points from continuous partition mask for saving in XVG format

Parameters:
  • partition_mask (numpy.ndarray) – 3D partition mask array

  • sampling_density (float, default=0.1) – Sampling density, 0.1 means sampling 10% of points

Returns:

List of coordinate points for each partition

Return type:

list of numpy.ndarray

save_partition_coords_to_xvg(partition_coords, dataid, output_dir)[source]

Save partition coordinates to XVG format file

Parameters:
  • partition_coords (list of numpy.ndarray) – List of partition coordinates

  • dataid (str) – Data identifier

  • output_dir (str) – Output directory

Returns:

Output directory path

Return type:

str

create_nepm_radial_partitions(ne_edge, pm_edge, shape, n_slices=None, pm_mask=None, ne_mask=None)[source]

Create radial partitions using pure NE-PM pair information without EDT guidance

This method generates radial partitions based solely on the shell points created from NE-PM pairs, without using Euclidean Distance Transform for edge refinement. It’s more straightforward and computationally efficient than the EDT-guided version.

Parameters:
  • ne_edge (numpy.ndarray) – Nuclear envelope boundary coordinates array, shape (N, 3)

  • pm_edge (numpy.ndarray) – Plasma membrane boundary coordinates array, shape (M, 3)

  • shape (tuple) – Shape of original image volume (Z, Y, X)

  • n_slices (int, optional) – Number of radial partitions, defaults to self.n_slices

  • pm_mask (numpy.ndarray, optional) – Original plasma membrane mask for defining cell boundaries

  • ne_mask (numpy.ndarray, optional) – Original nuclear envelope mask for defining nuclear boundaries

Returns:

3D partition mask array where each voxel value indicates partition ID (0=background)

Return type:

numpy.ndarray

Notes

This pure pair-based method is recommended when: - You want faster computation without EDT overhead - The cell shape is relatively regular - You prefer simpler, more interpretable partitioning logic

Examples

>>> partition_mask = partitioner.create_nepm_radial_partitions(
...     ne_coords, pm_coords, (100, 200, 200), n_slices=8
... )
>>> print(f"Created partitions with shape: {partition_mask.shape}")
create_shell_based_partitions(ne_edge, pm_edge, shape, n_slices=None, pm_mask=None, ne_mask=None)[source]

Deprecated: Use create_nepm_radial_partitions_with_edt instead.

This method is kept for backward compatibility.

create_nepm_radial_partitions_pure_pairs(ne_edge, pm_edge, shape, n_slices=None, pm_mask=None, ne_mask=None)[source]

Alias for create_nepm_radial_partitions().

This method name explicitly indicates it uses the pure NE-PM pair approach without EDT guidance. It’s provided for clarity and backward compatibility.

See also

create_nepm_radial_partitions

The main implementation

create_nepm_radial_partitions_with_edt

EDT-guided alternative

The Partitioning class provides the complete workflow for radial cytoplasmic partitioning, from boundary extraction to coordinate sampling.

Core Methods

extract_ne_pm_edges

Partitioning.extract_ne_pm_edges(pm_mask, ne_mask, smooth_sigma=1.0, smooth_close_iter=2)[source]

Extract nuclear envelope and plasma membrane boundary points and cellular center from 3D mask data

Uses morphological operations to extract proper boundaries instead of EDT method which may produce incorrect boundary representations.

Parameters:
  • pm_mask (numpy.ndarray) – 3D binary mask of plasma membrane, shape (Z, Y, X)

  • ne_mask (numpy.ndarray) – 3D binary mask of nuclear envelope, shape (Z, Y, X)

  • smooth_sigma (float, optional) – Sigma for Gaussian smoothing. Set to 0 to disable smoothing.

  • smooth_close_iter (int, optional) – Iterations for morphological closing to fill small gaps.

Returns:

  • center (numpy.ndarray) – Cell center coordinates, shape (3,), order (Z, Y, X)

  • ne_edge (numpy.ndarray) – Nuclear envelope boundary point coordinates array, shape (N, 3)

  • pm_edge (numpy.ndarray) – Plasma membrane boundary point coordinates array, shape (M, 3)

Extracts boundary points from nuclear envelope and plasma membrane masks using Gaussian smoothing for robust edge detection.

Parameters:

  • pm_mask (np.ndarray): Plasma membrane binary mask

  • ne_mask (np.ndarray): Nuclear envelope binary mask

  • smooth_sigma (float, optional): Gaussian smoothing sigma. Default: 1.0

Returns:

  • center (np.ndarray): Cell center coordinates (Z, Y, X)

  • ne_edge (np.ndarray): NE boundary point coordinates (N, 3)

  • pm_edge (np.ndarray): PM boundary point coordinates (M, 3)

Example:

from ipa.processing.partitioning import Partitioning
from ipa.data_loader import UniversalDataLoader

# Initialize partitioner
partitioner = Partitioning(root_dir="results/", n_slices=8)

# Load masks
ne_mask = UniversalDataLoader.load_data("nuclear_envelope.mrc")
pm_mask = UniversalDataLoader.load_data("plasma_membrane.mrc")

# Extract boundaries
center, ne_edge, pm_edge = partitioner.extract_ne_pm_edges(pm_mask, ne_mask)
print(f"Cell center: {center}")
print(f"NE points: {len(ne_edge)}, PM points: {len(pm_edge)}")

create_nepm_radial_partitions

Partitioning.create_nepm_radial_partitions(ne_edge, pm_edge, shape, n_slices=None, pm_mask=None, ne_mask=None)[source]

Create radial partitions using pure NE-PM pair information without EDT guidance

This method generates radial partitions based solely on the shell points created from NE-PM pairs, without using Euclidean Distance Transform for edge refinement. It’s more straightforward and computationally efficient than the EDT-guided version.

Parameters:
  • ne_edge (numpy.ndarray) – Nuclear envelope boundary coordinates array, shape (N, 3)

  • pm_edge (numpy.ndarray) – Plasma membrane boundary coordinates array, shape (M, 3)

  • shape (tuple) – Shape of original image volume (Z, Y, X)

  • n_slices (int, optional) – Number of radial partitions, defaults to self.n_slices

  • pm_mask (numpy.ndarray, optional) – Original plasma membrane mask for defining cell boundaries

  • ne_mask (numpy.ndarray, optional) – Original nuclear envelope mask for defining nuclear boundaries

Returns:

3D partition mask array where each voxel value indicates partition ID (0=background)

Return type:

numpy.ndarray

Notes

This pure pair-based method is recommended when: - You want faster computation without EDT overhead - The cell shape is relatively regular - You prefer simpler, more interpretable partitioning logic

Examples

>>> partition_mask = partitioner.create_nepm_radial_partitions(
...     ne_coords, pm_coords, (100, 200, 200), n_slices=8
... )
>>> print(f"Created partitions with shape: {partition_mask.shape}")

Creates radial partitions using pure NE-PM pair information without EDT guidance. This is the recommended method for most applications, offering fast computation with interpretable logic.

Algorithm Overview:

  1. Generate NE-PM point pairs based on angular similarity from cell center

  2. Create intermediate shell points along each NE-PM pair at equal intervals

  3. Assign cytoplasmic voxels to partitions based on distance to shells with relative position interpolation (60%/40% threshold)

  4. Post-process with morphological closing to fill small gaps

Parameters:

  • ne_edge (np.ndarray): NE boundary coordinates (N, 3)

  • pm_edge (np.ndarray): PM boundary coordinates (M, 3)

  • shape (tuple): Output volume shape (Z, Y, X)

  • n_slices (int, optional): Number of radial partitions. Default: 8

  • pm_mask (np.ndarray, optional): Original PM mask for defining cell boundaries

  • ne_mask (np.ndarray, optional): Original NE mask for defining nuclear boundaries

Returns:

  • partition_mask (np.ndarray): 3D partition mask where voxel values indicate partition ID (0=background, 1-N=partition IDs)

Biological Significance:

This method creates concentric shells that normalize for cell shape variations, enabling direct comparison of organelle distributions across different cells and imaging modalities. The pure pair-based approach is particularly suitable for SIM/WFM data where cell shapes are relatively regular.

Example:

# Create 8 radial partitions
partition_mask = partitioner.create_nepm_radial_partitions(
    ne_edge, pm_edge,
    shape=volume_shape,
    n_slices=8,
    pm_mask=pm_mask,
    ne_mask=ne_mask
)

# Check partition distribution
unique_ids = np.unique(partition_mask)
print(f"Created {len(unique_ids)-1} partitions")
for pid in unique_ids[unique_ids > 0]:
    count = np.sum(partition_mask == pid)
    print(f"Partition {pid}: {count} voxels")

extract_partition_coordinates

Partitioning.extract_partition_coordinates(partition_mask, sampling_density=0.1)[source]

Extract coordinate points from continuous partition mask for saving in XVG format

Parameters:
  • partition_mask (numpy.ndarray) – 3D partition mask array

  • sampling_density (float, default=0.1) – Sampling density, 0.1 means sampling 10% of points

Returns:

List of coordinate points for each partition

Return type:

list of numpy.ndarray

Extracts coordinate points from partition mask for downstream RDF analysis. Uses random sampling to reduce memory usage while maintaining spatial distribution patterns.

Parameters:

  • partition_mask (np.ndarray): 3D partition mask

  • sampling_density (float, optional): Sampling ratio (0-1). Default: 0.1 (10% of voxels)

Returns:

  • partition_coords (dict): Dictionary mapping partition ID (int) to coordinate arrays (np.ndarray of shape [N, 3])

Example:

# Extract coordinates with 5% sampling
partition_coords = partitioner.extract_partition_coordinates(
    partition_mask,
    sampling_density=0.05
)

for partition_id, coords in partition_coords.items():
    print(f"Partition {partition_id}: {len(coords)} sampled points")

save_partition_coords_to_xvg

Partitioning.save_partition_coords_to_xvg(partition_coords, dataid, output_dir)[source]

Save partition coordinates to XVG format file

Parameters:
  • partition_coords (list of numpy.ndarray) – List of partition coordinates

  • dataid (str) – Data identifier

  • output_dir (str) – Output directory

Returns:

Output directory path

Return type:

str

Saves partition coordinates in XVG format for compatibility with analysis modules. XVG is a simple text format that stores 3D coordinates with layer indices.

Parameters:

  • partition_coords (dict): Dictionary of partition coordinates from extract_partition_coordinates()

  • dataid (str): Dataset identifier for filename

  • output_dir (str): Output directory path

Output Format:

The XVG file contains lines in the format: x y z partition_id, where coordinates are in voxel units.

Example:

# Save coordinates for RDF analysis
partitioner.save_partition_coords_to_xvg(
    partition_coords,
    dataid="sample_cell_01",
    output_dir="results/partitions/"
)
# Creates: results/partitions/sample_cell_01_partition_coords.xvg

Visualization Functions

visualize_partitions

ipa.processing.partitioning.visualize_partitions(partition_masks, slice_idx=None, save_path=None)[source]

Visualize radial partition zones in 2D or 3D.

Parameters:
  • partition_masks (list of numpy.ndarray) – List of partition masks for each radial zone

  • slice_idx (int, optional) – Z-slice index for 2D visualization (shows 3D if None)

  • save_path (str, optional) – Path to save the visualization

Generates 2D slice visualization of partitioning results with customizable color mapping.

Parameters:

  • partition_list (list): List of binary masks for each partition layer

  • slice_idx (int): Index of Z-slice to display

  • save_path (str, optional): Path to save the figure. If None, displays interactively

Example:

from ipa.processing.partitioning import visualize_partitions

# Create list of binary masks for each partition
partition_list = [partition_mask == i for i in range(1, 9)]

# Visualize middle slice
visualize_partitions(
    partition_list=partition_list,
    slice_idx=partition_mask.shape[0] // 2,
    save_path="results/partitions_slice.png"
)

plot_partition_features

ipa.processing.partitioning.plot_partition_features(features_df, save_path=None)[source]

Plot partition feature analysis results.

Parameters:
  • features_df (pandas.DataFrame) – Feature table from compute_partition_features

  • save_path (str, optional) – Path to save the plot

Generates statistical charts showing volume distribution and geometric features of each partition layer.

Parameters:

  • partition_mask (np.ndarray): 3D partition mask

  • save_path (str, optional): Path to save the figure

Output:

  • Bar chart showing voxel count per partition

  • Summary statistics table

Example:

from ipa.processing.partitioning import plot_partition_features

plot_partition_features(
    partition_mask,
    save_path="results/partition_statistics.png"
)

Complete Workflow Example

This example demonstrates the complete partitioning workflow from mask loading to coordinate extraction:

from ipa.processing.partitioning import Partitioning
from ipa.data_loader import UniversalDataLoader
import numpy as np

# Step 1: Initialize partitioner
partitioner = Partitioning(
    root_dir="results/",
    n_slices=8,  # Create 8 radial partitions
    num_cores=4  # Use 4 CPU cores for parallel processing
)

# Step 2: Load cellular membrane mask data
ne_mask = UniversalDataLoader.load_data("data/nuclear_envelope.mrc")
pm_mask = UniversalDataLoader.load_data("data/plasma_membrane.mrc")
print(f"Loaded masks with shape: {pm_mask.shape}")

# Step 3: Extract boundary coordinates
center, ne_edge, pm_edge = partitioner.extract_ne_pm_edges(pm_mask, ne_mask)
print(f"Detected cell center: {center}")
print(f"NE boundary points: {len(ne_edge)}, PM boundary points: {len(pm_edge)}")

# Step 4: Create radial partitions using pure NE-PM pair method
partition_mask = partitioner.create_nepm_radial_partitions(
    ne_edge, pm_edge,
    shape=pm_mask.shape,
    n_slices=8,
    pm_mask=pm_mask,
    ne_mask=ne_mask
)

# Check partition distribution
unique_ids = np.unique(partition_mask)
print(f"Created {len(unique_ids)-1} radial partitions")
for pid in sorted(unique_ids[unique_ids > 0]):
    count = np.sum(partition_mask == pid)
    print(f"  Partition {pid}: {count} voxels")

# Step 5: Extract and save coordinate data for RDF analysis
partition_coords = partitioner.extract_partition_coordinates(
    partition_mask,
    sampling_density=0.05  # Sample 5% of voxels
)

partitioner.save_partition_coords_to_xvg(
    partition_coords,
    dataid="sample_cell_01",
    output_dir="results/partitions/"
)
print("Partition coordinates saved to: results/partitions/sample_cell_01_partition_coords.xvg")

# Step 6: Visualize results
from ipa.processing.partitioning import visualize_partitions, plot_partition_features

# 2D slice visualization
partition_list = [partition_mask == i for i in range(1, 9)]
visualize_partitions(
    partition_list=partition_list,
    slice_idx=pm_mask.shape[0] // 2,
    save_path="results/partitions_slice.png"
)

# Statistical analysis
plot_partition_features(
    partition_mask,
    save_path="results/partition_statistics.png"
)

Output Data Formats

XVG Coordinate Files

The XVG format is a simple text-based format for storing 3D coordinates with layer indices. Each line contains:

` x y z partition_id `

Features:

  • 3D coordinate points for each partition layer

  • Layer index markers (1 to N)

  • Sampling density control via sampling_density parameter

  • Compatible with downstream RDF and interaction analysis modules

Example XVG file content:

# Partition coordinates for sample_cell_01
# Format: x y z partition_id
45.2 120.5 30.1 1
46.8 121.2 30.5 1
...
50.3 130.7 35.2 2
...

Usage in Analysis Modules

The partition coordinates can be directly used with analysis functions:

from ipa.analysis import calculate_rdf_from_xvg, load_partition_coords_from_xvg

# Load partition coordinates
partition_coords = load_partition_coords_from_xvg(
    "results/partitions/sample_cell_01_partition_coords.xvg"
)

# Calculate RDF for ISG
rdf_results = calculate_rdf_from_xvg(
    organelle_mask=isg_mask,
    partition_coords=partition_coords,
    radial_positions=radial_positions_dict,
    bins=8
)

print(f"ISG RDF values: {rdf_results['rdf']}")