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