Module sagemaker_defect_detection.utils.visualize
None
None
View Source
from typing import Iterable, List, Union, Tuple
import numpy as np
import torch
from matplotlib import pyplot as plt
import cv2
import torch
TEXT_COLOR = (255, 255, 255) # White
CLASSES = {
"crazing": "Cr",
"inclusion": "In",
"pitted_surface": "PS",
"patches": "Pa",
"rolled-in_scale": "RS",
"scratches": "Sc",
}
CATEGORY_ID_TO_NAME = {i: name for i, name in enumerate(CLASSES.keys(), start=1)}
def unnormalize_to_hwc(
image: torch.Tensor, mean: List[float] = [0.485, 0.456, 0.406], std: List[float] = [0.229, 0.224, 0.225]
) -> np.ndarray:
"""
Unnormalizes and a normlized image tensor [0, 1] CHW -> HWC [0, 255]
Parameters
----------
image : torch.Tensor
Normalized image
mean : List[float], optional
RGB averages used in normalization, by default [0.485, 0.456, 0.406] from imagenet1k
std : List[float], optional
RGB standard deviations used in normalization, by default [0.229, 0.224, 0.225] from imagenet1k
Returns
-------
np.ndarray
Unnormalized image as numpy array
"""
image = image.numpy().transpose(1, 2, 0) # HWC
image = (image * std + mean).clip(0, 1)
image = (image * 255).astype(np.uint8)
return image
def visualize_bbox(img: np.ndarray, bbox: np.ndarray, class_name: str, color, thickness: int = 2) -> np.ndarray:
"""
Uses cv2 to draw colored bounding boxes and class names in an image
Parameters
----------
img : np.ndarray
[description]
bbox : np.ndarray
[description]
class_name : str
Class name
color : tuple
BGR tuple
thickness : int, optional
Bouding box thickness, by default 2
"""
x_min, y_min, x_max, y_max = tuple(map(int, bbox))
cv2.rectangle(img, (x_min, y_min), (x_max, y_max), color=color, thickness=thickness)
((text_width, text_height), _) = cv2.getTextSize(class_name, cv2.FONT_HERSHEY_SIMPLEX, 0.35, 1)
cv2.rectangle(img, (x_min, y_min - int(1.3 * text_height)), (x_min + text_width, y_min), color, -1)
cv2.putText(
img,
text=class_name,
org=(x_min, y_min - int(0.3 * text_height)),
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=0.35,
color=TEXT_COLOR,
lineType=cv2.LINE_AA,
)
return img
def visualize(
image: np.ndarray,
bboxes: Iterable[Union[torch.Tensor, np.ndarray]] = [],
category_ids: Iterable[Union[torch.Tensor, np.ndarray]] = [],
colors: Iterable[Tuple[int, int, int]] = [],
titles: Iterable[str] = [],
category_id_to_name=CATEGORY_ID_TO_NAME,
dpi=150,
) -> None:
"""
Applies the bounding boxes and category ids to an image
Parameters
----------
image : np.ndarray
Image as numpy array
bboxes : Iterable[Union[torch.Tensor, np.ndarray]], optional
Bouding boxes, by default []
category_ids : Iterable[Union[torch.Tensor, np.ndarray]], optional
Category ids, by default []
colors : Iterable[Tuple[int, int, int]], optional
Colors for each bounding box, by default [()]
titles : Iterable[str], optional
Titles for each image, by default []
category_id_to_name : Dict[str, str], optional
Dictionary of category ids to names, by default CATEGORY_ID_TO_NAME
dpi : int, optional
DPI for clarity, by default 150
"""
bboxes, category_ids, colors, titles = list(map(list, [bboxes, category_ids, colors, titles])) # type: ignore
n = len(bboxes)
assert (
n == len(category_ids) == len(colors) == len(titles) - 1
), f"number of bboxes, category ids, colors and titles (minus one) do not match"
plt.figure(dpi=dpi)
ncols = n + 1
plt.subplot(1, ncols, 1)
img = image.copy()
plt.axis("off")
plt.title(titles[0])
plt.imshow(image)
if not len(bboxes):
return
titles = titles[1:]
for i in range(2, ncols + 1):
img = image.copy()
plt.subplot(1, ncols, i)
plt.axis("off")
j = i - 2
plt.title(titles[j])
for bbox, category_id in zip(bboxes[j], category_ids[j]): # type: ignore
if isinstance(bbox, torch.Tensor):
bbox = bbox.numpy()
if isinstance(category_id, torch.Tensor):
category_id = category_id.numpy()
if isinstance(category_id, np.ndarray):
category_id = category_id.item()
class_name = category_id_to_name[category_id]
img = visualize_bbox(img, bbox, class_name, color=colors[j])
plt.imshow(img)
return
Variables
CATEGORY_ID_TO_NAME
CLASSES
TEXT_COLOR
Functions
unnormalize_to_hwc
def unnormalize_to_hwc(
image: torch.Tensor,
mean: List[float] = [0.485, 0.456, 0.406],
std: List[float] = [0.229, 0.224, 0.225]
) -> numpy.ndarray
Unnormalizes and a normlized image tensor [0, 1] CHW -> HWC [0, 255]
Parameters:
Name | Type | Description | Default |
---|---|---|---|
image | torch.Tensor | Normalized image | None |
mean | List[float] | RGB averages used in normalization, by default [0.485, 0.456, 0.406] from imagenet1k | None |
std | List[float] | RGB standard deviations used in normalization, by default [0.229, 0.224, 0.225] from imagenet1k | None |
Returns:
Type | Description |
---|---|
np.ndarray | Unnormalized image as numpy array |
View Source
def unnormalize_to_hwc(
image: torch.Tensor, mean: List[float] = [0.485, 0.456, 0.406], std: List[float] = [0.229, 0.224, 0.225]
) -> np.ndarray:
"""
Unnormalizes and a normlized image tensor [0, 1] CHW -> HWC [0, 255]
Parameters
----------
image : torch.Tensor
Normalized image
mean : List[float], optional
RGB averages used in normalization, by default [0.485, 0.456, 0.406] from imagenet1k
std : List[float], optional
RGB standard deviations used in normalization, by default [0.229, 0.224, 0.225] from imagenet1k
Returns
-------
np.ndarray
Unnormalized image as numpy array
"""
image = image.numpy().transpose(1, 2, 0) # HWC
image = (image * std + mean).clip(0, 1)
image = (image * 255).astype(np.uint8)
return image
visualize
def visualize(
image: numpy.ndarray,
bboxes: Iterable[Union[torch.Tensor, numpy.ndarray]] = [],
category_ids: Iterable[Union[torch.Tensor, numpy.ndarray]] = [],
colors: Iterable[Tuple[int, int, int]] = [],
titles: Iterable[str] = [],
category_id_to_name={1: 'crazing', 2: 'inclusion', 3: 'pitted_surface', 4: 'patches', 5: 'rolled-in_scale', 6: 'scratches'},
dpi=150
) -> None
Applies the bounding boxes and category ids to an image
Parameters:
Name | Type | Description | Default |
---|---|---|---|
image | np.ndarray | Image as numpy array | None |
bboxes | Iterable[Union[torch.Tensor, np.ndarray]] | Bouding boxes, by default [] | None |
category_ids | Iterable[Union[torch.Tensor, np.ndarray]] | Category ids, by default [] | None |
colors | Iterable[Tuple[int, int, int]] | Colors for each bounding box, by default [()] | None |
titles | Iterable[str] | Titles for each image, by default [] | None |
category_id_to_name | Dict[str, str] | Dictionary of category ids to names, by default CATEGORY_ID_TO_NAME | CATEGORY_ID_TO_NAME |
dpi | int | DPI for clarity, by default 150 | 150 |
View Source
def visualize(
image: np.ndarray,
bboxes: Iterable[Union[torch.Tensor, np.ndarray]] = [],
category_ids: Iterable[Union[torch.Tensor, np.ndarray]] = [],
colors: Iterable[Tuple[int, int, int]] = [],
titles: Iterable[str] = [],
category_id_to_name=CATEGORY_ID_TO_NAME,
dpi=150,
) -> None:
"""
Applies the bounding boxes and category ids to an image
Parameters
----------
image : np.ndarray
Image as numpy array
bboxes : Iterable[Union[torch.Tensor, np.ndarray]], optional
Bouding boxes, by default []
category_ids : Iterable[Union[torch.Tensor, np.ndarray]], optional
Category ids, by default []
colors : Iterable[Tuple[int, int, int]], optional
Colors for each bounding box, by default [()]
titles : Iterable[str], optional
Titles for each image, by default []
category_id_to_name : Dict[str, str], optional
Dictionary of category ids to names, by default CATEGORY_ID_TO_NAME
dpi : int, optional
DPI for clarity, by default 150
"""
bboxes, category_ids, colors, titles = list(map(list, [bboxes, category_ids, colors, titles])) # type: ignore
n = len(bboxes)
assert (
n == len(category_ids) == len(colors) == len(titles) - 1
), f"number of bboxes, category ids, colors and titles (minus one) do not match"
plt.figure(dpi=dpi)
ncols = n + 1
plt.subplot(1, ncols, 1)
img = image.copy()
plt.axis("off")
plt.title(titles[0])
plt.imshow(image)
if not len(bboxes):
return
titles = titles[1:]
for i in range(2, ncols + 1):
img = image.copy()
plt.subplot(1, ncols, i)
plt.axis("off")
j = i - 2
plt.title(titles[j])
for bbox, category_id in zip(bboxes[j], category_ids[j]): # type: ignore
if isinstance(bbox, torch.Tensor):
bbox = bbox.numpy()
if isinstance(category_id, torch.Tensor):
category_id = category_id.numpy()
if isinstance(category_id, np.ndarray):
category_id = category_id.item()
class_name = category_id_to_name[category_id]
img = visualize_bbox(img, bbox, class_name, color=colors[j])
plt.imshow(img)
return
visualize_bbox
def visualize_bbox(
img: numpy.ndarray,
bbox: numpy.ndarray,
class_name: str,
color,
thickness: int = 2
) -> numpy.ndarray
Uses cv2 to draw colored bounding boxes and class names in an image
Parameters:
Name | Type | Description | Default |
---|---|---|---|
img | np.ndarray | [description] | None |
bbox | np.ndarray | [description] | None |
class_name | str | Class name | None |
color | tuple | BGR tuple | None |
thickness | int | Bouding box thickness, by default 2 | 2 |
View Source
def visualize_bbox(img: np.ndarray, bbox: np.ndarray, class_name: str, color, thickness: int = 2) -> np.ndarray:
"""
Uses cv2 to draw colored bounding boxes and class names in an image
Parameters
----------
img : np.ndarray
[description]
bbox : np.ndarray
[description]
class_name : str
Class name
color : tuple
BGR tuple
thickness : int, optional
Bouding box thickness, by default 2
"""
x_min, y_min, x_max, y_max = tuple(map(int, bbox))
cv2.rectangle(img, (x_min, y_min), (x_max, y_max), color=color, thickness=thickness)
((text_width, text_height), _) = cv2.getTextSize(class_name, cv2.FONT_HERSHEY_SIMPLEX, 0.35, 1)
cv2.rectangle(img, (x_min, y_min - int(1.3 * text_height)), (x_min + text_width, y_min), color, -1)
cv2.putText(
img,
text=class_name,
org=(x_min, y_min - int(0.3 * text_height)),
fontFace=cv2.FONT_HERSHEY_SIMPLEX,
fontScale=0.35,
color=TEXT_COLOR,
lineType=cv2.LINE_AA,
)
return img