Vance Honeycutt 3 different swings via Prospects Live YouTube
Here is a look at the velocities of the body parts that we tracked in each of the three swings. The tracking is not precise down to the exact body part in every frame, but it does a good job regardless with the peaks and valleys. The peaks in all three graphs are the actual swings. The highest elbow, hip, and wrist velocity all came on the final swing...which was pimped so I'm guessing that was a bomb! Cool to see the impact of each body part on the swing relative to the wait and load.
The Metric chart below shows the relative angles of each body point that was tracked on the swings. The points were not exactly precise across all three swings and the camera is slightly different in one of the games, but the results are still pretty close! Looking for consistency in approach and attack!
Gerrit Cole Slow Motion Delivery via Pitching Paradigm on YouTube
I was recently doing a write up of Tanner Schobel and some of his scouting video from college and Prospects Live. I decided to use two of his swings in my exploratory research.
Tanner Schobel video via ProspectsLive on YouTube
BELOW:
The circles here are what I constituted as the "approach", or load into the ball.
The shoulder angle is the negative value and the elbow angle is the positive.
The tails are the follow through of each respective swing, so no need to look deep into that
The triangles are the respective impact points based on the video and data (low res cam causes some inaccuracies)
Even though the video frames are not aligned perfectly, we can see the relative spike for the ball that was put in play compared to the foul ball. Check it out below:
Clayton Thompson still shot image from Open CV via Driveline Baseball
IN PYTHON:
# Step 1: Mount Google Drive
from google.colab import drive
drive.mount('/content/drive')
# Step 2: Define the path to the video file (adjust path based on where it's located in your Google Drive)
video_path = "/content/drive/MyDrive/GC.mp4"
# Now, you can use `video_path` to load the video in your previous code
import cv2
import pandas as pd
import mediapipe as mp
from google.colab.patches import cv2_imshow
# Initialize empty list to store joint data
joint_data = []
# Load the video
cap = cv2.VideoCapture(video_path)
# Check if video opened successfully
if not cap.isOpened():
print(f"Error: Could not open video at {video_path}")
exit()
# Initialize MediaPipe Pose
mp_pose = mp.solutions.pose
pose = mp_pose.Pose(static_image_mode=False, min_detection_confidence=0.5, min_tracking_confidence=0.5)
frame_count = 0
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# Process only every 4th frame
if frame_count % 4 == 0:
# Convert the BGR frame to RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = pose.process(rgb_frame)
# If any landmarks are found
if results.pose_landmarks:
# Draw landmarks on the frame
mp.solutions.drawing_utils.draw_landmarks(frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
# Extract joint positions (shoulder, elbow, wrist)
shoulder = (int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_SHOULDER].x * frame.shape[1]),
int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_SHOULDER].y * frame.shape[0]))
elbow = (int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_ELBOW].x * frame.shape[1]),
int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_ELBOW].y * frame.shape[0]))
wrist = (int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_WRIST].x * frame.shape[1]),
int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_WRIST].y * frame.shape[0]))
# Draw the points on the frame
cv2.circle(frame, shoulder, 5, (0, 255, 0), -1) # Green for shoulder
cv2.circle(frame, elbow, 5, (255, 0, 0), -1) # Blue for elbow
cv2.circle(frame, wrist, 5, (0, 0, 255), -1) # Red for wrist
# Save the joint data for this frame
joint_data.append({
'frame': frame_count,
'shoulder_x': shoulder[0],
'shoulder_y': shoulder[1],
'elbow_x': elbow[0],
'elbow_y': elbow[1],
'wrist_x': wrist[0],
'wrist_y': wrist[1]
})
# Display the frame (optional, comment this out if not needed)
cv2_imshow(frame)
frame_count += 1
# Release the video capture object
cap.release()
# Convert joint data to a DataFrame and save it
joint_data_df = pd.DataFrame(joint_data)
joint_data_df.to_csv('joint_data.csv', index=False)
print("Joint data saved to 'joint_data.csv'")
IN R:
gerritcole <- read.csv("C://Users/Drew Duffy/Downloads/joint_data_1.csv")
colnames(gerritcole)
library(ggplot2)
library(gganimate)
# Create the plot
p <- ggplot(gerritcole, aes(frame = frame)) +
# Left Shoulder trajectory and points
geom_point(aes(x = shoulder_l_x, y = shoulder_l_y, color = "Left Shoulder"), size = 3) +
geom_path(aes(x = shoulder_l_x, y = shoulder_l_y, group = 1, color = "Left Shoulder"), size = 1) +
# Right Shoulder trajectory and points
geom_point(aes(x = shoulder_r_x, y = shoulder_r_y, color = "Right Shoulder"), size = 3) +
geom_path(aes(x = shoulder_r_x, y = shoulder_r_y, group = 1, color = "Right Shoulder"), size = 1) +
# Left Elbow trajectory and points
geom_point(aes(x = elbow_l_x, y = elbow_l_y, color = "Left Elbow"), size = 3) +
geom_path(aes(x = elbow_l_x, y = elbow_l_y, group = 1, color = "Left Elbow"), size = 1) +
# Right Elbow trajectory and points
geom_point(aes(x = elbow_r_x, y = elbow_r_y, color = "Right Elbow"), size = 3) +
geom_path(aes(x = elbow_r_x, y = elbow_r_y, group = 1, color = "Right Elbow"), size = 1) +
# Left Wrist trajectory and points
geom_point(aes(x = wrist_l_x, y = wrist_l_y, color = "Left Wrist"), size = 3) +
geom_path(aes(x = wrist_l_x, y = wrist_l_y, group = 1, color = "Left Wrist"), size = 1) +
# Right Wrist trajectory and points
geom_point(aes(x = wrist_r_x, y = wrist_r_y, color = "Right Wrist"), size = 3) +
geom_path(aes(x = wrist_r_x, y = wrist_r_y, group = 1, color = "Right Wrist"), size = 1) +
# Left Knee trajectory and points
geom_point(aes(x = knee_l_x, y = knee_l_y, color = "Left Knee"), size = 3) +
geom_path(aes(x = knee_l_x, y = knee_l_y, group = 1, color = "Left Knee"), size = 1) +
# Right Knee trajectory and points
geom_point(aes(x = knee_r_x, y = knee_r_y, color = "Right Knee"), size = 3) +
geom_path(aes(x = knee_r_x, y = knee_r_y, group = 1, color = "Right Knee"), size = 1) +
# Animation settings
transition_reveal(frame) + # Smoothly reveal each frame
theme_minimal() +
# Title and axis labels
labs(title = "2D Trajectory of Shoulders, Elbows, Wrists, and Knees: Frame {frame}",
x = "X Coordinate", y = "Y Coordinate", color = "Joint") +
# Slowing down the animation by adjusting frame duration
ease_aes('linear') +
theme(plot.title = element_text(size = 15))
# Save or animate
animate(p, duration = 10, fps = 100, width = 800, height = 600)
# Save or display the animation
anim_save("joint_trajectory.gif")
IN PYTHON:
# Step 1: Mount Google Drive
from google.colab import drive
drive.mount('/content/drive')
# Step 2: Define the path to the video file
video_path = "/content/drive/MyDrive/vh.mp4"
import cv2
import pandas as pd
import mediapipe as mp
from google.colab.patches import cv2_imshow
import time
# Initialize empty list to store joint data
joint_data = []
# Load the video
cap = cv2.VideoCapture(video_path)
# Check if video opened successfully
if not cap.isOpened():
print(f"Error: Could not open video at {video_path}")
exit()
# Initialize MediaPipe Pose
mp_pose = mp.solutions.pose
pose = mp_pose.Pose(static_image_mode=False, min_detection_confidence=0.5, min_tracking_confidence=0.5)
frame_count = 0
fps = cap.get(cv2.CAP_PROP_FPS)
delay = int(1000 / fps) # Delay in milliseconds for cv2.imshow()
# Process the video frame by frame
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# Process every 4th frame (this will also slow it down)
if frame_count % 7 == 0:
# Convert the BGR frame to RGB
rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
results = pose.process(rgb_frame)
# If any landmarks are found
if results.pose_landmarks:
# Draw landmarks on the frame
mp.solutions.drawing_utils.draw_landmarks(frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS)
# Extract joint positions (shoulders, elbows, wrists, hips, knees)
joints = {
'shoulder_l_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_SHOULDER].x * frame.shape[1]),
'shoulder_l_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_SHOULDER].y * frame.shape[0]),
'shoulder_r_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER].x * frame.shape[1]),
'shoulder_r_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER].y * frame.shape[0]),
'elbow_l_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_ELBOW].x * frame.shape[1]),
'elbow_l_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_ELBOW].y * frame.shape[0]),
'elbow_r_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_ELBOW].x * frame.shape[1]),
'elbow_r_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_ELBOW].y * frame.shape[0]),
'wrist_l_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_WRIST].x * frame.shape[1]),
'wrist_l_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_WRIST].y * frame.shape[0]),
'wrist_r_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_WRIST].x * frame.shape[1]),
'wrist_r_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_WRIST].y * frame.shape[0]),
'hip_l_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_HIP].x * frame.shape[1]),
'hip_l_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_HIP].y * frame.shape[0]),
'hip_r_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_HIP].x * frame.shape[1]),
'hip_r_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_HIP].y * frame.shape[0]),
'knee_l_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_KNEE].x * frame.shape[1]),
'knee_l_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.LEFT_KNEE].y * frame.shape[0]),
'knee_r_x': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_KNEE].x * frame.shape[1]),
'knee_r_y': int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_KNEE].y * frame.shape[0]),
}
# Save the joint data for this frame
joint_data.append({'frame': frame_count, **joints})
# Display the frame (optional, comment this out if not needed)
cv2_imshow(frame)
frame_count += 1
# Slowing down the processing
time.sleep(0.1) # Adjust this to slow down the playback more if needed
# Release the video capture object
cap.release()
# Convert joint data to a DataFrame and save it
joint_data_df = pd.DataFrame(joint_data)
joint_data_df.to_csv('/content/drive/MyDrive/joint_data.csv', index=False)
print("Joint data saved to 'joint_data.csv'")
IN R:
vance <- read.csv("C://Users/Drew Duffy/Downloads/vh2.csv")
library(dplyr)
library(tidyr)
library(ggplot2)
swing_indices <- list(
swing1 = c(1, 6, 7, 8, 9, 10),
swing2 = c(14, 16, 17, 18, 29, 20),
swing3 = c(25, 26, 27, 28, 29)
)
calculate_swing_metrics <- function(data, indices) {
swing_data <- data %>% slice(indices)
metrics <- swing_data %>%
summarise(
wrist_speed = sqrt(mean((wrist_r_x - wrist_l_x)^2 + (wrist_r_y - wrist_l_y)^2)),
elbow_angle = sqrt(mean((elbow_r_x - elbow_l_x)^2 + (elbow_r_y - elbow_l_y)^2)),
hip_angle = sqrt(mean((hip_r_x - hip_l_x)^2 + (hip_r_y - hip_l_y)^2)),
knee_angle = sqrt(mean((knee_r_x - knee_l_x)^2 + (knee_r_y - knee_l_y)^2))
)
return(metrics)
}
swing_metrics <- lapply(swing_indices, function(indices) calculate_swing_metrics(vance, indices))
swing_metrics_df <- do.call(rbind, swing_metrics) %>%
mutate(swing = row_number())
print(swing_metrics_df)
swing_metrics_long <- swing_metrics_df %>%
pivot_longer(cols = -swing, names_to = "metric", values_to = "value")
ggplot(swing_metrics_long, aes(x = factor(swing), y = value, fill = metric)) +
geom_bar(stat = "identity", position = position_dodge()) +
labs(title = "Swing Metrics for Vance's 3 Swings", x = "Swing", y = "Value") +
theme_minimal() +
theme(legend.position = "bottom") +
scale_fill_brewer(palette = "Set1")
swing_indices <- list(
swing1 = c(1, 6, 7, 8, 9, 10, 11),
swing2 = c(14, 16, 17, 18, 19, 20), # Fixed index from 29 to 19
swing3 = c(25, 26, 27, 28, 29, 30)
)
calculate_velocity <- function(data, part_x, part_y) {
velocities <- data %>%
mutate(
velocity_x = c(0, diff(get(part_x))), # Calculate difference in x-coordinates
velocity_y = c(0, diff(get(part_y))) # Calculate difference in y-coordinates
) %>%
rowwise() %>%
mutate(velocity = sqrt(velocity_x^2 + velocity_y^2)) # Calculate resultant velocity
return(velocities$velocity)
}
swing_velocities <- list()
for (swing in names(swing_indices)) {
indices <- swing_indices[[swing]]
swing_data <- vance %>% slice(indices) # Get data for the current swing
swing_velocities[[swing]] <- swing_data %>%
mutate(
wrist_velocity = calculate_velocity(swing_data, "wrist_r_x", "wrist_r_y"),
elbow_velocity = calculate_velocity(swing_data, "elbow_r_x", "elbow_r_y"),
hip_velocity = calculate_velocity(swing_data, "hip_r_x", "hip_r_y"),
knee_velocity = calculate_velocity(swing_data, "knee_r_x", "knee_r_y")
)
}
swing_velocity_combined <- do.call(rbind, lapply(swing_velocities, function(x) x %>% select(frame, wrist_velocity, elbow_velocity, hip_velocity, knee_velocity)))
velocity_data_long <- swing_velocity_combined %>%
pivot_longer(cols = -frame, names_to = "body_part", values_to = "velocity")
ggplot(velocity_data_long, aes(x = frame, y = velocity, color = body_part)) +
geom_line(size = 1) +
labs(title = "Velocities of Body Parts for Vance's Swings", x = "Frame", y = "Velocity (units/frame)") +
theme_minimal() +
theme(legend.position = "bottom") +
facet_wrap(~body_part) +
scale_color_brewer(palette = "Set1")