c819d615d0
- 좌우 방향 전면 수정: ch4(왼쪽)길다→←, ch5(오른쪽)길다→→ ch4만 감지→← (기기 오른쪽 치우침), ch5만→→ - Almost there 제거: 방향OK+CV미달 → 그냥 GREEN 통과 - postMaxIdx 런타임 슬라이더 (개발자 설정, 기본 80, 범위 50-95) Method A/B 공통 적용 — 바닥반사 peak 무시 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
139 lines
7.2 KiB
Kotlin
139 lines
7.2 KiB
Kotlin
package com.example.medilightv2android.managers
|
|
|
|
/**
|
|
* Green Zone 전체 파이프라인의 공유 상수 — Single Source of Truth.
|
|
*
|
|
* UrinAI, PeakDetection, Scoring 모두 이 상수를 참조.
|
|
* 파일 간 임계값 불일치 방지.
|
|
*
|
|
* 원본: xBench/GreenZoneConstants.kt (Charles KWON)
|
|
* 포팅일: 2026-04-17
|
|
*
|
|
* ⚠ 미세 조정 시 이 파일만 수정하면 전체 파이프라인에 반영됨.
|
|
* 각 상수의 의미와 영향 범위를 아래 주석 참고.
|
|
*/
|
|
enum class DetectionMethod { METHOD_A, METHOD_B, METHOD_C }
|
|
enum class PlacementGuideMode { SIMPLE, BOUNDARY, SWEEP }
|
|
enum class BvMethod { FRUSTUM, V41 }
|
|
|
|
object GreenZoneConstants {
|
|
|
|
var postMaxIdx: Int = 80 // 후벽 탐색 최대 sample index (이 이상 peak 무시)
|
|
|
|
var detectionMethod: DetectionMethod = DetectionMethod.METHOD_C
|
|
var bvMethod: BvMethod = BvMethod.V41
|
|
var placementGuideMode: PlacementGuideMode = PlacementGuideMode.SIMPLE
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// 신호 범위
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
/** raw ADC 중 사용할 샘플 수.
|
|
* Kotlin 원본: 90. VesiScan maa 응답은 100 samples 전송하지만 끝부분 10개는
|
|
* CRC/noise 가능성 → 앞 90개만 사용.
|
|
* 영향: UrinAI 탐색 범위, PeakDetection 탐색 범위 */
|
|
const val useSamples: Int = 90
|
|
|
|
/** 초기 ringdown 스킵 (센서 근접 반사 무시).
|
|
* idx 0~2는 피에조 소자 직접 반사 → 항상 높은 값 → 액체로 오판 방지.
|
|
* 영향: UrinAI.fw 탐색 시작점, PeakDetection.fw 탐색 시작점 */
|
|
const val ringSkip: Int = 3
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// 액체(소변) 판정 임계 — 12-bit ADC (0~4095)
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
/** Low-echo detection 임계값 (denoised 신호 기준).
|
|
* denoised ≤ 이 값 → low-echo (소변 후보).
|
|
* UrinAI의 liquidThrLoose(raw 기준)와 다름 — 여기는 TVD+SG 적용 후 신호 기준.
|
|
* Python 원본: 1150 (low_echo_detection_method_b.py LOW_ECHO_AMP, 6ch 버전)
|
|
* 영향: PiezoEchoAnalyzer → low-echo span 탐지, 벽 찾기의 기반 */
|
|
var lowEchoAmp: Float = 1250f
|
|
|
|
/** 구조 이진화 임계값: raw < 이 값 → 액체(liquid), ≥ → 조직(tissue).
|
|
* 낮출수록 엄격 (더 확실한 액체만 인정), 높일수록 관대.
|
|
* Kotlin 원본: 1400.
|
|
* 영향: UrinAI.binarize → fw/bw 탐색의 기반 */
|
|
const val liquidThrLoose: Float = 1400f
|
|
|
|
/** 순수 액체 확인용 엄격 임계값: raw < 이 값 → "확실한 소변".
|
|
* liquidThrLoose보다 낮아야 함.
|
|
* 이 기준으로 연속 액체 구간(liquidRun) 측정.
|
|
* Kotlin 원본: 1100. VesiScan 실측에서 팬텀 low-echo가 1100~1400 범위.
|
|
* 1100→run=0, 1300→run=2 (미달). 1400으로 상향.
|
|
* liquidThrLoose(1400)와 동일하게 설정 — 팬텀에서는 loose/strict 구분 불필요.
|
|
* ⚠ 인체 측정 시 loose > strict 으로 재분리 필요할 수 있음
|
|
* 영향: UrinAI.maxRun 계산 → 최종 판정의 핵심 */
|
|
const val liquidThrStrict: Float = 1400f
|
|
|
|
/** 최소 연속 순수 액체 길이.
|
|
* maxRun ≥ 이 값이어야 "소변 있음" 판정.
|
|
* 소방광(50mL) 대응: chord≈24 중 순수 액체 5+ 필요.
|
|
* Kotlin 원본: 5.
|
|
* 영향: UrinAI 최종 판정 (detected 조건 1/3) */
|
|
const val minLiquidRun: Int = 5
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// 벽 검출 범위
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
/** bw - fw 최소 (chord 최소 길이).
|
|
* 소방광 한계: 15 → 30mL(chord≈20) 이상 검출 가능.
|
|
* 50mL(chord≈24): margin 9 ✓
|
|
* 30mL(chord≈20): margin 5 ⚠
|
|
* 25mL 이하: 검출 불가 (임상적으로 PVR 50mL+ 의미 있는 범위)
|
|
* Kotlin 원본: 15.
|
|
* 영향: UrinAI 최종 판정 (detected 조건 2/3), PeakDetection bw 탐색 하한 */
|
|
const val minChord: Int = 15
|
|
|
|
/** bw - fw 최대 (이론적 상한).
|
|
* USE_SAMPLES(90)가 실질 제한.
|
|
* Kotlin 원본: 80.
|
|
* 영향: PeakDetection bw 탐색 상한 */
|
|
const val maxChord: Int = 80
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// 품질
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
/** (wallPeak - lumenFloor) / wallPeak 최소.
|
|
* 벽과 소변 사이 대비가 이 값 이상이어야 유효.
|
|
* 높일수록 엄격 (선명한 벽 요구), 낮출수록 관대.
|
|
* Kotlin 원본: 0.15.
|
|
* 영향: UrinAI 최종 판정 (detected 조건 3/3) */
|
|
const val contrastMin: Float = 0.15f
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// Green Zone Finder
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
/** score ≥ 이 값 → center lock (Green Zone 확정).
|
|
* Scoring.THR_EXCELLENT(75)보다 낮아 빠른 UX 확보.
|
|
* Kotlin 원본: 70.
|
|
* 영향: Placement Guide에서 Good 판정 기준 */
|
|
const val lockThreshold: Int = 70
|
|
|
|
/** 최대 기억 측정 수 (히스토리).
|
|
* Kotlin 원본: 20. */
|
|
const val maxHistory: Int = 20
|
|
|
|
// ═══════════════════════════════════════════════════════════
|
|
// 물리 상수 (교정용)
|
|
// ═══════════════════════════════════════════════════════════
|
|
|
|
/** ADC 샘플링 레이트 (Hz). VesiScan HW 사양. */
|
|
const val fs: Double = 400_000.0
|
|
|
|
/** 기준 조직 음속 (m/s). */
|
|
const val cRef: Double = 1540.0
|
|
|
|
/** 교정용 팬텀 직경 (mm). V=530mL 구. */
|
|
const val dPhantomMm: Double = 100.406
|
|
|
|
/** 팬텀 용적 (mL). */
|
|
const val vPhantomMl: Double = 530.0
|
|
|
|
/** 팬텀 반지름 (mm). */
|
|
const val rPhantomMm: Double = 50.203
|
|
}
|