Source code for ark.segmentation.ez_seg.composites

import pathlib
from typing import List, Union
import numpy as np
import xarray as xr
from alpineer import misc_utils, image_utils, load_utils
from ark.segmentation.ez_seg.ez_seg_utils import log_creator


[docs]def composite_builder( image_data_dir: Union[str, pathlib.Path], img_sub_folder: str, fov_list: list[str], images_to_add: list[str], images_to_subtract: list[str], image_type: str, composite_method: str, composite_directory: Union[str, pathlib.Path] = None, composite_name: str = None, log_dir: Union[str, pathlib.Path] = None, ) -> None: """ Adds tiffs together, either pixel clusters or base signal tiffs and returns a composite channel or mask. Args: image_data_dir (Union[str, pathlib.Path]): The path to dir containing the set of all images which get filtered out with `images_to_add` and `images_to_subtract`. img_sub_folder (str): A name for sub-folders within each fov in the image_data location. fov_list: A list of fov's to create composite channels through. images_to_add (List[str]): A list of channels or pixel cluster names to add together. images_to_subtract (List[str]): A list of channels or pixel cluster names to subtract from the composite. image_type (str): Either "signal" or "pixel_cluster" data. composite_method (str): Binarized mask returns ("binary") or intensity, gray-scale tiffs returned ("total"). composite_directory (Union[str, pathlib.Path]): The directory to save the composite array. composite_name (str): The name of the composite array to save. log_dir: The directory to save log information to. Returns: np.ndarray: Saves the composite array, either as a binary mask, or as a scaled intensity array. If composite_directory is None, return a dictionary with keys being FOV names and values are np.ndarray of the composite image. """ composite_images = {} for fov in fov_list: # load in tiff images and verify channels are present fov_data = load_utils.load_imgs_from_tree( data_dir=image_data_dir, img_sub_folder=img_sub_folder, fovs=fov ) image_shape = fov_data.shape[1:3] misc_utils.verify_in_list( images_to_add=images_to_add, image_names=fov_data.channels.values ) misc_utils.verify_in_list( images_to_subtract=images_to_subtract, image_names=fov_data.channels.values ) misc_utils.verify_in_list( composite_method=composite_method, options=["binary", "total"] ) # Initialize composite array, and add & subtract channels composite_array = np.zeros(shape=image_shape, dtype=np.float32) if images_to_add: composite_array = add_to_composite( fov_data, composite_array, images_to_add, image_type, composite_method ) if images_to_subtract: composite_array = subtract_from_composite( fov_data, composite_array, images_to_subtract, image_type, composite_method ) if composite_directory: # Create the fov dir within the composite dir composite_fov_dir = pathlib.Path(composite_directory) / fov composite_fov_dir.mkdir(parents=True, exist_ok=True) # Save the composite image image_utils.save_image( fname=pathlib.Path(composite_directory) / fov / f"{composite_name}.tiff", data=composite_array.astype(np.uint32) ) composite_images[fov] = composite_array.astype(np.float32) # Write a log saving composite builder info if log_dir: variables_to_log = { "image_data_dir": image_data_dir, "fov_list": fov_list, "images_to_add": images_to_add, "images_to_subtract": images_to_subtract, "image_type": image_type, "composite_method": composite_method, "composite_directory": composite_directory, "composite_name": composite_name, } log_creator(variables_to_log, log_dir, f"{composite_name}_composite_log.txt") else: return composite_images print("Composites built and saved")
[docs]def add_to_composite( data: xr.DataArray, composite_array: np.ndarray, images_to_add: List[str], image_type: str, composite_method: str, ) -> np.ndarray: """ Adds tiffs together to form a composite array. Args: data (xr.DataArray): The data array containing the set of all images which get filtered out with `images_to_add`. composite_array (np.ndarray): The array to add channels to. images_to_add (List[str]): A list of channels or pixel cluster names to add together. image_type (str): Either "signal" or "pixel_cluster" data. composite_method (str): Binarized mask returns ("binary") or intensity, gray-scale tiffs returned ("total"). Returns: np.ndarray: The composite array, either as a binary mask, or as a scaled intensity array. """ filtered_channels: xr.DataArray = data.sel( {"channels": images_to_add}).squeeze().astype(np.float32) if len(images_to_add) > 1: composite_array = filtered_channels.sum(dim="channels").values else: composite_array = filtered_channels if image_type == "pixel_cluster" or composite_method == "binary": composite_array = composite_array.clip(min=None, max=1) return composite_array
[docs]def subtract_from_composite( data: xr.DataArray, composite_array: np.ndarray, images_to_subtract: List[str], image_type: str, composite_method: str, ) -> np.ndarray: """ Subtracts tiffs from a composite array. Args: data (xr.DataArray): The data array containing the set of all images which get filtered out with `images_to_subtract`. composite_array (np.ndarray): An array to subtract channels from. images_to_subtract (List[str]): A list of channels or pixel cluster names to subtract from the composite. image_type (str): Either "signal" or "pixel_cluster" data. composite_method (str): Binarized mask returns ('binary') or intensity, gray-scale tiffs returned ('total'). Returns: np.ndarray: The composite array, either as a binary mask, or as a scaled intensity array. """ filtered_channels: xr.DataArray = data.sel( {"channels": images_to_subtract}).squeeze().astype(np.float32) if len(images_to_subtract) > 1: composite_array2sub = filtered_channels.sum(dim="channels").values else: composite_array2sub = filtered_channels if image_type == "signal" and composite_method == "binary": mask_2_zero = composite_array2sub > 0 composite_array[mask_2_zero] = 0 composite_array[composite_array > 1] = 1 else: composite_array -= composite_array2sub composite_array = composite_array.clip(min=0, max=None) return composite_array