Files
VesiscanClinicalAndroid/docs/ALGORITHM_COMPARISON.md
T
dw.jang a1042b808c docs: README 전체 업데이트 (v5, 2026-05-11 기준)
- Pipeline Summary: GREEN 상수 (CV preset별, LR 0.20, 7s hold, 3fail exit),
  힌트 텍스트 표 추가, Spot 5회/Auto 10윈도우, 태블릿 fontScale,
  향후 과제 갱신 (IMU 포팅 예정)
- USER_GUIDE: 전체 재작성 — Start flow, 힌트 화살표(↑↓←→),
  페어링 다이얼로그, Void/Catheterization, Single Scan/Auto Scan,
  Checking/Final check/In position! 텍스트 반영
- ALGORITHM_COMPARISON: Placement 섹션 현행화 — Start 버튼 필수,
  phase protection, 화살표 항상 갱신, GREEN 7s+3fail

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 17:43:39 +09:00

7.8 KiB
Raw Blame History

Method A / B / C Algorithm Comparison & Placement Test

Wall Detection Comparison

Pipeline

Method A:  raw → SG → composite(sg+|d1|+|d2|) → plateau score → plateau spans
           → prominence wall (ANT=nearest, POST=edge_score)
           → cross-validation → BV(frustum+ellipse cap, 1ch=sphere)
           ※ 채널별 min_wall_contrast_ratio × cos(θ) 보정

Method B:  raw → SG → TGC(multichannel log-fit) → initial detect(Otsu×0.9×cos(θ))
           → BR candidate(per-ch, post-only, prominence≥50)
           → consensus(median, outlier_tol=4, min_votes=3)
           → is_reflection_only(shared+cluster, amp_ratio=1.5)
           → resolve(suspicious post: post>ant×1.5)
           → suppress(valley early-stop 50.0) → rerun detect
           → choose(rerun vs initial) → BV(frustum+ellipse cap, 1ch=sphere)

Method C:  raw → MedianFilter(win=7) → SG+wavelet
           → ImpulseReject(2-pass Hampel, lumen clean)
           → OS-CFAR(per-sample) → lumen mask
           → prominence wall + subsample refine
           → StaLta impulse purity check
           → anatomical gate (antDepth≥10mm, chord 5-110mm)
           → BModeScore V41 (wamp=0.45, stalta=0.20, far=0.15, dark=0.10)
           → ChordConsensus (Tukey MAD + Fischler-Bolles pair test)
           → BV dispatch(frustum/chord/sphere, trusted channels only)

Stage-by-Stage

Stage A (Plateau) B (Otsu+TGC+BR) C (V4.1 CFAR)
Pre-filter none none MedianFilter(win=7) + ImpulseReject(Hampel)
Denoising SG(5,2) SG(5,2) → log-TGC SG(5,2) + db4 wavelet 3-level
Threshold sliding plateau score Q=0.5 Otsu×0.9×cos(θ) OS-CFAR (win=15, rank=40%, scale=1.05)
Low-echo mask platScore < thr sg <= otsu (full) sg <= median(CFAR)
Span merge gap≤5, simple gap<3: unconditional, ≥3: wall check mergeGapMax=5, gapPeakMargin=50
Span refine none refineRightEdge none
Wall select ANT=nearest, POST=edge_score, decay=0.15 ANT=nearest, POST=edge_score, decay=0.12 prominence + subsample, MAX_CANDIDATES_POST=64
Valley logic descending-first, rise=5 descending-first, rise=50 rise=50
Extra conditions contrast×cos(θ)≥1.2, prom≥3 peakMin=max(low+30,thr), sg[ant/post]≥thr, postProm≥30 Kremkau ant fallback, antMinIdx=7
Score sg[ant]+sg[post] asymmetric ant_reliability BModeScore V41 (wamp+stalta+far+dark+grad)
FP filter cross-validation score<3000 AnatomicalGate + ChordConsensus
Impulse check none none StaLta (STA/LTA ratio)
Channel filter none none ChordConsensus (MAD outlier + pair test)
Back reflection none consensus multichannel BR none
Fallback none ant=max(0,s-(post-e)) B-mode tier

BV Estimation

A B C
Model shared with B frustum + ellipse cap Multi-dispatch (trusted ch only)
1-channel sphere V=(4/3)πR³ same Verathon chord
Channel filter none none ChordConsensus (drop inconsistent)
Cap height 8-point ellipse LSQ same fixed: bottom=hemisphere, top=cone
LR ratio shared with B 2-lateral ellipse solve + shrinkage 1/avg_ratio, clamp[1.0,1.6]
Confidence none none 0.95(4C+2L) ~ 0.20(1ch)
DPS PiezoHW (1.968) same 1.9309

Angle Correction (PR#20)

Both Method A and B apply per-channel beam angle correction:

  • Method B: otsu_ratio = OTSU_RATIO × cos(θ) per channel
  • Method A: min_wall_contrast_ratio = 1.2 × cos(θ) per channel
  • Larger angles → lower threshold → better detection of weak posterior walls

Back Reflection System (Method B)

Per-channel:
  1. initial detect → (ant, post)
  2. build_candidate: strongest peak AFTER post (prominence≥50)
     - global BR ≈ post (±3) → None (no separate BR)
     - else → strongest significant peak after post

Multichannel:
  3. consensus: median, outlier_tol=4, min_votes=3
  4. is_reflection_only: post > ant × 1.5 (amp_ratio)
     - Condition A: shared_idx based
     - Condition B: post cluster based

Per-channel resolve:
  5. is_suspicious_post: post > ant × 1.5 OR post ≈ shared (±2)
  6. select_idx → resolve → suppress(valley early-stop 50.0)
  7. rerun detect → choose(rerun vs initial, min_gap=8)

Method C V4.1 New Features (2026-05-04)

Pre-processing:
  MedianFilter(win=7) → remove speckle bumps
  ImpulseReject(2-pass Hampel) → clean 1-3 sample impulses in lumen

Detection:
  DetectLumenFirst V41_PARAMS:
    mergeGapMax=5, gapPeakMargin=50
    maxCandidatesPost=64, medianWin=7, antMinIdx=7
    Kremkau half-amplitude ant fallback

Quality:
  StaLta: STA/LTA ratio → wall edge vs reverberation discrimination
  BModeScore V41_WEIGHTS:
    wamp=0.45 (wall-peak amplitude)
    stalta=0.20 (impulse purity)
    far=0.15, dark=0.10, ant=0.05, post=0.05

Channel Filtering:
  ChordConsensus: Tukey MAD outlier + Fischler-Bolles pair test
  → drop geometrically inconsistent channels before BV

Anatomical Gate:
  antDepthMin: 22 → 10mm (Neyman-Pearson loose prior)
  strict threshold removed → delegated to B-mode score

Python vs Kotlin Sync Status (2026-05-04)

Method A/B: 18 functions compared — CRITICAL 0, MINOR 5

All config constants match: LOW_ECHO_AMP=1250, OTSU_RATIO=0.9, MIN_SCORE=3000, EDGE_DIST_DECAY=0.12, VALLEY_STOP_RISE=50, etc.

Method C: Direct copy from CharlesKWONsLaw (45 files, package renamed)


Placement Test Algorithm

Overview (2026-05-11 updated)

Screen entry → "Place VesiScan above the pubic bone, then press Start"
    ↓ Start Alignment 버튼 탭 (waitingForStart=false)
전체 상태 리셋
    ↓
[1-second interval loop]
    ↓
mpa (first time, 500ms delay) → maa → 6ch reb
    ↓
Detachment check (6ch received, THR=30) → LED 4 if detached
    ↓
Wall detection (Method A/B/C) → urineLen ≥ 12
    ↓
computePlacementGuide() → 3-stage guide (VERTICAL → LATERAL → GREEN)
    ↓
Debounce (2 consecutive + 1.5s/3s hold) + Phase protection (3 scans)
    ↓
Arrow direction: always updated (directionIcon synced with guideResult)
    ↓
LED: stage change only (4=detach, 5=searching, 6=complete)
    ↓
GREEN: 7s hold + 3 consecutive fail to exit
    ↓
Screen exit → LED OFF

Guide Modes

Gradient (default): weighted center gradient → LATERAL → GREEN Boundary: up → detect boundary (max drops) → come back → LATERAL SWEEP (CKLaw): navel → slide down → sagittalArgMaxCh → LATERAL → GREEN

3-Stage Guide

Stage 1 VERTICAL: detect count + gradient + "Slide up/down ↑↓" Stage 2 LATERAL: |len4-len5| ≤ 10 → GREEN, else "Slide left/right ←→" Stage 3 GREEN: CV ≤ threshold AND |LR dev| ≤ 0.20 → "In position!" (7s hold)

Measurement Modes

  • Auto Scan: min 800ms gap, 10-sample trimmed mean (max/min removed, 8 avg)
  • Single Scan (Spot): 5회 측정 → trimmed mean (max 8 attempts, min 3)
  • valid ≥ 4 channels only
  • Analysis on coroutine (Dispatchers.Default)
  • Auto fail: 2ch+ missing 5 consecutive → realign dialog
  • Detachment → LED 4 (once), recovery → LED 0 (once)
  • Foreground Service: BLE connection maintained during screen off
  • BLE 로그: 실시간 파일 기록 (Downloads/VesiScan_BLE_*.log, 화면 잠금에도 보존)
  • CSV: 세션별 파일 (YYYY-MM-DD_HHmmss.csv)

Placement GREEN 기준

CV threshold = DEFAULT_CV_THR + (scanCount / 5) × 0.03
  DEFAULT_CV_THR: V0=0.07, V1/V2=0.10, 기타=0.08
LR deviation ≤ 0.20
GREEN hold: 7초 (greenLockedAtMs 기준)
GREEN exit: 3회 연속 실패 (greenFailCount)

Test Results (2026-05-04, 500ml phantom, Method C V4.1)

총 측정: 223회
평균: 492.4ml (오차 -1.5%)
±15% 이내: 223/223 (100%)
범위: 441~537ml