删除video检测的代码,添加延时输出的代码。
This commit is contained in:
@@ -139,6 +139,51 @@ class FaceLandmarkerHelper(
|
||||
}
|
||||
}
|
||||
|
||||
private var frameId: Long = 0
|
||||
// 简化的延时跟踪
|
||||
private val frameTimings = mutableMapOf<Long, FrameTiming>()
|
||||
private var lastFpsUpdateTime: Long = 0
|
||||
private var frameCount: Int = 0
|
||||
private var currentFps: Double = 0.0
|
||||
|
||||
// 内部数据类用于存储时间信息
|
||||
private data class FrameTiming(
|
||||
var captureTime: Long = 0,
|
||||
var preprocessStartTime: Long = 0,
|
||||
var preprocessEndTime: Long = 0,
|
||||
var detectionStartTime: Long = 0
|
||||
)
|
||||
|
||||
// 辅助方法:记录帧时间信息
|
||||
private fun recordFrameTiming(frameId: Long, block: (FrameTiming) -> Unit) {
|
||||
val timing = frameTimings.getOrPut(frameId) { FrameTiming() }
|
||||
block(timing)
|
||||
}
|
||||
|
||||
// 辅助方法:更新FPS计算
|
||||
private fun updateFps() {
|
||||
frameCount++
|
||||
val currentTime = SystemClock.uptimeMillis()
|
||||
|
||||
if (lastFpsUpdateTime == 0L) {
|
||||
lastFpsUpdateTime = currentTime
|
||||
return
|
||||
}
|
||||
|
||||
val timeDiff = currentTime - lastFpsUpdateTime
|
||||
if (timeDiff >= 1000) { // 每秒更新一次FPS
|
||||
currentFps = frameCount * 1000.0 / timeDiff
|
||||
frameCount = 0
|
||||
lastFpsUpdateTime = currentTime
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:清理旧的时间记录
|
||||
private fun cleanupOldTimings(currentFrameId: Long) {
|
||||
val framesToRemove = frameTimings.keys.filter { it < currentFrameId - 10 }
|
||||
framesToRemove.forEach { frameTimings.remove(it) }
|
||||
}
|
||||
|
||||
// Convert the ImageProxy to MP Image and feed it to FacelandmakerHelper.
|
||||
fun detectLiveStream(
|
||||
imageProxy: ImageProxy,
|
||||
@@ -152,6 +197,15 @@ class FaceLandmarkerHelper(
|
||||
}
|
||||
val frameTime = SystemClock.uptimeMillis()
|
||||
|
||||
val currentFrameId = frameId++
|
||||
val captureTime = SystemClock.uptimeMillis()
|
||||
|
||||
// 记录捕获时间
|
||||
recordFrameTiming(currentFrameId) {
|
||||
it.captureTime = captureTime
|
||||
it.preprocessStartTime = SystemClock.uptimeMillis()
|
||||
}
|
||||
|
||||
// Copy out RGB bits from the frame to a bitmap buffer
|
||||
val bitmapBuffer =
|
||||
Bitmap.createBitmap(
|
||||
@@ -198,152 +252,54 @@ class FaceLandmarkerHelper(
|
||||
// be returned in returnLivestreamResult function
|
||||
}
|
||||
|
||||
// Accepts the URI for a video file loaded from the user's gallery and attempts to run
|
||||
// face landmarker inference on the video. This process will evaluate every
|
||||
// frame in the video and attach the results to a bundle that will be
|
||||
// returned.
|
||||
fun detectVideoFile(
|
||||
videoUri: Uri,
|
||||
inferenceIntervalMs: Long
|
||||
): VideoResultBundle? {
|
||||
if (runningMode != RunningMode.VIDEO) {
|
||||
throw IllegalArgumentException(
|
||||
"Attempting to call detectVideoFile" +
|
||||
" while not using RunningMode.VIDEO"
|
||||
)
|
||||
}
|
||||
|
||||
// Inference time is the difference between the system time at the start and finish of the
|
||||
// process
|
||||
val startTime = SystemClock.uptimeMillis()
|
||||
|
||||
var didErrorOccurred = false
|
||||
|
||||
// Load frames from the video and run the face landmarker.
|
||||
val retriever = MediaMetadataRetriever()
|
||||
retriever.setDataSource(context, videoUri)
|
||||
val videoLengthMs =
|
||||
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
|
||||
?.toLong()
|
||||
|
||||
// Note: We need to read width/height from frame instead of getting the width/height
|
||||
// of the video directly because MediaRetriever returns frames that are smaller than the
|
||||
// actual dimension of the video file.
|
||||
val firstFrame = retriever.getFrameAtTime(0)
|
||||
val width = firstFrame?.width
|
||||
val height = firstFrame?.height
|
||||
|
||||
// If the video is invalid, returns a null detection result
|
||||
if ((videoLengthMs == null) || (width == null) || (height == null)) return null
|
||||
|
||||
// Next, we'll get one frame every frameInterval ms, then run detection on these frames.
|
||||
val resultList = mutableListOf<FaceLandmarkerResult>()
|
||||
val numberOfFrameToRead = videoLengthMs.div(inferenceIntervalMs)
|
||||
|
||||
for (i in 0..numberOfFrameToRead) {
|
||||
val timestampMs = i * inferenceIntervalMs // ms
|
||||
|
||||
retriever
|
||||
.getFrameAtTime(
|
||||
timestampMs * 1000, // convert from ms to micro-s
|
||||
MediaMetadataRetriever.OPTION_CLOSEST
|
||||
)
|
||||
?.let { frame ->
|
||||
// Convert the video frame to ARGB_8888 which is required by the MediaPipe
|
||||
val argb8888Frame =
|
||||
if (frame.config == Bitmap.Config.ARGB_8888) frame
|
||||
else frame.copy(Bitmap.Config.ARGB_8888, false)
|
||||
|
||||
// Convert the input Bitmap object to an MPImage object to run inference
|
||||
val mpImage = BitmapImageBuilder(argb8888Frame).build()
|
||||
|
||||
// Run face landmarker using MediaPipe Face Landmarker API
|
||||
faceLandmarker?.detectForVideo(mpImage, timestampMs)
|
||||
?.let { detectionResult ->
|
||||
resultList.add(detectionResult)
|
||||
} ?: {
|
||||
didErrorOccurred = true
|
||||
faceLandmarkerHelperListener?.onError(
|
||||
"ResultBundle could not be returned" +
|
||||
" in detectVideoFile"
|
||||
)
|
||||
}
|
||||
}
|
||||
?: run {
|
||||
didErrorOccurred = true
|
||||
faceLandmarkerHelperListener?.onError(
|
||||
"Frame at specified time could not be" +
|
||||
" retrieved when detecting in video."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
retriever.release()
|
||||
|
||||
val inferenceTimePerFrameMs =
|
||||
(SystemClock.uptimeMillis() - startTime).div(numberOfFrameToRead)
|
||||
|
||||
return if (didErrorOccurred) {
|
||||
null
|
||||
} else {
|
||||
VideoResultBundle(resultList, inferenceTimePerFrameMs, height, width)
|
||||
}
|
||||
}
|
||||
|
||||
// Accepted a Bitmap and runs face landmarker inference on it to return
|
||||
// results back to the caller
|
||||
fun detectImage(image: Bitmap): ResultBundle? {
|
||||
if (runningMode != RunningMode.IMAGE) {
|
||||
throw IllegalArgumentException(
|
||||
"Attempting to call detectImage" +
|
||||
" while not using RunningMode.IMAGE"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// Inference time is the difference between the system time at the
|
||||
// start and finish of the process
|
||||
val startTime = SystemClock.uptimeMillis()
|
||||
|
||||
// Convert the input Bitmap object to an MPImage object to run inference
|
||||
val mpImage = BitmapImageBuilder(image).build()
|
||||
|
||||
// Run face landmarker using MediaPipe Face Landmarker API
|
||||
faceLandmarker?.detect(mpImage)?.also { landmarkResult ->
|
||||
val inferenceTimeMs = SystemClock.uptimeMillis() - startTime
|
||||
return ResultBundle(
|
||||
landmarkResult,
|
||||
inferenceTimeMs,
|
||||
image.height,
|
||||
image.width
|
||||
)
|
||||
}
|
||||
|
||||
// If faceLandmarker?.detect() returns null, this is likely an error. Returning null
|
||||
// to indicate this.
|
||||
faceLandmarkerHelperListener?.onError(
|
||||
"Face Landmarker failed to detect."
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
// Return the landmark result to this FaceLandmarkerHelper's caller
|
||||
private fun returnLivestreamResult(
|
||||
result: FaceLandmarkerResult,
|
||||
input: MPImage
|
||||
) {
|
||||
val resultTime = SystemClock.uptimeMillis()
|
||||
val frameId = result.timestampMs()
|
||||
if( result.faceLandmarks().size > 0 ) {
|
||||
val finishTimeMs = SystemClock.uptimeMillis()
|
||||
val inferenceTime = finishTimeMs - result.timestampMs()
|
||||
// 计算各种延时
|
||||
val timings = frameTimings[frameId]
|
||||
if (timings != null) {
|
||||
val totalLatency = resultTime - timings.captureTime
|
||||
val preprocessLatency = timings.preprocessEndTime - timings.preprocessStartTime
|
||||
val detectionLatency = resultTime - timings.detectionStartTime
|
||||
val captureToDetectionLatency = timings.detectionStartTime - timings.captureTime
|
||||
|
||||
faceLandmarkerHelperListener?.onResults(
|
||||
ResultBundle(
|
||||
result,
|
||||
inferenceTime,
|
||||
input.height,
|
||||
input.width
|
||||
Log.d("TimeLatency",
|
||||
"总延时: ${totalLatency}ms | " +
|
||||
"预处理: ${preprocessLatency}ms | " +
|
||||
"检测: ${detectionLatency}ms | " +
|
||||
"捕获到检测: ${captureToDetectionLatency}ms | " +
|
||||
"FPS: ${String.format("%.1f", currentFps)}"
|
||||
)
|
||||
)
|
||||
|
||||
val finishTimeMs = SystemClock.uptimeMillis()
|
||||
val inferenceTime = finishTimeMs - result.timestampMs()
|
||||
faceLandmarkerHelperListener?.onResults(
|
||||
ResultBundle(
|
||||
result,
|
||||
inferenceTime, // 主要是检测时间
|
||||
input.height,
|
||||
input.width,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val finishTimeMs = SystemClock.uptimeMillis()
|
||||
val inferenceTime = finishTimeMs - result.timestampMs()
|
||||
faceLandmarkerHelperListener?.onResults(
|
||||
ResultBundle(
|
||||
result,
|
||||
inferenceTime,
|
||||
input.height,
|
||||
input.width
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
else {
|
||||
faceLandmarkerHelperListener?.onEmpty()
|
||||
@@ -379,13 +335,6 @@ class FaceLandmarkerHelper(
|
||||
val inputImageWidth: Int,
|
||||
)
|
||||
|
||||
data class VideoResultBundle(
|
||||
val results: List<FaceLandmarkerResult>,
|
||||
val inferenceTime: Long,
|
||||
val inputImageHeight: Int,
|
||||
val inputImageWidth: Int,
|
||||
)
|
||||
|
||||
interface LandmarkerListener {
|
||||
fun onError(error: String, errorCode: Int = OTHER_ERROR)
|
||||
fun onResults(resultBundle: ResultBundle)
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
@@ -182,6 +183,12 @@ public class MainActivity extends GameActivity implements FaceLandmarkerHelper.L
|
||||
@Override
|
||||
public void analyze(@NonNull ImageProxy image) {
|
||||
try {
|
||||
long analyzeTime = SystemClock.elapsedRealtimeNanos();
|
||||
long captureTime = image.getImageInfo().getTimestamp();
|
||||
long latencyNs = analyzeTime - captureTime;
|
||||
double latencyMs = latencyNs / 1_000_000.0;
|
||||
|
||||
Log.d("CameraLatency", String.format("Image capture to analyze latency: %.2f ms", latencyMs));
|
||||
//Log.d("Camera", "Received image: " + image.getWidth() + "x" + image.getHeight());
|
||||
processImageForVulkan(image);
|
||||
detectFace(image);
|
||||
@@ -315,7 +322,7 @@ public class MainActivity extends GameActivity implements FaceLandmarkerHelper.L
|
||||
nativeBuffer.order(ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
|
||||
long start_time = System.currentTimeMillis();
|
||||
|
||||
// ArrayList<Vector> vertexs = new ArrayList<>();
|
||||
// ArrayList<Integer> triangle_index = new ArrayList<>();
|
||||
@@ -358,7 +365,10 @@ public class MainActivity extends GameActivity implements FaceLandmarkerHelper.L
|
||||
FloatBuffer floatBuffer = nativeBuffer.asFloatBuffer();
|
||||
floatBuffer.put(points);
|
||||
floatBuffer.position(0);
|
||||
long cur_time = System.currentTimeMillis();
|
||||
Log.i("TimeLatency","Result Data ProcessTime:" + (cur_time-start_time));
|
||||
passDataToNative(nativeBuffer, index, width, height);
|
||||
Log.i("TimeLatency","passDataToNative ProcessTime:" + (System.currentTimeMillis() - cur_time));
|
||||
}
|
||||
|
||||
public float z_rate = -1.0f;
|
||||
|
||||
Reference in New Issue
Block a user