Skip to content

steinbock.measurement

cellprofiler special

cellprofiler

create_and_save_measurement_pipeline(measurement_pipeline_file, num_channels)

Source code in steinbock/measurement/cellprofiler/cellprofiler.py
def create_and_save_measurement_pipeline(
    measurement_pipeline_file: Union[str, PathLike], num_channels: int
):
    with _measurement_pipeline_file_template.open(mode="r") as f:
        s = f.read()
    s = s.replace("{{NUM_CHANNELS}}", str(num_channels))
    with Path(measurement_pipeline_file).open(mode="w") as f:
        f.write(s)

run_object_measurement(cellprofiler_binary, measurement_pipeline_file, input_dir, output_dir, cellprofiler_plugin_dir=None)

Source code in steinbock/measurement/cellprofiler/cellprofiler.py
def run_object_measurement(
    cellprofiler_binary: str,
    measurement_pipeline_file: Union[str, PathLike],
    input_dir: Union[str, PathLike],
    output_dir: Union[str, PathLike],
    cellprofiler_plugin_dir: Union[str, PathLike, None] = None,
):
    args = [
        cellprofiler_binary,
        "-c",
        "-r",
        "-p",
        str(measurement_pipeline_file),
        "-i",
        str(input_dir),
        "-o",
        str(output_dir),
    ]
    if cellprofiler_plugin_dir is not None:
        args.append("--plugins-directory")
        args.append(str(cellprofiler_plugin_dir))
    return run_captured(args)

data

Measurement

MAX

MEAN

MEDIAN

MIN

STD

SUM

VAR

measure_intensites(img, mask, channel_names, measurement)

Source code in steinbock/measurement/data.py
def measure_intensites(
    img: np.ndarray,
    mask: np.ndarray,
    channel_names: Sequence[str],
    measurement: Measurement,
) -> pd.DataFrame:
    object_ids = np.unique(mask[mask != 0])
    data = {
        channel_name: measurement.value(img[i], labels=mask, index=object_ids)
        for i, channel_name in enumerate(channel_names)
    }
    return pd.DataFrame(
        data=data,
        index=pd.Index(object_ids, dtype=io.mask_dtype, name="Object"),
    )

measure_intensities_from_disk(img_files, mask_files, channel_names, measurement)

Source code in steinbock/measurement/data.py
def measure_intensities_from_disk(
    img_files: Sequence[Union[str, PathLike]],
    mask_files: Sequence[Union[str, PathLike]],
    channel_names: Sequence[str],
    measurement: Measurement,
) -> Generator[Tuple[Path, Path, pd.DataFrame], None, None]:
    for img_file, mask_file in zip(img_files, mask_files):
        intensities = measure_intensites(
            io.read_image(img_file),
            io.read_mask(mask_file),
            channel_names,
            measurement,
        )
        yield Path(img_file), Path(mask_file), intensities
        del intensities

measure_regionprops(img, mask, skimage_regionprops)

Source code in steinbock/measurement/data.py
def measure_regionprops(
    img: np.ndarray, mask: np.ndarray, skimage_regionprops: Sequence[str]
) -> pd.DataFrame:
    data = regionprops_table(
        mask,
        intensity_image=np.moveaxis(img, 0, -1),
        properties=skimage_regionprops,
    )
    object_ids = data.pop("label")
    return pd.DataFrame(
        data=data,
        index=pd.Index(object_ids, dtype=io.mask_dtype, name="Object"),
    )

measure_regionprops_from_disk(img_files, mask_files, skimage_regionprops)

Source code in steinbock/measurement/data.py
def measure_regionprops_from_disk(
    img_files: Sequence[Union[str, PathLike]],
    mask_files: Sequence[Union[str, PathLike]],
    skimage_regionprops: Sequence[str],
) -> Generator[Tuple[Path, Path, pd.DataFrame], None, None]:
    skimage_regionprops = list(skimage_regionprops)
    if "label" not in skimage_regionprops:
        skimage_regionprops.insert(0, "label")
    for img_file, mask_file in zip(img_files, mask_files):
        regionprops = measure_regionprops(
            io.read_image(img_file),
            io.read_mask(mask_file),
            skimage_regionprops,
        )
        yield Path(img_file), Path(mask_file), regionprops
        del regionprops

graphs

construct_centroid_dist_graph(mask, metric, dmax=None, kmax=None)

Source code in steinbock/measurement/graphs.py
def construct_centroid_dist_graph(
    mask: np.ndarray,
    metric: str,
    dmax: Optional[float] = None,
    kmax: Optional[int] = None,
) -> pd.DataFrame:
    props = regionprops(mask)
    labels = np.array([p.label for p in props])
    centroids = np.array([p.centroid for p in props])
    condensed_dists = pdist(centroids, metric=metric)
    if kmax is not None:
        k = min(kmax, len(props) - 1)
        dist_mat = squareform(condensed_dists, checks=False).astype(float)
        np.fill_diagonal(dist_mat, np.inf)
        knn_indices = np.argpartition(dist_mat, k - 1)[:, :k]
        if dmax is not None:
            knn_dists = np.take_along_axis(dist_mat, knn_indices, -1)
            indices1, indices2 = np.nonzero(knn_dists <= dmax)
            indices2 = knn_indices[(indices1, indices2)]
        else:
            indices1 = np.repeat(np.arange(len(props)), k)
            indices2 = np.ravel(knn_indices)
        distances = dist_mat[(indices1, indices2)]
    elif dmax is not None:
        (condensed_indices,) = np.nonzero(condensed_dists <= dmax)
        indices1, indices2 = _to_triu_indices(condensed_indices, len(props))
        distances = condensed_dists[condensed_indices]
        indices1, indices2, distances = (
            np.concatenate((indices1, indices2)),
            np.concatenate((indices2, indices1)),
            np.concatenate((distances, distances)),
        )
    else:
        raise ValueError("Specify either dmax or kmax (or both)")
    return pd.DataFrame(
        data={
            "Object": np.asarray(labels[indices1], dtype=io.mask_dtype),
            "Neighbor": np.asarray(labels[indices2], dtype=io.mask_dtype),
            "Distance": np.asarray(distances, dtype=float),
        }
    )

construct_euclidean_border_dist_graph(mask, dmax=None, kmax=None)

Source code in steinbock/measurement/graphs.py
def construct_euclidean_border_dist_graph(
    mask: np.ndarray,
    dmax: Optional[float] = None,
    kmax: Optional[int] = None,
) -> pd.DataFrame:
    labels1 = []
    labels2 = []
    distances = []
    unique_labels = np.unique(mask)
    unique_labels = unique_labels[unique_labels != 0]
    for label in unique_labels:
        if dmax is not None:
            ys, xs = np.nonzero(mask == label)
            dmax_int = int(np.ceil(dmax))
            ymin, ymax = np.amin(ys) - dmax_int, np.amax(ys) + dmax_int
            xmin, xmax = np.amin(xs) - dmax_int, np.amax(xs) + dmax_int
            mask_or_patch = mask[
                max(0, ymin) : min(mask.shape[0], ymax),
                max(0, xmin) : min(mask.shape[1], xmax),
            ]
            neighbor_labels = np.unique(mask_or_patch)
            neighbor_labels = neighbor_labels[neighbor_labels != 0]
        else:
            mask_or_patch = mask
            neighbor_labels = unique_labels
        dists = distance_transform_edt(mask_or_patch != label)
        neighbor_labels = neighbor_labels[neighbor_labels != label]
        neighbor_dists = np.array(
            [
                np.amin(dists[mask_or_patch == neighbor_label])
                for neighbor_label in neighbor_labels
            ]
        )
        if dmax is not None:
            neighbor_labels = neighbor_labels[neighbor_dists <= dmax]
            neighbor_dists = neighbor_dists[neighbor_dists <= dmax]
        if kmax is not None and len(neighbor_labels) > kmax:
            knn_indices = np.argpartition(neighbor_dists, kmax - 1)[:kmax]
            neighbor_labels = neighbor_labels[knn_indices]
            neighbor_dists = neighbor_dists[knn_indices]
        labels1 += [label] * len(neighbor_labels)
        labels2 += neighbor_labels.tolist()
        distances += neighbor_dists.tolist()
    return pd.DataFrame(
        data={
            "Object": np.asarray(labels1, dtype=io.mask_dtype),
            "Neighbor": np.asarray(labels2, dtype=io.mask_dtype),
            "Distance": np.asarray(distances, dtype=float),
        }
    )

construct_graph(mask, graph_type, metric=None, dmax=None, kmax=None)

Source code in steinbock/measurement/graphs.py
def construct_graph(
    mask: np.ndarray,
    graph_type: str,
    metric: Optional[str] = None,
    dmax: Optional[float] = None,
    kmax: Optional[int] = None,
) -> pd.DataFrame:
    if graph_type == "centroid":
        if metric is None:
            raise ValueError("Metric is required")
        return construct_centroid_dist_graph(
            mask, metric, dmax=dmax, kmax=kmax
        )
    if graph_type == "border":
        if metric not in (None, "euclidean"):
            raise ValueError("Metric has to be Euclidean for border distances")
        return construct_euclidean_border_dist_graph(
            mask, dmax=dmax, kmax=kmax
        )
    if graph_type == "expand":
        if metric not in (None, "euclidean"):
            raise ValueError("Metric has to be Euclidean for pixel expansion")
        if dmax is None:
            raise ValueError("Maximum distance required for pixel expansion")
        if kmax is not None:
            raise ValueError("Pixel expansion does not support kNN graphs")
        mask = expand_mask_euclidean(mask, dmax)
        return construct_euclidean_border_dist_graph(mask, dmax=1.0)
    raise ValueError(f"Unknown graph type: {graph_type}")

construct_graphs_from_disk(mask_files, graph_type, metric=None, dmax=None, kmax=None)

Source code in steinbock/measurement/graphs.py
def construct_graphs_from_disk(
    mask_files: Sequence[Union[str, PathLike]],
    graph_type: str,
    metric: Optional[str] = None,
    dmax: Optional[float] = None,
    kmax: Optional[int] = None,
) -> Generator[Tuple[Path, pd.DataFrame], None, None]:
    for mask_file in mask_files:
        mask = io.read_mask(mask_file)
        graph = construct_graph(
            mask,
            graph_type,
            metric=metric,
            dmax=dmax,
            kmax=kmax,
        )
        yield Path(mask_file), graph
        del graph

expand_mask_euclidean(mask, dmax)

Source code in steinbock/measurement/graphs.py
def expand_mask_euclidean(mask: np.ndarray, dmax: float) -> np.ndarray:
    dists, (i, j) = distance_transform_edt(mask == 0, return_indices=True)
    return np.where(dists <= dmax, mask[i, j], mask)
Back to top