优化性能第一版

This commit is contained in:
2026-05-10 19:21:07 +08:00
parent 4e875819b6
commit a26eed922a
3 changed files with 64 additions and 5 deletions

View File

@@ -86,6 +86,7 @@ class MainActivity : AppCompatActivity() {
private var cosineThreshold = DEFAULT_COSINE_THRESHOLD private var cosineThreshold = DEFAULT_COSINE_THRESHOLD
@Volatile private var matcher = FaceMatcher(DEFAULT_EUCLIDEAN_THRESHOLD, DEFAULT_COSINE_THRESHOLD) @Volatile private var matcher = FaceMatcher(DEFAULT_EUCLIDEAN_THRESHOLD, DEFAULT_COSINE_THRESHOLD)
private val recognitionHistory = mutableMapOf<Int, ArrayDeque<RecognitionResult>>() private val recognitionHistory = mutableMapOf<Int, ArrayDeque<RecognitionResult>>()
private val recognitionCache = mutableMapOf<Int, CachedRecognition>()
private val permissionLauncher = registerForActivityResult( private val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions() ActivityResultContracts.RequestMultiplePermissions()
@@ -415,6 +416,8 @@ class MainActivity : AppCompatActivity() {
matcherConfigSummaryText.text = matcherConfigSummary() matcherConfigSummaryText.text = matcherConfigSummary()
euclideanThresholdInput.setText(formatThreshold(euclideanThreshold)) euclideanThresholdInput.setText(formatThreshold(euclideanThreshold))
cosineThresholdInput.setText(formatThreshold(cosineThreshold)) cosineThresholdInput.setText(formatThreshold(cosineThreshold))
recognitionHistory.clear()
recognitionCache.clear()
overlayView.update(emptyList()) overlayView.update(emptyList())
if (hasCameraPermission()) { if (hasCameraPermission()) {
@@ -473,6 +476,8 @@ class MainActivity : AppCompatActivity() {
analysisUseCase = null analysisUseCase = null
cameraProvider?.unbindAll() cameraProvider?.unbindAll()
cameraProvider = null cameraProvider = null
recognitionHistory.clear()
recognitionCache.clear()
overlayView.update(emptyList()) overlayView.update(emptyList())
processor?.close() processor?.close()
processor = null processor = null
@@ -513,6 +518,7 @@ class MainActivity : AppCompatActivity() {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (currentMode == AppMode.RECOGNITION) { if (currentMode == AppMode.RECOGNITION) {
recognitionHistory.clear() recognitionHistory.clear()
recognitionCache.clear()
overlayView.update(emptyList()) overlayView.update(emptyList())
updateRecognitionStatus(emptyList()) updateRecognitionStatus(emptyList())
} }
@@ -520,11 +526,26 @@ class MainActivity : AppCompatActivity() {
return@launch return@launch
} }
val bitmap = ImageUtils.imageProxyToBitmap(image)
val activeTrackIds = faces.mapNotNull { it.trackingId }.toSet() 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 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( RawDetection(
bounds = face.boundingBox, bounds = face.boundingBox,
result = result, result = result,
@@ -532,6 +553,7 @@ class MainActivity : AppCompatActivity() {
) )
} }
recognitionHistory.keys.retainAll(activeTrackIds) recognitionHistory.keys.retainAll(activeTrackIds)
recognitionCache.keys.retainAll(activeTrackIds)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (currentMode == AppMode.RECOGNITION) { if (currentMode == AppMode.RECOGNITION) {
@@ -710,12 +732,37 @@ class MainActivity : AppCompatActivity() {
return if (best.value.size >= 2) best.value.last() else result 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 { companion object {
private const val MATCHER_PREFS_NAME = "face_matcher_config" private const val MATCHER_PREFS_NAME = "face_matcher_config"
private const val KEY_EUCLIDEAN_THRESHOLD = "euclidean_threshold" private const val KEY_EUCLIDEAN_THRESHOLD = "euclidean_threshold"
private const val KEY_COSINE_THRESHOLD = "cosine_threshold" private const val KEY_COSINE_THRESHOLD = "cosine_threshold"
private const val DEFAULT_EUCLIDEAN_THRESHOLD = 1.0f private const val DEFAULT_EUCLIDEAN_THRESHOLD = 1.0f
private const val DEFAULT_COSINE_THRESHOLD = 0.6f private const val DEFAULT_COSINE_THRESHOLD = 0.6f
private const val RECOGNITION_REFRESH_MS = 400L
private const val ANALYSIS_INTERVAL_MS = 120L private const val ANALYSIS_INTERVAL_MS = 120L
private val ANALYSIS_SIZE = Size(640, 480) private val ANALYSIS_SIZE = Size(640, 480)
} }
@@ -732,3 +779,8 @@ private data class RawDetection(
val result: RecognitionResult, val result: RecognitionResult,
val confidence: Float? val confidence: Float?
) )
private data class CachedRecognition(
val result: RecognitionResult,
val updatedAtMs: Long
)

View File

@@ -128,7 +128,7 @@ class FaceProcessor(context: Context) : AutoCloseable {
fun embedFace(bitmap: Bitmap, face: Face): FloatArray { fun embedFace(bitmap: Bitmap, face: Face): FloatArray {
val cropped = cropFace(bitmap, face.boundingBox) val cropped = cropFace(bitmap, face.boundingBox)
val aligned = alignFaceWithLandmarks(cropped, face, 0, 0) val aligned = alignFaceWithLandmarks(cropped, face, 0, 0)
val preprocessed = preprocessor.preprocess(aligned) val preprocessed = preprocessor.preprocessRealtime(aligned)
return embedder.embed(preprocessed) return embedder.embed(preprocessed)
} }

View File

@@ -28,6 +28,13 @@ class ImagePreprocessor {
return histogramEqualize(enhanced) return histogramEqualize(enhanced)
} }
/**
* 实时识别场景使用轻量预处理,减少每帧 CPU 开销。
*/
fun preprocessRealtime(bitmap: Bitmap): Bitmap {
return enhanceBrightnessContrast(bitmap, brightness = 0.06f, contrast = 1.08f)
}
/** /**
* 亮度对比度增强 * 亮度对比度增强
* @param brightness 亮度调整 (-1 到 10 为不调整) * @param brightness 亮度调整 (-1 到 10 为不调整)