├── .gitattributes ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── app.py ├── requirements.txt ├── static ├── pytorch.png └── style.css ├── templates └── index.html └── tests_scripts ├── readme_copy.md ├── restapi.py ├── test_inference.py ├── test_request.py └── zidane.jpg /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | .vscode 3 | *.pt 4 | #*.jpg 5 | static/tmp.jpg 6 | *.pyc 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 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"] 13 | COPY . . 14 | 15 | CMD [ "flask", "run", "--host=0.0.0.0"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yolov5 Object Detection on Live Stream Video via Flask 2 | ![framework](https://img.shields.io/badge/framework-flask-red) 3 | ![libraries](https://img.shields.io/badge/libraries-opencv-green) 4 | ![models](https://img.shields.io/badge/models-yolov5-yellow) 5 | 6 | The Yolov5s pretained model is deployed using flask. 7 | 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. 8 | 9 | 10 | 11 | ## Web app 12 | Simple app that enables live webcam detection using pretrained YOLOv5s weights and see real time inference result of the model in the browser. 13 | 14 | ![yolov5-real-time](https://user-images.githubusercontent.com/69728128/156182901-98c58df9-d23f-4e92-a4aa-7a9d9dc8ba67.JPG) 15 | 16 | ## Run & Develop locally 17 | Run locally and dev: 18 | * `conda create -n ` 19 | * `conda activate ` 20 | * `() $ pip install -r requirements.txt` 21 | * `() $ flask run` 22 | 23 | ## Docker 24 | The example dockerfile shows how to expose the rest API: 25 | ``` 26 | # Build 27 | docker build -t yolov5 . 28 | # Run 29 | docker run -p 5000:5000 yolov5-flask:latest 30 | ``` 31 | 32 | ## reference 33 | - https://github.com/ultralytics/yolov5 34 | - https://github.com/jzhang533/yolov5-flask 35 | - https://github.com/avinassh/pytorch-flask-api-heroku 36 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 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 | import cv2 10 | import numpy as np 11 | 12 | import torch 13 | from flask import Flask, render_template, request, redirect, Response 14 | 15 | app = Flask(__name__) 16 | 17 | 18 | #''' 19 | # Load Pre-trained Model 20 | model = torch.hub.load( 21 | "ultralytics/yolov5", "yolov5s", pretrained=True, force_reload=True 22 | )#.autoshape() # force_reload = recache latest code 23 | #''' 24 | # Load Custom Model 25 | #model = torch.hub.load("ultralytics/yolov5", "custom", path = "./best_damage.pt", force_reload=True) 26 | 27 | # Set Model Settings 28 | model.eval() 29 | model.conf = 0.6 # confidence threshold (0-1) 30 | model.iou = 0.45 # NMS IoU threshold (0-1) 31 | 32 | from io import BytesIO 33 | 34 | def gen(): 35 | cap=cv2.VideoCapture(0) 36 | # Read until video is completed 37 | while(cap.isOpened()): 38 | 39 | # Capture frame-by-fram ## read the camera frame 40 | success, frame = cap.read() 41 | if success == True: 42 | 43 | ret,buffer=cv2.imencode('.jpg',frame) 44 | frame=buffer.tobytes() 45 | 46 | #print(type(frame)) 47 | 48 | img = Image.open(io.BytesIO(frame)) 49 | results = model(img, size=640) 50 | #print(results) 51 | #print(results.pandas().xyxy[0]) 52 | #results.render() # updates results.imgs with boxes and labels 53 | results.print() # print results to screen 54 | #results.show() 55 | #print(results.imgs) 56 | #print(type(img)) 57 | #print(results) 58 | #plt.imshow(np.squeeze(results.render())) 59 | #print(type(img)) 60 | #print(img.mode) 61 | 62 | #convert remove single-dimensional entries from the shape of an array 63 | img = np.squeeze(results.render()) #RGB 64 | # read image as BGR 65 | img_BGR = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) #BGR 66 | 67 | #print(type(img)) 68 | #print(img.shape) 69 | #frame = img 70 | #ret,buffer=cv2.imencode('.jpg',img) 71 | #frame=buffer.tobytes() 72 | #print(type(frame)) 73 | #for img in results.imgs: 74 | #img = Image.fromarray(img) 75 | #ret,img=cv2.imencode('.jpg',img) 76 | #img=img.tobytes() 77 | 78 | #encode output image to bytes 79 | #img = cv2.imencode('.jpg', img)[1].tobytes() 80 | #print(type(img)) 81 | else: 82 | break 83 | #print(cv2.imencode('.jpg', img)[1]) 84 | 85 | #print(b) 86 | #frame = img_byte_arr 87 | 88 | # Encode BGR image to bytes so that cv2 will convert to RGB 89 | frame = cv2.imencode('.jpg', img_BGR)[1].tobytes() 90 | #print(frame) 91 | 92 | yield(b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') 93 | 94 | 95 | @app.route('/') 96 | def index(): 97 | 98 | return render_template('index.html') 99 | 100 | @app.route('/video') 101 | def video(): 102 | """Video streaming route. Put this in the src attribute of an img tag.""" 103 | 104 | return Response(gen(), 105 | mimetype='multipart/x-mixed-replace; boundary=frame') 106 | ''' 107 | @app.route('/video') 108 | def video(): 109 | return Response(generate_frames(),mimetype='multipart/x-mixed-replace; boundary=frame') 110 | ''' 111 | ''' 112 | @app.route("/", methods=["GET", "POST"]) 113 | def predict(): 114 | if request.method == "POST": 115 | if "file" not in request.files: 116 | return redirect(request.url) 117 | file = request.files["file"] 118 | if not file: 119 | return 120 | 121 | img_bytes = file.read() 122 | img = Image.open(io.BytesIO(img_bytes)) 123 | results = model(img, size=640) 124 | 125 | # for debugging 126 | # data = results.pandas().xyxy[0].to_json(orient="records") 127 | # return data 128 | 129 | results.render() # updates results.imgs with boxes and labels 130 | for img in results.imgs: 131 | img_base64 = Image.fromarray(img) 132 | img_base64.save("static/image0.jpg", format="JPEG") 133 | return redirect("static/image0.jpg") 134 | 135 | return render_template("index.html") 136 | ''' 137 | 138 | if __name__ == "__main__": 139 | parser = argparse.ArgumentParser(description="Flask app exposing yolov5 models") 140 | parser.add_argument("--port", default=5000, type=int, help="port number") 141 | args = parser.parse_args() 142 | ''' 143 | model = torch.hub.load( 144 | "ultralytics/yolov5", "yolov5s", pretrained=True, force_reload=True 145 | ).autoshape() # force_reload = recache latest code 146 | model.eval() 147 | ''' 148 | app.run(host="0.0.0.0", port=args.port) # debug=True causes Restarting with stat 149 | 150 | # Docker Shortcuts 151 | # docker build --tag yolov5 . 152 | # docker run --env="DISPLAY" --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" --device="/dev/video0:/dev/video0" yolov5 153 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 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 -------------------------------------------------------------------------------- /static/pytorch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngzhili/Yolov5-Real-Time-Object-Detection/61c33222bb085494dd48458face4c085c7e49b1a/static/pytorch.png -------------------------------------------------------------------------------- /static/style.css: -------------------------------------------------------------------------------- 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 | } 30 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | YOLOV5 Real Time Inference 63 | 81 | 82 | 83 | 84 |
85 |
86 | 87 |

Template for YOLOV5 Object Detection Model Real-Time Inference Using Web Cam

88 | 89 |

Built by Zhili

90 |
91 |
92 | 93 |
94 | 95 | 96 | 97 |
98 | 99 | 100 | -------------------------------------------------------------------------------- /tests_scripts/readme_copy.md: -------------------------------------------------------------------------------- 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 |

12 | 13 |

14 | 15 |

16 | 17 |

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 76 | -------------------------------------------------------------------------------- /tests_scripts/restapi.py: -------------------------------------------------------------------------------- 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 42 | -------------------------------------------------------------------------------- /tests_scripts/test_inference.py: -------------------------------------------------------------------------------- 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 | model = torch.hub.load("ultralytics/yolov5", "custom", path = "./best_damage.pt", force_reload=True) 8 | 9 | # img = Image.open("zidane.jpg") # PIL image direct open 10 | 11 | # Read from bytes as we do in app 12 | with open("1.jpg", "rb") as file: 13 | img_bytes = file.read() 14 | img = Image.open(io.BytesIO(img_bytes)) 15 | 16 | results = model(img, size=640) # includes NMS 17 | 18 | print(results.pandas().xyxy[0]) 19 | -------------------------------------------------------------------------------- /tests_scripts/test_request.py: -------------------------------------------------------------------------------- 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) 13 | -------------------------------------------------------------------------------- /tests_scripts/zidane.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngzhili/Yolov5-Real-Time-Object-Detection/61c33222bb085494dd48458face4c085c7e49b1a/tests_scripts/zidane.jpg --------------------------------------------------------------------------------