Skip to content

Commit 79daaf8

Browse files
committed
yolov8
1 parent 23641d0 commit 79daaf8

18 files changed

+1483
-0
lines changed

.vscode/PythonImportHelper-v2-Completion.json

Lines changed: 578 additions & 0 deletions
Large diffs are not rendered by default.

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
6+
7+
## [2023.1.31]
8+
### ADD
9+
- Start Repo

Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM tiangolo/uvicorn-gunicorn:python3.10
2+
3+
RUN apt update && \
4+
apt install -y htop libgl1-mesa-glx libglib2.0-0
5+
6+
COPY requirements.txt /tmp/requirements.txt
7+
RUN pip install --no-cache-dir -r /tmp/requirements.txt

README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,104 @@
11
# yolov8-fastapi
22
FastAPI for YOLOv8 (Object Detection) in Docker. Project template.
3+
4+
### Sample
5+
6+
<img width=600 src="./tests/res/fastapi_sample.png" alt="">
7+
8+
## What's inside:
9+
- YOLOv8
10+
- FastAPI
11+
- Docker
12+
- Python
13+
- PyTorch
14+
15+
16+
---
17+
# Getting Start
18+
```
19+
docker-compose up
20+
```
21+
22+
## FAST API Docs url:
23+
http://0.0.0.0:8008/docs#/
24+
25+
<img width=600 src="./tests/res/fastapi.png" alt="FAST API">
26+
27+
---
28+
# 🚀 Code Examples
29+
start the service before starting the tests
30+
> uvicorn main:app --reload --host 0.0.0.0 --port 8008
31+
*you can change the address and port in the file **docker-compose.yaml***
32+
### To value
33+
```python
34+
import requests
35+
36+
input_image_name = 'test_image.jpg'
37+
api_host = 'http://0.0.0.0:8008/'
38+
type_rq = 'img_object_detection_to_json'
39+
40+
files = {'file': open(input_image_name, 'rb')}
41+
42+
response = requests.post(api_host+type_rq, files=files)
43+
44+
data = response.json()
45+
print(data)
46+
```
47+
Output:
48+
```
49+
{'detect_objects': [{'name': 'cat', 'confidence': 0.926225245}, {'name': 'dog', 'confidence': 0.9109069705}], 'detect_objects_names': 'cat, dog'}
50+
```
51+
52+
### To Image
53+
```python
54+
import requests
55+
from PIL import Image
56+
from io import BytesIO
57+
import matplotlib.pyplot as plt
58+
59+
input_image_name = 'test_image.jpg'
60+
api_host = 'http://0.0.0.0:8008/'
61+
type_rq = 'img_object_detection_to_img'
62+
63+
files = {'file': open(input_image_name, 'rb')}
64+
65+
response = requests.post(api_host+type_rq, files=files)
66+
67+
img = Image.open(BytesIO(response.content))
68+
plt.imshow(img)
69+
```
70+
71+
More examples in the notebook [test.ipynb](./tests/test.ipynb)
72+
73+
---
74+
75+
# Overview of the code
76+
[main.py](./main.py) Base FASTAPI functions
77+
[app.py](./app.py) YoloV8 functions
78+
[./models](./models) YoloV8 models folder
79+
80+
---
81+
# Test
82+
I wrote functional tests for the program to check the operation of the service
83+
84+
first install the necessary environment or run the docker container
85+
```
86+
pip install -r requirements.txt
87+
```
88+
89+
then just run the tests from the program directory
90+
```
91+
pytest -v --disable-warnings
92+
```
93+
94+
if all is well, you will get the following result:
95+
<img width=600 src="./tests/res/tests.png" alt="">
96+
97+
Also added a jupyter notebook with visualization [test.ipynb](./tests/test.ipynb)
98+
99+
100+
---
101+
102+
# Contact
103+
104+
[Telegram Group](https://t.me/automlalex)

__init__.py

Whitespace-only changes.

app.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
from PIL import Image
2+
import io
3+
import pandas as pd
4+
import numpy as np
5+
6+
from typing import Optional
7+
8+
from ultralytics import YOLO
9+
from ultralytics.yolo.utils.plotting import Annotator, colors
10+
11+
12+
# Initialize the models
13+
model_sample_model = YOLO("./models/sample_model/yolov8n.pt")
14+
15+
16+
def get_image_from_bytes(binary_image: bytes) -> Image:
17+
"""Convert image from bytes to PIL RGB format
18+
19+
Args:
20+
binary_image (bytes): The binary representation of the image
21+
22+
Returns:
23+
PIL.Image: The image in PIL RGB format
24+
"""
25+
input_image = Image.open(io.BytesIO(binary_image)).convert("RGB")
26+
return input_image
27+
28+
29+
def get_bytes_from_image(image: Image) -> bytes:
30+
"""
31+
Convert PIL image to Bytes
32+
33+
Args:
34+
image (Image): A PIL image instance
35+
36+
Returns:
37+
bytes : BytesIO object that contains the image in JPEG format with quality 85
38+
"""
39+
return_image = io.BytesIO()
40+
image.save(return_image, format='JPEG', quality=85) # save the image in JPEG format with quality 85
41+
return_image.seek(0) # set the pointer to the beginning of the file
42+
return return_image
43+
44+
def transform_predict_to_df(results: list, labeles_dict: dict) -> pd.DataFrame:
45+
"""
46+
Transform predict from yolov8 (torch.Tensor) to pandas DataFrame.
47+
48+
Args:
49+
results (list): A list containing the predict output from yolov8 in the form of a torch.Tensor.
50+
labeles_dict (dict): A dictionary containing the labels names, where the keys are the class ids and the values are the label names.
51+
52+
Returns:
53+
predict_bbox (pd.DataFrame): A DataFrame containing the bounding box coordinates, confidence scores and class labels.
54+
"""
55+
# Transform the Tensor to numpy array
56+
predict_bbox = pd.DataFrame(results[0].to("cpu").numpy().boxes.xyxy, columns=['xmin', 'ymin', 'xmax','ymax'])
57+
# Add the confidence of the prediction to the DataFrame
58+
predict_bbox['confidence'] = results[0].to("cpu").numpy().boxes.conf
59+
# Add the class of the prediction to the DataFrame
60+
predict_bbox['class'] = (results[0].to("cpu").numpy().boxes.cls).astype(int)
61+
# Replace the class number with the class name from the labeles_dict
62+
predict_bbox['name'] = predict_bbox["class"].replace(labeles_dict)
63+
return predict_bbox
64+
65+
def get_model_predict(model: YOLO, input_image: Image, save: bool = False, image_size: int = 1248, conf: float = 0.5, augment: bool = False) -> pd.DataFrame:
66+
"""
67+
Get the predictions of a model on an input image.
68+
69+
Args:
70+
model (YOLO): The trained YOLO model.
71+
input_image (Image): The image on which the model will make predictions.
72+
save (bool, optional): Whether to save the image with the predictions. Defaults to False.
73+
image_size (int, optional): The size of the image the model will receive. Defaults to 1248.
74+
conf (float, optional): The confidence threshold for the predictions. Defaults to 0.5.
75+
augment (bool, optional): Whether to apply data augmentation on the input image. Defaults to False.
76+
77+
Returns:
78+
pd.DataFrame: A DataFrame containing the predictions.
79+
"""
80+
# Make predictions
81+
predictions = model.predict(
82+
imgsz=image_size,
83+
source=input_image,
84+
conf=conf,
85+
save=save,
86+
augment=augment,
87+
flipud= 0.0,
88+
fliplr= 0.0,
89+
mosaic = 0.0,
90+
)
91+
92+
# Transform predictions to pandas dataframe
93+
predictions = transform_predict_to_df(predictions, model.model.names)
94+
return predictions
95+
96+
97+
################################# BBOX Func #####################################
98+
99+
def add_bboxs_on_img(image: Image, predict: pd.DataFrame()) -> Image:
100+
"""
101+
add a bounding box on the image
102+
103+
Args:
104+
image (Image): input image
105+
predict (pd.DataFrame): predict from model
106+
107+
Returns:
108+
Image: image whis bboxs
109+
"""
110+
# Create an annotator object
111+
annotator = Annotator(np.array(image))
112+
113+
# sort predict by xmin value
114+
predict = predict.sort_values(by=['xmin'], ascending=True)
115+
116+
# iterate over the rows of predict dataframe
117+
for i, row in predict.iterrows():
118+
# create the text to be displayed on image
119+
text = f"{row['name']}: {int(row['confidence']*100)}%"
120+
# get the bounding box coordinates
121+
bbox = [row['xmin'], row['ymin'], row['xmax'], row['ymax']]
122+
# add the bounding box and text on the image
123+
annotator.box_label(bbox, text, color=colors(row['class'], True))
124+
# convert the annotated image to PIL image
125+
return Image.fromarray(annotator.result())
126+
127+
128+
################################# Models #####################################
129+
130+
131+
def detect_sample_model(input_image: Image) -> pd.DataFrame:
132+
"""
133+
Predict from sample_model.
134+
Base on YoloV8
135+
136+
Args:
137+
input_image (Image): The input image.
138+
139+
Returns:
140+
pd.DataFrame: DataFrame containing the object location.
141+
"""
142+
predict = get_model_predict(
143+
model=model_sample_model,
144+
input_image=input_image,
145+
save=False,
146+
image_size=640,
147+
augment=False,
148+
conf=0.5,
149+
)
150+
return predict

docker-compose.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
version: '3'
2+
services:
3+
yolov8_fastapi:
4+
build: .
5+
restart: "always"
6+
volumes:
7+
- ./:/app
8+
working_dir: /app
9+
ports:
10+
- "8001:8001"
11+
command: uvicorn main:app --reload --host 0.0.0.0 --port 8001

0 commit comments

Comments
 (0)