Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix visualizer for classification, mode=simple #442

Merged
merged 3 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions anomalib/post_processing/visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ def _visualize_simple(self, image_result):
return (visualization * 255).astype(np.uint8)
if self.task == "classification":
if image_result.pred_label:
image_classified = add_anomalous_label(image_result.heat_map, image_result.pred_score)
image_classified = add_anomalous_label(image_result.image, image_result.pred_score)
else:
image_classified = add_normal_label(image_result.heat_map, 1 - image_result.pred_score)
image_classified = add_normal_label(image_result.image, 1 - image_result.pred_score)
return image_classified
raise ValueError(f"Unknown task type: {self.task}")

Expand Down
19 changes: 15 additions & 4 deletions tests/helpers/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# and limitations under the License.

import os
from typing import Dict, List, Tuple, Union
from typing import Dict, List, Optional, Tuple, Union

import numpy as np
from omegaconf import DictConfig, ListConfig
Expand All @@ -36,9 +36,10 @@ def setup_model_train(
project_path: str,
nncf: bool,
category: str,
score_type: str = None,
weight_file: str = "weights/model.ckpt",
score_type: Optional[str] = None,
fast_run: bool = False,
dataset_task: Optional[str] = None,
visualizer_mode: Optional[str] = None,
device: Union[List[int], int] = [0],
) -> Tuple[Union[DictConfig, ListConfig], LightningDataModule, AnomalyModule, Trainer]:
"""Train the model based on the parameters passed.
Expand All @@ -50,9 +51,12 @@ def setup_model_train(
nncf (bool): Add nncf callback.
category (str): Category to train on.
score_type (str, optional): Only used for DFM. Defaults to None.
weight_file (str, optional): Path to weight file.
fast_run (bool, optional): If set to true, the model trains for only 1 epoch. We train for one epoch as
this ensures that both anomalous and non-anomalous images are present in the validation step.
dataset_task (str, optional): Specify the type of task. Must be in ["classification", "segmentation"].
Used for integration testing of model / task / visualizer_mode.
visualizer_mode (str, optional): Specify the type of visualization. Must be in ["full", "simple"].
Used for integration testing of model / task / visualizer_mode.
device (List[int], int, optional): Select which device you want to train the model on. Defaults to first GPU.

Returns:
Expand All @@ -67,6 +71,13 @@ def setup_model_train(
config.project.log_images_to = []
config.trainer.devices = device
config.trainer.accelerator = "gpu" if device != 0 else "cpu"
if dataset_task is not None:
config.dataset.task = dataset_task
if visualizer_mode is not None:
config.visualization.mode = visualizer_mode
config.visualization.save_images = True # Enforce processing by Visualizer
if "pixel" in config.metrics and dataset_task == "classification":
del config.metrics.pixel

# Remove legacy flags
for legacy_device in ["num_processes", "gpus", "ipus", "tpu_cores"]:
Expand Down
33 changes: 33 additions & 0 deletions tests/pre_merge/post_processing/test_visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@
# See the License for the specific language governing permissions
# and limitations under the License.

import tempfile

import numpy as np
import pytest
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas

from anomalib.post_processing.visualizer import ImageGrid
from tests.helpers.dataset import TestDataset
from tests.helpers.model import setup_model_train


def test_visualize_fully_defected_masks():
Expand All @@ -36,3 +41,31 @@ def test_visualize_fully_defected_masks():

# assert that the plotted image is completely white
assert np.all(plotted_img[0][..., 0] == 255)


class TestVisualizer:
@pytest.mark.parametrize(
["model_name", "nncf"],
[
("padim", False),
("ganomaly", False),
],
)
@pytest.mark.parametrize("task", ("classification", "segmentation"))
@pytest.mark.parametrize("mode", ("full", "simple"))
@TestDataset(num_train=20, num_test=10)
def test_model_visualizer_mode(self, model_name, nncf, task, mode, category="shapes", path=""):
"""Test combination of model/visualizer/mode on only 1 epoch as a sanity check before merge."""
with tempfile.TemporaryDirectory() as project_path:
# Train test
datamodule, model, trainer = setup_model_train(
model_name,
dataset_path=path,
project_path=project_path,
nncf=nncf,
category=category,
fast_run=True,
dataset_task=task,
visualizer_mode=mode,
)[1:]
trainer.test(model=model, datamodule=datamodule)