Spaces:
Sleeping
Sleeping
File size: 4,011 Bytes
8f72b1f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
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
|