Skip to content

modosaic.services.boundary

modosaic.services.boundary

BoundaryService

Helpers for converting masks and dense maps into boundary maps.

coerce_hw_float staticmethod

coerce_hw_float(depth, convert_np=True)

Coerce a depth-like array to HxW float values.

Parameters:

Name Type Description Default
depth ndarray

Depth-like array.

required
convert_np bool

Whether to replace NaN and infinite values with zeros.

True

Returns:

Type Description
ndarray

Coerced HxW float array.

Source code in modosaic/services/boundary.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@staticmethod
def coerce_hw_float(depth: np.ndarray, convert_np: bool = True) -> np.ndarray:
    """Coerce a depth-like array to `HxW` float values.

    Args:
        depth: Depth-like array.
        convert_np: Whether to replace NaN and infinite values with zeros.

    Returns:
        Coerced `HxW` float array.
    """
    d, _ = BoundaryService.coerce_hw_float_with_mask(depth)
    if convert_np:
        d = np.nan_to_num(d, nan=0.0, posinf=0.0, neginf=0.0)
    return d

coerce_hw_float_with_mask staticmethod

coerce_hw_float_with_mask(depth)

Coerce a depth-like array to HxW float values and validity mask.

Parameters:

Name Type Description Default
depth ndarray

Depth-like array with shape HxW, HxWx1, or 1xHxW.

required

Returns:

Type Description
tuple[ndarray, ndarray]

Coerced float array and finite-value mask.

Raises:

Type Description
ValueError

If the input cannot be interpreted as a 2D map.

Source code in modosaic/services/boundary.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@staticmethod
def coerce_hw_float_with_mask(depth: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
    """Coerce a depth-like array to `HxW` float values and validity mask.

    Args:
        depth: Depth-like array with shape `HxW`, `HxWx1`, or `1xHxW`.

    Returns:
        Coerced float array and finite-value mask.

    Raises:
        ValueError: If the input cannot be interpreted as a 2D map.
    """
    d = np.asarray(depth)
    if d.ndim == 3 and d.shape[-1] == 1:
        d = d[..., 0]
    if d.ndim == 3 and d.shape[0] == 1:
        d = d[0]
    if d.ndim != 2:
        raise ValueError(f"Expected HxW depth; got {d.shape}")
    d = d.astype(np.float32, copy=False)
    valid_mask = np.isfinite(d)
    d = np.where(valid_mask, d, np.nan)
    return d, valid_mask

coerce_mask_binary staticmethod

coerce_mask_binary(mask, expected_shape=None)

Coerce a mask-like array to a boolean mask.

Parameters:

Name Type Description Default
mask ndarray

Mask-like array.

required
expected_shape tuple[int, int] | None

Optional expected (height, width) shape.

None

Returns:

Type Description
ndarray | None

Boolean mask, or None if the mask is empty or shape-incompatible.

Source code in modosaic/services/boundary.py
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
@staticmethod
def coerce_mask_binary(mask: np.ndarray, expected_shape: tuple[int, int] | None = None) -> np.ndarray | None:
    """Coerce a mask-like array to a boolean mask.

    Args:
        mask: Mask-like array.
        expected_shape: Optional expected `(height, width)` shape.

    Returns:
        Boolean mask, or `None` if the mask is empty or shape-incompatible.
    """
    m = np.asarray(mask).squeeze()
    if m.ndim != 2 or m.size == 0:
        return None
    if expected_shape is not None and m.shape != expected_shape:
        return None

    if np.issubdtype(m.dtype, np.bool_):
        return m.astype(bool, copy=False)

    m = np.nan_to_num(m.astype(np.float32, copy=False), nan=0.0, posinf=1.0, neginf=0.0)
    mn, mx = float(np.min(m)), float(np.max(m))

    if 0.0 <= mn and mx <= 1.0:
        return m > 0.5
    if 0.0 <= mn and mx <= 255.0:
        return m > 127.5
    return m > 0.0

dilate_bool staticmethod

dilate_bool(x, radius)

Dilate a boolean map by a circular kernel.

Parameters:

Name Type Description Default
x ndarray

Boolean-like input map.

required
radius int

Dilation radius in pixels.

required

Returns:

Type Description
ndarray

Dilated boolean map.

Source code in modosaic/services/boundary.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
@staticmethod
def dilate_bool(x: np.ndarray, radius: int) -> np.ndarray:
    """Dilate a boolean map by a circular kernel.

    Args:
        x: Boolean-like input map.
        radius: Dilation radius in pixels.

    Returns:
        Dilated boolean map.
    """
    if radius <= 0:
        return x.astype(bool, copy=False)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2 * radius + 1, 2 * radius + 1))
    y = cv2.dilate(x.astype(np.uint8), kernel, iterations=1)
    return y.astype(bool)

masks_to_boundary staticmethod

masks_to_boundary(masks, expected_shape=None, thickness=1)

Convert instance masks to a combined boundary map.

Parameters:

Name Type Description Default
masks list[ndarray]

Instance masks.

required
expected_shape tuple[int, int] | None

Optional expected output shape.

None
thickness int

Boundary thickness in pixels.

1

Returns:

Type Description
ndarray

Boolean boundary map.

Source code in modosaic/services/boundary.py
 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
@staticmethod
def masks_to_boundary(
        masks: list[np.ndarray],
        expected_shape: tuple[int, int] | None = None,
        thickness: int = 1,
) -> np.ndarray:
    """Convert instance masks to a combined boundary map.

    Args:
        masks: Instance masks.
        expected_shape: Optional expected output shape.
        thickness: Boundary thickness in pixels.

    Returns:
        Boolean boundary map.
    """
    valid: list[np.ndarray] = []
    for mask in masks:
        bm = BoundaryService.coerce_mask_binary(mask, expected_shape)
        if bm is None or not np.any(bm):
            continue
        valid.append(bm)

    if not valid:
        if expected_shape is None:
            return np.zeros((1, 1), dtype=bool)
        return np.zeros(expected_shape, dtype=bool)

    k = max(1, int(thickness))
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2 * k + 1, 2 * k + 1))
    boundary_maps: list[np.ndarray] = []
    for mask in valid:
        mask_u8 = mask.astype(np.uint8)
        eroded = cv2.erode(mask_u8, kernel, iterations=1)
        boundary_maps.append((mask_u8 ^ eroded).astype(bool))

    return np.any(np.stack(boundary_maps, axis=0), axis=0)