Skip to content
Open
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 .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ jobs:
with:
test-type: "integration"
runner: "self-hosted"
timeout: 60
enable-cache: "false"
secrets:
codecov-token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
112 changes: 44 additions & 68 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Libraries",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
]
Expand Down Expand Up @@ -100,19 +101,11 @@ test = [
]
# PyTorch dependency groups
cpu = [
"torch>=2.4.0,<=2.8.0",
"torchvision>=0.19.0",
]
cu118 = [
"torch>=2.4.0,<=2.8.0",
"torchvision>=0.19.0",
]
cu121 = [
"torch>=2.4.0,<=2.8.0",
"torch>=2.4.0",
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The removal of the upper bound <=2.8.0 for the cpu extra creates potential compatibility risks. If PyTorch 3.0+ introduces breaking changes, users could encounter issues. Consider retaining an upper bound (e.g., <3.0) or adding a comment explaining why the constraint was relaxed.

Suggested change
"torch>=2.4.0",
"torch>=2.4.0,<3.0",

Copilot uses AI. Check for mistakes.
"torchvision>=0.19.0",
]
cu124 = [
"torch>=2.4.0,<=2.8.0",
"torch>=2.4.0",
"torchvision>=0.19.0",
]
cu130 = [
Expand Down Expand Up @@ -159,8 +152,6 @@ managed = true
conflicts = [
[
{ extra = "cpu" },
{ extra = "cu118" },
{ extra = "cu121" },
{ extra = "cu124" },
{ extra = "cu130" },
{ extra = "rocm" },
Expand All @@ -174,16 +165,6 @@ name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

[[tool.uv.index]]
name = "pytorch-cu118"
url = "https://download.pytorch.org/whl/cu118"
explicit = true

[[tool.uv.index]]
name = "pytorch-cu121"
url = "https://download.pytorch.org/whl/cu121"
explicit = true

[[tool.uv.index]]
name = "pytorch-cu124"
url = "https://download.pytorch.org/whl/cu124"
Expand All @@ -208,17 +189,13 @@ explicit = true
[tool.uv.sources]
torch = [
{ index = "pytorch-cpu", extra = "cpu" },
{ index = "pytorch-cu118", extra = "cu118" },
{ index = "pytorch-cu121", extra = "cu121" },
{ index = "pytorch-cu124", extra = "cu124" },
{ index = "pytorch-cu130", extra = "cu130" },
{ index = "pytorch-rocm61", extra = "rocm" },
{ index = "pytorch-xpu", extra = "xpu" },
]
torchvision = [
{ index = "pytorch-cpu", extra = "cpu" },
{ index = "pytorch-cu118", extra = "cu118" },
{ index = "pytorch-cu121", extra = "cu121" },
{ index = "pytorch-cu124", extra = "cu124" },
{ index = "pytorch-cu130", extra = "cu130" },
{ index = "pytorch-rocm61", extra = "rocm" },
Expand All @@ -235,9 +212,46 @@ pytorch-triton-xpu = [
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# RUFF CONFIGURATION #
[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]

# Same as Black.
line-length = 120

# Assume Python 3.10.
target-version = "py310"

# Allow imports relative to the "src" and "tests" directories.
src = ["src", "tests"]

[tool.ruff.lint]
# Enable preview features
preview = true

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

# Enable rules
select = [
"F", # Pyflakes (`F`)
Expand Down Expand Up @@ -286,8 +300,6 @@ select = [
"NPY", # NumPy-specific rules (`NPY`)
"PERF", # Perflint (`PERF`)
"RUF", # Ruff-specific rules (`RUF`)
# "FURB", # refurb (`FURB`) - ERROR: Unknown rule selector: `FURB`
# "LOG", # flake8-logging (`LOG`) - ERROR: Unknown rule selector: `LOG`
]

ignore = [
Expand Down Expand Up @@ -346,57 +358,21 @@ ignore = [
fixable = ["ALL"]
unfixable = []

# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]

# Same as Black.
line-length = 120

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

# Assume Python 3.10.
target-version = "py310"

# Allow imports relative to the "src" and "tests" directories.
src = ["src", "tests"]

[tool.ruff.mccabe]
[tool.ruff.lint.mccabe]
# Unlike Flake8, default to a complexity level of 10.
max-complexity = 15


[tool.ruff.pydocstyle]
[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.ruff.flake8-copyright]
[tool.ruff.lint.flake8-copyright]
notice-rgx = """
# Copyright \\(C\\) (\\d{4}(-\\d{4})?) Intel Corporation
# SPDX-License-Identifier: Apache-2\\.0
"""

[tool.ruff.per-file-ignores]
[tool.ruff.lint.per-file-ignores]
"examples/notebooks/**/*" = ["CPY001"]

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
Expand Down
5 changes: 3 additions & 2 deletions src/anomalib/data/dataclasses/torch/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@

from anomalib.data.dataclasses.generic import ImageT, _GenericBatch, _GenericItem

NumpyT = TypeVar("NumpyT")


class InferenceBatch(NamedTuple):
"""Batch for use in torch and inference models.
Expand All @@ -43,6 +41,9 @@ class InferenceBatch(NamedTuple):
pred_mask: torch.Tensor | None = None


NumpyT = TypeVar("NumpyT")
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Moving NumpyT after InferenceBatch appears to be for ordering purposes, but it creates a gap between related type definitions. Consider adding a comment explaining why this TypeVar is defined here rather than at the top with other type-related imports, especially since it's used by the ToNumpyMixin class below.

Copilot uses AI. Check for mistakes.


@dataclass
class ToNumpyMixin(Generic[NumpyT]):
"""Mixin for converting torch-based dataclasses to numpy.
Expand Down
16 changes: 9 additions & 7 deletions src/anomalib/data/datasets/image/datumaro.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,15 @@ def make_datumaro_dataset(
image_path = Path(root) / "images" / "default" / item["image"]["path"]
label_index = item["annotations"][0]["label_id"]
label = categories[label_index]
samples.append({
"image_path": str(image_path),
"label": label,
"label_index": label_index,
"split": None,
"mask_path": "", # mask is provided in annotation file
})
samples.append(
{
"image_path": str(image_path),
"label": label,
"label_index": label_index,
"split": None,
"mask_path": "", # mask is provided in annotation file
},
)
samples_df = pd.DataFrame(
samples,
columns=["image_path", "label", "label_index", "split", "mask_path"],
Expand Down
21 changes: 19 additions & 2 deletions src/anomalib/engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ def fit(
val_dataloaders: EVAL_DATALOADERS | None = None,
datamodule: AnomalibDataModule | None = None,
ckpt_path: str | Path | None = None,
**kwargs, # noqa: ARG002
) -> None:
"""Fit the model using the trainer.

Expand All @@ -379,6 +380,7 @@ def fit(
Defaults to None.
ckpt_path (str | None, optional): Checkpoint path. If provided, the model will be loaded from this path.
Defaults to None.
**kwargs: Additional arguments passed to PyTorch Lightning Trainer's fit method.

CLI Usage:
1. you can pick a model, and you can run through the MVTec dataset.
Expand Down Expand Up @@ -415,7 +417,14 @@ def fit(
weights_only=False,
)
else:
self.trainer.fit(model, train_dataloaders, val_dataloaders, datamodule, ckpt_path, weights_only=False)
self.trainer.fit(
model,
train_dataloaders,
val_dataloaders,
datamodule,
ckpt_path,
weights_only=False,
)

def validate(
self,
Expand All @@ -424,6 +433,7 @@ def validate(
ckpt_path: str | Path | None = None,
verbose: bool = True,
datamodule: AnomalibDataModule | None = None,
**kwargs, # noqa: ARG002
) -> _EVALUATE_OUTPUT | None:
"""Validate the model using the trainer.

Expand All @@ -441,6 +451,7 @@ def validate(
AnomalibDataModule` that defines the
:class:`~lightning.pytorch.core.hooks.DataHooks.val_dataloader` hook.
Defaults to None.
**kwargs: Additional arguments passed to PyTorch Lightning Trainer's validate method.

Returns:
_EVALUATE_OUTPUT | None: Validation results.
Expand Down Expand Up @@ -472,6 +483,7 @@ def test(
ckpt_path: str | Path | None = None,
verbose: bool = True,
datamodule: AnomalibDataModule | None = None,
**kwargs, # noqa: ARG002
) -> _EVALUATE_OUTPUT:
"""Test the model using the trainer.

Expand All @@ -497,6 +509,7 @@ def test(
A :class:`~lightning.pytorch.core.datamodule.AnomalibDataModule` that defines
the :class:`~lightning.pytorch.core.hooks.DataHooks.test_dataloader` hook.
Defaults to None.
**kwargs: Additional arguments passed to PyTorch Lightning Trainer's test method.

Returns:
_EVALUATE_OUTPUT: A List of dictionaries containing the test results. 1 dict per dataloader.
Expand Down Expand Up @@ -556,7 +569,7 @@ def test(

if self._should_run_validation(model or self.model, ckpt_path):
logger.info("Running validation before testing to collect normalization metrics and/or thresholds.")
self.trainer.validate(model, dataloaders, None, verbose=False, datamodule=datamodule)
self.trainer.validate(model, dataloaders, None, verbose=False, datamodule=datamodule, weights_only=False)
return self.trainer.test(model, dataloaders, ckpt_path, verbose, datamodule, weights_only=False)

def predict(
Expand All @@ -568,6 +581,7 @@ def predict(
return_predictions: bool | None = None,
ckpt_path: str | Path | None = None,
data_path: str | Path | None = None,
**kwargs, # noqa: ARG002
) -> _PREDICT_OUTPUT | None:
"""Predict using the model using the trainer.

Expand Down Expand Up @@ -601,6 +615,7 @@ def predict(
data_path (str | Path | None):
Path to the image or folder containing images to generate predictions for.
Defaults to None.
**kwargs: Additional arguments passed to PyTorch Lightning Trainer's predict method.

Returns:
_PREDICT_OUTPUT | None: Predictions.
Expand Down Expand Up @@ -747,6 +762,7 @@ def export(
ov_kwargs: dict[str, Any] | None = None,
onnx_kwargs: dict[str, Any] | None = None,
ckpt_path: str | Path | None = None,
**kwargs, # noqa: ARG002
) -> Path | None:
r"""Export the model in PyTorch, ONNX or OpenVINO format.

Expand Down Expand Up @@ -785,6 +801,7 @@ def export(
See https://pytorch.org/docs/stable/onnx.html#torch.onnx.export for details.
Defaults to ``None``.
ckpt_path (str | Path | None): Checkpoint path. If provided, the model will be loaded from this path.
**kwargs: Additional arguments.

Returns:
Path: Path to the exported model.
Expand Down
2 changes: 1 addition & 1 deletion src/anomalib/metrics/threshold/f1_adaptive_threshold.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@


@contextmanager
def handle_mac(metric: "_F1AdaptiveThreshold") -> Generator[None, None, None]:
def handle_mac(metric: "_F1AdaptiveThreshold") -> Generator[None]:
"""Temporarily move tensors to CPU on macOS/MPS and restore after.

This context manager checks whether the provided metric instance has
Expand Down
10 changes: 6 additions & 4 deletions src/anomalib/models/components/base/anomalib_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,12 @@ def configure_pre_processor(image_size: tuple[int, int] | None = None) -> PrePro
"""
image_size = image_size or (256, 256)
return PreProcessor(
transform=Compose([
Resize(image_size, antialias=True),
Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
]),
transform=Compose(
[
Resize(image_size, antialias=True),
Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
],
),
)

def configure_post_processor(self) -> PostProcessor | None:
Expand Down
Loading