|
from skimage.morphology import skeletonize
|
|
import numpy as np
|
|
from skimage.measure import label
|
|
from tqdm.contrib.concurrent import 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):
|
|
|
|
|
|
|
|
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):
|
|
|
|
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):
|
|
|
|
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)
|
|
|
|
|
|
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))
|
|
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 |