Contributing#

Contributing to ultrasound-metrics#

We welcome contributions from the community! This guide explains how to contribute to the project.

Development Setup#

  1. Fork and clone the repository:

    git clone https://github.com/yourusername/ultrasound-metrics.git
    cd ultrasound-metrics
    
  2. Install development dependencies:

    make install
    

Contributing Workflow#

1. Create a Feature Branch#

git checkout -b feature/your-feature-name

2. Make Your Changes#

  • Follow the coding standards (see below)

  • Add tests for new functionality

  • Update documentation as needed

3. Test Your Changes#

# Run linting
make check

# Run tests
make test

# Build documentation
make docs

4. Submit a Pull Request#

  • Push your branch to your fork

  • Create a pull request with a clear description

  • Link any relevant issues

Coding Standards#

Type Annotations#

All public functions should have complete type annotations:

from jaxtyping import Float, Complex, jaxtyped
from beartype import beartype as typechecker

@jaxtyped(typechecker=typechecker)
def new_metric(
    data: Complex[ArrayAPIObj, "elements *dims"],
    param: float = 1.0,
) -> Float[ArrayAPIObj, "*dims"]:
    """Clear docstring with parameters and returns sections."""

If you’re not sure how to use jaxtyping, feel free to open a draft PR and tag a maintainer for help.

Array API Compliance#

  • Use array_namespace() to detect backend

  • Implement using Array API standard functions when possible

  • Avoid backend-specific operations

  • See array_api_design.md for more context

from array_api_compat import array_namespace

def new_metric(data):
    xp = array_namespace(data)
    # Use xp.* operations for backend compatibility
    return xp.mean(xp.abs(data)**2)

Documentation#

Docstring Format#

Use NumPy-style docstrings:

def coherence_factor(channel_images, power_based=True):
    """Compute the coherence factor of beamformed ultrasound data.

    The coherence factor measures the degree of coherence between signals
    received from different elements.

    Parameters
    ----------
    channel_images : array_like, shape (receive_elements, *img_dims)
        Complex-valued beamformed data before summation across receive elements.
    power_based : bool, optional
        If True, uses power-based coherence. Default is True.

    Returns
    -------
    coherence : array_like, shape (*img_dims)
        Coherence factor values ranging from 0 to 1.

    References
    ----------
    .. [1] Hollman, K. W., et al. (1999). Coherence factor of speckle from
           a multi-row probe. IEEE Ultrasonics Symposium.
    """

Mathematical Formulations#

Try to include precise mathematical descriptions:

def gcnr(values_inside, values_outside):
    """Compute the Generalized Contrast-to-Noise Ratio.

    The GCNR is defined as:

    .. math::
        GCNR = 1 - \\sum_x \\min\\{f_1(x), f_2(x)\\}

    where f_1 and f_2 are the normalized histograms of the inside
    and outside regions, respectively.
    """

These will get rendered in the API documentation.

References#

Include literature references for published metrics.

e.g.

"""
    Reference:
    ----------
    A. Rodriguez-Molares, O. M. Hoel Rindal, J. D'hooge, S. -E. Måsøy, A. Austeng
    and H. Torp, "The Generalized Contrast-to-Noise Ratio," 2018 IEEE International
    Ultrasonics Symposium (IUS), Kobe, Japan, 2018, pp. 1-4,
    doi: 10.1109/ULTSYM.2018.8580101.
""""

Testing#

Test Structure#

  • Place tests in tests/ directory matching source structure

  • Use pytest for all testing

  • Test edge cases (empty arrays, single values, 0-values, etc.)

  • Test across multiple backends when applicable

  • Compare against reference implementations when avaialble

Backend Testing#

For now, we use the scipy.conftest backend iterator to test all installed array backends. It provides the skip_xp_backends option to skip unsupported array libraries.

import pytest
from scipy.conftest import array_api_compatible, skip_xp_backends  # noqa: F401

from ultrasound_metrics.metrics.gcnr import gcnr

@pytest.mark.skip_xp_backends("array_api_strict", reason="Array-API v2024.12 does not define histogram.")
@pytest.mark.usefixtures("skip_xp_backends")
@array_api_compatible
def test_gcnr_synthetic_data(xp):
    """Test gCNR calculation with synthetic data simulating a hypoechoic lesion."""
    n_samples = 1000
    # ...

Test Categories#

  • Unit tests: Individual function behavior

  • Backend tests: Consistency across array libraries

  • Performance tests: optionally show GPU acceleration