Shengxiao0709's picture
Upload 78 files
8f72b1f verified
import itertools
import numpy as np
import pandas as pd
from skimage.measure import regionprops_table
# the property keys that are supported for 2 and 3 dim
_PROPERTIES = {
2: {
# FIXME: The only image regionprop possible now (when compressing) is mean_intensity,
# since we store a mask with the mean intensity of each detection as the image.
"regionprops": (
"label",
"area",
"intensity_mean",
"eccentricity",
"solidity",
"inertia_tensor",
),
# faster
"regionprops2": (
"label",
"area",
"intensity_mean",
"inertia_tensor",
),
"patch_regionprops": (
"label",
"area",
"intensity_mean",
"inertia_tensor",
),
},
3: {
"regionprops2": (
"label",
"area",
"intensity_mean",
"inertia_tensor",
),
"patch_regionprops": (
"label",
"area",
"intensity_mean",
"inertia_tensor",
),
},
}
def extract_features_regionprops(
mask: np.ndarray,
img: np.ndarray,
labels: np.ndarray,
properties="regionprops2",
):
ndim = mask.ndim
assert ndim in (2, 3)
assert mask.shape == img.shape
prop_dict = _PROPERTIES[ndim]
if properties not in prop_dict:
raise ValueError(f"properties must be one of {prop_dict.keys()}")
properties_tuple = prop_dict[properties]
assert properties_tuple[0] == "label"
labels = np.asarray(labels)
# remove mask labels that are not present
# not needed, remove for speed
# mask[~np.isin(mask, labels)] = 0
df = pd.DataFrame(
regionprops_table(mask, intensity_image=img, properties=properties_tuple)
)
assert df.columns[0] == "label"
assert df.columns[1] == "area"
# the bnumber of inertia tensor columns depends on the dimensionality
n_cols_inertia = ndim**2
assert np.all(["inertia_tensor" in col for col in df.columns[-n_cols_inertia:]])
# Hack for backwards compatibility
if properties in ("regionprops", "patch_regionprops"):
# Nice for conceptual clarity, but does not matter for speed
# drop upper triangular part of symmetric inertia tensor
for i, j in itertools.product(range(ndim), repeat=2):
if i > j:
df.drop(f"inertia_tensor-{i}-{j}", axis=1, inplace=True)
table = df.to_numpy()
table[:, 1] *= 0.001
table[:, -n_cols_inertia:] *= 0.01
# reorder according to labels
features = np.zeros((len(labels), len(df.columns) - 1))
# faster than iterating over pandas dataframe
for row in table:
# old version with tuple indexing, slow.
# n = labels.index(int(row.label))
# features[n] = row.to_numpy()[1:]
# Only process regions present in the labels
n = np.where(labels == int(row[0]))[0]
if len(n) > 0:
# Remove label column (0)!
features[n[0]] = row[1:]
return features
def extract_features_patch(
mask: np.ndarray,
img: np.ndarray,
coords: np.ndarray,
labels: np.ndarray,
width_patch: int = 16,
):
"""16x16 Image patch around detection."""
ndim = mask.ndim
assert ndim in (2, 3) and mask.shape == img.shape
if len(coords) == 0:
return np.zeros((0, width_patch * width_patch))
pads = (width_patch // 2,) * ndim
img = np.pad(
img,
tuple((p, p) for p in pads),
mode="constant",
)
coords = coords.astype(int) + np.array(pads)
ss = tuple(
tuple(slice(_c - width_patch // 2, _c + width_patch // 2) for _c in c)
for c in coords
)
fs = tuple(img[_s] for _s in ss)
# max project along z if 3D
if ndim == 3:
fs = tuple(f.max(0) for f in fs)
features = np.stack([f.flatten() for f in fs])
return features