diff --git a/demos/index.html b/demos/index.html index b56417b..3b7ff52 100644 --- a/demos/index.html +++ b/demos/index.html @@ -26,7 +26,8 @@ - + + diff --git a/demos/src/_variables.css b/demos/src/_variables.css index 0c4b68a..71a5aa6 100644 --- a/demos/src/_variables.css +++ b/demos/src/_variables.css @@ -3,6 +3,7 @@ $color-1-light: rgba(27, 188, 155, 0.6); $color-1-lighter: rgba(27, 188, 155, 0.3); $color-2: #69707a; $color-3: #393E46; +$color-err: #D24D57; $font-1: 'Fira Sans', sans-serif; $font-2: 'Inconsolata', sans-serif; diff --git a/demos/src/mnist-vae.js b/demos/src/mnist-vae.js index b602078..085457a 100644 --- a/demos/src/mnist-vae.js +++ b/demos/src/mnist-vae.js @@ -218,9 +218,9 @@ export const MnistVae = Vue.extend({ this.layerResultImages.forEach((result, layerNum) => { const scalingFactor = this.layerDisplayConfig[result.name].scalingFactor result.images.forEach((image, imageNum) => { - let ctx = document.getElementById(`intermediate-result-${layerNum}-${imageNum}`).getContext('2d') + const ctx = document.getElementById(`intermediate-result-${layerNum}-${imageNum}`).getContext('2d') ctx.putImageData(image, 0, 0) - let ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d') + const ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d') ctxScaled.save() ctxScaled.scale(scalingFactor, scalingFactor) ctxScaled.clearRect(0, 0, ctxScaled.canvas.width, ctxScaled.canvas.height) @@ -234,7 +234,7 @@ export const MnistVae = Vue.extend({ this.layerResultImages.forEach((result, layerNum) => { const scalingFactor = this.layerDisplayConfig[result.name].scalingFactor result.images.forEach((image, imageNum) => { - let ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d') + const ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d') ctxScaled.save() ctxScaled.scale(scalingFactor, scalingFactor) ctxScaled.clearRect(0, 0, ctxScaled.canvas.width, ctxScaled.canvas.height) diff --git a/demos/src/resnet50.css b/demos/src/resnet50.css index 067f240..32af4db 100644 --- a/demos/src/resnet50.css +++ b/demos/src/resnet50.css @@ -36,6 +36,24 @@ .column.input-column { justify-content: flex-end; + .loading-indicator { + display: flex; + flex-direction: column; + align-self: flex-start; + + .mdl-spinner { + margin: 20px; + align-self: center; + } + + .error { + color: $color-err; + font-size: 14px; + font-family: $font-1; + margin: 20px; + } + } + .canvas-container { display: inline-flex; justify-content: flex-end; diff --git a/demos/src/resnet50.js b/demos/src/resnet50.js index 3d32d12..cb8fa82 100644 --- a/demos/src/resnet50.js +++ b/demos/src/resnet50.js @@ -1,4 +1,4 @@ -/* global Vue */ +/* global Vue, loadImage */ import './resnet50.css' import * as utils from './utils' @@ -37,12 +37,14 @@ export const ResNet50 = Vue.extend({ return { model: new KerasJS.Model(MODEL_CONFIG), modelLoading: true, - imageURL: '', + imageURLInput: null, + imageURLSelect: null, imageURLSelectList: [ - { name: 'cat', value: 'URL1' }, + { name: 'cat', value: 'http://i.imgur.com/CzXTtJV.jpg' }, { name: 'dog', value: 'URL2' } ], - input: new Float32Array(224 * 224 * 3), + imageLoading: false, + imageLoadingError: false, output: new Float32Array(1000), layerResultImages: [], layerDisplayConfig: LAYER_DISPLAY_CONFIG, @@ -61,7 +63,7 @@ export const ResNet50 = Vue.extend({ this.model.initialize() this.model.ready().then(() => { this.modelLoading = false - this.getIntermediateResults() + //this.getIntermediateResults() }) }, @@ -71,6 +73,69 @@ export const ResNet50 = Vue.extend({ this.model.gpu = !this.useGpu }, + imageURLInputChanged: function (e) { + this.imageURLSelect = null + this.loadImageToCanvas(this.imageURLInput) + }, + + imageURLSelectChanged: function (e) { + this.imageURLInput = this.imageURLSelect + this.loadImageToCanvas(this.imageURLSelect) + }, + + loadImageToCanvas: function (url) { + this.imageLoading = true + loadImage( + url, + img => { + if (img.type === 'error') { + this.imageLoadingError = true + this.imageLoading = false + } else { + // load image data onto input canvas + const ctx = document.getElementById('input-canvas').getContext('2d') + ctx.drawImage(img, 0, 0) + this.imageLoadingError = false + this.imageLoading = false + // model predict + this.runModel() + } + }, + { + maxWidth: 224, + maxHeight: 224, + cover: true, + crop: true, + canvas: true, + crossOrigin: 'Anonymous' + } + ) + }, + + runModel: function () { + const ctx = document.getElementById('input-canvas').getContext('2d') + const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height) + const { data, width, height } = imageData + + // data processing + // see https://github.com/fchollet/keras/blob/master/keras/applications/imagenet_utils.py + let dataProcessed = new Float32Array(width * height * 3) + for (let i = 0, len = data.length; i < len; i += 4) { + // RGB -> BGR + dataProcessed[i / 4 + 2] = data[i] - 103.939 + dataProcessed[i / 4 + 1] = data[i + 1] - 116.779 + dataProcessed[i / 4] = data[i + 2] - 123.68 + } + + const inputData = { + 'input_1': dataProcessed + } + const outputData = this.model.predict(inputData) + this.output = outputData['fc1000'] + console.log(this.output) + //this.getIntermediateResults() + }, + getIntermediateResults: function () { let results = [] for (let [name, layer] of this.model.modelLayersMap.entries()) { @@ -101,9 +166,9 @@ export const ResNet50 = Vue.extend({ this.layerResultImages.forEach((result, layerNum) => { const scalingFactor = this.layerDisplayConfig[result.name].scalingFactor result.images.forEach((image, imageNum) => { - let ctx = document.getElementById(`intermediate-result-${layerNum}-${imageNum}`).getContext('2d') + const ctx = document.getElementById(`intermediate-result-${layerNum}-${imageNum}`).getContext('2d') ctx.putImageData(image, 0, 0) - let ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d') + const ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d') ctxScaled.save() ctxScaled.scale(scalingFactor, scalingFactor) ctxScaled.clearRect(0, 0, ctxScaled.canvas.width, ctxScaled.canvas.height) @@ -117,7 +182,7 @@ export const ResNet50 = Vue.extend({ this.layerResultImages.forEach((result, layerNum) => { const scalingFactor = this.layerDisplayConfig[result.name].scalingFactor result.images.forEach((image, imageNum) => { - let ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d') + const ctxScaled = document.getElementById(`intermediate-result-${layerNum}-${imageNum}-scaled`).getContext('2d') ctxScaled.save() ctxScaled.scale(scalingFactor, scalingFactor) ctxScaled.clearRect(0, 0, ctxScaled.canvas.width, ctxScaled.canvas.height) diff --git a/demos/src/resnet50.template.html b/demos/src/resnet50.template.html index eebdb7e..c4335c6 100644 --- a/demos/src/resnet50.template.html +++ b/demos/src/resnet50.template.html @@ -9,13 +9,21 @@
Enter an image URL or select from dropdown
- + or - +
+
+ +
Error loading URL
+
@@ -28,7 +36,7 @@
-
+
diff --git a/index.html b/index.html index 1f34ef8..3a32d2a 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,7 @@
- +
@@ -26,7 +26,8 @@ - + +