initial commit
This commit is contained in:
commit
29a75fa442
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
env
|
||||
.git
|
||||
**/__pycache__
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
env
|
||||
**/__pycache__
|
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@ -0,0 +1,16 @@
|
||||
FROM python:3
|
||||
|
||||
ENV PYTHONUNBUFFERED=value
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY requirements.txt ./
|
||||
|
||||
RUN pip install --no-cache-dir --extra-index-url https://download.pytorch.org/whl/cpu -r requirements.txt
|
||||
|
||||
RUN apt update && apt install -y ffmpeg libsm6 libxext6
|
||||
|
||||
COPY src .
|
||||
COPY best.pt /best.pt
|
||||
|
||||
CMD [ "gunicorn", "-b 0.0.0.0:80", "app:app" ]
|
8
docker-compose.yml
Normal file
8
docker-compose.yml
Normal file
@ -0,0 +1,8 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
alv.cx-glass:
|
||||
build: .
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 8759:8000
|
40
index.html
Normal file
40
index.html
Normal file
@ -0,0 +1,40 @@
|
||||
<img id="img" src="" />
|
||||
|
||||
<p id="statusline">submit an image to get started!</p>
|
||||
<form id="form" action="http://localhost:5000/submit" method="post">
|
||||
<input type="file" id="job" name="image" accept="image/png,image/jpeg" /><br>
|
||||
<button type="submit" id="submit">submit</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const form = document.getElementById("form");
|
||||
const img = document.getElementById("img");
|
||||
const statusline = document.getElementById("statusline");
|
||||
|
||||
async function handleSubmit(ev) {
|
||||
ev.preventDefault();
|
||||
const form = ev.currentTarget;
|
||||
const data = new FormData(form);
|
||||
const url = form.action;
|
||||
const fetchOptions = {
|
||||
method: form.method,
|
||||
body: data
|
||||
}
|
||||
|
||||
const resp = await fetch(url, fetchOptions);
|
||||
const id = (await resp.json()).id;
|
||||
console.log(id);
|
||||
|
||||
while (true) {
|
||||
const resp = await fetch('http://localhost:5000/state/' + id);
|
||||
const state = (await resp.json()).state;
|
||||
statusline.innerHTML = state;
|
||||
if (state == "READY") {
|
||||
img.src = 'http://localhost:5000/result/' + id
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form.addEventListener('submit', handleSubmit)
|
||||
</script>
|
45
requirements.txt
Normal file
45
requirements.txt
Normal file
@ -0,0 +1,45 @@
|
||||
blinker==1.7.0
|
||||
certifi==2022.12.7
|
||||
charset-normalizer==2.1.1
|
||||
click==8.1.7
|
||||
contourpy==1.2.0
|
||||
cycler==0.12.1
|
||||
filelock==3.9.0
|
||||
Flask==3.0.0
|
||||
fonttools==4.47.0
|
||||
fsspec==2023.4.0
|
||||
gunicorn==21.2.0
|
||||
idna==3.4
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.2
|
||||
kiwisolver==1.4.5
|
||||
MarkupSafe==2.1.3
|
||||
matplotlib==3.8.2
|
||||
mpmath==1.3.0
|
||||
networkx==3.0
|
||||
numpy==1.24.1
|
||||
opencv-python==4.8.1.78
|
||||
packaging==23.2
|
||||
pandas==2.1.4
|
||||
Pillow==9.3.0
|
||||
psutil==5.9.7
|
||||
py-cpuinfo==9.0.0
|
||||
pyparsing==3.1.1
|
||||
python-dateutil==2.8.2
|
||||
pytz==2023.3.post1
|
||||
PyYAML==6.0.1
|
||||
requests==2.28.1
|
||||
scipy==1.11.4
|
||||
seaborn==0.13.0
|
||||
six==1.16.0
|
||||
sympy==1.12
|
||||
thop==0.1.1.post2209072238
|
||||
torch==2.1.2+cpu
|
||||
torchaudio==2.1.2+cpu
|
||||
torchvision==0.16.2+cpu
|
||||
tqdm==4.66.1
|
||||
typing_extensions==4.4.0
|
||||
tzdata==2023.3
|
||||
ultralytics==8.0.230
|
||||
urllib3==1.26.13
|
||||
Werkzeug==3.0.1
|
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
90
src/app.py
Executable file
90
src/app.py
Executable file
@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import uuid
|
||||
import json
|
||||
import time
|
||||
import cv2
|
||||
import queue
|
||||
from pathlib import Path as P
|
||||
from PIL import Image
|
||||
from flask import Flask, request, send_from_directory
|
||||
from flask_cors import CORS
|
||||
from threading import Thread
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
QUEUE_STATE_IN_QUEUE = 'IN_QUEUE'
|
||||
QUEUE_STATE_PROCESSING = 'PROCESSING'
|
||||
QUEUE_STATE_READY = 'READY'
|
||||
|
||||
q = queue.Queue(maxsize=1000)
|
||||
state = {}
|
||||
root = P("./q").absolute()
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def process():
|
||||
print("importing ultralytics")
|
||||
from ultralytics import YOLO
|
||||
print("creating model")
|
||||
model = YOLO(os.environ.get("MODEL_PATH", "/best.pt"))
|
||||
print("created model")
|
||||
last = 0
|
||||
while True:
|
||||
if time.time() < last + 10:
|
||||
print("aa")
|
||||
time.sleep(0.5)
|
||||
continue
|
||||
|
||||
print("BB")
|
||||
|
||||
print("waiting for/getting job")
|
||||
job = q.get()
|
||||
filepath = root.joinpath(job)
|
||||
print("got job")
|
||||
state[job] = {
|
||||
'state': QUEUE_STATE_PROCESSING,
|
||||
}
|
||||
print(f"processing job: {job=}")
|
||||
img = cv2.imread(str(filepath))
|
||||
results = model.predict(img)
|
||||
|
||||
for r in results:
|
||||
im_array = r.plot()
|
||||
im = Image.fromarray(im_array[..., ::-1])
|
||||
im.save(str(filepath.with_suffix(".png")))
|
||||
state[job] = {
|
||||
'state': QUEUE_STATE_READY,
|
||||
}
|
||||
|
||||
filepath.unlink()
|
||||
|
||||
|
||||
@app.route("/submit", methods=['POST'])
|
||||
def render():
|
||||
id = str(uuid.uuid4())
|
||||
request.files['image'].save(root.joinpath(id))
|
||||
q.put(id)
|
||||
state[id] = {'state': QUEUE_STATE_IN_QUEUE}
|
||||
return json.dumps({'id': id})
|
||||
|
||||
|
||||
@app.route("/state/<id>")
|
||||
def get_state(id):
|
||||
return json.dumps(state[id])
|
||||
|
||||
|
||||
@app.route("/result/<i>")
|
||||
def get_result(i):
|
||||
print(root)
|
||||
print(i + '.png')
|
||||
return send_from_directory(root, i + '.png')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
p = Thread(target=process)
|
||||
p.start()
|
||||
app.run(debug=True)
|
||||
p.join()
|
Loading…
Reference in New Issue
Block a user