This commit is contained in:
2026-08-20 18:00:47 +08:00
parent b9934c996d
commit 45d55028d7
17 changed files with 500 additions and 1520 deletions
+1 -3
View File
@@ -1,4 +1,2 @@
pheasant
hare
dove
fish
cover
@@ -32,7 +32,7 @@ class TFLiteDetector private constructor(
ByteBuffer.allocateDirect(1 * INPUT_SIZE * INPUT_SIZE * 3 * 4)
.order(ByteOrder.nativeOrder())
private val outputFloats = FloatArray((4 + NUM_CLASSES) * NUM_ANCHORS)
private val outputFloats = FloatArray((4 + labels.size) * NUM_ANCHORS)
override fun detect(bitmap: Bitmap): List<DetectionResult> {
preprocess(bitmap)
@@ -68,7 +68,7 @@ class TFLiteDetector private constructor(
val h = outputFloats[3 * NUM_ANCHORS + a]
var bestCls = 0
var bestScore = 0f
for (c in 0 until NUM_CLASSES) {
for (c in 0 until labels.size) {
val s = outputFloats[(4 + c) * NUM_ANCHORS + a]
if (s > bestScore) {
bestScore = s
@@ -96,7 +96,6 @@ class TFLiteDetector private constructor(
/** 动物类最低保留分数:低于此分不输出(低分候选由运动检测提升显示) */
const val MIN_SCORE = 0.20f
private const val NUM_CLASSES = 4
private const val NUM_ANCHORS = 8400
private const val IOU_THRESHOLD = 0.45f
private const val MAX_DETECTIONS = 20
@@ -4,10 +4,11 @@ import android.content.Context
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import kotlin.math.roundToInt
import kotlin.math.tan
/**
* 单目距离估计(针孔模型):距离 = 焦距px × 参考体型 / 框高px。
* 误差预期 ±30%5~50m);鱼类目标受折射影响、生境区域按植被高度估算,均仅供参考。
* 误差预期 ±30%(5~50m);生境区域按植被高度估算,均仅供参考。
*/
class DistanceEstimator(context: Context) {
@@ -19,27 +20,24 @@ class DistanceEstimator(context: Context) {
// 参考体型(米)
private val speciesSizeM = mapOf(
"pheasant" to 0.45f, // 身高
"hare" to 0.45f, // 身长
"dove" to 0.30f, // 体长
"fish" to 1.00f, // 典型可见体长(误差大)
"cover" to 0.50f, // 植被高度(水面区域误差大)
"cover" to 0.50f, // 植被高度(误差大)
)
fun estimate(
label: String,
boxHeightNorm: Float,
imageHeightPx: Int,
visibleHeightPx: Int,
cameraId: String?,
): Float? {
val realH = speciesSizeM[label] ?: return null
val boxH = boxHeightNorm * imageHeightPx
val boxH = boxHeightNorm * visibleHeightPx
if (boxH < 8f) return null // 过小目标不估算
val focalPx = focalPx(imageHeightPx, cameraId)
val focalPx = focalPx(visibleHeightPx, cameraId)
if (focalPx <= 0f) return null
return (focalPx * realH / boxH).roundToInt().toFloat()
}
/** focal_px = focal_mm × (imageHeightPx / sensorHeightMm) */
/** focal_px = focal_mm × (imageHeightPx / sensorHeightMm);内参缺失时用视场角推算 */
private fun focalPx(imageHeightPx: Int, cameraId: String?): Float {
val key = "$cameraId:$imageHeightPx"
focalPxCache[key]?.let { return it }
@@ -52,7 +50,7 @@ class DistanceEstimator(context: Context) {
if (focalMm != null && sensor != null) {
focalMm * imageHeightPx / sensor.height
} else {
-1f
fovFallback(imageHeightPx, c)
}
} catch (e: Exception) {
-1f
@@ -60,4 +58,11 @@ class DistanceEstimator(context: Context) {
focalPxCache[key] = value
return value
}
/** focal_px = imageHeightPx / (2·tan(fovV/2)) */
private fun fovFallback(imageHeightPx: Int, c: CameraCharacteristics): Float {
val fovV = c.get(CameraCharacteristics.LENS_INFO_AVAILABLE_VERTICAL_VIEW_ANGLES)
?.firstOrNull() ?: return -1f
return (imageHeightPx / (2.0 * tan(Math.toRadians(fovV / 2.0)))).toFloat()
}
}
@@ -18,17 +18,11 @@ import kotlin.math.roundToInt
private val speciesColors = mapOf(
"pheasant" to Color(0xFFE53935),
"hare" to Color(0xFF1E88E5),
"dove" to Color(0xFF8E24AA),
"fish" to Color(0xFF00ACC1),
"cover" to Color(0xFFFDD835),
)
private val speciesLabels = mapOf(
"pheasant" to "野鸡",
"hare" to "野兔",
"dove" to "斑鸠",
"fish" to "",
"cover" to "疑似区域",
)
@@ -108,7 +108,10 @@ class CameraViewModel(
val boosted = r.score < confThreshold &&
motionRegions.any { MotionAggregator.centerInRegion(r, it) }
val distance = if (showDistance) {
distanceEstimator.estimate(r.label, r.height, imageHeightPx, cameraId)
// 模型 CENTER_CROP 到方形输入, 归一化框高对应原图较短边(最大内接正方形)
distanceEstimator.estimate(
r.label, r.height, minOf(imageWidthPx, imageHeightPx), cameraId,
)
} else {
null
}