Initial commit
This commit is contained in:
29
app/app.py
Normal file
29
app/app.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from base64 import b64decode
|
||||
from flask import Flask, render_template, request
|
||||
import io
|
||||
from keras.preprocessing.image import img_to_array
|
||||
import model
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
app = Flask(__name__)
|
||||
HOST="0.0.0.0"
|
||||
PORT=3000
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
return render_template("index.html")
|
||||
|
||||
@app.route("/shape_model")
|
||||
def shape_model():
|
||||
encoded_img = request.args["img"]
|
||||
encoded_img = encoded_img.replace("data:image/png;base64,", "", 1)
|
||||
img = b64decode(encoded_img)
|
||||
img = Image.open(io.BytesIO(img))
|
||||
img = img.convert("L")
|
||||
img = img_to_array(img)
|
||||
prediction = model.run_model(img)
|
||||
return prediction
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(HOST, port=PORT)
|
||||
12
app/model.py
Normal file
12
app/model.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from keras import models, layers
|
||||
import numpy as np
|
||||
|
||||
model = models.load_model("model/shape_model.keras")
|
||||
labels = ["circle ○", "rectangle ▭", "square □", "triangle △"]
|
||||
def run_model(image):
|
||||
img = np.expand_dims(image, axis=0)
|
||||
prediction = np.argmax(model.predict(img))
|
||||
return labels[prediction]
|
||||
|
||||
if __name__=="__main__":
|
||||
print(run_model(input("Image path: ")))
|
||||
BIN
app/model/shape_model.keras
Normal file
BIN
app/model/shape_model.keras
Normal file
Binary file not shown.
43
app/requirements.txt
Normal file
43
app/requirements.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
absl-py==2.3.1
|
||||
astunparse==1.6.3
|
||||
blinker==1.9.0
|
||||
certifi==2025.10.5
|
||||
charset-normalizer==3.4.4
|
||||
click==8.3.0
|
||||
Flask==3.1.2
|
||||
flatbuffers==25.9.23
|
||||
gast==0.6.0
|
||||
google-pasta==0.2.0
|
||||
grpcio==1.76.0
|
||||
gunicorn==23.0.0
|
||||
h5py==3.15.1
|
||||
idna==3.11
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.6
|
||||
keras==3.11.3
|
||||
libclang==18.1.1
|
||||
Markdown==3.9
|
||||
markdown-it-py==4.0.0
|
||||
MarkupSafe==3.0.3
|
||||
mdurl==0.1.2
|
||||
ml_dtypes==0.5.3
|
||||
namex==0.1.0
|
||||
numpy==2.2.6
|
||||
opencv-python==4.12.0.88
|
||||
opt_einsum==3.4.0
|
||||
optree==0.17.0
|
||||
packaging==25.0
|
||||
pillow==12.0.0
|
||||
protobuf==6.33.0
|
||||
Pygments==2.19.2
|
||||
requests==2.32.5
|
||||
rich==14.2.0
|
||||
six==1.17.0
|
||||
tensorboard==2.20.0
|
||||
tensorboard-data-server==0.7.2
|
||||
tensorflow==2.20.0
|
||||
termcolor==3.1.0
|
||||
typing_extensions==4.15.0
|
||||
urllib3==2.5.0
|
||||
Werkzeug==3.1.3
|
||||
wrapt==2.0.0
|
||||
BIN
app/static/quentin.jpg
Normal file
BIN
app/static/quentin.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
57
app/static/script.js
Normal file
57
app/static/script.js
Normal file
@@ -0,0 +1,57 @@
|
||||
var c = document.getElementById("canvas");
|
||||
var ctx = c.getContext("2d");
|
||||
|
||||
var aiBox = document.getElementById("shapeBox");
|
||||
|
||||
var isDragging = false;
|
||||
|
||||
function draw(e){
|
||||
var canvas_width = 0.4 * document.documentElement.clientWidth;
|
||||
if (document.documentElement.clientWidth <= 1000){
|
||||
canvas_width = 0.8 * document.documentElement.clientWidth;
|
||||
}
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
|
||||
if (e.type.includes(`touch`)) {
|
||||
const { touches, changedTouches } = e.originalEvent ?? e;
|
||||
const touch = touches[0] ?? changedTouches[0];
|
||||
var posx = (touch.pageX - rect.left) * 64 / canvas_width;
|
||||
var posy = (touch.pageY - rect.top) * 64 / canvas_width;
|
||||
} else if (e.type.includes(`mouse`)) {
|
||||
var posx = (e.clientX - rect.left) * 64 / canvas_width;
|
||||
var posy = (e.clientY - rect.top) * 64 / canvas_width;
|
||||
}
|
||||
if (isDragging){
|
||||
ctx.fillStyle = "#000000";
|
||||
ctx.beginPath()
|
||||
ctx.arc(posx, posy, 1, 0, 2*Math.PI);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
function clear_canvas(){
|
||||
ctx.fillStyle = "#FFFFFF";
|
||||
ctx.beginPath();
|
||||
ctx.fillRect(0, 0, 64, 64);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
function send_image(){
|
||||
var img = c.toDataURL();
|
||||
const params = new URLSearchParams();
|
||||
params.append("img", img);
|
||||
fetch(`/shape_model?${params}`).then(
|
||||
function (r) {return r.text();}
|
||||
).then(
|
||||
function (r) {aiBox.innerHTML = r;}
|
||||
);
|
||||
}
|
||||
|
||||
clear_canvas();
|
||||
setInterval(send_image, 1000);
|
||||
c.addEventListener("mousemove", draw);
|
||||
c.addEventListener('touchmove', draw);
|
||||
c.addEventListener('mousedown', function(e){isDragging = true;});
|
||||
c.addEventListener('touchstart', function(e){isDragging = true;});
|
||||
c.addEventListener('mouseup', function(e){isDragging = false;});
|
||||
c.addEventListener('touchend', function(e){isDragging = false;});
|
||||
60
app/static/style.css
Normal file
60
app/static/style.css
Normal file
@@ -0,0 +1,60 @@
|
||||
html, body{
|
||||
margin: 0px;
|
||||
font-family: "Lexend", sans-serif;
|
||||
font-weight: 300;
|
||||
overscroll-behavior-y: contain;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
#textArea{
|
||||
background: #f8f8ff;
|
||||
width: 40vw;
|
||||
height: 100vh;
|
||||
float: left;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
#drawingArea{
|
||||
width: 60vw;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#canvas{
|
||||
border-style: solid;
|
||||
border-width: 4px;
|
||||
margin-left: 10vw;
|
||||
margin-right: 10vw;
|
||||
margin-top: calc(50vh - 20vw);
|
||||
width:40vw;
|
||||
height:40vw;
|
||||
}
|
||||
#quentinImg{
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
width: auto;
|
||||
border-radius: 30%;
|
||||
}
|
||||
h1{
|
||||
font-size: 64px;
|
||||
}
|
||||
h2{
|
||||
font-size: 48px;
|
||||
}
|
||||
p{
|
||||
font-size: 24px;
|
||||
}
|
||||
@media only screen and (max-width:1000px) {
|
||||
#textArea{
|
||||
width: 100vw;
|
||||
height: 20vh;
|
||||
}
|
||||
#drawingArea{
|
||||
width: 100vw;
|
||||
height: 80vh;
|
||||
}
|
||||
#canvas{
|
||||
width: 80vw;
|
||||
height: 80vw;
|
||||
margin-top: calc(40vh - 40vw);
|
||||
}
|
||||
}
|
||||
24
app/templates/index.html
Normal file
24
app/templates/index.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>hello @quentinbkk i have found ur github :D</title>
|
||||
<link href="/static/style.css" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lexend:wght@100..900&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="textArea">
|
||||
<div>
|
||||
<h1>Shape AI</h1>
|
||||
<h2>I see a <span id="shapeBox">...</span></h2>
|
||||
<p>Based on <a href="https://github.com/quentinbkk/shapeAI">ShapeAI</a> by <a href="https://github.com/quentinbkk">@quentinbkk</a> <img id="quentinImg" src="/static/quentin.jpg"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="drawingArea">
|
||||
<canvas id="canvas" id="canvas", width=64px, height=64px></canvas><br>
|
||||
<center><h3 onclick="clear_canvas()">Reset</h3></center>
|
||||
</div>
|
||||
</body>
|
||||
<script src="/static/script.js"></script>
|
||||
</html>
|
||||
Reference in New Issue
Block a user