UNPKG

bowling-analysis-system

Version:

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

126 lines (103 loc) 3.83 kB
/** * @fileoverview Bias correlation calculations for event detection */ /** * Calculate bias correlation between metric values and event frame * @param {Array} metricValues - Array of metric values * @param {number} eventFrame - Frame index of event * @param {string} metricName - Name of metric * @param {string} eventType - Type of event * @returns {Promise<Object>} Correlation data */ async function getBiasCorrelation(metricValues, eventFrame, metricName, eventType) { if (!Array.isArray(metricValues) || eventFrame === undefined) { return null; } // Get valid values around event frame const windowSize = 5; const startFrame = Math.max(0, eventFrame - windowSize); const endFrame = Math.min(metricValues.length - 1, eventFrame + windowSize); const values = metricValues.slice(startFrame, endFrame + 1); const validValues = values.filter(v => v !== null); if (validValues.length < 3) { return null; } // Calculate correlation using weighted window const correlation = await calculateWeightedCorrelation( validValues, eventFrame - startFrame, windowSize ); // Determine correlation direction const direction = determineCorrelationDirection( validValues, eventFrame - startFrame ); return { correlation: Math.abs(correlation), direction, windowSize, validValues: validValues.length, totalValues: values.length }; } /** * Calculate weighted correlation within window * @param {Array} values - Array of values * @param {number} centerIndex - Index of center point * @param {number} windowSize - Size of window * @returns {Promise<number>} Correlation value */ async function calculateWeightedCorrelation(values, centerIndex, windowSize) { if (values.length < 3) return 0; // Create weights array centered on event frame const weights = values.map((_, i) => { const distance = Math.abs(i - centerIndex); return Math.exp(-distance * distance / (2 * windowSize * windowSize)); }); // Calculate weighted means const weightedSum = weights.reduce((sum, w) => sum + w, 0); const weightedMean = values.reduce((sum, v, i) => sum + v * weights[i], 0) / weightedSum; // Calculate weighted covariance and variances let weightedCovariance = 0; let weightedVarianceX = 0; let weightedVarianceY = 0; for (let i = 0; i < values.length; i++) { const xDiff = values[i] - weightedMean; const yDiff = i - centerIndex; const weight = weights[i]; weightedCovariance += weight * xDiff * yDiff; weightedVarianceX += weight * xDiff * xDiff; weightedVarianceY += weight * yDiff * yDiff; } weightedCovariance /= weightedSum; weightedVarianceX /= weightedSum; weightedVarianceY /= weightedSum; // Calculate correlation coefficient if (weightedVarianceX === 0 || weightedVarianceY === 0) return 0; return weightedCovariance / Math.sqrt(weightedVarianceX * weightedVarianceY); } /** * Determine direction of correlation * @param {Array} values - Array of values * @param {number} centerIndex - Index of center point * @returns {string} Correlation direction */ function determineCorrelationDirection(values, centerIndex) { if (values.length < 3) return 'unknown'; // Check values before and after center point const beforeValues = values.slice(0, centerIndex); const afterValues = values.slice(centerIndex + 1); if (beforeValues.length === 0 || afterValues.length === 0) { return 'unknown'; } const beforeMean = beforeValues.reduce((sum, v) => sum + v, 0) / beforeValues.length; const afterMean = afterValues.reduce((sum, v) => sum + v, 0) / afterValues.length; if (Math.abs(afterMean - beforeMean) < 0.01) { return 'neutral'; } return afterMean > beforeMean ? 'positive' : 'negative'; } module.exports = { getBiasCorrelation };