Files
VesiscanClinicalAndroid/docs/ALGO_MODE.md
T
dw.jang ccb44f0ea6 feat(algo): 레퍼런스 경로를 dev 스위치로 · 기본은 현행 유지
신 저장소에 맞춘 레퍼런스(piezo-phantom-test/vesiscan_test) 단계들을 옮기되,
demo-final 의 기본 동작은 건드리지 않았다. 2026-09-03 임상이 현재 HEAD 위에서
돌았으므로 그 값을 움직이는 변경을 기본 경로에 넣을 수 없다.

`AlgoMode.reference` (계측 화면 dev 패널 → "알고리즘", 기본 꺼짐) 를 켜면:

  · CCC 3단계 → 7단계
    envelope(①.9) · ant_tiebreak(①) · Λ-dip(①.7) · edge_post(①.8) ·
    lumen_return(①.95) · NTV(② Rule A 만) · fp_gate(③). inward_post 는 빠진다.
  · 파이프라인 4개 추가
    scale_recovery(2) · missing_outside 산출(4) · channel_confidence→untrusted(5b) ·
    CONF_DEMOTE(6). missing_outside 는 CCC **앞**에서 계산한다 — CCC 가 복구한
    채널은 방광 밖이 아니다.
  · BV
    si_floor/missing_outside 로 극 cap clamp 의 방향(상한/하한)을 관측 증거로 가름,
    bottom cap 제한 신설, 중앙 채널 보간 은행가 반올림,
    adaptive_large_bladder_relax 제거(b_si 를 0.85·a_ap 로 바닥 처리해 cap 축 비를
    정확히 0.85 로 만들어 바로 다음 cap 축 폴백을 경계에서 막고 있었다).

검출 파라미터(d_max 15 · inward_walk_win 4) · ANT_FLOOR_REF · cap 축 폴백 ·
ELLIPSE_CAP_R_MAX · TOP_CAP_EDGE_K · cap_d_adaptive · 프리셋 유도 dps/delay 는
ebddc52/20a48ad 에서 이미 기본 경로에 들어가 있어 양쪽 공통이다. 되돌리지 않았다.

검증
  · 기존 경로 무변경 — LegacyEquivalenceDumpTest 가 44 cycle × CCC on/off 를
    검출→BV 로 돌려 덤프하고, HEAD 코드 덤프와 대조. 88 행 전부 일치.
    (이 테스트는 일부러 AlgoMode 를 참조하지 않는다 — HEAD 에 없는 타입이라
     참조하면 대조 자체가 불가능해진다.)
  · 토글 배선 — AlgoModeSwitchTest: 켜면 44 중 40 cycle 이 갈리고
    missing_outside 가 산출되며, 끄면 기존 값이 정확히 재현된다.
  · 전체 30개 통과.

함께 고친 것

저장소가 갈리며 없어진 픽스처 경로(medilightv2android/data123)를
vesiscan-design-archive/data123 로 옮겼다. 5개 테스트가 FileNotFoundException 으로
조용히 실패하고 있었다.

그래서 ebddc52 이후 한 번도 안 돌던 CenterAlignerValidationTest 가 다시 돌았고
hit 7→8 · 10→11 로 어긋났다. 현행 레퍼런스 AnchorGuide(ver='v1') 를 같은 세션에
직접 돌려 보니 8 · 11 이었다 — 코틀린이 맞고 기대값이 낡은 것이다(구
v3_python_reference.py 기준, 현재 저장소에 없음). 기대값을 갱신했다.
bv_cv 는 현행 레퍼런스가 더 이상 내지 않아 대조할 수단이 없다. "Python 과
bit-exact" 라던 주석은 근거가 없어졌으므로 걷어내고 회귀 고정으로 성격을 바꿨다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 17:31:58 +09:00

7.3 KiB
Raw Blame History

알고리즘 경로 두 갈래 (AlgoMode)

demo-final 은 팬텀 시연과 임상 계측이 함께 도는 브랜치다. 여기에 신 저장소 (vesiscan_pre_product/vesiscan_android_user)가 레퍼런스 (piezo-phantom-test/vesiscan_test)와 1:1 로 맞춘 검출·BV 단계들을 옮기되, 기본 동작은 그대로 두고 dev 패널에서만 켤 수 있게 했다.

  • 코드: managers/AlgoMode.kt
  • UI: 계측 화면 dev 패널 → 알고리즘 스위치 (기본 꺼짐)
  • 상태는 프로세스 수명 동안만 유지된다. 앱을 다시 켜면 기존 경로로 돌아간다.

"기존" 이 가리키는 것

ebddc52(검출·BV 레퍼런스 정합) 와 20a48ad(실리콘 cap 억제) 가 들어간 현재 demo-final HEAD 다. 2026-09-03 임상이 이 코드 위에서 돌았으므로, 그 값을 움직이는 변경은 기본 경로에 넣지 않는다.

따라서 아래 항목들은 양쪽 공통이며 모드와 무관하다:

항목 값 들어간 커밋
d_max · inward_walk_win 15 · 4 ebddc52
prominence 기준 span 바닥 (ANT_FLOOR_REF) ebddc52
cap 축 붕괴 폴백 CAP_AXIS_RATIO_MAX 0.85 / CAP_AXIS_DMAX_MAX 100 ebddc52
ELLIPSE_CAP_R_MAX · TOP_CAP_EDGE_K 0.92 · 0.6 ebddc52
cap_d_adaptive (실리콘) ("aspect", 80.0, 2.0) 20a48ad
dps · delay 프리셋 유도 (v*=1.936/6.85, r*=1.897/7.651) 기존

스위치를 켜면 바뀌는 것

부분 적용은 없다. 단계들이 서로를 전제하므로 섞으면 어느 쪽도 아닌 값이 나온다.

1. 교차채널 보정 (CCC) — 3단계 → 7단계

기존 레퍼런스
순서 ant_tiebreak → NTV(Rule A+B) → inward_post ①.9 envelope_research → ① ant_tiebreak → ①.7 Λ-dip → ①.8 edge_post_recovery → ①.95 lumen_return_recovery → ② NTV(Rule A 만) → ③ fp_channel_gate
inward_post 있음 없음
NTV Rule B 있음 없음

레퍼런스 쪽 데이터에서 각 단계가 결과를 바꾼 횟수(파이썬 ablation, 1512 케이스): ant_tiebreak 158 · edge_post_recovery 73 · Λ-dip 7 · fp_gate 4 · 나머지 0.

2. 검출 파이프라인 — 4개 단계 추가

단계 하는 일 왜
2 scale_recovery 검출 실패 채널만 SG5 로 재탐색 window 를 전역으로 바꾸면 모든 벽이 함께 밀려 부피가 통째로 이동한다. 실패 채널만 건드려 커버리지(nch)만 얻는다.
4 missing_outside span 조차 없는 채널 = 진짜 방광 밖 CCC 앞에서 계산해야 한다. CCC 가 복구한 채널은 방광 밖이 아니다.
5b channel_confidence 약한 채널을 CCC 복구 단계에서만 숨김 판정(NTV·FP) 전에 되돌린다. 끝까지 숨기면 CCC 가 그 채널을 FP 로 기각할 기회를 잃어 오히려 살아남는다.
6 CONF_DEMOTE CCC 후 여전히 약한 채널을 단면에서 제외 남는 채널이 3 미만이 되면 아무것도 하지 않는다.

신뢰도는 비-TGC min(ant_amp, post_amp) / lumen_min 을 trace 최고값으로 나눈 값이다. TGC 로 재면 원거리 이득이 약한 채널을 정상 채널에 붙여 놓는다(CH1↔CH3 간격 TGC 0.05 vs 비-TGC 0.21).

3. BV

  • si_floor / missing_outside — 극 cap clamp 의 방향이 관측 증거로 갈린다. 미검출 채널에 span 이 없으면 방광이 그 빔에 없다는 뜻이라 상한(종전), span 이 있으면 방광이 거기까지는 온다는 뜻이라 하한이다. 외삽을 관측으로 바꾸는 것이다. SI_FLOOR_WALK 는 span 이 이어지는 가장 바깥 채널까지 걸어간다.

  • bottom cap 제한 신설 — top 만 있고 bottom 은 제약이 전혀 없었다. sitting 에서 CH3(치골)이 소실될 때 bottom cap 이 과외삽된다.

  • 중앙 채널 보간 은행가 반올림 — 레퍼런스는 int(round((int(prev)+int(next))/2.0)) 이다. 보간 채널은 subsample 정밀도를 가질 근거가 없다(이웃 둘의 정수 위치뿐). 실측 f01 tr11: 이 차이로 CH2 단면이 2971.0 vs 2788.2 mm² 로 갈렸다.

  • adaptive_large_bladder_relax 제거 — 레퍼런스에서 빠진 완화다. 남아 있는 동안 b_si 를 0.85·a_ap 로 바닥 처리해 cap 축 비를 정확히 0.85 로 만들었고, 그래서 바로 다음의 cap 축 폴백(< 0.85)이 경계에서 발동하지 않았다. 실측 PHANTOM mean: Python 160.03 mL vs 완화가 살아 있는 코틀린 232.49 mL.

    폴백 재계산에는 missing_outside·si_floor 를 넘기지 않는다 — 레퍼런스 runners.estimate_bv 의 폴백 호출이 그 인자를 빼고 부르기 때문이다. 실측 f04 cyc20: 넘기면 249.95 mL, 안 넘기면 278.43 mL(= 레퍼런스 값).

검증

무엇을 어떻게 결과
기존 경로 무변경 LegacyEquivalenceDumpTest 가 align_cm{0,1}.csv 44 cycle × CCC on/off 를 검출→BV 로 돌려 덤프. HEAD 코드와 작업트리 코드의 덤프를 대조. 88 행 전부 일치
토글 배선 AlgoModeSwitchTest — 켜면 값이 달라지고 missing_outside 가 산출되며, 끄면 기존 값이 정확히 재현되는지 44 cycle 중 40 개가 갈림, 끈 뒤 완전 복귀
정렬 판정 현행 레퍼런스 AnchorGuide(ver='v1') 를 data123 3 세션에 돌려 대조 nch·ch3·hit 전부 일치 (0cm 3/X/2·11, 1cm 4/O/8·11, 3cm 3/O/11·11)
전체 :app:testDemoDebugUnitTest 30개 전부 통과

LegacyEquivalenceDumpTest 는 의도적으로 AlgoMode 를 참조하지 않는다 — HEAD 에는 그 타입이 없어서, 참조하면 대조 자체가 불가능해진다.

이번에 함께 고친 것

  • 테스트 픽스처 경로 — 저장소가 갈리면서 c:/Projects/medilightv2android/data123 가 없어졌다. 5개 테스트가 FileNotFoundException 으로 조용히 실패하고 있었다. c:/Projects/vesiscan-design-archive/data123 로 옮겼다.
  • CenterAlignerValidationTest 기대값 — 위 픽스처가 깨져 있어 ebddc52 이후 이 테스트가 한 번도 돌지 않았다. 다시 돌리니 hit 7→8 · 10→11 로 어긋났는데, 현행 레퍼런스를 같은 세션에 돌려 보니 8 · 11 이었다 — 코틀린이 맞고 기대값이 낡은 것이다(구 v3_python_reference.py 기준, 현재 저장소에 없음). 기대값을 현행 레퍼런스 산출값으로 갱신했다. bv_cv 는 현행 레퍼런스가 더 이상 내지 않는 값이라 대조할 수단이 없다. "Python 과 bit-exact" 라던 종전 주석은 근거가 없어져 걷어내고, 성격을 회귀 고정으로 바꿔 적었다.

아직 안 옮긴 것

항목 왜
post_tie_lock 상태기 — 스트리밍 경로와 함께 설계해야 한다
_sibeam_cap_bounds nch=2 전용
F83 cap 보간 실리콘 + supine 조건

레퍼런스 저장소의 library/analysis/abdomen.py 가 커밋되어 있지 않아 pipeline_dump.py · compare.py 가 그대로는 돌지 않는다. 이번 대조는 스크래치 사본에 호출 시 즉시 실패하는 스텁을 놓아 우회했다(정렬·검출·BV 경로는 이 모듈을 쓰지 않는다).