// Use JS to write whatever HTML and data you want to the page
// One way of using JS to write HTML and data is with a multi-line string
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
document.write ( `
<div class="digit-demo-container">
<h3 id="digit-recognizer-live-demo">Digit Recognizer using TensorFlow.js Demo</h3>
<div class="flex-two" style="margin-top: 20px;">
<button id="clear_canvas" class="material-button-pink" onclick="clearCanvas(this.id)">Clear</button>
<select id="select_model">
<option>MLP</option>
<option>CNN</option>
</select>
<button id="predict_canvas" class="material-button-pink" onclick="predict(this.id)">Predict</button>
</div>
<div class="flex-two">
<div id="canvas_box_wrapper" class="canvas-box-wrapper">
<div id="canvas_box" class="canvas-box">
</div>
</div>
<div id="result_box">
<canvas id="chart_box" width="100" height="100"></canvas>
</div>
</div>
</div>
` );
const cssText = `
.digit-demo-container {
background-color: #4CAF50;
border-radius: 5px;
margin: 20px auto;
h3 {
width: 100%;
font-size: 15px;
background-color: #356937;
padding: 10px;
color: white;
margin: 0px;
border: 1px solid #295b2b;
border-bottom: 0px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
select {
margin-top: 0px;
height: 30px;
font-size: 12px;
}
}
.flex-two {
display: flex;
flex-wrap: wrap;
div {
flex: 1;
padding: 20px;
text-align: center;
@include mobile {
padding: 5px;
}
}
}
.canvas-box-wrapper {
display: block !important;
}
.material-button-pink {
background-color: #FFD740;
height: 30px;
color: black;
display: block;
font-family: $font_body;
font-weight: bold;
padding: 5px 10px;
cursor: pointer;
border: 1px solid #a58e3a;
font-size: 12px;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
margin: 0px auto;
border-radius: 5px;
}
.material-button-pink:focus {
outline: none;
}
.material-button-pink:hover {
box-shadow: 0 1px 8px rgba(0,0,0,0.3), 0 5px 10px rgba(0,0,0,0.22);
}
#chart_box {
background-color: white;
padding: 20px;
border-radius: 5px;
@include mobile {
padding: 5px;
}
}
`
// const tfurl = "https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"
const chartjsurl = "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.min.js";
$.getScript ( chartjsurl, function() {
console.log('chartjs loaded');
});
// $.getScript ( tfurl, function() {
// console.log('tfjs loaded');
// });
const TFurl = 'https://unpkg.com/@tensorflow/tfjs';
// var MNurl = 'https://unpkg.com/@tensorflow-models/mobilenet';
// var ml5url = 'https://unpkg.com/ml5@0.4.3/dist/ml5.min.js'
$.getScript( TFurl, function() {
console.log('tensorflow.js loaded');
});
const head = document.getElementsByTagName('head')[0];
let script = document.createElement('script');
script.type = 'text/javascript';
script.onload = function() {
console.log('tf inserted');
}
script.src = TFurl;
head.appendChild(script);
// insert css
// let style = document.createElement('style');
// style.setAttribute('type', 'text/css');
// if (style.styleSheet) { // IE
// style.styleSheet.cssText = css;
// } else { // the world
// style.appendChild(document.createTextNode(css));
// }
// head.appendChild(style);
// $.getScript ( tfurl, function()
// {
// $.getScript ( "https://code.jquery.com/jquery-2.1.1.min.js", function()
// {
// $.getScript ( "/uploads/codingtrain/mnist.js", function()
// {
// console.log ("All JS loaded");
// });
// });
// });
// }
// GLOBAL variables
var modelName = "digitrecognizermlp";
let model;
// canvas related variables
// you can change these variables
var canvasWidth = 150;
var canvasHeight = 150;
var canvasStrokeStyle = "white";
var canvasLineJoin = "round";
var canvasLineWidth = 12;
var canvasBackgroundColor = "black";
var canvasId = "canvas";
// variables to hold coordinates and dragging boolean
var clickX = new Array();
var clickY = new Array();
var clickD = new Array();
var drawing;
document.getElementById('chart_box').innerHTML = "";
document.getElementById('chart_box').style.display = "none";
//---------------------
// Create canvas
//---------------------
var canvasBox = document.getElementById('canvas_box');
var canvas = document.createElement("canvas");
canvas.setAttribute("width", canvasWidth);
canvas.setAttribute("height", canvasHeight);
canvas.setAttribute("id", canvasId);
canvas.style.backgroundColor = canvasBackgroundColor;
canvasBox.appendChild(canvas);
if(typeof G_vmlCanvasManager != 'undefined') {
canvas = G_vmlCanvasManager.initElement(canvas);
}
ctx = canvas.getContext("2d");
//---------------------
// MOUSE DOWN function
//---------------------
$("#canvas").mousedown(function(e) {
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
drawing = true;
addUserGesture(mouseX, mouseY);
drawOnCanvas();
});
//---------------------
// TOUCH START function
//---------------------
canvas.addEventListener("touchstart", function (e) {
if (e.target == canvas) {
e.preventDefault();
}
var rect = canvas.getBoundingClientRect();
var touch = e.touches[0];
var mouseX = touch.clientX - rect.left;
var mouseY = touch.clientY - rect.top;
drawing = true;
addUserGesture(mouseX, mouseY);
drawOnCanvas();
}, false);
// MOUSE MOVE function
//---------------------
$("#canvas").mousemove(function(e) {
if(drawing) {
var mouseX = e.pageX - this.offsetLeft;
var mouseY = e.pageY - this.offsetTop;
addUserGesture(mouseX, mouseY, true);
drawOnCanvas();
}
});
//---------------------
// TOUCH MOVE function
//---------------------
canvas.addEventListener("touchmove", function (e) {
if (e.target == canvas) {
e.preventDefault();
}
if(drawing) {
var rect = canvas.getBoundingClientRect();
var touch = e.touches[0];
var mouseX = touch.clientX - rect.left;
var mouseY = touch.clientY - rect.top;
addUserGesture(mouseX, mouseY, true);
drawOnCanvas();
}
}, false);
//---------------------
// MOUSE UP function
//---------------------
$("#canvas").mouseup(function(e) {
drawing = false;
});
//---------------------
// TOUCH END function
//---------------------
canvas.addEventListener("touchend", function (e) {
if (e.target == canvas) {
e.preventDefault();
}
drawing = false;
}, false);
//----------------------
// MOUSE LEAVE function
//----------------------
$("#canvas").mouseleave(function(e) {
drawing = false;
});
//---------------------
// TOUCH LEAVE function
//---------------------
canvas.addEventListener("touchleave", function (e) {
if (e.target == canvas) {
e.preventDefault();
}
drawing = false;
}, false);
//----------------------
// ADD CLICK function
//----------------------
function addUserGesture(x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickD.push(dragging);
}
//----------------------
// RE DRAW function
//----------------------
function drawOnCanvas() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.strokeStyle = canvasStrokeStyle;
ctx.lineJoin = canvasLineJoin;
ctx.lineWidth = canvasLineWidth;
for (var i = 0; i < clickX.length; i++) {
ctx.beginPath();
if(clickD[i] && i) {
ctx.moveTo(clickX[i-1], clickY[i-1]);
} else {
ctx.moveTo(clickX[i]-1, clickY[i]);
}
ctx.lineTo(clickX[i], clickY[i]);
ctx.closePath();
ctx.stroke();
}
}
//----------------------
// CLEAR CANVAS function
//----------------------
function clearCanvas(id) {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
clickX = new Array();
clickY = new Array();
clickD = new Array();
}
//-----------------------
// select model handler
//-----------------------
$("#select_model").change(function() {
var select_model = document.getElementById("select_model");
var select_option = select_model.options[select_model.selectedIndex].value;
if (select_option == "MLP") {
modelName = "digitrecognizermlp";
} else if (select_option == "CNN") {
modelName = "digitrecognizercnn";
} else {
modelName = "digitrecognizermlp";
}
loadModel(modelName);
});
//-------------------------------------
// loader for digitrecognizermlp model
//-------------------------------------
async function loadModel() {
console.log("model loading..");
// clear the model variable
model = undefined;
// load the model using a HTTPS request (where you have stored your model files)
if (modelName === "digitrecognizermlp") {
model = await tf.loadLayersModel("uploads/lukescales/mlp_model_update.json");
} else {
model = await tf.loadLayersModel("uploads/lukescales/cnn_model_update.json");
}
console.log("model loaded..");
}
//-----------------------------------------------
// preprocess the canvas to be DNN friendly
//-----------------------------------------------
function preprocessCanvas(image, modelName) {
// if model is not available, send the tensor with expanded dimensions
if (modelName === undefined) {
alert("No model defined..")
}
// if model is digitrecognizermlp, perform all the preprocessing
else if (modelName === "digitrecognizermlp") {
// resize the input image to digitrecognizermlp's target size of (784, )
let tensor = tf.browser.fromPixels(image)
.resizeNearestNeighbor([28, 28])
.mean(2)
.toFloat()
.reshape([1 , 784]);
return tensor.div(255.0);
}
// if model is digitrecognizercnn, perform all the preprocessing
else if (modelName === "digitrecognizercnn") {
// resize the input image to digitrecognizermlp's target size of (1, 28, 28, 1)
let tensor = tf.browser.fromPixels(image)
.resizeNearestNeighbor([28, 28])
.mean(2)
.expandDims(2)
.expandDims()
.toFloat();
console.log(tensor.shape);
return tensor.div(255.0);
}
// else throw an error
else {
alert("Unknown model name..")
}
}
//----------------------------
// Bounding box for centering
//----------------------------
function boundingBox() {
var minX = Math.min.apply(Math, clickX) - 20;
var maxX = Math.max.apply(Math, clickX) + 20;
var minY = Math.min.apply(Math, clickY) - 20;
var maxY = Math.max.apply(Math, clickY) + 20;
var tempCanvas = document.createElement("canvas"),
tCtx = tempCanvas.getContext("2d");
tempCanvas.width = maxX - minX;
tempCanvas.height = maxY - minY;
tCtx.drawImage(canvas, minX, minY, maxX - minX, maxY - minY, 0, 0, maxX - minX, maxY - minY);
// var imgBox = document.getElementById("canvas_image");
// imgBox.src = tempCanvas.toDataURL();
return tempCanvas;
}
//--------------------------------------------
// predict function for digit recognizer mlp
//--------------------------------------------
async function predict() {
// model = await tf.loadLayersModel("uploads/lukescales/processed_model_v2.json");
await loadModel();
// get the user drawn region alone cropped
croppedCanvas = boundingBox();
// show the cropped image
// document.getElementById("canvas_output").style.display = "block";
// preprocess canvas
let tensor = preprocessCanvas(croppedCanvas, modelName);
// make predictions on the preprocessed image tensor
let predictions = await model.predict(tensor).data();
// get the model's prediction results
let results = Array.from(predictions)
// display the predictions in chart
displayChart(results)
console.log(results);
}
//------------------------------
// Chart to display predictions
//------------------------------
var chart = "";
var firstTime = 0;
function loadChart(label, data, modelSelected) {
var context = document.getElementById('chart_box').getContext('2d');
chart = new Chart(context, {
// we are in need of a bar chart
type: 'bar',
// we feed in data dynamically using data variable
// that is passed as an argument to this function
data: {
labels: label,
datasets: [{
label: modelSelected + " prediction",
backgroundColor: '#f50057',
borderColor: 'rgb(255, 99, 132)',
data: data,
}]
},
// you can also play around with options for the
// chart if you find time!
options: {}
});
}
//----------------------------
// display chart with updated
// drawing from canvas
//----------------------------
function displayChart(data) {
var select_model = document.getElementById("select_model");
var select_option = select_model.options[select_model.selectedIndex].value;
label = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
if (firstTime == 0) {
loadChart(label, data, select_option);
firstTime = 1;
} else {
chart.destroy();
loadChart(label, data, select_option);
}
document.getElementById('chart_box').style.display = "block";
}