1
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user