UNPKG

bowling-analysis-system

Version:

A comprehensive system for analyzing bowling techniques using video processing and metrics calculation

125 lines (104 loc) 4.23 kB
/** * @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 };