Buckets:
| import numpy as np | |
| from scipy.ndimage import binary_erosion, distance_transform_edt | |
| def boundary(mask): | |
| return mask & ~binary_erosion(mask) | |
| class MaskLoss: | |
| def __init__(self, masks, weights): | |
| self.masks = np.asarray(masks, dtype=bool) | |
| self.weights = np.asarray(weights, dtype=float) | |
| if self.masks.ndim != 4 or self.weights.shape != self.masks.shape[:2]: | |
| raise ValueError( | |
| "Masks must be (objects, frames, height, width) with matching weights" | |
| ) | |
| if ( | |
| not np.isfinite(self.weights).all() | |
| or (self.weights < 0).any() | |
| or not self.weights.any() | |
| ): | |
| raise ValueError("Mask weights must be finite, nonnegative and nonzero") | |
| self.boundaries = np.asarray( | |
| [[boundary(mask) for mask in track] for track in self.masks] | |
| ) | |
| self.distances = np.asarray( | |
| [ | |
| [distance_transform_edt(~edge) for edge in track] | |
| for track in self.boundaries | |
| ] | |
| ) | |
| self.diagonal = np.linalg.norm(self.masks.shape[-2:]) | |
| def __call__(self, predictions): | |
| predictions = np.asarray(predictions, dtype=bool) | |
| if predictions.shape != self.masks.shape: | |
| raise ValueError("Predicted and target mask shapes must match") | |
| values = np.zeros(self.weights.shape) | |
| for object_id, frame in np.argwhere(self.weights > 0): | |
| target, prediction = ( | |
| self.masks[object_id, frame], | |
| predictions[object_id, frame], | |
| ) | |
| intersection = np.count_nonzero(target & prediction) | |
| union = np.count_nonzero(target | prediction) | |
| overlap = intersection / union if union else 1.0 | |
| target_edge, predicted_edge = ( | |
| self.boundaries[object_id, frame], | |
| boundary(prediction), | |
| ) | |
| if target_edge.any() and predicted_edge.any(): | |
| distance = ( | |
| self.distances[object_id, frame][predicted_edge].mean() | |
| + distance_transform_edt(~predicted_edge)[target_edge].mean() | |
| ) / (2 * self.diagonal) | |
| else: | |
| distance = float(target_edge.any() != predicted_edge.any()) | |
| values[object_id, frame] = 1 - overlap + 0.25 * distance | |
| totals = self.weights.sum(axis=1) | |
| per_object = (values * self.weights).sum(axis=1) / np.maximum(totals, 1e-12) | |
| return float(per_object[totals > 0].mean()) | |
Xet Storage Details
- Size:
- 2.56 kB
- Xet hash:
- 2dcc720eb8a2270f96a86b18cda47a7487be18ef69c8db6f1eafe83c83eccaf9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.