diff options
Diffstat (limited to 'tools/cnn_v2_test')
| -rw-r--r-- | tools/cnn_v2_test/index.html | 100 |
1 files changed, 54 insertions, 46 deletions
diff --git a/tools/cnn_v2_test/index.html b/tools/cnn_v2_test/index.html index 9a5b2c0..c112905 100644 --- a/tools/cnn_v2_test/index.html +++ b/tools/cnn_v2_test/index.html @@ -316,24 +316,13 @@ </div> </div> </div> - <video id="videoSource" muted></video> + <video id="videoSource" muted loop></video> <div class="content"> <div class="left-sidebar"> <input type="file" id="weightsFile" accept=".bin" style="display: none;"> <div class="drop-zone" id="weightsDrop" onclick="document.getElementById('weightsFile').click()"> Drop .bin Weights or Click to Browse </div> - <div class="panel"> - <div class="panel-header">Features (p0-p3)</div> - <div class="panel-content"> - <label for="mipLevel" style="font-size: 11px;">Mip Level:</label> - <select id="mipLevel" style="width: 100%; background: #1a1a1a; color: #e0e0e0; border: 1px solid #404040; padding: 4px; margin-top: 4px;"> - <option value="0">Mip 0 (original)</option> - <option value="1">Mip 1 (half res)</option> - <option value="2">Mip 2 (quarter res)</option> - </select> - </div> - </div> <div class="panel" id="weightsInfoPanel"> <div class="panel-header">Weights Info</div> <div class="panel-content" id="weightsInfo"> @@ -347,6 +336,16 @@ <canvas id="weightsCanvas" style="width: 100%; image-rendering: pixelated; border: 1px solid #404040;"></canvas> </div> </div> + <div class="panel"> + <div class="panel-content"> + <label for="mipLevel" style="font-size: 11px;">Mip Level:</label> + <select id="mipLevel" style="width: 100%; background: #1a1a1a; color: #e0e0e0; border: 1px solid #404040; padding: 4px; margin-top: 4px;"> + <option value="0">Mip 0 (original)</option> + <option value="1">Mip 1 (half res)</option> + <option value="2">Mip 2 (quarter res)</option> + </select> + </div> + </div> </div> <div class="main" id="mainDrop"> <div class="video-controls-float" id="videoControls"> @@ -374,8 +373,29 @@ </div> <script> +// ============================================================================ +// EMBEDDED WEIGHTS & CONSTANTS +// ============================================================================ + +// Default pre-trained weights (base64-encoded binary format) const DEFAULT_WEIGHTS_B64 = 'Q05OMgEAAAADAAAAEAUAAAMAAAAMAAAABAAAAAAAAACwAQAAAwAAAAwAAAAEAAAAsAEAALABAAADAAAADAAAAAQAAABgAwAAsAEAAFQ3KjroN2yxSjQHsFi5HyjQtSo62zsJOq00bTXTqxC2zCnntyo44zoWOuwpajKup861uis/tj4kqyl6GkOp6CjTLbukERc2K0M4ijm8NpysMjQysgW4kLAHuJo5hTvGOtI0IDTio3C0yTDLtpM2ajq1ObctXjNlHEu2vqolt/ut2KMzJxWfSKf1p4et1C1eGfgt0atJMCihIKTyLZekxxRkKBas7ygWKpqoFS8LqHkruBhVqBwsELComn0yviiZMkUuVamVMKww2i/nMN0p/C1YKGmgBi8XKVilMCrApWGtaiJpI5OtJalcrZSkR5ceKvWrwSfxroSulyRcpXymsxTUIEcmXSx+rdap4KxIrCisTivhF1+tGi6VraYry6mdLNun26kKLHYod6o9r3AsyR7TpMytuykIpbStQ6ZoIZSsJqw/rM8o5K0eIbOgYCkXqAQpHysapWOsKCZcKPwtz6OPrRAuAapTq9AnBib9rPetVK4Lr6WstayeHqEo+Sk0LBskyqc6FueiOKn/JTknFqvapjcsJiy6K0+scqHVoyWnhKZ1rtWmkiiJq18sqaU3rpUyxS3pt2E4vDEIt7gj6ipZtSU5+zWjtKQ8uDaLqEguii/Qtck1SyRMtOs4UTA6ss6zSq8TtfSeFSwlJaOspisuowSuhCbGK3QxQaslt+83HTT4tAWoNKiTtSs4XTZos1g86zcZrLopxDBMtJY09iletg46dC13qy+vdax4tjIl8ynULASrUyG2rGqsGRklq5qpPasootsi3So3LJimlqpVJC8x6imJLmSoCyzPLkOrDCQtokovPaqMKmcsYKwWpS0gcSnzL2YseqXkJqorki1RMawtGC+MMEE0MiyfsCAzGTNEsgAz9qkJtBguArUutnqugieqtBk1FJ1Yszc1RK8vtqgpNidgtfYzpy5CsBwoHKopJGaZdKxwJFaqyJp/q4kzJSwrs78xeTMXs6AziYror8YwTrN8tcukZy0htKM1nC2qsOc00rDYtBwtXa3GtN00qqo5sf8m7aoBLnGqBaZIrbMekSa7LEam56g6rPaojJ1VIfoqbiRjIwkutSxCKGsr559+L4aw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'; +// Reusable fullscreen quad vertex shader (2 triangles covering NDC) +const FULLSCREEN_QUAD_VS = ` +@vertex +fn vs_main(@builtin(vertex_index) idx: u32) -> @builtin(position) vec4<f32> { + var pos = array<vec2<f32>, 6>( + vec2<f32>(-1.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(-1.0, 1.0), + vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0) + ); + return vec4<f32>(pos[idx], 0.0, 1.0); +}`; + +// ============================================================================ +// WGSL SHADERS +// ============================================================================ + +// Static features: 7D parametric features (RGBD + UV + sin(10*uv_x) + bias) const STATIC_SHADER = ` @group(0) @binding(0) var input_tex: texture_2d<f32>; @group(0) @binding(1) var input_sampler: sampler; @@ -677,6 +697,21 @@ class CNNTester { this.status.style.color = isError ? '#ff4a4a' : '#4a9eff'; } + // Get current source dimensions (video or image) + getDimensions() { + if (this.isVideo) { + return { width: this.video.videoWidth, height: this.video.videoHeight }; + } + return { width: this.image.width, height: this.image.height }; + } + + // Enable/disable video playback controls + setVideoControlsEnabled(enabled) { + ['playPauseBtn', 'stepBackBtn', 'stepForwardBtn'].forEach(id => + document.getElementById(id).disabled = !enabled + ); + } + parseWeights(buffer) { const view = new DataView(buffer); const magic = view.getUint32(0, true); @@ -757,9 +792,7 @@ class CNNTester { this.isVideo = false; this.canvas.width = img.width; this.canvas.height = img.height; - document.getElementById('playPauseBtn').disabled = true; - document.getElementById('stepBackBtn').disabled = true; - document.getElementById('stepForwardBtn').disabled = true; + this.setVideoControlsEnabled(false); this.log(`Loaded image: ${file.name} (${img.width}×${img.height})`); if (this.weights) { this.setStatus(`Ready: ${img.width}×${img.height}`); @@ -788,11 +821,7 @@ class CNNTester { this.canvas.height = h; this.fps = 30; this.log(`Loaded video: ${file.name} (${w}×${h}, ${this.video.duration.toFixed(1)}s)`); - - // Enable video controls - ['playPauseBtn', 'stepBackBtn', 'stepForwardBtn'].forEach(id => - document.getElementById(id).disabled = false - ); + this.setVideoControlsEnabled(true); // Set up event handlers this.video.onpause = () => { document.getElementById('playPauseBtn').textContent = 'Play'; }; @@ -925,16 +954,8 @@ class CNNTester { generateMipmaps(texture, width, height) { if (!this.mipmapPipeline) { - const mipmapShader = ` + const mipmapShader = FULLSCREEN_QUAD_VS + ` @group(0) @binding(0) var src: texture_2d<f32>; - @vertex - fn vs_main(@builtin(vertex_index) idx: u32) -> @builtin(position) vec4<f32> { - var pos = array<vec2<f32>, 6>( - vec2<f32>(-1.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(-1.0, 1.0), - vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0) - ); - return vec4<f32>(pos[idx], 0.0, 1.0); - } @fragment fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> { let coord = vec2<i32>(i32(pos.x) * 2, i32(pos.y) * 2); @@ -993,8 +1014,7 @@ class CNNTester { const source = this.isVideo ? this.video : this.image; if (!source || !this.device) return; - const width = this.isVideo ? this.video.videoWidth : this.image.width; - const height = this.isVideo ? this.video.videoHeight : this.image.height; + const { width, height } = this.getDimensions(); this.context.configure({ device: this.device, format: this.format }); const inputTex = this.device.createTexture({ @@ -1009,18 +1029,8 @@ class CNNTester { [width, height] ); - const simpleShader = ` - @vertex - fn vs_main(@builtin(vertex_index) idx: u32) -> @builtin(position) vec4<f32> { - var pos = array<vec2<f32>, 6>( - vec2<f32>(-1.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(-1.0, 1.0), - vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0) - ); - return vec4<f32>(pos[idx], 0.0, 1.0); - } - + const simpleShader = FULLSCREEN_QUAD_VS + ` @group(0) @binding(0) var tex: texture_2d<f32>; - @fragment fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> { let coord = vec2<i32>(pos.xy); @@ -1064,8 +1074,7 @@ class CNNTester { const t0 = performance.now(); const source = this.isVideo ? this.video : this.image; if (!source) return; - const width = this.isVideo ? this.video.videoWidth : this.image.width; - const height = this.isVideo ? this.video.videoHeight : this.image.height; + const { width, height } = this.getDimensions(); this.log(`Running CNN pipeline (${this.weights.layers.length} layers)...`); @@ -1394,8 +1403,7 @@ class CNNTester { const layerName = layerIdx === 0 ? `Static Features (${channelOffset}-${channelOffset + 3})` : `Layer ${layerIdx - 1}`; const layerTex = this.layerOutputs[layerIdx]; - const width = this.isVideo ? this.video.videoWidth : this.image.width; - const height = this.isVideo ? this.video.videoHeight : this.image.height; + const { width, height } = this.getDimensions(); this.log(`Visualizing ${layerName} activations (${width}×${height})`); |
