diff --git a/src/ark/segmentation/ez_seg/ez_seg_display.py b/src/ark/segmentation/ez_seg/ez_seg_display.py index 17f297c6e..6de603dbf 100644 --- a/src/ark/segmentation/ez_seg/ez_seg_display.py +++ b/src/ark/segmentation/ez_seg/ez_seg_display.py @@ -47,7 +47,7 @@ def display_channel_image( base_image: np.ndarray = imread(image_path, as_gray=True) # convert image to presentable RGB - base_image = base_image / 255 + base_image_scaled = base_image / 255 # Plot fig: Figure = plt.figure(dpi=300, figsize=(6, 6)) @@ -104,8 +104,8 @@ def overlay_mask_outlines( channel_image: np.ndarray = imread(channel_image_path, as_gray=True) mask_image: np.ndarray = imread(mask_image_path, as_gray=True) - # Auto-scale the base image - channel_image_scaled = img_as_ubyte(channel_image) + # convert image to presentable RGB + channel_image_scaled = channel_image / 255 # Apply Canny edge detection to extract outlines edges: np.ndarray = feature.canny( diff --git a/src/ark/segmentation/ez_seg/merge_masks.py b/src/ark/segmentation/ez_seg/merge_masks.py index 5651cb088..046d9976d 100644 --- a/src/ark/segmentation/ez_seg/merge_masks.py +++ b/src/ark/segmentation/ez_seg/merge_masks.py @@ -4,7 +4,7 @@ import numpy as np import os from skimage.io import imread -from scipy.ndimage import label +from skimage.morphology import label from alpineer import load_utils, image_utils from ark.segmentation.ez_seg.ez_seg_utils import log_creator @@ -14,6 +14,7 @@ def merge_masks_seq( object_list: List[str], object_mask_dir: Union[pathlib.Path, str], cell_mask_path: Union[pathlib.Path, str], + cell_mask_suffix: str, overlap_percent_threshold: int, save_path: Union[pathlib.Path, str], log_dir: Union[pathlib.Path, str] @@ -28,6 +29,7 @@ def merge_masks_seq( object_list (List[str]): A list of names representing previously generated object masks. Note, order matters. object_mask_dir (Union[pathlib.Path, str]): Directory where object (ez) segmented masks are located cell_mask_path (Union[str, pathlib.Path]): Path to where the original cell masks are located. + cell_mask_suffix (str): Name of the cell type you are merging. Usually "whole_cell". overlap_percent_threshold (int): Percent overlap of total pixel area needed fo object to be merged to a cell. save_path (Union[str, pathlib.Path]): The directory where merged masks and remaining cell mask will be saved. log_dir (Union[str, pathlib.Path]): The directory to save log information to. @@ -43,7 +45,7 @@ def merge_masks_seq( # for each fov, import cell and object masks (multiple mask types into single xr.DataArray) for fov in fov_list: curr_cell_mask = imread(fname=os.path.join( - cell_mask_path, '_'.join([f'{fov}', 'whole_cell.tiff'])) + cell_mask_path, '_'.join([f'{fov}', f'{cell_mask_suffix}.tiff'])) ) fov_object_names = [f'{fov}_' + obj + '.tiff' for obj in object_list] @@ -69,7 +71,7 @@ def merge_masks_seq( curr_cell_mask = remaining_cells # save the unmerged cells as a tiff. - image_utils.save_image(fname=save_path / (fov + "_final_cells_remaining.tiff"), data=curr_cell_mask.astype(np.int32)) + image_utils.save_image(fname=save_path / (fov + f"_final_{cell_mask_suffix}_remaining.tiff"), data=curr_cell_mask.astype(np.int32)) # Write a log saving mask merging info variables_to_log = { @@ -77,6 +79,7 @@ def merge_masks_seq( "object_list": object_list, "object_mask_dir": object_mask_dir, "cell_mask_path": cell_mask_path, + "cell_mask_suffix": cell_mask_suffix, "overlap_percent_threshold": overlap_percent_threshold, "save_path": save_path } @@ -111,8 +114,8 @@ def merge_masks_single( raise ValueError("Both masks must have the same shape") # Relabel cell, object masks - cell_labels, num_cell_labels = label(cell_mask) - object_labels, num_object_labels = label(object_mask) + cell_labels, num_cell_labels = label(cell_mask, return_num=True) + object_labels, num_object_labels = label(object_mask, return_num=True) # Instantiate new array for merging merged_mask = object_labels.copy() diff --git a/src/ark/segmentation/marker_quantification.py b/src/ark/segmentation/marker_quantification.py index 92d895094..758aa868b 100644 --- a/src/ark/segmentation/marker_quantification.py +++ b/src/ark/segmentation/marker_quantification.py @@ -1,6 +1,7 @@ import copy import warnings from typing import List +import re import numpy as np import pandas as pd @@ -596,16 +597,25 @@ def process_lists(fov_names: List[str], mask_names: List[str]) -> List[str]: Function to strip prefixes from list: fov_names, strip '.tiff' suffix from list: mask names, and remove underscore prefixes, returning unique mask values (i.e. categories of masks). - Args: - fov_names (List[str]): list of fov names. Matching fov names in mask names will be returned without fov prefix. - mask_names (List[str]): list of mask names. Mask names will be returned without tif suffix. + Args: + fov_names (List[str]): list of fov names. Matching fov names in mask names will be returned without fov prefix. + mask_names (List[str]): list of mask names. Mask names will be returned without tif suffix. - Returns: - List[str]: Unique mask names (i.e. categories of masks) + Returns: + List[str]: Unique mask names (i.e. categories of masks) """ stripped_mask_names = io_utils.remove_file_extensions(mask_names) - result = [itemB[len(prefix):] for itemB in stripped_mask_names for prefix in fov_names if itemB.startswith(prefix)] + + # break fov names into tokens, compare against mask names and return only those mask names that also contain the fov + result = [] + for prefix in fov_names: + prefix_tokens = list(filter(bool, re.split("[^a-zA-Z0-9]", prefix))) + for itemB in stripped_mask_names: + itemB_tokens = list(filter(bool, re.split("[^a-zA-Z0-9]", itemB))) + if set(prefix_tokens).issubset(itemB_tokens): + result.append(itemB[len(prefix):]) + # Remove underscore prefixes and return unique values cleaned_result = [item.lstrip('_') for item in result] unique_result = list(set(cleaned_result)) - return unique_result + return unique_result \ No newline at end of file diff --git a/templates/ez_segmenter.ipynb b/templates/ez_segmenter.ipynb index 5187b3b5c..f555e5dbd 100644 --- a/templates/ez_segmenter.ipynb +++ b/templates/ez_segmenter.ipynb @@ -20,7 +20,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "tags": [ "import" @@ -564,6 +564,7 @@ "* `merge_masks_list`: list of object masks to merge to the base (cell) `image.List` of object masks to merge to the base (cell) image.\n", "* `percent_overlap`: percent threshold required for a cell mask to be merged into an object mask\n", "* `cell_dir`: the final mask directory\n", + "* `cell_mask_suffix`: Suffix name of the cell mask files. Usually \"whole_cell\"\n", "* `merged_masks_dir`: the directory to store the merged masks" ] }, @@ -582,6 +583,7 @@ "\n", "# Overwrite if different from above\n", "cell_dir = os.path.join(segmentation_dir, \"deepcell_output\")\n", + "cell_mask_suffix = \"whole_cell\"\n", "merged_masks_dir = os.path.join(segmentation_dir, \"merged_masks_dir\")" ] }, @@ -629,6 +631,7 @@ " object_list=merge_masks_list,\n", " object_mask_dir=ez_masks_dir,\n", " cell_mask_path=cell_dir,\n", + " cell_mask_suffix=cell_mask_suffix,\n", " overlap_percent_threshold=percent_overlap,\n", " save_path=merged_masks_dir,\n", " log_dir=log_dir\n",