eb25c01c97
- tools/align_analyze.py 신규: AlignGuide4Stage 상태머신 Python 재현
- `--both` 모드로 NEW (majority + relaxed + soft-hint) vs LEGACY (연속 3 hit)
로직 병행 replay 지원
- Kotlin AlignmentConstants (V_WIN/V_MAJ/L_WIN/L_MAJ/STUCK/SOFT) 값 그대로 매핑
- tools/README.md: align_analyze.py 사용법 + 파라미터 매핑 표 + data123/
4 세션 검증 결과 표 (Phantom / Human 0·1·3 CM) 정리
- tools/labdb_upload.py 신규 추가 (기존 문서만 있고 실체 미커밋 상태였음)
- VesiScan_Android_Pipeline_Summary.md v7 patch (2026-07-13):
align_analyze.py 도구 언급 + CH3 flicker fix 실측 검증 결과 요약
검증 결과 요약 (data123/ human 4 세션):
- Human 0CM (CH3 31.8%): LEGACY 즉시 회귀 → NEW flick 1/4 흡수 유지
- Human 1CM (CH3 77.3%): LEGACY idx15 oscillation → NEW idx13 조기 진입
- Human 3CM (CH3 97.2%): 양쪽 정상 (회귀 없음)
- Phantom: 양쪽 정상 (relaxed mode 오진입 없음)
251 lines
8.7 KiB
Markdown
251 lines
8.7 KiB
Markdown
# tools/
|
|
|
|
VesiScan-Basic 측정 결과 분석 / 검증 툴.
|
|
|
|
## analyze_csv.py — App CSV → Python pipeline 비교
|
|
|
|
App 이 측정 시 저장하는 CSV (`Downloads/VesiScan_ADC/*.csv`) 를 읽어,
|
|
appshare 의 Python pipeline (`for_app_share`) 으로 BV 를 재계산하고
|
|
다음을 비교한다:
|
|
|
|
- App-computed BV vs Python BV (per-scan, aggregate)
|
|
- Method D wall detection rate
|
|
- lr_ratio 분포
|
|
- DPS / lr floor 등 알고리즘 변경 사항의 ablation
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
# 의존성
|
|
pip install numpy pandas matplotlib
|
|
|
|
# appshare repo 경로 (둘 중 하나)
|
|
export APPSHARE_DIR=/path/to/appshare/piezo-phantom-test
|
|
# 또는 명시: --appshare /path/...
|
|
```
|
|
|
|
### Usage
|
|
|
|
```bash
|
|
# 기본 비교 (phantom 150 mL)
|
|
python tools/analyze_csv.py ~/Desktop/measure.csv --true 150
|
|
|
|
# Ablation — 5가지 알고리즘 config 비교 (어느 fix 가 임팩트 큰지)
|
|
python tools/analyze_csv.py measure.csv --true 150 --ablation
|
|
|
|
# 시각화 plot 저장
|
|
python tools/analyze_csv.py measure.csv --true 195 --plot ./out
|
|
|
|
# 특정 cycle 디테일 (per-channel walls + signals)
|
|
python tools/analyze_csv.py measure.csv --true 150 --cycle 42
|
|
```
|
|
|
|
### 출력 예시 (compare mode)
|
|
|
|
```
|
|
=== BV comparison ===
|
|
APP (Kotlin): mean= 148.1± 4.2 trim10%= 148.5 bias= -1.9 ( -1.3%) CV= 2.9% lr mean=1.154
|
|
Python : mean= 92.7±13.4 trim10%= 91.1 bias= -57.3 (-38.2%) CV=14.4% lr mean=0.680
|
|
|
|
=== App ↔ Python diff ===
|
|
mean= +55.03 std=15.83 range=[-45.3, +74.7]
|
|
|diff| < 5 mL: 5/551 (0.9%)
|
|
```
|
|
|
|
### Ablation Config 종류
|
|
|
|
| 코드 | DPS | lr_ratio | 설명 |
|
|
|---|---|---|---|
|
|
| A | 1.936 | =1.0 강제 | OLD Kotlin equivalent (sim) |
|
|
| B | 1.981 | =1.0 강제 | DPS fix 단독 |
|
|
| C | 1.981 | Python 알고리즘 | **현재 b733d4f 결과** |
|
|
| D | 1.981 | Python + floor 1.0 | hybrid |
|
|
| E | 1.936 | Python 알고리즘 | lr 단독 영향 |
|
|
|
|
### CSV 포맷 (AdcCsvLogger 기준)
|
|
|
|
```
|
|
scan_id, timestamp, ..., volume_ml, lr_ratio, ..., channel, s0..s99
|
|
```
|
|
한 scan = 6 rows (CH0..CH5), 100 samples / row.
|
|
|
|
### 활용 예
|
|
|
|
1. **임상 BV 검증**: catheter 직후 측정 → `--true <catheter_vol>` 로 정확도 비교
|
|
2. **알고리즘 변경 검증**: 새 fix 후 같은 CSV 로 `--ablation` 돌려 회귀 여부 확인
|
|
3. **Device-to-device variance**: 두 device 의 같은 phantom 측정 → CV 비교
|
|
4. **lr_ratio 패턴**: human cohort 의 `--plot` 으로 lr 분포 시각화
|
|
|
|
---
|
|
|
|
## labdb_upload.py — 세션 폴더 일괄 업로드
|
|
|
|
앱이 저장한 세션 폴더 (`measurement_<name>.json` 포함) 를 labdb REST API
|
|
로 일괄 업로드. 앱 안의 `LabdbUploader.kt` (buildPayload) 로직을 그대로
|
|
Python 으로 옮긴 것으로, 폰이 없는 상황에서 데스크톱에 백업한 데이터를
|
|
바로 서버로 넣을 때 사용.
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
# 별도 의존성 없음 (표준 라이브러리만 사용)
|
|
export LABDB_API_KEY=xbk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
```
|
|
|
|
apiKey 는 labdb admin 콘솔 또는 backend 담당자로부터 발급.
|
|
|
|
### Usage
|
|
|
|
```bash
|
|
# 기본 — 폴더 안의 모든 세션 업로드
|
|
python tools/labdb_upload.py "C:/Users/장동우/Desktop/newdata"
|
|
|
|
# apiKey 를 명령줄로 전달
|
|
python tools/labdb_upload.py ./sessions --api-key xbk_live_xxx...
|
|
|
|
# dry-run — 전송하지 않고 payload 검증만
|
|
python tools/labdb_upload.py ./sessions --dry-run
|
|
|
|
# 이미 업로드된 폴더도 재전송 (labdb_upload.json 마커 무시)
|
|
python tools/labdb_upload.py ./sessions --force
|
|
|
|
# meta.data_type 없을 때 사용할 fallback (기본 "001")
|
|
python tools/labdb_upload.py ./sessions --data-type 002
|
|
```
|
|
|
|
### 폴더 구조 요구사항
|
|
|
|
각 세션 폴더는 앱이 만든 표준 형식:
|
|
|
|
```
|
|
<folder>/
|
|
meta_<folder>.json
|
|
measurement_<folder>.json ← 이걸 payload 로 변환
|
|
adc.csv
|
|
imu.csv
|
|
ble.log
|
|
labdb_upload.json ← 성공 시 생성 (sessionId, inserted 개수 등)
|
|
labdb_upload_error.json ← 실패 시 생성 (다음 실행 시 참고)
|
|
```
|
|
|
|
`measurement.json` 또는 `measurement_<folder>.json` 이 있으면 대상. 없으면
|
|
"measurement json not found" 로 실패.
|
|
|
|
### 재실행 안전성
|
|
|
|
- 이미 업로드된 폴더 (`labdb_upload.json` 있음) 는 자동 skip
|
|
- `--force` 로 강제 재전송 가능
|
|
- 실패한 폴더는 다음 실행 시 자동 재시도
|
|
|
|
### 출력 예시
|
|
|
|
```
|
|
[OK ] HUMAN-kai_VBT26040302_SITTING_ALIGN_0CM_2026-07-08_110903: uploaded: 130/130 (dup=0)
|
|
[OK ] HUMAN-kai_VBT26040302_SITTING_ALIGN_1CM_2026-07-08_111049: uploaded: 3/3 (dup=0)
|
|
[FAIL] HUMAN-kai_VBT26050202_SITTING_ALIGN_2CM_2026-07-08_161928: upload fail: HTTP 401
|
|
[OK ] ... (skip already uploaded)
|
|
|
|
summary: 19 uploaded, 0 skipped, 1 failed (total 20)
|
|
```
|
|
|
|
### buildPayload 로직 (LabdbUploader.kt 와 동일)
|
|
|
|
- **testId**: 폴더명 sanitize (`^[A-Za-z0-9_-]{1,40}$`). 40자 초과 시 앞
|
|
31자 + `_` + 8자 SHA-1 prefix 로 축약 (uniqueness 유지).
|
|
- **dataType**: `meta.data_type` 우선, 없으면 `--data-type` fallback.
|
|
- **records[]**: `measurement.json` 의 각 cycle → labdb record 로 변환:
|
|
- `sensor.imu`: 마지막 (가장 최근) IMU sample
|
|
- `sensor.imu_samples`: 전체 시계열
|
|
- `channels[0..5]`: peak / peakIdx / data 배열
|
|
- alignment session 은 align_phase / align_score / align_hint 등 부가 필드.
|
|
- **params**: posture / step / is_alignment / examiner / firmware / hardware / subject 등
|
|
meta 요약.
|
|
|
|
### 활용 예
|
|
|
|
1. **개발용 백업 재전송**: 폰이 없는 상황에서 데스크톱에 백업한 세션 폴더를
|
|
labdb 로 일괄 업로드.
|
|
2. **자동 재시도 실패 복구**: 앱의 `LabdbAutoRetry` 도 실패한 케이스 (예:
|
|
apiKey 만료) → apiKey 갱신 후 스크립트로 일괄 재전송.
|
|
3. **다른 폰 → labdb 이관**: A 폰에서 측정 → `Downloads/VesiScan_Sessions/`
|
|
폴더 → PC 로 복사 → 스크립트로 서버 반영.
|
|
|
|
---
|
|
|
|
## align_analyze.py — Alignment session Python replay
|
|
|
|
`AlignGuide4Stage` (Kotlin) 의 상태머신 로직을 Python 으로 그대로 재현하여
|
|
alignment 세션 JSON 을 오프라인 replay 한다. Method D 파이프라인
|
|
(`vesiscan_test.library`) 을 통해 실제 앱과 동일한 검출 결과 위에서
|
|
phase 전이 · action 시퀀스 · CH3 flicker 통계를 산출한다.
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
# piezophantomtest repo 경로 필요 (align_analyze.py 상단 sys.path.insert 참조)
|
|
# 기본값: c:/Projects/piezophantomtest/piezo-phantom-test
|
|
pip install numpy pandas
|
|
```
|
|
|
|
### Usage
|
|
|
|
```bash
|
|
# 세션 요약 (detection matrix + per-channel 통계)
|
|
python tools/align_analyze.py session.json
|
|
|
|
# NEW 로직 (majority + relaxed + soft-hint) 로 replay
|
|
python tools/align_analyze.py session.json --replay
|
|
|
|
# LEGACY 로직 (연속 3회 hit) 로 replay — 비교용
|
|
python tools/align_analyze.py session.json --replay --legacy
|
|
|
|
# NEW + LEGACY 동시 실행 (fix 전후 비교)
|
|
python tools/align_analyze.py session.json --both
|
|
|
|
# accum 조절 (기본 10 — BLE commit 주기 매칭)
|
|
python tools/align_analyze.py session.json --both --accum 10 --no-summary
|
|
|
|
# 여러 세션 일괄 (files 생략 시 c:/Projects/medilightv2android/data123/*.json 전체)
|
|
python tools/align_analyze.py --both --no-summary
|
|
```
|
|
|
|
### 재현 파라미터 (Kotlin AlignmentConstants 매핑)
|
|
|
|
| Python | Kotlin | 의미 |
|
|
|---|---|---|
|
|
| `V_WIN=6` | `VERTICAL_HIT_WINDOW` | VERTICAL_CLIMB 진입 sliding window |
|
|
| `V_MAJ=3` | `VERTICAL_HIT_MAJORITY` | window 내 최소 hit |
|
|
| `L_WIN=4` | `STABILIZE_LOST_WINDOW` | CH3 lost 판정 window |
|
|
| `L_MAJ=3` | `STABILIZE_LOST_MAJORITY` | window 내 최소 lost |
|
|
| `STUCK_TH=20` | `VERTICAL_STUCK_THRESHOLD` | relaxed mode 진입 threshold |
|
|
| `SOFT_AFTER=12` | `VERTICAL_SOFT_HINT_AFTER` | soft hint 문구 전환 시점 |
|
|
|
|
### 활용 예 (CH3 flicker fix 검증 2026-07-10)
|
|
|
|
`data123/` 인체 임상 4 세션에 `--both` 로 NEW / LEGACY 병행 replay 실행.
|
|
결과 요약:
|
|
|
|
| 세션 | Records | LEGACY | NEW |
|
|
|---|---|---|---|
|
|
| Phantom 0CM | 62 | LR_BALANCE 도달 정상 | 동일 궤적, 회귀 없음 (false-positive 없음) |
|
|
| Human 0CM (CH3 31.8%) | 22 | CH3_STAB 진입 즉시 회귀 | flick 1/4 흡수하며 유지 |
|
|
| Human 1CM (CH3 77.3%) | 22 | idx 15 진입 + 2회 oscillation | idx 13 조기 진입 + 2 flick 흡수 |
|
|
| Human 3CM (CH3 97.2%) | 36 | CENTER_OPTIMIZE 도달 정상 | 동일 궤적, 회귀 없음 |
|
|
|
|
결론: sliding-window majority + soft-hint 조합만으로 human 0CM/1CM 회귀·조기
|
|
진입 개선 확인. relaxed mode 는 팬텀·정상 케이스에서 트리거되지 않아 false
|
|
positive 위험 없음.
|
|
|
|
### 출력 (예)
|
|
|
|
```
|
|
--- RollingAligner replay NEW ---
|
|
window = 10
|
|
idx phase relax chN CH3 ch0..3 chord CV action
|
|
10 VERTICAL . 6 Y 67.3 104.5 103.8 103.5 0.17 MOVE_UP (hit 1/6)
|
|
11 VERTICAL . 6 Y 65.3 104.5 103.8 103.5 0.18 MOVE_UP (hit 2/6)
|
|
12 CH3_STAB . 6 Y 67.3 104.5 103.8 103.5 0.17 STOP → CH3_STAB (hit 3/6)
|
|
...
|
|
```
|
|
|