기존 §3 BV Estimation 은 브랜치 비교 관점만 · 두 method 자체를 나란히 놓고 상세 대조하는 섹션 없어서 §3-4 로 추가 (사용자 요청). 포함 (7 sub-section): 3-4-1. 부피 공식 (Frustum+Cap vs 4/3πr³) 3-4-2. 파이프라인 stage 비교 표 (11 stage · METHOD_D 만 있는 것 명시) 3-4-3. 결과 특성 표 (정확도 · 계산 비용 · 재현성 · phantom vs 인체) 3-4-4. 예시 계산 (phantom 300mL · 지름 83mm · 두 method 각각 산수) 3-4-5. 언제 어느 method 써야 하나 (권장 매트릭스) 3-4-6. 코드 진입점 요약 (dispatcher) 3-4-7. 향후 개선 여지 (하이브리드 · ellipsoid 확장 등) 핵심 근거: · phantom 300mL 실측 → METHOD_D_PHANTOM 296mL (-1.3%) vs METHOD_D 250mL (-17%) · 원인: 큰 대칭 방광은 구 가정이 정확 · Frustum+cap 은 adaptive 로도 저추정
18 KiB
demo-final vs feature/cloud-mvp 알고리즘 브랜치 비교
마지막 업데이트: 2026-08-11 · 대상: Alignment · Detection · BV Estimation
배경
VesiScan-Basic Android 앱은 두 개의 활성 브랜치로 관리됨.
| 브랜치 | 용도 | 대상 방광 | 상태 |
|---|---|---|---|
| demo-final | 시연/데모 전용 · 안정판 | phantom (구 모형 방광) | 동결 시연 |
| feature/cloud-mvp | 실사용/개발 · 인체 임상 | 실제 인체 (타원 방광) | 활성 개발 |
핵심 원칙: 두 브랜치 알고리즘 결과가 서로 다른 것이 정상 설계. phantom (구) 과 인체 (비대칭 타원) 은 기하가 근본 다르므로 · 같은 코드로 두 상황 모두 정확 계산 불가.
- demo-final = 데모 시 팬텀에서 300ml 근처 값이 안정적으로 나오는 것이 목표. 구 공식 (V = 4/3πr³) 사용.
- cloud-mvp = 인체 정확도 (Halir-Flusser + adaptive_large_bladder_relax + b_si_floor 등 Python parity).
또한 cloud-mvp 는 패키지 domain-driven 재배치를 완료: managers/* → piezo/* · alignment/* · common/util/*. 두 브랜치 동일 파일이 경로만 다르고 로직은 대부분 동일한 경우가 많음.
1. Alignment (정렬 · placement guide)
1-1. 기본 설정값
| 항목 | demo-final | feature/cloud-mvp |
|---|---|---|
alignmentAlgo (default) |
V1 | V1 |
placementGuideMode (default) |
SIMPLE (표시명 "Gradient") | SIMPLE |
| V2 진입 조건 | clinical alignment session 만 | clinical alignment session 만 |
verticalHitRequired (V2) |
3 | 3 |
VERTICAL_HIT_WINDOW / _MAJORITY |
6 / 3 | 6 / 3 |
STABILIZE_LOST_WINDOW / _MAJORITY |
4 / 3 | 4 / 3 |
| V3 CenterAligner (2-Pass batch) | ✅ 있음 | ❌ 없음 |
1-2. 로직 차이
- V1/V2 state machine 은 두 브랜치 100% 동일 (phase 진입 · hysteresis · CV 계산 · MOVE_UP/DOWN 방향 판정).
- RollingAligner의
detectMultichannel호출부:- demo-final:
applyCross = false(piezophantomtest ea3f15c 이식 · 2026-07-06) → 정렬 위치 선택은 base 검출로만 수행 · 교차채널 보정이 nch 를 흔들지 못하게 격리 · BV 산출은 별도 경로에서applyCross=true. - cloud-mvp:
applyCross파라미터 자체 제거 → 정렬 위치 판정에도 cross-channel 보정 결과 사용.
- demo-final:
- V3 CenterAligner (demo-final 전용): Pass 1 = 5 위치 각 20 cycle 스캔 → Rule A (ch3 필수 · 검출률 ≥ 80% · max nch · min bv_cv) 로 최적 위치 선택. Pass 2 = 재측정 검증. cloud-mvp 는 이 배치 알고리즘 자체가 없음.
1-3. 관련 파일
| 파일 | demo-final | cloud-mvp |
|---|---|---|
| PlacementGuideView | ui/views/monitoring/PlacementGuideView.kt (2317 L) |
alignment/ui/PlacementGuideView.kt (2187 L) |
| AlignmentAdvisorV2 (+ RollingAligner) | managers/AlignmentAdvisorV2.kt:677 (applyCross=false) |
alignment/AlignmentAdvisorV2.kt:680 (파라미터 제거) |
| AlignmentAdvisorV3 | managers/AlignmentAdvisorV3.kt (신규) |
— |
| ImuAnalysis / ImuPostureClassifier | managers/ |
alignment/ |
2. Detection (벽 검출)
2-1. 기본 설정값
| 항목 | demo-final | feature/cloud-mvp |
|---|---|---|
detectionMethod (default) |
METHOD_C (V4.1 SphereFit walls) | METHOD_D (Python for_app_share 1:1) |
MethodDParams.otsuRatio |
0.88 | 0.88 |
MethodDParams.oscfarWin |
9 | 9 |
MethodDParams.promGamma / shoulderPromGamma |
1.5 / 1.5 | 1.5 / 1.5 |
MethodDParams.minWallLumenRatio |
1.16 | 1.16 |
MethodDParams.minPostRawRatio |
1.08 | 1.08 |
MethodDParams.antDMax / dMax |
18 / 10 | 18 / 10 |
| Cross-channel stage 순서 | ①→②→③ (3-stage) | ①→①.5→②→③ (4-stage) |
VDIP_POST_RECOVERY |
❌ 없음 | ✅ true (DIP_TOL=8.0mm · REACH_MARGIN=15.0mm) |
NEIGHBOR_TOP_VALIDATE / INWARD_POST_SEARCH |
true / true | true / true |
applyCross 파라미터 |
✅ 있음 (default true) | ❌ 제거 (항상 적용) |
topRecoverWallRatio 파라미터 |
❌ 없음 | ✅ 있음 (streaming relaxed pass 용) |
2-2. 로직 차이
-
MethodDParams는 모든 필드 100% 동일 (임계값 · 필터 파라미터 전부). -
MethodDRunner는 105 lines 차이 (443L vs 548L). 실질 로직 차이 3가지:applyVdipPostRecovery(cloud-mvp only · stage ①.5)- V-dip 심부후벽 복구. 중간 center 채널 후벽 z 가 양 이웃 모두보다
VDIP_DIP_TOL=8.0mm얕고, 두 이웃이 서로 8.0mm 이내 일치 (=under-detected 심부후벽) 이면 consensus 깊이로 재탐색. - Gate (wall/lumen ratio + post_raw_ratio) 통과한 심부 peak 만 채택.
- 원본: piezophantomtest c800f5d (2026-08-06).
- V-dip 심부후벽 복구. 중간 center 채널 후벽 z 가 양 이웃 모두보다
applyCross파라미터- demo-final:
detectMultichannel(..., applyCross: Boolean = true)유지 → 정렬 / BV 경로 분리 가능. - cloud-mvp: 제거됨 → 두 경로 모두 cross-channel 적용된 결과 사용.
- demo-final:
topRecoverWallRatio파라미터 (cloud-mvp only)applyNeighborTopValidateRule A (FN 복원) 재탐색 시minWallLumenRatiogate override.StreamingBladderEstimatorrelaxed pass (게이트 1.16 → 1.13) 용 hook.
-
detection default 가 다른 것 자체가 브랜치 성격의 표현: demo-final = phantom-검증된 V4.1 (안정 · 시연 유효), cloud-mvp = 임상용 METHOD_D (Python for_app_share 1:1).
2-3. 관련 파일
| 파일 | demo-final | cloud-mvp |
|---|---|---|
| MethodDRunner | walldetect/MethodDRunner.kt (443 L) |
piezo/walldetect/MethodDRunner.kt (548 L) |
| MethodDParams | walldetect/algo/methodd/MethodDParams.kt |
piezo/walldetect/algo/methodd/MethodDParams.kt (동일) |
| VDIP 신설 함수 | — | MethodDRunner.kt:214-306 (vdipDeepCandidate + applyVdipPostRecovery) |
3. BV Estimation (부피 계산)
3-1. 기본 설정값
| 항목 | demo-final | feature/cloud-mvp |
|---|---|---|
bvMethod (default) |
METHOD_D_PHANTOM (2026-08-11 신설 · 구 공식) | METHOD_D (Halir + adaptive) |
BvMethod enum |
{FRUSTUM, V41, METHOD_D, METHOD_D_PHANTOM} (4개) |
{FRUSTUM, V41, METHOD_D} (3개) |
distancePerSample (dps) |
1.968 mm/sample (2026-08-11 phantom rollback) | 1.936 mm/sample (Python config_6ch.py:90) |
lrRatioOverride (default) |
1.0 | 1.0 |
lrRatioMin / lrRatioMax clip |
❌ 없음 | ✅ 0.5 / 1.5 (해부학적 clip) |
estimateBv(walls, adaptive=true) 구현 |
✅ 있음 (동일) | ✅ 있음 (동일) |
estimateBvPhantomSphere(walls) |
✅ 있음 (신규 · 구 공식) | ❌ 없음 |
Halíř–Flusser (EllipseFitSpecific) |
✅ 있음 (로직 완전 동일) | ✅ 있음 |
adaptive_large_bladder_relax (low_wide_endpoint) |
✅ 있음 (동일) | ✅ 있음 |
b_si_floor_ratio / _edge_min |
✅ 있음 (동일) | ✅ 있음 |
| Subsample refined (Double ant/post) | ✅ 있음 | ✅ 있음 |
StreamingBladderEstimator |
❌ 없음 | ✅ 있음 (age-based strict/relaxed 병합) |
useStreamingBv dev toggle |
❌ 없음 | ✅ 있음 |
3-2. 로직 차이
estimateBv(walls)본체는 두 브랜치 100% 동일evalOnce()→applyLumenInsetOne(0.15)→estimateBladderVolume6ch→ Halíř–Flusser 타원 fit → 결과 검사 (capFrac < 0.20 && edge ≥ 0.48 && (edge ≥ 0.70 && hRel < 0.42)) → low_wide_endpoint 이면urineInsetFrac=0.05 + bSiFloorRatio=0.85로 재계산.- Python
runners.estimate_bv1:1.
estimateBvPhantomSphere(walls)— demo-final 전용 (2026-08-11 신설)- 위치:
PiezoBVEstimator.kt:600 - 로직: center CH0~3 각 채널의
(post − ant) × dps를 지름으로 보고 평균 →V = 4/3·π·r³(r = D/2). Halíř · adaptive · b_si_floor · lr_ratio 전부 우회. - 검증: D = 42 samples × 1.968 dps = 82.7 mm → r = 41.35 mm → V ≈ 296 ml (300 ml phantom 시연에서 재현 성공).
- 인체 방광 (비대칭 타원) 에는 부적합 · phantom 전용.
- 위치:
distancePerSample편차 (1.968 vs 1.936)- 두 브랜치 계산 결과가 다른 결정적 원인 중 하나.
- dps 는
WdConfig.DPS_DEFAULT도 함께 동기화 (V41 · Geometry · AnatomicalGate 모두 이 상수 사용) → 한 값만 바꾸면 두 경로 (6ch estimate vs V41/gate) dps 불일치 방지.
StreamingBladderEstimator(cloud-mvp only)- 파일:
piezo/StreamingBladderEstimator.kt(신규). - 각 채널별
age(마지막 strict 검출 이후 trace 수) 유지 → strict 실패 채널을 최근 K trace 내 strict 이력 있으면 relaxed (게이트 1.13) 로만 복구 · 이력 없으면 완화 안 함 → 게이트 경계 flicker 억제. MethodDRunner.detectMultichannel(topRecoverWallRatio=…)로 게이트 override.PiezoMonitoringView:406에서 dev toggle 로 라우팅. 배뇨/탈착 시reset().
- 파일:
lrRatioMin/Maxclip [0.5, 1.5]- cloud-mvp 만 도입 (
PiezoHW:164-165·computeLrRatio반환값 clip). - demo-final 은 computed lr 그대로 (실제로는
lrRatioOverride=1.0강제 사용해서 clip 무의미).
- cloud-mvp 만 도입 (
3-3. 관련 파일
| 파일 | demo-final | cloud-mvp |
|---|---|---|
| PiezoBVEstimator | managers/PiezoBVEstimator.kt (1174 L) |
piezo/PiezoBVEstimator.kt (1124 L) |
| EllipseFitSpecific | managers/EllipseFitSpecific.kt |
piezo/EllipseFitSpecific.kt (package 다름) |
| GreenZoneConstants | managers/GreenZoneConstants.kt:38, :59 |
common/util/GreenZoneConstants.kt:38, :69 |
| BV 신설 함수 (sphere) | PiezoBVEstimator.kt:600 estimateBvPhantomSphere |
— |
| Streaming BV | — | piezo/StreamingBladderEstimator.kt (신규) |
| BV 분기점 | ui/views/monitoring/PiezoMonitoringView.kt:487-491 (METHOD_D_PHANTOM → estimateBvPhantomSphere) |
piezo/ui/PiezoMonitoringView.kt:406-418 (useStreamingBv → streamingBv.update(signals)) |
3-4. 심층 비교 · METHOD_D vs METHOD_D_PHANTOM
두 method 는 같은 detection walls 를 입력받지만 · 부피 계산 철학이 근본 다름.
3-4-1. 부피 공식
METHOD_D — 다각형 근사 + 타원 cap
BV = V_frustum + V_topCap + V_bottomCap
┌───────┬─────────┬──────────┐
│ 6채널 │ Halir │ Sphere │
│ frustum│ ellipse │ (fixed) │
│ 적분 │ fit │ │
└───────┴─────────┴──────────┘
방광을 6채널 벽 사이 다각형(frustum) + 상하 cap 으로 근사. adaptive_large_bladder_relax 로 큰 방광 처리 시 constraint 완화. Python runners.estimate_bv 1:1.
METHOD_D_PHANTOM — 순수 구
BV = (4/3) · π · r³
r = mean(D_center 4채널) / 2
D_ch = (post_ch - ant_ch) × dps
방광을 완전 대칭 구로 가정 · center CH0~CH3 지름 평균으로 반지름 산출. Cap · lr_ratio · Halir 다 우회.
3-4-2. 파이프라인 stage 비교
| Stage | METHOD_D | METHOD_D_PHANTOM |
|---|---|---|
| 1. walls 입력 | List<WallWithSpan?> (subsample refined) |
List<WallWithSpan?> (동일) |
| 2. lumen inset | applyLumenInsetOne(0.15) — 벽 15% 안쪽 이동 |
❌ 없음 (raw walls) |
| 3. center wall repair | gap 보간 · gap≥2 top drop | ❌ 없음 |
| 4. lr_ratio | lrRatioOverride ?: computeLrRatio() |
❌ 없음 (구 = 1:1) |
| 5. Frustum 계산 | 6채널 x-z 타원 · SI-sorted · frustum 적분 | ❌ 없음 |
| 6. Top cap | Halir-Flusser 타원 fit + ellipse_cap_height |
❌ 없음 |
| 7. Bottom cap | Sphere (bottomKind = "sphere") |
❌ 없음 (전체가 구) |
| 8. adaptive relax | low_wide_endpoint 검사 → 재계산 | ❌ 없음 |
| 9. b_si_floor | 하한 b_si ≥ 0.85 × edge_min |
❌ 없음 |
| 10. Post outlier | post_outlier_filter=false (default) |
❌ 없음 |
| 11. 최종 | Σ frustum + top cap + bottom cap | 4/3 π (D̄/2)³ |
3-4-3. 결과 특성
| 특성 | METHOD_D | METHOD_D_PHANTOM |
|---|---|---|
| 정확도 (실제 방광) | 높음 (Python parity · Δ~0mL 검증) | 낮음 (구 가정) |
| 정확도 (phantom 300mL) | 250mL (adaptive 로도 -17%) | 296mL (-1.3%) ✓ |
| CH3 flick 민감도 | 낮음 (frustum 은 채널 몇 개 빠져도 ok) | 높음 (평균에 직접 반영) |
| 비대칭 방광 | 잘 대응 (frustum + LR ratio) | 왜곡 심함 (구 가정) |
| 작은 방광 (<100mL) | 정확 | 정확 (구 가정 잘 맞음) |
| 큰 방광 (>400mL) | 정확 | 저추정 (구 부피 성장률 < 실제) |
| 계산 비용 | 높음 (타원 fit · SVD · relax 재계산) | 매우 낮음 (곱셈 몇 번) |
| 결과 재현성 | 파이썬 소수점까지 일치 | trivial · dps · walls 만 의존 |
3-4-4. 예시 계산 (phantom 300mL · 지름 83mm)
Method D walls 예시:
CH0: ant=15, post=57 → span 42
CH1: ant=13, post=59 → span 46
CH2: ant=14, post=57 → span 43
CH3: ant=16, post=55 → span 39
CH4/5: ant=18, post=44 (LR wings)
METHOD_D_PHANTOM 계산:
D̄ = mean(42, 46, 43, 39) × 1.968 = 42.5 × 1.968 = 83.64 mm
r = 41.82 mm
V = 4/3 · π · 41.82³ = 306,326 mm³ ≈ 306 mL ✓
METHOD_D 계산 (근사):
V_frustum = Σ frustum(z_i, z_{i+1}, S_i, S_{i+1}) (4채널 → 3 slice)
≈ 165 mL (본체)
V_top (Halir + adaptive) ≈ 55 mL (배꼽쪽 cap · b_si_floor 로 확장)
V_bottom (sphere) ≈ 30 mL (치골쪽 cap)
V_total ≈ 250 mL (-17% 저추정 · adaptive 로도 큰 phantom 처리 어려움)
3-4-5. 언제 어느 것 써야 하나
| 시나리오 | 권장 method | 근거 |
|---|---|---|
| Phantom 시연 (구·타원 방광 모형) | METHOD_D_PHANTOM | 구 가정 정확 · 300mL 재현 |
| 인체 실측 (임상) | METHOD_D | 비대칭 방광 · Halir + adaptive 필수 |
| Alignment 위치 판정 | detection method 만 사용 (BV 무관) | walls 만 필요 |
| BV parity 테스트 (Python 대조) | METHOD_D | bit-perfect 일치 |
| CH3 검출 불안정 자세 | METHOD_D | frustum 이 채널 결손 관용 |
| 빠른 UI 반응 (계산 지연 최소) | METHOD_D_PHANTOM | O(1) 계산 |
3-4-6. 코드 진입점 요약 (demo-final)
// PiezoMonitoringView.kt:487-491
val bvResult = if (bvMethod == BvMethod.METHOD_D_PHANTOM) {
estimateBvPhantomSphere(walls) // PiezoBVEstimator.kt:600 · ~30 L
} else {
estimateBv(walls) // PiezoBVEstimator.kt:595 · ~600 L
}
estimateBvPhantomSphere— 30 lines · 순수 계산 · state 없음estimateBv— 600 lines + EllipseFitSpecific.kt (150 L) + adaptive wrapper
3-4-7. 향후 개선 여지 (선택)
- 하이브리드 method: walls 대칭성 분산 계산 → 낮으면 PHANTOM · 높으면 METHOD_D 자동 선택
- METHOD_D_PHANTOM 확장: 반지름 산출 시 CH1/CH2 만 사용 (CH0/CH3 는 flick 심함) 옵션
- METHOD_D_PHANTOM_ELLIPSOID: 구 대신 반지름 2개 (SI + AP) 로 회전 타원체 · 근사 개선
4. 브랜치 전용 파일/기능 요약
demo-final only
managers/AlignmentAdvisorV3.kt(CenterAligner · Rule A + bvcv batch scan-select)BvMethod.METHOD_D_PHANTOMenum valuefun estimateBvPhantomSphere(walls)(구 공식 · phantom 시연 전용)MethodDRunner.detectMultichannel(..., applyCross = false)API 보존
cloud-mvp only
piezo/StreamingBladderEstimator.kt(age-based strict/relaxed 병합)GreenZoneConstants.useStreamingBvdev toggle + custom-setter audit loggingPiezoHW.lrRatioMin / lrRatioMaxclip +coerceInincomputeLrRatioMethodDRunner.applyVdipPostRecovery+vdipDeepCandidate(V-dip 심부후벽 복구)MethodDRunner.detectMultichannel(..., topRecoverWallRatio: Double?)APIPiezoHW.autoDetectPresetPhiRedactor 마스킹 (cybersecurity DC-02)- Custom-setter audit-logger wrappers on
_detectionMethod/_bvMethod/_lrRatioOverride/_placementGuideMode/_alignmentAlgo/_useStreamingBv(cybersecurity UC-04) piezo/ui/PiezoPersonalizationView.ktstreaming BV dev toggle UI
공통 (구현 동일 · 위치만 다름)
- Halíř–Flusser
EllipseFitSpecific·estimateBv(walls)본체 ·adaptive_large_bladder_relax·b_si_floor· subsample refined ·MethodDParams전 필드 · V1/V2 alignment state machine 임계
5. 브랜치 default 조합 종합
| 축 | demo-final | cloud-mvp |
|---|---|---|
| 정렬 | V1 · Gradient (SIMPLE) | V1 · Gradient (SIMPLE) |
| 검출 | METHOD_C (V4.1 SphereFit walls) | METHOD_D (Python for_app_share) |
| BV | METHOD_D_PHANTOM (구 공식 · V=4/3πr³) | METHOD_D (Halir + adaptive) |
| dps | 1.968 mm/sample | 1.936 mm/sample |
| Streaming BV | — | dev toggle |
| Audit logging | — | 활성 |
6. 브랜치별 의사결정 기록 (핵심 커밋)
| 커밋 | 날짜 | 브랜치 | 변경 |
|---|---|---|---|
bcbc7b4 |
2026-07-02 | demo-final | 패키지 통일 (com.example → com.medithings) · BV 로직 안정판 |
d732150 |
2026-07-20 | demo-final | Halíř-Flusser ellipse fit 이식 (BV Δ 43→13mL) |
3152783 |
2026-07-20 | demo-final | estimateBv wrapper + adaptive_large_bladder_relax + b_si_floor |
430d9ad |
2026-07-30 | demo-final | feature-folder refactor (147 파일 이동) |
1c3748b |
2026-08-07 | demo-final | vdip_post_recovery 이식 (MethodDRunner) |
8cab1d0 |
2026-08-10 | demo-final | rollback 후 재조정 · detection=C + bv=METHOD_D |
d054b3b |
2026-08-11 | demo-final | dps 1.936 → 1.968 (phantom 스케일 재현) |
d1d17f8 |
2026-08-11 | demo-final | METHOD_D_PHANTOM (구 공식) 신설 · demo default |
6dfe5e6 |
2026-08-11 | demo-final | BleManager.disconnect() 즉시 UI 반영 fix |
7. 향후 유지 원칙
- cloud-mvp 의 알고리즘 개선을 demo-final 로 무분별 이식하지 않기
- Halir · adaptive 등은 인체 정확도 개선이지만 phantom 에서는 오히려 값 왜곡.
- demo-final 은 phantom 시연 재현성 최우선
- 알고리즘 변경 시 300 ml phantom 결과가 296 ml 근처를 유지하는지 확인.
- 동시 이식이 필요한 fix (예: BLE 안정성 · UI 버그) 는 두 브랜치 각각 별도 커밋
- cherry-pick 대신 각 브랜치에서 명시적으로 적용 후 커밋 메시지에 "demo-final 이식" / "cloud-mvp 이식" 명시.