Spaces:
Build error
Build error
"""Anomalib Inferencer Script. | |
This script performs inference by reading a model config file from | |
command line, and show the visualization results. | |
""" | |
# 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 warnings | |
from argparse import ArgumentParser, Namespace | |
from importlib import import_module | |
from pathlib import Path | |
from typing import Optional | |
import cv2 | |
import numpy as np | |
from anomalib.config import get_configurable_parameters | |
from anomalib.deploy.inferencers.base import Inferencer | |
def get_args() -> Namespace: | |
"""Get command line arguments. | |
Returns: | |
Namespace: List of arguments. | |
""" | |
parser = ArgumentParser() | |
# --model_config_path will be deprecated in 0.2.8 and removed in 0.2.9 | |
parser.add_argument("--model_config_path", type=str, required=False, help="Path to a model config file") | |
parser.add_argument("--config", type=Path, required=True, help="Path to a model config file") | |
parser.add_argument("--weight_path", type=Path, required=True, help="Path to a model weights") | |
parser.add_argument("--image_path", type=Path, required=True, help="Path to an image to infer.") | |
parser.add_argument("--save_path", type=Path, required=False, help="Path to save the output image.") | |
parser.add_argument("--meta_data", type=Path, required=False, help="Path to JSON file containing the metadata.") | |
parser.add_argument( | |
"--overlay_mask", | |
type=bool, | |
required=False, | |
default=False, | |
help="Overlay the segmentation mask on the image. It assumes that the task is segmentation.", | |
) | |
args = parser.parse_args() | |
if args.model_config_path is not None: | |
warnings.warn( | |
message="--model_config_path will be deprecated in v0.2.8 and removed in v0.2.9. Use --config instead.", | |
category=DeprecationWarning, | |
stacklevel=2, | |
) | |
args.config = args.model_config_path | |
return args | |
def add_label(prediction: np.ndarray, scores: float, font: int = cv2.FONT_HERSHEY_PLAIN) -> np.ndarray: | |
"""If the model outputs score, it adds the score to the output image. | |
Args: | |
prediction (np.ndarray): Resized anomaly map. | |
scores (float): Confidence score. | |
Returns: | |
np.ndarray: Image with score text. | |
""" | |
text = f"Confidence Score {scores:.0%}" | |
font_size = prediction.shape[1] // 1024 + 1 # Text scale is calculated based on the reference size of 1024 | |
(width, height), baseline = cv2.getTextSize(text, font, font_size, thickness=font_size // 2) | |
label_patch = np.zeros((height + baseline, width + baseline, 3), dtype=np.uint8) | |
label_patch[:, :] = (225, 252, 134) | |
cv2.putText(label_patch, text, (0, baseline // 2 + height), font, font_size, 0) | |
prediction[: baseline + height, : baseline + width] = label_patch | |
return prediction | |
def stream() -> None: | |
"""Stream predictions. | |
Show/save the output if path is to an image. If the path is a directory, go over each image in the directory. | |
""" | |
# Get the command line arguments, and config from the config.yaml file. | |
# This config file is also used for training and contains all the relevant | |
# information regarding the data, model, train and inference details. | |
args = get_args() | |
config = get_configurable_parameters(config_path=args.config) | |
# Get the inferencer. We use .ckpt extension for Torch models and (onnx, bin) | |
# for the openvino models. | |
extension = args.weight_path.suffix | |
inferencer: Inferencer | |
if extension in (".ckpt"): | |
module = import_module("anomalib.deploy.inferencers.torch") | |
TorchInferencer = getattr(module, "TorchInferencer") # pylint: disable=invalid-name | |
inferencer = TorchInferencer(config=config, model_source=args.weight_path, meta_data_path=args.meta_data) | |
elif extension in (".onnx", ".bin", ".xml"): | |
module = import_module("anomalib.deploy.inferencers.openvino") | |
OpenVINOInferencer = getattr(module, "OpenVINOInferencer") # pylint: disable=invalid-name | |
inferencer = OpenVINOInferencer(config=config, path=args.weight_path, meta_data_path=args.meta_data) | |
else: | |
raise ValueError( | |
f"Model extension is not supported. Torch Inferencer exptects a .ckpt file," | |
f"OpenVINO Inferencer expects either .onnx, .bin or .xml file. Got {extension}" | |
) | |
if args.image_path.is_dir(): | |
# Write the output to save_path in the same structure as the input directory. | |
for image in args.image_path.glob("**/*"): | |
if image.is_file() and image.suffix in (".jpg", ".png", ".jpeg"): | |
# Here save_path is assumed to be a directory. Image subdirectories are appended to the save_path. | |
save_path = Path(args.save_path / image.relative_to(args.image_path).parent) if args.save_path else None | |
infer(image, inferencer, save_path, args.overlay_mask) | |
elif args.image_path.suffix in (".jpg", ".png", ".jpeg"): | |
infer(args.image_path, inferencer, args.save_path, args.overlay_mask) | |
else: | |
raise ValueError( | |
f"Image extension is not supported. Supported extensions are .jpg, .png, .jpeg." | |
f" Got {args.image_path.suffix}" | |
) | |
def infer(image_path: Path, inferencer: Inferencer, save_path: Optional[Path] = None, overlay: bool = False) -> None: | |
"""Perform inference on a single image. | |
Args: | |
image_path (Path): Path to image/directory containing images. | |
inferencer (Inferencer): Inferencer to use. | |
save_path (Path, optional): Path to save the output image. If this is None, the output is visualized. | |
overlay (bool, optional): Overlay the segmentation mask on the image. It assumes that the task is segmentation. | |
""" | |
# Perform inference for the given image or image path. if image | |
# path is provided, `predict` method will read the image from | |
# file for convenience. We set the superimpose flag to True | |
# to overlay the predicted anomaly map on top of the input image. | |
output = inferencer.predict(image=image_path, superimpose=True, overlay_mask=overlay) | |
# Incase both anomaly map and scores are returned add scores to the image. | |
if isinstance(output, tuple): | |
anomaly_map, score = output | |
output = add_label(anomaly_map, score) | |
# Show or save the output image, depending on what's provided as | |
# the command line argument. | |
output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR) | |
if save_path is None: | |
cv2.imshow("Anomaly Map", output) | |
cv2.waitKey(0) # wait for any key press | |
else: | |
# Create directory for parents if it doesn't exist. | |
save_path.parent.mkdir(parents=True, exist_ok=True) | |
if save_path.suffix == "": # This is a directory | |
save_path.mkdir(exist_ok=True) # Create current directory | |
save_path = save_path / image_path.name | |
cv2.imwrite(filename=str(save_path), img=output) | |
if __name__ == "__main__": | |
stream() | |