csi_images.csi_images

  1import numpy as np
  2import pandas as pd
  3from skimage.measure import regionprops_table
  4
  5
  6def extract_mask_info(
  7    mask: np.ndarray,
  8    images: list[np.ndarray] = None,
  9    image_labels: list[str] = None,
 10    properties: list[str] = None,
 11) -> pd.DataFrame:
 12    """
 13    Extracts events from a mask. Originated from @vishnu
 14    :param mask: mask to extract events from
 15    :param images: list of intensity images to extract from
 16    :param image_labels: list of labels for images
 17    :param properties: list of properties to extract in addition to the defaults:
 18    label, centroid, axis_major_length. See
 19    https://scikit-image.org/docs/stable/api/skimage.measure.html#skimage.measure.regionprops
 20    for additional properties.
 21    :return: pd.DataFrame with columns: id, x, y, size, or an empty DataFrame
 22    """
 23    # Return empty if the mask is empty
 24    if np.max(mask) == 0:
 25        return pd.DataFrame()
 26    # Reshape any intensity images
 27    if images is not None:
 28        if isinstance(images, list):
 29            images = np.stack(images, axis=-1)
 30        if image_labels is not None and len(image_labels) != images.shape[-1]:
 31            raise ValueError("Number of image labels must match number of images.")
 32    # Accumulate any extra properties
 33    base_properties = ["label", "centroid", "axis_major_length"]
 34    if properties is not None:
 35        properties = base_properties + properties
 36    else:
 37        properties = base_properties
 38
 39    # Use skimage.measure.regionprops_table to compute properties
 40    info = pd.DataFrame(
 41        regionprops_table(mask, intensity_image=images, properties=properties)
 42    )
 43
 44    # Rename columns to match desired output
 45    info = info.rename(
 46        columns={
 47            "label": "id",
 48            "centroid-0": "y",
 49            "centroid-1": "x",
 50            "axis_major_length": "size",
 51        },
 52    )
 53    renamings = {}
 54    for column in info.columns:
 55        for i in range(len(image_labels)):
 56            suffix = f"-{i}"
 57            if column.endswith(suffix):
 58                renamings[column] = f"{image_labels[i]}_{column[:-len(suffix)]}"
 59    info = info.rename(columns=renamings)
 60
 61    return info
 62
 63
 64def make_rgb(
 65    images: list[np.ndarray], colors=list[tuple[float, float, float]]
 66) -> np.ndarray:
 67    """
 68    Combine multiple channels into a single RGB image.
 69    :param images: list of numpy arrays representing the channels.
 70    :param colors: list of RGB tuples for each channel.
 71    :return:
 72    """
 73    if len(images) == 0:
 74        raise ValueError("No images provided.")
 75    if len(colors) == 0:
 76        raise ValueError("No colors provided.")
 77    if len(images) != len(colors):
 78        raise ValueError("Number of images and colors must match.")
 79    if not all([isinstance(image, np.ndarray) for image in images]):
 80        raise ValueError("Images must be numpy arrays.")
 81    if not all([len(c) == 3 for c in colors]):
 82        raise ValueError("Colors must be RGB tuples.")
 83
 84    # Create an output with same shape and larger type to avoid overflow
 85    dims = images[0].shape
 86    dtype = images[0].dtype
 87    if dtype not in [np.uint8, np.uint16]:
 88        raise ValueError("Image dtype must be uint8 or uint16.")
 89    rgb = np.zeros((*dims, 3), dtype=np.uint16 if dtype == np.uint8 else np.uint32)
 90
 91    # Combine images with colors (can also be thought of as gains)
 92    for image, color in zip(images, colors):
 93        if image.shape != dims:
 94            raise ValueError("All images must have the same shape.")
 95        if image.dtype != dtype:
 96            raise ValueError("All images must have the same dtype.")
 97        rgb[..., 0] += (image * color[0]).astype(rgb.dtype)
 98        rgb[..., 1] += (image * color[1]).astype(rgb.dtype)
 99        rgb[..., 2] += (image * color[2]).astype(rgb.dtype)
100
101    # Cut off any overflow and convert back to original dtype
102    rgb = np.clip(rgb, np.iinfo(dtype).min, np.iinfo(dtype).max).astype(dtype)
103    return rgb
def extract_mask_info( mask: numpy.ndarray, images: list[numpy.ndarray] = None, image_labels: list[str] = None, properties: list[str] = None) -> pandas.core.frame.DataFrame:
 7def extract_mask_info(
 8    mask: np.ndarray,
 9    images: list[np.ndarray] = None,
10    image_labels: list[str] = None,
11    properties: list[str] = None,
12) -> pd.DataFrame:
13    """
14    Extracts events from a mask. Originated from @vishnu
15    :param mask: mask to extract events from
16    :param images: list of intensity images to extract from
17    :param image_labels: list of labels for images
18    :param properties: list of properties to extract in addition to the defaults:
19    label, centroid, axis_major_length. See
20    https://scikit-image.org/docs/stable/api/skimage.measure.html#skimage.measure.regionprops
21    for additional properties.
22    :return: pd.DataFrame with columns: id, x, y, size, or an empty DataFrame
23    """
24    # Return empty if the mask is empty
25    if np.max(mask) == 0:
26        return pd.DataFrame()
27    # Reshape any intensity images
28    if images is not None:
29        if isinstance(images, list):
30            images = np.stack(images, axis=-1)
31        if image_labels is not None and len(image_labels) != images.shape[-1]:
32            raise ValueError("Number of image labels must match number of images.")
33    # Accumulate any extra properties
34    base_properties = ["label", "centroid", "axis_major_length"]
35    if properties is not None:
36        properties = base_properties + properties
37    else:
38        properties = base_properties
39
40    # Use skimage.measure.regionprops_table to compute properties
41    info = pd.DataFrame(
42        regionprops_table(mask, intensity_image=images, properties=properties)
43    )
44
45    # Rename columns to match desired output
46    info = info.rename(
47        columns={
48            "label": "id",
49            "centroid-0": "y",
50            "centroid-1": "x",
51            "axis_major_length": "size",
52        },
53    )
54    renamings = {}
55    for column in info.columns:
56        for i in range(len(image_labels)):
57            suffix = f"-{i}"
58            if column.endswith(suffix):
59                renamings[column] = f"{image_labels[i]}_{column[:-len(suffix)]}"
60    info = info.rename(columns=renamings)
61
62    return info

Extracts events from a mask. Originated from @vishnu

Parameters
Returns

pd.DataFrame with columns: id, x, y, size, or an empty DataFrame

def make_rgb( images: list[numpy.ndarray], colors=list[tuple[float, float, float]]) -> numpy.ndarray:
 65def make_rgb(
 66    images: list[np.ndarray], colors=list[tuple[float, float, float]]
 67) -> np.ndarray:
 68    """
 69    Combine multiple channels into a single RGB image.
 70    :param images: list of numpy arrays representing the channels.
 71    :param colors: list of RGB tuples for each channel.
 72    :return:
 73    """
 74    if len(images) == 0:
 75        raise ValueError("No images provided.")
 76    if len(colors) == 0:
 77        raise ValueError("No colors provided.")
 78    if len(images) != len(colors):
 79        raise ValueError("Number of images and colors must match.")
 80    if not all([isinstance(image, np.ndarray) for image in images]):
 81        raise ValueError("Images must be numpy arrays.")
 82    if not all([len(c) == 3 for c in colors]):
 83        raise ValueError("Colors must be RGB tuples.")
 84
 85    # Create an output with same shape and larger type to avoid overflow
 86    dims = images[0].shape
 87    dtype = images[0].dtype
 88    if dtype not in [np.uint8, np.uint16]:
 89        raise ValueError("Image dtype must be uint8 or uint16.")
 90    rgb = np.zeros((*dims, 3), dtype=np.uint16 if dtype == np.uint8 else np.uint32)
 91
 92    # Combine images with colors (can also be thought of as gains)
 93    for image, color in zip(images, colors):
 94        if image.shape != dims:
 95            raise ValueError("All images must have the same shape.")
 96        if image.dtype != dtype:
 97            raise ValueError("All images must have the same dtype.")
 98        rgb[..., 0] += (image * color[0]).astype(rgb.dtype)
 99        rgb[..., 1] += (image * color[1]).astype(rgb.dtype)
100        rgb[..., 2] += (image * color[2]).astype(rgb.dtype)
101
102    # Cut off any overflow and convert back to original dtype
103    rgb = np.clip(rgb, np.iinfo(dtype).min, np.iinfo(dtype).max).astype(dtype)
104    return rgb

Combine multiple channels into a single RGB image.

Parameters
  • images: list of numpy arrays representing the channels.
  • colors: list of RGB tuples for each channel.
Returns