79 lines
3.2 KiB
Kotlin
79 lines
3.2 KiB
Kotlin
package com.example.jnicpp.bowling
|
|||
|
|
|
||
|
|
import android.media.MediaMetadataRetriever
|
||
|
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||
|
|
import androidx.test.platform.app.InstrumentationRegistry
|
||
|
|
import com.google.android.gms.tasks.Tasks
|
||
|
|
import com.google.mlkit.vision.common.InputImage
|
||
|
|
import com.google.mlkit.vision.pose.PoseDetection
|
||
|
|
import com.google.mlkit.vision.pose.accurate.AccuratePoseDetectorOptions
|
||
|
|
import org.junit.Test
|
||
|
|
import org.junit.runner.RunWith
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Replays a pre-recorded reference video through the exact same
|
||
|
|
* detection/smoothing/step-counting pipeline the live camera screen uses
|
||
|
|
* (PoseAnalyzer's detector config -> PoseLandmarkSmoother ->
|
||
|
|
* AnkleHipMovingAverageFilter -> buildPoseFrame -> LiveStepDetector), so the
|
||
|
|
* algorithm can be validated against a video with a known, hand-counted
|
||
|
|
* step count without needing a live device recording session each time.
|
||
|
|
*
|
||
|
|
* Not run as part of the normal test suite -- this is a diagnostic tool,
|
||
|
|
* invoked directly via `connectedAndroidTest` with a specific video pushed
|
||
|
|
* to the device first.
|
||
|
|
*/
|
||
|
|
@RunWith(AndroidJUnit4::class)
|
||
|
|
class VideoStepReplayTest {
|
||
|
|
|
||
|
|
@Test
|
||
|
|
fun replayReferenceVideo() {
|
||
|
|
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||
|
|
val videoPath = context.getExternalFilesDir(null)!!.resolve("reference_test.mp4").absolutePath
|
||
|
|
|
||
|
|
val retriever = MediaMetadataRetriever()
|
||
|
|
retriever.setDataSource(videoPath)
|
||
|
|
val durationMs = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
|
||
|
|
?.toLongOrNull() ?: 0L
|
||
|
|
|
||
|
|
val detector = PoseDetection.getClient(
|
||
|
|
AccuratePoseDetectorOptions.Builder()
|
||
|
|
.setDetectorMode(AccuratePoseDetectorOptions.STREAM_MODE)
|
||
|
|
.build()
|
||
|
|
)
|
||
|
|
val landmarkSmoother = PoseLandmarkSmoother()
|
||
|
|
val ankleHipSmoother = AnkleHipMovingAverageFilter()
|
||
|
|
val liveStepDetector = LiveStepDetector()
|
||
|
|
val logger = DebugSessionLogger(context)
|
||
|
|
logger.start()
|
||
|
|
|
||
|
|
val stepMs = 33L
|
||
|
|
var t = 0L
|
||
|
|
var finalStepCount = 0
|
||
|
|
var framesProcessed = 0
|
||
|
|
while (t < durationMs) {
|
||
|
|
val bitmap = retriever.getFrameAtTime(t * 1000, MediaMetadataRetriever.OPTION_CLOSEST)
|
||
|
|
if (bitmap != null) {
|
||
|
|
val inputImage = InputImage.fromBitmap(bitmap, 0)
|
||
|
|
val pose = Tasks.await(detector.process(inputImage))
|
||
|
|
val landmarks = landmarkSmoother.smooth(pose)
|
||
|
|
val angles = PoseAngleCalculator.compute(landmarks)
|
||
|
|
val smoothedAnkleHip = ankleHipSmoother.smooth(landmarks)
|
||
|
|
val frame = buildPoseFrame(t, landmarks, smoothedAnkleHip, angles)
|
||
|
|
val result = liveStepDetector.update(frame)
|
||
|
|
finalStepCount = result.stepCount
|
||
|
|
logger.log(landmarks, t, finalStepCount)
|
||
|
|
framesProcessed++
|
||
|
|
}
|
||
|
|
t += stepMs
|
||
|
|
}
|
||
|
|
logger.stop()
|
||
|
|
detector.close()
|
||
|
|
retriever.release()
|
||
|
|
|
||
|
|
println(
|
||
|
|
"VideoStepReplayTest: processed $framesProcessed frames over ${durationMs}ms, " +
|
||
|
|
"final step count = $finalStepCount"
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|