Skip to content
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
fb3726e
Add validators
samet-akcay Sep 18, 2024
67f3032
Add depth validators to depth classes
samet-akcay Sep 18, 2024
d01f8bf
Add image validators to depth classes
samet-akcay Sep 18, 2024
5a943a1
Add video validators to depth classes
samet-akcay Sep 18, 2024
0e3ddf3
Add numpy validators and update dataclasses
samet-akcay Sep 18, 2024
539ddd0
Run all the tests on the ci
samet-akcay Sep 18, 2024
d75f068
Fix the tests
samet-akcay Sep 19, 2024
eda14fa
Created validator tests and added numpy video tests
samet-akcay Sep 19, 2024
a3f4c57
Add numpy image tests
samet-akcay Sep 19, 2024
3f718d3
Add numpy depth tests
samet-akcay Sep 19, 2024
8116506
Add torch validator tests
samet-akcay Sep 19, 2024
92b96fd
Fix numpy validation tests
samet-akcay Sep 19, 2024
47f183a
Convet private _validate methods to public validate method
samet-akcay Sep 20, 2024
18e11fd
Revert "Convet private _validate methods to public validate method"
samet-akcay Sep 22, 2024
37ff383
Convert private _validate methods to public validate method
samet-akcay Sep 22, 2024
42217e2
Remove batch_size arg from validators
samet-akcay Sep 23, 2024
56c1a3e
Remove anomaly_map from validators
samet-akcay Sep 23, 2024
965d13c
Use validators as mixins
samet-akcay Sep 23, 2024
6934f7a
convert abstractmethods to static abstractmethods
samet-akcay Sep 23, 2024
0df8b4e
Move pred-score computation to model implementations
samet-akcay Sep 23, 2024
3447160
Add missing pred_score implemenations in models
samet-akcay Sep 23, 2024
6ea1428
Fix the numpy validation tests
samet-akcay Sep 23, 2024
20ad208
Fix visualization tests
samet-akcay Sep 24, 2024
02549eb
Add numpy video validator mixin to numpy video item, and remove valid…
samet-akcay Sep 24, 2024
f8f7967
Add np.bool_ to validate np label
samet-akcay Sep 24, 2024
584254c
add convert_to_title_case function to path utils
samet-akcay Oct 1, 2024
6eba3fe
Add ImageVisualizer
samet-akcay Oct 1, 2024
ff525e4
Create image visualization functionals
samet-akcay Oct 1, 2024
1c2c598
Create image visualization class
samet-akcay Oct 1, 2024
5d37462
Add a comment in visualize-item
samet-akcay Oct 2, 2024
95bceda
Add a comment in visualize-item
samet-akcay Oct 2, 2024
c17c77c
Update functional.py
samet-akcay Oct 2, 2024
2195cd4
Add warning for invalid overlays
samet-akcay Oct 2, 2024
b42cc5a
Merge branch 'pil-based-visualization' of github.com:samet-akcay/anom…
samet-akcay Oct 2, 2024
64f33c7
Add overlay_mask function with fill and contour modes
samet-akcay Oct 3, 2024
78a4c4b
Moved `visualize_image_item` to a new module
samet-akcay Oct 3, 2024
ed4b44e
Fix typo in visualizer
samet-akcay Oct 3, 2024
eb81add
Pass mask color to visualize_mask
samet-akcay Oct 3, 2024
1304e5f
Fix tests
samet-akcay Oct 3, 2024
660fe24
Modify item_visualizer
Oct 8, 2024
941d3e2
Update visualize item
samet-akcay Oct 8, 2024
d39d530
Update the item visualization
samet-akcay Oct 8, 2024
e12edf7
Update the item visualization
samet-akcay Oct 8, 2024
72d02ac
Fix tests
samet-akcay Oct 9, 2024
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
13 changes: 4 additions & 9 deletions src/anomalib/engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@
from anomalib.callbacks.checkpoint import ModelCheckpoint
from anomalib.callbacks.metrics import _MetricsCallback
from anomalib.callbacks.timer import TimerCallback
from anomalib.callbacks.visualizer import _VisualizationCallback
from anomalib.data import AnomalibDataModule, AnomalibDataset, PredictDataset
from anomalib.deploy import CompressionType, ExportType
from anomalib.models import AnomalyModule
from anomalib.utils.path import create_versioned_dir
from anomalib.utils.visualization import ImageVisualizer
from anomalib.visualization import ImageVisualizer

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -378,13 +377,9 @@ def _setup_anomalib_callbacks(self, model: AnomalyModule) -> None:
# Add the metrics callback.
_callbacks.append(_MetricsCallback(self.task, self.image_metric_names, self.pixel_metric_names))

_callbacks.append(
_VisualizationCallback(
visualizers=ImageVisualizer(task=self.task, normalize=False),
save=True,
root=self._cache.args["default_root_dir"] / "images",
),
)
# Add the image visualizer callback if it is passed by the user.
if not any(isinstance(callback, ImageVisualizer) for callback in self._cache.args["callbacks"]):
_callbacks.append(ImageVisualizer())

_callbacks.append(TimerCallback())

Expand Down
146 changes: 146 additions & 0 deletions src/anomalib/utils/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,149 @@ def convert_to_snake_case(s: str) -> str:

# Replace multiple consecutive underscores with a single underscore
return re.sub(r"__+", "_", s)


def convert_to_title_case(text: str) -> str:
"""Converts a given text to title case, handling regular text, snake_case, and camelCase.

Args:
text (str): The input text to be converted to title case.

Returns:
str: The input text converted to title case.

Raises:
TypeError: If the input is not a string.

Examples:
Regular text:
>>> convert_to_title_case("the quick brown fox")
'The Quick Brown Fox'

Snake case:
>>> convert_to_title_case("convert_snake_case_to_title_case")
'Convert Snake Case To Title Case'

Camel case:
>>> convert_to_title_case("convertCamelCaseToTitleCase")
'Convert Camel Case To Title Case'

Pascal case:
>>> convert_to_title_case("ConvertPascalCaseToTitleCase")
'Convert Pascal Case To Title Case'

Mixed cases:
>>> convert_to_title_case("mixed_snake_camelCase and PascalCase")
'Mixed Snake Camel Case And Pascal Case'

Handling punctuation and contractions:
>>> convert_to_title_case("what's the_weather_like? it'sSunnyToday.")
"What's The Weather Like? It's Sunny Today."

With numbers and special characters:
>>> convert_to_title_case("python3.9_features and camelCaseNames")
'Python 3.9 Features And Camel Case Names'
"""
if not isinstance(text, str):
msg = "Input must be a string"
raise TypeError(msg)

# Handle snake_case
text = text.replace("_", " ")

# Handle camelCase and PascalCase
text = re.sub(r"([a-z])([A-Z])", r"\1 \2", text)
text = re.sub(r"([A-Z])([A-Z][a-z])", r"\1 \2", text)

# Split the text into words, preserving punctuation
words = re.findall(r"[\w']+|[.,!?;]", text)

# Capitalize each word
result = [word.capitalize() if word.isalpha() or "'" in word else word for word in words]

# Join the words back together
return " ".join(result)


def generate_output_filename(
input_path: str | Path,
output_path: str | Path,
dataset_name: str,
category: str | None = None,
mkdir: bool = True,
) -> Path:
"""Generate an output filename based on the input path, preserving the directory structure.

This function takes an input path, an output base directory, a dataset name, and an optional
category. It generates an output path that preserves the directory structure after the dataset
name (and category, if provided) while placing the file in the specified output directory.

Args:
input_path (str | Path): The input file path.
output_path (str | Path): The base output directory.
dataset_name (str): The name of the dataset in the input path.
category (str | None, optional): The category name in the input path. Defaults to None.
mkdir (bool, optional): Whether to create the output directory. Defaults to True.

Returns:
Path: The generated output file path.

Raises:
ValueError: If the dataset name or category (if provided) is not found in the input path.

Examples:
>>> input_path = "/data/MVTec/bottle/test/broken_large/000.png"
>>> output_base = "/results"
>>> dataset = "MVTec"

# With category
>>> generate_output_filename(input_path, output_base, dataset, "bottle")
PosixPath('/results/test/broken_large/000.png')

# Without category
>>> generate_output_filename(input_path, output_base, dataset)
PosixPath('/results/bottle/test/broken_large/000.png')

# Different dataset structure
>>> input_path = "/datasets/MyDataset/train/class_A/image_001.jpg"
>>> generate_output_filename(input_path, "/output", "MyDataset", "class_A")
PosixPath('/output/image_001.jpg')

# Error case: Dataset not in path
>>> generate_output_filename("/wrong/path/image.png", "/out", "NonexistentDataset")
Traceback (most recent call last):
...
ValueError: Dataset name 'NonexistentDataset' not found in the input path.
"""
input_path = Path(input_path)
output_path = Path(output_path)

# Find the position of the dataset name in the path
try:
dataset_index = input_path.parts.index(dataset_name)
except ValueError:
msg = f"Dataset name '{dataset_name}' not found in the input path."
raise ValueError(msg) from None

# Determine the start index for preserving subdirectories
start_index = dataset_index + 1
if category:
try:
category_index = input_path.parts.index(category, dataset_index)
start_index = category_index + 1
except ValueError:
msg = f"Category '{category}' not found in the input path after the dataset name."
raise ValueError(msg) from None

# Preserve all subdirectories after the category (or dataset if no category)
subdirs = input_path.parts[start_index:-1] # Exclude the filename

# Construct the output path
output_path = output_path / Path(*subdirs)

# Create the output directory if it doesn't exist
if mkdir:
output_path.mkdir(parents=True, exist_ok=True)

# Create and return the output filename
return output_path / input_path.name
17 changes: 17 additions & 0 deletions src/anomalib/visualization/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Visualization module."""

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

from .image import ImageVisualizer, visualize_anomaly_map, visualize_mask
from .image.item_visualizer import visualize_field, visualize_image_item

__all__ = [
# Image visualizer class
"ImageVisualizer",
# Image visualization functions
"visualize_anomaly_map",
"visualize_field",
"visualize_image_item",
"visualize_mask",
]
31 changes: 31 additions & 0 deletions src/anomalib/visualization/image/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Image visualization module."""

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

from .functional import (
add_text_to_image,
apply_colormap,
overlay_images,
overlay_mask,
visualize_anomaly_map,
visualize_field,
visualize_mask,
)
from .item_visualizer import visualize_image_item
from .visualizer import ImageVisualizer

__all__ = [
# Visualization functions
"add_text_to_image",
"apply_colormap",
"overlay_images",
"overlay_mask",
"visualize_anomaly_map",
"visualize_field",
"visualize_mask",
# Visualize ImageItem
"visualize_image_item",
# Visualization class
"ImageVisualizer",
]
Loading