From 434a7addeea8adc8f39e5f1182b1dd1ba6ad9468 Mon Sep 17 00:00:00 2001
From: jpcbertoldo <24547377+jpcbertoldo@users.noreply.github.com>
Date: Thu, 1 Sep 2022 21:14:53 +0200
Subject: [PATCH 01/38] copy mvtec and add some config conts in comments
---
anomalib/data/mvtec_loco.py | 556 ++++++++++++++++++++++++++++++++++++
1 file changed, 556 insertions(+)
create mode 100644 anomalib/data/mvtec_loco.py
diff --git a/anomalib/data/mvtec_loco.py b/anomalib/data/mvtec_loco.py
new file mode 100644
index 0000000000..940f2b4927
--- /dev/null
+++ b/anomalib/data/mvtec_loco.py
@@ -0,0 +1,556 @@
+"""
+
+distinguishes structural and logical anomalies
+n_image: 3644
+splits:
+no overlap and fixed
+train
+normal-only
+n_image_train: 1772
+validation
+normal-only
+n_image_validation: 304
+test
+normal + anomalous (structural and logical)
+n_image_test: 1568
+n_category: 5
+breakfast_box
+juice_bottle
+pushpins
+screw_bag
+splicing_connectors
+n_defect_type: 89
+
+##################################################
+
+configs
+https://docs.google.com/spreadsheets/d/1qHbyTsU2At1fusQsV8KH3_qdp3SYHHLy7QtpohKJIk0/edit?usp=sharing
+
+stats overview
+https://docs.google.com/spreadsheets/d/11GSf1SVsHFYDSwMAULEd7g5QK7P3Y21YMB10D_g0-Gk/edit?usp=sharing
+
+##################################################
+
+assumptions
+objects are in a fixed position (mechanical alignment)
+illumination is well suited
+the access to images with real anomalies is limited (“impossible”)
+images only show a single object or logically ensemble set of objects (i.e. one-class setting although a “class” here is a composed object)
+no training annotations -- although it is assumed that the images in training are indeed from the target class (i.e. no noise)
+problem 1 (image-wise anomaly detection): “is there an anomaly in the image?”
+problem 2 (pixel-wise anomaly detection or anomaly segmentation): “which pixels belong to the anomaly?”
+pixel-wise metric: Saturated Per-Region Overlap (sPRO)
+structural anomaly pixel annotation policy
+defects are confined to local regions
+each pixel that introduces a visual structure that is not present in the anomaly-free images is anomalous
+logical anomaly pixel annotation policy
+the union of all areas of the image that could be the cause for the anomaly is anomalous
+a method is not necessarily required to predict the whole ground truth area as anomalous
+
+##################################################
+
+breakfast box
+n_anomaly_type (n_structural, n_logical): 22 (5, 17)
+logical constraints
+contains 2 tangerines
+contains 1 nectarine
+the tangerines and the nectarine on the left
+cereals (C) and a mix of banana chips and almonds (B&A) on the right
+the ratio between C and B&A is fixed
+the relative position of C and B&A is fixed
+examples of logical defects
+too many banana chips and almonds
+
+##################################################
+
+juice bottle
+n_anomaly_type (n_structural, n_logical): 18 (7, 11)
+logical constraints
+there is 1 bottle
+the bottle is filled with a liquid and the fill level is always the same
+the liquid is of 1 out of 3 colors (red, yellow, white-ish)
+the bottle carries 2 labels
+the first label is attached to the center of the bottle
+the first label displays an icon that determines the type of liquid (cherry, orange, banana)
+cherry: red
+orange: yellow
+banana: white-ish
+the second label is attached to the lower part of the bottle
+the second label contains the text “100% Juice”
+examples of logical defects
+(left) the icon does not match the type of juice
+(middle) the icon is slightly misplaced
+(right) the fill level is too high
+
+##################################################
+
+pushpins
+n_anomaly_type (n_structural, n_logical): 8 (4, 4)
+logical constraints
+each compartment contains 1 pushpin
+examples of logical defects
+1 compartment has a missing pin
+
+##################################################
+
+screw bag
+n_anomaly_type (n_structural, n_logical): 20 (4, 16)
+logical constraints
+the bag contains
+2 washers
+2 nuts
+1 long screw
+2 short screw
+examples of logical defects
+two long screws and lacks a short one
+
+##################################################
+
+splicing connectors
+n_anomaly_type (n_structural, n_logical): 21 (8, 13)
+logical constraints
+there are 2 splicing connectors
+they have the same number of cable clamps
+they are linked by 1 cable
+the number of clamps has a one-to-one correspondence to the color of the cable
+2: yellow
+3: blue
+5: red
+the cable has to terminate in the same relative position on its two ends such that the whole construction exhibits a mirror symmetry
+examples of logical defects
+(left) the two splicing connectors do not have the same number of clamps
+(center) the color of the cable does not match the number of clamps
+(right) the cable terminates in different positions
+
+##################################################
+
+missing objects
+the area in which the object could occur
+the saturation threshold is chosen to be equal to the area of the missing object
+the saturation threshold for an object is chosen from the lower end of the distribution of its (manually annotated) area
+example (image): pushpin
+the missing pushpin can occur anywhere inside its compartment, therefore its entire area is annotated
+the saturation threshold is set to the size of a pushpin
+
+##################################################
+
+additional objects
+too many instances of an object: all instances of the object are annotated
+the saturation threshold is set to the area of the extraneous objects
+example (image): splicing connectors
+an additional cable is present between the two splicing connectors
+it is not clear which of the two cables represents the anomaly, therefore both are annotated
+the saturation threshold is set to the area of one cable (i.e., half of the annotated region)
+properties
+a method can obtain a perfect score even if it only marks one of the two cables as an anomaly
+a method that marks both is neither penalized nor (extra-)rewarded
+
+##################################################
+
+other logical constraints
+example (image, left): juice bottle
+the bottle is filled with orange juice but carries the label of the cherry juice
+both the orange juice and the label with the cherry are present in the training set, but the logical anomaly arises due to the erroneous combination of the two in the same image
+either the area filled with juice or the cherry as could be considered anomalous, therefore the union of the two regions is annotated
+the saturation threshold is set to the area of the cherry because the segmentation of the cherry is sufficient to solve the anomaly localization
+
+"""
+
+
+"""
+category anomaly type gt_value saturation_definition saturation_parameter
+breakfast_box missing_almonds logical 255 relative_to_anomaly 1.0000000
+breakfast_box missing_bananas logical 254 relative_to_anomaly 1.0000000
+breakfast_box missing_toppings logical 253 relative_to_anomaly 1.0000000
+breakfast_box missing_cereals logical 252 relative_to_anomaly 1.0000000
+breakfast_box missing_cereals_and_toppings logical 251 relative_to_anomaly 1.0000000
+breakfast_box nectarines_2_tangerine_1 logical 250 relative_to_image 0.0488770
+breakfast_box nectarine_1_tangerine_1 logical 249 relative_to_image 0.0411621
+breakfast_box nectarines_0_tangerines_2 logical 248 relative_to_image 0.0488770
+breakfast_box nectarines_0_tangerines_3 logical 247 relative_to_image 0.0488770
+breakfast_box nectarines_3_tangerines_0 logical 246 relative_to_image 0.0977539
+breakfast_box nectarines_0_tangerine_1 logical 245 relative_to_image 0.0900391
+breakfast_box nectarines_0_tangerines_0 logical 244 relative_to_image 0.1312012
+breakfast_box nectarines_0_tangerines_4 logical 243 relative_to_image 0.0823242
+breakfast_box compartments_swapped logical 242 relative_to_anomaly 1.0000000
+breakfast_box overflow logical 241 relative_to_anomaly 1.0000000
+breakfast_box underflow logical 240 relative_to_anomaly 1.0000000
+breakfast_box wrong_ratio logical 239 relative_to_anomaly 1.0000000
+breakfast_box mixed_cereals structural 238 relative_to_anomaly 1.0000000
+breakfast_box fruit_damaged structural 237 relative_to_anomaly 1.0000000
+breakfast_box box_damaged structural 236 relative_to_anomaly 1.0000000
+breakfast_box toppings_crushed structural 235 relative_to_anomaly 1.0000000
+breakfast_box contamination structural 234 relative_to_anomaly 1.0000000
+juice_box missing_top_label logical 255 relative_to_image 0.0550000
+juice_box missing_bottom_label logical 254 relative_to_image 0.0255469
+juice_box swapped_labels logical 253 relative_to_image 0.1100000
+juice_box damaged_label structural 252 relative_to_anomaly 1.0000000
+juice_box rotated_label structural 251 relative_to_anomaly 1.0000000
+juice_box misplaced_label_top logical 250 relative_to_image 0.0550000
+juice_box misplaced_label_bottom logical 249 relative_to_image 0.0255469
+juice_box label_text_incomplete structural 248 relative_to_anomaly 1.0000000
+juice_box empty_bottle logical 247 relative_to_anomaly 1.0000000
+juice_box wrong_fill_level_too_much logical 246 relative_to_anomaly 1.0000000
+juice_box wrong_fill_level_not_enough logical 245 relative_to_anomaly 1.0000000
+juice_box misplaced_fruit_icon logical 244 relative_to_anomaly 1.0000000
+juice_box missing_fruit_icon logical 243 relative_to_anomaly 1.0000000
+juice_box unknown_fruit_icon structural 242 relative_to_anomaly 1.0000000
+juice_box incomplete_fruit_icon structural 241 relative_to_anomaly 1.0000000
+juice_box wrong_juice_type logical 240 relative_to_image 0.0035156
+juice_box juice_color structural 239 relative_to_anomaly 1.0000000
+juice_box contamination structural 238 relative_to_anomaly 1.0000000
+pushpins additional_1_pushpin logical 255 relative_to_image 0.0037059
+pushpins additional_2_pushpins logical 254 relative_to_image 0.0074118
+pushpins missing_pushpin logical 253 relative_to_image 0.0037059
+pushpins missing_separator logical 252 relative_to_anomaly 1.0000000
+pushpins front_bent structural 251 relative_to_anomaly 1.0000000
+pushpins broken structural 250 relative_to_anomaly 1.0000000
+pushpins color structural 249 relative_to_anomaly 1.0000000
+pushpins contamination structural 248 relative_to_anomaly 1.0000000
+screw_bag screw_too_long logical 255 relative_to_image 0.0051136
+screw_bag screw_too_shor logical 254 relative_to_image 0.0051136
+screw_bag screws_1_very_short logical 253 relative_to_anomaly 1.0000000
+screw_bag screws_2_very_short logical 252 relative_to_image 0.0102273
+screw_bag additional_1_long_screw logical 251 relative_to_image 0.0168182
+screw_bag additional_1_short_screw logical 250 relative_to_image 0.0117045
+screw_bag additional_1_nut_ logical 249 relative_to_image 0.0042614
+screw_bag additional_2_nuts_ logical 248 relative_to_image 0.0085227
+screw_bag additional_1_washer_ logical 247 relative_to_image 0.0031250
+screw_bag additional_2_washers_ logical 246 relative_to_image 0.0062500
+screw_bag missing_1_long_screw logical 245 relative_to_image 0.0168182
+screw_bag missing_1_short_screw logical 244 relative_to_image 0.0117045
+screw_bag missing_1_nut logical 243 relative_to_image 0.0042614
+screw_bag missing_2_nuts logical 242 relative_to_image 0.0085227
+screw_bag missing_1_washer logical 241 relative_to_image 0.0031250
+screw_bag missing_2_washers logical 240 relative_to_image 0.0062500
+screw_bag bag_broken structural 239 relative_to_anomaly 1.0000000
+screw_bag color structural 238 relative_to_anomaly 1.0000000
+screw_bag contamination structural 237 relative_to_anomaly 1.0000000
+screw_bag part_broken structural 236 relative_to_anomaly 1.0000000
+splicing_connectors wrong_connector_type_5_2 logical 255 relative_to_image 0.0464360
+splicing_connectors wrong_connector_type_5_3 logical 254 relative_to_image 0.0306574
+splicing_connectors wrong_connector_type_3_2 logical 253 relative_to_image 0.0152941
+splicing_connectors cable_too_short_t2 logical 252 relative_to_image 0.0368858
+splicing_connectors cable_too_short_t3 logical 251 relative_to_image 0.0526644
+splicing_connectors cable_too_short_t5 logical 250 relative_to_image 0.0830450
+splicing_connectors missing_connector logical 249 relative_to_anomaly 1.0000000
+splicing_connectors missing_connector_and_cable logical 248 relative_to_image 0.0716955
+splicing_connectors missing_cable logical 247 relative_to_image 0.0124567
+splicing_connectors extra_cable logical 246 relative_to_anomaly 0.5000000
+splicing_connectors cable_color logical 245 relative_to_image 0.0124567
+splicing_connectors broken_cable structural 244 relative_to_anomaly 1.0000000
+splicing_connectors cable_cut logical 243 relative_to_anomaly 1.0000000
+splicing_connectors cable_not_plugged structural 242 relative_to_anomaly 1.0000000
+splicing_connectors unknown_cable_color structural 241 relative_to_anomaly 1.0000000
+splicing_connectors wrong_cable_location logical 240 relative_to_image 0.0124567
+splicing_connectors flipped_connector structural 239 relative_to_anomaly 1.0000000
+splicing_connectors broken_connector structural 238 relative_to_anomaly 1.0000000
+splicing_connectors open_lever structural 237 relative_to_anomaly 1.0000000
+splicing_connectors color structural 236 relative_to_anomaly 1.0000000
+splicing_connectors contamination structural 235 relative_to_anomaly 1.0000000
+"""
+
+import logging
+import tarfile
+import warnings
+from pathlib import Path
+from typing import Dict, Optional, Tuple, Union
+from urllib.request import urlretrieve
+
+import albumentations as A
+import cv2
+import numpy as np
+import pandas as pd
+from pandas.core.frame import DataFrame
+from pytorch_lightning.core.datamodule import LightningDataModule
+from pytorch_lightning.utilities.cli import DATAMODULE_REGISTRY
+from pytorch_lightning.utilities.types import EVAL_DATALOADERS, TRAIN_DATALOADERS
+from torch import Tensor
+from torch.utils.data import DataLoader
+from torch.utils.data.dataset import Dataset
+from torchvision.datasets.folder import VisionDataset
+
+from anomalib.data.inference import InferenceDataset
+from anomalib.data.utils import DownloadProgressBar, hash_check, read_image
+from anomalib.data.utils.split import (
+ create_validation_set_from_test_set,
+ split_normal_images_in_train_set,
+)
+from anomalib.pre_processing import PreProcessor
+
+logger = logging.getLogger(__name__)
+
+
+def make_mvtec_loco_dataset(
+ path: Path,
+ split: Optional[str] = None,
+ split_ratio: float = 0.1,
+ seed: Optional[int] = None,
+ create_validation_set: bool = False,
+) -> DataFrame:
+ samples_list = [(str(path),) + filename.parts[-3:] for filename in path.glob("**/*.png")]
+ if len(samples_list) == 0:
+ raise RuntimeError(f"Found 0 images in {path}")
+
+ samples = pd.DataFrame(samples_list, columns=["path", "split", "label", "image_path"])
+ samples = samples[samples.split != "ground_truth"]
+
+ # Create mask_path column
+ samples["mask_path"] = (
+ samples.path
+ + "/ground_truth/"
+ + samples.label
+ + "/"
+ + samples.image_path.str.rstrip("png").str.rstrip(".")
+ + "_mask.png"
+ )
+
+ # Modify image_path column by converting to absolute path
+ samples["image_path"] = samples.path + "/" + samples.split + "/" + samples.label + "/" + samples.image_path
+
+ # Split the normal images in training set if test set doesn't
+ # contain any normal images. This is needed because AUC score
+ # cannot be computed based on 1-class
+ if sum((samples.split == "test") & (samples.label == "good")) == 0:
+ samples = split_normal_images_in_train_set(samples, split_ratio, seed)
+
+ # Good images don't have mask
+ samples.loc[(samples.split == "test") & (samples.label == "good"), "mask_path"] = ""
+
+ # Create label index for normal (0) and anomalous (1) images.
+ samples.loc[(samples.label == "good"), "label_index"] = 0
+ samples.loc[(samples.label != "good"), "label_index"] = 1
+ samples.label_index = samples.label_index.astype(int)
+
+ if create_validation_set:
+ samples = create_validation_set_from_test_set(samples, seed=seed)
+
+ # Get the data frame for the split.
+ if split is not None and split in ["train", "val", "test"]:
+ samples = samples[samples.split == split]
+ samples = samples.reset_index(drop=True)
+
+ return samples
+
+
+class MVTecLOCODataset(VisionDataset):
+ """MVTec LOCO AD PyTorch Dataset."""
+
+ def __init__(
+ self,
+ root: Union[Path, str],
+ category: str,
+ pre_process: PreProcessor,
+ split: str,
+ task: str = "segmentation",
+ seed: Optional[int] = None,
+ create_validation_set: bool = False,
+ ) -> None:
+ super().__init__(root)
+
+ if seed is None:
+ warnings.warn(
+ "seed is None."
+ " When seed is not set, images from the normal directory are split between training and test dir."
+ " This will lead to inconsistency between runs."
+ )
+
+ self.root = Path(root) if isinstance(root, str) else root
+ self.category: str = category
+ self.split = split
+ self.task = task
+
+ self.pre_process = pre_process
+
+ self.samples = make_mvtec_loco_dataset(
+ path=self.root / category,
+ split=self.split,
+ seed=seed,
+ create_validation_set=create_validation_set,
+ )
+
+ def __len__(self) -> int:
+ """Get length of the dataset."""
+ return len(self.samples)
+
+ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
+ """Get dataset item for the index ``index``.
+
+ Args:
+ index (int): Index to get the item.
+
+ Returns:
+ Union[Dict[str, Tensor], Dict[str, Union[str, Tensor]]]: Dict of image tensor during training.
+ Otherwise, Dict containing image path, target path, image tensor, label and transformed bounding box.
+ """
+ item: Dict[str, Union[str, Tensor]] = {}
+
+ image_path = self.samples.image_path[index]
+ image = read_image(image_path)
+
+ pre_processed = self.pre_process(image=image)
+ item = {"image": pre_processed["image"]}
+
+ if self.split in ["val", "test"]:
+ label_index = self.samples.label_index[index]
+
+ item["image_path"] = image_path
+ item["label"] = label_index
+
+ if self.task == "segmentation":
+ mask_path = self.samples.mask_path[index]
+
+ # Only Anomalous (1) images has masks in MVTec AD dataset.
+ # Therefore, create empty mask for Normal (0) images.
+ if label_index == 0:
+ mask = np.zeros(shape=image.shape[:2])
+ else:
+ mask = cv2.imread(mask_path, flags=0) / 255.0
+
+ pre_processed = self.pre_process(image=image, mask=mask)
+
+ item["mask_path"] = mask_path
+ item["image"] = pre_processed["image"]
+ item["mask"] = pre_processed["mask"]
+
+ return item
+
+
+@DATAMODULE_REGISTRY
+class MVTecLOCO(LightningDataModule):
+ """MVTec LOCO AD Lightning Data Module."""
+
+ def __init__(
+ self,
+ root: str,
+ category: str,
+ # TODO: Remove default values. IAAALD-211
+ image_size: Optional[Union[int, Tuple[int, int]]] = None,
+ train_batch_size: int = 32,
+ test_batch_size: int = 32,
+ num_workers: int = 8,
+ task: str = "segmentation",
+ transform_config_train: Optional[Union[str, A.Compose]] = None,
+ transform_config_val: Optional[Union[str, A.Compose]] = None,
+ seed: Optional[int] = None,
+ create_validation_set: bool = False,
+ ) -> None:
+ super().__init__()
+
+ self.root = root if isinstance(root, Path) else Path(root)
+ self.category = category
+ self.dataset_path = self.root / self.category
+ self.transform_config_train = transform_config_train
+ self.transform_config_val = transform_config_val
+ self.image_size = image_size
+
+ if self.transform_config_train is not None and self.transform_config_val is None:
+ self.transform_config_val = self.transform_config_train
+
+ self.pre_process_train = PreProcessor(config=self.transform_config_train, image_size=self.image_size)
+ self.pre_process_val = PreProcessor(config=self.transform_config_val, image_size=self.image_size)
+
+ self.train_batch_size = train_batch_size
+ self.test_batch_size = test_batch_size
+ self.num_workers = num_workers
+
+ self.create_validation_set = create_validation_set
+ self.task = task
+ self.seed = seed
+
+ self.train_data: Dataset
+ self.test_data: Dataset
+ if create_validation_set:
+ self.val_data: Dataset
+ self.inference_data: Dataset
+
+ def prepare_data(self) -> None:
+ """Download the dataset if not available."""
+ if (self.root / self.category).is_dir():
+ logger.info("Found the dataset.")
+ else:
+ self.root.mkdir(parents=True, exist_ok=True)
+
+ logger.info("Downloading the Mvtec AD dataset.")
+ url = "https://www.mydrive.ch/shares/38536/3830184030e49fe74747669442f0f282/download/420938113-1629952094"
+ dataset_name = "mvtec_anomaly_detection.tar.xz"
+ zip_filename = self.root / dataset_name
+ with DownloadProgressBar(unit="B", unit_scale=True, miniters=1, desc="MVTec AD") as progress_bar:
+ urlretrieve(
+ url=f"{url}/{dataset_name}",
+ filename=zip_filename,
+ reporthook=progress_bar.update_to,
+ )
+ logger.info("Checking hash")
+ hash_check(zip_filename, "eefca59f2cede9c3fc5b6befbfec275e")
+
+ logger.info("Extracting the dataset.")
+ with tarfile.open(zip_filename) as tar_file:
+ tar_file.extractall(self.root)
+
+ logger.info("Cleaning the tar file")
+ (zip_filename).unlink()
+
+ def setup(self, stage: Optional[str] = None) -> None:
+ """Setup train, validation and test data.
+
+ Args:
+ stage: Optional[str]: Train/Val/Test stages. (Default value = None)
+
+ """
+ logger.info("Setting up train, validation, test and prediction datasets.")
+ if stage in (None, "fit"):
+ self.train_data = MVTecDataset(
+ root=self.root,
+ category=self.category,
+ pre_process=self.pre_process_train,
+ split="train",
+ task=self.task,
+ seed=self.seed,
+ create_validation_set=self.create_validation_set,
+ )
+
+ if self.create_validation_set:
+ self.val_data = MVTecDataset(
+ root=self.root,
+ category=self.category,
+ pre_process=self.pre_process_val,
+ split="val",
+ task=self.task,
+ seed=self.seed,
+ create_validation_set=self.create_validation_set,
+ )
+
+ self.test_data = MVTecLOCODataset(
+ root=self.root,
+ category=self.category,
+ pre_process=self.pre_process_val,
+ split="test",
+ task=self.task,
+ seed=self.seed,
+ create_validation_set=self.create_validation_set,
+ )
+
+ if stage == "predict":
+ self.inference_data = InferenceDataset(
+ path=self.root, image_size=self.image_size, transform_config=self.transform_config_val
+ )
+
+ def train_dataloader(self) -> TRAIN_DATALOADERS:
+ """Get train dataloader."""
+ return DataLoader(self.train_data, shuffle=True, batch_size=self.train_batch_size, num_workers=self.num_workers)
+
+ def val_dataloader(self) -> EVAL_DATALOADERS:
+ """Get validation dataloader."""
+ dataset = self.val_data if self.create_validation_set else self.test_data
+ return DataLoader(dataset=dataset, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers)
+
+ def test_dataloader(self) -> EVAL_DATALOADERS:
+ """Get test dataloader."""
+ return DataLoader(self.test_data, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers)
+
+ def predict_dataloader(self) -> EVAL_DATALOADERS:
+ """Get predict dataloader."""
+ return DataLoader(
+ self.inference_data, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers
+ )
From 92ad7e24f6b11b6a7298e27dfc78f6cf32e1b233 Mon Sep 17 00:00:00 2001
From: jpcbertoldo <24547377+jpcbertoldo@users.noreply.github.com>
Date: Sat, 3 Sep 2022 22:02:29 +0200
Subject: [PATCH 02/38] first version building the dataset
---
anomalib/data/mvtec_loco.py | 857 +++++++++++++++++++++++---------
anomalib/data/utils/__init__.py | 8 +-
anomalib/data/utils/download.py | 13 +
anomalib/data/utils/image.py | 5 +
4 files changed, 639 insertions(+), 244 deletions(-)
diff --git a/anomalib/data/mvtec_loco.py b/anomalib/data/mvtec_loco.py
index 940f2b4927..639ddc2f64 100644
--- a/anomalib/data/mvtec_loco.py
+++ b/anomalib/data/mvtec_loco.py
@@ -39,7 +39,7 @@
no training annotations -- although it is assumed that the images in training are indeed from the target class (i.e. no noise)
problem 1 (image-wise anomaly detection): “is there an anomaly in the image?”
problem 2 (pixel-wise anomaly detection or anomaly segmentation): “which pixels belong to the anomaly?”
-pixel-wise metric: Saturated Per-Region Overlap (sPRO)
+pixel-wise metric: Saturated Per-Region Overlap (sPRO)
structural anomaly pixel annotation policy
defects are confined to local regions
each pixel that introduces a visual structure that is not present in the anomaly-free images is anomalous
@@ -53,11 +53,11 @@
n_anomaly_type (n_structural, n_logical): 22 (5, 17)
logical constraints
contains 2 tangerines
-contains 1 nectarine
-the tangerines and the nectarine on the left
+contains 1 nectarine
+the tangerines and the nectarine on the left
cereals (C) and a mix of banana chips and almonds (B&A) on the right
-the ratio between C and B&A is fixed
-the relative position of C and B&A is fixed
+the ratio between C and B&A is fixed
+the relative position of C and B&A is fixed
examples of logical defects
too many banana chips and almonds
@@ -66,7 +66,7 @@
juice bottle
n_anomaly_type (n_structural, n_logical): 18 (7, 11)
logical constraints
-there is 1 bottle
+there is 1 bottle
the bottle is filled with a liquid and the fill level is always the same
the liquid is of 1 out of 3 colors (red, yellow, white-ish)
the bottle carries 2 labels
@@ -74,8 +74,8 @@
the first label displays an icon that determines the type of liquid (cherry, orange, banana)
cherry: red
orange: yellow
-banana: white-ish
-the second label is attached to the lower part of the bottle
+banana: white-ish
+the second label is attached to the lower part of the bottle
the second label contains the text “100% Juice”
examples of logical defects
(left) the icon does not match the type of juice
@@ -87,7 +87,7 @@
pushpins
n_anomaly_type (n_structural, n_logical): 8 (4, 4)
logical constraints
-each compartment contains 1 pushpin
+each compartment contains 1 pushpin
examples of logical defects
1 compartment has a missing pin
@@ -96,7 +96,7 @@
screw bag
n_anomaly_type (n_structural, n_logical): 20 (4, 16)
logical constraints
-the bag contains
+the bag contains
2 washers
2 nuts
1 long screw
@@ -120,7 +120,7 @@
examples of logical defects
(left) the two splicing connectors do not have the same number of clamps
(center) the color of the cable does not match the number of clamps
-(right) the cable terminates in different positions
+(right) the cable terminates in different positions
##################################################
@@ -128,9 +128,9 @@
the area in which the object could occur
the saturation threshold is chosen to be equal to the area of the missing object
the saturation threshold for an object is chosen from the lower end of the distribution of its (manually annotated) area
-example (image): pushpin
+example (image): pushpin
the missing pushpin can occur anywhere inside its compartment, therefore its entire area is annotated
-the saturation threshold is set to the size of a pushpin
+the saturation threshold is set to the size of a pushpin
##################################################
@@ -142,127 +142,38 @@
it is not clear which of the two cables represents the anomaly, therefore both are annotated
the saturation threshold is set to the area of one cable (i.e., half of the annotated region)
properties
-a method can obtain a perfect score even if it only marks one of the two cables as an anomaly
-a method that marks both is neither penalized nor (extra-)rewarded
+a method can obtain a perfect score even if it only marks one of the two cables as an anomaly
+a method that marks both is neither penalized nor (extra-)rewarded
##################################################
-other logical constraints
+other logical constraints
example (image, left): juice bottle
the bottle is filled with orange juice but carries the label of the cherry juice
both the orange juice and the label with the cherry are present in the training set, but the logical anomaly arises due to the erroneous combination of the two in the same image
either the area filled with juice or the cherry as could be considered anomalous, therefore the union of the two regions is annotated
the saturation threshold is set to the area of the cherry because the segmentation of the cherry is sufficient to solve the anomaly localization
-
"""
-
-"""
-category anomaly type gt_value saturation_definition saturation_parameter
-breakfast_box missing_almonds logical 255 relative_to_anomaly 1.0000000
-breakfast_box missing_bananas logical 254 relative_to_anomaly 1.0000000
-breakfast_box missing_toppings logical 253 relative_to_anomaly 1.0000000
-breakfast_box missing_cereals logical 252 relative_to_anomaly 1.0000000
-breakfast_box missing_cereals_and_toppings logical 251 relative_to_anomaly 1.0000000
-breakfast_box nectarines_2_tangerine_1 logical 250 relative_to_image 0.0488770
-breakfast_box nectarine_1_tangerine_1 logical 249 relative_to_image 0.0411621
-breakfast_box nectarines_0_tangerines_2 logical 248 relative_to_image 0.0488770
-breakfast_box nectarines_0_tangerines_3 logical 247 relative_to_image 0.0488770
-breakfast_box nectarines_3_tangerines_0 logical 246 relative_to_image 0.0977539
-breakfast_box nectarines_0_tangerine_1 logical 245 relative_to_image 0.0900391
-breakfast_box nectarines_0_tangerines_0 logical 244 relative_to_image 0.1312012
-breakfast_box nectarines_0_tangerines_4 logical 243 relative_to_image 0.0823242
-breakfast_box compartments_swapped logical 242 relative_to_anomaly 1.0000000
-breakfast_box overflow logical 241 relative_to_anomaly 1.0000000
-breakfast_box underflow logical 240 relative_to_anomaly 1.0000000
-breakfast_box wrong_ratio logical 239 relative_to_anomaly 1.0000000
-breakfast_box mixed_cereals structural 238 relative_to_anomaly 1.0000000
-breakfast_box fruit_damaged structural 237 relative_to_anomaly 1.0000000
-breakfast_box box_damaged structural 236 relative_to_anomaly 1.0000000
-breakfast_box toppings_crushed structural 235 relative_to_anomaly 1.0000000
-breakfast_box contamination structural 234 relative_to_anomaly 1.0000000
-juice_box missing_top_label logical 255 relative_to_image 0.0550000
-juice_box missing_bottom_label logical 254 relative_to_image 0.0255469
-juice_box swapped_labels logical 253 relative_to_image 0.1100000
-juice_box damaged_label structural 252 relative_to_anomaly 1.0000000
-juice_box rotated_label structural 251 relative_to_anomaly 1.0000000
-juice_box misplaced_label_top logical 250 relative_to_image 0.0550000
-juice_box misplaced_label_bottom logical 249 relative_to_image 0.0255469
-juice_box label_text_incomplete structural 248 relative_to_anomaly 1.0000000
-juice_box empty_bottle logical 247 relative_to_anomaly 1.0000000
-juice_box wrong_fill_level_too_much logical 246 relative_to_anomaly 1.0000000
-juice_box wrong_fill_level_not_enough logical 245 relative_to_anomaly 1.0000000
-juice_box misplaced_fruit_icon logical 244 relative_to_anomaly 1.0000000
-juice_box missing_fruit_icon logical 243 relative_to_anomaly 1.0000000
-juice_box unknown_fruit_icon structural 242 relative_to_anomaly 1.0000000
-juice_box incomplete_fruit_icon structural 241 relative_to_anomaly 1.0000000
-juice_box wrong_juice_type logical 240 relative_to_image 0.0035156
-juice_box juice_color structural 239 relative_to_anomaly 1.0000000
-juice_box contamination structural 238 relative_to_anomaly 1.0000000
-pushpins additional_1_pushpin logical 255 relative_to_image 0.0037059
-pushpins additional_2_pushpins logical 254 relative_to_image 0.0074118
-pushpins missing_pushpin logical 253 relative_to_image 0.0037059
-pushpins missing_separator logical 252 relative_to_anomaly 1.0000000
-pushpins front_bent structural 251 relative_to_anomaly 1.0000000
-pushpins broken structural 250 relative_to_anomaly 1.0000000
-pushpins color structural 249 relative_to_anomaly 1.0000000
-pushpins contamination structural 248 relative_to_anomaly 1.0000000
-screw_bag screw_too_long logical 255 relative_to_image 0.0051136
-screw_bag screw_too_shor logical 254 relative_to_image 0.0051136
-screw_bag screws_1_very_short logical 253 relative_to_anomaly 1.0000000
-screw_bag screws_2_very_short logical 252 relative_to_image 0.0102273
-screw_bag additional_1_long_screw logical 251 relative_to_image 0.0168182
-screw_bag additional_1_short_screw logical 250 relative_to_image 0.0117045
-screw_bag additional_1_nut_ logical 249 relative_to_image 0.0042614
-screw_bag additional_2_nuts_ logical 248 relative_to_image 0.0085227
-screw_bag additional_1_washer_ logical 247 relative_to_image 0.0031250
-screw_bag additional_2_washers_ logical 246 relative_to_image 0.0062500
-screw_bag missing_1_long_screw logical 245 relative_to_image 0.0168182
-screw_bag missing_1_short_screw logical 244 relative_to_image 0.0117045
-screw_bag missing_1_nut logical 243 relative_to_image 0.0042614
-screw_bag missing_2_nuts logical 242 relative_to_image 0.0085227
-screw_bag missing_1_washer logical 241 relative_to_image 0.0031250
-screw_bag missing_2_washers logical 240 relative_to_image 0.0062500
-screw_bag bag_broken structural 239 relative_to_anomaly 1.0000000
-screw_bag color structural 238 relative_to_anomaly 1.0000000
-screw_bag contamination structural 237 relative_to_anomaly 1.0000000
-screw_bag part_broken structural 236 relative_to_anomaly 1.0000000
-splicing_connectors wrong_connector_type_5_2 logical 255 relative_to_image 0.0464360
-splicing_connectors wrong_connector_type_5_3 logical 254 relative_to_image 0.0306574
-splicing_connectors wrong_connector_type_3_2 logical 253 relative_to_image 0.0152941
-splicing_connectors cable_too_short_t2 logical 252 relative_to_image 0.0368858
-splicing_connectors cable_too_short_t3 logical 251 relative_to_image 0.0526644
-splicing_connectors cable_too_short_t5 logical 250 relative_to_image 0.0830450
-splicing_connectors missing_connector logical 249 relative_to_anomaly 1.0000000
-splicing_connectors missing_connector_and_cable logical 248 relative_to_image 0.0716955
-splicing_connectors missing_cable logical 247 relative_to_image 0.0124567
-splicing_connectors extra_cable logical 246 relative_to_anomaly 0.5000000
-splicing_connectors cable_color logical 245 relative_to_image 0.0124567
-splicing_connectors broken_cable structural 244 relative_to_anomaly 1.0000000
-splicing_connectors cable_cut logical 243 relative_to_anomaly 1.0000000
-splicing_connectors cable_not_plugged structural 242 relative_to_anomaly 1.0000000
-splicing_connectors unknown_cable_color structural 241 relative_to_anomaly 1.0000000
-splicing_connectors wrong_cable_location logical 240 relative_to_image 0.0124567
-splicing_connectors flipped_connector structural 239 relative_to_anomaly 1.0000000
-splicing_connectors broken_connector structural 238 relative_to_anomaly 1.0000000
-splicing_connectors open_lever structural 237 relative_to_anomaly 1.0000000
-splicing_connectors color structural 236 relative_to_anomaly 1.0000000
-splicing_connectors contamination structural 235 relative_to_anomaly 1.0000000
-"""
+# TODO: clear module docstring
import logging
import tarfile
import warnings
from pathlib import Path
-from typing import Dict, Optional, Tuple, Union
+from posixpath import split
+from typing import Dict, List, Optional, Tuple, Union
+from unicodedata import category
from urllib.request import urlretrieve
import albumentations as A
import cv2
import numpy as np
import pandas as pd
+from numpy import ndarray
from pandas.core.frame import DataFrame
from pytorch_lightning.core.datamodule import LightningDataModule
+from pytorch_lightning.trainer.states import TrainerFn
from pytorch_lightning.utilities.cli import DATAMODULE_REGISTRY
from pytorch_lightning.utilities.types import EVAL_DATALOADERS, TRAIN_DATALOADERS
from torch import Tensor
@@ -271,64 +182,425 @@
from torchvision.datasets.folder import VisionDataset
from anomalib.data.inference import InferenceDataset
-from anomalib.data.utils import DownloadProgressBar, hash_check, read_image
-from anomalib.data.utils.split import (
- create_validation_set_from_test_set,
- split_normal_images_in_train_set,
-)
+from anomalib.data.utils import DownloadProgressBar, hash_check, read_image, read_mask
+from anomalib.data.utils.download import tar_extract_all
from anomalib.pre_processing import PreProcessor
+# TODO: open discussion about keeping pre-resized tensors in the dataset folder
+# TODO: create an issue in mvtec so the dataset will retain the information abou the anomaly type (label) so one can do per-label evaluation
+# TODO: document the notion of label and superlabel
+
logger = logging.getLogger(__name__)
-def make_mvtec_loco_dataset(
+TASK_SEGMENTATION = "segmentation"
+TASKS = TASK_SEGMENTATION
+
+SPLIT_TRAIN = "train"
+# "validation" instead of "val" is an explicit choice because this will match the name of the folder
+SPLIT_VALIDATION = "validation"
+SPLIT_TEST = "test"
+SPLITS = (SPLIT_TRAIN, SPLIT_VALIDATION, SPLIT_TEST)
+
+# each image is read upon demand
+IMREAD_STRATEGY_ONTHEFLY = "onthefly"
+# all images are pre-loaded into memory at initialization
+IMREAD_STRATEGY_PRELOAD = "preload"
+IMREAD_STRATEGIES = (IMREAD_STRATEGY_ONTHEFLY, IMREAD_STRATEGY_PRELOAD)
+# TODO make a strategy preload_tensor
+
+CATEGORY_BREAKFAST_BOX = "breakfast_box"
+CATEGORY_JUICE_BOTTLE = "juice_bottle"
+CATEGORY_PUSHPINS = "pushpins"
+CATEGORY_SCREW_BAG = "screw_bag"
+CATEGORY_SPLICING_CONNECTORS = "splicing_connectors"
+CATEGORIES: Tuple[str, ...] = (
+ CATEGORY_BREAKFAST_BOX,
+ CATEGORY_JUICE_BOTTLE,
+ CATEGORY_PUSHPINS,
+ CATEGORY_SCREW_BAG,
+ CATEGORY_SPLICING_CONNECTORS,
+)
+
+LABEL_NORMAL = 0
+LABEL_ANOMALOUS = 1
+
+SUPER_ANOTYPE_GOOD = "good"
+SUPER_ANOTYPE_LOGICAL = "logical_anomalies"
+SUPER_ANOTYPE_STRUCTURAL = "structural_anomalies"
+SUPER_ANOTYPES: Tuple[str, ...] = (SUPER_ANOTYPE_GOOD, SUPER_ANOTYPE_LOGICAL, SUPER_ANOTYPE_STRUCTURAL)
+
+ANOTYPE_GOOD = "good"
+
+ANOTYPES_BREAKFAST_BOX: Tuple[str, ...] = (
+ ANOTYPE_BB_GOOD := "good",
+ ANOTYPE_BB_COMPARTMENTS_SWAPPED := "compartments_swapped",
+ ANOTYPE_BB_MISSING_ALMONDS := "missing_almonds",
+ ANOTYPE_BB_MISSING_BANANAS := "missing_bananas",
+ ANOTYPE_BB_MISSING_CEREALS := "missing_cereals",
+ ANOTYPE_BB_MISSING_CEREALS_AND_TOPPINGS := "missing_cereals_and_toppings",
+ ANOTYPE_BB_MISSING_TOPPINGS := "missing_toppings",
+ ANOTYPE_BB_NECTARINE_1_TANGERINE_1 := "nectarine_1_tangerine_1",
+ ANOTYPE_BB_NECTARINES_0_TANGERINE_1 := "nectarines_0_tangerine_1",
+ ANOTYPE_BB_NECTARINES_0_TANGERINES_0 := "nectarines_0_tangerines_0",
+ ANOTYPE_BB_NECTARINES_0_TANGERINES_2 := "nectarines_0_tangerines_2",
+ ANOTYPE_BB_NECTARINES_0_TANGERINES_3 := "nectarines_0_tangerines_3",
+ ANOTYPE_BB_NECTARINES_0_TANGERINES_4 := "nectarines_0_tangerines_4",
+ ANOTYPE_BB_NECTARINES_2_TANGERINE_1 := "nectarines_2_tangerine_1",
+ ANOTYPE_BB_NECTARINES_3_TANGERINES_0 := "nectarines_3_tangerines_0",
+ ANOTYPE_BB_OVERFLOW := "overflow",
+ ANOTYPE_BB_UNDERFLOW := "underflow",
+ ANOTYPE_BB_WRONG_RATIO := "wrong_ratio",
+ ANOTYPE_BB_BOX_DAMAGED := "box_damaged",
+ ANOTYPE_BB_CONTAMINATION := "contamination",
+ ANOTYPE_BB_FRUIT_DAMAGED := "fruit_damaged",
+ ANOTYPE_BB_MIXED_CEREALS := "mixed_cereals",
+ ANOTYPE_BB_TOPPINGS_CRUSHED := "toppings_crushed",
+)
+
+ANOTYPES_JUICE_BOTTLE: Tuple[str, ...] = (
+ ANOTYPE_JB_GOOD := "good",
+ ANOTYPE_JB_EMPTY_BOTTLE := "empty_bottle",
+ ANOTYPE_JB_MISPLACED_FRUIT_ICON := "misplaced_fruit_icon",
+ ANOTYPE_JB_MISPLACED_LABEL_BOTTOM := "misplaced_label_bottom",
+ ANOTYPE_JB_MISPLACED_LABEL_TOP := "misplaced_label_top",
+ ANOTYPE_JB_MISSING_BOTTOM_LABEL := "missing_bottom_label",
+ ANOTYPE_JB_MISSING_FRUIT_ICON := "missing_fruit_icon",
+ ANOTYPE_JB_MISSING_TOP_LABEL := "missing_top_label",
+ ANOTYPE_JB_SWAPPED_LABELS := "swapped_labels",
+ ANOTYPE_JB_WRONG_FILL_LEVEL_NOT_ENOUGH := "wrong_fill_level_not_enough",
+ ANOTYPE_JB_WRONG_FILL_LEVEL_TOO_MUCH := "wrong_fill_level_too_much",
+ ANOTYPE_JB_WRONG_JUICE_TYPE := "wrong_juice_type",
+ ANOTYPE_JB_CONTAMINATION := "contamination",
+ ANOTYPE_JB_DAMAGED_LABEL := "damaged_label",
+ ANOTYPE_JB_INCOMPLETE_FRUIT_ICON := "incomplete_fruit_icon",
+ ANOTYPE_JB_JUICE_COLOR := "juice_color",
+ ANOTYPE_JB_LABEL_TEXT_INCOMPLETE := "label_text_incomplete",
+ ANOTYPE_JB_ROTATED_LABEL := "rotated_label",
+ ANOTYPE_JB_UNKNOWN_FRUIT_ICON := "unknown_fruit_icon",
+)
+
+ANOTYPES_PUSHPINS: Tuple[str, ...] = (
+ ANOTYPE_P_GOOD := "good",
+ ANOTYPE_P_ADDITIONAL_1_PUSHPIN := "additional_1_pushpin",
+ ANOTYPE_P_ADDITIONAL_2_PUSHPINS := "additional_2_pushpins",
+ ANOTYPE_P_MISSING_PUSHPIN := "missing_pushpin",
+ ANOTYPE_P_MISSING_SEPARATOR := "missing_separator",
+ ANOTYPE_P_BROKEN := "broken",
+ ANOTYPE_P_COLOR := "color",
+ ANOTYPE_P_CONTAMINATION := "contamination",
+ ANOTYPE_P_FRONT_BENT := "front_bent",
+)
+
+ANOTYPES_SCREW_BAG: Tuple[str, ...] = (
+ ANOTYPE_SB_GOOD := "good",
+ ANOTYPE_SB_ADDITIONAL_1_LONG_SCREW := "additional_1_long_screw",
+ ANOTYPE_SB_ADDITIONAL_1_NUT_ := "additional_1_nut_",
+ ANOTYPE_SB_ADDITIONAL_1_SHORT_SCREW := "additional_1_short_screw",
+ ANOTYPE_SB_ADDITIONAL_1_WASHER_ := "additional_1_washer_",
+ ANOTYPE_SB_ADDITIONAL_2_NUTS_ := "additional_2_nuts_",
+ ANOTYPE_SB_ADDITIONAL_2_WASHERS_ := "additional_2_washers_",
+ ANOTYPE_SB_MISSING_1_LONG_SCREW := "missing_1_long_screw",
+ ANOTYPE_SB_MISSING_1_NUT := "missing_1_nut",
+ ANOTYPE_SB_MISSING_1_SHORT_SCREW := "missing_1_short_screw",
+ ANOTYPE_SB_MISSING_1_WASHER := "missing_1_washer",
+ ANOTYPE_SB_MISSING_2_NUTS := "missing_2_nuts",
+ ANOTYPE_SB_MISSING_2_WASHERS := "missing_2_washers",
+ ANOTYPE_SB_SCREW_TOO_LONG := "screw_too_long",
+ ANOTYPE_SB_SCREW_TOO_SHOR := "screw_too_shor",
+ ANOTYPE_SB_SCREWS_1_VERY_SHORT := "screws_1_very_short",
+ ANOTYPE_SB_SCREWS_2_VERY_SHORT := "screws_2_very_short",
+ ANOTYPE_SB_BAG_BROKEN := "bag_broken",
+ ANOTYPE_SB_COLOR := "color",
+ ANOTYPE_SB_CONTAMINATION := "contamination",
+ ANOTYPE_SB_PART_BROKEN := "part_broken",
+)
+
+ANOTYPES_SPLICING_CONNECTORS: Tuple[str, ...] = (
+ ANOTYPE_SC_GOOD := "good",
+ ANOTYPE_SC_CABLE_COLOR := "cable_color",
+ ANOTYPE_SC_CABLE_CUT := "cable_cut",
+ ANOTYPE_SC_CABLE_TOO_SHORT_T2 := "cable_too_short_t2",
+ ANOTYPE_SC_CABLE_TOO_SHORT_T3 := "cable_too_short_t3",
+ ANOTYPE_SC_CABLE_TOO_SHORT_T5 := "cable_too_short_t5",
+ ANOTYPE_SC_EXTRA_CABLE := "extra_cable",
+ ANOTYPE_SC_MISSING_CABLE := "missing_cable",
+ ANOTYPE_SC_MISSING_CONNECTOR := "missing_connector",
+ ANOTYPE_SC_MISSING_CONNECTOR_AND_CABLE := "missing_connector_and_cable",
+ ANOTYPE_SC_WRONG_CABLE_LOCATION := "wrong_cable_location",
+ ANOTYPE_SC_WRONG_CONNECTOR_TYPE_3_2 := "wrong_connector_type_3_2",
+ ANOTYPE_SC_WRONG_CONNECTOR_TYPE_5_2 := "wrong_connector_type_5_2",
+ ANOTYPE_SC_WRONG_CONNECTOR_TYPE_5_3 := "wrong_connector_type_5_3",
+ ANOTYPE_SC_BROKEN_CABLE := "broken_cable",
+ ANOTYPE_SC_BROKEN_CONNECTOR := "broken_connector",
+ ANOTYPE_SC_CABLE_NOT_PLUGGED := "cable_not_plugged",
+ ANOTYPE_SC_COLOR := "color",
+ ANOTYPE_SC_CONTAMINATION := "contamination",
+ ANOTYPE_SC_FLIPPED_CONNECTOR := "flipped_connector",
+ ANOTYPE_SC_OPEN_LEVER := "open_lever",
+ ANOTYPE_SC_UNKNOWN_CABLE_COLOR := "unknown_cable_color",
+)
+
+# this is given at the paper, each anomaly type (label) has a different gtvalue in the mask
+# source: Beyond Dents and Scratches: Logical Constraints in Unsupervised Anomaly Detection and Localization (Bergmann, P. et al, 2022).
+_MAP_ANOTYPE_2_GTVALUE: Dict[Tuple[str, str, str], int] = {
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_COMPARTMENTS_SWAPPED): 242,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_MISSING_ALMONDS): 255,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_MISSING_BANANAS): 254,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_MISSING_CEREALS): 252,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_MISSING_CEREALS_AND_TOPPINGS): 251,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_MISSING_TOPPINGS): 253,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_NECTARINE_1_TANGERINE_1): 249,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_NECTARINES_0_TANGERINE_1): 245,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_NECTARINES_0_TANGERINES_0): 244,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_NECTARINES_0_TANGERINES_2): 248,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_NECTARINES_0_TANGERINES_3): 247,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_NECTARINES_0_TANGERINES_4): 243,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_NECTARINES_2_TANGERINE_1): 250,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_NECTARINES_3_TANGERINES_0): 246,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_OVERFLOW): 241,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_UNDERFLOW): 240,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_WRONG_RATIO): 239,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_BB_BOX_DAMAGED): 236,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_BB_CONTAMINATION): 234,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_BB_FRUIT_DAMAGED): 237,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_BB_MIXED_CEREALS): 238,
+ (CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_BB_TOPPINGS_CRUSHED): 235,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_EMPTY_BOTTLE): 247,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_MISPLACED_FRUIT_ICON): 244,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_MISPLACED_LABEL_BOTTOM): 249,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_MISPLACED_LABEL_TOP): 250,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_MISSING_BOTTOM_LABEL): 254,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_MISSING_FRUIT_ICON): 243,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_MISSING_TOP_LABEL): 255,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_SWAPPED_LABELS): 253,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_WRONG_FILL_LEVEL_NOT_ENOUGH): 245,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_WRONG_FILL_LEVEL_TOO_MUCH): 246,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_LOGICAL, ANOTYPE_JB_WRONG_JUICE_TYPE): 240,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_JB_CONTAMINATION): 238,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_JB_DAMAGED_LABEL): 252,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_JB_INCOMPLETE_FRUIT_ICON): 241,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_JB_JUICE_COLOR): 239,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_JB_LABEL_TEXT_INCOMPLETE): 248,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_JB_ROTATED_LABEL): 251,
+ (CATEGORY_JUICE_BOTTLE, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_JB_UNKNOWN_FRUIT_ICON): 242,
+ (CATEGORY_PUSHPINS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_P_ADDITIONAL_1_PUSHPIN): 255,
+ (CATEGORY_PUSHPINS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_P_ADDITIONAL_2_PUSHPINS): 254,
+ (CATEGORY_PUSHPINS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_P_MISSING_PUSHPIN): 253,
+ (CATEGORY_PUSHPINS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_P_MISSING_SEPARATOR): 252,
+ (CATEGORY_PUSHPINS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_P_BROKEN): 250,
+ (CATEGORY_PUSHPINS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_P_COLOR): 249,
+ (CATEGORY_PUSHPINS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_P_CONTAMINATION): 248,
+ (CATEGORY_PUSHPINS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_P_FRONT_BENT): 251,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_ADDITIONAL_1_LONG_SCREW): 251,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_ADDITIONAL_1_NUT_): 249,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_ADDITIONAL_1_SHORT_SCREW): 250,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_ADDITIONAL_1_WASHER_): 247,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_ADDITIONAL_2_NUTS_): 248,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_ADDITIONAL_2_WASHERS_): 246,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_MISSING_1_LONG_SCREW): 245,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_MISSING_1_NUT): 243,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_MISSING_1_SHORT_SCREW): 244,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_MISSING_1_WASHER): 241,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_MISSING_2_NUTS): 242,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_MISSING_2_WASHERS): 240,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_SCREW_TOO_LONG): 255,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_SCREW_TOO_SHOR): 254,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_SCREWS_1_VERY_SHORT): 253,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SB_SCREWS_2_VERY_SHORT): 252,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SB_BAG_BROKEN): 239,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SB_COLOR): 238,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SB_CONTAMINATION): 237,
+ (CATEGORY_SCREW_BAG, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SB_PART_BROKEN): 236,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_CABLE_COLOR): 245,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_CABLE_CUT): 243,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_CABLE_TOO_SHORT_T2): 252,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_CABLE_TOO_SHORT_T3): 251,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_CABLE_TOO_SHORT_T5): 250,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_EXTRA_CABLE): 246,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_MISSING_CABLE): 247,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_MISSING_CONNECTOR): 249,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_MISSING_CONNECTOR_AND_CABLE): 248,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_WRONG_CABLE_LOCATION): 240,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_WRONG_CONNECTOR_TYPE_3_2): 253,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_WRONG_CONNECTOR_TYPE_5_2): 255,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_LOGICAL, ANOTYPE_SC_WRONG_CONNECTOR_TYPE_5_3): 254,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SC_BROKEN_CABLE): 244,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SC_BROKEN_CONNECTOR): 238,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SC_CABLE_NOT_PLUGGED): 242,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SC_COLOR): 236,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SC_CONTAMINATION): 235,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SC_FLIPPED_CONNECTOR): 239,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SC_OPEN_LEVER): 237,
+ (CATEGORY_SPLICING_CONNECTORS, SUPER_ANOTYPE_STRUCTURAL, ANOTYPE_SC_UNKNOWN_CABLE_COLOR): 241,
+}
+
+# map: (category, gtvalue) -> (superlabel, label)
+_MAP_GTVALUE_2_ANOTYPE: Dict[Tuple[str, int], Tuple[str, str]] = {
+ (category, gtvalue): (super_anotype, anotype)
+ for (category, super_anotype, anotype), gtvalue in _MAP_ANOTYPE_2_GTVALUE.items()
+}
+
+# expected number of images in each category split so that we can check if the dataset is complete
+# source: Beyond Dents and Scratches: Logical Constraints in Unsupervised Anomaly Detection and Localization (Bergmann, P. et al, 2022).
+_EXPECTED_NSAMPLES: Dict[Tuple[str, str], int] = {
+ (CATEGORY_BREAKFAST_BOX, SPLIT_TRAIN): 351,
+ (CATEGORY_BREAKFAST_BOX, SPLIT_VALIDATION): 62,
+ (CATEGORY_BREAKFAST_BOX, SPLIT_TEST): 275,
+ (CATEGORY_JUICE_BOTTLE, SPLIT_TRAIN): 335,
+ (CATEGORY_JUICE_BOTTLE, SPLIT_VALIDATION): 54,
+ (CATEGORY_JUICE_BOTTLE, SPLIT_TEST): 330,
+ (CATEGORY_PUSHPINS, SPLIT_TRAIN): 372,
+ (CATEGORY_PUSHPINS, SPLIT_VALIDATION): 69,
+ (CATEGORY_PUSHPINS, SPLIT_TEST): 310,
+ (CATEGORY_SCREW_BAG, SPLIT_TRAIN): 360,
+ (CATEGORY_SCREW_BAG, SPLIT_VALIDATION): 60,
+ (CATEGORY_SCREW_BAG, SPLIT_TEST): 341,
+ # these two below were wrong in the paper
+ # TODO send a correction to the authors
+ # (CATEGORY_SPLICING_CONNECTORS, SPLIT_TRAIN): 354,
+ # (CATEGORY_SPLICING_CONNECTORS, SPLIT_VALIDATION): 59,
+ (CATEGORY_SPLICING_CONNECTORS, SPLIT_TRAIN): 360,
+ (CATEGORY_SPLICING_CONNECTORS, SPLIT_VALIDATION): 60,
+ (CATEGORY_SPLICING_CONNECTORS, SPLIT_TEST): 312,
+}
+
+
+def _binarize_mask_float(mask: np.ndarray) -> np.ndarray:
+ """
+ the masks use different gtvalue values for the different anomaly types so the > 0 is making it binary
+ this operation is very simple but it is in a function to make sure its standard because it is used in different places
+ e.g. preloading while building the dataset and on the fly while training
+ """
+ return (mask > 0).astype(float)
+
+
+def _make_dataset(
path: Path,
split: Optional[str] = None,
- split_ratio: float = 0.1,
- seed: Optional[int] = None,
- create_validation_set: bool = False,
+ imread_strategy: str = IMREAD_STRATEGY_PRELOAD,
) -> DataFrame:
- samples_list = [(str(path),) + filename.parts[-3:] for filename in path.glob("**/*.png")]
- if len(samples_list) == 0:
- raise RuntimeError(f"Found 0 images in {path}")
-
- samples = pd.DataFrame(samples_list, columns=["path", "split", "label", "image_path"])
- samples = samples[samples.split != "ground_truth"]
-
- # Create mask_path column
- samples["mask_path"] = (
- samples.path
- + "/ground_truth/"
- + samples.label
- + "/"
- + samples.image_path.str.rstrip("png").str.rstrip(".")
- + "_mask.png"
- )
-
- # Modify image_path column by converting to absolute path
- samples["image_path"] = samples.path + "/" + samples.split + "/" + samples.label + "/" + samples.image_path
-
- # Split the normal images in training set if test set doesn't
- # contain any normal images. This is needed because AUC score
- # cannot be computed based on 1-class
- if sum((samples.split == "test") & (samples.label == "good")) == 0:
- samples = split_normal_images_in_train_set(samples, split_ratio, seed)
-
- # Good images don't have mask
- samples.loc[(samples.split == "test") & (samples.label == "good"), "mask_path"] = ""
-
- # Create label index for normal (0) and anomalous (1) images.
- samples.loc[(samples.label == "good"), "label_index"] = 0
- samples.loc[(samples.label != "good"), "label_index"] = 1
- samples.label_index = samples.label_index.astype(int)
-
- if create_validation_set:
- samples = create_validation_set_from_test_set(samples, seed=seed)
-
- # Get the data frame for the split.
- if split is not None and split in ["train", "val", "test"]:
- samples = samples[samples.split == split]
- samples = samples.reset_index(drop=True)
+ # todo create optional to get a subset of anomlies in the test
+
+ assert split is None or split in SPLITS, f"Invalid split: {split}"
+ assert imread_strategy in IMREAD_STRATEGIES, f"Invalid imread strategy: {imread_strategy}"
+
+ category = path.resolve().name
+ assert category in CATEGORIES, f"Invalid path '{path}'. The directory ('{category}') must be one of {CATEGORIES}"
+
+ if split is None:
+ return pd.concat([_make_dataset(path, split_, imread_strategy) for split_ in SPLITS], axis=0)
+
+ logger.info(f"Creating MVTec LOCO AD dataset for category '{category}' split '{split}'")
+
+ """
+ structure of the files in the dataset ("/" is 'path')
+
+ images: /{split}/{super_anotype}/{image_index}.png
+
+ /train/good/000.png
+ /train/good/...
+ /train/good/350.png
+
+ /validation/good/...
+
+ /test/good/...
+ /test/logical_anomalies/...
+ /test/structural_anomalies/...
+
+ masks: /ground_truth/{super_anotype}/{image_index}/000.png
+
+ /ground_truth/logical_anomalies/000/000.png
+ /ground_truth/logical_anomalies/.../000.png
+ /ground_truth/logical_anomalies/079/000.png
+
+ /ground_truth/structural_anomalies/.../000.png
+ ...
+ """
+
+ # these values look like "(train|validation|test)/(good|logical_anomalies|structural_anomalies)/(000|...|n).png"
+ # where (a|b) means either a or b
+ samples_paths = sorted(path.glob(f"{split}/**/*.png"))
+ expected_nsamples = _EXPECTED_NSAMPLES[(category, split)]
+
+ if len(samples_paths) != expected_nsamples:
+ warnings.warn(
+ f"Expected {expected_nsamples} samples for split '{split}' in category '{category}' but found {len(samples_paths)}."
+ "Is the dataset corrupted?"
+ )
+
+ def build_record(sample_path: Path):
+
+ ret: Dict[str, Union[Path, None, str, int]] = {
+ "image_path": sample_path,
+ **dict(zip(("split", "super_anotype", "image_filename"), sample_path.parts[-3:])),
+ }
+
+ super_anotype: str = ret["super_anotype"] # type: ignore
+
+ if super_anotype == SUPER_ANOTYPE_GOOD:
+ ret.update(
+ {
+ "mask_path": None,
+ "label": LABEL_NORMAL,
+ "super_anotype": SUPER_ANOTYPE_GOOD,
+ "anotype": ANOTYPE_GOOD,
+ }
+ )
+ return ret
+
+ elif super_anotype in (SUPER_ANOTYPE_LOGICAL, SUPER_ANOTYPE_STRUCTURAL):
+
+ mask_path: Path = path / "ground_truth" / super_anotype / sample_path.stem / "000.png"
+
+ assert mask_path.exists(), f"Mask file '{mask_path}' does not exist. Is the dataset corrupted?"
+
+ # mask images are supposed to have only two values: 0 and GTVALUE_ANOMALY
+ # GTVALUE_ANOMALY \in {234, ..., 255} and is given in the paper, encoded in the mapping below
+ # TODO create an issue to cache this info so the mask is not read here
+ gtvalue = read_mask(mask_path).astype(int).max()
+ _, anotype = _MAP_GTVALUE_2_ANOTYPE[(category, gtvalue)]
+
+ ret.update(
+ {
+ "mask_path": mask_path,
+ "gtvalue": gtvalue,
+ "label": LABEL_ANOMALOUS,
+ "super_anotype": super_anotype,
+ "anotype": anotype,
+ }
+ )
+
+ return ret
+
+ else:
+ # there should only be the folders "good", "logical_anomalies" and "structural_anomalies"
+ raise RuntimeError(
+ f"Something wrong in the dataset folder. Unknown folder {super_anotype}, path={sample_path}"
+ )
+
+ samples = pd.DataFrame.from_records([build_record(sp) for sp in samples_paths])
+
+ if imread_strategy == IMREAD_STRATEGY_PRELOAD:
+
+ # warnings.warn(
+ # "Preloading images into memory. "
+ # "If your dataset is too large, consider using another imread_strategy instead.",
+ # stacklevel=3
+ # )
+
+ logger.debug(f"Preloading images into memory")
+ samples["image"] = samples.image_path.map(read_image)
+
+ logger.debug(f"Preloading masks into memory")
+
+ # this is used to select the rows in the dataframe
+ has_mask = ~samples.mask_path.isnull()
+
+ samples.loc[has_mask, "mask"] = samples.loc[has_mask, "mask_path"].map(
+ lambda x: _binarize_mask_float(read_mask(x))
+ )
+ samples.loc[~has_mask, "mask"] = None
return samples
@@ -342,40 +614,71 @@ def __init__(
category: str,
pre_process: PreProcessor,
split: str,
- task: str = "segmentation",
- seed: Optional[int] = None,
- create_validation_set: bool = False,
+ task: str = TASK_SEGMENTATION,
+ # create_validation_set: bool = False,
+ imread_strategy: str = IMREAD_STRATEGY_PRELOAD,
) -> None:
super().__init__(root)
- if seed is None:
- warnings.warn(
- "seed is None."
- " When seed is not set, images from the normal directory are split between training and test dir."
- " This will lead to inconsistency between runs."
- )
+ assert split in SPLITS, f"Split '{split}' is not supported. Supported splits are {SPLITS}"
+ assert task in TASKS, f"Task '{task}' is not supported. Supported tasks are {TASKS}"
+ assert (
+ imread_strategy in IMREAD_STRATEGIES
+ ), f"Imread strategy '{imread_strategy}' is not supported. Supported imread strategies are {IMREAD_STRATEGIES}"
self.root = Path(root) if isinstance(root, str) else root
self.category: str = category
self.split = split
self.task = task
-
self.pre_process = pre_process
+ self.imread_strategy = imread_strategy
- self.samples = make_mvtec_loco_dataset(
- path=self.root / category,
+ self.samples = _make_dataset(
+ path=self.dataset_path,
split=self.split,
- seed=seed,
- create_validation_set=create_validation_set,
+ imread_strategy=self.imread_strategy,
)
+ @property
+ def dataset_path(self) -> Path:
+ """Path to the dataset folder."""
+ return self.root / self.category
+
def __len__(self) -> int:
"""Get length of the dataset."""
return len(self.samples)
+ def _get_image(self, index: int) -> ndarray:
+ """Get image at index."""
+
+ if self.imread_strategy == IMREAD_STRATEGY_PRELOAD:
+ return self.samples.image[index]
+
+ elif self.imread_strategy == IMREAD_STRATEGY_ONTHEFLY:
+ return read_image(self.samples.image_path[index])
+
+ else:
+ raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
+
+ def _get_mask(self, index: int) -> ndarray:
+ """Get mask at index."""
+
+ if self.imread_strategy == IMREAD_STRATEGY_PRELOAD:
+ return self.samples.mask[index]
+
+ elif self.imread_strategy == IMREAD_STRATEGY_ONTHEFLY:
+ return _binarize_mask_float(read_mask(self.samples.mask_path[index]))
+
+ else:
+ raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
+
def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
"""Get dataset item for the index ``index``.
+ TODO: include the label string
+ TODO: include the label anomaly type (strutural, logical)
+ TODO: here? probably better to separate it... return the sPRO saturation value
+
Args:
index (int): Index to get the item.
@@ -385,33 +688,41 @@ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
"""
item: Dict[str, Union[str, Tensor]] = {}
- image_path = self.samples.image_path[index]
- image = read_image(image_path)
-
+ image = self._get_image(index)
pre_processed = self.pre_process(image=image)
- item = {"image": pre_processed["image"]}
-
- if self.split in ["val", "test"]:
- label_index = self.samples.label_index[index]
-
- item["image_path"] = image_path
- item["label"] = label_index
-
- if self.task == "segmentation":
- mask_path = self.samples.mask_path[index]
+ item = {
+ "image": pre_processed["image"],
+ }
+
+ if self.split not in (SPLIT_VALIDATION, SPLIT_TEST):
+ return item
+
+ item.update(
+ {
+ "label": self.samples.label[index],
+ "image_path": self.samples.image_path[index],
+ }
+ )
- # Only Anomalous (1) images has masks in MVTec AD dataset.
- # Therefore, create empty mask for Normal (0) images.
- if label_index == 0:
- mask = np.zeros(shape=image.shape[:2])
- else:
- mask = cv2.imread(mask_path, flags=0) / 255.0
+ if self.task != TASK_SEGMENTATION:
+ return item
- pre_processed = self.pre_process(image=image, mask=mask)
+ # Only Anomalous (1) images has masks in MVTec LOCO AD dataset.
+ # Therefore, create empty mask for Normal (0) images.
+ if self.samples.label[index] == LABEL_NORMAL:
+ mask = np.zeros(shape=image.shape[:2]) # shape: (H, W, C)
- item["mask_path"] = mask_path
- item["image"] = pre_processed["image"]
- item["mask"] = pre_processed["mask"]
+ else:
+ mask = self._get_mask(index)
+
+ # TODO: ask how this works, does the transform re-apply the last call when mask is not None?
+ pre_processed = self.pre_process(image=image, mask=mask)
+ item.update(
+ {
+ "mask_path": self.samples.mask_path[index],
+ "mask": pre_processed["mask"],
+ }
+ )
return item
@@ -420,33 +731,37 @@ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
class MVTecLOCO(LightningDataModule):
"""MVTec LOCO AD Lightning Data Module."""
+ # todo correct inconsistency: `transform_config_*val*` used for val and test set, but `*test*_batch_size` used for val and set
+
def __init__(
self,
root: str,
category: str,
- # TODO: Remove default values. IAAALD-211
+ # TODO: add a parameter to specify the anomaly types and (more specifically) the anomaly classes -- "label"
image_size: Optional[Union[int, Tuple[int, int]]] = None,
train_batch_size: int = 32,
test_batch_size: int = 32,
num_workers: int = 8,
- task: str = "segmentation",
+ task: str = TASK_SEGMENTATION,
transform_config_train: Optional[Union[str, A.Compose]] = None,
transform_config_val: Optional[Union[str, A.Compose]] = None,
seed: Optional[int] = None,
- create_validation_set: bool = False,
+ imread_strategy: str = IMREAD_STRATEGY_PRELOAD,
) -> None:
super().__init__()
+ # todo? add input assertions/validations
+ assert task in TASKS, f"Task '{task}' is not supported. Supported tasks are {TASKS}"
+ assert (
+ imread_strategy in IMREAD_STRATEGIES
+ ), f"Imread strategy '{imread_strategy}' is not supported. Supported imread strategies are {IMREAD_STRATEGIES}"
+
self.root = root if isinstance(root, Path) else Path(root)
self.category = category
- self.dataset_path = self.root / self.category
self.transform_config_train = transform_config_train
self.transform_config_val = transform_config_val
self.image_size = image_size
- if self.transform_config_train is not None and self.transform_config_val is None:
- self.transform_config_val = self.transform_config_train
-
self.pre_process_train = PreProcessor(config=self.transform_config_train, image_size=self.image_size)
self.pre_process_val = PreProcessor(config=self.transform_config_val, image_size=self.image_size)
@@ -454,84 +769,90 @@ def __init__(
self.test_batch_size = test_batch_size
self.num_workers = num_workers
- self.create_validation_set = create_validation_set
self.task = task
self.seed = seed
+ self.imread_strategy = imread_strategy
self.train_data: Dataset
self.test_data: Dataset
- if create_validation_set:
- self.val_data: Dataset
+ self.val_data: Dataset
self.inference_data: Dataset
+ @property
+ def dataset_path(self) -> Path:
+ return self.root / self.category
+
def prepare_data(self) -> None:
"""Download the dataset if not available."""
- if (self.root / self.category).is_dir():
+
+ if self.dataset_path.is_dir():
logger.info("Found the dataset.")
+
else:
self.root.mkdir(parents=True, exist_ok=True)
- logger.info("Downloading the Mvtec AD dataset.")
- url = "https://www.mydrive.ch/shares/38536/3830184030e49fe74747669442f0f282/download/420938113-1629952094"
- dataset_name = "mvtec_anomaly_detection.tar.xz"
+ logger.info("Downloading the Mvtec LOCO AD dataset.")
+ URL_MVTEC_LOCO_TARGZ = "https://www.mydrive.ch/shares/48237/1b9106ccdfbb09a0c414bd49fe44a14a/download/430647091-1646842701/mvtec_loco_anomaly_detection.tar.xz"
+ dataset_name = "mvtec_loco_anomaly_detection.tar.xz"
zip_filename = self.root / dataset_name
- with DownloadProgressBar(unit="B", unit_scale=True, miniters=1, desc="MVTec AD") as progress_bar:
+ with DownloadProgressBar(unit="B", unit_scale=True, miniters=1, desc="MVTec LOCO download") as progress_bar:
urlretrieve(
- url=f"{url}/{dataset_name}",
+ url=URL_MVTEC_LOCO_TARGZ,
filename=zip_filename,
reporthook=progress_bar.update_to,
)
+
logger.info("Checking hash")
- hash_check(zip_filename, "eefca59f2cede9c3fc5b6befbfec275e")
+ MD5HASH_MVTEC_LOCO = "d40f092ac6f88433f609583c4a05f56f"
+ hash_check(zip_filename, MD5HASH_MVTEC_LOCO)
- logger.info("Extracting the dataset.")
- with tarfile.open(zip_filename) as tar_file:
- tar_file.extractall(self.root)
+ logger.info(f"Extracting the dataset.")
+ logger.debug(f"Extracting to {self.root}")
+ tar_extract_all(zip_filename, self.root)
logger.info("Cleaning the tar file")
- (zip_filename).unlink()
+ zip_filename.unlink()
def setup(self, stage: Optional[str] = None) -> None:
"""Setup train, validation and test data.
Args:
- stage: Optional[str]: Train/Val/Test stages. (Default value = None)
+ stage: Optional[str]: fit/validate/test/predict stages. (Default value = None = fit)
"""
- logger.info("Setting up train, validation, test and prediction datasets.")
- if stage in (None, "fit"):
- self.train_data = MVTecDataset(
+ logger.info("Setting up {} dataset." % str(stage or TrainerFn.FITTING))
+
+ if stage in (None, TrainerFn.FITTING):
+ self.train_data = MVTecLOCODataset(
root=self.root,
category=self.category,
+ split=SPLIT_TRAIN,
pre_process=self.pre_process_train,
- split="train",
task=self.task,
- seed=self.seed,
- create_validation_set=self.create_validation_set,
+ imread_strategy=self.imread_strategy,
)
- if self.create_validation_set:
- self.val_data = MVTecDataset(
+ if stage == TrainerFn.VALIDATING:
+ self.val_data = MVTecLOCODataset(
root=self.root,
category=self.category,
pre_process=self.pre_process_val,
- split="val",
+ split=SPLIT_VALIDATION,
task=self.task,
- seed=self.seed,
- create_validation_set=self.create_validation_set,
+ imread_strategy=self.imread_strategy,
)
- self.test_data = MVTecLOCODataset(
- root=self.root,
- category=self.category,
- pre_process=self.pre_process_val,
- split="test",
- task=self.task,
- seed=self.seed,
- create_validation_set=self.create_validation_set,
- )
+ if stage == TrainerFn.TESTING:
+ self.test_data = MVTecLOCODataset(
+ root=self.root,
+ category=self.category,
+ pre_process=self.pre_process_val,
+ split=SPLIT_TEST,
+ task=self.task,
+ imread_strategy=self.imread_strategy,
+ )
- if stage == "predict":
+ if stage == TrainerFn.PREDICTING:
self.inference_data = InferenceDataset(
path=self.root, image_size=self.image_size, transform_config=self.transform_config_val
)
@@ -542,8 +863,7 @@ def train_dataloader(self) -> TRAIN_DATALOADERS:
def val_dataloader(self) -> EVAL_DATALOADERS:
"""Get validation dataloader."""
- dataset = self.val_data if self.create_validation_set else self.test_data
- return DataLoader(dataset=dataset, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers)
+ return DataLoader(self.val_data, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers)
def test_dataloader(self) -> EVAL_DATALOADERS:
"""Get test dataloader."""
@@ -554,3 +874,54 @@ def predict_dataloader(self) -> EVAL_DATALOADERS:
return DataLoader(
self.inference_data, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers
)
+
+
+# TODO remove me
+# debug _make_dataset in main
+if __name__ == "__main__":
+ import itertools
+
+ for cat in CATEGORIES:
+ _make_dataset(Path(f"/home/jcasagrandebertoldo/Downloads/loco/{cat}"))
+
+ for cat, split_ in itertools.product(CATEGORIES, SPLITS):
+ pass
+ # _make_dataset(Path(f"/home/jcasagrandebertoldo/Downloads/loco/{cat}"), split_)
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # test instantiate of the two classes
+ # then test with script
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
+ # next
diff --git a/anomalib/data/utils/__init__.py b/anomalib/data/utils/__init__.py
index 53cf04e4fd..e8ce1cfe28 100644
--- a/anomalib/data/utils/__init__.py
+++ b/anomalib/data/utils/__init__.py
@@ -5,7 +5,12 @@
from .download import DownloadProgressBar, hash_check
from .generators import random_2d_perlin
-from .image import generate_output_image_filename, get_image_filenames, read_image
+from .image import (
+ generate_output_image_filename,
+ get_image_filenames,
+ read_image,
+ read_mask,
+)
__all__ = [
"generate_output_image_filename",
@@ -13,5 +18,6 @@
"hash_check",
"random_2d_perlin",
"read_image",
+ "read_mask",
"DownloadProgressBar",
]
diff --git a/anomalib/data/utils/download.py b/anomalib/data/utils/download.py
index eefe001f44..b16a56834a 100644
--- a/anomalib/data/utils/download.py
+++ b/anomalib/data/utils/download.py
@@ -5,6 +5,7 @@
import hashlib
import io
+import tarfile
from pathlib import Path
from typing import Dict, Iterable, Optional, Union
@@ -195,3 +196,15 @@ def hash_check(file_path: Path, expected_hash: str):
assert (
hashlib.md5(hash_file.read()).hexdigest() == expected_hash
), f"Downloaded file {file_path} does not match the required hash."
+
+
+def tar_extract_all(file_path: Path, output_dir: Path):
+ """Extract all files from a targz archive.
+
+ Args:
+ file_path (Path): Path to archive.
+ output_dir (Path): Output directory.
+ """
+ with tarfile.open(name=file_path) as tar_file:
+ for member in tqdm(iterable=tar_file.getmembers(), total=len(tar_file.getmembers())):
+ tar_file.extract(member=member, path=output_dir)
diff --git a/anomalib/data/utils/image.py b/anomalib/data/utils/image.py
index c8c5039882..cb4e48723b 100644
--- a/anomalib/data/utils/image.py
+++ b/anomalib/data/utils/image.py
@@ -160,6 +160,11 @@ def read_image(path: Union[str, Path]) -> np.ndarray:
return image
+def read_mask(mask_path: Union[str, Path]) -> np.ndarray:
+ """Read a mask image from disk and keep the original values."""
+ return cv2.imread(str(mask_path), flags=cv2.IMREAD_GRAYSCALE)
+
+
def pad_nextpow2(batch: Tensor) -> Tensor:
"""Compute required padding from input size and return padded images.
From 7a80914dc99d17f88c9bb0eca3ac6ace6d3c0d86 Mon Sep 17 00:00:00 2001
From: jpcbertoldo <24547377+jpcbertoldo@users.noreply.github.com>
Date: Sat, 3 Sep 2022 23:13:58 +0200
Subject: [PATCH 03/38] pass pre-commit hooks
---
anomalib/data/mvtec_loco.py | 154 ++++++++++++++++++++++--------------
1 file changed, 93 insertions(+), 61 deletions(-)
diff --git a/anomalib/data/mvtec_loco.py b/anomalib/data/mvtec_loco.py
index 639ddc2f64..25d173992b 100644
--- a/anomalib/data/mvtec_loco.py
+++ b/anomalib/data/mvtec_loco.py
@@ -1,4 +1,26 @@
-"""
+"""MVTec LOCO AD Dataset (CC BY-NC-SA 4.0).
+
+Description:
+ This script contains PyTorch Dataset, Dataloader and PyTorch
+ Lightning DataModule for the MVTec LOCO AD dataset.
+
+ If the dataset is not on the file system, the script downloads and extracts the dataset.
+
+License:
+ MVTec LOCO AD dataset is released under the Creative Commons
+ Attribution-NonCommercial-ShareAlike 4.0 International License
+ (CC BY-NC-SA 4.0)(https://creativecommons.org/licenses/by-nc-sa/4.0/).
+
+Reference:
+
+ - Paul Bergmann, Kilian Batzner, Michael Fauser, David Sattlegger, Carsten Steger:
+ Beyond Dents and Scratches: Logical Constraints in Unsupervised Anomaly Detection
+ and Localization; in: International Journal of Computer Vision, 2022,
+ DOI: 10.1007/s11263-022-01578-9.
+
+ - https://www.mvtec.com/company/research/datasets/mvtec-loco
+
+##################################################
distinguishes structural and logical anomalies
n_image: 3644
@@ -35,8 +57,10 @@
objects are in a fixed position (mechanical alignment)
illumination is well suited
the access to images with real anomalies is limited (“impossible”)
-images only show a single object or logically ensemble set of objects (i.e. one-class setting although a “class” here is a composed object)
-no training annotations -- although it is assumed that the images in training are indeed from the target class (i.e. no noise)
+images only show a single object or logically ensemble set of objects
+ (i.e. one-class setting although a “class” here is a composed object)
+no training annotations -- although it is assumed that the images in training
+ are indeed from the target class (i.e. no noise)
problem 1 (image-wise anomaly detection): “is there an anomaly in the image?”
problem 2 (pixel-wise anomaly detection or anomaly segmentation): “which pixels belong to the anomaly?”
pixel-wise metric: Saturated Per-Region Overlap (sPRO)
@@ -116,7 +140,8 @@
2: yellow
3: blue
5: red
-the cable has to terminate in the same relative position on its two ends such that the whole construction exhibits a mirror symmetry
+the cable has to terminate in the same relative position on its two ends such
+ that the whole construction exhibits a mirror symmetry
examples of logical defects
(left) the two splicing connectors do not have the same number of clamps
(center) the color of the cable does not match the number of clamps
@@ -127,7 +152,8 @@
missing objects
the area in which the object could occur
the saturation threshold is chosen to be equal to the area of the missing object
-the saturation threshold for an object is chosen from the lower end of the distribution of its (manually annotated) area
+the saturation threshold for an object is chosen from the lower end of
+ the distribution of its (manually annotated) area
example (image): pushpin
the missing pushpin can occur anywhere inside its compartment, therefore its entire area is annotated
the saturation threshold is set to the size of a pushpin
@@ -150,24 +176,23 @@
other logical constraints
example (image, left): juice bottle
the bottle is filled with orange juice but carries the label of the cherry juice
-both the orange juice and the label with the cherry are present in the training set, but the logical anomaly arises due to the erroneous combination of the two in the same image
-either the area filled with juice or the cherry as could be considered anomalous, therefore the union of the two regions is annotated
-the saturation threshold is set to the area of the cherry because the segmentation of the cherry is sufficient to solve the anomaly localization
+both the orange juice and the label with the cherry are present in the training set,
+ but the logical anomaly arises due to the erroneous combination of the two in the same image
+either the area filled with juice or the cherry as could be considered anomalous,
+ therefore the union of the two regions is annotated
+the saturation threshold is set to the area of the cherry because the
+ segmentation of the cherry is sufficient to solve the anomaly localization
"""
# TODO: clear module docstring
import logging
-import tarfile
import warnings
from pathlib import Path
-from posixpath import split
-from typing import Dict, List, Optional, Tuple, Union
-from unicodedata import category
+from typing import Dict, Optional, Tuple, Union
from urllib.request import urlretrieve
import albumentations as A
-import cv2
import numpy as np
import pandas as pd
from numpy import ndarray
@@ -187,7 +212,9 @@
from anomalib.pre_processing import PreProcessor
# TODO: open discussion about keeping pre-resized tensors in the dataset folder
-# TODO: create an issue in mvtec so the dataset will retain the information abou the anomaly type (label) so one can do per-label evaluation
+# obs: mvtecad's doc says "...and create PyTorch data objects." but it does not!!!
+# TODO: create an issue in mvtec so the dataset will retain the information abou
+# the anomaly type (label) so one can do per-label evaluation
# TODO: document the notion of label and superlabel
logger = logging.getLogger(__name__)
@@ -342,7 +369,8 @@
)
# this is given at the paper, each anomaly type (label) has a different gtvalue in the mask
-# source: Beyond Dents and Scratches: Logical Constraints in Unsupervised Anomaly Detection and Localization (Bergmann, P. et al, 2022).
+# source: Beyond Dents and Scratches: Logical Constraints in
+# Unsupervised Anomaly Detection and Localization (Bergmann, P. et al, 2022).
_MAP_ANOTYPE_2_GTVALUE: Dict[Tuple[str, str, str], int] = {
(CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_COMPARTMENTS_SWAPPED): 242,
(CATEGORY_BREAKFAST_BOX, SUPER_ANOTYPE_LOGICAL, ANOTYPE_BB_MISSING_ALMONDS): 255,
@@ -442,7 +470,8 @@
}
# expected number of images in each category split so that we can check if the dataset is complete
-# source: Beyond Dents and Scratches: Logical Constraints in Unsupervised Anomaly Detection and Localization (Bergmann, P. et al, 2022).
+# source: Beyond Dents and Scratches: Logical Constraints
+# in Unsupervised Anomaly Detection and Localization (Bergmann, P. et al, 2022).
_EXPECTED_NSAMPLES: Dict[Tuple[str, str], int] = {
(CATEGORY_BREAKFAST_BOX, SPLIT_TRAIN): 351,
(CATEGORY_BREAKFAST_BOX, SPLIT_VALIDATION): 62,
@@ -466,10 +495,11 @@
}
-def _binarize_mask_float(mask: np.ndarray) -> np.ndarray:
+def _binarize_mask_float(mask: np.ndarray) -> np.ndarray: # noqa
"""
the masks use different gtvalue values for the different anomaly types so the > 0 is making it binary
- this operation is very simple but it is in a function to make sure its standard because it is used in different places
+ this operation is very simple but it is in a function to make
+ sure its standard because it is used in different places
e.g. preloading while building the dataset and on the fly while training
"""
return (mask > 0).astype(float)
@@ -479,22 +509,11 @@ def _make_dataset(
path: Path,
split: Optional[str] = None,
imread_strategy: str = IMREAD_STRATEGY_PRELOAD,
-) -> DataFrame:
- # todo create optional to get a subset of anomlies in the test
-
- assert split is None or split in SPLITS, f"Invalid split: {split}"
- assert imread_strategy in IMREAD_STRATEGIES, f"Invalid imread strategy: {imread_strategy}"
-
- category = path.resolve().name
- assert category in CATEGORIES, f"Invalid path '{path}'. The directory ('{category}') must be one of {CATEGORIES}"
-
- if split is None:
- return pd.concat([_make_dataset(path, split_, imread_strategy) for split_ in SPLITS], axis=0)
-
- logger.info(f"Creating MVTec LOCO AD dataset for category '{category}' split '{split}'")
-
+) -> DataFrame: # noqa D212
"""
- structure of the files in the dataset ("/" is 'path')
+ Find the images in the given path and create a DataFrame with all the information from each sample.
+
+ Expected structure of the files in the dataset ("/" is 'path')
images: /{split}/{super_anotype}/{image_index}.png
@@ -517,6 +536,18 @@ def _make_dataset(
/ground_truth/structural_anomalies/.../000.png
...
"""
+ # todo create optional to get a subset of anomlies in the test
+
+ assert split is None or split in SPLITS, f"Invalid split: {split}"
+ assert imread_strategy in IMREAD_STRATEGIES, f"Invalid imread strategy: {imread_strategy}"
+
+ category = path.resolve().name
+ assert category in CATEGORIES, f"Invalid path '{path}'. The directory ('{category}') must be one of {CATEGORIES}"
+
+ if split is None:
+ return pd.concat([_make_dataset(path, split_, imread_strategy) for split_ in SPLITS], axis=0)
+
+ logger.info("Creating MVTec LOCO AD dataset for category '%s' split '%s'", category, split)
# these values look like "(train|validation|test)/(good|logical_anomalies|structural_anomalies)/(000|...|n).png"
# where (a|b) means either a or b
@@ -525,7 +556,8 @@ def _make_dataset(
if len(samples_paths) != expected_nsamples:
warnings.warn(
- f"Expected {expected_nsamples} samples for split '{split}' in category '{category}' but found {len(samples_paths)}."
+ f"Expected {expected_nsamples} samples for split '{split}' "
+ "in category '{category}' but found {len(samples_paths)}."
"Is the dataset corrupted?"
)
@@ -549,7 +581,7 @@ def build_record(sample_path: Path):
)
return ret
- elif super_anotype in (SUPER_ANOTYPE_LOGICAL, SUPER_ANOTYPE_STRUCTURAL):
+ if super_anotype in (SUPER_ANOTYPE_LOGICAL, SUPER_ANOTYPE_STRUCTURAL):
mask_path: Path = path / "ground_truth" / super_anotype / sample_path.stem / "000.png"
@@ -573,11 +605,8 @@ def build_record(sample_path: Path):
return ret
- else:
- # there should only be the folders "good", "logical_anomalies" and "structural_anomalies"
- raise RuntimeError(
- f"Something wrong in the dataset folder. Unknown folder {super_anotype}, path={sample_path}"
- )
+ # there should only be the folders "good", "logical_anomalies" and "structural_anomalies"
+ raise RuntimeError(f"Something wrong in the dataset folder. Unknown folder {super_anotype}, path={sample_path}")
samples = pd.DataFrame.from_records([build_record(sp) for sp in samples_paths])
@@ -589,10 +618,10 @@ def build_record(sample_path: Path):
# stacklevel=3
# )
- logger.debug(f"Preloading images into memory")
+ logger.debug("Preloading images into memory")
samples["image"] = samples.image_path.map(read_image)
- logger.debug(f"Preloading masks into memory")
+ logger.debug("Preloading masks into memory")
# this is used to select the rows in the dataframe
has_mask = ~samples.mask_path.isnull()
@@ -634,14 +663,14 @@ def __init__(
self.imread_strategy = imread_strategy
self.samples = _make_dataset(
- path=self.dataset_path,
+ path=self.category_dataset_path,
split=self.split,
imread_strategy=self.imread_strategy,
)
@property
- def dataset_path(self) -> Path:
- """Path to the dataset folder."""
+ def category_dataset_path(self) -> Path:
+ """Path to the category dataset (root/category) folder."""
return self.root / self.category
def __len__(self) -> int:
@@ -654,11 +683,10 @@ def _get_image(self, index: int) -> ndarray:
if self.imread_strategy == IMREAD_STRATEGY_PRELOAD:
return self.samples.image[index]
- elif self.imread_strategy == IMREAD_STRATEGY_ONTHEFLY:
+ if self.imread_strategy == IMREAD_STRATEGY_ONTHEFLY:
return read_image(self.samples.image_path[index])
- else:
- raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
+ raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
def _get_mask(self, index: int) -> ndarray:
"""Get mask at index."""
@@ -666,11 +694,10 @@ def _get_mask(self, index: int) -> ndarray:
if self.imread_strategy == IMREAD_STRATEGY_PRELOAD:
return self.samples.mask[index]
- elif self.imread_strategy == IMREAD_STRATEGY_ONTHEFLY:
+ if self.imread_strategy == IMREAD_STRATEGY_ONTHEFLY:
return _binarize_mask_float(read_mask(self.samples.mask_path[index]))
- else:
- raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
+ raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
"""Get dataset item for the index ``index``.
@@ -731,7 +758,8 @@ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
class MVTecLOCO(LightningDataModule):
"""MVTec LOCO AD Lightning Data Module."""
- # todo correct inconsistency: `transform_config_*val*` used for val and test set, but `*test*_batch_size` used for val and set
+ # todo correct inconsistency: `transform_config_*val*` used for val and
+ # test set, but `*test*_batch_size` used for val and set
def __init__(
self,
@@ -779,35 +807,38 @@ def __init__(
self.inference_data: Dataset
@property
- def dataset_path(self) -> Path:
+ def category_dataset_path(self) -> Path:
+ """Path to the category dataset (root/category) folder."""
return self.root / self.category
def prepare_data(self) -> None:
"""Download the dataset if not available."""
- if self.dataset_path.is_dir():
+ if self.category_dataset_path.is_dir():
logger.info("Found the dataset.")
else:
self.root.mkdir(parents=True, exist_ok=True)
logger.info("Downloading the Mvtec LOCO AD dataset.")
- URL_MVTEC_LOCO_TARGZ = "https://www.mydrive.ch/shares/48237/1b9106ccdfbb09a0c414bd49fe44a14a/download/430647091-1646842701/mvtec_loco_anomaly_detection.tar.xz"
+ # flake8: noqa: E501
+ # pylint: disable=line-too-long
+ url_mvtec_loco_targz = "https://www.mydrive.ch/shares/48237/1b9106ccdfbb09a0c414bd49fe44a14a/download/430647091-1646842701/mvtec_loco_anomaly_detection.tar.xz"
dataset_name = "mvtec_loco_anomaly_detection.tar.xz"
zip_filename = self.root / dataset_name
with DownloadProgressBar(unit="B", unit_scale=True, miniters=1, desc="MVTec LOCO download") as progress_bar:
urlretrieve(
- url=URL_MVTEC_LOCO_TARGZ,
+ url=url_mvtec_loco_targz,
filename=zip_filename,
reporthook=progress_bar.update_to,
)
logger.info("Checking hash")
- MD5HASH_MVTEC_LOCO = "d40f092ac6f88433f609583c4a05f56f"
- hash_check(zip_filename, MD5HASH_MVTEC_LOCO)
+ md5hash_mvtec_loco = "d40f092ac6f88433f609583c4a05f56f"
+ hash_check(zip_filename, md5hash_mvtec_loco)
- logger.info(f"Extracting the dataset.")
- logger.debug(f"Extracting to {self.root}")
+ logger.info("Extracting the dataset.")
+ logger.debug("Extracting to %s", self.root)
tar_extract_all(zip_filename, self.root)
logger.info("Cleaning the tar file")
@@ -820,7 +851,8 @@ def setup(self, stage: Optional[str] = None) -> None:
stage: Optional[str]: fit/validate/test/predict stages. (Default value = None = fit)
"""
- logger.info("Setting up {} dataset." % str(stage or TrainerFn.FITTING))
+ # pylint: disable=consider-using-f-string
+ logger.info("Setting up %s dataset." % stage or TrainerFn.FITTING)
if stage in (None, TrainerFn.FITTING):
self.train_data = MVTecLOCODataset(
From 375ac656749dc998e3547959c0999ec4a84e0229 Mon Sep 17 00:00:00 2001
From: jpcbertoldo <24547377+jpcbertoldo@users.noreply.github.com>
Date: Sat, 3 Sep 2022 23:49:48 +0200
Subject: [PATCH 04/38] remove todos and correct a const
---
anomalib/data/mvtec_loco.py | 185 +-----------------------------------
1 file changed, 5 insertions(+), 180 deletions(-)
diff --git a/anomalib/data/mvtec_loco.py b/anomalib/data/mvtec_loco.py
index 25d173992b..98e13ef8e0 100644
--- a/anomalib/data/mvtec_loco.py
+++ b/anomalib/data/mvtec_loco.py
@@ -19,173 +19,8 @@
DOI: 10.1007/s11263-022-01578-9.
- https://www.mvtec.com/company/research/datasets/mvtec-loco
-
-##################################################
-
-distinguishes structural and logical anomalies
-n_image: 3644
-splits:
-no overlap and fixed
-train
-normal-only
-n_image_train: 1772
-validation
-normal-only
-n_image_validation: 304
-test
-normal + anomalous (structural and logical)
-n_image_test: 1568
-n_category: 5
-breakfast_box
-juice_bottle
-pushpins
-screw_bag
-splicing_connectors
-n_defect_type: 89
-
-##################################################
-
-configs
-https://docs.google.com/spreadsheets/d/1qHbyTsU2At1fusQsV8KH3_qdp3SYHHLy7QtpohKJIk0/edit?usp=sharing
-
-stats overview
-https://docs.google.com/spreadsheets/d/11GSf1SVsHFYDSwMAULEd7g5QK7P3Y21YMB10D_g0-Gk/edit?usp=sharing
-
-##################################################
-
-assumptions
-objects are in a fixed position (mechanical alignment)
-illumination is well suited
-the access to images with real anomalies is limited (“impossible”)
-images only show a single object or logically ensemble set of objects
- (i.e. one-class setting although a “class” here is a composed object)
-no training annotations -- although it is assumed that the images in training
- are indeed from the target class (i.e. no noise)
-problem 1 (image-wise anomaly detection): “is there an anomaly in the image?”
-problem 2 (pixel-wise anomaly detection or anomaly segmentation): “which pixels belong to the anomaly?”
-pixel-wise metric: Saturated Per-Region Overlap (sPRO)
-structural anomaly pixel annotation policy
-defects are confined to local regions
-each pixel that introduces a visual structure that is not present in the anomaly-free images is anomalous
-logical anomaly pixel annotation policy
-the union of all areas of the image that could be the cause for the anomaly is anomalous
-a method is not necessarily required to predict the whole ground truth area as anomalous
-
-##################################################
-
-breakfast box
-n_anomaly_type (n_structural, n_logical): 22 (5, 17)
-logical constraints
-contains 2 tangerines
-contains 1 nectarine
-the tangerines and the nectarine on the left
-cereals (C) and a mix of banana chips and almonds (B&A) on the right
-the ratio between C and B&A is fixed
-the relative position of C and B&A is fixed
-examples of logical defects
-too many banana chips and almonds
-
-##################################################
-
-juice bottle
-n_anomaly_type (n_structural, n_logical): 18 (7, 11)
-logical constraints
-there is 1 bottle
-the bottle is filled with a liquid and the fill level is always the same
-the liquid is of 1 out of 3 colors (red, yellow, white-ish)
-the bottle carries 2 labels
-the first label is attached to the center of the bottle
-the first label displays an icon that determines the type of liquid (cherry, orange, banana)
-cherry: red
-orange: yellow
-banana: white-ish
-the second label is attached to the lower part of the bottle
-the second label contains the text “100% Juice”
-examples of logical defects
-(left) the icon does not match the type of juice
-(middle) the icon is slightly misplaced
-(right) the fill level is too high
-
-##################################################
-
-pushpins
-n_anomaly_type (n_structural, n_logical): 8 (4, 4)
-logical constraints
-each compartment contains 1 pushpin
-examples of logical defects
-1 compartment has a missing pin
-
-##################################################
-
-screw bag
-n_anomaly_type (n_structural, n_logical): 20 (4, 16)
-logical constraints
-the bag contains
-2 washers
-2 nuts
-1 long screw
-2 short screw
-examples of logical defects
-two long screws and lacks a short one
-
-##################################################
-
-splicing connectors
-n_anomaly_type (n_structural, n_logical): 21 (8, 13)
-logical constraints
-there are 2 splicing connectors
-they have the same number of cable clamps
-they are linked by 1 cable
-the number of clamps has a one-to-one correspondence to the color of the cable
-2: yellow
-3: blue
-5: red
-the cable has to terminate in the same relative position on its two ends such
- that the whole construction exhibits a mirror symmetry
-examples of logical defects
-(left) the two splicing connectors do not have the same number of clamps
-(center) the color of the cable does not match the number of clamps
-(right) the cable terminates in different positions
-
-##################################################
-
-missing objects
-the area in which the object could occur
-the saturation threshold is chosen to be equal to the area of the missing object
-the saturation threshold for an object is chosen from the lower end of
- the distribution of its (manually annotated) area
-example (image): pushpin
-the missing pushpin can occur anywhere inside its compartment, therefore its entire area is annotated
-the saturation threshold is set to the size of a pushpin
-
-##################################################
-
-additional objects
-too many instances of an object: all instances of the object are annotated
-the saturation threshold is set to the area of the extraneous objects
-example (image): splicing connectors
-an additional cable is present between the two splicing connectors
-it is not clear which of the two cables represents the anomaly, therefore both are annotated
-the saturation threshold is set to the area of one cable (i.e., half of the annotated region)
-properties
-a method can obtain a perfect score even if it only marks one of the two cables as an anomaly
-a method that marks both is neither penalized nor (extra-)rewarded
-
-##################################################
-
-other logical constraints
-example (image, left): juice bottle
-the bottle is filled with orange juice but carries the label of the cherry juice
-both the orange juice and the label with the cherry are present in the training set,
- but the logical anomaly arises due to the erroneous combination of the two in the same image
-either the area filled with juice or the cherry as could be considered anomalous,
- therefore the union of the two regions is annotated
-the saturation threshold is set to the area of the cherry because the
- segmentation of the cherry is sufficient to solve the anomaly localization
"""
-# TODO: clear module docstring
-
import logging
import warnings
from pathlib import Path
@@ -211,17 +46,12 @@
from anomalib.data.utils.download import tar_extract_all
from anomalib.pre_processing import PreProcessor
-# TODO: open discussion about keeping pre-resized tensors in the dataset folder
-# obs: mvtecad's doc says "...and create PyTorch data objects." but it does not!!!
-# TODO: create an issue in mvtec so the dataset will retain the information abou
-# the anomaly type (label) so one can do per-label evaluation
-# TODO: document the notion of label and superlabel
-
logger = logging.getLogger(__name__)
+TASK_CLASSIFICATION = "classification"
TASK_SEGMENTATION = "segmentation"
-TASKS = TASK_SEGMENTATION
+TASKS = (TASK_CLASSIFICATION, TASK_SEGMENTATION)
SPLIT_TRAIN = "train"
# "validation" instead of "val" is an explicit choice because this will match the name of the folder
@@ -234,7 +64,6 @@
# all images are pre-loaded into memory at initialization
IMREAD_STRATEGY_PRELOAD = "preload"
IMREAD_STRATEGIES = (IMREAD_STRATEGY_ONTHEFLY, IMREAD_STRATEGY_PRELOAD)
-# TODO make a strategy preload_tensor
CATEGORY_BREAKFAST_BOX = "breakfast_box"
CATEGORY_JUICE_BOTTLE = "juice_bottle"
@@ -486,7 +315,6 @@
(CATEGORY_SCREW_BAG, SPLIT_VALIDATION): 60,
(CATEGORY_SCREW_BAG, SPLIT_TEST): 341,
# these two below were wrong in the paper
- # TODO send a correction to the authors
# (CATEGORY_SPLICING_CONNECTORS, SPLIT_TRAIN): 354,
# (CATEGORY_SPLICING_CONNECTORS, SPLIT_VALIDATION): 59,
(CATEGORY_SPLICING_CONNECTORS, SPLIT_TRAIN): 360,
@@ -702,10 +530,6 @@ def _get_mask(self, index: int) -> ndarray:
def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
"""Get dataset item for the index ``index``.
- TODO: include the label string
- TODO: include the label anomaly type (strutural, logical)
- TODO: here? probably better to separate it... return the sPRO saturation value
-
Args:
index (int): Index to get the item.
@@ -728,6 +552,8 @@ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
{
"label": self.samples.label[index],
"image_path": self.samples.image_path[index],
+ "anotype": self.samples.anotype[index],
+ "super_anotype": self.samples.super_anotype[index],
}
)
@@ -742,7 +568,6 @@ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
else:
mask = self._get_mask(index)
- # TODO: ask how this works, does the transform re-apply the last call when mask is not None?
pre_processed = self.pre_process(image=image, mask=mask)
item.update(
{
@@ -765,7 +590,7 @@ def __init__(
self,
root: str,
category: str,
- # TODO: add a parameter to specify the anomaly types and (more specifically) the anomaly classes -- "label"
+ # TODO: add a parameter to specify the anomaly types and (more specifically)
image_size: Optional[Union[int, Tuple[int, int]]] = None,
train_batch_size: int = 32,
test_batch_size: int = 32,
From f63d5a95eff9ef893dec689787992e69fc7e889f Mon Sep 17 00:00:00 2001
From: jpcbertoldo <24547377+jpcbertoldo@users.noreply.github.com>
Date: Sat, 3 Sep 2022 23:51:33 +0200
Subject: [PATCH 05/38] remove todos and correct a const
---
anomalib/data/mvtec_loco.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/anomalib/data/mvtec_loco.py b/anomalib/data/mvtec_loco.py
index 98e13ef8e0..63a9fd9c89 100644
--- a/anomalib/data/mvtec_loco.py
+++ b/anomalib/data/mvtec_loco.py
@@ -440,11 +440,11 @@ def build_record(sample_path: Path):
if imread_strategy == IMREAD_STRATEGY_PRELOAD:
- # warnings.warn(
- # "Preloading images into memory. "
- # "If your dataset is too large, consider using another imread_strategy instead.",
- # stacklevel=3
- # )
+ warnings.warn(
+ "Preloading images into memory. "
+ "If your dataset is too large, consider using another imread_strategy instead.",
+ stacklevel=3,
+ )
logger.debug("Preloading images into memory")
samples["image"] = samples.image_path.map(read_image)
From fd2d9171a1e26959b5f24220293162e4eb355e29 Mon Sep 17 00:00:00 2001
From: jpcbertoldo <24547377+jpcbertoldo@users.noreply.github.com>
Date: Sun, 4 Sep 2022 14:48:15 +0200
Subject: [PATCH 06/38] create notebook and make small corrections
---
anomalib/data/__init__.py | 15 +
anomalib/data/mvtec_loco.py | 287 ++++++++-----
.../100_datamodules/104_mvtec_loco.ipynb | 394 ++++++++++++++++++
3 files changed, 587 insertions(+), 109 deletions(-)
create mode 100644 notebooks/100_datamodules/104_mvtec_loco.ipynb
diff --git a/anomalib/data/__init__.py b/anomalib/data/__init__.py
index 8c295a1061..fa0a9036b4 100644
--- a/anomalib/data/__init__.py
+++ b/anomalib/data/__init__.py
@@ -13,6 +13,7 @@
from .folder import Folder
from .inference import InferenceDataset
from .mvtec import MVTec
+from .mvtec_loco import MVTecLOCO
logger = logging.getLogger(__name__)
@@ -79,6 +80,19 @@ def get_datamodule(config: Union[DictConfig, ListConfig]) -> LightningDataModule
transform_config_val=config.dataset.transform_config.val,
create_validation_set=config.dataset.create_validation_set,
)
+ elif config.dataset.format.lower() == "mvtec_loco":
+ datamodule = MVTecLOCO(
+ root=config.dataset.path,
+ category=config.dataset.category,
+ image_size=(config.dataset.image_size[0], config.dataset.image_size[1]),
+ train_batch_size=config.dataset.train_batch_size,
+ test_batch_size=config.dataset.test_batch_size,
+ num_workers=config.dataset.num_workers,
+ task=config.dataset.task,
+ transform_config_train=config.dataset.transform_config.train,
+ transform_config_val=config.dataset.transform_config.val,
+ imread_strategy=config.dataset.imread_strategy,
+ )
else:
raise ValueError(
"Unknown dataset! \n"
@@ -95,4 +109,5 @@ def get_datamodule(config: Union[DictConfig, ListConfig]) -> LightningDataModule
"Folder",
"InferenceDataset",
"MVTec",
+ "MVTecLOCO",
]
diff --git a/anomalib/data/mvtec_loco.py b/anomalib/data/mvtec_loco.py
index 63a9fd9c89..5280cf470e 100644
--- a/anomalib/data/mvtec_loco.py
+++ b/anomalib/data/mvtec_loco.py
@@ -59,11 +59,14 @@
SPLIT_TEST = "test"
SPLITS = (SPLIT_TRAIN, SPLIT_VALIDATION, SPLIT_TEST)
-# each image is read upon demand
IMREAD_STRATEGY_ONTHEFLY = "onthefly"
-# all images are pre-loaded into memory at initialization
+"""Images are read into memory upon demand (no cache)."""
+
IMREAD_STRATEGY_PRELOAD = "preload"
+"""All images are read into memory at initialization."""
+
IMREAD_STRATEGIES = (IMREAD_STRATEGY_ONTHEFLY, IMREAD_STRATEGY_PRELOAD)
+"""Options of strategies to read images into memory."""
CATEGORY_BREAKFAST_BOX = "breakfast_box"
CATEGORY_JUICE_BOTTLE = "juice_bottle"
@@ -333,9 +336,46 @@ def _binarize_mask_float(mask: np.ndarray) -> np.ndarray: # noqa
return (mask > 0).astype(float)
+def download_and_extract_mvtec_loco(root: Union[str, Path]) -> None:
+ """Download and extract the MVTec LOCO dataset to the given root directory.
+
+ Args:
+ root (Union[str, Path]): directory where the dataset will be stored.
+
+ """
+ root = Path(root)
+ root.mkdir(parents=True, exist_ok=True)
+
+ logger.info("Downloading the Mvtec LOCO AD dataset.")
+
+ # flake8: noqa: E501
+ # pylint: disable=line-too-long
+ url_mvtec_loco_targz = "https://www.mydrive.ch/shares/48237/1b9106ccdfbb09a0c414bd49fe44a14a/download/430647091-1646842701/mvtec_loco_anomaly_detection.tar.xz"
+
+ dataset_name = "mvtec_loco_anomaly_detection.tar.xz"
+ zip_filename = root / dataset_name
+ with DownloadProgressBar(unit="B", unit_scale=True, miniters=1, desc="MVTec LOCO download") as progress_bar:
+ urlretrieve(
+ url=url_mvtec_loco_targz,
+ filename=zip_filename,
+ reporthook=progress_bar.update_to,
+ )
+
+ logger.info("Checking hash")
+ md5hash_mvtec_loco = "d40f092ac6f88433f609583c4a05f56f"
+ hash_check(zip_filename, md5hash_mvtec_loco)
+
+ logger.info("Extracting the dataset.")
+ logger.debug("Extracting to %s", root)
+ tar_extract_all(zip_filename, root)
+
+ logger.info("Cleaning the tar file")
+ zip_filename.unlink()
+
+
def _make_dataset(
path: Path,
- split: Optional[str] = None,
+ split: str,
imread_strategy: str = IMREAD_STRATEGY_PRELOAD,
) -> DataFrame: # noqa D212
"""
@@ -364,16 +404,12 @@ def _make_dataset(
/ground_truth/structural_anomalies/.../000.png
...
"""
- # todo create optional to get a subset of anomlies in the test
- assert split is None or split in SPLITS, f"Invalid split: {split}"
+ assert split in SPLITS, f"Invalid split: {split}"
assert imread_strategy in IMREAD_STRATEGIES, f"Invalid imread strategy: {imread_strategy}"
category = path.resolve().name
- assert category in CATEGORIES, f"Invalid path '{path}'. The directory ('{category}') must be one of {CATEGORIES}"
-
- if split is None:
- return pd.concat([_make_dataset(path, split_, imread_strategy) for split_ in SPLITS], axis=0)
+ assert category in CATEGORIES, f"Invalid path '{path}'. The category '{category}' must be one of {CATEGORIES}"
logger.info("Creating MVTec LOCO AD dataset for category '%s' split '%s'", category, split)
@@ -382,10 +418,12 @@ def _make_dataset(
samples_paths = sorted(path.glob(f"{split}/**/*.png"))
expected_nsamples = _EXPECTED_NSAMPLES[(category, split)]
+ assert len(samples_paths) > 0, f"No samples found in {path}"
+
if len(samples_paths) != expected_nsamples:
warnings.warn(
f"Expected {expected_nsamples} samples for split '{split}' "
- "in category '{category}' but found {len(samples_paths)}."
+ f"in category '{category}' but found {len(samples_paths)}."
"Is the dataset corrupted?"
)
@@ -447,12 +485,12 @@ def build_record(sample_path: Path):
)
logger.debug("Preloading images into memory")
- samples["image"] = samples.image_path.map(read_image)
+ samples["image"] = samples["image_path"].map(read_image)
logger.debug("Preloading masks into memory")
# this is used to select the rows in the dataframe
- has_mask = ~samples.mask_path.isnull()
+ has_mask = ~samples["mask_path"].isnull()
samples.loc[has_mask, "mask"] = samples.loc[has_mask, "mask_path"].map(
lambda x: _binarize_mask_float(read_mask(x))
@@ -469,12 +507,31 @@ def __init__(
self,
root: Union[Path, str],
category: str,
- pre_process: PreProcessor,
split: str,
+ pre_process: PreProcessor,
task: str = TASK_SEGMENTATION,
- # create_validation_set: bool = False,
imread_strategy: str = IMREAD_STRATEGY_PRELOAD,
) -> None:
+ """Mvtec LOCO AD Dataset class.
+
+ Args:
+ root: Path to the MVTec LOCO AD dataset root folder.
+ category: Name of the MVTec LOCO AD category (there are 5).
+ See ``anomalib.data.mvtec_loco.CATEGORIES``.
+ split: 'train', 'validation' or 'test'
+ See anomalib.data.mvtec_loco.SPLITS.
+ pre_process: List of pre_processing object containing albumentation compose or config.
+ task: ``classification`` or ``segmentation``
+ Default: ``segmentation``
+ ``anomalib.data.mvtec_loco.TASKS``.
+ imread_strategy: When should images be read into memory?
+ Default: ``preload``
+ See ``anomalib.data.mvtec_loco.IMREAD_STRATEGIES``.
+
+ TODO add link
+ See examples in the repository ``anomalib/notebooks/100_datamodules/104_mvtec_loco.ipynb``.
+ """
+
super().__init__(root)
assert split in SPLITS, f"Split '{split}' is not supported. Supported splits are {SPLITS}"
@@ -509,10 +566,10 @@ def _get_image(self, index: int) -> ndarray:
"""Get image at index."""
if self.imread_strategy == IMREAD_STRATEGY_PRELOAD:
- return self.samples.image[index]
+ return self.samples.iloc[index]["image"]
if self.imread_strategy == IMREAD_STRATEGY_ONTHEFLY:
- return read_image(self.samples.image_path[index])
+ return read_image(self.samples.iloc[index]["image_path"])
raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
@@ -520,14 +577,14 @@ def _get_mask(self, index: int) -> ndarray:
"""Get mask at index."""
if self.imread_strategy == IMREAD_STRATEGY_PRELOAD:
- return self.samples.mask[index]
+ return self.samples.iloc[index]["mask"]
if self.imread_strategy == IMREAD_STRATEGY_ONTHEFLY:
- return _binarize_mask_float(read_mask(self.samples.mask_path[index]))
+ return _binarize_mask_float(read_mask(self.samples.iloc[index]["mask_path"]))
raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
- def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
+ def __getitem__(self, index: int) -> Union[Dict[str, Tensor], Dict[str, Union[str, Tensor, int]]]:
"""Get dataset item for the index ``index``.
Args:
@@ -535,9 +592,10 @@ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
Returns:
Union[Dict[str, Tensor], Dict[str, Union[str, Tensor]]]: Dict of image tensor during training.
- Otherwise, Dict containing image path, target path, image tensor, label and transformed bounding box.
+ Otherwise, Dict containing image path, image tensor, label, anomaly type and,
+ if it is segmentation task, mask path and mask tensor.
"""
- item: Dict[str, Union[str, Tensor]] = {}
+ item: Dict[str, Union[str, Tensor, int]] = {}
image = self._get_image(index)
pre_processed = self.pre_process(image=image)
@@ -550,10 +608,10 @@ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
item.update(
{
- "label": self.samples.label[index],
- "image_path": self.samples.image_path[index],
- "anotype": self.samples.anotype[index],
- "super_anotype": self.samples.super_anotype[index],
+ "label": self.samples.iloc[index]["label"],
+ "image_path": str(self.samples.iloc[index]["image_path"]),
+ "anotype": self.samples.iloc[index]["anotype"],
+ "super_anotype": self.samples.iloc[index]["super_anotype"],
}
)
@@ -562,7 +620,7 @@ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
# Only Anomalous (1) images has masks in MVTec LOCO AD dataset.
# Therefore, create empty mask for Normal (0) images.
- if self.samples.label[index] == LABEL_NORMAL:
+ if self.samples.iloc[index]["label"] == LABEL_NORMAL:
mask = np.zeros(shape=image.shape[:2]) # shape: (H, W, C)
else:
@@ -571,7 +629,7 @@ def __getitem__(self, index: int) -> Dict[str, Union[str, Tensor]]:
pre_processed = self.pre_process(image=image, mask=mask)
item.update(
{
- "mask_path": self.samples.mask_path[index],
+ "mask_path": str(self.samples.iloc[index]["mask_path"]),
"mask": pre_processed["mask"],
}
)
@@ -590,20 +648,47 @@ def __init__(
self,
root: str,
category: str,
- # TODO: add a parameter to specify the anomaly types and (more specifically)
+ task: str = TASK_SEGMENTATION,
+ imread_strategy: str = IMREAD_STRATEGY_PRELOAD,
image_size: Optional[Union[int, Tuple[int, int]]] = None,
- train_batch_size: int = 32,
- test_batch_size: int = 32,
num_workers: int = 8,
- task: str = TASK_SEGMENTATION,
+ train_batch_size: int = 32,
transform_config_train: Optional[Union[str, A.Compose]] = None,
+ test_batch_size: int = 32,
transform_config_val: Optional[Union[str, A.Compose]] = None,
- seed: Optional[int] = None,
- imread_strategy: str = IMREAD_STRATEGY_PRELOAD,
+ # TODO: add a parameter to specify the anomaly types and (more specifically)
) -> None:
+ """Mvtec LOCO AD Lightning Data Module.
+
+ Args:
+ root: Path to the MVTec LOCO AD dataset root folder.
+ category: Name of the MVTec LOCO AD category (there are 5).
+ See ``anomalib.data.mvtec_loco.CATEGORIES``.
+ task: ``classification`` or ``segmentation``
+ Default: ``segmentation``
+ See ``anomalib.data.mvtec_loco.TASKS``.
+ imread_strategy: When should images be read into memory?
+ Default: ``preload``
+ See ``anomalib.data.mvtec_loco.IMREAD_STRATEGIES``.
+ image_size: Images are resized to `image_size` (HEIGHT, WIDTH), or (SIZE, SIZE) if a single value is given.
+ num_workers: Number of workers.
+ train_batch_size: Training batch size.
+ transform_config_train: List of pre_processing object containing albumentation compose or
+ config applied during training.
+ test_batch_size: Testing batch size.
+ transform_config_val: List of pre_processing object containing albumentation compose or
+ config applied during validation.
+
+ TODO add link
+ See examples in the repository ``anomalib/notebooks/100_datamodules/104_mvtec_loco.ipynb``.
+ """
super().__init__()
- # todo? add input assertions/validations
+ # TODO create option to get a subset of anomalies in the test
+ # TODO the images are not squared here, maybe we should add warn the user if
+ # the ration from image_size is too different from the original image when
+ # the image size is given as an int
+
assert task in TASKS, f"Task '{task}' is not supported. Supported tasks are {TASKS}"
assert (
imread_strategy in IMREAD_STRATEGIES
@@ -623,7 +708,6 @@ def __init__(
self.num_workers = num_workers
self.task = task
- self.seed = seed
self.imread_strategy = imread_strategy
self.train_data: Dataset
@@ -643,31 +727,7 @@ def prepare_data(self) -> None:
logger.info("Found the dataset.")
else:
- self.root.mkdir(parents=True, exist_ok=True)
-
- logger.info("Downloading the Mvtec LOCO AD dataset.")
- # flake8: noqa: E501
- # pylint: disable=line-too-long
- url_mvtec_loco_targz = "https://www.mydrive.ch/shares/48237/1b9106ccdfbb09a0c414bd49fe44a14a/download/430647091-1646842701/mvtec_loco_anomaly_detection.tar.xz"
- dataset_name = "mvtec_loco_anomaly_detection.tar.xz"
- zip_filename = self.root / dataset_name
- with DownloadProgressBar(unit="B", unit_scale=True, miniters=1, desc="MVTec LOCO download") as progress_bar:
- urlretrieve(
- url=url_mvtec_loco_targz,
- filename=zip_filename,
- reporthook=progress_bar.update_to,
- )
-
- logger.info("Checking hash")
- md5hash_mvtec_loco = "d40f092ac6f88433f609583c4a05f56f"
- hash_check(zip_filename, md5hash_mvtec_loco)
-
- logger.info("Extracting the dataset.")
- logger.debug("Extracting to %s", self.root)
- tar_extract_all(zip_filename, self.root)
-
- logger.info("Cleaning the tar file")
- zip_filename.unlink()
+ download_and_extract_mvtec_loco(self.root)
def setup(self, stage: Optional[str] = None) -> None:
"""Setup train, validation and test data.
@@ -680,6 +740,11 @@ def setup(self, stage: Optional[str] = None) -> None:
logger.info("Setting up %s dataset." % stage or TrainerFn.FITTING)
if stage in (None, TrainerFn.FITTING):
+
+ if hasattr(self, "train_data"):
+ logger.debug("Train data already exists. Skipping setup.")
+ return
+
self.train_data = MVTecLOCODataset(
root=self.root,
category=self.category,
@@ -690,6 +755,11 @@ def setup(self, stage: Optional[str] = None) -> None:
)
if stage == TrainerFn.VALIDATING:
+
+ if hasattr(self, "val_data"):
+ logger.debug("Validation data already exists. Skipping setup.")
+ return
+
self.val_data = MVTecLOCODataset(
root=self.root,
category=self.category,
@@ -700,6 +770,11 @@ def setup(self, stage: Optional[str] = None) -> None:
)
if stage == TrainerFn.TESTING:
+
+ if hasattr(self, "test_data"):
+ logger.debug("Test data already exists. Skipping setup.")
+ return
+
self.test_data = MVTecLOCODataset(
root=self.root,
category=self.category,
@@ -710,75 +785,69 @@ def setup(self, stage: Optional[str] = None) -> None:
)
if stage == TrainerFn.PREDICTING:
+
+ if hasattr(self, "inference_data"):
+ logger.debug("Inference data already exists. Skipping setup.")
+ return
+
self.inference_data = InferenceDataset(
path=self.root, image_size=self.image_size, transform_config=self.transform_config_val
)
def train_dataloader(self) -> TRAIN_DATALOADERS:
"""Get train dataloader."""
+ if not hasattr(self, "train_data"):
+ raise RuntimeError("Train data not setup. Did you run `datamodule.setup('fit')`?")
return DataLoader(self.train_data, shuffle=True, batch_size=self.train_batch_size, num_workers=self.num_workers)
def val_dataloader(self) -> EVAL_DATALOADERS:
"""Get validation dataloader."""
+ if not hasattr(self, "val_data"):
+ raise RuntimeError("Validation data not setup. Did you run `datamodule.setup('validate')`?")
return DataLoader(self.val_data, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers)
def test_dataloader(self) -> EVAL_DATALOADERS:
"""Get test dataloader."""
+ if not hasattr(self, "test_data"):
+ raise RuntimeError("Test data not setup. Did you run `datamodule.setup('test')`?")
return DataLoader(self.test_data, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers)
def predict_dataloader(self) -> EVAL_DATALOADERS:
"""Get predict dataloader."""
+ if not hasattr(self, "inference_data"):
+ raise RuntimeError("Inference data not setup. Did you run `datamodule.setup('predict')`?")
return DataLoader(
self.inference_data, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers
)
-# TODO remove me
-# debug _make_dataset in main
-if __name__ == "__main__":
- import itertools
-
- for cat in CATEGORIES:
- _make_dataset(Path(f"/home/jcasagrandebertoldo/Downloads/loco/{cat}"))
-
- for cat, split_ in itertools.product(CATEGORIES, SPLITS):
- pass
- # _make_dataset(Path(f"/home/jcasagrandebertoldo/Downloads/loco/{cat}"), split_)
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # test instantiate of the two classes
- # then test with script
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
- # next
+# next
+# correct the multi-image ground truth
+# then show it in the notebook
+# then create unit tests
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
+# next
diff --git a/notebooks/100_datamodules/104_mvtec_loco.ipynb b/notebooks/100_datamodules/104_mvtec_loco.ipynb
new file mode 100644
index 0000000000..0f5fe9585c
--- /dev/null
+++ b/notebooks/100_datamodules/104_mvtec_loco.ipynb
@@ -0,0 +1,394 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## MVTec LOCO AD"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pathlib import Path\n",
+ "\n",
+ "import numpy as np\n",
+ "from IPython.core.interactiveshell import InteractiveShell\n",
+ "from PIL import Image\n",
+ "from torchvision.transforms import ToPILImage\n",
+ "\n",
+ "from anomalib.data.mvtec_loco import (\n",
+ " MVTecLOCO,\n",
+ " MVTecLOCODataset,\n",
+ " download_and_extract_mvtec_loco,\n",
+ ")\n",
+ "from anomalib.pre_processing import PreProcessor\n",
+ "from anomalib.pre_processing.transforms import Denormalize\n",
+ "\n",
+ "# make a cell print all the outputs instead of just the last one\n",
+ "InteractiveShell.ast_node_interactivity = \"all\"\n",
+ "\n",
+ "# pylint: disable=locally-disabled, pointless-statement\n",
+ "# the ``pointless-statement`` warning is disabled because we use them to print stuff"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Download and extract the dataset"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "root = Path(\"../../datasets/MVTecLOCO\")\n",
+ "if not root.exists():\n",
+ " download_and_extract_mvtec_loco(root)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Torch Dataset"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "MVTecLOCODataset??"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To create `MVTecDataset` we need to import `pre_process` that applies transforms to the input image."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "PreProcessor??"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pre_process = PreProcessor(image_size=(100, 170), to_tensor=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Classification Task"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# MVTec LOCO Classification Train Set\n",
+ "mvtec_loco_dataset_classification_train = MVTecLOCODataset(\n",
+ " root=\"../../datasets/MVTecLOCO\",\n",
+ " category=\"pushpins\",\n",
+ " split=\"train\",\n",
+ " pre_process=pre_process,\n",
+ " task=\"classification\",\n",
+ ")\n",
+ "mvtec_loco_dataset_classification_train.samples.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sample = mvtec_loco_dataset_classification_train[0]\n",
+ "sample.keys()\n",
+ "sample[\"image\"].shape"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As can be seen above, when we choose `classification` task and `train` split, the dataset only returns `image`. This is mainly because training only requires normal images and no labels. Now let's try `test` split for the `classification` task"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# MVTec Classification Test Set\n",
+ "mvtec_loco_dataset_classification_test = MVTecLOCODataset(\n",
+ " root=\"../../datasets/MVTecLOCO\",\n",
+ " category=\"pushpins\",\n",
+ " split=\"test\",\n",
+ " pre_process=pre_process,\n",
+ " task=\"classification\",\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sample = mvtec_loco_dataset_classification_test[0]\n",
+ "sample.keys()\n",
+ "sample[\"image\"].shape\n",
+ "sample[\"image_path\"]\n",
+ "sample[\"label\"]\n",
+ "sample[\"super_anotype\"], sample[\"anotype\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Negative indices are also enabled."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sample = mvtec_loco_dataset_classification_test[-1]\n",
+ "sample.keys()\n",
+ "sample[\"image\"].shape\n",
+ "sample[\"image_path\"]\n",
+ "sample[\"label\"]\n",
+ "sample[\"super_anotype\"], sample[\"anotype\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Segmentation Task\n",
+ "\n",
+ "It is also possible to configure the MVTec LOCO dataset for the segmentation task, where the dataset object returns image and ground-truth mask."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# MVTec LOCO Segmentation Train Set\n",
+ "mvtec_loco_dataset_segmentation_train = MVTecLOCODataset(\n",
+ " root=\"../../datasets/MVTecLOCO\",\n",
+ " category=\"pushpins\",\n",
+ " pre_process=pre_process,\n",
+ " split=\"train\",\n",
+ " task=\"segmentation\",\n",
+ ")\n",
+ "mvtec_loco_dataset_segmentation_train.samples.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# MVTec LOCO Segmentation Test Set\n",
+ "mvtec_loco_dataset_segmentation_test = MVTecLOCODataset(\n",
+ " root=\"../../datasets/MVTecLOCO\",\n",
+ " category=\"pushpins\",\n",
+ " pre_process=pre_process,\n",
+ " split=\"test\",\n",
+ " task=\"segmentation\",\n",
+ ")\n",
+ "sample = mvtec_loco_dataset_segmentation_test[20]\n",
+ "sample.keys()\n",
+ "sample[\"image\"].shape\n",
+ "sample[\"mask\"].shape"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's visualize the image and the mask..."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "img = ToPILImage()(Denormalize()(sample[\"image\"].clone()))\n",
+ "msk = ToPILImage()(sample[\"mask\"]).convert(\"RGB\")\n",
+ "\n",
+ "Image.fromarray(np.vstack((np.array(img), np.array(msk))))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### DataModule\n",
+ "\n",
+ "So far, we have shown the Torch Dateset implementation of MVTec LOCO AD dataset. This is quite useful to get a sample, but we do need more than this when we train models in an end-to-end fashion.\n",
+ " \n",
+ "The [PyTorch Lightning DataModule](https://pytorch-lightning.readthedocs.io/en/latest/data/datamodule.html) for MVTec LOCO AD (shown below) is handles the the dataset download, and train/val/test/inference dataloaders instantiation."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "MVTecLOCO??"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "mvtec_datamodule = MVTecLOCO(\n",
+ " root=\"../../datasets/MVTecLOCO\",\n",
+ " category=\"pushpins\",\n",
+ " image_size=(200, 340), # (height, width) 5x smaller than original\n",
+ " train_batch_size=32,\n",
+ " test_batch_size=32,\n",
+ " num_workers=8,\n",
+ " task=\"segmentation\",\n",
+ ")\n",
+ "\n",
+ "# verify if the dataset is available and download it if not\n",
+ "mvtec_datamodule.prepare_data()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Train images\n",
+ "\n",
+ "# instantiate the Torch Dataset(s), loading the (meta-)data into memory\n",
+ "mvtec_datamodule.setup(\"fit\")\n",
+ "\n",
+ "i, data = next(enumerate(mvtec_datamodule.train_dataloader()))\n",
+ "data.keys()\n",
+ "data[\"image\"].shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Validation images\n",
+ "mvtec_datamodule.setup(\"validate\")\n",
+ "i, data = next(enumerate(mvtec_datamodule.val_dataloader()))\n",
+ "data.keys()\n",
+ "data[\"image\"].shape\n",
+ "data[\"mask\"].shape\n",
+ "data[\"super_anotype\"][0], data[\"anotype\"][0]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Test images\n",
+ "mvtec_datamodule.setup(\"test\")\n",
+ "# iterate a few times so we can find a sample with an anomaly\n",
+ "for i, data in enumerate(mvtec_datamodule.test_dataloader()):\n",
+ " if i == 5:\n",
+ " break\n",
+ "data.keys()\n",
+ "data[\"image\"].shape\n",
+ "data[\"mask\"].shape\n",
+ "data[\"super_anotype\"][0], data[\"anotype\"][0]\n",
+ "\n",
+ "img = ToPILImage()(Denormalize()(data[\"image\"][0].clone()))\n",
+ "msk = ToPILImage()(data[\"mask\"][0]).convert(\"RGB\")\n",
+ "\n",
+ "Image.fromarray(np.vstack((np.array(img), np.array(msk))))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "TODO: show that the ground truth is divided in multiple images\n",
+ "\n",
+ "TODO: create issue to correct docs in mvtec, e.g. it should not give example in the docstring but send the user\n",
+ " to the notebooks (more maintainable)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As can be seen above, creating the dataloaders are pretty straghtforward, which could be directly used for training/testing/inference."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3.8.13 ('anomalib-dev')",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.13"
+ },
+ "orig_nbformat": 4,
+ "vscode": {
+ "interpreter": {
+ "hash": "8787c31053eaf11dad02e12159779e58bcbd87fee611b470525fee7090bb4db2"
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
From 43f154e6f3d7ddd6ed9cda7bd7e538c9e9678ced Mon Sep 17 00:00:00 2001
From: jpcbertoldo <24547377+jpcbertoldo@users.noreply.github.com>
Date: Fri, 9 Sep 2022 23:35:58 +0200
Subject: [PATCH 07/38] manage multiple masks
---
anomalib/data/mvtec_loco.py | 132 +-
.../100_datamodules/104_mvtec_loco.ipynb | 1526 ++++++++++++++++-
2 files changed, 1564 insertions(+), 94 deletions(-)
diff --git a/anomalib/data/mvtec_loco.py b/anomalib/data/mvtec_loco.py
index 5280cf470e..d5274ddab9 100644
--- a/anomalib/data/mvtec_loco.py
+++ b/anomalib/data/mvtec_loco.py
@@ -376,12 +376,12 @@ def download_and_extract_mvtec_loco(root: Union[str, Path]) -> None:
def _make_dataset(
path: Path,
split: str,
- imread_strategy: str = IMREAD_STRATEGY_PRELOAD,
) -> DataFrame: # noqa D212
"""
Find the images in the given path and create a DataFrame with all the information from each sample.
Expected structure of the files in the dataset ("/" is 'path')
+ Important: notice that the groud truth masks can be in multiple files!
images: /{split}/{super_anotype}/{image_index}.png
@@ -402,11 +402,11 @@ def _make_dataset(
/ground_truth/logical_anomalies/079/000.png
/ground_truth/structural_anomalies/.../000.png
+ /ground_truth/structural_anomalies/.../001.png
...
"""
assert split in SPLITS, f"Invalid split: {split}"
- assert imread_strategy in IMREAD_STRATEGIES, f"Invalid imread strategy: {imread_strategy}"
category = path.resolve().name
assert category in CATEGORIES, f"Invalid path '{path}'. The category '{category}' must be one of {CATEGORIES}"
@@ -429,7 +429,7 @@ def _make_dataset(
def build_record(sample_path: Path):
- ret: Dict[str, Union[Path, None, str, int]] = {
+ ret: Dict[str, Union[Path, Tuple[Path, ...], None, str, int]] = {
"image_path": sample_path,
**dict(zip(("split", "super_anotype", "image_filename"), sample_path.parts[-3:])),
}
@@ -439,7 +439,7 @@ def build_record(sample_path: Path):
if super_anotype == SUPER_ANOTYPE_GOOD:
ret.update(
{
- "mask_path": None,
+ "mask_paths": None,
"label": LABEL_NORMAL,
"super_anotype": SUPER_ANOTYPE_GOOD,
"anotype": ANOTYPE_GOOD,
@@ -449,23 +449,27 @@ def build_record(sample_path: Path):
if super_anotype in (SUPER_ANOTYPE_LOGICAL, SUPER_ANOTYPE_STRUCTURAL):
- mask_path: Path = path / "ground_truth" / super_anotype / sample_path.stem / "000.png"
+ mask_paths: Tuple[Path, ...] = tuple(
+ sorted((path / "ground_truth" / super_anotype / sample_path.stem).glob("*.png"))
+ )
- assert mask_path.exists(), f"Mask file '{mask_path}' does not exist. Is the dataset corrupted?"
+ assert len(mask_paths) > 0, f"No masks found for sample '{sample_path}'. Is the dataset corrupted?"
# mask images are supposed to have only two values: 0 and GTVALUE_ANOMALY
# GTVALUE_ANOMALY \in {234, ..., 255} and is given in the paper, encoded in the mapping below
# TODO create an issue to cache this info so the mask is not read here
- gtvalue = read_mask(mask_path).astype(int).max()
+ first_mask_path = mask_paths[0]
+ gtvalue = read_mask(first_mask_path).astype(int).max()
_, anotype = _MAP_GTVALUE_2_ANOTYPE[(category, gtvalue)]
ret.update(
{
- "mask_path": mask_path,
+ "mask_paths": mask_paths,
"gtvalue": gtvalue,
"label": LABEL_ANOMALOUS,
"super_anotype": super_anotype,
"anotype": anotype,
+ "is_multimask": len(mask_paths) > 1,
}
)
@@ -476,27 +480,6 @@ def build_record(sample_path: Path):
samples = pd.DataFrame.from_records([build_record(sp) for sp in samples_paths])
- if imread_strategy == IMREAD_STRATEGY_PRELOAD:
-
- warnings.warn(
- "Preloading images into memory. "
- "If your dataset is too large, consider using another imread_strategy instead.",
- stacklevel=3,
- )
-
- logger.debug("Preloading images into memory")
- samples["image"] = samples["image_path"].map(read_image)
-
- logger.debug("Preloading masks into memory")
-
- # this is used to select the rows in the dataframe
- has_mask = ~samples["mask_path"].isnull()
-
- samples.loc[has_mask, "mask"] = samples.loc[has_mask, "mask_path"].map(
- lambda x: _binarize_mask_float(read_mask(x))
- )
- samples.loc[~has_mask, "mask"] = None
-
return samples
@@ -528,7 +511,6 @@ def __init__(
Default: ``preload``
See ``anomalib.data.mvtec_loco.IMREAD_STRATEGIES``.
- TODO add link
See examples in the repository ``anomalib/notebooks/100_datamodules/104_mvtec_loco.ipynb``.
"""
@@ -550,9 +532,50 @@ def __init__(
self.samples = _make_dataset(
path=self.category_dataset_path,
split=self.split,
- imread_strategy=self.imread_strategy,
)
+ if self.imread_strategy == IMREAD_STRATEGY_PRELOAD:
+
+ warnings.warn(
+ "Preloading images into memory. "
+ "If your dataset is too large, consider using another imread_strategy instead.",
+ stacklevel=2,
+ )
+
+ logger.debug("Preloading images into memory")
+ self.samples["image"] = self.samples["image_path"].map(read_image)
+
+ logger.debug("Preloading masks into memory")
+ # this is used to select the rows in the dataframe
+ has_mask = ~self.samples["mask_paths"].isnull()
+
+ # iterate the mask paths and read the masks returning a tuple of masks
+ self.samples.loc[has_mask, "masks"] = self.samples.loc[has_mask, "mask_paths"].map(
+ lambda tupe_of_paths: tuple(_binarize_mask_float(read_mask(mask_path)) for mask_path in tupe_of_paths)
+ )
+
+ # combine the multiple masks into a single (binary) mask
+ self.samples.loc[has_mask, "mask"] = self.samples.loc[has_mask, "masks"].map(
+ lambda masks: np.stack(masks, axis=0).sum(axis=0).clip(0, 1)
+ )
+
+ # replace the tuple of masks by a single array where each anomaly has
+ # a different value encoding an individual anomaly region
+ self.samples.loc[has_mask, "masks"] = self.samples.loc[has_mask, "masks"].map(self._sum_masks)
+
+ self.samples.loc[~has_mask, "masks"] = None
+ self.samples.loc[~has_mask, "mask"] = None
+
+ @staticmethod
+ def _sum_masks(tupe_of_masks: Tuple[np.ndarray, ...]) -> np.ndarray:
+ """Combines multiple masks into a single mask by encoding each mask with a different value and summing them."""
+ n_masks = len(tupe_of_masks)
+ # +1 is to compensate the open interval on the right
+ # expand_dims is to add the W and H dimensions, to make sure they are broadcasted
+ gtvalues = np.expand_dims(np.arange(1, n_masks + 1), (1, 2))
+ stacked_masks = np.stack(tupe_of_masks, axis=0)
+ return (gtvalues * stacked_masks).sum(axis=0)
+
@property
def category_dataset_path(self) -> Path:
"""Path to the category dataset (root/category) folder."""
@@ -573,14 +596,36 @@ def _get_image(self, index: int) -> ndarray:
raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
- def _get_mask(self, index: int) -> ndarray:
+ def _get_masks(self, index: int) -> Dict[str, ndarray]:
"""Get mask at index."""
if self.imread_strategy == IMREAD_STRATEGY_PRELOAD:
- return self.samples.iloc[index]["mask"]
+ return {
+ "masks": self.samples.iloc[index]["masks"],
+ "mask": self.samples.iloc[index]["mask"],
+ }
if self.imread_strategy == IMREAD_STRATEGY_ONTHEFLY:
- return _binarize_mask_float(read_mask(self.samples.iloc[index]["mask_path"]))
+
+ mask_paths = self.samples.iloc[index]["mask_paths"]
+ if mask_paths is None:
+ return {
+ "masks": None,
+ "mask": None,
+ }
+
+ # iterate the mask paths and read the masks returning a tuple of masks
+ masks: Tuple[np.ndarray, ...] = tuple(
+ _binarize_mask_float(read_mask(mask_path)) for mask_path in mask_paths
+ )
+
+ return {
+ # replace the tuple of masks by a single array where each anomaly has
+ # a different value encoding an individual anomaly region
+ "masks": self._sum_masks(masks),
+ # combine the multiple masks into a single (binary) mask
+ "mask": np.stack(masks, axis=0).sum(axis=0).clip(0, 1),
+ }
raise NotImplementedError(f"Imread strategy '{self.imread_strategy}' is not supported.")
@@ -618,19 +663,23 @@ def __getitem__(self, index: int) -> Union[Dict[str, Tensor], Dict[str, Union[st
if self.task != TASK_SEGMENTATION:
return item
+ mask_dict: Dict[str, ndarray]
+
# Only Anomalous (1) images has masks in MVTec LOCO AD dataset.
# Therefore, create empty mask for Normal (0) images.
if self.samples.iloc[index]["label"] == LABEL_NORMAL:
mask = np.zeros(shape=image.shape[:2]) # shape: (H, W, C)
+ mask_dict = {"mask": mask, "masks": mask}
else:
- mask = self._get_mask(index)
+ mask_dict = self._get_masks(index)
- pre_processed = self.pre_process(image=image, mask=mask)
item.update(
{
- "mask_path": str(self.samples.iloc[index]["mask_path"]),
- "mask": pre_processed["mask"],
+ "mask_paths": str(self.samples.iloc[index]["mask_paths"]),
+ # TODO CHECK IF THE DOUBLE CALL TO PREPROCESS WILL WORK WITH ALBUMENTATIONS
+ "masks": self.pre_process(image=image, mask=mask_dict["masks"])["mask"],
+ "mask": self.pre_process(image=image, mask=mask_dict["mask"])["mask"],
}
)
@@ -656,7 +705,6 @@ def __init__(
transform_config_train: Optional[Union[str, A.Compose]] = None,
test_batch_size: int = 32,
transform_config_val: Optional[Union[str, A.Compose]] = None,
- # TODO: add a parameter to specify the anomaly types and (more specifically)
) -> None:
"""Mvtec LOCO AD Lightning Data Module.
@@ -679,16 +727,10 @@ def __init__(
transform_config_val: List of pre_processing object containing albumentation compose or
config applied during validation.
- TODO add link
See examples in the repository ``anomalib/notebooks/100_datamodules/104_mvtec_loco.ipynb``.
"""
super().__init__()
- # TODO create option to get a subset of anomalies in the test
- # TODO the images are not squared here, maybe we should add warn the user if
- # the ration from image_size is too different from the original image when
- # the image size is given as an int
-
assert task in TASKS, f"Task '{task}' is not supported. Supported tasks are {TASKS}"
assert (
imread_strategy in IMREAD_STRATEGIES
diff --git a/notebooks/100_datamodules/104_mvtec_loco.ipynb b/notebooks/100_datamodules/104_mvtec_loco.ipynb
index 0f5fe9585c..9dce64e7da 100644
--- a/notebooks/100_datamodules/104_mvtec_loco.ipynb
+++ b/notebooks/100_datamodules/104_mvtec_loco.ipynb
@@ -9,7 +9,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -44,7 +44,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -62,9 +62,221 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\u001b[0;31mInit signature:\u001b[0m \u001b[0mMVTecLOCODataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mSource:\u001b[0m \n",
+ "\u001b[0;32mclass\u001b[0m \u001b[0mMVTecLOCODataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mVisionDataset\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"MVTec LOCO AD PyTorch Dataset.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mroot\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mPath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mcategory\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0msplit\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mpre_process\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mPreProcessor\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTASK_SEGMENTATION\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimread_strategy\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mIMREAD_STRATEGY_PRELOAD\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Mvtec LOCO AD Dataset class.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Args:\u001b[0m\n",
+ "\u001b[0;34m root: Path to the MVTec LOCO AD dataset root folder.\u001b[0m\n",
+ "\u001b[0;34m category: Name of the MVTec LOCO AD category (there are 5).\u001b[0m\n",
+ "\u001b[0;34m See ``anomalib.data.mvtec_loco.CATEGORIES``.\u001b[0m\n",
+ "\u001b[0;34m split: 'train', 'validation' or 'test'\u001b[0m\n",
+ "\u001b[0;34m See anomalib.data.mvtec_loco.SPLITS.\u001b[0m\n",
+ "\u001b[0;34m pre_process: List of pre_processing object containing albumentation compose or config.\u001b[0m\n",
+ "\u001b[0;34m task: ``classification`` or ``segmentation``\u001b[0m\n",
+ "\u001b[0;34m Default: ``segmentation``\u001b[0m\n",
+ "\u001b[0;34m ``anomalib.data.mvtec_loco.TASKS``.\u001b[0m\n",
+ "\u001b[0;34m imread_strategy: When should images be read into memory?\u001b[0m\n",
+ "\u001b[0;34m Default: ``preload``\u001b[0m\n",
+ "\u001b[0;34m See ``anomalib.data.mvtec_loco.IMREAD_STRATEGIES``.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m TODO add link\u001b[0m\n",
+ "\u001b[0;34m See examples in the repository ``anomalib/notebooks/100_datamodules/104_mvtec_loco.ipynb``.\u001b[0m\n",
+ "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msplit\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mSPLITS\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34mf\"Split '{split}' is not supported. Supported splits are {SPLITS}\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mtask\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mTASKS\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34mf\"Task '{task}' is not supported. Supported tasks are {TASKS}\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimread_strategy\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mIMREAD_STRATEGIES\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34mf\"Imread strategy '{imread_strategy}' is not supported. Supported imread strategies are {IMREAD_STRATEGIES}\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroot\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPath\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0mroot\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcategory\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcategory\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msplit\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtask\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpre_process\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpre_process\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mimread_strategy\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_make_dataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcategory_dataset_path\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0msplit\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mIMREAD_STRATEGY_PRELOAD\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mwarnings\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwarn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"Preloading images into memory. \"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"If your dataset is too large, consider using another imread_strategy instead.\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mstacklevel\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Preloading images into memory\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"image\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"image_path\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mread_image\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Preloading masks into memory\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# this is used to select the rows in the dataframe\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mhas_mask\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m~\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"mask_paths\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0misnull\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# iterate the mask paths and read the masks returning a tuple of masks\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mhas_mask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mhas_mask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"mask_paths\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mtupe_of_paths\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_binarize_mask_float\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mread_mask\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmask_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mmask_path\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtupe_of_paths\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# combine the multiple masks into a single (binary) mask\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mhas_mask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mhas_mask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mmasks\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmasks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# replace the tuple of masks by a single array where each anomaly has\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# a different value encoding an individual anomaly region\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mhas_mask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mhas_mask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_sum_masks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m~\u001b[0m\u001b[0mhas_mask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m~\u001b[0m\u001b[0mhas_mask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mstaticmethod\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_sum_masks\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtupe_of_masks\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mTuple\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mndarray\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"combines multiple masks into a single mask by encoding each mask with a different value and summing them.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mn_masks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtupe_of_masks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# +1 is to compensate the open interval on the right\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# expand_dims is to add the W and H dimensions, to make sure they are broadcasted\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mgtvalues\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexpand_dims\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mn_masks\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mstacked_masks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtupe_of_masks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mgtvalues\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mstacked_masks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mproperty\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mcategory_dataset_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mPath\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Path to the category dataset (root/category) folder.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroot\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcategory\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__len__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get length of the dataset.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_get_image\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mndarray\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get image at index.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mIMREAD_STRATEGY_PRELOAD\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"image\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mIMREAD_STRATEGY_ONTHEFLY\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mread_image\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"image_path\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mNotImplementedError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Imread strategy '{self.imread_strategy}' is not supported.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_get_masks\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mndarray\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get mask at index.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mIMREAD_STRATEGY_PRELOAD\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mIMREAD_STRATEGY_ONTHEFLY\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mmask_paths\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"mask_paths\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mmask_paths\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# iterate the mask paths and read the masks returning a tuple of masks\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mmasks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_binarize_mask_float\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mread_mask\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmask_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mmask_path\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mmask_paths\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# replace the tuple of masks by a single array where each anomaly has\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# a different value encoding an individual anomaly region \u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_sum_masks\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmasks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# combine the multiple masks into a single (binary) mask\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstack\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmasks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mNotImplementedError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Imread strategy '{self.imread_strategy}' is not supported.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__getitem__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTensor\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTensor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get dataset item for the index ``index``.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Args:\u001b[0m\n",
+ "\u001b[0;34m index (int): Index to get the item.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Returns:\u001b[0m\n",
+ "\u001b[0;34m Union[Dict[str, Tensor], Dict[str, Union[str, Tensor]]]: Dict of image tensor during training.\u001b[0m\n",
+ "\u001b[0;34m Otherwise, Dict containing image path, image tensor, label, anomaly type and,\u001b[0m\n",
+ "\u001b[0;34m if it is segmentation task, mask path and mask tensor.\u001b[0m\n",
+ "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTensor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_image\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mpre_processed\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpre_process\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimage\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mimage\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mitem\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"image\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mpre_processed\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"image\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mSPLIT_VALIDATION\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mSPLIT_TEST\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"label\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"label\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"image_path\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"image_path\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"anotype\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"anotype\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"super_anotype\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"super_anotype\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtask\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0mTASK_SEGMENTATION\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mmask_dict\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mndarray\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# Only Anomalous (1) images has masks in MVTec LOCO AD dataset.\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# Therefore, create empty mask for Normal (0) images.\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"label\"\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mLABEL_NORMAL\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mmask\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzeros\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mimage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshape\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# shape: (H, W, C)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mmask_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mmask\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mmask\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mmask_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_masks\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"mask_paths\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msamples\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miloc\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"mask_paths\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# TODO CHECK IF THE DOUBLE CALL TO PREPROCESS WILL WORK WITH ALBUMENTATIONS\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpre_process\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimage\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mimage\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmask\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmask_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"masks\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpre_process\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimage\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mimage\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmask\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmask_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"mask\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mFile:\u001b[0m ~/repos/anomalib/anomalib/data/mvtec_loco.py\n",
+ "\u001b[0;31mType:\u001b[0m type\n",
+ "\u001b[0;31mSubclasses:\u001b[0m \n"
+ ]
+ }
+ ],
"source": [
"MVTecLOCODataset??"
]
@@ -78,16 +290,154 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\u001b[0;31mInit signature:\u001b[0m\n",
+ "\u001b[0mPreProcessor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0malbumentations\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomposition\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mNoneType\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimage_size\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTuple\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mNoneType\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mto_tensor\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mbool\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mSource:\u001b[0m \n",
+ "\u001b[0;32mclass\u001b[0m \u001b[0mPreProcessor\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Applies pre-processing and data augmentations to the input and returns the transformed output.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Output could be either numpy ndarray or torch tensor.\u001b[0m\n",
+ "\u001b[0;34m When `PreProcessor` class is used for training, the output would be `torch.Tensor`.\u001b[0m\n",
+ "\u001b[0;34m For the inference it returns a numpy array.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Args:\u001b[0m\n",
+ "\u001b[0;34m config (Optional[Union[str, A.Compose]], optional): Transformation configurations.\u001b[0m\n",
+ "\u001b[0;34m When it is ``None``, ``PreProcessor`` only applies resizing. When it is ``str``\u001b[0m\n",
+ "\u001b[0;34m it loads the config via ``albumentations`` deserialisation methos . Defaults to None.\u001b[0m\n",
+ "\u001b[0;34m image_size (Optional[Union[int, Tuple[int, int]]], optional): When there is no config,\u001b[0m\n",
+ "\u001b[0;34m ``image_size`` resizes the image. Defaults to None.\u001b[0m\n",
+ "\u001b[0;34m to_tensor (bool, optional): Boolean to check whether the augmented image is transformed\u001b[0m\n",
+ "\u001b[0;34m into a tensor or not. Defaults to True.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Examples:\u001b[0m\n",
+ "\u001b[0;34m >>> import skimage\u001b[0m\n",
+ "\u001b[0;34m >>> image = skimage.data.astronaut()\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m >>> pre_processor = PreProcessor(image_size=256, to_tensor=False)\u001b[0m\n",
+ "\u001b[0;34m >>> output = pre_processor(image=image)\u001b[0m\n",
+ "\u001b[0;34m >>> output[\"image\"].shape\u001b[0m\n",
+ "\u001b[0;34m (256, 256, 3)\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m >>> pre_processor = PreProcessor(image_size=256, to_tensor=True)\u001b[0m\n",
+ "\u001b[0;34m >>> output = pre_processor(image=image)\u001b[0m\n",
+ "\u001b[0;34m >>> output[\"image\"].shape\u001b[0m\n",
+ "\u001b[0;34m torch.Size([3, 256, 256])\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Transforms could be read from albumentations Compose object.\u001b[0m\n",
+ "\u001b[0;34m >>> import albumentations as A\u001b[0m\n",
+ "\u001b[0;34m >>> from albumentations.pytorch import ToTensorV2\u001b[0m\n",
+ "\u001b[0;34m >>> config = A.Compose([A.Resize(512, 512), ToTensorV2()])\u001b[0m\n",
+ "\u001b[0;34m >>> pre_processor = PreProcessor(config=config, to_tensor=False)\u001b[0m\n",
+ "\u001b[0;34m >>> output = pre_processor(image=image)\u001b[0m\n",
+ "\u001b[0;34m >>> output[\"image\"].shape\u001b[0m\n",
+ "\u001b[0;34m (512, 512, 3)\u001b[0m\n",
+ "\u001b[0;34m >>> type(output[\"image\"])\u001b[0m\n",
+ "\u001b[0;34m numpy.ndarray\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Transforms could be deserialized from a yaml file.\u001b[0m\n",
+ "\u001b[0;34m >>> transforms = A.Compose([A.Resize(1024, 1024), ToTensorV2()])\u001b[0m\n",
+ "\u001b[0;34m >>> A.save(transforms, \"/tmp/transforms.yaml\", data_format=\"yaml\")\u001b[0m\n",
+ "\u001b[0;34m >>> pre_processor = PreProcessor(config=\"/tmp/transforms.yaml\")\u001b[0m\n",
+ "\u001b[0;34m >>> output = pre_processor(image=image)\u001b[0m\n",
+ "\u001b[0;34m >>> output[\"image\"].shape\u001b[0m\n",
+ "\u001b[0;34m torch.Size([3, 1024, 1024])\u001b[0m\n",
+ "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimage_size\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTuple\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mto_tensor\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mbool\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconfig\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mimage_size\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_tensor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mto_tensor\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransforms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_transforms\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mget_transforms\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get transforms from config or image size.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Returns:\u001b[0m\n",
+ "\u001b[0;34m A.Compose: List of albumentation transformations to apply to the\u001b[0m\n",
+ "\u001b[0;34m input image.\u001b[0m\n",
+ "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"Both config and image_size cannot be `None`. \"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"Provide either config file to de-serialize transforms \"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"or image_size to get the default transformations\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransforms\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mheight\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwidth\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_height_and_width\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransforms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mResize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheight\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheight\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwidth\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0malways_apply\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mNormalize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmean\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.485\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.456\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.406\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstd\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.229\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.224\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0.225\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mToTensorV2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransforms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata_format\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"yaml\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransforms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"config could be either ``str`` or ``A.Compose``\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_tensor\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtransforms\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mToTensorV2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransforms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtransforms\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# always resize to specified image size\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0many\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtransform\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mResize\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mtransform\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtransforms\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mheight\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwidth\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_get_height_and_width\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransforms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mResize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheight\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mheight\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mwidth\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0malways_apply\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtransforms\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mtransforms\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__call__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Return transformed arguments.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransforms\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_get_height_and_width\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mTuple\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Extract height and width from image size attribute.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"``image_size`` could be either int or Tuple[int, int]\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mFile:\u001b[0m ~/repos/anomalib/anomalib/pre_processing/pre_process.py\n",
+ "\u001b[0;31mType:\u001b[0m type\n",
+ "\u001b[0;31mSubclasses:\u001b[0m \n"
+ ]
+ }
+ ],
"source": [
"PreProcessor??"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@@ -103,9 +453,148 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 6,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/tmp/ipykernel_115879/3968494898.py:2: UserWarning: Preloading images into memory. If your dataset is too large, consider using another imread_strategy instead.\n",
+ " mvtec_loco_dataset_classification_train = MVTecLOCODataset(\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " image_path | \n",
+ " split | \n",
+ " super_anotype | \n",
+ " image_filename | \n",
+ " mask_paths | \n",
+ " label | \n",
+ " anotype | \n",
+ " image | \n",
+ " masks | \n",
+ " mask | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 000.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[11, 11, 12], [11, 11, 12], [11, 11, 13], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ " 1 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 001.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[12, 11, 11], [12, 10, 11], [12, 10, 12], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ " 2 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 002.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[14, 12, 12], [13, 12, 13], [12, 12, 13], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ " 3 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 003.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[12, 11, 12], [12, 11, 12], [11, 11, 12], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ " 4 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 004.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[12, 12, 13], [12, 11, 14], [11, 11, 14], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " image_path split super_anotype \\\n",
+ "0 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "1 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "2 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "3 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "4 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "\n",
+ " image_filename mask_paths label anotype \\\n",
+ "0 000.png None 0 good \n",
+ "1 001.png None 0 good \n",
+ "2 002.png None 0 good \n",
+ "3 003.png None 0 good \n",
+ "4 004.png None 0 good \n",
+ "\n",
+ " image masks mask \n",
+ "0 [[[11, 11, 12], [11, 11, 12], [11, 11, 13], [1... None None \n",
+ "1 [[[12, 11, 11], [12, 10, 11], [12, 10, 12], [1... None None \n",
+ "2 [[[14, 12, 12], [13, 12, 13], [12, 12, 13], [1... None None \n",
+ "3 [[[12, 11, 12], [12, 11, 12], [11, 11, 12], [1... None None \n",
+ "4 [[[12, 12, 13], [12, 11, 14], [11, 11, 14], [1... None None "
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"# MVTec LOCO Classification Train Set\n",
"mvtec_loco_dataset_classification_train = MVTecLOCODataset(\n",
@@ -120,9 +609,30 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 7,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image'])"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([3, 100, 170])"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"sample = mvtec_loco_dataset_classification_train[0]\n",
"sample.keys()\n",
@@ -138,9 +648,18 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 8,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/tmp/ipykernel_115879/3604180834.py:2: UserWarning: Preloading images into memory. If your dataset is too large, consider using another imread_strategy instead.\n",
+ " mvtec_loco_dataset_classification_test = MVTecLOCODataset(\n"
+ ]
+ }
+ ],
"source": [
"# MVTec Classification Test Set\n",
"mvtec_loco_dataset_classification_test = MVTecLOCODataset(\n",
@@ -154,9 +673,60 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 9,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image', 'label', 'image_path', 'anotype', 'super_anotype'])"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([3, 100, 170])"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "'../../datasets/MVTecLOCO/pushpins/test/good/000.png'"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "0"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "('good', 'good')"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"sample = mvtec_loco_dataset_classification_test[0]\n",
"sample.keys()\n",
@@ -175,9 +745,60 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 10,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image', 'label', 'image_path', 'anotype', 'super_anotype'])"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([3, 100, 170])"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "'../../datasets/MVTecLOCO/pushpins/test/structural_anomalies/080.png'"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "1"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "('structural_anomalies', 'front_bent')"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"sample = mvtec_loco_dataset_classification_test[-1]\n",
"sample.keys()\n",
@@ -198,9 +819,148 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 11,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/tmp/ipykernel_115879/3202540915.py:2: UserWarning: Preloading images into memory. If your dataset is too large, consider using another imread_strategy instead.\n",
+ " mvtec_loco_dataset_segmentation_train = MVTecLOCODataset(\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " image_path | \n",
+ " split | \n",
+ " super_anotype | \n",
+ " image_filename | \n",
+ " mask_paths | \n",
+ " label | \n",
+ " anotype | \n",
+ " image | \n",
+ " masks | \n",
+ " mask | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 000.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[11, 11, 12], [11, 11, 12], [11, 11, 13], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ " 1 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 001.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[12, 11, 11], [12, 10, 11], [12, 10, 12], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ " 2 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 002.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[14, 12, 12], [13, 12, 13], [12, 12, 13], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ " 3 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 003.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[12, 11, 12], [12, 11, 12], [11, 11, 12], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ " 4 | \n",
+ " ../../datasets/MVTecLOCO/pushpins/train/good/0... | \n",
+ " train | \n",
+ " good | \n",
+ " 004.png | \n",
+ " None | \n",
+ " 0 | \n",
+ " good | \n",
+ " [[[12, 12, 13], [12, 11, 14], [11, 11, 14], [1... | \n",
+ " None | \n",
+ " None | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " image_path split super_anotype \\\n",
+ "0 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "1 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "2 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "3 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "4 ../../datasets/MVTecLOCO/pushpins/train/good/0... train good \n",
+ "\n",
+ " image_filename mask_paths label anotype \\\n",
+ "0 000.png None 0 good \n",
+ "1 001.png None 0 good \n",
+ "2 002.png None 0 good \n",
+ "3 003.png None 0 good \n",
+ "4 004.png None 0 good \n",
+ "\n",
+ " image masks mask \n",
+ "0 [[[11, 11, 12], [11, 11, 12], [11, 11, 13], [1... None None \n",
+ "1 [[[12, 11, 11], [12, 10, 11], [12, 10, 12], [1... None None \n",
+ "2 [[[14, 12, 12], [13, 12, 13], [12, 12, 13], [1... None None \n",
+ "3 [[[12, 11, 12], [12, 11, 12], [11, 11, 12], [1... None None \n",
+ "4 [[[12, 12, 13], [12, 11, 14], [11, 11, 14], [1... None None "
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"# MVTec LOCO Segmentation Train Set\n",
"mvtec_loco_dataset_segmentation_train = MVTecLOCODataset(\n",
@@ -215,9 +975,48 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 12,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/tmp/ipykernel_115879/3616612057.py:2: UserWarning: Preloading images into memory. If your dataset is too large, consider using another imread_strategy instead.\n",
+ " mvtec_loco_dataset_segmentation_test = MVTecLOCODataset(\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image', 'label', 'image_path', 'anotype', 'super_anotype', 'mask_paths', 'masks', 'mask'])"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([3, 100, 170])"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([100, 170])"
+ ]
+ },
+ "execution_count": 12,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"# MVTec LOCO Segmentation Test Set\n",
"mvtec_loco_dataset_segmentation_test = MVTecLOCODataset(\n",
@@ -242,16 +1041,290 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 13,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "img = ToPILImage()(Denormalize()(sample[\"image\"].clone()))\n",
+ "msk = ToPILImage()(sample[\"mask\"]).convert(\"RGB\")\n",
+ "\n",
+ "Image.fromarray(np.vstack((np.array(img), np.array(msk))))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "An example of structural anomaly"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image', 'label', 'image_path', 'anotype', 'super_anotype', 'mask_paths', 'masks', 'mask'])"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "'structural_anomalies'"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "'broken'"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "sample = mvtec_loco_dataset_segmentation_test[250]\n",
+ "sample.keys()\n",
+ "sample[\"super_anotype\"]\n",
+ "sample[\"anotype\"]\n",
+ "img = ToPILImage()(Denormalize()(sample[\"image\"].clone()))\n",
+ "msk = ToPILImage()(sample[\"mask\"]).convert(\"RGB\")\n",
+ "\n",
+ "Image.fromarray(np.vstack((np.array(img), np.array(msk))))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "An example of logical anomaly"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image', 'label', 'image_path', 'anotype', 'super_anotype', 'mask_paths', 'masks', 'mask'])"
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "'logical_anomalies'"
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "'missing_separator'"
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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y/YX+3WoNQJBekDJdOceCNaDGjeY4b5y0tCyIvCA9to7ojMbBjexo5eeWGLywpd+/F+97V0v+vhP/q2/+ge6ehIdYY3HMdJxp9YNPjApK4auwZ6xqPz2X7nwM99wGCaYzLonl3d/b3neq3eWg19yaGa9Tjc7bJbVggYg21tbQGNmKOyVr/iKez965wfuvhuip06dUekIMWUEiNZgg2zMggLJZ6ErCqQh11mTUXRFO8ik01kv231/5Mjxz/7cT13WOptuvPc977vv4dNOEStQpRR201R8sL5XCCHaqdoMClH2pahiop1NrFeZFu0F08K+n5ihA2ZbW9NONyd9sS7Cd4dh98J5hEA9gqoS0RUdqhNSgApYRKggTCVCihroESq9AK2mBlO6lGKw6WxSzDY606J1MR98ubdXBbWii4hlZEHanQALYwlAXKsiIiChDIVWCIJKhBZiEDc1VHGlWpHNftbPShAmvrNfT156xQP33AXV+WIeQUuEmxC4QSopcEAqqc4ANFhRVQvqslf26mfmvjvUvaEiuCGc9JPtzY0XPfum533RN6ioe4iiNGB1JI5NijGWIBY+AFAND3jkXPaApXiXo2l8BDwhrpwWFQqPUDAwtG6HTGv25cELL3zx+Z39xdYR/8u3vuehRx9LojCJaKUOGFAbuJ87vavYmP4lfVuSDMTUwBCMYmKmitf96L9fLvah+OGf/dUH7r8PK6GQJpCRbLwQM9KzetRWqQgabSH3EM85IglzQdRw05Of/i1f/jLUxWxz899824+iYc5NggWtljN+3/jYSTfLQInIXqRG2gfRAjDT4ycuedYN137mp71kPh/K1F79Ez8L02E5ECBVJKLJpCjdiehKN1RPmoiKmoSIFUVn2lsppqWbWCkbW9su3Nreuur4sSuv2WI3Od5XoHF6ARQiRLSRt9SUEWaAhlcQHtlZuOKSgJJzIVqojRF7yvUCVIweFxn2I/daQezvPXbf/btx/wOnz59N1nDWg1sO0sicbEmAgEwdNBISPqB1ArkA9NZkagpIzh792u/88V/76e/+9p/59Qfuvw8AII2UGsgmQwm4Au6mCLikrlAiZmwJeDLpahaZPBFogPbhD7z/9353+IIv/LSv/vYfybarkcobDVzLEAgN7iYgMQQzjxxULBKAFCeR/SRIyKny0Ycfe8vZM09/0rVHTxz97h94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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
+ "sample = mvtec_loco_dataset_segmentation_test[200]\n",
+ "sample.keys()\n",
+ "sample[\"super_anotype\"]\n",
+ "sample[\"anotype\"]\n",
"img = ToPILImage()(Denormalize()(sample[\"image\"].clone()))\n",
"msk = ToPILImage()(sample[\"mask\"]).convert(\"RGB\")\n",
"\n",
"Image.fromarray(np.vstack((np.array(img), np.array(msk))))"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "An example of logical anomaly with multiple anomalous regions\n",
+ "\n",
+ "**Important**: the ground truth can have multiple masks (one for each logical anomalous region).\n",
+ "\n",
+ "The **union** of the (multiple) masks is conveniently returned in the field `\"mask\"`, but the individual `\"masks\"` (with **s**!) should be considered for correct evaluation!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image', 'label', 'image_path', 'anotype', 'super_anotype', 'mask_paths', 'masks', 'mask'])"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "'logical_anomalies'"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "'additional_1_pushpin'"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "tensor([0., 1.], dtype=torch.float64)"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "array([ 0, 255], dtype=uint8)"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "tensor([0., 1., 2.], dtype=torch.float64)"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "array([ 0, 56, 156], dtype=uint8)"
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 38,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "sample = mvtec_loco_dataset_segmentation_test[150]\n",
+ "sample.keys()\n",
+ "sample[\"super_anotype\"]\n",
+ "sample[\"anotype\"]\n",
+ "img = np.array(ToPILImage()(Denormalize()(sample[\"image\"].clone())))\n",
+ "msk = np.array(ToPILImage()(sample[\"mask\"]).convert(\"RGB\"))\n",
+ "\n",
+ "# !!!!!\n",
+ "# \"100 * \" is artifically increasing the gtvalue of the mask to make it more visible\n",
+ "msks = np.array(ToPILImage()(100 * sample[\"masks\"]).convert(\"RGB\"))\n",
+ "# !!!!!\n",
+ "\n",
+ "sample[\"mask\"].unique()\n",
+ "np.unique(msk)\n",
+ "\n",
+ "sample[\"masks\"].unique()\n",
+ "np.unique(msks)\n",
+ "\n",
+ "Image.fromarray(np.vstack((img, msk)))\n",
+ "Image.fromarray(np.vstack((img, msks)))"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -265,16 +1338,221 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 39,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\u001b[0;31mInit signature:\u001b[0m\n",
+ "\u001b[0mMVTecLOCO\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mroot\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mcategory\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'segmentation'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimread_strategy\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'preload'\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimage_size\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTuple\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mNoneType\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mnum_workers\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m8\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtrain_batch_size\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m32\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransform_config_train\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0malbumentations\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomposition\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mNoneType\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtest_batch_size\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m32\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransform_config_val\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0malbumentations\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcomposition\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mNoneType\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mSource:\u001b[0m \n",
+ "\u001b[0;32mclass\u001b[0m \u001b[0mMVTecLOCO\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mLightningDataModule\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"MVTec LOCO AD Lightning Data Module.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# todo correct inconsistency: `transform_config_*val*` used for val and\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# test set, but `*test*_batch_size` used for val and set\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mroot\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mcategory\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTASK_SEGMENTATION\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimread_strategy\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mIMREAD_STRATEGY_PRELOAD\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimage_size\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTuple\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mnum_workers\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m8\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtrain_batch_size\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m32\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransform_config_train\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtest_batch_size\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mint\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m32\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtransform_config_val\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mUnion\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mA\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mCompose\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# TODO: add a parameter to specify the anomaly types and (more specifically)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Mvtec LOCO AD Lightning Data Module.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Args:\u001b[0m\n",
+ "\u001b[0;34m root: Path to the MVTec LOCO AD dataset root folder.\u001b[0m\n",
+ "\u001b[0;34m category: Name of the MVTec LOCO AD category (there are 5).\u001b[0m\n",
+ "\u001b[0;34m See ``anomalib.data.mvtec_loco.CATEGORIES``.\u001b[0m\n",
+ "\u001b[0;34m task: ``classification`` or ``segmentation``\u001b[0m\n",
+ "\u001b[0;34m Default: ``segmentation``\u001b[0m\n",
+ "\u001b[0;34m See ``anomalib.data.mvtec_loco.TASKS``.\u001b[0m\n",
+ "\u001b[0;34m imread_strategy: When should images be read into memory?\u001b[0m\n",
+ "\u001b[0;34m Default: ``preload``\u001b[0m\n",
+ "\u001b[0;34m See ``anomalib.data.mvtec_loco.IMREAD_STRATEGIES``.\u001b[0m\n",
+ "\u001b[0;34m image_size: Images are resized to `image_size` (HEIGHT, WIDTH), or (SIZE, SIZE) if a single value is given.\u001b[0m\n",
+ "\u001b[0;34m num_workers: Number of workers.\u001b[0m\n",
+ "\u001b[0;34m train_batch_size: Training batch size.\u001b[0m\n",
+ "\u001b[0;34m transform_config_train: List of pre_processing object containing albumentation compose or\u001b[0m\n",
+ "\u001b[0;34m config applied during training.\u001b[0m\n",
+ "\u001b[0;34m test_batch_size: Testing batch size.\u001b[0m\n",
+ "\u001b[0;34m transform_config_val: List of pre_processing object containing albumentation compose or\u001b[0m\n",
+ "\u001b[0;34m config applied during validation.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m TODO add link\u001b[0m\n",
+ "\u001b[0;34m See examples in the repository ``anomalib/notebooks/100_datamodules/104_mvtec_loco.ipynb``.\u001b[0m\n",
+ "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# TODO create option to get a subset of anomalies in the test\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# TODO the images are not squared here, maybe we should add warn the user if\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# the ration from image_size is too different from the original image when\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# the image size is given as an int\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mtask\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mTASKS\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34mf\"Task '{task}' is not supported. Supported tasks are {TASKS}\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimread_strategy\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mIMREAD_STRATEGIES\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34mf\"Imread strategy '{imread_strategy}' is not supported. Supported imread strategies are {IMREAD_STRATEGIES}\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroot\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mroot\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mPath\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0mPath\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcategory\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcategory\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform_config_train\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtransform_config_train\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform_config_val\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtransform_config_val\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mimage_size\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpre_process_train\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPreProcessor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform_config_train\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mimage_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpre_process_val\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPreProcessor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mconfig\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform_config_val\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mimage_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_batch_size\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtrain_batch_size\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtest_batch_size\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtest_batch_size\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnum_workers\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtask\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mimread_strategy\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_data\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mDataset\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtest_data\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mDataset\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mval_data\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mDataset\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minference_data\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mDataset\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mproperty\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mcategory_dataset_path\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mPath\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Path to the category dataset (root/category) folder.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroot\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcategory\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mprepare_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Download the dataset if not available.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcategory_dataset_path\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_dir\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Found the dataset.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mdownload_and_extract_mvtec_loco\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msetup\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstage\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Setup train, validation and test data.\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m Args:\u001b[0m\n",
+ "\u001b[0;34m stage: Optional[str]: fit/validate/test/predict stages. (Default value = None = fit)\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m \"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;31m# pylint: disable=consider-using-f-string\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minfo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Setting up %s dataset.\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mstage\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mTrainerFn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFITTING\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mstage\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mTrainerFn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mFITTING\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"train_data\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Train data already exists. Skipping setup.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_data\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMVTecLOCODataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mroot\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mcategory\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcategory\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0msplit\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mSPLIT_TRAIN\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mpre_process\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpre_process_train\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtask\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimread_strategy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mstage\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mTrainerFn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mVALIDATING\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"val_data\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Validation data already exists. Skipping setup.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mval_data\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMVTecLOCODataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mroot\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mcategory\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcategory\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mpre_process\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpre_process_val\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0msplit\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mSPLIT_VALIDATION\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtask\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimread_strategy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mstage\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mTrainerFn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTESTING\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"test_data\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Test data already exists. Skipping setup.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtest_data\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mMVTecLOCODataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mroot\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mcategory\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcategory\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mpre_process\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpre_process_val\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0msplit\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mSPLIT_TEST\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mtask\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtask\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mimread_strategy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimread_strategy\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mstage\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mTrainerFn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPREDICTING\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"inference_data\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mlogger\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdebug\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Inference data already exists. Skipping setup.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minference_data\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mInferenceDataset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mroot\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mimage_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimage_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtransform_config\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransform_config_val\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtrain_dataloader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mTRAIN_DATALOADERS\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get train dataloader.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"train_data\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Train data not setup. Did you run `datamodule.setup('fit')`?\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mDataLoader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshuffle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtrain_batch_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum_workers\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mval_dataloader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mEVAL_DATALOADERS\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get validation dataloader.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"val_data\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Validation data not setup. Did you run `datamodule.setup('validate')`?\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mDataLoader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mval_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshuffle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtest_batch_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum_workers\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mtest_dataloader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mEVAL_DATALOADERS\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get test dataloader.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"test_data\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Test data not setup. Did you run `datamodule.setup('test')`?\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mDataLoader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtest_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshuffle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtest_batch_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum_workers\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mpredict_dataloader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mEVAL_DATALOADERS\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m\"\"\"Get predict dataloader.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"inference_data\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Inference data not setup. Did you run `datamodule.setup('predict')`?\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mDataLoader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minference_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mshuffle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtest_batch_size\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnum_workers\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum_workers\u001b[0m\u001b[0;34m\u001b[0m\n",
+ "\u001b[0;34m\u001b[0m \u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mFile:\u001b[0m ~/repos/anomalib/anomalib/data/mvtec_loco.py\n",
+ "\u001b[0;31mType:\u001b[0m type\n",
+ "\u001b[0;31mSubclasses:\u001b[0m \n"
+ ]
+ }
+ ],
"source": [
"MVTecLOCO??"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 40,
"metadata": {},
"outputs": [],
"source": [
@@ -294,9 +1572,38 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 41,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/jcasagrandebertoldo/repos/anomalib/anomalib/data/mvtec_loco.py:794: UserWarning: Preloading images into memory. If your dataset is too large, consider using another imread_strategy instead.\n",
+ " self.train_data = MVTecLOCODataset(\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image'])"
+ ]
+ },
+ "execution_count": 41,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([32, 3, 200, 340])"
+ ]
+ },
+ "execution_count": 41,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"# Train images\n",
"\n",
@@ -310,9 +1617,60 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 44,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image', 'label', 'image_path', 'anotype', 'super_anotype', 'mask_paths', 'masks', 'mask'])"
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([32, 3, 200, 340])"
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([32, 200, 340])"
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([32, 200, 340])"
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "('good', 'good')"
+ ]
+ },
+ "execution_count": 44,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"# Validation images\n",
"mvtec_datamodule.setup(\"validate\")\n",
@@ -320,14 +1678,98 @@
"data.keys()\n",
"data[\"image\"].shape\n",
"data[\"mask\"].shape\n",
+ "data[\"masks\"].shape\n",
"data[\"super_anotype\"][0], data[\"anotype\"][0]"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 46,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "dict_keys(['image', 'label', 'image_path', 'anotype', 'super_anotype', 'mask_paths', 'masks', 'mask'])"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([32, 3, 200, 340])"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([32, 200, 340])"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "tensor([0., 1.], dtype=torch.float64)"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([32, 200, 340])"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "tensor([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13.,\n",
+ " 14., 15.], dtype=torch.float64)"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "text/plain": [
+ "('logical_anomalies', 'additional_1_pushpin')"
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 46,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
"# Test images\n",
"mvtec_datamodule.setup(\"test\")\n",
@@ -338,6 +1780,9 @@
"data.keys()\n",
"data[\"image\"].shape\n",
"data[\"mask\"].shape\n",
+ "data[\"mask\"].unique()\n",
+ "data[\"masks\"].shape\n",
+ "data[\"masks\"].unique()\n",
"data[\"super_anotype\"][0], data[\"anotype\"][0]\n",
"\n",
"img = ToPILImage()(Denormalize()(data[\"image\"][0].clone()))\n",
@@ -345,23 +1790,6 @@
"\n",
"Image.fromarray(np.vstack((np.array(img), np.array(msk))))"
]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "TODO: show that the ground truth is divided in multiple images\n",
- "\n",
- "TODO: create issue to correct docs in mvtec, e.g. it should not give example in the docstring but send the user\n",
- " to the notebooks (more maintainable)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As can be seen above, creating the dataloaders are pretty straghtforward, which could be directly used for training/testing/inference."
- ]
}
],
"metadata": {
From 49a5a5c11b66f9ffed73f04dfc5dc9c31a74a910 Mon Sep 17 00:00:00 2001
From: jpcbertoldo <24547377+jpcbertoldo@users.noreply.github.com>
Date: Sun, 11 Sep 2022 14:22:13 +0200
Subject: [PATCH 08/38] add unit tests for mvtec loco
---
anomalib/data/mvtec_loco.py | 32 ----------
tests/pre_merge/datasets/test_dataset.py | 77 +++++++++++++++++++++++-
2 files changed, 75 insertions(+), 34 deletions(-)
diff --git a/anomalib/data/mvtec_loco.py b/anomalib/data/mvtec_loco.py
index d5274ddab9..1dd8f6cf3c 100644
--- a/anomalib/data/mvtec_loco.py
+++ b/anomalib/data/mvtec_loco.py
@@ -861,35 +861,3 @@ def predict_dataloader(self) -> EVAL_DATALOADERS:
return DataLoader(
self.inference_data, shuffle=False, batch_size=self.test_batch_size, num_workers=self.num_workers
)
-
-
-# next
-# correct the multi-image ground truth
-# then show it in the notebook
-# then create unit tests
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
-# next
diff --git a/tests/pre_merge/datasets/test_dataset.py b/tests/pre_merge/datasets/test_dataset.py
index 06d9629b45..3b93d190fd 100644
--- a/tests/pre_merge/datasets/test_dataset.py
+++ b/tests/pre_merge/datasets/test_dataset.py
@@ -6,12 +6,44 @@
import pytest
from anomalib.config import update_input_size_config
-from anomalib.data import BTech, Folder, MVTec, get_datamodule
+from anomalib.data import BTech, Folder, MVTec, MVTecLOCO, get_datamodule, mvtec_loco
from anomalib.pre_processing.transforms import Denormalize, ToNumpy
from tests.helpers.config import get_test_configurable_parameters
from tests.helpers.dataset import TestDataset, get_dataset_path
+@pytest.fixture
+def mvtec_loco_data_module(request):
+ datamodule = MVTecLOCO(
+ root=get_dataset_path(dataset="MVTecLOCO"),
+ category="pushpins",
+ task="segmentation",
+ image_size=(100, 170), # 10x smaller than original
+ train_batch_size=1,
+ test_batch_size=1,
+ num_workers=0,
+ imread_strategy=request.param.get("imread_strategy", mvtec_loco.IMREAD_STRATEGY_ONTHEFLY),
+ transform_config_train=None,
+ transform_config_val=None,
+ )
+ datamodule.prepare_data()
+ datamodule.setup("fit")
+ datamodule.setup("validate")
+ datamodule.setup("test")
+
+ yield datamodule
+
+
+MVTEC_LOCO_PARAMS_ALL_IMREAD_STRATEGIES = [
+ {"imread_strategy": mvtec_loco.IMREAD_STRATEGY_ONTHEFLY},
+ {"imread_strategy": mvtec_loco.IMREAD_STRATEGY_PRELOAD},
+]
+
+MVTEC_LOCO_PARAMS_ALL_IMREAD_ONTHEFLY_ONLY = [
+ {"imread_strategy": mvtec_loco.IMREAD_STRATEGY_ONTHEFLY},
+]
+
+
@pytest.fixture(autouse=True)
def mvtec_data_module():
datamodule = MVTec(
@@ -45,7 +77,7 @@ def btech_data_module():
return datamodule
-@pytest.fixture(autouse=True)
+@pytest.fixture(autouse=False)
def folder_data_module():
"""Create Folder Data Module."""
root = get_dataset_path(dataset="bottle")
@@ -74,6 +106,47 @@ def data_sample(mvtec_data_module):
return data
+class TestMVTecLOCODataModule:
+ """Test MVTec LOCO Data Module."""
+
+ @pytest.mark.parametrize("mvtec_loco_data_module", MVTEC_LOCO_PARAMS_ALL_IMREAD_STRATEGIES, indirect=True)
+ def test_sizes(self, mvtec_loco_data_module):
+ """test_mvtec_datamodule [summary]"""
+
+ _, train_data_sample = next(enumerate(mvtec_loco_data_module.train_dataloader()))
+ _, val_data_sample = next(enumerate(mvtec_loco_data_module.val_dataloader()))
+ _, test_data_sample = next(enumerate(mvtec_loco_data_module.test_dataloader()))
+
+ for split, data_sample in zip(["train", "val", "test"], [train_data_sample, val_data_sample, test_data_sample]):
+ image = data_sample["image"]
+ assert image.shape == (1, 3, 100, 170), f"Image shape is wrong for {split} split"
+
+ @pytest.mark.parametrize("mvtec_loco_data_module", MVTEC_LOCO_PARAMS_ALL_IMREAD_STRATEGIES, indirect=True)
+ def test_val_and_test_dataloaders_has_mask_and_gt(self, mvtec_loco_data_module):
+ """Test Validation and Test dataloaders should return more things than just the image."""
+ _, val_data = next(enumerate(mvtec_loco_data_module.val_dataloader()))
+ _, test_data = next(enumerate(mvtec_loco_data_module.test_dataloader()))
+ expected_keys = sorted(
+ ["image", "image_path", "mask", "masks", "mask_paths", "label", "super_anotype", "anotype"]
+ )
+ assert expected_keys == sorted(val_data.keys()), "Validation dataloader keys are wrong"
+ assert expected_keys == sorted(test_data.keys()), "Test dataloader keys are wrong"
+
+ @pytest.mark.parametrize("mvtec_loco_data_module", MVTEC_LOCO_PARAMS_ALL_IMREAD_ONTHEFLY_ONLY, indirect=True)
+ def test_non_overlapping_splits(self, mvtec_loco_data_module):
+ """This test ensures that the train and test splits generated are non-overlapping."""
+
+ train_paths = set(mvtec_loco_data_module.train_data.samples["image_path"].values)
+ val_paths = set(mvtec_loco_data_module.val_data.samples["image_path"].values)
+ test_paths = set(mvtec_loco_data_module.test_data.samples["image_path"].values)
+
+ assert len(set.intersection(train_paths, val_paths)) == 0, "Found train and val split contamination"
+
+ assert len(set.intersection(train_paths, test_paths)) == 0, "Found train and test split contamination"
+
+ assert len(set.intersection(test_paths, val_paths)) == 0, "Found val and test split contamination"
+
+
class TestMVTecDataModule:
"""Test MVTec AD Data Module."""
From e9809c47c815e8d7568c5ac3b4a5507594709635 Mon Sep 17 00:00:00 2001
From: Sid Mehta
Date: Wed, 14 Sep 2022 07:07:09 -0700
Subject: [PATCH 09/38] Benchmarking tool with Comet (#545)
* comet benchmarking enabled
* updated BM docs
* tweaked comment
* commen changet
* fixed end of file
Co-authored-by: Samet Akcay
---
docs/source/guides/benchmarking.rst | 3 ++-
tools/benchmarking/benchmark.py | 4 +++-
tools/benchmarking/utils/__init__.py | 4 ++--
tools/benchmarking/utils/metrics.py | 27 +++++++++++++++++++++++++++
4 files changed, 34 insertions(+), 4 deletions(-)
diff --git a/docs/source/guides/benchmarking.rst b/docs/source/guides/benchmarking.rst
index a1b04ef74d..cd844537e4 100644
--- a/docs/source/guides/benchmarking.rst
+++ b/docs/source/guides/benchmarking.rst
@@ -3,7 +3,7 @@
Benchmarking
=============
-To add to the suit of experiment tracking and optimization, anomalib also includes a benchmarking script for gathering results across different combinations of models, their parameters, and dataset categories. The model performance and throughputs are logged into a csv file that can also serve as a means to track model drift. Optionally, these same results can be logged to Weights and Biases and TensorBoard. A sample configuration file is shown below.
+To add to the suit of experiment tracking and optimization, anomalib also includes a benchmarking script for gathering results across different combinations of models, their parameters, and dataset categories. The model performance and throughputs are logged into a csv file that can also serve as a means to track model drift. Optionally, these same results can be logged to Comet, Weights and Biases and TensorBoard. A sample configuration file is shown below.
.. code-block:: yaml
@@ -13,6 +13,7 @@ To add to the suit of experiment tracking and optimization, anomalib also includ
- cpu
- gpu
writer:
+ - comet
- wandb
- tensorboard
grid_search:
diff --git a/tools/benchmarking/benchmark.py b/tools/benchmarking/benchmark.py
index 67a1ccaac5..3de36e464d 100644
--- a/tools/benchmarking/benchmark.py
+++ b/tools/benchmarking/benchmark.py
@@ -21,7 +21,7 @@
import torch
from omegaconf import DictConfig, ListConfig, OmegaConf
from pytorch_lightning import Trainer, seed_everything
-from utils import convert_to_openvino, upload_to_wandb, write_metrics
+from utils import convert_to_openvino, upload_to_comet, upload_to_wandb, write_metrics
from anomalib.config import get_configurable_parameters, update_input_size_config
from anomalib.data import get_datamodule
@@ -225,6 +225,8 @@ def distribute(config: Union[DictConfig, ListConfig]):
distribute_over_gpus(config, folder=runs_folder)
if "wandb" in config.writer:
upload_to_wandb(team="anomalib", folder=runs_folder)
+ if "comet" in config.writer:
+ upload_to_comet(folder=runs_folder)
def sweep(
diff --git a/tools/benchmarking/utils/__init__.py b/tools/benchmarking/utils/__init__.py
index 32cdd3840a..3c5124abaa 100644
--- a/tools/benchmarking/utils/__init__.py
+++ b/tools/benchmarking/utils/__init__.py
@@ -4,6 +4,6 @@
# SPDX-License-Identifier: Apache-2.0
from .convert import convert_to_openvino
-from .metrics import upload_to_wandb, write_metrics
+from .metrics import upload_to_comet, upload_to_wandb, write_metrics
-__all__ = ["convert_to_openvino", "write_metrics", "upload_to_wandb"]
+__all__ = ["convert_to_openvino", "write_metrics", "upload_to_comet", "upload_to_wandb"]
diff --git a/tools/benchmarking/utils/metrics.py b/tools/benchmarking/utils/metrics.py
index f61ecc1635..04bc3dff1f 100644
--- a/tools/benchmarking/utils/metrics.py
+++ b/tools/benchmarking/utils/metrics.py
@@ -10,6 +10,7 @@
from typing import Dict, List, Optional, Union
import pandas as pd
+from comet_ml import Experiment
from torch.utils.tensorboard.writer import SummaryWriter
import wandb
@@ -116,3 +117,29 @@ def upload_to_wandb(
)
wandb.log(row)
wandb.finish()
+
+
+def upload_to_comet(
+ folder: Optional[str] = None,
+):
+ """Upload the data in csv files to comet.
+
+ Creates a project named benchmarking_[two random characters]. This is so that the project names are unique.
+ One issue is that it does not check for collision
+
+ Args:
+ folder (optional, str): Sub-directory from which runs are picked up. Defaults to None. If none picks from runs.
+ """
+ project = f"benchmarking_{get_unique_key(2)}"
+ tag_list = ["dataset.category", "model_name", "dataset.image_size", "model.backbone", "device"]
+ search_path = "runs/*.csv" if folder is None else f"runs/{folder}/*.csv"
+ for csv_file in glob(search_path):
+ table = pd.read_csv(csv_file)
+ for index, row in table.iterrows():
+ row = dict(row[1:]) # remove index column
+ tags = [str(row[column]) for column in tag_list if column in row.keys()]
+ experiment = Experiment(project_name=project)
+ experiment.set_name(f"{row['model_name']}_{row['dataset.category']}_{index}")
+ experiment.log_metrics(row, step=1, epoch=1) # populates auto-generated charts on panel view
+ experiment.add_tags(tags)
+ experiment.log_table(filename=csv_file)
From 7305246c00746b9acb94a0532be20aa01f3af26e Mon Sep 17 00:00:00 2001
From: Samet Akcay
Date: Fri, 16 Sep 2022 15:46:09 +0100
Subject: [PATCH 10/38] =?UTF-8?q?=F0=9F=90=9E=20Fix:=20Add=20map=5Flocatio?=
=?UTF-8?q?n=20when=20loading=20the=20weights=20(#562)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
anomalib/utils/callbacks/model_loader.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/anomalib/utils/callbacks/model_loader.py b/anomalib/utils/callbacks/model_loader.py
index a89b5ecd68..ad1e7ebc71 100644
--- a/anomalib/utils/callbacks/model_loader.py
+++ b/anomalib/utils/callbacks/model_loader.py
@@ -27,7 +27,7 @@ def on_test_start(self, _trainer, pl_module: AnomalyModule) -> None: # pylint:
Loads the model weights from ``weights_path`` into the PyTorch module.
"""
logger.info("Loading the model from %s", self.weights_path)
- pl_module.load_state_dict(torch.load(self.weights_path)["state_dict"])
+ pl_module.load_state_dict(torch.load(self.weights_path, map_location=pl_module.device)["state_dict"])
def on_predict_start(self, _trainer, pl_module: AnomalyModule) -> None:
"""Call when inference begins.
@@ -35,4 +35,4 @@ def on_predict_start(self, _trainer, pl_module: AnomalyModule) -> None:
Loads the model weights from ``weights_path`` into the PyTorch module.
"""
logger.info("Loading the model from %s", self.weights_path)
- pl_module.load_state_dict(torch.load(self.weights_path)["state_dict"])
+ pl_module.load_state_dict(torch.load(self.weights_path, map_location=pl_module.device)["state_dict"])
From a055416495cd51280a7ab917fbf8ea6e16066e58 Mon Sep 17 00:00:00 2001
From: Ashwin Vaidya
Date: Tue, 20 Sep 2022 09:48:27 +0200
Subject: [PATCH 11/38] Add patchcore to openvino export test + upgrade
lightning (#565)
---
requirements/base.txt | 4 ++--
tests/pre_merge/deploy/test_inferencer.py | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/requirements/base.txt b/requirements/base.txt
index 4b6ac9dab6..37dc678cc3 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -12,6 +12,6 @@ pandas>=1.1.0
pytorch-lightning>=1.6.0,<1.7.0
timm==0.5.4
torchmetrics>=0.9.1,<=0.9.3
-torchvision>=0.9.1,<=0.12.0
-torchtext>=0.9.1,<=0.12.0
+torchvision>=0.9.1,<=0.13.0
+torchtext>=0.9.1,<=0.13.0
wandb==0.12.17
diff --git a/tests/pre_merge/deploy/test_inferencer.py b/tests/pre_merge/deploy/test_inferencer.py
index fbde1211c3..2a86d20482 100644
--- a/tests/pre_merge/deploy/test_inferencer.py
+++ b/tests/pre_merge/deploy/test_inferencer.py
@@ -80,7 +80,7 @@ def test_torch_inference(self, model_name: str, category: str = "shapes", path:
@pytest.mark.parametrize(
"model_name",
- ["dfm", "draem", "ganomaly", "padim", "stfpm"],
+ ["dfm", "draem", "ganomaly", "padim", "patchcore", "stfpm"],
)
@TestDataset(num_train=20, num_test=1, path=get_dataset_path(), use_mvtec=False)
def test_openvino_inference(self, model_name: str, category: str = "shapes", path: str = "./datasets/MVTec"):
From 4860abc78b0976faf2c423713d5b90cfaa55ed22 Mon Sep 17 00:00:00 2001
From: Samet Akcay
Date: Tue, 20 Sep 2022 11:01:56 +0100
Subject: [PATCH 12/38] =?UTF-8?q?=F0=9F=90=9E=20Fix=20category=20check=20f?=
=?UTF-8?q?or=20folder=20dataset=20in=20anomalib=20CLI=20(#567)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Fix category check
* Fix config file
---
anomalib/utils/cli/cli.py | 2 +-
configs/model/fastflow.yaml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/anomalib/utils/cli/cli.py b/anomalib/utils/cli/cli.py
index 826e98cd46..48c1769163 100644
--- a/anomalib/utils/cli/cli.py
+++ b/anomalib/utils/cli/cli.py
@@ -144,7 +144,7 @@ def __set_default_root_dir(self) -> None:
root_dir = config.trainer.default_root_dir if config.trainer.default_root_dir else "./results"
model_name = config.model.class_path.split(".")[-1].lower()
data_name = config.data.class_path.split(".")[-1].lower()
- category = config.data.init_args.category if config.data.init_args.keys() else ""
+ category = config.data.init_args.category if "category" in config.data.init_args else ""
time_stamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
default_root_dir = os.path.join(root_dir, model_name, data_name, category, time_stamp)
diff --git a/configs/model/fastflow.yaml b/configs/model/fastflow.yaml
index c63491f4b5..1a2217ab6f 100644
--- a/configs/model/fastflow.yaml
+++ b/configs/model/fastflow.yaml
@@ -30,7 +30,7 @@ model:
hidden_ratio: 1.0 # options: [1.0, 1.0, 0.16, 0.16] - for each supported backbone
optimizer:
- class_path: torch.optim._multi_tensor.Adam
+ class_path: torch.optim._multi_tensor.adam.Adam
init_args:
lr: 0.001
weight_decay: 0.00001
From 5b3fc2b117de0011d39759128602d9b50c0ddb8c Mon Sep 17 00:00:00 2001
From: Samet Akcay
Date: Fri, 23 Sep 2022 07:21:48 +0100
Subject: [PATCH 13/38] =?UTF-8?q?=F0=9F=9A=9C=20Refactor=20`PreProcessor`?=
=?UTF-8?q?=20and=20fix=20`Visualizer`=20denormalization=20issue.=20(#570)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* move sample generation to datamodule instead of dataset
* move sample generation from init to setup
* remove inference stage and add base classes
* replace dataset classes with AnomalibDataset
* move setup to base class, create samples as class method
* update docstrings
* refactor btech to new format
* allow training with no anomalous data
* remove MVTec name from comment
* raise NotImplementedError in base class
* allow both png and bmp images for btech
* use label_index to check if dataset contains anomalous images
* refactor getitem in dataset class
* use iloc for indexing
* move dataloader getters to base class
* refactor to add validate stage in setup
* Add warning message when there is no config file passed
* Extract get_transforms and get_height_and_width functions
* refactor pre-processor and fix visualizer normalization issue
* Revert thenew data refactor
* rename variable
* Revert the changes not merged yet
* Fix tests
* Fix tests
* Address codacy concerns
Co-authored-by: Dick Ameln
---
anomalib/data/utils/__init__.py | 8 +-
anomalib/data/utils/image.py | 52 +++++-
anomalib/post_processing/visualizer.py | 7 +-
anomalib/pre_processing/pre_process.py | 157 +++++++++++-------
.../dummy_lightning_model.py | 3 +-
.../visualizer_callback/test_visualizer.py | 4 +-
6 files changed, 166 insertions(+), 65 deletions(-)
diff --git a/anomalib/data/utils/__init__.py b/anomalib/data/utils/__init__.py
index 53cf04e4fd..5059b51c06 100644
--- a/anomalib/data/utils/__init__.py
+++ b/anomalib/data/utils/__init__.py
@@ -5,11 +5,17 @@
from .download import DownloadProgressBar, hash_check
from .generators import random_2d_perlin
-from .image import generate_output_image_filename, get_image_filenames, read_image
+from .image import (
+ generate_output_image_filename,
+ get_image_filenames,
+ get_image_height_and_width,
+ read_image,
+)
__all__ = [
"generate_output_image_filename",
"get_image_filenames",
+ "get_image_height_and_width",
"hash_check",
"random_2d_perlin",
"read_image",
diff --git a/anomalib/data/utils/image.py b/anomalib/data/utils/image.py
index c8c5039882..758124f42e 100644
--- a/anomalib/data/utils/image.py
+++ b/anomalib/data/utils/image.py
@@ -6,7 +6,7 @@
import math
import warnings
from pathlib import Path
-from typing import List, Union
+from typing import List, Optional, Tuple, Union
import cv2
import numpy as np
@@ -141,7 +141,48 @@ def generate_output_image_filename(input_path: Union[str, Path], output_path: Un
return file_path
-def read_image(path: Union[str, Path]) -> np.ndarray:
+def get_image_height_and_width(image_size: Optional[Union[int, Tuple]] = None) -> Tuple[Optional[int], Optional[int]]:
+ """Get image height and width from ``image_size`` variable.
+
+ Args:
+ image_size (Optional[Union[int, Tuple[int, int]]], optional): Input image size.
+
+ Raises:
+ ValueError: Image size not None, int or tuple.
+
+ Examples:
+ >>> get_image_height_and_width(image_size=256)
+ (256, 256)
+
+ >>> get_image_height_and_width(image_size=(256, 256))
+ (256, 256)
+
+ >>> get_image_height_and_width(image_size=(256, 256, 3))
+ (256, 256)
+
+ >>> get_image_height_and_width(image_size=256.)
+ Traceback (most recent call last):
+ File "", line 1, in
+ File "", line 18, in get_image_height_and_width
+ ValueError: ``image_size`` could be either int or Tuple[int, int]
+
+ Returns:
+ Tuple[Optional[int], Optional[int]]: A tuple containing image height and width values.
+ """
+ height_and_width: Tuple[Optional[int], Optional[int]]
+ if isinstance(image_size, int):
+ height_and_width = (image_size, image_size)
+ elif isinstance(image_size, tuple):
+ height_and_width = int(image_size[0]), int(image_size[1])
+ elif image_size is None:
+ height_and_width = (None, None)
+ else:
+ raise ValueError("``image_size`` could be either int or Tuple[int, int]")
+
+ return height_and_width
+
+
+def read_image(path: Union[str, Path], image_size: Optional[Union[int, Tuple]] = None) -> np.ndarray:
"""Read image from disk in RGB format.
Args:
@@ -157,6 +198,13 @@ def read_image(path: Union[str, Path]) -> np.ndarray:
image = cv2.imread(path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
+ if image_size:
+ # This part is optional, where the user wants to quickly resize the image
+ # with a one-liner code. This would particularly be useful especially when
+ # prototyping new ideas.
+ height, width = get_image_height_and_width(image_size)
+ image = cv2.resize(image, dsize=(width, height), interpolation=cv2.INTER_AREA)
+
return image
diff --git a/anomalib/post_processing/visualizer.py b/anomalib/post_processing/visualizer.py
index 409a52b4a2..19b65887a7 100644
--- a/anomalib/post_processing/visualizer.py
+++ b/anomalib/post_processing/visualizer.py
@@ -13,12 +13,12 @@
import numpy as np
from skimage.segmentation import mark_boundaries
+from anomalib.data.utils import read_image
from anomalib.post_processing.post_process import (
add_anomalous_label,
add_normal_label,
superimpose_anomaly_map,
)
-from anomalib.pre_processing.transforms import Denormalize
@dataclass
@@ -73,9 +73,10 @@ def visualize_batch(self, batch: Dict) -> Iterator[np.ndarray]:
Returns:
Generator that yields a display-ready visualization for each image.
"""
- for i in range(batch["image"].size(0)):
+ batch_size, _num_channels, height, width = batch["image"].size()
+ for i in range(batch_size):
image_result = ImageResult(
- image=Denormalize()(batch["image"][i].cpu()),
+ image=read_image(path=batch["image_path"][i], image_size=(height, width)),
pred_score=batch["pred_scores"][i].cpu().numpy().item(),
pred_label=batch["pred_labels"][i].cpu().numpy().item(),
anomaly_map=batch["anomaly_maps"][i].cpu().numpy() if "anomaly_maps" in batch else None,
diff --git a/anomalib/pre_processing/pre_process.py b/anomalib/pre_processing/pre_process.py
index 28740496eb..44cbc294e4 100644
--- a/anomalib/pre_processing/pre_process.py
+++ b/anomalib/pre_processing/pre_process.py
@@ -7,11 +7,111 @@
# Copyright (C) 2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
+import logging
from typing import Optional, Tuple, Union
import albumentations as A
from albumentations.pytorch import ToTensorV2
+from anomalib.data.utils import get_image_height_and_width
+
+logger = logging.getLogger(__name__)
+
+
+def get_transforms(
+ config: Optional[Union[str, A.Compose]] = None,
+ image_size: Optional[Union[int, Tuple]] = None,
+ to_tensor: bool = True,
+) -> A.Compose:
+ """Get transforms from config or image size.
+
+ Args:
+ config (Optional[Union[str, A.Compose]], optional): Albumentations transforms.
+ Either config or albumentations ``Compose`` object. Defaults to None.
+ image_size (Optional[Union[int, Tuple]], optional): Image size to transform. Defaults to None.
+ to_tensor (bool, optional): Boolean to convert the final transforms into Torch tensor. Defaults to True.
+
+ Raises:
+ ValueError: When both ``config`` and ``image_size`` is ``None``.
+ ValueError: When ``config`` is not a ``str`` or `A.Compose`` object.
+
+ Returns:
+ A.Compose: Albumentation ``Compose`` object containing the image transforms.
+
+ Examples:
+ >>> import skimage
+ >>> image = skimage.data.astronaut()
+
+ >>> transforms = get_transforms(image_size=256, to_tensor=False)
+ >>> output = transforms(image=image)
+ >>> output["image"].shape
+ (256, 256, 3)
+
+ >>> transforms = get_transforms(image_size=256, to_tensor=True)
+ >>> output = transforms(image=image)
+ >>> output["image"].shape
+ torch.Size([3, 256, 256])
+
+
+ Transforms could be read from albumentations Compose object.
+ >>> import albumentations as A
+ >>> from albumentations.pytorch import ToTensorV2
+ >>> config = A.Compose([A.Resize(512, 512), ToTensorV2()])
+ >>> transforms = get_transforms(config=config, to_tensor=False)
+ >>> output = transforms(image=image)
+ >>> output["image"].shape
+ (512, 512, 3)
+ >>> type(output["image"])
+ numpy.ndarray
+
+ Transforms could be deserialized from a yaml file.
+ >>> transforms = A.Compose([A.Resize(1024, 1024), ToTensorV2()])
+ >>> A.save(transforms, "/tmp/transforms.yaml", data_format="yaml")
+ >>> transforms = get_transforms(config="/tmp/transforms.yaml")
+ >>> output = transforms(image=image)
+ >>> output["image"].shape
+ torch.Size([3, 1024, 1024])
+ """
+ if config is None and image_size is None:
+ raise ValueError(
+ "Both config and image_size cannot be `None`. "
+ "Provide either config file to de-serialize transforms "
+ "or image_size to get the default transformations"
+ )
+
+ transforms: A.Compose
+
+ if config is None and image_size is not None:
+ logger.warning("Transform configs has not been provided. Images will be normalized using ImageNet statistics.")
+
+ height, width = get_image_height_and_width(image_size)
+ transforms = A.Compose(
+ [
+ A.Resize(height=height, width=width, always_apply=True),
+ A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
+ ToTensorV2(),
+ ]
+ )
+
+ if config is not None:
+ if isinstance(config, str):
+ transforms = A.load(filepath=config, data_format="yaml")
+ elif isinstance(config, A.Compose):
+ transforms = config
+ else:
+ raise ValueError("config could be either ``str`` or ``A.Compose``")
+
+ if not to_tensor:
+ if isinstance(transforms[-1], ToTensorV2):
+ transforms = A.Compose(transforms[:-1])
+
+ # always resize to specified image size
+ if not any(isinstance(transform, A.Resize) for transform in transforms) and image_size is not None:
+ height, width = get_image_height_and_width(image_size)
+ transforms = A.Compose([A.Resize(height=height, width=width, always_apply=True), transforms])
+
+ return transforms
+
class PreProcessor:
"""Applies pre-processing and data augmentations to the input and returns the transformed output.
@@ -74,63 +174,8 @@ def __init__(
self.image_size = image_size
self.to_tensor = to_tensor
- self.transforms = self.get_transforms()
-
- def get_transforms(self) -> A.Compose:
- """Get transforms from config or image size.
-
- Returns:
- A.Compose: List of albumentation transformations to apply to the
- input image.
- """
- if self.config is None and self.image_size is None:
- raise ValueError(
- "Both config and image_size cannot be `None`. "
- "Provide either config file to de-serialize transforms "
- "or image_size to get the default transformations"
- )
-
- transforms: A.Compose
-
- if self.config is None and self.image_size is not None:
- height, width = self._get_height_and_width()
- transforms = A.Compose(
- [
- A.Resize(height=height, width=width, always_apply=True),
- A.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)),
- ToTensorV2(),
- ]
- )
-
- if self.config is not None:
- if isinstance(self.config, str):
- transforms = A.load(filepath=self.config, data_format="yaml")
- elif isinstance(self.config, A.Compose):
- transforms = self.config
- else:
- raise ValueError("config could be either ``str`` or ``A.Compose``")
-
- if not self.to_tensor:
- if isinstance(transforms[-1], ToTensorV2):
- transforms = A.Compose(transforms[:-1])
-
- # always resize to specified image size
- if not any(isinstance(transform, A.Resize) for transform in transforms) and self.image_size is not None:
- height, width = self._get_height_and_width()
- transforms = A.Compose([A.Resize(height=height, width=width, always_apply=True), transforms])
-
- return transforms
+ self.transforms = get_transforms(config, image_size, to_tensor)
def __call__(self, *args, **kwargs):
"""Return transformed arguments."""
return self.transforms(*args, **kwargs)
-
- def _get_height_and_width(self) -> Tuple[Optional[int], Optional[int]]:
- """Extract height and width from image size attribute."""
- if isinstance(self.image_size, int):
- return self.image_size, self.image_size
- if isinstance(self.image_size, tuple):
- return int(self.image_size[0]), int(self.image_size[1])
- if self.image_size is None:
- return None, None
- raise ValueError("``image_size`` could be either int or Tuple[int, int]")
diff --git a/tests/pre_merge/utils/callbacks/visualizer_callback/dummy_lightning_model.py b/tests/pre_merge/utils/callbacks/visualizer_callback/dummy_lightning_model.py
index 6072852fbe..8644871661 100644
--- a/tests/pre_merge/utils/callbacks/visualizer_callback/dummy_lightning_model.py
+++ b/tests/pre_merge/utils/callbacks/visualizer_callback/dummy_lightning_model.py
@@ -11,6 +11,7 @@
from anomalib.models.components import AnomalyModule
from anomalib.utils.callbacks import ImageVisualizerCallback
from anomalib.utils.metrics import get_metrics
+from tests.helpers.dataset import get_dataset_path
class DummyDataset(Dataset):
@@ -68,7 +69,7 @@ def test_step(self, batch, _):
"""Only used to trigger on_test_epoch_end."""
self.log(name="loss", value=0.0, prog_bar=True)
outputs = dict(
- image_path=[Path("test1.jpg")],
+ image_path=[Path(get_dataset_path("bottle")) / "broken_large/000.png"],
image=torch.rand((1, 3, 100, 100)),
mask=torch.zeros((1, 100, 100)),
anomaly_maps=torch.ones((1, 100, 100)),
diff --git a/tests/pre_merge/utils/callbacks/visualizer_callback/test_visualizer.py b/tests/pre_merge/utils/callbacks/visualizer_callback/test_visualizer.py
index 6ecd92c6f0..35fdb2e129 100644
--- a/tests/pre_merge/utils/callbacks/visualizer_callback/test_visualizer.py
+++ b/tests/pre_merge/utils/callbacks/visualizer_callback/test_visualizer.py
@@ -1,7 +1,7 @@
import glob
import os
import tempfile
-from unittest import mock
+from pathlib import Path
import pytest
import pytorch_lightning as pl
@@ -42,7 +42,7 @@ def test_add_images(dataset):
)
trainer.test(model=model, datamodule=DummyDataModule())
# test if images are logged
- if len(glob.glob(os.path.join(dir_loc, "images", "*.jpg"))) != 1:
+ if len(list(Path(dir_loc).glob("**/*.png"))) != 1:
raise Exception("Failed to save to local path")
# test if tensorboard logs are created
From de1bea206735aef71405a5f55a77ed78638b53c9 Mon Sep 17 00:00:00 2001
From: Ashwin Vaidya
Date: Fri, 23 Sep 2022 08:22:57 +0200
Subject: [PATCH 14/38] =?UTF-8?q?=F0=9F=94=A8=20Check=20for=20successful?=
=?UTF-8?q?=20openvino=20conversion=20(#571)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Check for successful openvino conversion
---
anomalib/deploy/optimize.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/anomalib/deploy/optimize.py b/anomalib/deploy/optimize.py
index a91fcc08ca..93b433da5b 100644
--- a/anomalib/deploy/optimize.py
+++ b/anomalib/deploy/optimize.py
@@ -68,7 +68,7 @@ def export_convert(
if export_mode == "openvino":
export_path = os.path.join(str(export_path), "openvino")
optimize_command = "mo --input_model " + str(onnx_path) + " --output_dir " + str(export_path)
- os.system(optimize_command)
+ assert os.system(optimize_command) == 0, "OpenVINO conversion failed"
with open(Path(export_path) / "meta_data.json", "w", encoding="utf-8") as metadata_file:
meta_data = get_model_metadata(model)
# Convert metadata from torch
From 353d981e8a6500034bd44ae8d9ed07a93baf3e84 Mon Sep 17 00:00:00 2001
From: Sid Mehta
Date: Mon, 26 Sep 2022 00:54:58 -0700
Subject: [PATCH 15/38] =?UTF-8?q?=F0=9F=93=8A=20Comet=20HPO=20=20(#563)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* added hpo
* lint fixed
* Update hyperparameter_optimization.rst
* fixed file lint
* fixed documentation images
* added sweep doc image
* updated hpo docs to include images
* fixed linting errors
* added config folder to store sample sweeps
* fixed docs for new location of config files
* not needed. moved to config directory
* not needed moved to config directory
* renamed to configs
* changed to "configs"
* fixed grammar
---
.../guides/hyperparameter_optimization.rst | 41 +++++++++++--
docs/source/guides/logging.rst | 4 +-
docs/source/images/logging/comet_sweep.png | Bin 0 -> 350955 bytes
tools/hpo/configs/comet_sweep.yaml | 15 +++++
.../{sweep.yaml => configs/wandb_sweep.yaml} | 0
tools/hpo/sweep.py | 57 +++++++++++++++++-
6 files changed, 106 insertions(+), 11 deletions(-)
create mode 100644 docs/source/images/logging/comet_sweep.png
create mode 100644 tools/hpo/configs/comet_sweep.yaml
rename tools/hpo/{sweep.yaml => configs/wandb_sweep.yaml} (100%)
diff --git a/docs/source/guides/hyperparameter_optimization.rst b/docs/source/guides/hyperparameter_optimization.rst
index a275ffb4c4..92aa5da882 100644
--- a/docs/source/guides/hyperparameter_optimization.rst
+++ b/docs/source/guides/hyperparameter_optimization.rst
@@ -3,12 +3,34 @@
Hyperparameter Optimization
===========================
-The default configuration for the models will not always work on a new dataset. Additionally, to increase performance, learning rate, optimizers, activation functions, etc. need to be tuned/selected. To make it easier to run such broad experiments that isolate the right combination of hyperparameters, Anomalib supports hyperparameter optimization using weights and biases.
+The default configuration for the models will not always work on a new dataset. Additionally, to increase performance, learning rate, optimizers, activation functions, etc. need to be tuned/selected. To make it easier to run such broad experiments that isolate the right combination of hyperparameters, Anomalib supports hyperparameter optimization using Comet or weights and biases.
YAML file
**********
-A sample configuration file for hyperparameter optimization is provided at ``tools/hpo/sweep.yaml`` and is reproduced below:
+A Sample configuration files for hyperparameter optimization with Comet is provided at ``tools/hpo/config/comet_sweep.yaml`` and reproduced below:
+
+.. code-block:: yaml
+
+ algorithm: "bayes"
+ spec:
+ maxCombo: 10
+ metric: "image_F1Score"
+ objective: "maximize"
+ parameters:
+ dataset:
+ category: capsule
+ image_size:
+ type: discrete
+ values: [128, 256]
+ model:
+ backbone:
+ type: categorical
+ values: ["resnet18", "wide_resnet50_2"]
+
+The maxCombo defines the total number of experiments to run. The algorithm is the optimization method to be used. The metric is the metric to be used to evaluate the performance of the model. The parameters are the hyperparameters to be optimized. For details on other possible configurations with Comet's Optimizer , refer to the `Comet's `_ documentation.
+
+A sample configuration file for hyperparameter optimization with Weights and Bias is provided at ``tools/hpo/config/wandb_sweep.yaml`` and is reproduced below:
.. code-block:: yaml
@@ -26,14 +48,16 @@ A sample configuration file for hyperparameter optimization is provided at ``too
backbone:
values: [resnet18, wide_resnet50_2]
-The observation budget defines the total number of experiments to run. The method is the optimization method to be used. The metric is the metric to be used to evaluate the performance of the model. The parameters are the hyperparameters to be optimized. For details on methods other than ``bayes`` and parameter values apart from list, refer the `Weights and Biases `_ documentation. Everything under the ``parameters`` key overrides the default values defined in the model configuration. Currently, only the dataset and model parameters are overridden for the HPO search.
+The observation budget defines the total number of experiments to run. The method is the optimization method to be used. The metric is the metric to be used to evaluate the performance of the model. The parameters are the hyperparameters to be optimized. For details on methods other than ``bayes`` and parameter values apart from list, refer the `Weights and Biases `_ documentation.
+
+Everything under the ``parameters`` key (in both configuration formats) overrides the default values defined in the model configuration. In these examples, only the dataset and model parameters are overridden for the HPO search.
Running HPO
************
.. note::
- You will need to have logged into a wandb account to use HPO search and view the results.
+ You will need to have logged into a Comet or wandb account to use HPO search and view the results.
To run the hyperparameter optimization, use the following command:
@@ -41,18 +65,23 @@ To run the hyperparameter optimization, use the following command:
python tools/hpo/sweep.py --model padim \
--model_config ./path_to_config.yaml \
- --sweep_config tools/hpo/sweep.yaml
+ --sweep_config tools/hpo/config/comet_sweep.yaml
In case ``model_config`` is not provided, the script looks at the default config location for that model.
.. code-block:: bash
- python tools/hpo/sweep.py --sweep_config tools/hpo/sweep.yaml
+ python tools/hpo/sweep.py --sweep_config tools/hpo/config/comet_sweep.yaml
Sample Output
**************
+.. figure:: ../images/logging/comet_sweep.png
+ :alt: Sample configuration of a Comet sweep
+
+ Sample Comet sweep on Padim
+
.. figure:: ../images/logging/wandb_sweep.png
:alt: Sample configuration of a wandb sweep
diff --git a/docs/source/guides/logging.rst b/docs/source/guides/logging.rst
index 38eba09179..162f074def 100644
--- a/docs/source/guides/logging.rst
+++ b/docs/source/guides/logging.rst
@@ -51,7 +51,7 @@ Anomalib allows you to save predictions to the file system by setting ``log_imag
Logging images to Comet,TensorBoard and wandb won't work if you don't have ``logger: [comet, tensorboard, wandb]`` set as well. This ensures that the respective logger is passed to the trainer object.
-.. figure:: ../images/logging/comet_media.jpg
+.. figure:: ../images/logging/comet_media.png
:alt: comet dashboard showing logged images
Comet Images in TensorBoard Dashboard
@@ -84,7 +84,7 @@ Anomalib makes it easier to log your model graph to Comet, TensorBoard or Weight
logger: [comet, tensorboard]
log_graph: true
-.. figure:: ../images/logging/comet_graph.jpg
+.. figure:: ../images/logging/comet_graph.png
:alt: comet dashboard showing model graph
Model Graph in Comet Dashboard
diff --git a/docs/source/images/logging/comet_sweep.png b/docs/source/images/logging/comet_sweep.png
new file mode 100644
index 0000000000000000000000000000000000000000..2f14c85ad3c017dbffc9cad0c104cc47321b0904
GIT binary patch
literal 350955
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