summaryrefslogtreecommitdiff
path: root/tools/cnn_v2_test/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'tools/cnn_v2_test/index.html')
-rw-r--r--tools/cnn_v2_test/index.html100
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})`);