-
Notifications
You must be signed in to change notification settings - Fork 704
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
[Bug]: The results of running an ONNX model with ONNX Runtime and OpenVINO are significantly different. #2483
Comments
Hi, could you please share the exact code used to produce the results and also provide the version of anomalib that you used. Thank you. |
My anomalib repository version is the latest, and the corresponding Python package version is 2.0.0.dev0. Below is my ONNX inference code: import onnx
import onnxruntime as ort
import numpy as np
import cv2
import os
import time
import numpy as np
import json
from omegaconf import DictConfig, OmegaConf
from typing import Any, cast
import torch
from torchvision.transforms.v2.functional import to_dtype, to_image
from PIL import Image
def preprocess(img):
if img.dtype != np.float32:
img = img.astype(np.float32)
if img.max() > 1.0:
img /= 255.0
img = np.expand_dims(img,axis=0)
img = np.transpose(img,(0,3,1,2))
return img
def read_image(path, as_tensor: bool = False) -> torch.Tensor | np.ndarray:
"""Read image from disk in RGB format.
Args:
path (str, Path): path to the image file
as_tensor (bool, optional): If True, returns the image as a tensor. Defaults to False.
Example:
>>> image = read_image("test_image.jpg")
>>> type(image)
<class 'numpy.ndarray'>
>>>
>>> image = read_image("test_image.jpg", as_tensor=True)
>>> type(image)
<class 'torch.Tensor'>
Returns:
image as numpy array
"""
image = Image.open(path).convert("RGB")
return to_dtype(to_image(image), torch.float32, scale=True) if as_tensor else np.array(image) / 255.0
def _load_metadata(path= None) -> dict | DictConfig: # noqa: PLR6301
"""Load the meta data from the given path.
Args:
path (str | Path | dict | None, optional): Path to JSON file containing the metadata.
If no path is provided, it returns an empty dict. Defaults to None.
Returns:
dict | DictConfig: Dictionary containing the metadata.
"""
metadata: dict[str, float | np.ndarray | torch.Tensor] | DictConfig = {}
print(path)
if path is not None:
config = OmegaConf.load(path)
metadata = cast(DictConfig, config)
return metadata
def normalize_min_max(
targets: np.ndarray | np.float32 | torch.Tensor,
threshold: float | np.ndarray | torch.Tensor,
min_val: float | np.ndarray | torch.Tensor,
max_val: float | np.ndarray | torch.Tensor,
) -> np.ndarray | torch.Tensor:
"""Apply min-max normalization and shift the values such that the threshold value is centered at 0.5."""
normalized = ((targets - threshold) / (max_val - min_val)) + 0.5
print('-'*20)
print(normalized)
print(targets)
if isinstance(targets, np.ndarray | np.float32 | np.float64):
normalized = np.minimum(normalized, 1)
normalized = np.maximum(normalized, 0)
elif isinstance(targets, torch.Tensor):
normalized = torch.minimum(normalized, torch.tensor(1)) # pylint: disable=not-callable
normalized = torch.maximum(normalized, torch.tensor(0)) # pylint: disable=not-callable
else:
msg = f"Targets must be either Tensor or Numpy array. Received {type(targets)}"
raise TypeError(msg)
return normalized
def _normalize(
pred_scores: torch.Tensor | np.float32,
metadata,
anomaly_maps: torch.Tensor | np.ndarray | None = None,
) -> tuple[np.ndarray | torch.Tensor | None, float]:
"""Apply normalization and resizes the image.
Args:
pred_scores (Tensor | np.float32): Predicted anomaly score
metadata (dict | DictConfig): Meta data. Post-processing step sometimes requires
additional meta data such as image shape. This variable comprises such info.
anomaly_maps (Tensor | np.ndarray | None): Predicted raw anomaly map.
Returns:
tuple[np.ndarray | torch.Tensor | None, float]: Post processed predictions that are ready to be
visualized and predicted scores.
"""
# min max normalization
#print(f'metadata is {metadata}')
if "pred_scores.min" in metadata and "pred_scores.max" in metadata:
if anomaly_maps is not None and "anomaly_maps.max" in metadata:
anomaly_maps = normalize_min_max(
anomaly_maps,
metadata["pixel_threshold"],
metadata["anomaly_maps.min"],
metadata["anomaly_maps.max"],
)
pred_scores = normalize_min_max(
pred_scores,
metadata["image_threshold"],
metadata["pred_scores.min"],
metadata["pred_scores.max"],
)
#print(f'process pred_scores is {pred_scores}')
return anomaly_maps, float(pred_scores)
# load ONNX model
onnx_model_path = '/home/ubuntu/ltt-files/anomalib-main/export/yinzhang/weights/onnx/modelModify.onnx'
metadata = _load_metadata('/home/ubuntu/ltt-files/anomalib-main/export/yinzhang/weights/onnx/metadata.json')
model = onnx.load(onnx_model_path)
# check model
onnx.checker.check_model(model)
# init ONNX Runtime
session = ort.InferenceSession(onnx_model_path, providers=['CPUExecutionProvider','CUDAExecutionProvider'])
print(f"get_providers: {session.get_providers()}")
# get input
input_name = session.get_inputs()[0].name
img_files = [f for f in os.listdir('/home/ubuntu/ltt-files/anomalib-main/datasets/bottle/test/broken_small/') if f.endswith(('.jpg', '.png'))]
# for i in img_files:
img = read_image('/home/ubuntu/ltt-files/anomalib-main/datasets/yinzhang_imgs/train/'+'22 (1).jpg')
img = preprocess(img.copy())
#print(img)
outputs = session.run(None, {input_name: img})
print(outputs)
predictions = outputs[0]
start = time.time()
anomaly_map = predictions.squeeze()
pred_score = anomaly_map.reshape(-1).max()
_, pred_score = _normalize(pred_scores=pred_score, metadata=metadata)
# print(f'times is {time.time()-start}')
# if pred_score/2 >= 0.31506848335266113:
# print('NORMAL')
# else:
# print('ABNORMAL')
#print(pred_score) |
Do you think this is similar to #2448? |
Describe the bug
When I use a general ONNX model, the preprocessing part is the same for both ONNX Runtime and OpenVINO, but the final outputs from the models are significantly different. The discrepancies between the image data and the pred_score are particularly large.
Dataset
MVTec
Model
PatchCore
Steps to reproduce the behavior
I first extract the necessary preprocessing part from openvino_inferencer.py, then the image is preprocessed and passed into the ONNX inference.
OS information
OS information:
Expected behavior
111
Screenshots
No response
Pip/GitHub
GitHub
What version/branch did you use?
No response
Configuration YAML
no
Logs
Code of Conduct
The text was updated successfully, but these errors were encountered: