Spaces:
Runtime error
Runtime error
import os | |
import cv2 | |
import numpy as np | |
from moviepy.editor import ImageSequenceClip | |
def get_video_frames(video_path): | |
"""Reads a video frame by frame | |
Args: | |
video_path: A video path | |
Returns: | |
A list of numpy arrays representing video frames | |
""" | |
vid_cap = cv2.VideoCapture(video_path) | |
frames = [] | |
while True: | |
ret, frame = vid_cap.read() | |
if ret: | |
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) | |
frames.append(frame) | |
else: | |
break | |
return frames | |
class Plot: | |
"""Class for plotting keypoints on video frames and creating a video | |
""" | |
KEYPOINT_EDGE_INDS_TO_COLOR = { | |
(0, 1): "m", | |
(0, 2): "c", | |
(1, 3): "m", | |
(2, 4): "c", | |
(0, 5): "m", | |
(0, 6): "c", | |
(5, 7): "m", | |
(7, 9): "m", | |
(6, 8): "c", | |
(8, 10): "c", | |
(5, 6): "y", | |
(5, 11): "m", | |
(6, 12): "c", | |
(11, 12): "y", | |
(11, 13): "m", | |
(13, 15): "m", | |
(12, 14): "c", | |
(14, 16): "c", | |
} | |
def add_keypoints_to_image( | |
images_array, keypoints_list | |
): | |
""" | |
Adds keypoints to the image | |
Args: | |
images_array: list of images to represent the keypoints | |
keypoints_list : list of keypoints | |
model: name of the model used to detect the keypoints | |
Returns: | |
None | |
""" | |
output_overlay_array = images_array.astype(np.int32) | |
output_overlay_list = Plot.draw_prediction_on_image( | |
output_overlay_array, keypoints_list | |
) | |
return np.array(output_overlay_list) | |
def draw_prediction_on_image( | |
image_list, keypoints_list | |
): | |
"""Draws the keypoint predictions on image. | |
Args: | |
image_list: A numpy array with shape [n, height, width, channel] | |
representing the | |
pixel values of the input image where n is number of images. | |
keypoints_list: A numpy array with shape [n, 17, 2] representing the | |
coordinates of 17 keypoints where n is number of images. | |
Returns: | |
A numpy array with shape [n, out_height, out_width, channel] | |
representing | |
the list of | |
images overlaid with keypoint predictions. | |
""" | |
height, width, channel = image_list[0].shape | |
keypoint_locs, keypoint_edges = Plot._keypoints_and_edges_for_display( | |
keypoints_list, | |
height, width | |
) | |
for img_i in range(keypoint_locs.shape[0]): | |
for edge in keypoint_edges[img_i]: | |
image = cv2.line( | |
image_list[img_i], (int(edge[0]), int(edge[1])), | |
(int(edge[2]), int(edge[3])), color=(0, 0, 255), thickness=3 | |
) | |
for center_x, center_y in keypoint_locs[img_i]: | |
image = cv2.circle( | |
image_list[img_i], (int(center_x), int(center_y)), radius=5, | |
color=(255, 0, 0), thickness=-1 | |
) | |
image_list[img_i] = image | |
return image_list | |
def _keypoints_and_edges_for_display( | |
keypoints_list, height, width, | |
): | |
"""Returns high confidence keypoints and edges for visualization. | |
Args: | |
keypoints_list: A numpy array with shape [1, 1, 17, 3] representing | |
the keypoint coordinates and scores returned from the MoveNet model. | |
height: height of the image in pixels. | |
width: width of the image in pixels. | |
keypoint_threshold: minimum confidence score for keypoint to be | |
visualized. | |
Returns: | |
A (kpts_absolute_xy, edges_xy) containing: | |
* array with shape [n, 17, 2] representing the coordinates of all | |
keypoints of all detected entities in n images; | |
* array with shape [n, 18, 4] representing the coordinates of all | |
skeleton edges of all detected entities in n images; | |
""" | |
kpts_x = width * keypoints_list[:, :, 0] | |
kpts_y = height * keypoints_list[:, :, 1] | |
edge_pair = np.array(list(Plot.KEYPOINT_EDGE_INDS_TO_COLOR.keys())) | |
kpts_absolute_xy = np.stack( | |
[kpts_x, kpts_y], axis=-1 | |
) | |
x_start = kpts_x[:, edge_pair[:, 0]] | |
y_start = kpts_y[:, edge_pair[:, 0]] | |
x_end = kpts_x[:, edge_pair[:, 1]] | |
y_end = kpts_y[:, edge_pair[:, 1]] | |
edges = np.stack([x_start, y_start, x_end, y_end], axis=2) | |
return kpts_absolute_xy, edges | |
def resize_and_concat(ref_image_list, test_image_list): | |
"""Resizes either of reference frames list or test frames list to | |
make both list of equal shape and merges both frames side by side | |
Args: | |
ref_image_list: A list of numpy array representing reference video | |
frames | |
test_image_list: A list of numpy array representing test video frames | |
Returns: | |
concat_img_list: A list of numpy array representing merged video | |
frames | |
""" | |
def pad_image(image_list, pad_axis, pad_len, odd_len_diff): | |
"""pads given number of pixels to image_list on given axis | |
Args: | |
image_list: A list of numpy array representing video frames | |
pad_axis: A list of numpy array representing test video frames | |
pad_len: number of pixels to pad on either side of frame | |
odd_len_diff: 1 if difference between reference and test is odd | |
else 0 | |
Returns: | |
padded video frame | |
""" | |
if pad_axis == 0: | |
return np.pad( | |
image_list, ( | |
(0, 0), (pad_len, pad_len + odd_len_diff), (0, 0), | |
(0, 0)), 'constant', | |
constant_values=(0) | |
) | |
elif pad_axis == 1: | |
return np.pad( | |
image_list, ( | |
(0, 0), (0, 0), (pad_len, pad_len + odd_len_diff), | |
(0, 0)), 'constant', | |
constant_values=(0) | |
) | |
ref_height, ref_width, _ = ref_image_list[0].shape | |
test_height, test_width, _ = test_image_list[0].shape | |
pad_height = abs(test_height - ref_height) // 2 | |
odd_height_diff = (test_height - ref_height) % 2 | |
if ref_height < test_height: | |
ref_image_list = pad_image( | |
ref_image_list, 0, pad_height, odd_height_diff | |
) | |
elif ref_height > test_height: | |
test_image_list = pad_image( | |
test_image_list, 0, pad_height, odd_height_diff | |
) | |
pad_width = abs(test_width - ref_width) // 2 | |
odd_width_diff = (test_width - ref_width) % 2 | |
if ref_width < test_width: | |
ref_image_list = pad_image( | |
ref_image_list, 1, pad_width, odd_width_diff | |
) | |
elif ref_width > test_width: | |
test_image_list = pad_image( | |
test_image_list, 1, pad_width, odd_width_diff | |
) | |
concat_img_list = np.concatenate( | |
(ref_image_list, test_image_list), axis=2 | |
) | |
return concat_img_list | |
def overlay_score_on_images(image_list, scores): | |
"""writes score on given image list | |
Args: | |
image_list: A list of numpy array representing video frames | |
scores: A list of score between reference and test keypoints | |
Returns: | |
A list of numpy array with score overlayed on it | |
""" | |
for i in range(len(image_list)): | |
image = image_list[i, :, :, :] | |
txt = f"Score : {scores[i]}" | |
image = cv2.putText( | |
image, txt, (5, 10), cv2.FONT_HERSHEY_SIMPLEX, | |
0.4, (255, 0, 0), 1, cv2.LINE_AA | |
) | |
image_list[i, :, :, :] = image | |
return image_list | |
def plot_matching( | |
ref_frames, test_frames, ref_keypoints, test_keypoints, ref_frames_idx, | |
test_frames_idx, costs, output_path | |
): | |
"""creates a video of reference and test video frames with keypoints | |
overlayed on them | |
Args: | |
ref_frames: A list of numpy array representing reference video frames | |
test_frames: A list of numpy array representing test video frames | |
ref_keypoints: A list of numpy array representing reference video | |
keypoints | |
test_keypoints: A list of numpy array representing reference video | |
keypoints | |
ref_frames_idx: A list of reference frame indices | |
test_frames_idx: A list of test frame indices | |
costs: A list of score between reference and test keypoints | |
output_path: path at which output video to be stored | |
""" | |
if (ref_frames_idx is not None) and ( | |
test_frames_idx is not None) and len(ref_frames_idx) == len( | |
test_frames_idx | |
): | |
ref_frames = ref_frames[ref_frames_idx] | |
test_frames = test_frames[test_frames_idx] | |
ref_keypoints = ref_keypoints[ref_frames_idx] | |
test_keypoints = test_keypoints[test_frames_idx] | |
if costs is None: | |
costs = ['N/A'] * len(ref_frames) | |
display_line = [ | |
f"Cost: {costs[i]} Ref frame: {ref_frames_idx[i]} Test " \ | |
f"frame: {test_frames_idx[i]}" | |
for i in range(len(ref_frames_idx))] | |
ref_image_list = Plot.add_keypoints_to_image( | |
ref_frames, ref_keypoints | |
) | |
test_image_list = Plot.add_keypoints_to_image( | |
test_frames, test_keypoints | |
) | |
comparison_img_list = Plot.resize_and_concat( | |
ref_image_list, test_image_list | |
) | |
comparison_img_list = Plot.overlay_score_on_images( | |
comparison_img_list, display_line | |
) | |
video = ImageSequenceClip(list(comparison_img_list), fps=5) | |
video.write_videofile(output_path, fps=5) | |