Files
micarrsim/apps/acoustic-camera/index.html
T

257 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="다중 채널 CPU·WebGPU acoustic camera simulator" />
<title>Acoustic Camera · micarrsim</title>
<link rel="stylesheet" href="/packages/ui/app-shell.css" />
<link rel="stylesheet" href="/apps/acoustic-camera/styles.css" />
</head>
<body>
<div class="app-shell camera-shell">
<header class="app-header camera-header">
<a class="app-brand" href="/"><strong>micarrsim</strong><span>Acoustic Camera</span></a>
<div class="header-status">
<span class="run-status" id="run-status">기본 입력 준비 중…</span>
<span class="status-chip status-chip--neutral" data-runtime-status>런타임 확인 중…</span>
</div>
<div class="header-actions">
<button class="button button--ghost is-hidden" id="cancel-button" type="button">계산 취소</button>
<button class="button button--ghost" id="export-result-button" type="button" disabled>Result JSON</button>
</div>
</header>
<nav class="workflow-strip" id="camera-workflow" aria-label="Acoustic Camera 작업 순서">
<a href="#input-step" data-workflow-step="1"><span>1</span><strong>입력</strong><small>Scene · Array</small></a>
<a href="#configure-step" data-workflow-step="2"><span>2</span><strong>설정</strong><small>Grid · Band</small></a>
<a href="#run-step" data-workflow-step="3"><span>3</span><strong>실행</strong><small>정지 · 이동</small></a>
<a href="#inspect-step" data-workflow-step="4"><span>4</span><strong>확인</strong><small>Map · 지표</small></a>
</nav>
<main class="camera-layout">
<aside class="control-panel" aria-label="입력 및 시뮬레이션 설정">
<section class="workflow-card input-card" id="input-step" data-workflow-section="1">
<div class="step-heading">
<span class="step-number">1</span>
<div><h2>입력 준비</h2><p>기본 예제를 쓰거나 파일을 순서대로 교체하세요.</p></div>
</div>
<div class="workspace-tools input-file-grid" aria-label="입력 파일">
<label class="file-button file-choice" title="Scene JSON을 선택하세요."><span class="file-sequence">1</span><span><strong>Scene</strong><small>장면·음원</small></span><input id="scene-input" type="file" accept="application/json,.json" /></label>
<label class="file-button file-choice" title="사용자 배열이 있을 때 Array JSON을 선택하세요."><span class="file-sequence">2</span><span><strong>Array</strong><small>마이크 배열</small></span><input id="array-input" type="file" accept="application/json,.json" /></label>
<label class="file-button file-choice" title="현재 Scene과 Array를 참조하는 Experiment JSON을 선택하세요."><span class="file-sequence">3</span><span><strong>Experiment</strong><small>분석 설정</small></span><input id="experiment-input" type="file" accept="application/json,.json" /></label>
<label class="file-button file-choice" title="Scene에 사용한 원본 파일을 다시 선택하세요."><span class="file-sequence">4</span><span><strong>Assets</strong><small>오디오·이미지</small></span><input id="asset-input" type="file" accept="audio/*,image/*,.wav,.flac,.mp3,.ogg,.m4a,.png,.jpg,.jpeg,.webp,.svg" multiple /></label>
</div>
<div class="input-summary">
<div class="compact-section-heading"><h3>현재 장면</h3><a class="builder-link" href="/apps/dataset-builder/">Builder에서 편집 →</a></div>
<div class="source-summary" id="source-summary"></div>
<div class="asset-list" id="asset-list"></div>
</div>
<div class="array-card">
<div class="array-heading"><div><span class="compact-label">MIC ARRAY</span><h3 id="array-title">배열</h3></div><strong id="microphone-count">— ch</strong></div>
<label class="field">
<span>배열 선택</span>
<select id="array-preset">
<optgroup label="기준 배열">
<option value="rectangular-16x12">16×12 rectangular · 192 ch / 0.45 m</option>
<option value="fermat-spiral-192">Fermat spiral · 192 ch / Ø 0.45 m</option>
</optgroup>
<optgroup label="채널 수 비교 · 0.45 m">
<option value="rectangular-2x2-450mm">2×2 sparse · 4 ch</option>
<option value="rectangular-4x3-450mm">4×3 sparse · 12 ch</option>
<option value="rectangular-8x6-450mm">8×6 · 48 ch</option>
<option value="rectangular-24x16-450mm">24×16 dense · 384 ch</option>
</optgroup>
<optgroup label="물리 크기 비교 · 192 ch">
<option value="rectangular-16x12-compact">16×12 compact · 0.12 m</option>
<option value="rectangular-16x12-large">16×12 large · 1.20 m</option>
</optgroup>
<optgroup label="사용자 배열">
<option value="custom">불러온 Array JSON</option>
</optgroup>
</select>
</label>
<details class="inline-details" id="array-details">
<summary>배열 미리보기와 좌표 정보</summary>
<canvas id="array-canvas" aria-label="마이크 배열 평면도"></canvas>
<p id="array-meta">배열을 불러오고 있습니다.</p>
</details>
</div>
</section>
<section class="workflow-card configure-card" id="configure-step" data-workflow-section="2">
<div class="step-heading">
<span class="step-number">2</span>
<div><h2>분석 설정</h2><p>자주 쓰는 항목만 먼저 조정합니다.</p></div>
</div>
<div class="quick-field-grid">
<label class="field"><span>Backend</span><select id="backend"><option value="auto">Auto (GPU→CPU)</option><option value="webgpu">WebGPU 강제</option><option value="cpu">CPU 강제</option></select></label>
<label class="field"><span>Grid preset</span><select id="grid-preset"><option value="50">50×50 · 기준</option><option value="100">100×100 · 4× points</option><option value="200">200×200 · 16× points</option><option value="custom">직접 입력</option></select></label>
<label class="field"><span>Scene 거리 z (m)</span><input id="scene-distance" type="number" min="0.01" step="0.1" /></label>
<label class="field" title="최강 peak 대비 검출 임계값입니다. 낮추면 약한 음원을 더 찾지만 sidelobe 오검출도 늘 수 있습니다."><span>Peak 임계값 (%)</span><input id="detection-threshold" type="number" min="1" max="100" step="1" /></label>
</div>
<p class="workload-summary" id="workload-summary">연산량 계산 중…</p>
<div class="band-config">
<div class="compact-section-heading"><h3>주파수 대역</h3><button class="mini-button" id="add-band-button" type="button">+ 대역</button></div>
<div class="band-editor-list" id="band-list"></div>
</div>
<details class="advanced-settings" id="advanced-settings">
<summary><span>고급 설정</span><small>음향 · 범위 · STFT · 식별자</small></summary>
<div class="advanced-settings__body">
<div class="advanced-group">
<h3>실험과 환경</h3>
<div class="advanced-field-grid">
<label class="field field--wide"><span>이름</span><input id="experiment-name" type="text" maxlength="120" /></label>
<label class="field field--wide"><span>Experiment ID</span><input id="experiment-id" type="text" maxlength="120" /></label>
<label class="field"><span>음속 (m/s)</span><input id="speed-of-sound" type="number" min="250" max="400" step="0.1" /></label>
<label class="field"><span>Seed</span><input id="scene-seed" type="number" min="0" max="4294967295" step="1" /></label>
<label class="field field--wide"><span>음향 환경</span><select id="acoustic-profile"><option value="free-field">자유음장 · 직접음만</option><option value="treated-room">흡음된 실내 · 짧은 잔향</option><option value="typical-room">일반 실내 · 균형</option><option value="reflective-room">반사 강한 실내</option><option value="custom" disabled>Scene 사용자 설정</option></select></label>
<label class="field field--wide"><span>마이크 주파수 응답 처리</span><select id="frequency-response-mode"><option value="compensate">측정 curve 적용 + 위치추정에서 역보정</option><option value="raw">측정 curve 적용 + 보정하지 않음</option></select></label>
</div>
<p class="acoustic-environment-summary" id="acoustic-environment-summary">음향 모델을 확인하고 있습니다.</p>
</div>
<div class="advanced-group">
<h3>Scan 범위</h3>
<div class="advanced-field-grid">
<label class="field"><span>Columns</span><input id="scan-columns" type="number" min="8" max="512" step="1" /></label>
<label class="field"><span>Rows</span><input id="scan-rows" type="number" min="8" max="512" step="1" /></label>
<label class="field"><span>x min (m)</span><input id="scan-x-min" type="number" step="0.05" /></label>
<label class="field"><span>x max (m)</span><input id="scan-x-max" type="number" step="0.05" /></label>
<label class="field"><span>y min (m)</span><input id="scan-y-min" type="number" step="0.05" /></label>
<label class="field"><span>y max (m)</span><input id="scan-y-max" type="number" step="0.05" /></label>
</div>
</div>
<div class="advanced-group">
<h3>STFT와 표시</h3>
<div class="advanced-field-grid">
<label class="field"><span>FFT size</span><select id="fft-size"><option>128</option><option>256</option><option selected>512</option><option>1024</option><option>2048</option><option>4096</option></select></label>
<label class="field"><span>Hop size</span><input id="hop-size" type="number" min="1" max="4096" step="1" /></label>
<label class="field"><span>Map 정규화</span><select id="normalization"><option value="peak">Peak</option><option value="none">없음</option></select></label>
<label class="field"><span>Dynamic range (dB)</span><input id="dynamic-range" type="number" min="6" max="120" step="1" /></label>
<label class="field field--wide"><span>후처리</span><select id="postprocess"><option value="none">없음 · raw DAS</option><option disabled>DAMAS · 후속 milestone</option><option disabled>CLEAN-SC · 후속 milestone</option></select></label>
</div>
</div>
</div>
</details>
<details class="output-card utility-details" id="document-tools">
<summary>Scene · Experiment 문서 저장</summary>
<div class="output-actions">
<button class="button button--ghost" id="export-scene-button" type="button">Scene JSON</button>
<button class="button button--ghost" id="export-experiment-button" type="button">Experiment JSON</button>
</div>
<p class="output-save-state" id="output-save-state">저장하지 않은 변경 없음</p>
</details>
</section>
<section class="validation-card" aria-live="polite">
<div class="validation-dot" id="validation-dot"></div>
<div><strong id="validation-title">검증 대기</strong><p id="validation-message">입력을 준비하고 있습니다.</p></div>
</section>
</aside>
<section class="scene-workspace" aria-label="Acoustic image workspace">
<div class="workspace-heading">
<div><span class="compact-label">CURRENT SCENE</span><h1 id="workspace-title">음향 장면</h1></div>
<p class="workspace-meta" id="scene-meta">장면을 불러오고 있습니다.</p>
</div>
<section class="run-stage" id="run-step" data-workflow-section="3" aria-labelledby="run-step-title">
<div class="step-heading step-heading--horizontal">
<span class="step-number">3</span>
<div><h2 id="run-step-title">실행 방식 선택</h2><p>한 장을 계산할지, 이동 경로를 재생할지 선택하세요.</p></div>
</div>
<div class="mode-grid">
<article class="mode-card mode-card--snapshot" id="snapshot-mode">
<div class="mode-heading">
<div><span class="mode-kicker">SNAPSHOT</span><h3>정지 장면</h3></div>
<span class="mode-format">1 map</span>
</div>
<p>현재 위치와 설정으로 전체 장면을 한 번 분석합니다.</p>
<div class="mode-actions">
<button class="button button--primary button--full" id="run-button" type="button" disabled>▶ Snapshot 실행</button>
</div>
</article>
<article class="mode-card mode-card--moving live-panel" id="moving-mode" aria-labelledby="live-title">
<div class="mode-heading">
<div><span class="mode-kicker">MOVING TIMELINE</span><h3 id="live-title">이동 장면</h3></div>
<span class="mode-format">여러 map</span>
</div>
<p id="live-status" aria-live="polite">trajectory가 있는 Scene을 불러오거나 이동 예제를 여세요.</p>
<div class="live-actions">
<button class="button button--ghost" id="load-moving-example-button" type="button">이동 예제 열기</button>
<button class="button button--primary" id="prepare-live-button" type="button" disabled>Map 준비</button>
<button class="button button--ghost" id="live-play-button" type="button" disabled>▶ 재생</button>
</div>
<div class="live-controls">
<label class="live-scrubber"><span id="live-time-label">0.00 / 0.00 s</span><input id="live-time" type="range" min="0" max="0" step="0.001" value="0" disabled /></label>
<label class="compact-select"><span>Map rate</span><select id="live-map-rate"><option value="2" selected>2 Hz</option><option value="5">5 Hz</option><option value="8">8 Hz</option></select></label>
<label class="compact-select"><span>속도</span><select id="live-speed"><option value="0.5">0.5×</option><option value="1" selected>1×</option><option value="2">2×</option></select></label>
</div>
<details class="mode-help"><summary>동작 방식과 한계</summary><p class="live-note">짧은 분석창 중앙의 위치를 쓰는 준정적 시뮬레이션입니다. Heatmap과 위치를 frame 사이에서 보간하며 실제 마이크 스트림 입력은 아닙니다.</p></details>
</article>
</div>
</section>
<section class="inspect-stage" id="inspect-step" data-workflow-section="4" aria-labelledby="inspect-title">
<div class="inspect-heading">
<div class="step-heading step-heading--horizontal">
<span class="step-number">4</span>
<div><h2 id="inspect-title">Map과 결과 확인</h2><p>Canvas가 실제 음향 에너지와 추정 위치를 표시합니다.</p></div>
</div>
<span class="result-chip" id="result-badge">아직 실행하지 않음</span>
</div>
<div class="scene-canvas-card">
<canvas id="scene-canvas" aria-label="Ground truth와 주파수별 acoustic image"></canvas>
<div class="canvas-message" id="canvas-message"><strong>실행할 준비가 되면 위에서 모드를 선택하세요.</strong><span>결과 heatmap은 이 canvas에 표시됩니다.</span></div>
<div class="canvas-badge" id="canvas-badge">결과 없음</div>
</div>
<div class="overlay-toolbar">
<div class="band-legend" id="band-legend" aria-label="표시 주파수 대역"></div>
<label class="range-field"><span>Overlay</span><input id="overlay-opacity" type="range" min="0" max="1" step="0.05" value="0.72" /></label>
<label class="check-field" title="점선은 1× RMS, 실선 영역은 2× RMS 빔 분포입니다. 확률 또는 신뢰구간은 아닙니다."><input id="show-beam-spread" type="checkbox" checked /><span>빔 분포</span></label>
<label class="check-field"><input id="show-truth" type="checkbox" checked /><span>Ground truth</span></label>
</div>
<section class="result-section" aria-live="polite">
<div class="compact-section-heading"><h3>측정 요약</h3><span>peak · 오차 · beam</span></div>
<div class="empty-result" id="empty-result">실행 후 위치 오차, energy concentration, sidelobe와 timing을 표시합니다.</div>
<div class="metric-grid is-hidden" id="metric-grid"></div>
</section>
<details class="history-section utility-details" id="comparison-history">
<summary><span>배열 비교 기록</span><small>같은 설정의 재실행 결과</small></summary>
<div class="history-body">
<div class="history-actions"><button class="text-button" id="export-history-button" type="button" disabled>전체 기록 JSON</button><button class="text-button" id="clear-history-button" type="button">기록 비우기</button></div>
<p class="section-copy">배열을 바꿔 다시 실행하면 같은 scene·scan·STFT 결과를 이 세션에서 비교합니다.</p>
<div class="history-list" id="history-list"></div>
</div>
</details>
<details class="help-panel utility-details" id="camera-help">
<summary><span>표시 해석과 한계</span><small>DAS · beam spread · live</small></summary>
<div class="help-grid">
<p><strong>Heatmap</strong>DAS 공간 power이며 위치 확률분포가 아닙니다.</p>
<p><strong>빔 분포</strong>Half-power covariance 폭이며 신뢰구간이 아닙니다.</p>
<p><strong>이동 재생</strong>준정적 frame 시뮬레이션이며 실제 장치 스트림이 아닙니다.</p>
</div>
</details>
</section>
</section>
</main>
</div>
<script type="module" src="/packages/ui/runtime-status.js"></script>
<script type="module" src="/apps/acoustic-camera/main.js"></script>
</body>
</html>