Skip to content

Commit 729ee1d

Browse files
authored
Update index.html
1 parent 331a5c9 commit 729ee1d

File tree

1 file changed

+136
-64
lines changed

1 file changed

+136
-64
lines changed

index.html

Lines changed: 136 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ <h2>Session History</h2>
620620
class LacrosseBallTracker {
621621
constructor() {
622622
this.previousBalls = [];
623-
this.ballHistory = [];
623+
this.ballHistory = {};
624624
this.maxHistoryLength = 10;
625625
this.minBallRadius = 15;
626626
this.maxBallRadius = 50;
@@ -663,24 +663,44 @@ <h2>Session History</h2>
663663
circles,
664664
cv.HOUGH_GRADIENT,
665665
1, // dp: inverse ratio of accumulator resolution
666-
50, // minDist: minimum distance between circle centers
666+
80, // minDist: minimum distance between circle centers (increased)
667667
100, // param1: upper threshold for edge detection
668-
30, // param2: accumulator threshold for center detection
668+
35, // param2: accumulator threshold for center detection (increased for better quality)
669669
this.minBallRadius, // minRadius
670670
this.maxBallRadius // maxRadius
671671
);
672672

673-
const detectedBalls = [];
673+
let bestBall = null;
674+
let bestScore = 0;
674675

675-
// Process detected circles
676+
// Process detected circles and find the best one
676677
for (let i = 0; i < circles.cols; i++) {
677678
const x = circles.data32F[i * 3];
678679
const y = circles.data32F[i * 3 + 1];
679680
const radius = circles.data32F[i * 3 + 2];
680681

681682
// Validate detection
682683
if (this.isValidBall(x, y, radius, videoElement.width, videoElement.height)) {
683-
detectedBalls.push({ x, y, radius, timestamp: Date.now() });
684+
// Calculate quality score based on radius (prefer balls closer to expected size)
685+
const idealRadius = (this.minBallRadius + this.maxBallRadius) / 2;
686+
const radiusScore = 1 - Math.abs(radius - idealRadius) / idealRadius;
687+
688+
// Prefer balls in center of frame
689+
const centerX = videoElement.width / 2;
690+
const centerY = videoElement.height / 2;
691+
const distanceFromCenter = Math.sqrt(
692+
Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2)
693+
);
694+
const maxDistance = Math.sqrt(Math.pow(centerX, 2) + Math.pow(centerY, 2));
695+
const centerScore = 1 - (distanceFromCenter / maxDistance);
696+
697+
// Combined score
698+
const totalScore = radiusScore * 0.7 + centerScore * 0.3;
699+
700+
if (totalScore > bestScore) {
701+
bestScore = totalScore;
702+
bestBall = { x, y, radius, timestamp: Date.now(), score: totalScore };
703+
}
684704
}
685705
}
686706

@@ -690,7 +710,8 @@ <h2>Session History</h2>
690710
blurred.delete();
691711
circles.delete();
692712

693-
return this.filterAndTrackBalls(detectedBalls);
713+
// Return single best ball or empty array
714+
return bestBall ? [bestBall] : [];
694715

695716
} catch (error) {
696717
console.error('Ball detection error:', error);
@@ -714,94 +735,145 @@ <h2>Session History</h2>
714735
}
715736

716737
filterAndTrackBalls(detectedBalls) {
717-
// If no previous balls, return current detections
718-
if (this.previousBalls.length === 0) {
719-
this.previousBalls = detectedBalls;
720-
return detectedBalls;
738+
const currentTime = Date.now();
739+
740+
// Since we only track one ball, simplify the logic
741+
if (detectedBalls.length === 0) {
742+
this.previousBalls = [];
743+
return [];
721744
}
722745

723-
// Match current detections with previous balls
724-
const matchedBalls = [];
746+
const ball = detectedBalls[0]; // Only one ball
747+
let trackedBall;
725748

726-
detectedBalls.forEach(currentBall => {
727-
let bestMatch = null;
728-
let minDistance = Infinity;
729-
730-
this.previousBalls.forEach(prevBall => {
731-
const distance = Math.sqrt(
732-
Math.pow(currentBall.x - prevBall.x, 2) +
733-
Math.pow(currentBall.y - prevBall.y, 2)
734-
);
735-
736-
if (distance < minDistance && distance < this.movementThreshold) {
737-
minDistance = distance;
738-
bestMatch = prevBall;
739-
}
740-
});
749+
// Check if we have a previous ball to track from
750+
if (this.previousBalls.length > 0) {
751+
const prevBall = this.previousBalls[0];
752+
const distance = Math.sqrt(
753+
Math.pow(ball.x - prevBall.x, 2) +
754+
Math.pow(ball.y - prevBall.y, 2)
755+
);
741756

742-
if (bestMatch) {
743-
// Update ball with movement data
744-
currentBall.velocity = {
745-
x: currentBall.x - bestMatch.x,
746-
y: currentBall.y - bestMatch.y
757+
// If the ball moved too far, treat as new detection
758+
if (distance > this.maxTrackingDistance) {
759+
trackedBall = {
760+
...ball,
761+
id: Math.random().toString(36).substr(2, 9),
762+
velocity: { x: 0, y: 0, magnitude: 0 }
763+
};
764+
} else {
765+
// Continue tracking the same ball
766+
trackedBall = {
767+
...ball,
768+
id: prevBall.id,
769+
velocity: {
770+
x: ball.x - prevBall.x,
771+
y: ball.y - prevBall.y,
772+
magnitude: Math.sqrt(
773+
Math.pow(ball.x - prevBall.x, 2) +
774+
Math.pow(ball.y - prevBall.y, 2)
775+
)
776+
}
747777
};
748-
currentBall.speed = Math.sqrt(
749-
Math.pow(currentBall.velocity.x, 2) +
750-
Math.pow(currentBall.velocity.y, 2)
751-
);
752-
matchedBalls.push(currentBall);
753778
}
754-
});
779+
} else {
780+
// First detection
781+
trackedBall = {
782+
...ball,
783+
id: Math.random().toString(36).substr(2, 9),
784+
velocity: { x: 0, y: 0, magnitude: 0 }
785+
};
786+
}
787+
788+
// Update previous balls for next frame
789+
this.previousBalls = [trackedBall];
755790

756-
this.previousBalls = matchedBalls;
757-
this.updateBallHistory(matchedBalls);
791+
// Update ball history for movement analysis
792+
this.updateBallHistory([trackedBall]);
758793

759-
return matchedBalls;
794+
return [trackedBall];
760795
}
761796

762797
updateBallHistory(balls) {
763-
this.ballHistory.push({
764-
timestamp: Date.now(),
765-
balls: balls.map(ball => ({ ...ball }))
766-
});
798+
const currentTime = Date.now();
799+
800+
// Since we only track one ball, simplify history management
801+
if (balls.length === 0) {
802+
// Clear history if no ball detected
803+
this.ballHistory = {};
804+
return;
805+
}
806+
807+
const ball = balls[0]; // Only one ball
767808

768-
// Keep only recent history
769-
if (this.ballHistory.length > this.maxHistoryLength) {
770-
this.ballHistory.shift();
809+
if (!this.ballHistory[ball.id]) {
810+
this.ballHistory[ball.id] = [];
771811
}
812+
813+
this.ballHistory[ball.id].push({
814+
x: ball.x,
815+
y: ball.y,
816+
timestamp: currentTime,
817+
velocity: ball.velocity || { x: 0, y: 0, magnitude: 0 }
818+
});
819+
820+
// Keep only recent history (last 2 seconds)
821+
const cutoffTime = currentTime - 2000;
822+
this.ballHistory[ball.id] = this.ballHistory[ball.id].filter(
823+
point => point.timestamp > cutoffTime
824+
);
825+
826+
// Clean up old ball histories (keep only current ball)
827+
Object.keys(this.ballHistory).forEach(ballId => {
828+
if (ballId !== ball.id) {
829+
delete this.ballHistory[ballId];
830+
}
831+
});
772832
}
773833

774834
analyzeMovementPattern() {
775-
if (this.ballHistory.length < 3) {
835+
// Get the current ball's history
836+
const ballIds = Object.keys(this.ballHistory);
837+
if (ballIds.length === 0) {
776838
return { isThrow: false, direction: null };
777839
}
778840

779-
const recent = this.ballHistory.slice(-3);
780-
const balls = recent.map(frame => frame.balls[0]).filter(Boolean);
841+
const ballId = ballIds[0]; // Only one ball
842+
const history = this.ballHistory[ballId];
781843

782-
if (balls.length < 3) {
844+
if (history.length < 3) {
783845
return { isThrow: false, direction: null };
784846
}
785847

786-
// Analyze vertical movement pattern
787-
const verticalMovements = [];
788-
for (let i = 1; i < balls.length; i++) {
789-
verticalMovements.push(balls[i].y - balls[i-1].y);
848+
const recent = history.slice(-5); // Last 5 points
849+
let totalMovement = 0;
850+
let verticalMovement = 0;
851+
852+
for (let i = 1; i < recent.length; i++) {
853+
const prev = recent[i-1];
854+
const curr = recent[i];
855+
856+
const dx = curr.x - prev.x;
857+
const dy = curr.y - prev.y;
858+
const movement = Math.sqrt(dx*dx + dy*dy);
859+
860+
totalMovement += movement;
861+
verticalMovement += Math.abs(dy);
790862
}
791863

792-
// Check for throw pattern: upward then downward movement
793-
const hasUpwardMovement = verticalMovements.some(movement => movement < -10);
794-
const hasDownwardMovement = verticalMovements.some(movement => movement > 10);
864+
const avgMovement = totalMovement / (recent.length - 1);
865+
const isSignificantMovement = avgMovement > this.movementThreshold;
866+
const isVerticalMovement = verticalMovement > totalMovement * 0.6;
795867

796868
return {
797-
isThrow: hasUpwardMovement && hasDownwardMovement,
798-
direction: verticalMovements[verticalMovements.length - 1] > 0 ? 'down' : 'up',
799-
speed: balls[balls.length - 1].speed || 0
869+
isThrow: isSignificantMovement && isVerticalMovement,
870+
direction: verticalMovement > 0 ? 'down' : 'up',
871+
movement: avgMovement
800872
};
801873
}
802874

803875
reset() {
804-
this.ballHistory = [];
876+
this.ballHistory = {};
805877
this.previousBalls = [];
806878
}
807879
}

0 commit comments

Comments
 (0)