Added tuning feature and hand palm action to reset the timer
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package com.example.jnicpp.bowling
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.provider.MediaStore
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
/**
|
||||
* Diagnostic tool: extracts specific frames from a recorded video at given
|
||||
* millisecond offsets, saving each as a PNG for visual inspection. Used to
|
||||
* spot-check whether a confirmed step timestamp from a debug trace
|
||||
* corresponds to real foot motion in the recorded video's baked-in
|
||||
* skeleton overlay, or to a bowler standing still.
|
||||
*
|
||||
* Reads the video by querying its MediaStore content Uri (by display
|
||||
* name) rather than copying it into app-external storage first --
|
||||
* `connectedAndroidTest` reinstalls the app package before every run,
|
||||
* which wipes app-external storage each time, but the video's own
|
||||
* MediaStore entry (it was saved into the public Movies/bowling
|
||||
* collection by CameraXController.startRecording) is untouched by that.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FrameExtractTest {
|
||||
|
||||
@Test
|
||||
fun extractFrames() {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val displayName = "bowling_20260907_183828.mp4"
|
||||
|
||||
val uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
|
||||
val projection = arrayOf(MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME)
|
||||
var videoUri: android.net.Uri? = null
|
||||
var totalRows = 0
|
||||
val names = StringBuilder()
|
||||
context.contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
totalRows++
|
||||
val name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME))
|
||||
if (totalRows <= 5) names.append(name).append("; ")
|
||||
if (name == displayName) {
|
||||
val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID))
|
||||
videoUri = android.content.ContentUris.withAppendedId(uri, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
assertNotNull("video '$displayName' not found in MediaStore (saw $totalRows rows, e.g.: $names)", videoUri)
|
||||
|
||||
val outDir = context.getExternalFilesDir(null)!!.resolve("frames").apply { mkdirs() }
|
||||
|
||||
// Millisecond offsets into the video to extract, set per-investigation.
|
||||
val offsetsMs = listOf(59526L, 65668L, 102581L, 135492L, 7288L, 8657L)
|
||||
|
||||
val retriever = MediaMetadataRetriever()
|
||||
retriever.setDataSource(context, videoUri)
|
||||
|
||||
for (offsetMs in offsetsMs) {
|
||||
val bitmap: Bitmap? = retriever.getFrameAtTime(offsetMs * 1000, MediaMetadataRetriever.OPTION_CLOSEST)
|
||||
if (bitmap != null) {
|
||||
val file = File(outDir, "frame_${offsetMs}.png")
|
||||
FileOutputStream(file).use { out ->
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
retriever.release()
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ class VideoStepReplayTest {
|
||||
val frame = buildPoseFrame(t, landmarks, smoothedAnkleHip, angles)
|
||||
val result = liveStepDetector.update(frame)
|
||||
finalStepCount = result.stepCount
|
||||
logger.log(landmarks, t, finalStepCount)
|
||||
logger.log(landmarks, t, finalStepCount, result.handRaiseProgress)
|
||||
framesProcessed++
|
||||
}
|
||||
t += stepMs
|
||||
|
||||
Reference in New Issue
Block a user