Spaces:
Runtime error
Runtime error
Commit
•
c579946
1
Parent(s):
63c3845
added flash
Browse files- yolov5-flask-master/.github/FUNDING.yml +3 -0
- yolov5-flask-master/.gitignore +6 -0
- yolov5-flask-master/Dockerfile +12 -0
- yolov5-flask-master/LICENSE +21 -0
- yolov5-flask-master/README.md +75 -0
- yolov5-flask-master/docs/app_form.jpg +0 -0
- yolov5-flask-master/docs/app_result.jpg +0 -0
- yolov5-flask-master/requirements.txt +20 -0
- yolov5-flask-master/restapi.py +41 -0
- yolov5-flask-master/static/image0.jpg +0 -0
- yolov5-flask-master/static/pytorch.png +0 -0
- yolov5-flask-master/static/style.css +29 -0
- yolov5-flask-master/templates/index.html +52 -0
- yolov5-flask-master/tests/test_inference.py +17 -0
- yolov5-flask-master/tests/test_request.py +12 -0
- yolov5-flask-master/tests/zidane.jpg +0 -0
- yolov5-flask-master/webapp.py +56 -0
yolov5-flask-master/.github/FUNDING.yml
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# These are supported funding model platforms
|
2 |
+
|
3 |
+
github: robmarkcole
|
yolov5-flask-master/.gitignore
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
venv/
|
2 |
+
.vscode
|
3 |
+
*.pt
|
4 |
+
#*.jpg
|
5 |
+
static/tmp.jpg
|
6 |
+
*.pyc
|
yolov5-flask-master/Dockerfile
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.8-slim-buster
|
2 |
+
|
3 |
+
RUN apt-get update
|
4 |
+
RUN apt-get install ffmpeg libsm6 libxext6 -y
|
5 |
+
|
6 |
+
WORKDIR /app
|
7 |
+
ADD . /app
|
8 |
+
RUN pip install -r requirements.txt
|
9 |
+
|
10 |
+
EXPOSE 5000
|
11 |
+
|
12 |
+
CMD ["python", "restapi.py", "--port=5000"]
|
yolov5-flask-master/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2020 jzhang533
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
yolov5-flask-master/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Yolov5 object detection model deployment using flask
|
2 |
+
This repo contains example apps for exposing the [yolo5](https://github.com/ultralytics/yolov5) object detection model from [pytorch hub](https://pytorch.org/hub/ultralytics_yolov5/) via a [flask](https://flask.palletsprojects.com/en/1.1.x/) api/app.
|
3 |
+
|
4 |
+
## Web app
|
5 |
+
Simple app consisting of a form where you can upload an image, and see the inference result of the model in the browser. Run:
|
6 |
+
|
7 |
+
`$ python3 webapp.py --port 5000`
|
8 |
+
|
9 |
+
then visit http://localhost:5000/ in your browser:
|
10 |
+
|
11 |
+
<p align="center">
|
12 |
+
<img src="https://github.com/robmarkcole/yolov5-flask/blob/master/docs/app_form.jpg" width="450">
|
13 |
+
</p>
|
14 |
+
|
15 |
+
<p align="center">
|
16 |
+
<img src="https://github.com/robmarkcole/yolov5-flask/blob/master/docs/app_result.jpg" width="450">
|
17 |
+
</p>
|
18 |
+
|
19 |
+
## Rest API
|
20 |
+
Simple rest API exposing the model for consumption by another service. Run:
|
21 |
+
|
22 |
+
`$ python3 restapi.py --port 5000`
|
23 |
+
|
24 |
+
Then use [curl](https://curl.se/) to perform a request:
|
25 |
+
|
26 |
+
`$ curl -X POST -F image=@tests/zidane.jpg 'http://localhost:5000/v1/object-detection/yolov5s'`
|
27 |
+
|
28 |
+
The model inference results are returned:
|
29 |
+
|
30 |
+
```
|
31 |
+
[{'class': 0,
|
32 |
+
'confidence': 0.8197850585,
|
33 |
+
'name': 'person',
|
34 |
+
'xmax': 1159.1403808594,
|
35 |
+
'xmin': 750.912902832,
|
36 |
+
'ymax': 711.2583007812,
|
37 |
+
'ymin': 44.0350036621},
|
38 |
+
{'class': 0,
|
39 |
+
'confidence': 0.5667674541,
|
40 |
+
'name': 'person',
|
41 |
+
'xmax': 1065.5523681641,
|
42 |
+
'xmin': 116.0448303223,
|
43 |
+
'ymax': 713.8904418945,
|
44 |
+
'ymin': 198.4603881836},
|
45 |
+
{'class': 27,
|
46 |
+
'confidence': 0.5661227107,
|
47 |
+
'name': 'tie',
|
48 |
+
'xmax': 516.7975463867,
|
49 |
+
'xmin': 416.6880187988,
|
50 |
+
'ymax': 717.0524902344,
|
51 |
+
'ymin': 429.2020568848}]
|
52 |
+
```
|
53 |
+
|
54 |
+
An example python script to perform inference using [requests](https://docs.python-requests.org/en/master/) is given in `tests/test_request.py`
|
55 |
+
|
56 |
+
## Run & Develop locally
|
57 |
+
Run locally and dev:
|
58 |
+
* `python3 -m venv venv`
|
59 |
+
* `source venv/bin/activate`
|
60 |
+
* `(venv) $ pip install -r requirements.txt`
|
61 |
+
* `(venv) $ python3 webapp.py --port 5000`
|
62 |
+
|
63 |
+
## Docker
|
64 |
+
The example dockerfile shows how to expose the rest API:
|
65 |
+
```
|
66 |
+
# Build
|
67 |
+
docker build -t yolov5-flask .
|
68 |
+
# Run
|
69 |
+
docker run -p 5000:5000 yolov5-flask:latest
|
70 |
+
```
|
71 |
+
|
72 |
+
## reference
|
73 |
+
- https://github.com/ultralytics/yolov5
|
74 |
+
- https://github.com/jzhang533/yolov5-flask (this repo was forked from here)
|
75 |
+
- https://github.com/avinassh/pytorch-flask-api-heroku
|
yolov5-flask-master/docs/app_form.jpg
ADDED
![]() |
yolov5-flask-master/docs/app_result.jpg
ADDED
![]() |
yolov5-flask-master/requirements.txt
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
flask
|
2 |
+
requests
|
3 |
+
black
|
4 |
+
|
5 |
+
matplotlib>=3.2.2
|
6 |
+
numpy>=1.18.5
|
7 |
+
opencv-python>=4.1.2
|
8 |
+
Pillow
|
9 |
+
PyYAML>=5.3.1
|
10 |
+
scipy>=1.4.1
|
11 |
+
torch>=1.7.0
|
12 |
+
torchvision>=0.8.1
|
13 |
+
tqdm>=4.41.0
|
14 |
+
|
15 |
+
tensorboard>=2.4.1
|
16 |
+
|
17 |
+
seaborn>=0.11.0
|
18 |
+
pandas
|
19 |
+
|
20 |
+
thop # FLOPs computation
|
yolov5-flask-master/restapi.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Run a rest API exposing the yolov5s object detection model
|
3 |
+
"""
|
4 |
+
import argparse
|
5 |
+
import io
|
6 |
+
from PIL import Image
|
7 |
+
|
8 |
+
import torch
|
9 |
+
from flask import Flask, request
|
10 |
+
|
11 |
+
app = Flask(__name__)
|
12 |
+
|
13 |
+
DETECTION_URL = "/v1/object-detection/yolov5s"
|
14 |
+
|
15 |
+
|
16 |
+
@app.route(DETECTION_URL, methods=["POST"])
|
17 |
+
def predict():
|
18 |
+
if not request.method == "POST":
|
19 |
+
return
|
20 |
+
|
21 |
+
if request.files.get("image"):
|
22 |
+
image_file = request.files["image"]
|
23 |
+
image_bytes = image_file.read()
|
24 |
+
|
25 |
+
img = Image.open(io.BytesIO(image_bytes))
|
26 |
+
|
27 |
+
results = model(img, size=640)
|
28 |
+
data = results.pandas().xyxy[0].to_json(orient="records")
|
29 |
+
return data
|
30 |
+
|
31 |
+
|
32 |
+
if __name__ == "__main__":
|
33 |
+
parser = argparse.ArgumentParser(description="Flask api exposing yolov5 model")
|
34 |
+
parser.add_argument("--port", default=5000, type=int, help="port number")
|
35 |
+
args = parser.parse_args()
|
36 |
+
|
37 |
+
model = torch.hub.load(
|
38 |
+
"ultralytics/yolov5", "yolov5s", pretrained=True, force_reload=True
|
39 |
+
).autoshape() # force_reload = recache latest code
|
40 |
+
model.eval()
|
41 |
+
app.run(host="0.0.0.0", port=args.port) # debug=True causes Restarting with stat
|
yolov5-flask-master/static/image0.jpg
ADDED
![]() |
yolov5-flask-master/static/pytorch.png
ADDED
![]() |
yolov5-flask-master/static/style.css
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
html,
|
2 |
+
body {
|
3 |
+
height: 100%;
|
4 |
+
}
|
5 |
+
|
6 |
+
body {
|
7 |
+
display: -ms-flexbox;
|
8 |
+
display: flex;
|
9 |
+
-ms-flex-align: center;
|
10 |
+
align-items: center;
|
11 |
+
padding-top: 40px;
|
12 |
+
padding-bottom: 40px;
|
13 |
+
background-color: #f5f5f5;
|
14 |
+
}
|
15 |
+
|
16 |
+
.form-signin {
|
17 |
+
width: 100%;
|
18 |
+
max-width: 330px;
|
19 |
+
padding: 15px;
|
20 |
+
margin: auto;
|
21 |
+
}
|
22 |
+
|
23 |
+
.form-signin .form-control {
|
24 |
+
position: relative;
|
25 |
+
box-sizing: border-box;
|
26 |
+
height: auto;
|
27 |
+
padding: 10px;
|
28 |
+
font-size: 16px;
|
29 |
+
}
|
yolov5-flask-master/templates/index.html
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="utf-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
6 |
+
<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
|
7 |
+
<style>
|
8 |
+
.bd-placeholder-img {
|
9 |
+
font-size: 1.125rem;
|
10 |
+
text-anchor: middle;
|
11 |
+
}
|
12 |
+
|
13 |
+
@media (min-width: 768px) {
|
14 |
+
.bd-placeholder-img-lg {
|
15 |
+
font-size: 3.5rem;
|
16 |
+
}
|
17 |
+
}
|
18 |
+
</style>
|
19 |
+
<link rel="stylesheet" href="/static/style.css">
|
20 |
+
|
21 |
+
<title>yolov5 object detection</title>
|
22 |
+
</head>
|
23 |
+
<body class="text-center">
|
24 |
+
<form class="form-signin" method=post enctype=multipart/form-data>
|
25 |
+
<img class="mb-4" src="/static/pytorch.png" alt="" width="72">
|
26 |
+
<h1 class="h3 mb-3 font-weight-normal">Upload any image</h1>
|
27 |
+
<input type="file" name="file" class="form-control-file" id="inputfile">
|
28 |
+
<br/>
|
29 |
+
<button class="btn btn-lg btn-primary btn-block" type="submit">Upload</button>
|
30 |
+
<p class="mt-5 mb-3 text-muted">Built using Pytorch & Flask</p>
|
31 |
+
</form>
|
32 |
+
<script src="//code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
33 |
+
<script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
|
34 |
+
<script src="//stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
|
35 |
+
<script type="text/javascript">
|
36 |
+
$('#inputfile').bind('change', function() {
|
37 |
+
let fileSize = this.files[0].size/1024/1024; // this gives in MB
|
38 |
+
if (fileSize > 1) {
|
39 |
+
$("#inputfile").val(null);
|
40 |
+
alert('file is too big. images more than 1MB are not allowed')
|
41 |
+
return
|
42 |
+
}
|
43 |
+
|
44 |
+
let ext = $('#inputfile').val().split('.').pop().toLowerCase();
|
45 |
+
if($.inArray(ext, ['jpg','jpeg']) == -1) {
|
46 |
+
$("#inputfile").val(null);
|
47 |
+
alert('only jpeg/jpg files are allowed!');
|
48 |
+
}
|
49 |
+
});
|
50 |
+
</script>
|
51 |
+
</body>
|
52 |
+
</html>
|
yolov5-flask-master/tests/test_inference.py
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import io
|
2 |
+
import torch
|
3 |
+
from PIL import Image
|
4 |
+
|
5 |
+
# Model
|
6 |
+
model = torch.hub.load("ultralytics/yolov5", "yolov5s", pretrained=True, force_reload=True)
|
7 |
+
|
8 |
+
# img = Image.open("zidane.jpg") # PIL image direct open
|
9 |
+
|
10 |
+
# Read from bytes as we do in app
|
11 |
+
with open("zidane.jpg", "rb") as file:
|
12 |
+
img_bytes = file.read()
|
13 |
+
img = Image.open(io.BytesIO(img_bytes))
|
14 |
+
|
15 |
+
results = model(img, size=640) # includes NMS
|
16 |
+
|
17 |
+
print(results.pandas().xyxy[0])
|
yolov5-flask-master/tests/test_request.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Perform test request"""
|
2 |
+
import pprint
|
3 |
+
import requests
|
4 |
+
|
5 |
+
DETECTION_URL = "http://localhost:5000/v1/object-detection/yolov5s"
|
6 |
+
TEST_IMAGE = "zidane.jpg"
|
7 |
+
|
8 |
+
image_data = open(TEST_IMAGE, "rb").read()
|
9 |
+
|
10 |
+
response = requests.post(DETECTION_URL, files={"image": image_data}).json()
|
11 |
+
|
12 |
+
pprint.pprint(response)
|
yolov5-flask-master/tests/zidane.jpg
ADDED
![]() |
yolov5-flask-master/webapp.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Simple app to upload an image via a web form
|
3 |
+
and view the inference results on the image in the browser.
|
4 |
+
"""
|
5 |
+
import argparse
|
6 |
+
import io
|
7 |
+
import os
|
8 |
+
from PIL import Image
|
9 |
+
|
10 |
+
import torch
|
11 |
+
from flask import Flask, render_template, request, redirect
|
12 |
+
|
13 |
+
app = Flask(__name__)
|
14 |
+
|
15 |
+
|
16 |
+
@app.route("/", methods=["GET", "POST"])
|
17 |
+
def predict():
|
18 |
+
if request.method == "POST":
|
19 |
+
if "file" not in request.files:
|
20 |
+
return redirect(request.url)
|
21 |
+
file = request.files["file"]
|
22 |
+
if not file:
|
23 |
+
return
|
24 |
+
|
25 |
+
img_bytes = file.read()
|
26 |
+
img = Image.open(io.BytesIO(img_bytes))
|
27 |
+
results = model(img, size=640)
|
28 |
+
|
29 |
+
# for debugging
|
30 |
+
# data = results.pandas().xyxy[0].to_json(orient="records")
|
31 |
+
# return data
|
32 |
+
|
33 |
+
results.render() # updates results.imgs with boxes and labels
|
34 |
+
for img in results.imgs:
|
35 |
+
img_base64 = Image.fromarray(img)
|
36 |
+
img_base64.save("static/image0.jpg", format="JPEG")
|
37 |
+
return redirect("static/image0.jpg")
|
38 |
+
|
39 |
+
return render_template("index.html")
|
40 |
+
|
41 |
+
|
42 |
+
if __name__ == "__main__":
|
43 |
+
parser = argparse.ArgumentParser(description="Flask app exposing yolov5 models")
|
44 |
+
parser.add_argument("--port", default=5000, type=int, help="port number")
|
45 |
+
args = parser.parse_args()
|
46 |
+
|
47 |
+
model = torch.hub.load('../yolov5', 'custom', path='../saved_model/s1000_best.pt', source='local') # force_reload = recache latest code
|
48 |
+
model.eval()
|
49 |
+
app.run(host="0.0.0.0", port=args.port) # debug=True causes Restarting with stat
|
50 |
+
|
51 |
+
|
52 |
+
#python3 webapp.py --port 5000
|
53 |
+
|
54 |
+
|
55 |
+
# python webapp.py --port 5000
|
56 |
+
# http://localhost:5000/
|