Source code for realutils.face.insightface.visual

"""
This module provides functionality for visualizing face detection results on images.
It includes utilities for drawing bounding boxes, keypoints, and labels on detected faces.
The visualization can be customized with different colors, font sizes, and display options.
"""
import math
from typing import List, Optional

from PIL import ImageDraw, ImageFont
from hbutils.color import rnd_colors, Color
from imgutils.data import ImageTyping, load_image
from imgutils.detect.visual import _try_get_font_from_matplotlib

from .base import Face


[docs]def isf_faces_visualize(image: ImageTyping, faces: List[Face], text_padding: int = 6, fontsize: int = 12, keypoint_size: int = 12, box_color: str = '#ff00ee', max_short_edge_size: Optional[int] = None, fp=None, no_label: bool = False): """ Visualize face detection results by drawing bounding boxes, keypoints and labels on an image. This function takes an input image and a list of detected faces, then draws: - Bounding boxes around each detected face - Keypoints for facial features using randomly generated colors - Optional labels showing detection confidence scores The visualization can be customized through various parameters like font size, box colors, and whether to show labels. :param image: Input image to visualize detections on. Can be a PIL Image, numpy array, or path to image file. :type image: ImageTyping :param faces: List of detected Face objects containing bounding boxes, keypoints and scores. :type faces: List[Face] :param text_padding: Padding around label text in pixels. :type text_padding: int :param fontsize: Font size for label text. :type fontsize: int :param keypoint_size: Size of keypoint markers in pixels. :type keypoint_size: int :param box_color: Color of bounding box in hex format (e.g. '#ff00ee'). :type box_color: str :param max_short_edge_size: Maximum size of shortest image edge. If specified, image will be resized while maintaining aspect ratio. :type max_short_edge_size: Optional[int] :param fp: Font properties for matplotlib font. Only used if matplotlib is available. :type fp: matplotlib.font_manager.FontProperties or None :param no_label: If True, suppresses drawing of labels. :type no_label: bool :return: PIL Image with visualized detection results. :rtype: PIL.Image.Image :example: >>> from realutils.face.insightface import isf_faces_visualize, isf_detect_faces >>> from PIL import Image >>> >>> image = Image.open('face.jpg') >>> faces = isf_detect_faces(image) >>> visualized = isf_faces_visualize( ... image, ... faces, ... fontsize=14, ... box_color='#00ff00' ... ) >>> visualized.show() """ image = load_image(image, force_background=None, mode='RGBA') original_width, original_height = image.width, image.height if max_short_edge_size is not None and max_short_edge_size < min(original_height, original_width): r = max_short_edge_size / min(original_height, original_width) new_width = int(math.ceil(original_width * r)) new_height = int(math.ceil(original_height * r)) else: new_width, new_height = original_width, original_height visual_image = image.copy() if (new_width, new_height) != (original_width, original_height): visual_image = visual_image.resize((new_width, new_height)) draw = ImageDraw.Draw(visual_image, mode='RGBA') font = _try_get_font_from_matplotlib(fp, fontsize) or ImageFont.load_default() kps_count = max([len(face.keypoints) for face in faces]) if faces else 5 kps_colors = list(map(str, rnd_colors(kps_count))) for _, face in enumerate(faces): (x0, y0, x1, y1), label, score = face.to_det_tuple() x0, y0 = int(x0 * new_width / original_width), int(y0 * new_height / original_height) x1, y1 = int(x1 * new_width / original_width), int(y1 * new_height / original_height) keypoints = face.keypoints keypoints = [ (int(x * new_width / original_width), int(y * new_height / original_height)) for x, y in keypoints ] draw.rectangle((x0, y0, x1, y1), outline=box_color, width=2) for ki, (kx, ky) in enumerate(keypoints): draw.ellipse( ( kx - keypoint_size / 2, ky - keypoint_size / 2, kx + keypoint_size / 2, ky + keypoint_size / 2, ), fill=kps_colors[ki], outline=kps_colors[ki], ) if not no_label: label_text = f'{label}: {score * 100:.2f}%' _t_x0, _t_y0, _t_x1, _t_y1 = draw.textbbox((x0, y0), label_text, font=font) _t_width, _t_height = _t_x1 - _t_x0, _t_y1 - _t_y0 if y0 - _t_height - text_padding < 0: _t_text_rect = (x0, y0, x0 + _t_width + text_padding * 2, y0 + _t_height + text_padding * 2) _t_text_co = (x0 + text_padding, y0 + text_padding) else: _t_text_rect = (x0, y0 - _t_height - text_padding * 2, x0 + _t_width + text_padding * 2, y0) _t_text_co = (x0 + text_padding, y0 - _t_height - text_padding) draw.rectangle(_t_text_rect, fill=str(Color(box_color, alpha=0.5))) draw.text(_t_text_co, label_text, fill="black", font=font) return visual_image