osrokas commited on
Commit
be14aa6
1 Parent(s): 51533ea
.dockerignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .venv
2
+ .vscode
3
+ data
4
+ __pycache__
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .venv
2
+ .vscode
3
+ data
4
+ __pycache__
Dockerfile ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /code
4
+
5
+ COPY ./requirements.txt /code/requirements.txt
6
+
7
+ RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
8
+
9
+ RUN pip install torch==1.9.0+cpu torchvision==0.10.0+cpu -f https://download.pytorch.org/whl/torch_stable.html
10
+
11
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
12
+
13
+ COPY . /code/
14
+
15
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
main.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Import Fast API
2
+ from fastapi import FastAPI, Request, UploadFile, File
3
+ from fastapi.templating import Jinja2Templates
4
+ from fastapi.responses import StreamingResponse
5
+
6
+ # Import bytes
7
+ from io import BytesIO
8
+ import os
9
+
10
+ # Import logging
11
+ import logging
12
+
13
+ # Import utilities
14
+ from src.utils.utils import IMAGE_FORMATS
15
+
16
+ # Import machine learning
17
+ from src.predict import predict
18
+ from ultralytics import YOLO
19
+ from huggingface_hub import hf_hub_download
20
+
21
+
22
+ # Initialazing FastAPI application
23
+ app = FastAPI()
24
+
25
+ # Initialazing templates
26
+ templates = Jinja2Templates(directory="templates")
27
+
28
+ # Initialazing logger
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ logger.info(f"Loading YOLO model...")
33
+
34
+ # Download YOLO model from Hugging Face Hub
35
+ model_path = hf_hub_download(
36
+ repo_id="arnabdhar/YOLOv8-Face-Detection", filename="model.pt"
37
+ )
38
+
39
+
40
+ # Load YOLO model
41
+ model = YOLO(model_path)
42
+
43
+
44
+ # Index route
45
+ @app.get("/")
46
+ async def root(request: Request):
47
+ context = {"request": request}
48
+ # Render index.html
49
+ return templates.TemplateResponse("index.html", context)
50
+
51
+
52
+ # Upload images decorator
53
+ @app.post("/predict-img")
54
+ def predict_image(file: UploadFile = File(...)):
55
+ try:
56
+ # Try to read the file
57
+ contents = file.file.read()
58
+
59
+ # Open file and write contents
60
+ with open(file.filename, "wb") as f:
61
+ f.write(contents)
62
+
63
+ # Get image filename
64
+ image = file.filename
65
+
66
+ # Check if image format is valid
67
+ if not image.endswith(IMAGE_FORMATS):
68
+ # If not, raise an error
69
+ raise ValueError("Invalid image format")
70
+
71
+ except Exception as e:
72
+ # If there is an error, return the error
73
+ return {f"{e}"}
74
+
75
+ finally:
76
+ file.file.close()
77
+
78
+ # Getting image path
79
+ image = file.filename
80
+
81
+ # Predicting
82
+ results = predict(model, image)
83
+
84
+ # TODO
85
+ # extract extension from image and use it to save the image
86
+ # Convert image to bytes
87
+ img_bytes = BytesIO()
88
+ results.save(img_bytes, "JPEG")
89
+ img_bytes.seek(0)
90
+
91
+ # Removing the image
92
+ os.remove(image)
93
+
94
+ # Render image
95
+ return StreamingResponse(content=img_bytes, media_type="image/jpeg")
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi
2
+ pillow
3
+ ultralytics
4
+ huggingface-hub
5
+
6
+
src/__init__.py ADDED
File without changes
src/predict.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Import machine learning
2
+ from ultralytics import YOLO
3
+
4
+ # Import computer vision
5
+ import cv2
6
+ from PIL import Image
7
+
8
+
9
+ def predict(detection_model: YOLO, image_path: str) -> Image:
10
+ """
11
+ The function predicts with the given model and returns the image with the bounding boxes
12
+
13
+ Args:
14
+ detection_model (YOLO): The YOLO model
15
+ image_path (str): The path to the image
16
+
17
+ Returns:
18
+ Image: The image with the bounding boxes
19
+ """
20
+ # Read the image
21
+ img = cv2.imread(image_path)
22
+
23
+ # Predict with the model
24
+ output = detection_model.predict(image_path, verbose=False)
25
+
26
+ # If output is not None then draw the bounding boxes
27
+ if output:
28
+ # Loop through the output
29
+ for bbox in output:
30
+ for i, p in enumerate(bbox):
31
+
32
+ # Converting the p to cpu
33
+ p = p.to("cpu")
34
+
35
+ # Conveting to numpy
36
+ p = p.numpy()
37
+
38
+ # Extracting the coords
39
+ coords = p.boxes.xyxy[0]
40
+
41
+ # Extracting the coords
42
+ xmin = coords[0]
43
+ ymin = coords[1]
44
+ xmax = coords[2]
45
+ ymax = coords[3]
46
+
47
+ # Converting to int
48
+ xmin = int(xmin)
49
+ ymin = int(ymin)
50
+ xmax = int(xmax)
51
+ ymax = int(ymax)
52
+
53
+ # Extracting the prob
54
+ prob = p.boxes.conf[0]
55
+
56
+ # Drawing the bounding box
57
+ cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)
58
+
59
+ # Drawing the text with the probability
60
+ cv2.putText(
61
+ img,
62
+ f"{prob:.2f}",
63
+ (xmin, ymin - 10),
64
+ cv2.FONT_HERSHEY_SIMPLEX,
65
+ 0.9,
66
+ (36, 255, 12),
67
+ 2,
68
+ )
69
+
70
+ # Converting the image to RGB
71
+ img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
72
+
73
+ # Converting the image to PIL Image object
74
+ pil_image = Image.fromarray(img_rgb)
75
+
76
+ # Return the image
77
+ return pil_image
78
+
79
+ # If output is None then return the original image
80
+ return Image.open(image_path)
src/utils/__init__.py ADDED
File without changes
src/utils/utils.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ IMAGE_FORMATS = (
2
+ "jpg",
3
+ "jpeg",
4
+ "png",
5
+ "bmp",
6
+ "gif",
7
+ "tiff",
8
+ "webp",
9
+ "JPG",
10
+ "JPEG",
11
+ "PNG",
12
+ "BMP",
13
+ "GIF",
14
+ "TIFF",
15
+ "WEBP",
16
+ )
templates/index.html ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <!-- Required meta tags -->
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1, shrink-to-fit=no"
10
+ />
11
+
12
+ <!-- Bootstrap CSS -->
13
+ <link
14
+ href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
15
+ rel="stylesheet"
16
+ integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
17
+ crossorigin="anonymous"
18
+ />
19
+
20
+ <script
21
+ src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
22
+ integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
23
+ crossorigin="anonymous"
24
+ ></script>
25
+
26
+ <title>Hello, world!</title>
27
+ </head>
28
+ <body>
29
+ <div class="w-25 p-4 mx-auto">
30
+ <form method="post" action="/predict-img" enctype="multipart/form-data">
31
+
32
+ <input
33
+ class="form-control"
34
+ type="file"
35
+ id="formFileMultiple"
36
+ multiple
37
+ name="file"
38
+ />
39
+ <button type="submit" class="btn btn-primary">
40
+ Upload files
41
+ </button>
42
+ </div>
43
+ </form>
44
+ <!-- Optional JavaScript; choose one of the two! -->
45
+
46
+ <!-- Option 1: Bootstrap Bundle with Popper -->
47
+
48
+ <!-- Option 2: Separate Popper and Bootstrap JS -->
49
+
50
+ <script
51
+ src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js"
52
+ integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p"
53
+ crossorigin="anonymous"
54
+ ></script>
55
+ <script
56
+ src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
57
+ integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF"
58
+ crossorigin="anonymous"
59
+ ></script>
60
+ </body>
61
+ </html>
tests/__init__.py ADDED
File without changes
tests/integration/__init__.py ADDED
File without changes
tests/pipeline/__init__.py ADDED
File without changes
tests/unit/__init__.py ADDED
File without changes