|
9 | 9 | import numpy as np |
10 | 10 | import pytest |
11 | 11 | import torch |
| 12 | +from PIL import Image |
| 13 | +from torch.utils.data import DataLoader |
12 | 14 |
|
| 15 | +from anomalib.data import ImageBatch, NumpyImageBatch, PredictDataset |
13 | 16 | from anomalib.deploy import ExportType, OpenVINOInferencer, TorchInferencer |
14 | 17 | from anomalib.engine import Engine |
15 | 18 | from anomalib.models import Padim |
@@ -108,3 +111,82 @@ def test_openvino_inference(ckpt_path: Callable[[str], Path]) -> None: |
108 | 111 | for image in openvino_dataloader(): |
109 | 112 | prediction = openvino_inferencer.predict(image) |
110 | 113 | assert 0.0 <= prediction.pred_score <= 1.0 # confirm if predicted scores are normalized |
| 114 | + |
| 115 | + |
| 116 | +def compare_predictions( |
| 117 | + pred1: ImageBatch | NumpyImageBatch, |
| 118 | + pred2: ImageBatch | NumpyImageBatch, |
| 119 | + tolerance: float = 1e-3, |
| 120 | +) -> None: |
| 121 | + """Compare predictions from two different inference methods.""" |
| 122 | + score1 = pred1.pred_score if hasattr(pred1, "pred_score") else None |
| 123 | + score2 = pred2.pred_score if hasattr(pred2, "pred_score") else None |
| 124 | + |
| 125 | + map1 = pred1.anomaly_map if hasattr(pred1, "anomaly_map") else None |
| 126 | + map2 = pred2.anomaly_map if hasattr(pred2, "anomaly_map") else None |
| 127 | + |
| 128 | + if isinstance(map1, torch.Tensor): |
| 129 | + map1 = map1.cpu().numpy() |
| 130 | + if isinstance(map2, torch.Tensor): |
| 131 | + map2 = map2.cpu().numpy() |
| 132 | + |
| 133 | + if score1 is None and score2 is None and map1 is None and map2 is None: |
| 134 | + pytest.fail("No predictions found") |
| 135 | + |
| 136 | + if score1 is not None and score2 is not None: |
| 137 | + if isinstance(score1, torch.Tensor): |
| 138 | + score1 = score1.cpu().item() |
| 139 | + if isinstance(score2, torch.Tensor): |
| 140 | + score2 = score2.cpu().item() |
| 141 | + |
| 142 | + if score1 is not None and score2 is not None: |
| 143 | + score_diff = abs(score1 - score2) |
| 144 | + if score_diff > tolerance: |
| 145 | + pytest.fail(f"Anomaly score absolute difference: {score_diff:.3f}") |
| 146 | + |
| 147 | + if map1 is not None and map2 is not None: |
| 148 | + map_diff = np.abs(map1 - map2) |
| 149 | + if np.mean(map_diff) > tolerance: |
| 150 | + pytest.fail(f"Anomaly map mean absolute difference: {np.mean(map_diff):.3f}") |
| 151 | + |
| 152 | + |
| 153 | +def test_inference_similarity( |
| 154 | + ckpt_path: Callable[[str], Path], |
| 155 | + project_path: Path, |
| 156 | + tmp_path: Path, |
| 157 | + monkeypatch: pytest.MonkeyPatch, |
| 158 | +) -> None: |
| 159 | + """Test inference result.""" |
| 160 | + # Set TRUST_REMOTE_CODE environment variable for the test |
| 161 | + monkeypatch.setenv("TRUST_REMOTE_CODE", "1") |
| 162 | + |
| 163 | + rng = np.random.default_rng() |
| 164 | + image = rng.integers(0, 255, (256, 256, 3), dtype=np.uint8) |
| 165 | + image = Image.fromarray(image) |
| 166 | + test_image_path = tmp_path / "test_image.png" |
| 167 | + image.save(test_image_path) |
| 168 | + |
| 169 | + model = Padim() |
| 170 | + engine = Engine(logger=False, default_root_dir=project_path, devices=1) |
| 171 | + |
| 172 | + predict_dataset = PredictDataset(test_image_path) |
| 173 | + predict_dataloader = DataLoader( |
| 174 | + predict_dataset, |
| 175 | + batch_size=1, |
| 176 | + collate_fn=predict_dataset.collate_fn, |
| 177 | + pin_memory=True, |
| 178 | + ) |
| 179 | + engine_pred: list[ImageBatch] = engine.predict(model, dataloaders=predict_dataloader, ckpt_path=ckpt_path("Padim")) |
| 180 | + engine_pred = engine_pred[0] |
| 181 | + |
| 182 | + torch_path = engine.export(model, export_type=ExportType.TORCH, export_root=project_path) |
| 183 | + torch_inferencer = TorchInferencer(torch_path, device="cpu") |
| 184 | + torch_pred = torch_inferencer.predict(test_image_path) |
| 185 | + |
| 186 | + openvino_path = engine.export(model, export_type=ExportType.OPENVINO, export_root=project_path) |
| 187 | + openvino_inferencer = OpenVINOInferencer(openvino_path, device="CPU") |
| 188 | + openvino_pred = openvino_inferencer.predict(test_image_path) |
| 189 | + |
| 190 | + compare_predictions(engine_pred, torch_pred) |
| 191 | + compare_predictions(engine_pred, openvino_pred) |
| 192 | + compare_predictions(torch_pred, openvino_pred) |
0 commit comments