UNPKG

bowling-analysis-system

Version:

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

155 lines (136 loc) 5.49 kB
/** * @module bowling_analysis/pipeline * @description Main pipeline for bowling metrics processing */ const path = require('path'); const fs = require('fs'); const { performance } = require('perf_hooks'); // Using the modern pipeline implementation const { processBowlingMetrics: processMetrics } = require('./metrics/index'); const { FileLoader } = require('./stages/file_loader'); const { KeypointProcessor } = require('./stages/keypoint_processor'); const { FileSaver } = require('./stages/file_saver'); const { defaultLogger } = require('../utils/logger'); /** * Process bowling metrics from keypoint data through the complete pipeline * @param {Array} keypointData - Array of keypoint frames * @param {Object} options - Pipeline options * @param {string} [options.biasPath] - Path to existing bias file (optional) * @param {boolean} [options.debug] - Enable debug logging * @param {boolean} [options.preserveAllFrames=true] - Preserve all original frames * @returns {Promise<Object>} - The complete metrics result */ async function processBowlingMetrics(keypointData, options = {}) { const { biasPath, debug = false, preserveAllFrames = true, outputPath = path.join(process.cwd(), 'complete_metrics.json') } = options; const logger = defaultLogger.child('Pipeline'); if (debug) logger.setLevel('debug'); const startTime = performance.now(); const timings = {}; try { logger.info('Starting multi-phase bowling metrics pipeline'); // Initialize pipeline stages const keypointProcessor = new KeypointProcessor(); const fileSaver = new FileSaver(); // Stage 0: Keypoint processing const t0 = performance.now(); logger.info('Stage 0: Keypoint processing'); const processedKeypoints = await keypointProcessor.execute({ keypointData: { frames: keypointData }, options: { preserveAllFrames, debug } }); timings.keypointProcessing = performance.now() - t0; // Stage 1: Phase One metrics const { PhaseOneProcessor } = require('./metrics/PhaseOneProcessor'); const phaseOne = new PhaseOneProcessor({ debug }); const t1 = performance.now(); logger.info('Stage 1: Phase One metrics'); const phaseOneResult = await phaseOne.process({ keypointData: processedKeypoints.frames || processedKeypoints }); timings.phaseOne = performance.now() - t1; // Stage 2: Phase Two metrics const { PhaseTwoProcessor } = require('./metrics/PhaseTwoProcessor'); const phaseTwo = new PhaseTwoProcessor({ debug, biasPath }); const t2 = performance.now(); logger.info('Stage 2: Phase Two metrics'); const phaseTwoResult = await phaseTwo.process(phaseOneResult); timings.phaseTwo = performance.now() - t2; // Stage 3: Phase Three metrics (composite) const t3 = performance.now(); logger.info('Stage 3: Phase Three metrics'); const phaseThreeMetrics = {}; const phaseThreeTimeSeries = {}; // Example: combine phase one + phase two outputs // (In practice, add more composite calculations here) const calculations = require('./metrics/calculations'); // Placeholder: no-op, but you can add composite metrics here // e.g., phaseThreeMetrics.someMetric = calculations.someFunction(phaseOneResult, phaseTwoResult); timings.phaseThree = performance.now() - t3; // Merge all outputs const result = { metrics: { ...phaseOneResult.metrics, ...phaseTwoResult.eventMetrics, ...phaseThreeMetrics }, timeSeries: { ...phaseOneResult.timeSeries, ...phaseTwoResult.timeSeries, ...phaseThreeTimeSeries }, events: phaseTwoResult.events, bias: phaseTwoResult.bias, metadata: { totalFrames: keypointData.length, timings } }; // Log time series data for debugging logger.debug('Time series data in final result:'); if (result.timeSeries) { Object.keys(result.timeSeries).forEach(category => { if (result.timeSeries[category]) { logger.debug(`Category: ${category}`); Object.keys(result.timeSeries[category]).forEach(metric => { const values = result.timeSeries[category][metric]; if (Array.isArray(values)) { const nonNullCount = values.filter(v => v !== null).length; logger.debug(` ${metric}: ${nonNullCount} non-null values`); } }); } }); } // Ensure balance metrics are included if (!result.metrics.balance && phaseOneResult.metrics && phaseOneResult.metrics.balance) { result.metrics.balance = phaseOneResult.metrics.balance; logger.info(`Added ${Object.keys(phaseOneResult.metrics.balance).length} balance metrics to final output`); } else if (phaseOneResult.metrics && phaseOneResult.metrics.balance) { logger.info(`Including ${Object.keys(phaseOneResult.metrics.balance).length} balance metrics in final output`); } else { logger.warn('No balance metrics found in phase one result'); } // Save results if (outputPath) { logger.info('Saving results to file...'); await fileSaver.execute({ context: result, outputPath, options: { debug } }); } logger.info('Pipeline completed successfully'); return result; } catch (error) { logger.error('Pipeline failed:', error); throw error; } } module.exports = { processBowlingMetrics };