diff --git a/.gitignore b/.gitignore index a0935e1..20d3eb2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ **/out **/a.out .cache/* -.test-cache/* \ No newline at end of file +.test-cache/* +**/__pycache__ \ No newline at end of file diff --git a/src/webserver/app.py b/src/webserver/app.py new file mode 100644 index 0000000..1736aa8 --- /dev/null +++ b/src/webserver/app.py @@ -0,0 +1,71 @@ +#!/usr/bin/python3 +from flask import Flask, render_template, request +import subprocess +import json + +MAGIC_NUMBER = 2051 + +app = Flask(__name__) + +@app.route("/") +def index(): + return render_template("index.html") + +@app.route("/mnist") +def mnist(): + return render_template("mnist.html") + +@app.route("/post", methods=["POST"]) +def post_json_handler(): + """ + Gère les requêtes POST + """ + if request.is_json: + content = request.get_json() + req_type = content["type"] + if req_type == "prediction": + dataset = content["dataset"] + image = content["data"] + if dataset == "mnist": + return recognize_mnist(image) + return {"status": "404"} + + +def recognize_mnist(image): + """Appelle le programme C reconnaissant les images""" + # Créer le fichier binaire + write_image_to_binary(image, ".cache/image.bin") + + try: + return {"status": 200, "data": + json.loads(subprocess.check_output([ + 'out/main', + 'recognize', + '--modele', '.cache/reseau.bin', + '--in', '.cache/image.bin', + '--out', 'json' + ]))["0"]} + except subprocess.CalledProcessError: + return { + "status": 500, + "data": "Internal Server Error" + } + +def write_image_to_binary(image, filepath): + byteorder = "big" + + bytes_ = MAGIC_NUMBER.to_bytes(4, byteorder=byteorder) + bytes_ += (1).to_bytes(4, byteorder=byteorder) + bytes_ += len(image).to_bytes(4, byteorder=byteorder) + bytes_ += len(image[0]).to_bytes(4, byteorder=byteorder) + for row in image: + for nb in row: + bytes_ += int(nb).to_bytes(1, byteorder=byteorder) + + with open(filepath, "wb") as f: + f.write(bytes_) + + + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') \ No newline at end of file diff --git a/src/webserver/static/script.js b/src/webserver/static/script.js new file mode 100644 index 0000000..2b92c6d --- /dev/null +++ b/src/webserver/static/script.js @@ -0,0 +1,169 @@ +/* +Le code concernant l'entrée par l'utilisateur d'un nombre a été adapté depuis le code suivant: (License MIT) +https://github.com/maneprajakta/Digit_Recognition_Web_App/blob/master/js/main.js + +On été rajoutées les fonctions permettant de communiquer avec le serveur +*/ +// Variables globales +var canvas; +var ctx; +var touchX; +var touchY; +var mouseX; +var mouseY; +var mouseDown = 0; + +function init() { + canvas = document.getElementById('digit-canvas'); + ctx = canvas.getContext('2d'); + + ctx.fillStyle = "black"; + // Créer une zone de dessin + ctx.fillRect(0, 0, canvas.width, canvas.height); + + if(ctx) + { + canvas.addEventListener('mousedown', s_mouseDown, false); + canvas.addEventListener('mousemove', s_mouseMove, false); + window.addEventListener('mouseup', s_mouseUp, false); + document.getElementById('clear').addEventListener("click", clear); + } +} + + +function draw(ctx, x, y, isDown) { + // isDown représente le fait que le chemin ait déjà commencé ou non + // On ne dessine donc ue si le "stylo" est déjà posé + if (isDown) { + ctx.beginPath(); + // On choisit les options suivantes: + ctx.strokeStyle = "white"; + ctx.lineWidth = "10"; + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + ctx.moveTo(lastX, lastY); + ctx.lineTo(x, y); + ctx.closePath(); + ctx.stroke(); + } + lastX = x; + lastY = y; +} + +// Mouse moves +function s_mouseDown() { + mouseDown = 1; + draw(ctx, mouseX, mouseY, false); +} + +function s_mouseUp() { + mouseDown = 0; +} + +function s_mouseMove(e) { + // Si la souris bouge et est cliquée, on souhaite dessiner un trait + getMousePos(e); + if (mouseDown == 1) { + draw(ctx, mouseX, mouseY, true); + } +} + +function getMousePos(e) { + if (e.offsetX) { + mouseX = e.offsetX; + mouseY = e.offsetY; + } else if (e.layerX) { + mouseX = e.layerX; + mouseY = e.layerY; + } +} + + +function clear() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = "black"; + ctx.fillRect(0, 0, canvas.width, canvas.height); +} + + + +function sendJSON(data,callback){ + // Creating a XHR object + let xhr = new XMLHttpRequest(); + let url = "post"; + + // open a connection + xhr.open("POST", url, true); + + // Set the request header i.e. which type of content you are sending + xhr.setRequestHeader("Content-Type", "application/json"); + + // Create a state change callback + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + + // Print received data from server + callback(this.responseText); + + } + }; + + // Converting JSON data to string + var data = JSON.stringify(data); + + // Sending data with the request + xhr.send(data); +} + + +function getPrediction() { + let totalWidth = 280; + let totalHeight = 280; + let curWidth = 0; + let curHeight = 0; + let stepSize = 10; + + let tableau = []; + let tmp_tableau = []; + + while (curWidth < totalWidth) { + curHeight = 0; + tmp_tableau = []; + while (curHeight < totalHeight) { + data = ctx.getImageData(curWidth, curHeight, stepSize, stepSize); + size = data.width * data.height; + density = 0; + + for (let i=0;i < size;i++) { + density += (data.data[i*4]+data.data[i*4+1]+data.data[i*4+2])/3; + } + density = density*1.0 / size; + + tmp_tableau.push(density); + curHeight += stepSize; + } + curWidth += stepSize; + tableau.push(tmp_tableau); + } + + return sendJSON({ + "type": "prediction", + "dataset": "mnist", + "data": tableau + }, (data) => { + data = JSON.parse(data); + if (data["status"] != 200) { + document.getElementById("result").innerHTML = "500 Internal Server Error"; + } else { + let resultat = document.getElementById("result"); + resultat.innerHTML = "Résultat:"; + let dict = {}; + let i = 0; + data["data"].map((e) => { dict[e] = i; i++; }); + let res = Object.keys(dict).sort().reverse(); + for (let j=0; j < res.length; j++) { + resultat.innerHTML += "
"+dict[res[j]]+" : "+res[j]; + } + } + }) +} \ No newline at end of file diff --git a/src/webserver/static/style.css b/src/webserver/static/style.css new file mode 100644 index 0000000..4794e71 --- /dev/null +++ b/src/webserver/static/style.css @@ -0,0 +1,4 @@ +#digit-canvas { + border: 2px solid #e0e0e0; + border-radius: 5px; +} \ No newline at end of file diff --git a/src/webserver/templates/index.html b/src/webserver/templates/index.html new file mode 100644 index 0000000..d27f8b5 --- /dev/null +++ b/src/webserver/templates/index.html @@ -0,0 +1,12 @@ + + + + + Home page< + + + + + MNIST + + \ No newline at end of file diff --git a/src/webserver/templates/mnist.html b/src/webserver/templates/mnist.html new file mode 100644 index 0000000..2174c8d --- /dev/null +++ b/src/webserver/templates/mnist.html @@ -0,0 +1,17 @@ + + + + + MNIST interaction + + + + +
+
+ + +

+
+ + \ No newline at end of file