Skip to content

Scanline

Overview

Count root intersections with horizontal scan lines for distribution analysis.

Key functions: - count_scanline_intersections - Count intersections at given y-coordinates - get_scanline_first_ind - First intersection index - get_scanline_last_ind - Last intersection index

Quick Example

import sleap_roots as sr
import numpy as np

series = sr.Series.load("plant", primary_path="primary.slp")
pts = series.get_primary_points()

# Count intersections at y=100
intersections = sr.count_scanline_intersections(pts, y=100)
print(f"Intersections at y=100: {intersections}")

API Reference

count_scanline_intersections

count_scanline_intersections

count_scanline_intersections(
    pts_list: List[ndarray],
    height: int = 1080,
    n_line: int = 50,
) -> ndarray

Count intersections of roots with a series of horizontal scanlines.

This function calculates the number of intersections between the provided primary and lateral root points and a set of horizontal scanlines. The scanlines are equally spaced across the specified height.

Parameters:

Name Type Description Default
pts_list List[ndarray]

A list of arrays, each having shape (nodes, 2).

required
height int

The height of the image or cylinder. Defaults to 1080.

1080
n_line int

Number of scanlines to use. Defaults to 50.

50

Returns:

Type Description
ndarray

An array with shape (n_line,) representing the number of intersections of roots with each scanline.

Source code in sleap_roots/scanline.py
def count_scanline_intersections(
    pts_list: List[np.ndarray],
    height: int = 1080,
    n_line: int = 50,
) -> np.ndarray:
    """Count intersections of roots with a series of horizontal scanlines.

    This function calculates the number of intersections between the provided
    primary and lateral root points and a set of horizontal scanlines. The scanlines
    are equally spaced across the specified height.

    Args:
        pts_list: A list of arrays, each having shape `(nodes, 2)`.
        height: The height of the image or cylinder. Defaults to 1080.
        n_line: Number of scanlines to use. Defaults to 50.

    Returns:
        An array with shape `(n_line,)` representing the number of intersections
            of roots with each scanline.
    """
    # Input validation for pts_list
    if any(pts.ndim != 2 or pts.shape[-1] != 2 for pts in pts_list):
        raise ValueError(
            "Each pts array in pts_list should have a shape of `(nodes, 2)`."
        )

    # Calculate the interval between two scanlines
    interval = height / (n_line - 1)

    intersections = []

    # Iterate over scanlines
    for i in range(n_line):
        y_coord = interval * i
        line_intersections = 0

        for root_points in pts_list:
            # Remove NaN values
            valid_points = root_points[(~np.isnan(root_points)).any(axis=1)]

            if len(valid_points) > 1:
                for j in range(len(valid_points) - 1):
                    y1 = valid_points[j][1]
                    y2 = valid_points[j + 1][1]

                    if (y1 >= y_coord >= y2) or (y2 >= y_coord >= y1):
                        line_intersections += 1

        intersections.append(line_intersections)

    return np.array(intersections)

get_scanline_first_ind

get_scanline_first_ind

get_scanline_first_ind(
    scanline_intersection_counts: ndarray,
)

Get the index of count_scanline_interaction for the first interaction.

Parameters:

Name Type Description Default
scanline_intersection_counts ndarray

An array with shape of (#Nline,) of intersection numbers of each scan line.

required
Return

Scalar of count_scanline_interaction index for the first interaction.

Source code in sleap_roots/scanline.py
def get_scanline_first_ind(scanline_intersection_counts: np.ndarray):
    """Get the index of count_scanline_interaction for the first interaction.

    Args:
        scanline_intersection_counts: An array with shape of `(#Nline,)` of intersection
            numbers of each scan line.

    Return:
        Scalar of count_scanline_interaction index for the first interaction.
    """
    # get the first scanline index using scanline_intersection_counts
    if np.where((scanline_intersection_counts > 0))[0].shape[0] > 0:
        scanline_first_ind = np.where((scanline_intersection_counts > 0))[0][0]
        return scanline_first_ind
    else:
        return np.nan

get_scanline_last_ind

get_scanline_last_ind

get_scanline_last_ind(
    scanline_intersection_counts: ndarray,
)

Get the index of count_scanline_interaction for the last interaction.

Parameters:

Name Type Description Default
scanline_intersection_counts ndarray

An array with shape of (#Nline,) of intersection numbers of each scan line.

required
Return

Scalar of count_scanline_interaction index for the last interaction.

Source code in sleap_roots/scanline.py
def get_scanline_last_ind(scanline_intersection_counts: np.ndarray):
    """Get the index of count_scanline_interaction for the last interaction.

    Args:
        scanline_intersection_counts: An array with shape of `(#Nline,)` of intersection
            numbers of each scan line.

    Return:
        Scalar of count_scanline_interaction index for the last interaction.
    """
    # get the last scanline index using scanline_intersection_counts
    if np.where((scanline_intersection_counts > 0))[0].shape[0] > 0:
        scanline_last_ind = np.where((scanline_intersection_counts > 0))[0][-1]
        return scanline_last_ind
    else:
        return np.nan