diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/cnn_v2_test/index.html | 178 |
1 files changed, 62 insertions, 116 deletions
diff --git a/tools/cnn_v2_test/index.html b/tools/cnn_v2_test/index.html index cab20ea..6d3f223 100644 --- a/tools/cnn_v2_test/index.html +++ b/tools/cnn_v2_test/index.html @@ -68,14 +68,16 @@ input[type="range"] { width: 120px; } input[type="number"] { width: 60px; background: #1a1a1a; color: #e0e0e0; border: 1px solid #404040; padding: 4px; } .drop-zone { - border: 2px dashed #404040; - padding: 16px; + border: 3px dashed #606060; + padding: 20px; text-align: center; cursor: pointer; transition: all 0.2s; - font-size: 12px; - background: #1a1a1a; - border-radius: 4px; + font-size: 13px; + font-weight: bold; + background: #252525; + border-radius: 6px; + color: #4a9eff; } button { background: #1a1a1a; @@ -91,7 +93,7 @@ button:hover { border-color: #606060; background: #252525; } button:disabled { opacity: 0.3; cursor: not-allowed; } video { display: none; } - .drop-zone:hover { border-color: #606060; background: #252525; } + .drop-zone:hover { border-color: #4a9eff; background: #2a3545; } .drop-zone.active { border-color: #4a9eff; background: #1a2a3a; } .drop-zone.error { border-color: #ff4a4a; background: #3a1a1a; } .content { @@ -240,18 +242,25 @@ flex-direction: column; overflow: hidden; } - .layer-zoom { + .layer-preview { background: #1a1a1a; border: 1px solid #404040; display: flex; flex-direction: column; overflow: hidden; + margin-top: 8px; } - .layer-zoom canvas { + .layer-preview canvas { width: 100%; height: 100%; image-rendering: pixelated; } + .layer-view.active { + border: 2px solid #ffffff; + } + .layer-view canvas { + cursor: pointer; + } .layer-view-label { background: #2a2a2a; padding: 4px; @@ -413,7 +422,7 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) { let rgba = textureSampleLevel(input_tex, input_sampler, uv, f32(mip_level)); let d = textureLoad(depth_tex, coord, 0).r; let uv_x = f32(coord.x) / f32(dims.x); - let uv_y = 1.0 - (f32(coord.y) / f32(dims.y)); + let uv_y = f32(coord.y) / f32(dims.y); let sin20_y = sin(20.0 * uv_y); let packed = vec4<u32>( @@ -662,6 +671,7 @@ class CNNTester { this.fps = 30; this.isProcessing = false; this.mipLevel = 0; + this.selectedChannel = 0; this.init(); } @@ -1360,7 +1370,7 @@ class CNNTester { html += '</div>'; html += '<div class="layer-grid" id="layerGrid"></div>'; - html += '<div class="layer-zoom"><div class="layer-view-label">Zoom x4</div><canvas id="zoomCanvas"></canvas></div>'; + html += '<div class="layer-preview"><div class="layer-view-label" id="previewLabel">Ch0</div><canvas id="previewCanvas"></canvas></div>'; panel.innerHTML = html; this.log(`Layer visualization ready: ${this.layerOutputs.length} layers`); @@ -1395,8 +1405,10 @@ class CNNTester { <div class="layer-view-label" id="channelLabel${c}">Ch ${c}</div> <canvas id="layerCanvas${c}"></canvas> `; + div.onclick = () => this.selectChannel(c); grid.appendChild(div); } + this.selectedChannel = 0; } async visualizeLayer(layerIdx, channelOffset = 0) { @@ -1527,42 +1539,37 @@ class CNNTester { await this.device.queue.onSubmittedWorkDone(); this.log(`Rendered 4 channels for ${layerName}`); - // Set up mouse tracking for zoom view - this.setupZoomTracking(layerTex, channelOffset); + // Update active channel highlighting and preview + this.updateChannelSelection(); + await this.renderChannelPreview(); } - setupZoomTracking(layerTex, channelOffset) { - const zoomCanvas = document.getElementById('zoomCanvas'); - if (!zoomCanvas) return; - - const width = this.isVideo ? this.video.videoWidth : this.image.width; - const height = this.isVideo ? this.video.videoHeight : this.image.height; - const zoomSize = 32; // Show 32x32 area - zoomCanvas.width = zoomSize; - zoomCanvas.height = zoomSize; - - // Add mousemove handlers to all layer canvases - for (let c = 0; c < 4; c++) { - const canvas = document.getElementById(`layerCanvas${c}`); - if (!canvas) continue; + selectChannel(channelIdx) { + this.selectedChannel = channelIdx; + this.updateChannelSelection(); + this.renderChannelPreview(); + } - const updateZoom = (e) => { - const rect = canvas.getBoundingClientRect(); - const x = Math.floor((e.clientX - rect.left) / rect.width * width); - const y = Math.floor((e.clientY - rect.top) / rect.height * height); - this.renderZoom(layerTex, channelOffset, x, y, zoomSize); - }; + updateChannelSelection() { + const grid = document.getElementById('layerGrid'); + if (!grid) return; - canvas.onmousemove = updateZoom; - canvas.onmouseenter = updateZoom; - } + const views = grid.querySelectorAll('.layer-view'); + views.forEach((view, idx) => { + view.classList.toggle('active', idx === this.selectedChannel); + }); } - async renderZoom(layerTex, channelOffset, centerX, centerY, zoomSize) { - const zoomCanvas = document.getElementById('zoomCanvas'); - if (!zoomCanvas || !this.device) return; + async renderChannelPreview() { + const previewCanvas = document.getElementById('previewCanvas'); + const previewLabel = document.getElementById('previewLabel'); + if (!previewCanvas || !this.device) return; - const ctx = zoomCanvas.getContext('webgpu'); + const { width, height } = this.getDimensions(); + previewCanvas.width = width; + previewCanvas.height = height; + + const ctx = previewCanvas.getContext('webgpu'); if (!ctx) return; try { @@ -1571,91 +1578,30 @@ class CNNTester { return; } - const halfSize = Math.floor(zoomSize / 2); - const width = this.isVideo ? this.video.videoWidth : this.image.width; - const height = this.isVideo ? this.video.videoHeight : this.image.height; - - // Create shader for zoomed view (samples 4 channels and displays as 2x2 grid) - const zoomShader = ` - @group(0) @binding(0) var layer_tex: texture_2d<u32>; - @group(0) @binding(1) var<uniform> params: vec4<f32>; // centerX, centerY, channelOffset, scale - - @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 dims = textureDimensions(layer_tex); - let centerX = i32(params.x); - let centerY = i32(params.y); - let channelOffset = u32(params.z); - let scale = params.w; - - // Map output pixel to source pixel - let halfSize = 16; - let localX = i32(pos.x) - halfSize; - let localY = i32(pos.y) - halfSize; - let srcX = clamp(centerX + localX, 0, i32(dims.x) - 1); - let srcY = clamp(centerY + localY, 0, i32(dims.y) - 1); - - let coord = vec2<i32>(srcX, srcY); - let packed = textureLoad(layer_tex, coord, 0); - let v0 = unpack2x16float(packed.x); - let v1 = unpack2x16float(packed.y); - let v2 = unpack2x16float(packed.z); - let v3 = unpack2x16float(packed.w); - - var channels: array<f32, 8>; - channels[0] = v0.x; - channels[1] = v0.y; - channels[2] = v1.x; - channels[3] = v1.y; - channels[4] = v2.x; - channels[5] = v2.y; - channels[6] = v3.x; - channels[7] = v3.y; - - // Determine which quadrant (channel) to show - let quadX = i32(pos.x) / 16; - let quadY = i32(pos.y) / 16; - let channelIdx = min(channelOffset + u32(quadY * 2 + quadX), 7u); + // Update label + const channelLabel = document.getElementById(`channelLabel${this.selectedChannel}`); + if (channelLabel && previewLabel) { + previewLabel.textContent = channelLabel.textContent; + } - let val = clamp(channels[channelIdx] * scale, 0.0, 1.0); - return vec4<f32>(val, val, val, 1.0); - } - `; + // Render selected channel + const layerIdx = this.currentLayerIdx; + const channelOffset = this.currentChannelOffset; + const layerTex = this.layerOutputs[layerIdx]; + if (!layerTex) return; - if (!this.zoomPipeline) { - this.zoomPipeline = this.device.createRenderPipeline({ - layout: 'auto', - vertex: { - module: this.device.createShaderModule({ code: zoomShader }), - entryPoint: 'vs_main' - }, - fragment: { - module: this.device.createShaderModule({ code: zoomShader }), - entryPoint: 'fs_main', - targets: [{ format: this.format }] - } - }); - } + const vizScale = layerIdx === 0 ? 1.0 : 0.5; + const actualChannel = channelOffset + this.selectedChannel; - const vizScale = channelOffset === 0 ? 1.0 : 0.5; const paramsBuffer = this.device.createBuffer({ - size: 16, + size: 8, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }); - const paramsData = new Float32Array([centerX, centerY, channelOffset, vizScale]); + const paramsData = new Float32Array([actualChannel, vizScale]); this.device.queue.writeBuffer(paramsBuffer, 0, paramsData); const bindGroup = this.device.createBindGroup({ - layout: this.zoomPipeline.getBindGroupLayout(0), + layout: this.layerVizPipeline.getBindGroupLayout(0), entries: [ { binding: 0, resource: layerTex.createView() }, { binding: 1, resource: { buffer: paramsBuffer } } @@ -1671,7 +1617,7 @@ class CNNTester { }] }); - renderPass.setPipeline(this.zoomPipeline); + renderPass.setPipeline(this.layerVizPipeline); renderPass.setBindGroup(0, bindGroup); renderPass.draw(6); renderPass.end(); |
