|
import numpy as np
|
|
import cv2
|
|
from scipy.interpolate import interp1d
|
|
|
|
class DanceGenerator:
|
|
def __init__(self):
|
|
self.prev_moves = []
|
|
self.style_memory = []
|
|
self.avatar = cv2.imread('assets/dancer_avatar.png')
|
|
|
|
def generate_dance_sequence(self, all_poses, mode, total_frames, frame_size):
|
|
"""Generate complete dance sequence for the entire video"""
|
|
height, width = frame_size
|
|
sequence = []
|
|
|
|
if mode == "Sync Partner":
|
|
sequence = self._generate_sync_sequence(all_poses, total_frames, frame_size)
|
|
else:
|
|
sequence = self._generate_creative_sequence(all_poses, total_frames, frame_size)
|
|
|
|
return sequence
|
|
|
|
def _generate_sync_sequence(self, all_poses, total_frames, frame_size):
|
|
"""Generate synchronized dance sequence"""
|
|
height, width = frame_size
|
|
sequence = []
|
|
|
|
|
|
pose_arrays = []
|
|
for pose in all_poses:
|
|
if pose is not None:
|
|
pose_arrays.append(self._landmarks_to_array(pose))
|
|
else:
|
|
pose_arrays.append(None)
|
|
|
|
|
|
for i in range(total_frames):
|
|
frame = np.zeros((height, width, 3), dtype=np.uint8)
|
|
|
|
if pose_arrays[i] is not None:
|
|
|
|
mirrored = self._mirror_movements(pose_arrays[i])
|
|
|
|
|
|
if i > 0 and pose_arrays[i-1] is not None:
|
|
mirrored = self._smooth_transition(pose_arrays[i-1], mirrored, 0.3)
|
|
|
|
|
|
frame = self._create_enhanced_dance_frame(
|
|
mirrored,
|
|
frame_size,
|
|
add_effects=True
|
|
)
|
|
|
|
sequence.append(frame)
|
|
|
|
return sequence
|
|
|
|
def _generate_creative_sequence(self, all_poses, total_frames, frame_size):
|
|
"""Generate creative dance sequence based on style"""
|
|
height, width = frame_size
|
|
sequence = []
|
|
|
|
|
|
style_patterns = self._analyze_style_patterns(all_poses)
|
|
|
|
|
|
for i in range(total_frames):
|
|
frame = np.zeros((height, width, 3), dtype=np.uint8)
|
|
|
|
|
|
new_pose = self._generate_style_based_pose(style_patterns, i/total_frames)
|
|
|
|
if new_pose is not None:
|
|
frame = self._create_enhanced_dance_frame(
|
|
new_pose,
|
|
frame_size,
|
|
add_effects=True
|
|
)
|
|
|
|
sequence.append(frame)
|
|
|
|
return sequence
|
|
|
|
def _analyze_style_patterns(self, poses):
|
|
"""Analyze dance style patterns from poses"""
|
|
patterns = []
|
|
|
|
for pose in poses:
|
|
if pose is not None:
|
|
landmarks = self._landmarks_to_array(pose)
|
|
patterns.append(landmarks)
|
|
|
|
return patterns
|
|
|
|
def _generate_style_based_pose(self, patterns, progress):
|
|
"""Generate new pose based on style patterns and progress"""
|
|
if not patterns:
|
|
return None
|
|
|
|
|
|
num_patterns = len(patterns)
|
|
pattern_idx = int(progress * (num_patterns - 1))
|
|
|
|
if pattern_idx < num_patterns - 1:
|
|
t = progress * (num_patterns - 1) - pattern_idx
|
|
pose = self._interpolate_poses(
|
|
patterns[pattern_idx],
|
|
patterns[pattern_idx + 1],
|
|
t
|
|
)
|
|
else:
|
|
pose = patterns[-1]
|
|
|
|
return pose
|
|
|
|
def _interpolate_poses(self, pose1, pose2, t):
|
|
"""Smoothly interpolate between two poses"""
|
|
return pose1 * (1 - t) + pose2 * t
|
|
|
|
def _create_enhanced_dance_frame(self, pose_array, frame_size, add_effects=True):
|
|
"""Create enhanced visualization frame with effects"""
|
|
height, width = frame_size
|
|
frame = np.zeros((height, width, 3), dtype=np.uint8)
|
|
|
|
|
|
points = (pose_array[:, :2] * [width, height]).astype(int)
|
|
|
|
|
|
connections = self._get_pose_connections()
|
|
for connection in connections:
|
|
start_idx, end_idx = connection
|
|
if start_idx < len(points) and end_idx < len(points):
|
|
|
|
if add_effects:
|
|
self._draw_glowing_line(
|
|
frame,
|
|
points[start_idx],
|
|
points[end_idx],
|
|
(0, 255, 0)
|
|
)
|
|
else:
|
|
cv2.line(frame,
|
|
tuple(points[start_idx]),
|
|
tuple(points[end_idx]),
|
|
(0, 255, 0), 2)
|
|
|
|
|
|
for point in points:
|
|
if add_effects:
|
|
self._draw_glowing_point(frame, point, (0, 0, 255))
|
|
else:
|
|
cv2.circle(frame, tuple(point), 4, (0, 0, 255), -1)
|
|
|
|
return frame
|
|
|
|
def _draw_glowing_line(self, frame, start, end, color, thickness=2):
|
|
"""Draw a line with glow effect"""
|
|
|
|
cv2.line(frame, tuple(start), tuple(end), color, thickness)
|
|
|
|
|
|
for i in range(3):
|
|
alpha = 0.3 - i * 0.1
|
|
thickness = thickness + 2
|
|
cv2.line(frame, tuple(start), tuple(end),
|
|
tuple([int(c * alpha) for c in color]),
|
|
thickness)
|
|
|
|
def _draw_glowing_point(self, frame, point, color, radius=4):
|
|
"""Draw a point with glow effect"""
|
|
|
|
cv2.circle(frame, tuple(point), radius, color, -1)
|
|
|
|
|
|
for i in range(3):
|
|
alpha = 0.3 - i * 0.1
|
|
r = radius + i * 2
|
|
cv2.circle(frame, tuple(point), r,
|
|
tuple([int(c * alpha) for c in color]),
|
|
-1)
|
|
|
|
def _landmarks_to_array(self, landmarks):
|
|
"""Convert MediaPipe landmarks to numpy array"""
|
|
points = []
|
|
for landmark in landmarks.landmark:
|
|
points.append([landmark.x, landmark.y, landmark.z])
|
|
return np.array(points)
|
|
|
|
def _mirror_movements(self, landmarks):
|
|
"""Mirror the input movements"""
|
|
mirrored = landmarks.copy()
|
|
mirrored[:, 0] = 1 - mirrored[:, 0]
|
|
return mirrored
|
|
|
|
def _update_style_memory(self, landmarks):
|
|
"""Update memory of dance style"""
|
|
self.style_memory.append(landmarks)
|
|
if len(self.style_memory) > 30:
|
|
self.style_memory.pop(0)
|
|
|
|
def _generate_style_based_moves(self):
|
|
"""Generate new moves based on learned style"""
|
|
if not self.style_memory:
|
|
return np.zeros((33, 3))
|
|
|
|
|
|
base_pose = self.style_memory[-1]
|
|
if len(self.style_memory) > 1:
|
|
prev_pose = self.style_memory[-2]
|
|
t = np.random.random()
|
|
new_pose = t * base_pose + (1-t) * prev_pose
|
|
else:
|
|
new_pose = base_pose
|
|
|
|
return new_pose
|
|
|
|
def _create_dance_frame(self, pose_array):
|
|
"""Create visualization frame from pose array"""
|
|
frame = np.zeros((480, 640, 3), dtype=np.uint8)
|
|
|
|
|
|
points = (pose_array[:, :2] * [640, 480]).astype(int)
|
|
|
|
|
|
connections = self._get_pose_connections()
|
|
for connection in connections:
|
|
start_idx, end_idx = connection
|
|
if start_idx < len(points) and end_idx < len(points):
|
|
cv2.line(frame,
|
|
tuple(points[start_idx]),
|
|
tuple(points[end_idx]),
|
|
(0, 255, 0), 2)
|
|
|
|
|
|
for point in points:
|
|
cv2.circle(frame, tuple(point), 4, (0, 0, 255), -1)
|
|
|
|
return frame
|
|
|
|
def _get_pose_connections(self):
|
|
"""Define connections between pose landmarks"""
|
|
return [
|
|
(0, 1), (1, 2), (2, 3), (3, 7),
|
|
(0, 4), (4, 5), (5, 6), (6, 8),
|
|
(9, 10), (11, 12), (11, 13), (13, 15),
|
|
(12, 14), (14, 16),
|
|
(11, 23), (12, 24),
|
|
(23, 24), (23, 25), (24, 26),
|
|
(25, 27), (26, 28), (27, 29), (28, 30),
|
|
(29, 31), (30, 32)
|
|
]
|
|
|
|
def _smooth_transition(self, prev_pose, current_pose, smoothing_factor=0.3):
|
|
"""Create smooth transition between poses"""
|
|
if prev_pose is None or current_pose is None:
|
|
return current_pose
|
|
|
|
|
|
smoothed_pose = (1 - smoothing_factor) * prev_pose + smoothing_factor * current_pose
|
|
|
|
|
|
|
|
hip_center_idx = 23
|
|
|
|
prev_hip = prev_pose[hip_center_idx]
|
|
current_hip = current_pose[hip_center_idx]
|
|
smoothed_hip = smoothed_pose[hip_center_idx]
|
|
|
|
|
|
for i in range(len(smoothed_pose)):
|
|
if i != hip_center_idx:
|
|
|
|
prev_relative = prev_pose[i] - prev_hip
|
|
current_relative = current_pose[i] - current_hip
|
|
|
|
|
|
smoothed_relative = (1 - smoothing_factor) * prev_relative + smoothing_factor * current_relative
|
|
|
|
|
|
smoothed_pose[i] = smoothed_hip + smoothed_relative
|
|
|
|
return smoothed_pose |