Source code for carlanomaly.datasets.anomaly_seg
from __future__ import annotations
from typing import Any, Callable, Optional
import numpy as np
import torch
from PIL import Image
from ..index import ScenarioIndex
from ._base import AtomicDataset, PathLike
[docs]
class AnomalySegmentationDataset(AtomicDataset):
"""Per-timestep pixel-level anomaly masks from a single camera.
Returns a ``BoolTensor (T, H, W)``. For the train split (where no
anomaly masks exist), returns all-False tensors.
With ``download=True`` the required archive parts are fetched into ``root``
(``front`` lives in ``base``; other directions add ``camera-extended``).
Remaining keyword arguments (``clip_len``,
``stride``, ``parts``, ...) are forwarded to
:class:`~carlanomaly.index.ScenarioIndex`.
"""
modality = "anomaly_seg"
# Native resolution used when no mask file exists.
_DEFAULT_H = 1080
_DEFAULT_W = 1920
def __init__(
self,
root: Optional[PathLike] = None,
split: str = "train",
direction: str = "front",
*,
transform: Optional[Callable] = None,
index: Optional[ScenarioIndex] = None,
download: bool = False,
**index_kwargs: Any,
) -> None:
super().__init__(
root, split, direction=direction, transform=transform,
index=index, download=download, **index_kwargs,
)
[docs]
def __getitem__(self, idx: int) -> torch.Tensor:
rec, _ = self._index[idx]
timesteps = self._index.timesteps_for(idx)
masks = []
for f in timesteps:
if self._is_train:
masks.append(torch.zeros(
self._DEFAULT_H, self._DEFAULT_W, dtype=torch.bool
))
else:
path = rec.path / f"anomaly-{self.direction}" / f"{f:06d}.png"
if path.exists():
arr = np.array(Image.open(path).convert("L"))
masks.append(torch.from_numpy(arr > 0))
else:
masks.append(torch.zeros(
self._DEFAULT_H, self._DEFAULT_W, dtype=torch.bool
))
item = torch.stack(masks) # (T, H, W)
return self._apply_transform(item)