Source code for nicetoolbox.detectors.feature_detectors.leaning.utils

"""
Utility functions for visualizing leaning angles and calculating the angle between
three points.
"""

import logging
import os

import cv2
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas


[docs]def calculate_angle_btw_three_points(data): """ Calculate the angle between three points in a 3D space. Args: data (numpy.ndarray): A 4D numpy array with shape (num_subjects, num_frames, num_points, num_coordinates) representing the coordinates of points A, B, and C for each subject and frame. Returns: numpy.ndarray: A 4D numpy array with shape (num_subjects, num_frames, num_points, 1) containing the angle between the three points for each subject and frame. """ # Extract the coordinates of A, B, and C for all frames A = data[:, :, :, 0] B = data[:, :, :, 1] C = data[:, :, :, 2] # Compute vectors AB and BC for all frames AB = B - A BC = C - B # Compute the dot product and magnitudes for all frames dot_product = np.sum(AB * BC, axis=-1, keepdims=True) mag_AB = np.linalg.norm(AB, axis=-1, keepdims=True) mag_BC = np.linalg.norm(BC, axis=-1, keepdims=True) # Compute the cosine of the angle cos_angle = dot_product / (mag_AB * mag_BC) # Clip values to be in the range [-1, 1] to avoid issues with numerical precision cos_angle = np.clip(cos_angle, -1.0, 1.0) # Compute the angles in radians angles_rad = np.arccos(cos_angle) # Convert to degrees angles_deg = np.degrees(angles_rad) return angles_deg
[docs]def visualize_lean_in_out_per_person( hip_angle, person_list, output_folder, camera_names=None ): """ Visualize the leaning angle between midpoint of shoulders, hips, and knees for each person. Args: hip_angle (numpy.ndarray): A 4D numpy array with shape (num_subjects, num_cameras, num_frames, 2) representing the leaning angles (axis angle and derivative) for each subject, camera, and frame. person_list (list): A list of strings representing the names of the persons in frame. output_folder (str): The path to the output folder where the plots will be saved. camera_names (list, optional): A list of strings representing the names of the cameras. Defaults to None. Returns: None """ if len(hip_angle) != len(person_list): logging.error("Number of subjects and data shape mismatch!") for camera_idx in range(hip_angle.shape[1]): plt.title( f"Leaning Angle between Midpoint of Shoulders, Hips, and Knees " f"({person_list})" ) _, axes = plt.subplots(2, len(person_list), figsize=(10, 8)) axes = axes.reshape(2, len(person_list)) for i in range(len(person_list)): axes[0, i].plot( hip_angle[i, camera_idx, :, 0], label=f"Leaning Angle {person_list[i]}" ) axes[1, i].plot( hip_angle[i, camera_idx, :, 1], label=f"Derivative of Leaning Angle {person_list[i]}", ) # Set labels and legends for each subplot axes[0, i].set_xlabel("FrameNo") axes[1, i].set_xlabel("FrameNo") axes[0, i].set_ylabel("AxisAngle") axes[1, i].set_ylabel("Gradient of AxisAngle") axes[0, i].legend() axes[1, i].legend() # Adjust layout plt.tight_layout() # Save the plot camera_name = ( camera_names[camera_idx] if camera_names is not None else f"camera_{camera_idx}" ) plt.savefig( os.path.join(output_folder, f"leaning_angle_graph_{camera_name}.png"), dpi=500, )
[docs]def frame_with_linegraph(frame, current_frame, data, fig, canvas, axL, axR): """ Combine a video frame with the plots for PersonL and PersonR up to the current frame. Args: frame (numpy.ndarray): The current video frame as a numpy array. current_frame (int): The index of the current frame. data (list): A list containing two numpy arrays, dataL and dataR, representing the leaning angles for PersonL and PersonR respectively. fig (matplotlib.figure.Figure): The matplotlib figure object. canvas (matplotlib.backends.backend_agg.FigureCanvasAgg): The matplotlib canvas object. axL (matplotlib.axes._subplots.AxesSubplot): The left subplot axis for PersonL. axR (matplotlib.axes._subplots.AxesSubplot): The right subplot axis for PersonR. Returns: numpy.ndarray: A numpy array representing the combined video frame and the plots. """ colors = ["#98FB98", "#FFB347", "#DDA0DD", "#ADD8E6"] if len(data) != 2: logging.error( "The data shape is wrong. Data should be given as a list [dataL, dataR]" ) dataL, dataR = data axL.clear() axR.clear() axL.plot(dataL[:current_frame], label="leaning_angle", color=colors[1]) axR.plot(dataR[:current_frame], label="leaning_angle", color=colors[1]) # Redraw the canvas canvas.draw() graph_img = np.frombuffer(canvas.tostring_rgb(), dtype=np.uint8) graph_img = graph_img.reshape(fig.canvas.get_width_height()[::-1] + (3,)) combined_img = cv2.vconcat([frame, graph_img]) return combined_img
[docs]def create_video_canvas(num_of_frames, global_min, global_max): """ Create a matplotlib figure and canvas for creating a video with two subplots for PersonL and PersonR. Args: num_of_frames (int): The total number of frames in the video. global_min (float): The global minimum value for the y-axis. global_max (float): The global maximum value for the y-axis. Returns: fig (matplotlib.figure.Figure): The matplotlib figure object. canvas (matplotlib.backends.backend_agg.FigureCanvasAgg): The matplotlib canvas object. axL (matplotlib.axes._subplots.AxesSubplot): The left subplot axis for PersonL. axR (matplotlib.axes._subplots.AxesSubplot): The right subplot axis for PersonR. """ fig, (axL, axR) = plt.subplots(1, 2, figsize=(6.4, 2.4)) fig.patch.set_facecolor("black") for ax, title in zip([axL, axR], ["PersonL", "PersonR"]): ax.set_title(title, color="white") ax.set_facecolor("black") for ax in [axL, axR]: ax.set_xlim(0, num_of_frames) ax.set_ylim(global_min, global_max) ax.set_xticks(range(num_of_frames + 1)) ax.tick_params(axis="both", colors="white") ax.spines["bottom"].set_color("white") ax.spines["left"].set_color("white") ax.spines["right"].set_visible(False) ax.spines["top"].set_visible(False) plt.subplots_adjust(bottom=0.20) leg = plt.legend(loc="upper center", bbox_to_anchor=(1.0, 0.8), facecolor="black") for text in leg.get_texts(): text.set_color("white") canvas = FigureCanvas(fig) return fig, canvas, axL, axR