Add webserver

This commit is contained in:
augustin64 2022-04-11 18:00:16 +02:00
parent b93d1475e0
commit 1d4b56a14f
6 changed files with 275 additions and 1 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
**/a.out
.cache/*
.test-cache/*
**/__pycache__

71
src/webserver/app.py Normal file
View File

@ -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')

View File

@ -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 += "<br/>"+dict[res[j]]+" : "+res[j];
}
}
})
}

View File

@ -0,0 +1,4 @@
#digit-canvas {
border: 2px solid #e0e0e0;
border-radius: 5px;
}

View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Home page<</title>
<link href="{{ url_for('static',filename='style.css') }}" rel="stylesheet">
</head>
<body>
<a href="/mnist">MNIST</a>
</body>
</html>

View File

@ -0,0 +1,17 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>MNIST interaction</title>
<link href="{{ url_for('static',filename='style.css') }}" rel="stylesheet">
<script src="{{ url_for('static',filename='script.js') }}"></script>
</head>
<body onload="init()">
<div id="maincard">
<canvas id="digit-canvas" width="280" height="280"></canvas><br/>
<button id="clear">Clear</button>
<button id="predict" onclick="getPrediction()">Prédire</button>
</div><br/>
<div id="result"></div>
</body>
</html>