diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb62d60 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +yarn.lock +.cache +dist +node_modules diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5a43a25 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.7' + +services: + server: + image: cupcakearmy/static + restart: unless-stopped + ports: + - 80:80 + volumes: + - ./dist:/srv:ro \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..eb36497 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "scripts": { + "start": "parcel src/index.html", + "build": "parcel build src/index.html" + }, + "browserslist": [ + "last 1 Chrome versions" + ], + "dependencies": { + "@tensorflow/tfjs": "^1.5.1" + }, + "devDependencies": { + "parcel-bundler": "^1.12.4", + "parcel-plugin-static-files-copy": "^2.2.1" + } +} \ No newline at end of file diff --git a/src/canvas.js b/src/canvas.js new file mode 100644 index 0000000..4db43ea --- /dev/null +++ b/src/canvas.js @@ -0,0 +1,81 @@ +/* jslint esversion: 6, asi: true */ + +var canvas, ctx, flag = false, + prevX = 0, + currX = 0, + prevY = 0, + currY = 0, + dot_flag = false; + +var x = "black", + y = 2; + +function init() { + canvas = document.getElementById('can'); + ctx = canvas.getContext("2d"); + w = canvas.width; + h = canvas.height; + + canvas.addEventListener("mousemove", function (e) { + findxy('move', e) + }, false); + canvas.addEventListener("mousedown", function (e) { + findxy('down', e) + }, false); + canvas.addEventListener("mouseup", function (e) { + findxy('up', e) + }, false); + canvas.addEventListener("mouseout", function (e) { + findxy('out', e) + }, false); + + + window.document.getElementById('clear').addEventListener('click', erase) +} + +function draw() { + ctx.beginPath(); + ctx.moveTo(prevX, prevY); + ctx.lineTo(currX, currY); + ctx.strokeStyle = x; + ctx.lineWidth = y; + ctx.stroke(); + ctx.closePath(); +} + +function erase() { + ctx.clearRect(0, 0, w, h); +} + +function findxy(res, e) { + if (res == 'down') { + prevX = currX; + prevY = currY; + currX = e.clientX - canvas.offsetLeft; + currY = e.clientY - canvas.offsetTop; + + flag = true; + dot_flag = true; + if (dot_flag) { + ctx.beginPath(); + ctx.fillStyle = x; + ctx.fillRect(currX, currY, 2, 2); + ctx.closePath(); + dot_flag = false; + } + } + if (res == 'up' || res == "out") { + flag = false; + } + if (res == 'move') { + if (flag) { + prevX = currX; + prevY = currY; + currX = e.clientX - canvas.offsetLeft; + currY = e.clientY - canvas.offsetTop; + draw(); + } + } +} + +init() \ No newline at end of file diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..2b369ad --- /dev/null +++ b/src/index.html @@ -0,0 +1,61 @@ + + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/src/tf.js b/src/tf.js new file mode 100644 index 0000000..f667b87 --- /dev/null +++ b/src/tf.js @@ -0,0 +1,22 @@ +/* jslint esversion: 8, asi: true */ + +import * as tf from '@tensorflow/tfjs'; + +let model + +tf.loadLayersModel('/model.json').then(m => { + model = m +}) + +window.document.getElementById('test').addEventListener('click', async () => { + const canvas = window.document.querySelector('canvas') + + const { data, width, height } = canvas.getContext('2d').getImageData(0, 0, 28, 28) + + const tensor = tf.tensor(new Uint8Array(data.filter((_, i) => i % 4 === 3)), [1, 28, 28, 1]) + const prediction = model.predict(tensor) + const result = await prediction.data() + const guessed = result.indexOf(1) + console.log(guessed) + window.document.querySelector('#result').innerText = guessed +}) \ No newline at end of file diff --git a/static/group1-shard1of1.bin b/static/group1-shard1of1.bin new file mode 100644 index 0000000..7333e2e Binary files /dev/null and b/static/group1-shard1of1.bin differ diff --git a/static/model.json b/static/model.json new file mode 100644 index 0000000..41ddad5 --- /dev/null +++ b/static/model.json @@ -0,0 +1,310 @@ +{ + "format": "layers-model", + "generatedBy": "keras v2.2.4-tf", + "convertedBy": "TensorFlow.js Converter v1.4.0", + "modelTopology": { + "keras_version": "2.2.4-tf", + "backend": "tensorflow", + "model_config": { + "class_name": "Sequential", + "config": { + "name": "sequential", + "layers": [ + { + "class_name": "Conv2D", + "config": { + "name": "conv2d", + "trainable": true, + "batch_input_shape": [ + null, + 28, + 28, + 1 + ], + "dtype": "float32", + "filters": 64, + "kernel_size": [ + 3, + 3 + ], + "strides": [ + 1, + 1 + ], + "padding": "valid", + "data_format": "channels_last", + "dilation_rate": [ + 1, + 1 + ], + "activation": "relu", + "use_bias": true, + "kernel_initializer": { + "class_name": "GlorotUniform", + "config": { + "seed": 420, + "dtype": "float32" + } + }, + "bias_initializer": { + "class_name": "Zeros", + "config": { + "dtype": "float32" + } + }, + "kernel_regularizer": null, + "bias_regularizer": null, + "activity_regularizer": null, + "kernel_constraint": null, + "bias_constraint": null + } + }, + { + "class_name": "Conv2D", + "config": { + "name": "conv2d_1", + "trainable": true, + "dtype": "float32", + "filters": 32, + "kernel_size": [ + 3, + 3 + ], + "strides": [ + 1, + 1 + ], + "padding": "valid", + "data_format": "channels_last", + "dilation_rate": [ + 1, + 1 + ], + "activation": "relu", + "use_bias": true, + "kernel_initializer": { + "class_name": "GlorotUniform", + "config": { + "seed": 420, + "dtype": "float32" + } + }, + "bias_initializer": { + "class_name": "Zeros", + "config": { + "dtype": "float32" + } + }, + "kernel_regularizer": null, + "bias_regularizer": null, + "activity_regularizer": null, + "kernel_constraint": null, + "bias_constraint": null + } + }, + { + "class_name": "MaxPooling2D", + "config": { + "name": "max_pooling2d", + "trainable": true, + "dtype": "float32", + "pool_size": [ + 2, + 2 + ], + "padding": "valid", + "strides": [ + 2, + 2 + ], + "data_format": "channels_last" + } + }, + { + "class_name": "Dropout", + "config": { + "name": "dropout", + "trainable": true, + "dtype": "float32", + "rate": 0.25, + "noise_shape": null, + "seed": 420 + } + }, + { + "class_name": "Flatten", + "config": { + "name": "flatten", + "trainable": true, + "dtype": "float32", + "data_format": "channels_last" + } + }, + { + "class_name": "Dense", + "config": { + "name": "dense", + "trainable": true, + "dtype": "float32", + "units": 128, + "activation": "relu", + "use_bias": true, + "kernel_initializer": { + "class_name": "GlorotUniform", + "config": { + "seed": 420, + "dtype": "float32" + } + }, + "bias_initializer": { + "class_name": "Zeros", + "config": { + "dtype": "float32" + } + }, + "kernel_regularizer": null, + "bias_regularizer": null, + "activity_regularizer": null, + "kernel_constraint": null, + "bias_constraint": null + } + }, + { + "class_name": "Dropout", + "config": { + "name": "dropout_1", + "trainable": true, + "dtype": "float32", + "rate": 0.5, + "noise_shape": null, + "seed": 420 + } + }, + { + "class_name": "Dense", + "config": { + "name": "dense_1", + "trainable": true, + "dtype": "float32", + "units": 10, + "activation": "softmax", + "use_bias": true, + "kernel_initializer": { + "class_name": "GlorotUniform", + "config": { + "seed": 420, + "dtype": "float32" + } + }, + "bias_initializer": { + "class_name": "Zeros", + "config": { + "dtype": "float32" + } + }, + "kernel_regularizer": null, + "bias_regularizer": null, + "activity_regularizer": null, + "kernel_constraint": null, + "bias_constraint": null + } + } + ] + } + }, + "training_config": { + "loss": "categorical_crossentropy", + "metrics": [ + "accuracy" + ], + "weighted_metrics": null, + "sample_weight_mode": null, + "loss_weights": null, + "optimizer_config": { + "class_name": "Adam", + "config": { + "name": "Adam", + "learning_rate": 0.0010000000474974513, + "decay": 0.0, + "beta_1": 0.8999999761581421, + "beta_2": 0.9990000128746033, + "epsilon": 1e-07, + "amsgrad": false + } + } + } + }, + "weightsManifest": [ + { + "paths": [ + "group1-shard1of1.bin" + ], + "weights": [ + { + "name": "conv2d/kernel", + "shape": [ + 3, + 3, + 1, + 64 + ], + "dtype": "float32" + }, + { + "name": "conv2d/bias", + "shape": [ + 64 + ], + "dtype": "float32" + }, + { + "name": "conv2d_1/kernel", + "shape": [ + 3, + 3, + 64, + 32 + ], + "dtype": "float32" + }, + { + "name": "conv2d_1/bias", + "shape": [ + 32 + ], + "dtype": "float32" + }, + { + "name": "dense/kernel", + "shape": [ + 4608, + 128 + ], + "dtype": "float32" + }, + { + "name": "dense/bias", + "shape": [ + 128 + ], + "dtype": "float32" + }, + { + "name": "dense_1/kernel", + "shape": [ + 128, + 10 + ], + "dtype": "float32" + }, + { + "name": "dense_1/bias", + "shape": [ + 10 + ], + "dtype": "float32" + } + ] + } + ] +} \ No newline at end of file