优化性能第一版
This commit is contained in:
@@ -86,6 +86,7 @@ class MainActivity : AppCompatActivity() {
|
||||
private var cosineThreshold = DEFAULT_COSINE_THRESHOLD
|
||||
@Volatile private var matcher = FaceMatcher(DEFAULT_EUCLIDEAN_THRESHOLD, DEFAULT_COSINE_THRESHOLD)
|
||||
private val recognitionHistory = mutableMapOf<Int, ArrayDeque<RecognitionResult>>()
|
||||
private val recognitionCache = mutableMapOf<Int, CachedRecognition>()
|
||||
|
||||
private val permissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
@@ -415,6 +416,8 @@ class MainActivity : AppCompatActivity() {
|
||||
matcherConfigSummaryText.text = matcherConfigSummary()
|
||||
euclideanThresholdInput.setText(formatThreshold(euclideanThreshold))
|
||||
cosineThresholdInput.setText(formatThreshold(cosineThreshold))
|
||||
recognitionHistory.clear()
|
||||
recognitionCache.clear()
|
||||
overlayView.update(emptyList())
|
||||
|
||||
if (hasCameraPermission()) {
|
||||
@@ -473,6 +476,8 @@ class MainActivity : AppCompatActivity() {
|
||||
analysisUseCase = null
|
||||
cameraProvider?.unbindAll()
|
||||
cameraProvider = null
|
||||
recognitionHistory.clear()
|
||||
recognitionCache.clear()
|
||||
overlayView.update(emptyList())
|
||||
processor?.close()
|
||||
processor = null
|
||||
@@ -513,6 +518,7 @@ class MainActivity : AppCompatActivity() {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (currentMode == AppMode.RECOGNITION) {
|
||||
recognitionHistory.clear()
|
||||
recognitionCache.clear()
|
||||
overlayView.update(emptyList())
|
||||
updateRecognitionStatus(emptyList())
|
||||
}
|
||||
@@ -520,11 +526,26 @@ class MainActivity : AppCompatActivity() {
|
||||
return@launch
|
||||
}
|
||||
|
||||
val bitmap = ImageUtils.imageProxyToBitmap(image)
|
||||
val activeTrackIds = faces.mapNotNull { it.trackingId }.toSet()
|
||||
val rawDetections = faces.map { face ->
|
||||
val facesNeedingRefresh = faces.mapIndexedNotNull { index, face ->
|
||||
if (shouldRefreshRecognition(face.trackingId, now)) index to face else null
|
||||
}
|
||||
val refreshedResults = mutableMapOf<Int, RecognitionResult>()
|
||||
|
||||
if (facesNeedingRefresh.isNotEmpty()) {
|
||||
val bitmap = ImageUtils.imageProxyToBitmap(image)
|
||||
facesNeedingRefresh.forEach { (index, face) ->
|
||||
val embedding = faceProcessor.embedFace(bitmap, face)
|
||||
val result = stabilizeRecognition(face, matcher.findNearest(embedding, students))
|
||||
val refreshed = matcher.findNearest(embedding, students)
|
||||
refreshedResults[index] = refreshed
|
||||
face.trackingId?.let { trackId ->
|
||||
recognitionCache[trackId] = CachedRecognition(refreshed, now)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val rawDetections = faces.mapIndexed { index, face ->
|
||||
val result = resolveRecognitionResult(face, refreshedResults[index], now)
|
||||
RawDetection(
|
||||
bounds = face.boundingBox,
|
||||
result = result,
|
||||
@@ -532,6 +553,7 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
recognitionHistory.keys.retainAll(activeTrackIds)
|
||||
recognitionCache.keys.retainAll(activeTrackIds)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
if (currentMode == AppMode.RECOGNITION) {
|
||||
@@ -710,12 +732,37 @@ class MainActivity : AppCompatActivity() {
|
||||
return if (best.value.size >= 2) best.value.last() else result
|
||||
}
|
||||
|
||||
private fun shouldRefreshRecognition(trackId: Int?, now: Long): Boolean {
|
||||
if (trackId == null) return true
|
||||
val cached = recognitionCache[trackId] ?: return true
|
||||
return now - cached.updatedAtMs >= RECOGNITION_REFRESH_MS
|
||||
}
|
||||
|
||||
private fun resolveRecognitionResult(
|
||||
face: Face,
|
||||
refreshedResult: RecognitionResult?,
|
||||
now: Long
|
||||
): RecognitionResult {
|
||||
val trackId = face.trackingId
|
||||
val directResult = when {
|
||||
refreshedResult != null -> refreshedResult
|
||||
trackId != null && recognitionCache.containsKey(trackId) -> recognitionCache.getValue(trackId).result
|
||||
else -> RecognitionResult(null, Float.MAX_VALUE, 0f, MatchType.NO_DATA)
|
||||
}
|
||||
val stabilized = stabilizeRecognition(face, directResult)
|
||||
if (trackId != null) {
|
||||
recognitionCache[trackId] = CachedRecognition(stabilized, now)
|
||||
}
|
||||
return stabilized
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MATCHER_PREFS_NAME = "face_matcher_config"
|
||||
private const val KEY_EUCLIDEAN_THRESHOLD = "euclidean_threshold"
|
||||
private const val KEY_COSINE_THRESHOLD = "cosine_threshold"
|
||||
private const val DEFAULT_EUCLIDEAN_THRESHOLD = 1.0f
|
||||
private const val DEFAULT_COSINE_THRESHOLD = 0.6f
|
||||
private const val RECOGNITION_REFRESH_MS = 400L
|
||||
private const val ANALYSIS_INTERVAL_MS = 120L
|
||||
private val ANALYSIS_SIZE = Size(640, 480)
|
||||
}
|
||||
@@ -732,3 +779,8 @@ private data class RawDetection(
|
||||
val result: RecognitionResult,
|
||||
val confidence: Float?
|
||||
)
|
||||
|
||||
private data class CachedRecognition(
|
||||
val result: RecognitionResult,
|
||||
val updatedAtMs: Long
|
||||
)
|
||||
|
||||
@@ -128,7 +128,7 @@ class FaceProcessor(context: Context) : AutoCloseable {
|
||||
fun embedFace(bitmap: Bitmap, face: Face): FloatArray {
|
||||
val cropped = cropFace(bitmap, face.boundingBox)
|
||||
val aligned = alignFaceWithLandmarks(cropped, face, 0, 0)
|
||||
val preprocessed = preprocessor.preprocess(aligned)
|
||||
val preprocessed = preprocessor.preprocessRealtime(aligned)
|
||||
return embedder.embed(preprocessed)
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,13 @@ class ImagePreprocessor {
|
||||
return histogramEqualize(enhanced)
|
||||
}
|
||||
|
||||
/**
|
||||
* 实时识别场景使用轻量预处理,减少每帧 CPU 开销。
|
||||
*/
|
||||
fun preprocessRealtime(bitmap: Bitmap): Bitmap {
|
||||
return enhanceBrightnessContrast(bitmap, brightness = 0.06f, contrast = 1.08f)
|
||||
}
|
||||
|
||||
/**
|
||||
* 亮度对比度增强
|
||||
* @param brightness 亮度调整 (-1 到 1,0 为不调整)
|
||||
|
||||
Reference in New Issue
Block a user