Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 44 additions & 31 deletions nuc_morph_analysis/lib/visualization/write_data_for_colorizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
from nuc_morph_analysis.lib.preprocessing.global_dataset_filtering import (
load_dataset_with_features,
)
from nuc_morph_analysis.lib.visualization.write_mips_for_colorizer import (
save_colony_backdrop_mips
)

from nuc_morph_analysis.lib.visualization.plotting_tools import (
get_plot_labels_for_metric,
)
Expand Down Expand Up @@ -68,13 +72,7 @@ class NucMorphFeatureSpec:
# volume float In FMS manifest Volume of a single nucleus in pixels in a given frame
# height float In FMS manifest Height (in the z-direction) of the a single nucleus in pixels in a given frame
# NUC_PC1 float Needs calculated and added Value for shape mode 1 for a single nucleus in a given frame
# NUC_PC2 float Needs calculated and added Value for shape mode 2 for a single nucleus in a given frame
# NUC_PC3 float Needs calculated and added Value for shape mode 3 for a single nucleus in a given frame
# NUC_PC4 float Needs calculated and added Value for shape mode 4 for a single nucleus in a given frame
# NUC_PC5 float Needs calculated and added Value for shape mode 5 for a single nucleus in a given frame
# NUC_PC6 float Needs calculated and added Value for shape mode 6 for a single nucleus in a given frame
# NUC_PC7 float Needs calculated and added Value for shape mode 7 for a single nucleus in a given frame
# NUC_PC8 float Needs calculated and added Value for shape mode 8 for a single nucleus in a given frame



OBJECT_ID_COLUMN = "label_img"
Expand Down Expand Up @@ -231,26 +229,26 @@ class NucMorphFeatureSpec:
NucMorphFeatureSpec("SA_vol_ratio"),

# mitotic and apoptotic neighbor columns
NucMorphFeatureSpec(column_name="frame_of_breakdown", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="frame_of_formation", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="number_of_frame_of_breakdown_neighbors"),
NucMorphFeatureSpec(column_name="number_of_frame_of_formation_neighbors"),
NucMorphFeatureSpec(column_name="has_mitotic_neighbor_breakdown", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="has_mitotic_neighbor_formation", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="has_mitotic_neighbor_breakdown_forward_dilated", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="has_mitotic_neighbor_formation_backward_dilated", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="has_mitotic_neighbor", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="has_mitotic_neighbor_dilated", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="frame_of_death", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="has_dying_neighbor", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="has_dying_neighbor_forward_dilated", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec(column_name="number_of_frame_of_death_neighbors"),
NucMorphFeatureSpec(column_name="sum_has_mitotic_neighbor_breakdown"), # per track feature
NucMorphFeatureSpec(column_name="sum_has_mitotic_neighbor_formation"),# per track feature
NucMorphFeatureSpec(column_name="sum_has_mitotic_neighbor"),# per track feature
NucMorphFeatureSpec(column_name="sum_has_dying_neighbor"),# per track feature
NucMorphFeatureSpec(column_name="sum_number_of_frame_of_breakdown_neighbors"),# per track feature
NucMorphFeatureSpec(column_name="number_of_frame_of_death_neighbors"),# per track feature
NucMorphFeatureSpec("frame_of_breakdown", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("frame_of_formation", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("number_of_frame_of_breakdown_neighbors"),
NucMorphFeatureSpec("number_of_frame_of_formation_neighbors"),
NucMorphFeatureSpec("has_mitotic_neighbor_breakdown", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("has_mitotic_neighbor_formation", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("has_mitotic_neighbor_breakdown_forward_dilated", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("has_mitotic_neighbor_formation_backward_dilated", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("has_mitotic_neighbor", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("has_mitotic_neighbor_dilated", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("frame_of_death", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("has_dying_neighbor", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("has_dying_neighbor_forward_dilated", type=FeatureType.CATEGORICAL, categories=["False", "True"]),
NucMorphFeatureSpec("number_of_frame_of_death_neighbors"),
NucMorphFeatureSpec("sum_has_mitotic_neighbor_breakdown"), # per track feature
NucMorphFeatureSpec("sum_has_mitotic_neighbor_formation"),# per track feature
NucMorphFeatureSpec("sum_has_mitotic_neighbor"),# per track feature
NucMorphFeatureSpec("sum_has_dying_neighbor"),# per track feature
NucMorphFeatureSpec("sum_number_of_frame_of_breakdown_neighbors"),# per track feature
NucMorphFeatureSpec("number_of_frame_of_death_neighbors"),# per track feature


# new columns
Expand Down Expand Up @@ -419,7 +417,7 @@ def make_features(
if scale_factor is not None:
data = data * scale_factor

description = GLOSSARY[feature.column_name]
description = GLOSSARY.get(feature.column_name, "")

writer.write_feature(
data,
Expand Down Expand Up @@ -447,6 +445,7 @@ def make_dataset(
do_frames=True,
scale=0.25,
parallel=False,
make_backdrops=True,
):
"""Make a new dataset from the given data, and write the complete dataset
files to the given output directory.
Expand All @@ -459,11 +458,11 @@ def make_dataset(

# load the dataset once
df_all = load_dataset_with_features("all_baseline", remove_growth_outliers=False)

for filter in filters:
output_dir_subset = Path(output_dir) / filter
output_dir_subset.mkdir(parents=True, exist_ok=True)
output_dir_subset = str(output_dir_subset)
output_dir_subset = str(output_dir_subset)

df_filter = df_all.copy()
df_filter.loc[df_filter[filter] == False, "is_outlier"] = True
Expand Down Expand Up @@ -511,12 +510,19 @@ def make_dataset(
)

# Make the features, frame data, and manifest.
nframes = len(grouped_frames)
nframes = len(grouped_frames)
writer.set_frame_paths(generate_frame_paths(nframes))

make_features(full_dataset, FEATURE_COLUMNS[filter], dataset, writer)

if do_frames:
make_all_frames(grouped_frames, scale, writer, parallel)

if make_backdrops:
save_colony_backdrop_mips(dataset, output_dir_subset + f"/{dataset}/backdrops/")
backdrop_paths = [f"./backdrops/{i}.png" for i in range(nframes)]
writer.add_backdrops("Max intensity z-projection of Lamin B1", backdrop_paths)

writer.write_manifest(metadata=metadata)


Expand Down Expand Up @@ -558,6 +564,12 @@ def make_dataset(
"Has no effect with --noframes.",
)

parser.add_argument(
"--make_backdrops",
type=bool,
default=True,
help="True will generate backdrops (Default). False will use previously generated backdrops or display none if they have not been generated before.",
)
args = parser.parse_args()


Expand All @@ -572,6 +584,7 @@ def main():
do_frames=not args.noframes,
scale=args.scale,
parallel=args.parallel,
make_backdrops=args.make_backdrops,
)


Expand Down
58 changes: 58 additions & 0 deletions nuc_morph_analysis/lib/visualization/write_mips_for_colorizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#%%
import os
import numpy as np
import skimage.exposure as skex
from skimage.transform import resize
from aicsimageio.writers import two_d_writer
from nuc_morph_analysis.lib.preprocessing.single_track_contact.export_code import export_helper
from nuc_morph_analysis.lib.preprocessing import load_data

# %%
def save_colony_backdrop_mips(colony, figdir, dtype ="uint8", downsample_factor=2, zarr_res_level=0):
"""
Save maximum intensity projection (MIP) images for a given colony.
This function reads the raw field of view images for the specified colony. The images are
processed to flip them to match the orientation of the segmentation images on TFE, intensity
rescaled, downsampled and max intensity projections in z are saved as png files to a directory.
Two downsampling methods are available.
1. Load directly a precomputed lower resolution zarr. Options are 0, 1, 2, 3, 4.
2. Resize the image using skimage and downsample the image using a custom scaling factor.
The default behavior is to resize the image using skimage. This is the same method used to generate
MIPs in Figure 1.
Parameters:
-----------
colony (str): The name of the colony to process.
figdir (str): The directory where the MIP images will be saved.
dtype (str): The data type to use for the images. Default is "uint8".
downsample_factor (int): The factor by which to downsample the images. Default is 2.
zarr_res_level (int): The resolution level to use when reading the zarr files. Default is 0.
Returns:
--------
None
"""
print(f"Processing {colony} colony backdrops")
reader = load_data.get_dataset_original_file_reader(colony)
reader.set_resolution_level(zarr_res_level)

for timepoint_frame in range(reader.dims.T):
egfp = export_helper.load_raw_fov_image(colony, timepoint_frame, reader, channel="egfp")
egfp_flip = np.flip(egfp, axis=1)
egfp_mip = egfp_flip.max(axis=0).astype(dtype)

if downsample_factor is not None:
downsample_shape = (egfp_mip.shape[0] // downsample_factor, egfp_mip.shape[1] // downsample_factor)
egfp_mip = resize(egfp_mip, downsample_shape, anti_aliasing=True, preserve_range=True).astype(dtype)

egfp_mip = skex.rescale_intensity(image=egfp_mip, in_range=(110, 140), out_range=dtype).astype(dtype)

os.makedirs(figdir, exist_ok=True)
save_image = os.path.join(figdir, f'{timepoint_frame}.png')
two_d_writer.TwoDWriter.save(egfp_mip, uri=save_image)

#%%