ClementP's picture
Upload 55 files
69591a9 verified
from skimage.morphology import skeletonize
import numpy as np
from skimage.measure import label
from tqdm.contrib.concurrent import thread_map # or thread_map
def extract_fiber_properties(mask):
binary_mask = mask > 0
skeleton = skeletonize(binary_mask)
r = mask == 1
g = mask == 2
labeled_skeleton = label(skeleton, connectivity=2)
properties = {"R": [], "G": [], "ratio": []}
for i in range(1, labeled_skeleton.max() + 1):
fiber_mask = labeled_skeleton == i
sum_r = np.sum(r & fiber_mask)
sum_g = np.sum(g & fiber_mask)
if sum_r == 0 or sum_g == 0:
continue
properties["R"].append(np.sum(r & fiber_mask))
properties["G"].append(np.sum(g & fiber_mask))
properties["R"] = np.array(properties["R"])
properties["G"] = np.array(properties["G"])
properties["ratio"] = properties["R"] / (properties["G"])
properties["label"] = labeled_skeleton
return properties
def filter_non_commons_fibers(properties):
# Properties is a a list of dicts. For each dict, we have a labelmap and a list of reds, greens and ratios
# We want to filter out the fibers that are not common in all images
binary_labels = [p['label'] > 0 for p in properties]
common_labels = np.logical_and.reduce(binary_labels)
filtered_properties = {k:[] for k in properties.keys()}
for i, p in enumerate(properties):
# We want to keep the labels that are common in all images
good_labels = common_labels * p['label']
indices = np.unique(good_labels[good_labels > 0])
filtered_properties.append({
"R": p["R"][common_labels],
"G": p["G"][common_labels],
"ratio": p["ratio"][common_labels],
"label": p["label"][common_labels]
})
def skeletonize_mask(mask):
# Skeletonize the mask and return the skeleton
binary_mask = mask > 0
skeleton = skeletonize(binary_mask) * mask
return skeleton
def skeletonize_data_dict(data_dict):
skeletons = dict()
for annotator, images in data_dict.items():
skeletons[annotator] = dict()
for image_type, masks in images.items():
skeletons[annotator][image_type] = thread_map(skeletonize_mask, masks, max_workers=8)
return skeletons
def extract_properties_from_datadict(data_dict, with_common_analysis=True):
"""
Extract the properties of the fibers from the data dictionary.
The data dictionary is a dict of annotators. Each value is a dict of images. Each image is a list of masks.
"""
properties = dict(annotator=[], image_type=[], red=[], green=[], ratio=[], fiber_type=[])
all_annotators = list(data_dict.keys())
found_by = {a: [] for a in all_annotators}
properties.update(found_by)
for annotator, images in data_dict.items():
for image_type, masks in images.items():
for i, mask in enumerate(masks):
if with_common_analysis:
others_masks = []
other_annotators = []
for other in all_annotators:
if other == annotator:
continue
other_annotators.append(other)
others_masks.append(data_dict[other][image_type][i] > 0)
labels, num = label(mask>0, connectivity=2, return_num=True)
for l in range(1, num + 1):
fiber = labels == l
if np.sum(fiber) < 10:
continue
properties["annotator"].append(annotator)
properties["image_type"].append(image_type)
# Check for common fibers
properties[annotator].append(True)
if with_common_analysis:
for i, (other_mask, other_annotator) in enumerate(zip(others_masks, other_annotators)):
properties[other_annotator].append(np.any(fiber & other_mask))
red_length = np.sum(fiber & (mask == 1))
green_length = np.sum(fiber & (mask == 2))
if red_length == 0 or green_length == 0:
continue
properties["ratio"].append(green_length / (red_length + 1e-7)) # Avoid division by zero
properties["red"].append(red_length)
properties["green"].append(green_length)
segments, count = label(mask[fiber], connectivity=1, return_num=True)
if count == 1:
properties["fiber_type"].append("single")
elif count == 2:
properties["fiber_type"].append("double")
elif count > 2:
properties["fiber_type"].append("multiple")
else:
properties["fiber_type"].append("unknown")
return properties