bowling-analysis-system
Version:
A comprehensive system for analyzing bowling techniques using video processing and metrics calculation
122 lines (101 loc) • 5.02 kB
JavaScript
/**
* @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
};