Skip to content

Commit 7cfdc6c

Browse files
authored
Add Input Image Support for First Layer (#68)
* Add image support * Fix Tensor opacity / filter opacity sliders issue * Fix lagging in SVG mode * Create separate image control section * Add horizontal image flip functionality * Change fliping method to avoid POT warning * Change flipping method to avoid POT warning
1 parent 603cfa6 commit 7cfdc6c

2 files changed

Lines changed: 88 additions & 6 deletions

File tree

AlexNet.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,26 @@ <h4>Style:</h4>
148148
<input type="range" id="betweenLayers" name="" min="10" max="100" step="1" value="20" style="position: relative; top: 3px;">
149149
</div>
150150

151+
<div id="inputImageControl">
152+
<hr>
153+
<div>
154+
<label for="inputImage">Upload Image for First Layer</label>
155+
<input type="file" id="inputImage" accept="image/*" class="form-control-file">
156+
<button id="removeImage" class="btn btn-secondary" style="display:none; margin-top: 5px; padding: 3px;">
157+
Remove Image
158+
</button>
159+
</div>
160+
<div id="opacityControl" style="display:none;">
161+
<label for="imageOpacity">Image Opacity</label>
162+
<input type="range" id="imageOpacity" min="0" max="1" step="0.01" value="0.5"
163+
style="position: relative; top: 3px;">
164+
</div>
165+
<div id="flipControl" style="display:none;">
166+
<input type="checkbox" id="imageFlip" name="imageFlip" value="imageFlip">
167+
<label for="imageFlip">Flip Image Horizontally</label>
168+
</div>
169+
</div>
170+
151171
<hr>
152172
<div>
153173
<input type="checkbox" id="logDepth" name="logDepth" value="logDepth" checked>
@@ -358,9 +378,11 @@ <h4>Architecture:</h4>
358378
rendererType = this.value;
359379
if (rendererType == 'svg') {
360380
$("#download").removeClass('disabled');
381+
$("#inputImageControl").hide();
361382
}
362383
else if (rendererType == 'webgl') {
363384
$("#download").addClass('disabled');
385+
$("#inputImageControl").show();
364386
}
365387
$(this).blur();
366388
alexnet.restartRenderer({'rendererType_':rendererType});
@@ -372,6 +394,10 @@ <h4>Architecture:</h4>
372394
$("#fontScale").change( function () { alexnet.style({'fontScale_': this.value}); });
373395
$("#rectOpacity").change( function () { alexnet.style({'rectOpacity_': parseFloat(this.value)}); $(this).blur(); });
374396
$("#filterOpacity").change( function () { alexnet.style({'filterOpacity_': parseFloat(this.value)}); $(this).blur(); });
397+
$("#inputImage").change(function(){var file=this.files[0];$('#imageOpacity').closest('div').toggle(!!file);$('#flipControl').toggle(!!file);$("#removeImage").toggle(!!file);if(file){alexnet.redraw({'inputimage_':URL.createObjectURL(file)});}$(this).blur();});
398+
$("#removeImage").click(function () { $("#inputImage").val(""); $("#imageOpacity").closest('div').hide(); $(this).hide(); alexnet.redraw({ 'inputimage_': null }); });
399+
$("#imageOpacity").change(function () { alexnet.style({ 'imageOpacity_': parseFloat(this.value) }); $(this).blur(); });
400+
$("#imageFlip").change(function () { alexnet.style({'imageFlip_': this.checked}); });
375401
$("#betweenLayers").change( function () { alexnet.redraw({'betweenLayers_': parseFloat(this.value)}); $(this).blur(); });
376402
$("#logDepth").change( function () { alexnet.redraw({'logDepth_': this.checked}); });
377403
$("#depthScale").change( function () { alexnet.redraw({'depthScale_': parseFloat(this.value)}); $(this).blur(); $("#depthSpan").text(this.value); });

AlexNet.js

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@ function AlexNet() {
1414

1515
var rectOpacity = 0.4;
1616
var filterOpacity = 0.4;
17+
var imageOpacity = 0.5;
18+
var imageFlip = false;
1719
var fontScale = 1;
1820

1921
var line_material = new THREE.LineBasicMaterial( { 'color':0x000000 } );
22+
var image_material = new THREE.MeshBasicMaterial({ 'side': THREE.DoubleSide, 'transparent': true, 'opacity': imageOpacity, 'depthWrite': false, 'needsUpdate': true });
2023
var box_material = new THREE.MeshBasicMaterial( {'color':color1, 'side':THREE.DoubleSide, 'transparent':true, 'opacity':rectOpacity, 'depthWrite':false, 'needsUpdate':true} );
2124
var conv_material = new THREE.MeshBasicMaterial( {'color':color2, 'side':THREE.DoubleSide, 'transparent':true, 'opacity':filterOpacity, 'depthWrite':false, 'needsUpdate':true} );
2225
var pyra_material = new THREE.MeshBasicMaterial( {'color':color3, 'side':THREE.DoubleSide, 'transparent':true, 'opacity':filterOpacity, 'depthWrite':false, 'needsUpdate':true} );
2326

2427
var architecture = [];
2528
var architecture2 = [];
29+
var inputImage = null;
2630
var betweenLayers = 20;
2731

2832
var logDepth = true;
@@ -100,6 +104,7 @@ function AlexNet() {
100104

101105
function redraw({architecture_=architecture,
102106
architecture2_=architecture2,
107+
inputimage_ = inputImage,
103108
betweenLayers_=betweenLayers,
104109
logDepth_=logDepth,
105110
depthScale_=depthScale,
@@ -112,6 +117,7 @@ function AlexNet() {
112117

113118
architecture = architecture_;
114119
architecture2 = architecture2_;
120+
inputImage = inputimage_;
115121
betweenLayers = betweenLayers_;
116122
logDepth = logDepth_;
117123
depthScale = depthScale_;
@@ -132,9 +138,42 @@ function AlexNet() {
132138

133139
// Layer
134140
layer_geometry = new THREE.BoxGeometry( wf(layer), hf(layer), depthFn(layer['depth']) );
135-
layer_object = new THREE.Mesh( layer_geometry, box_material );
136-
layer_object.position.set(0, 0, layer_offsets[index]);
137-
layers.add( layer_object );
141+
if (index === 0 && inputImage && rendererType === 'webgl') {
142+
var textureLoader = new THREE.TextureLoader();
143+
const layer_geometry_cache = layer_geometry.clone();
144+
const layer_offset_cache = layer_offsets[index];
145+
textureLoader.load(inputImage, function(texture) {
146+
texture.minFilter = THREE.LinearFilter;
147+
texture.magFilter = THREE.LinearFilter;
148+
texture.wrapS = THREE.ClampToEdgeWrapping;
149+
texture.wrapT = THREE.ClampToEdgeWrapping;
150+
if (imageFlip) {
151+
texture.matrix.setUvTransform(0, 0, -1, 1, 0, 0.5, 0);
152+
texture.matrixAutoUpdate = false;
153+
}
154+
image_material.map = texture;
155+
var materials = [
156+
box_material,
157+
box_material,
158+
box_material,
159+
box_material,
160+
box_material,
161+
image_material
162+
];
163+
layer_object = new THREE.Mesh(layer_geometry_cache, materials);
164+
layer_object.position.set(0, 0, layer_offset_cache);
165+
layers.add(layer_object);
166+
if (showDims) {
167+
makeTextSprite(rendererType === 'svg', layer['depth'].toString(), layer_object.position, new THREE.Vector3(wf(layer) / 2 + 2, hf(layer) / 2 + 2, 0));
168+
makeTextSprite(rendererType === 'svg', layer['width'].toString(), layer_object.position, new THREE.Vector3(wf(layer) / 2 + 3, 0, depthFn(layer['depth']) / 2 + 3));
169+
makeTextSprite(rendererType === 'svg', layer['height'].toString(), layer_object.position, new THREE.Vector3(0, -hf(layer) / 2 - 3, depthFn(layer['depth']) / 2 + 3));
170+
}
171+
});
172+
} else {
173+
layer_object = new THREE.Mesh(layer_geometry, box_material);
174+
layer_object.position.set(0, 0, layer_offsets[index]);
175+
layers.add(layer_object);
176+
}
138177

139178
layer_edges_geometry = new THREE.EdgesGeometry( layer_geometry );
140179
layer_edges_object = new THREE.LineSegments( layer_edges_geometry, line_material );
@@ -179,7 +218,7 @@ function AlexNet() {
179218

180219
}
181220

182-
if (showDims) {
221+
if (showDims&&(index!=0||inputImage==null)) {
183222

184223
// Dims
185224
sprite = makeTextSprite(rendererType === 'svg', layer['depth'].toString(), layer_object.position, new THREE.Vector3( wf(layer)/2 + 2, hf(layer)/2 + 2, 0 ));
@@ -247,7 +286,19 @@ function AlexNet() {
247286
}
248287

249288
if ( obj.geometry ) { obj.geometry.dispose(); }
250-
if ( obj.material ) { obj.material.dispose(); }
289+
if (obj.material) {
290+
if (Array.isArray(obj.material)) {
291+
obj.material.forEach(material => {
292+
if (material && typeof material.dispose === 'function') {
293+
material.dispose();
294+
}
295+
});
296+
} else {
297+
if (typeof obj.material.dispose === 'function') {
298+
obj.material.dispose();
299+
}
300+
}
301+
}
251302
if ( obj.texture ) { obj.texture.dispose(); }
252303
}
253304

@@ -308,21 +359,26 @@ function AlexNet() {
308359
color3_=color3,
309360
rectOpacity_=rectOpacity,
310361
filterOpacity_=filterOpacity,
362+
imageOpacity_ = imageOpacity,
363+
imageFlip_=imageFlip,
311364
fontScale_ =fontScale,
312365
}={}) {
313366
color1 = color1_;
314367
color2 = color2_;
315368
color3 = color3_;
369+
316370
rectOpacity = rectOpacity_;
317371
filterOpacity = filterOpacity_;
372+
imageOpacity = imageOpacity_;
373+
imageFlip = imageFlip_;
318374
fontScale = fontScale_;
319-
320375
box_material.color = new THREE.Color(color1);
321376
conv_material.color = new THREE.Color(color2);
322377
pyra_material.color = new THREE.Color(color3);
323378

324379
box_material.opacity = rectOpacity;
325380

381+
image_material.opacity = imageOpacity;
326382
conv_material.opacity = filterOpacity;
327383
pyra_material.opacity = filterOpacity;
328384
}

0 commit comments

Comments
 (0)