...
 
Commits (3)
......@@ -7,7 +7,7 @@
<link rel="icon" href="conticini.png" type="image/png" />
</head>
<body onload="randomize();">
<body onload="Conticini.randomize();">
<h1>QUANTO FA?</h1>
<div class="content" id="conticino">
<div id="operazione">
......
......@@ -2,76 +2,77 @@
// Basic idea from https://gist.github.com/peshoicov/ab3286875d980948ad3f5f434fec37a9
// and http://bencentra.com/code/2014/12/05/html5-canvas-touch-events.html
// some variables we'll need ..
var drawing = null;
var mousePos = {x:0, y:0};
var lastPos = mousePos;
var isMobile = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
const canvasDim = 0.45*Conticini.CONTICINO.clientWidth;
(function () {
var drawing = null;
var mousePos = {x:0, y:0};
var lastPos = mousePos;
var isMobile = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
const canvasDim = 0.45*Conticini.CONTICINO.clientWidth;
setupCanvas(Conticini.CIFRA_D);
setupCanvas(Conticini.CIFRA_U);
setupCanvas(Conticini.CIFRA_D);
setupCanvas(Conticini.CIFRA_U);
function setupCanvas(canvas){
// Setup canvas ..
const ctx = canvas.getContext('2d');
function setupCanvas(canvas){
// Setup canvas ..
const ctx = canvas.getContext('2d');
canvas.width = canvasDim;
canvas.height = canvas.width;
canvas.width = canvasDim;
canvas.height = canvas.width;
// setup lines styles ..
//ctx.strokeStyle = "#22222";
ctx.lineWidth = canvasDim / 28;
ctx.lineCap = 'round';
// setup lines styles ..
//ctx.strokeStyle = "#22222";
ctx.lineWidth = canvasDim / 28;
ctx.lineCap = 'round';
// mouse/touch events ..
canvas.addEventListener((isMobile ? 'touchstart' : 'mousedown'), function(e) {
drawing = ctx.canvas.id;
lastPos = getMousePos(canvas, e);
mousePos = lastPos;
});
canvas.addEventListener((isMobile ? 'touchmove' : 'mousemove'), function(e) {
mousePos = getMousePos(canvas, e);
});
canvas.addEventListener((isMobile ? 'touchend' : 'mouseup'), function(e) {
drawing = null;
});
// mouse/touch events ..
canvas.addEventListener((isMobile ? 'touchstart' : 'mousedown'), function(e) {
drawing = ctx.canvas.id;
lastPos = getMousePos(canvas, e);
mousePos = lastPos;
});
canvas.addEventListener((isMobile ? 'touchmove' : 'mousemove'), function(e) {
mousePos = getMousePos(canvas, e);
});
canvas.addEventListener((isMobile ? 'touchend' : 'mouseup'), function(e) {
drawing = null;
});
// drawing ..
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000/60);
};
})();
// drawing ..
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000/60);
};
})();
function drawLoop() {
window.requestAnimFrame(drawLoop);
renderCanvas(ctx);
}
function drawLoop() {
window.requestAnimFrame(drawLoop);
renderCanvas(ctx);
}
drawLoop();
}
drawLoop();
}
// helper functions ..
function getMousePos(canvasDom, touchOrMouseEvent) {
const rect = canvasDom.getBoundingClientRect();
return {
x: (isMobile ? touchOrMouseEvent.touches[0].clientX : touchOrMouseEvent.clientX) - rect.left,
y: (isMobile ? touchOrMouseEvent.touches[0].clientY : touchOrMouseEvent.clientY) - rect.top
};
}
// helper functions ..
function getMousePos(canvasDom, touchOrMouseEvent) {
const rect = canvasDom.getBoundingClientRect();
return {
x: (isMobile ? touchOrMouseEvent.touches[0].clientX : touchOrMouseEvent.clientX) - rect.left,
y: (isMobile ? touchOrMouseEvent.touches[0].clientY : touchOrMouseEvent.clientY) - rect.top
};
}
function renderCanvas(ctx) {
if (drawing === ctx.canvas.id) {
ctx.moveTo(lastPos.x, lastPos.y);
ctx.lineTo(mousePos.x, mousePos.y);
ctx.stroke();
lastPos = mousePos;
}
}
function renderCanvas(ctx) {
if (drawing === ctx.canvas.id) {
ctx.moveTo(lastPos.x, lastPos.y);
ctx.lineTo(mousePos.x, mousePos.y);
ctx.stroke();
lastPos = mousePos;
}
}
}());
// Copyright (C) 2020 by Mattia Monga <[email protected]>
// Code that consume ONNX.js mainly from https://github.com/microsoft/onnxjs-demo
async function getNumber(canvas, tmp) {
const myOnnxSession = new onnx.InferenceSession({ backendHint: "webgl" });
// load the ONNX model file
await myOnnxSession.loadModel("./model.onnx");
// Preprocess the image data to match input dimension requirement, which is 1*1*28*28.
const width = 28;
const height = 28;
const preprocessedData = preprocess(canvas, tmp, width, height);
const inputTensor = new onnx.Tensor(preprocessedData, 'float32', [1, 1, width, height]);
Conticini.nn = {
getNumber: async function(canvas, tmp) {
const myOnnxSession = new onnx.InferenceSession({ backendHint: "webgl" });
// load the ONNX model file
await myOnnxSession.loadModel("./model.onnx");
// Preprocess the image data to match input dimension requirement, which is 1*1*28*28.
const width = 28;
const height = 28;
const preprocessedData = this.preprocess(canvas, tmp, width, height);
const inputTensor = new onnx.Tensor(preprocessedData, 'float32', [1, 1, width, height]);
const outputMap = await myOnnxSession.run([inputTensor]);
const outputData = outputMap.values().next().value.data;
const probabilities = softmax(outputData);
console.log(probabilities);
return probabilities.findIndex((x) => x > .5); // -1 is not found
}
const outputMap = await myOnnxSession.run([inputTensor]);
const outputData = outputMap.values().next().value.data;
const probabilities = this.softmax(outputData);
console.log(probabilities);
return probabilities.findIndex((x) => x > .5); // -1 is not found
},
function softmax(arr) {
const C = Math.max(...arr);
const d = arr.map((y) => Math.exp(y - C)).reduce((a, b) => a + b);
return arr.map((value, index) => {
return Math.exp(value - C) / d;
});
}
softmax: function(arr) {
const C = Math.max(...arr);
const d = arr.map((y) => Math.exp(y - C)).reduce((a, b) => a + b);
return arr.map((value, index) => {
return Math.exp(value - C) / d;
});
},
function preprocess(canvas, tmp, width, height) {
const ctx = canvas.getContext('2d');
const ctxScaled = tmp.getContext('2d');
preprocess: function(canvas, tmp, width, height) {
const ctx = canvas.getContext('2d');
const ctxScaled = tmp.getContext('2d');
ctxScaled.save();
ctxScaled.scale(width / ctx.canvas.width, height / ctx.canvas.height);
ctxScaled.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctxScaled.drawImage(canvas, 0, 0);
const imageDataScaled = ctxScaled.getImageData(0, 0, ctxScaled.canvas.width, ctxScaled.canvas.height);
ctxScaled.restore();
// process image data for model input
const data = imageDataScaled.data;
const input = new Float32Array(width * height);
for (let i = 0; i < data.length; i += 4) {
ctxScaled.save();
ctxScaled.scale(width / ctx.canvas.width, height / ctx.canvas.height);
ctxScaled.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctxScaled.drawImage(canvas, 0, 0);
const imageDataScaled = ctxScaled.getImageData(0, 0, ctxScaled.canvas.width, ctxScaled.canvas.height);
ctxScaled.restore();
// process image data for model input
const data = imageDataScaled.data;
const input = new Float32Array(width * height);
for (let i = 0; i < data.length; i += 4) {
input[i / 4] = data[i + 3] / 255;
}
return input;
}
}
return input;
},
};
// Copyright (C) 2020 by Mattia Monga <[email protected]>
function randomize() {
Conticini.randomize = function() {
var op_sx, op, op_dx;
do {
op_sx = Math.floor(Conticini.MIN_OPERANDO
......@@ -14,44 +14,48 @@ function randomize() {
Conticini.OPERANDO_SX.innerHTML = "" + op_sx;
Conticini.OPERANDO_DX.innerHTML = "" + op_dx;
Conticini.OPERATORE.innerHTML = op;
reset_canvases();
}
Conticini.reset_canvases();
};
function reset_canvases() {
Conticini.reset_canvases = function() {
const ctx_u = Conticini.CIFRA_U.getContext('2d');
const ctx_d = Conticini.CIFRA_D.getContext('2d');
ctx_u.clearRect(0, 0, ctx_u.canvas.width, ctx_u.canvas.height);
ctx_u.beginPath();
ctx_d.clearRect(0, 0, ctx_d.canvas.width, ctx_d.canvas.height);
ctx_d.beginPath();
Conticini.check_msg();
};
Conticini.check_msg = function() {
Conticini.PULSANTE.value = Conticini.MSG_CHECK;
Conticini.PULSANTE.className = 'check';
Conticini.PULSANTE.onclick = check_result;
}
Conticini.PULSANTE.onclick = Conticini.check_result;
};
function ko_msg(timeout) {
Conticini.ko_msg = function(timeout) {
Conticini.PULSANTE.value = Conticini.MSG_KO;
Conticini.PULSANTE.className = 'ko';
window.setTimeout(reset_canvases, timeout);
}
window.setTimeout(Conticini.reset_canvases, timeout);
};
function ok_msg(timeout) {
Conticini.ok_msg = function(timeout) {
Conticini.PULSANTE.value = Conticini.MSG_OK;
Conticini.PULSANTE.className = 'ok';
window.setTimeout(randomize, timeout);
}
window.setTimeout(Conticini.randomize, timeout);
};
async function check_result() {
const u = await getNumber(Conticini.CIFRA_U, Conticini.CANVAS_TMP);
Conticini.check_result = async function() {
const u = await Conticini.nn.getNumber(Conticini.CIFRA_U, Conticini.CANVAS_TMP);
if (u === -1) {
ko_msg(Conticini.KO_TIMEOUT);
Conticini.ko_msg(Conticini.KO_TIMEOUT);
return;
}
const d = await getNumber(Conticini.CIFRA_D, Conticini.CANVAS_TMP);
const d = await Conticini.nn.getNumber(Conticini.CIFRA_D, Conticini.CANVAS_TMP);
if ((d === -1 && u === Conticini.expected) || (d > -1 && 10*d + u === Conticini.expected)) {
ok_msg(Conticini.OK_TIMEOUT);
Conticini.ok_msg(Conticini.OK_TIMEOUT);
return;
}
ko_msg(Conticini.KO_TIMEOUT);
}
Conticini.ko_msg(Conticini.KO_TIMEOUT);
};