Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/anomalib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,3 @@ class TaskType(str, Enum):
CLASSIFICATION = "classification"
DETECTION = "detection"
SEGMENTATION = "segmentation"
EXPLANATION = "explanation"
2 changes: 1 addition & 1 deletion src/anomalib/callbacks/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def setup(
pixel_metric_names: list[str] | dict[str, dict[str, Any]]
if self.pixel_metric_names is None:
pixel_metric_names = []
elif self.task in {TaskType.CLASSIFICATION, TaskType.EXPLANATION}:
elif self.task in TaskType.CLASSIFICATION:
pixel_metric_names = []
logger.warning(
"Cannot perform pixel-level evaluation when task type is {self.task.value}. "
Expand Down
2 changes: 1 addition & 1 deletion src/anomalib/data/base/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def __getitem__(self, index: int) -> dict[str, str | torch.Tensor]:
image = read_image(image_path, as_tensor=True)
item = {"image_path": image_path, "label": label_index}

if self.task in {TaskType.CLASSIFICATION, TaskType.EXPLANATION}:
if self.task in TaskType.CLASSIFICATION:
item["image"] = self.transform(image) if self.transform else image
elif self.task in {TaskType.DETECTION, TaskType.SEGMENTATION}:
# Only Anomalous (1) images have masks in anomaly datasets
Expand Down
2 changes: 1 addition & 1 deletion src/anomalib/data/base/depth.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def __getitem__(self, index: int) -> dict[str, str | torch.Tensor]:
depth_image = to_tensor(read_depth_image(depth_path))
item = {"image_path": image_path, "depth_path": depth_path, "label": label_index}

if self.task in {TaskType.CLASSIFICATION, TaskType.EXPLANATION}:
if self.task == TaskType.CLASSIFICATION:
item["image"], item["depth_image"] = (
self.transform(image, depth_image) if self.transform else (image, depth_image)
)
Expand Down
2 changes: 1 addition & 1 deletion src/anomalib/deploy/inferencers/openvino_inferencer.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def post_process(self, predictions: np.ndarray, metadata: dict | DictConfig | No
pred_idx = pred_score >= metadata["image_threshold"]
pred_label = LabelName.ABNORMAL if pred_idx else LabelName.NORMAL

if task in {TaskType.CLASSIFICATION, TaskType.EXPLANATION}:
if task == TaskType.CLASSIFICATION:
_, pred_score = self._normalize(pred_scores=pred_score, metadata=metadata)
elif task in {TaskType.SEGMENTATION, TaskType.DETECTION}:
if "pixel_threshold" in metadata:
Expand Down
8 changes: 5 additions & 3 deletions src/anomalib/engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ def _setup_trainer(self, model: AnomalyModule) -> None:
self._cache.update(model)

# Setup anomalib callbacks to be used with the trainer
self._setup_anomalib_callbacks()
self._setup_anomalib_callbacks(model)

# Temporarily set devices to 1 to avoid issues with multiple processes
self._cache.args["devices"] = 1
Expand Down Expand Up @@ -405,7 +405,7 @@ def _setup_transform(
if not getattr(dataloader.dataset, "transform", None):
dataloader.dataset.transform = transform

def _setup_anomalib_callbacks(self) -> None:
def _setup_anomalib_callbacks(self, model: AnomalyModule) -> None:
"""Set up callbacks for the trainer."""
_callbacks: list[Callback] = []

Expand Down Expand Up @@ -433,7 +433,9 @@ def _setup_anomalib_callbacks(self) -> None:
_callbacks.append(_MetricsCallback(self.task, self.image_metric_names, self.pixel_metric_names))

visualizer: BaseVisualizer
if self.task == TaskType.EXPLANATION:

# TODO(ashwinvaidya17): temporary # noqa: TD003 ignoring as visualizer is getting a complete overhaul
if model.__class__.__name__ == "VlmAd":
visualizer = ExplanationVisualizer()
else:
visualizer = ImageVisualizer(task=self.task, normalize=self.normalization == NormalizationMethod.NONE)
Expand Down
28 changes: 28 additions & 0 deletions src/anomalib/models/image/vlm_ad/lightning_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,31 @@ def configure_transforms(image_size: tuple[int, int] | None = None) -> None:
"""This modes does not require any transforms."""
if image_size is not None:
logger.warning("Ignoring image_size argument as each backend has its own transforms.")

@staticmethod
def _export_not_supported_message() -> None:
logging.warning("Exporting the model is not supported for VLM-AD model. Skipping...")

def to_torch( # type: ignore[override]
self,
*_,
**__,
) -> None:
"""Skip export to torch."""
return self._export_not_supported_message()

def to_onnx( # type: ignore[override]
self,
*_,
**__,
) -> None:
"""Skip export to onnx."""
return self._export_not_supported_message()

def to_openvino( # type: ignore[override]
self,
*_,
**__,
) -> None:
"""Skip export to openvino."""
return self._export_not_supported_message()
6 changes: 6 additions & 0 deletions tests/integration/model/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# SPDX-License-Identifier: Apache-2.0

from pathlib import Path
from unittest.mock import MagicMock

import pytest

Expand Down Expand Up @@ -209,6 +210,11 @@ def _get_objects(
)

model = get_model(model_name, **extra_args)

if model_name == "vlm_ad":
model.vlm_backend = MagicMock()
model.vlm_backend.predict.return_value = "YES: Because reasons..."

engine = Engine(
logger=False,
default_root_dir=project_path,
Expand Down