mirror of
https://github.com/augustin64/projet-tipe
synced 2025-04-22 21:23:53 +02:00
Add webserver
This commit is contained in:
parent
b93d1475e0
commit
1d4b56a14f
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
|||||||
**/a.out
|
**/a.out
|
||||||
.cache/*
|
.cache/*
|
||||||
.test-cache/*
|
.test-cache/*
|
||||||
|
**/__pycache__
|
71
src/webserver/app.py
Normal file
71
src/webserver/app.py
Normal 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')
|
169
src/webserver/static/script.js
Normal file
169
src/webserver/static/script.js
Normal 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
4
src/webserver/static/style.css
Normal file
4
src/webserver/static/style.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#digit-canvas {
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
12
src/webserver/templates/index.html
Normal file
12
src/webserver/templates/index.html
Normal 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>
|
17
src/webserver/templates/mnist.html
Normal file
17
src/webserver/templates/mnist.html
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user