Skip to content

Commit

Permalink
🔨 Replace imgaug with Native PyTorch Transforms (#2436)
Browse files Browse the repository at this point in the history
* Add multi random choice transform

* Add DRAEMAugmenter class and Perlin noise generation to new_perlin.py

- Introduced DRAEMAugmenter for advanced image augmentations using torchvision v2.
- Implemented various augmentation techniques including ColorJitter, RandomAdjustSharpness, and custom transformations.
- Added functionality for comparing augmentation methods and visualizing results.
- Included utility functions for metrics computation and image processing.
- Established logging for better traceability of operations.

This commit enhances the image processing capabilities within the Anomalib framework, facilitating more robust anomaly detection workflows.

* Add the new perlin noise

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* Add the new perlin noise

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* add generate_perlin_noise relative import

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* add tiffile as a dependency

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* Remove upper bound from wandb

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* Added skimage

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* add scikit-learn as a dependency

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* limit ollama to < 0.4.0 as it has breaking changes

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* Fix data generators in test helpers

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* Update the perlin augmenters

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* Fix numpy validator tests caused by numpy upgrade

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* Fix CS-Flow tests

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

* Fix the tests

Signed-off-by: Samet Akcay <samet.akcay@intel.com>

---------

Signed-off-by: Samet Akcay <samet.akcay@intel.com>
  • Loading branch information
samet-akcay authored Nov 27, 2024
1 parent c16f51e commit 2f3d616
Show file tree
Hide file tree
Showing 16 changed files with 453 additions and 342 deletions.
7 changes: 4 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ core = [
"av>=10.0.0",
"einops>=0.3.2",
"freia>=0.2",
"imgaug==0.4.0",
"kornia>=0.6.6",
"matplotlib>=3.4.3",
"opencv-python>=4.5.3.56",
"pandas>=1.1.0",
"scikit-image", # NOTE: skimage should be removed as part of dependency cleanup
"tifffile",
"timm",
"lightning>=2.2",
"torch>=2",
Expand All @@ -57,12 +58,12 @@ core = [
"open-clip-torch>=2.23.0,<2.26.1",
]
openvino = ["openvino>=2024.0", "nncf>=2.10.0", "onnx>=1.16.0"]
vlm = ["ollama", "openai", "python-dotenv","transformers"]
vlm = ["ollama<0.4.0", "openai", "python-dotenv","transformers"]
loggers = [
"comet-ml>=3.31.7",
"gradio>=4",
"tensorboard",
"wandb>=0.12.17,<=0.15.9",
"wandb",
"mlflow >=1.0.0",
]
notebooks = ["gitpython", "ipykernel", "ipywidgets", "notebook"]
Expand Down
3 changes: 2 additions & 1 deletion src/anomalib/data/transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
# SPDX-License-Identifier: Apache-2.0

from .center_crop import ExportableCenterCrop
from .multi_random_choice import MultiRandomChoice

__all__ = ["ExportableCenterCrop"]
__all__ = ["ExportableCenterCrop", "MultiRandomChoice"]
82 changes: 82 additions & 0 deletions src/anomalib/data/transforms/multi_random_choice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""Multi random choice transform."""

# Copyright (C) 2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

from collections.abc import Callable, Sequence

import torch
from torchvision.transforms import v2


class MultiRandomChoice(v2.Transform):
"""Apply multiple transforms randomly picked from a list.
This transform does not support torchscript.
Args:
transforms (sequence or torch.nn.Module): List of transformations to choose from.
probabilities (list[float] | None, optional): Probability of each transform being picked.
If None (default), all transforms have equal probability. If provided, probabilities
will be normalized to sum to 1.
num_transforms (int): Maximum number of transforms to apply at once.
Defaults to ``1``.
fixed_num_transforms (bool): If ``True``, always applies exactly ``num_transforms`` transforms.
If ``False``, randomly picks between 1 and ``num_transforms``.
Defaults to ``False``.
Examples:
>>> import torchvision.transforms.v2 as v2
>>> transforms = [
... v2.RandomHorizontalFlip(p=1.0),
... v2.ColorJitter(brightness=0.5),
... v2.RandomRotation(10),
... ]
>>> # Apply 1-2 random transforms with equal probability
>>> transform = MultiRandomChoice(transforms, num_transforms=2)
>>> # Always apply exactly 2 transforms with custom probabilities
>>> transform = MultiRandomChoice(
... transforms,
... probabilities=[0.5, 0.3, 0.2],
... num_transforms=2,
... fixed_num_transforms=True
... )
"""

def __init__(
self,
transforms: Sequence[Callable],
probabilities: list[float] | None = None,
num_transforms: int = 1,
fixed_num_transforms: bool = False,
) -> None:
if not isinstance(transforms, Sequence):
msg = "Argument transforms should be a sequence of callables"
raise TypeError(msg)

if probabilities is None:
probabilities = [1.0] * len(transforms)
elif len(probabilities) != len(transforms):
msg = f"Length of p doesn't match the number of transforms: {len(probabilities)} != {len(transforms)}"
raise ValueError(msg)

super().__init__()

self.transforms = transforms
total = sum(probabilities)
self.probabilities = [probability / total for probability in probabilities]

self.num_transforms = num_transforms
self.fixed_num_transforms = fixed_num_transforms

def forward(self, *inputs: torch.Tensor) -> torch.Tensor | tuple[torch.Tensor, ...]:
"""Apply randomly selected transforms to the input."""
# First determine number of transforms to apply
num_transforms = (
self.num_transforms if self.fixed_num_transforms else int(torch.randint(self.num_transforms, (1,)) + 1)
)
# Get transforms
idx = torch.multinomial(torch.tensor(self.probabilities), num_transforms).tolist()
transform = v2.Compose([self.transforms[i] for i in idx])
return transform(*inputs)
6 changes: 2 additions & 4 deletions src/anomalib/data/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
# Copyright (C) 2022-2024 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

from .augmenter import Augmenter
from .boxes import boxes_to_anomaly_maps, boxes_to_masks, masks_to_boxes
from .download import DownloadInfo, download_and_extract
from .generators import random_2d_perlin
from .generators import generate_perlin_noise
from .image import (
generate_output_image_filename,
get_image_filenames,
Expand All @@ -30,7 +29,7 @@
"generate_output_image_filename",
"get_image_filenames",
"get_image_height_and_width",
"random_2d_perlin",
"generate_perlin_noise",
"read_image",
"read_mask",
"read_depth_image",
Expand All @@ -42,7 +41,6 @@
"TestSplitMode",
"LabelName",
"DirType",
"Augmenter",
"masks_to_boxes",
"boxes_to_masks",
"boxes_to_anomaly_maps",
Expand Down
171 changes: 0 additions & 171 deletions src/anomalib/data/utils/augmenter.py

This file was deleted.

4 changes: 2 additions & 2 deletions src/anomalib/data/utils/generators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
# Copyright (C) 2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0

from .perlin import random_2d_perlin
from .perlin import PerlinAnomalyGenerator, generate_perlin_noise

__all__ = ["random_2d_perlin"]
__all__ = ["PerlinAnomalyGenerator", "generate_perlin_noise"]
Loading

0 comments on commit 2f3d616

Please sign in to comment.