fix(bv): post_outlier_filter default false — cm=3 CH0 부당 drop 해소 (Δ=0)
Python `_bv_core` (bv_estimation.py:1138) 는 `post_outlier_filter=False` default. Kotlin `estimateBladderVolume` 은 이 flag 없이 **무조건 실행**하는 버그로 CH0 이 median 25% 벗어나면 부당하게 dropped 되어 nch=3 대신 2 처리. 이 divergence 는 특정 trace 에서만 발현 (data123 cm=3 trace 0-4, 9-10): - Python : valid_channels = [0,1,2,3] (CH0 D_mm=63.43 포함) - Kotlin : valid_channels = [1,2,3] (CH0 부당 drop) → BV +125 mL 편차 원인: trace 0 case 로 검증 centerWalls inset : (15,48), (15,67), (15,67), null d_post_mm : [99.78, 136.5, 136.5] median : 136.5 post_tol_mm : max(136.5·0.25, 5·1.936) = 34.125 |99.78-136.5| : 36.72 → tolerance 초과 → CH0 drop 하지만 Python default (filter off) 는 CH0 유지 → 올바른 nch=3. 수정: - `estimateBladderVolume(Double)` 에 `postOutlierFilter: Boolean = false` 추가 (Python default 매칭). block 안에서 flag 체크. - `estimateBladderVolume(Int)` legacy 오버로드는 default true 유지 (PiezoMonitoring 기존 동작 보존, phantom QC 회귀 방지). 검증 (data123 cm=3 11 trace): trace 0 py=283.156 → kt=283.156 Δ=0.000 trace 1 py=439.303 → kt=439.303 Δ=0.000 ... (전체 11 trace bit-perfect) V3 CenterAligner: cm=0 bv_cv : 0.183 (py) == 0.183 (kt) ★ cm=1 bv_cv : 0.260 (py) == 0.260 (kt) ★ cm=3 bv_cv : 0.130 (py) == 0.130 (kt) ★ selected_cm = 3 일치, target 완전 동일. CenterAlignerValidationTest tolerance 0.10 → 0.001 로 강화. 추후 회귀 방지. Cm3PerTraceDumpTest.kt 신규 — cm=3 per-trace bit-perfect 회귀 게이트.
This commit is contained in:
@@ -631,7 +631,8 @@ private fun evalOnce(
|
||||
}
|
||||
}
|
||||
|
||||
/** Int 오버로드 — legacy 호환. */
|
||||
/** Int 오버로드 — legacy 호환. 기본 postOutlierFilter=true 로 기존 PiezoMonitoring 경로
|
||||
* 동작 보존 (Python default 는 false 지만 legacy Kotlin 은 무조건 실행이었음). */
|
||||
@JvmName("estimateBladderVolumeInt")
|
||||
fun estimateBladderVolume(
|
||||
walls: List<Pair<Int, Int>?>,
|
||||
@@ -644,6 +645,7 @@ fun estimateBladderVolume(
|
||||
applyAngleCorrection: Boolean = true,
|
||||
edgeCh: Int = walls.size - 1,
|
||||
edgeRefChannels: List<Int> = listOf(walls.size - 2, walls.size - 3),
|
||||
postOutlierFilter: Boolean = true,
|
||||
): BVResult? = estimateBladderVolume(
|
||||
walls = walls.map { it?.let { p -> Pair(p.first.toDouble(), p.second.toDouble()) } },
|
||||
distancePerSample = distancePerSample,
|
||||
@@ -655,6 +657,7 @@ fun estimateBladderVolume(
|
||||
applyAngleCorrection = applyAngleCorrection,
|
||||
edgeCh = edgeCh,
|
||||
edgeRefChannels = edgeRefChannels,
|
||||
postOutlierFilter = postOutlierFilter,
|
||||
)
|
||||
|
||||
fun estimateBladderVolume(
|
||||
@@ -672,6 +675,10 @@ fun estimateBladderVolume(
|
||||
bSiFloorRatio: Double? = null,
|
||||
/** Python `b_si_floor_edge_min`. edge 채널이 이 비율 이상 넓어야 floor 발동. */
|
||||
bSiFloorEdgeMin: Double = 0.5,
|
||||
/** Python `post_outlier_filter` (bv_estimation.py:1138). **default false** — Python
|
||||
* 기본 동작과 매칭. true 였을 때 CH0 이 median 25% 벗어나면 부당하게 dropped 됨
|
||||
* (data123 cm=3 trace 0 실증). */
|
||||
postOutlierFilter: Boolean = false,
|
||||
): BVResult? {
|
||||
|
||||
// 1) Edge channel filter — cap 방식 결정용 플래그
|
||||
@@ -735,7 +742,9 @@ fun estimateBladderVolume(
|
||||
var n = validChannels.size
|
||||
|
||||
// 2-1) Post median outlier 제거 (#21 merge)
|
||||
if (n >= 3) {
|
||||
// Python `_bv_core` (bv_estimation.py:1199) — `post_outlier_filter=False` default.
|
||||
// 기본으론 실행 안 함. flag true 일 때만.
|
||||
if (postOutlierFilter && n >= 3) {
|
||||
val posts = dPost.toDoubleArray()
|
||||
val med = posts.sorted()[posts.size / 2]
|
||||
val postTol = max(med * 0.25, 5.0 * distancePerSample)
|
||||
|
||||
Reference in New Issue
Block a user