bowling-analysis-system
Version:
A comprehensive system for analyzing bowling techniques using video processing and metrics calculation
125 lines (104 loc) • 4.23 kB
JavaScript
/**
* @fileoverview Position calculations for bowling analysis
* @module PositionCalculations
*/
/**
* Calculates the center of mass from pose landmarks
* @param {Array<Object>} landmarks - Array of pose landmarks
* @returns {Object|null} Center of mass coordinates or null if calculation fails
*/
function calculateCenterOfMass(landmarks) {
if (!Array.isArray(landmarks) || landmarks.length === 0) return null;
// Get key landmarks
const nose = landmarks.find(l => l.index === 0);
const leftShoulder = landmarks.find(l => l.index === 11);
const rightShoulder = landmarks.find(l => l.index === 12);
const leftHip = landmarks.find(l => l.index === 23);
const rightHip = landmarks.find(l => l.index === 24);
if (!nose || !leftShoulder || !rightShoulder || !leftHip || !rightHip) return null;
// Calculate midpoints
const shoulderMidpoint = {
x: (leftShoulder.x + rightShoulder.x) / 2,
y: (leftShoulder.y + rightShoulder.y) / 2,
z: (leftShoulder.z + rightShoulder.z) / 2
};
const hipMidpoint = {
x: (leftHip.x + rightHip.x) / 2,
y: (leftHip.y + rightHip.y) / 2,
z: (leftHip.z + rightHip.z) / 2
};
// Calculate weighted average for center of mass
// Head: 8%, Upper body: 52%, Lower body: 40%
return {
x: nose.x * 0.08 + shoulderMidpoint.x * 0.52 + hipMidpoint.x * 0.40,
y: nose.y * 0.08 + shoulderMidpoint.y * 0.52 + hipMidpoint.y * 0.40,
z: nose.z * 0.08 + shoulderMidpoint.z * 0.52 + hipMidpoint.z * 0.40
};
}
/**
* Calculates the shoulder width from pose landmarks
* @param {Array<Object>} landmarks - Array of pose landmarks
* @returns {number|null} Shoulder width in units or null if calculation fails
*/
function calculateShoulderWidth(landmarks) {
if (!Array.isArray(landmarks) || landmarks.length === 0) return null;
const leftShoulder = landmarks.find(l => l.index === 11);
const rightShoulder = landmarks.find(l => l.index === 12);
if (!leftShoulder || !rightShoulder) return null;
return Math.sqrt(
Math.pow(rightShoulder.x - leftShoulder.x, 2) +
Math.pow(rightShoulder.y - leftShoulder.y, 2) +
Math.pow(rightShoulder.z - leftShoulder.z, 2)
);
}
/**
* Calculates the hip width from pose landmarks
* @param {Array<Object>} landmarks - Array of pose landmarks
* @returns {number|null} Hip width in units or null if calculation fails
*/
function calculateHipWidth(landmarks) {
if (!Array.isArray(landmarks) || landmarks.length === 0) return null;
const leftHip = landmarks.find(l => l.index === 23);
const rightHip = landmarks.find(l => l.index === 24);
if (!leftHip || !rightHip) return null;
return Math.sqrt(
Math.pow(rightHip.x - leftHip.x, 2) +
Math.pow(rightHip.y - leftHip.y, 2) +
Math.pow(rightHip.z - leftHip.z, 2)
);
}
/**
* Calculates the torso length from pose landmarks
* @param {Array<Object>} landmarks - Array of pose landmarks
* @returns {number|null} Torso length in units or null if calculation fails
*/
function calculateTorsoLength(landmarks) {
if (!Array.isArray(landmarks) || landmarks.length === 0) return null;
// Get midpoints of shoulders and hips
const leftShoulder = landmarks.find(l => l.index === 11);
const rightShoulder = landmarks.find(l => l.index === 12);
const leftHip = landmarks.find(l => l.index === 23);
const rightHip = landmarks.find(l => l.index === 24);
if (!leftShoulder || !rightShoulder || !leftHip || !rightHip) return null;
const shoulderMidpoint = {
x: (leftShoulder.x + rightShoulder.x) / 2,
y: (leftShoulder.y + rightShoulder.y) / 2,
z: (leftShoulder.z + rightShoulder.z) / 2
};
const hipMidpoint = {
x: (leftHip.x + rightHip.x) / 2,
y: (leftHip.y + rightHip.y) / 2,
z: (leftHip.z + rightHip.z) / 2
};
return Math.sqrt(
Math.pow(shoulderMidpoint.x - hipMidpoint.x, 2) +
Math.pow(shoulderMidpoint.y - hipMidpoint.y, 2) +
Math.pow(shoulderMidpoint.z - hipMidpoint.z, 2)
);
}
module.exports = {
calculateCenterOfMass,
calculateShoulderWidth,
calculateHipWidth,
calculateTorsoLength
};