Clean spherical masks with morphology¶
This tutorial closes a small hole in a binary region that crosses the ERP longitude seam. The structuring radius is four degrees on the sphere, not four image pixels.
import numpy as np # noqa: F811 - included example is standalone
from panorai import EquirectangularImage, OnSphere, Pipeline
from panorai.data import RasterFieldSpec
from panorai.ops.spherical import Closing, GeodesicDisk
mask = np.zeros((48, 96), dtype=bool)
mask[18:30, 94:] = True
mask[18:30, :2] = True # one object crossing the longitude seam
mask[23, 0] = False # a small hole to close intrinsically
panorama_mask = EquirectangularImage.from_field(
"mask",
mask,
spec=RasterFieldSpec.categorical(),
)
operation = Closing(GeodesicDisk(radius_deg=4.0))
cleaned = panorama_mask.filter(operation, field="mask")
equivalent = Pipeline(OnSphere(operation, input="mask", output="mask"))(
panorama_mask
)
assert cleaned.field("mask").dtype == np.bool_
assert cleaned.field("mask")[23, 0]
np.testing.assert_array_equal(cleaned.field("mask"), equivalent.values)
assert equivalent.grid.transforms[-1].operation == "closing"
Choose the operation¶
Dilate expands bright/true regions and Erode shrinks them. Opening
removes isolated bright regions; Closing fills small dark holes and joins
nearby regions. All four use the same GeodesicDisk contract and preserve
the canonical ERP grid.
The configured object is reusable and safely serializable inside a Pipeline.
The equivalent direct functions are dilate, erode, opening and
closing.
Compose a Torch model¶
With the optional Torch extra, the same roles are available as
panorai.ops.spherical.nn.Dilate, Erode, Opening and Closing.
They accept floating NCHW tensors, preserve the spatial grid, propagate
autograd to selected extrema, and compose in torch.nn.Sequential. The
Stable device contract is CPU float32/float64; this does not imply a
CUDA or MPS guarantee.
Read the concept before choosing a radius or sampling step, especially when comparing multiple ERP resolutions.