Spaces:
Build error
Build error
"""Utils to help compute inference statistics.""" | |
# Copyright (C) 2020 Intel Corporation | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, | |
# software distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions | |
# and limitations under the License. | |
import time | |
from pathlib import Path | |
from typing import Dict, Iterable, List, Tuple, Union | |
import numpy as np | |
import torch | |
from omegaconf import DictConfig, ListConfig | |
from torch.utils.data import DataLoader | |
from anomalib.deploy import OpenVINOInferencer, TorchInferencer | |
from anomalib.models.components import AnomalyModule | |
class MockImageLoader: | |
"""Create mock images for inference on CPU based on the specifics of the original torch test dataset. | |
Uses yield so as to avoid storing everything in the memory. | |
Args: | |
image_size (List[int]): Size of input image | |
total_count (int): Total images in the test dataset | |
""" | |
def __init__(self, image_size: List[int], total_count: int): | |
self.total_count = total_count | |
self.image_size = image_size | |
self.image = np.ones((*self.image_size, 3)).astype(np.uint8) | |
def __len__(self): | |
"""Get total count of images.""" | |
return self.total_count | |
def __call__(self) -> Iterable[np.ndarray]: | |
"""Yield batch of generated images. | |
Args: | |
idx (int): Unused | |
""" | |
for _ in range(self.total_count): | |
yield self.image | |
def get_meta_data(model: AnomalyModule, input_size: Tuple[int, int]) -> Dict: | |
"""Get meta data for inference. | |
Args: | |
model (AnomalyModule): Trained model from which the metadata is extracted. | |
input_size (Tuple[int, int]): Input size used to resize the pixel level mean and std. | |
Returns: | |
(Dict): Metadata as dictionary. | |
""" | |
meta_data = { | |
"image_threshold": model.image_threshold.value.cpu().numpy(), | |
"pixel_threshold": model.pixel_threshold.value.cpu().numpy(), | |
"min": model.min_max.min.cpu().numpy(), | |
"max": model.min_max.max.cpu().numpy(), | |
"stats": {}, | |
} | |
image_mean = model.training_distribution.image_mean.cpu().numpy() | |
if image_mean.size > 0: | |
meta_data["stats"]["image_mean"] = image_mean | |
image_std = model.training_distribution.image_std.cpu().numpy() | |
if image_std.size > 0: | |
meta_data["stats"]["image_std"] = image_std | |
pixel_mean = model.training_distribution.pixel_mean.cpu().numpy() | |
if pixel_mean.size > 0: | |
meta_data["stats"]["pixel_mean"] = pixel_mean.reshape(input_size) | |
pixel_std = model.training_distribution.pixel_std.cpu().numpy() | |
if pixel_std.size > 0: | |
meta_data["stats"]["pixel_std"] = pixel_std.reshape(input_size) | |
return meta_data | |
def get_torch_throughput( | |
config: Union[DictConfig, ListConfig], model: AnomalyModule, test_dataset: DataLoader, meta_data: Dict | |
) -> float: | |
"""Tests the model on dummy data. Images are passed sequentially to make the comparision with OpenVINO model fair. | |
Args: | |
config (Union[DictConfig, ListConfig]): Model config. | |
model (Path): Model on which inference is called. | |
test_dataset (DataLoader): The test dataset used as a reference for the mock dataset. | |
meta_data (Dict): Metadata used for normalization. | |
Returns: | |
float: Inference throughput | |
""" | |
torch.set_grad_enabled(False) | |
model.eval() | |
inferencer = TorchInferencer(config, model) | |
torch_dataloader = MockImageLoader(config.dataset.image_size, len(test_dataset)) | |
start_time = time.time() | |
# Since we don't care about performance metrics and just the throughput, use mock data. | |
for image in torch_dataloader(): | |
inferencer.predict(image, superimpose=False, meta_data=meta_data) | |
# get throughput | |
inference_time = time.time() - start_time | |
throughput = len(test_dataset) / inference_time | |
torch.set_grad_enabled(True) | |
return throughput | |
def get_openvino_throughput( | |
config: Union[DictConfig, ListConfig], model_path: Path, test_dataset: DataLoader, meta_data: Dict | |
) -> float: | |
"""Runs the generated OpenVINO model on a dummy dataset to get throughput. | |
Args: | |
config (Union[DictConfig, ListConfig]): Model config. | |
model_path (Path): Path to folder containing the OpenVINO models. It then searches `model.xml` in the folder. | |
test_dataset (DataLoader): The test dataset used as a reference for the mock dataset. | |
meta_data (Dict): Metadata used for normalization. | |
Returns: | |
float: Inference throughput | |
""" | |
inferencer = OpenVINOInferencer(config, model_path / "model.xml") | |
openvino_dataloader = MockImageLoader(config.dataset.image_size, total_count=len(test_dataset)) | |
start_time = time.time() | |
# Create test images on CPU. Since we don't care about performance metrics and just the throughput, use mock data. | |
for image in openvino_dataloader(): | |
inferencer.predict(image, superimpose=False, meta_data=meta_data) | |
# get throughput | |
inference_time = time.time() - start_time | |
throughput = len(test_dataset) / inference_time | |
return throughput | |