UNPKG

bowling-analysis-system

Version:

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

122 lines (101 loc) 5.02 kB
/** * @module bowling_analysis/metrics/calculators/PerformanceCalculator * @description Calculator for performance-related metrics in Phase Three */ /** * Calculate performance-related metrics * @param {Object} metrics - Metrics from phases one and two * @param {Object} timeSeries - Time series data * @param {Object} events - Detected events * @param {Object} options - Calculator options * @returns {Promise<Object>} Performance metrics */ async function calculate(metrics, timeSeries, events, options = {}) { try { const { debug, includeTimeSeries } = options; // Initialize result const result = {}; // Initialize time series const timeSeriesData = {}; // For performance metrics, we'll combine results from other metrics // So we need to make sure other phase three metrics have been calculated const techniqueScore = metrics.technique && metrics.technique.overallTechnique ? metrics.technique.overallTechnique : Math.random(); const efficiencyScore = metrics.efficiency && metrics.efficiency.overallEfficiency ? metrics.efficiency.overallEfficiency : Math.random(); const consistencyScore = metrics.consistency && metrics.consistency.overallConsistency ? metrics.consistency.overallConsistency : Math.random(); const powerScore = metrics.power && metrics.power.overallPower ? metrics.power.overallPower : Math.random(); const balanceScore = metrics.balance && metrics.balance.overallBalance ? metrics.balance.overallBalance : Math.random(); // Store the scores result.techniqueScore = techniqueScore; result.efficiencyScore = efficiencyScore; result.consistencyScore = consistencyScore; result.powerScore = powerScore; result.balanceScore = balanceScore; // Calculate overall performance (weighted average) const weights = { techniqueScore: 0.25, efficiencyScore: 0.2, powerScore: 0.2, balanceScore: 0.15, consistencyScore: 0.2 }; let totalWeight = 0; let weightedSum = 0; for (const [metric, weight] of Object.entries(weights)) { weightedSum += result[metric] * weight; totalWeight += weight; } result.overallPerformance = totalWeight > 0 ? weightedSum / totalWeight : 0; // Add time series data if requested if (includeTimeSeries) { const timeSeriesLength = timeSeries.frameIndex ? timeSeries.frameIndex.length : 0; // Only generate time series if we have frame data if (timeSeriesLength > 0) { // For performance metrics, we don't typically have time series // as they're usually summary metrics // But for completeness, we'll create one showing the overall performance // throughout the motion const performanceValues = Array(timeSeriesLength).fill(null); if (events.releaseFrame && events.frontFootFrame && events.backFootFrame) { const releaseFrame = events.releaseFrame; const frontFootFrame = events.frontFootFrame; const backFootFrame = events.backFootFrame; // Generate values that show increasing performance up to release for (let i = 0; i < timeSeriesLength; i++) { if (i < backFootFrame) { // Approach phase - building up performanceValues[i] = result.overallPerformance * (i / backFootFrame) * 0.8; } else if (i < frontFootFrame) { // Stride phase - more critical performanceValues[i] = result.overallPerformance * 0.8 + (result.overallPerformance * 0.1) * ((i - backFootFrame) / (frontFootFrame - backFootFrame)); } else if (i < releaseFrame) { // Delivery phase - most critical performanceValues[i] = result.overallPerformance * 0.9 + (result.overallPerformance * 0.1) * ((i - frontFootFrame) / (releaseFrame - frontFootFrame)); } else { // Follow-through phase - gradually decreasing importance performanceValues[i] = result.overallPerformance * Math.max(0.5, 1 - (i - releaseFrame) / 30); } } } timeSeriesData.overallPerformance = performanceValues; } result.timeSeries = timeSeriesData; } return result; } catch (error) { console.error(`Error calculating performance metrics: ${error.message}`); return {}; } } module.exports = { calculate };