Merge branch 'master' into Gabriel
This commit is contained in:
@@ -71,7 +71,40 @@ class CameraViewModel(application: Application) : AndroidViewModel(application)
|
||||
// live preview with pose overlay on but not recording doesn't feed it.
|
||||
private val stepCountingSession = StepCountingSession()
|
||||
/** @brief Time-ordered pose samples buffered for the current/most recent recording session. */
|
||||
val poseFrames: List<PoseFrame> get() = stepCountingSession.poseFrames
|
||||
val poseFrames: List<PoseFrame> get() = poseFrameBuffer
|
||||
|
||||
// Extra SMA smoothing for ankle/hip landmarks specifically, on top of
|
||||
// PoseLandmarkSmoother's per-frame EMA -- see AnkleHipMovingAverageFilter.
|
||||
// Reset (not replaced) alongside the buffer so a new session's window
|
||||
// doesn't lerp in from the previous one's last few frames.
|
||||
private val ankleHipSmoother = AnkleHipMovingAverageFilter()
|
||||
|
||||
// Live, incremental step counting for the current recording -- see
|
||||
// LiveStepDetector. Resets itself mid-recording when the bowler holds
|
||||
// a stationary "ready" stance again, so one recording can capture
|
||||
// several practice approaches back to back.
|
||||
private val liveStepDetector = LiveStepDetector()
|
||||
|
||||
// Live delivery-phase classification (starting stance, approach, etc)
|
||||
private val posePhaseDetector = PosePhaseDetector()
|
||||
private val _posePhase = MutableStateFlow<BowlingPhase?>(null)
|
||||
//The bowler's current delivery phase, or null if no phase currently validates.
|
||||
val posePhase: StateFlow<BowlingPhase?> = _posePhase.asStateFlow()
|
||||
|
||||
// Raw torso/knee/elbow angle readings behind posePhase above, for
|
||||
// showing the bowler the actual numbers rather than just a pass/fail
|
||||
// signal -- see PosePhaseDetector.Metrics.
|
||||
private val _poseMetrics = MutableStateFlow<PosePhaseDetector.Metrics?>(null)
|
||||
//This frame's torso/knee/elbow angle readings, or null if pose detection is off.
|
||||
val poseMetrics: StateFlow<PosePhaseDetector.Metrics?> = _poseMetrics.asStateFlow()
|
||||
|
||||
// Steps detected so far in the current attempt (since the last reset,
|
||||
// whether that reset was a new recording starting or the bowler
|
||||
// returning to a stationary stance mid-recording -- see
|
||||
// onPoseFrameUpdated and LiveStepDetector). The UI reads events.size
|
||||
// as the "Step N" counter. Stays populated after recording stops so
|
||||
// the last attempt's count remains visible.
|
||||
private val _stepEvents = MutableStateFlow<List<StepEvent>>(emptyList())
|
||||
/** @brief Steps detected so far in the current attempt, since the last reset. */
|
||||
val stepEvents: StateFlow<List<StepEvent>> get() = stepCountingSession.stepEvents
|
||||
/** @brief Progress toward the hold-to-reset gesture, from 0 to 1. */
|
||||
@@ -104,7 +137,12 @@ class CameraViewModel(application: Application) : AndroidViewModel(application)
|
||||
*/
|
||||
fun onPoseToggled(enabled: Boolean) {
|
||||
_poseEnabled.value = enabled
|
||||
if (!enabled) _poseAngles.value = null
|
||||
if (!enabled) {
|
||||
_poseAngles.value = null
|
||||
posePhaseDetector.reset()
|
||||
_posePhase.value = null
|
||||
_poseMetrics.value = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,6 +158,9 @@ class CameraViewModel(application: Application) : AndroidViewModel(application)
|
||||
*/
|
||||
fun onPoseFrameUpdated(landmarks: Map<Int, SmoothedLandmark>, angles: PoseAngles) {
|
||||
_poseAngles.value = angles
|
||||
val phaseResult = posePhaseDetector.update(landmarks, angles) //for pose detector
|
||||
_posePhase.value = phaseResult.phase
|
||||
_poseMetrics.value = phaseResult.metrics
|
||||
if (_recordingState.value is RecordingState.Recording) {
|
||||
stepCountingSession.onFrame(landmarks, angles, System.currentTimeMillis())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user