KewinLimaSilva commited on
Commit
fdc027e
·
verified ·
1 Parent(s): d08c426

Upload Dockerfile and main.py

Browse files
Files changed (2) hide show
  1. Dockerfile +21 -0
  2. main.py +135 -0
Dockerfile ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Usa uma imagem oficial do Python otimizada
2
+ FROM python:3.11-slim
3
+
4
+ # Instala dependências do Linux necessárias para o OpenCV e YOLO rodarem sem quebrar
5
+ RUN apt-get update && apt-get install -y \
6
+ libgl1-mesa-glx \
7
+ libglib2.0-0 \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Cria e define a pasta de trabalho dentro do servidor
11
+ WORKDIR /code
12
+
13
+ # Copia o arquivo de requisitos e instala as bibliotecas
14
+ COPY ./requirements.txt /code/requirements.txt
15
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
16
+
17
+ # Copia todo o resto do seu código e a pasta weights para o servidor
18
+ COPY . .
19
+
20
+ # Comando padrão para iniciar a API dentro do Hugging Face usando a porta 7860
21
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
main.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from ultralytics import YOLO
4
+ from PIL import Image
5
+ import io
6
+ import numpy as np
7
+
8
+ app = FastAPI(title="YOLO Custom API - Gadoline")
9
+
10
+ # Configuração de CORS para permitir requisições da sua aplicação Web
11
+ app.add_middleware(
12
+ CORSMiddleware,
13
+ allow_origins=["*"],
14
+ allow_credentials=True,
15
+ allow_methods=["*"],
16
+ allow_headers=["*"],
17
+ )
18
+
19
+ # =========================================================
20
+ # CONFIGURAÇÕES ADAPTADAS DO SEU COLAB
21
+ # =========================================================
22
+ # Lembre-se de colocar o arquivo "best.pt" dentro da pasta weights do seu projeto!
23
+ MODEL_PATH = "weights/best.pt"
24
+ CONFIDENCE_THRESHOLD = 0.1
25
+ IMG_SIZE = 640
26
+ MIN_SIZE = 5
27
+ MAX_SIZE = 500
28
+ IOU_THRESHOLD = 0.70
29
+
30
+ # Carrega o modelo localmente
31
+ model = YOLO(MODEL_PATH)
32
+
33
+ # =========================================================
34
+ # FUNÇÕES MATEMÁTICAS (IoU e Merge)
35
+ # =========================================================
36
+ def calculate_iou(boxA, boxB):
37
+ xA = max(boxA[0], boxB[0])
38
+ yA = max(boxA[1], boxB[1])
39
+ xB = min(boxA[2], boxB[2])
40
+ yB = min(boxA[3], boxB[3])
41
+
42
+ inter_w = max(0, xB - xA)
43
+ inter_h = max(0, yB - yA)
44
+ inter_area = inter_w * inter_h
45
+
46
+ if inter_area == 0:
47
+ return 0.0
48
+
49
+ areaA = (boxA[2] - boxA[0]) * (boxA[3] - boxA[1])
50
+ areaB = (boxB[2] - boxB[0]) * (boxB[3] - boxB[1])
51
+ union = areaA + areaB - inter_area
52
+
53
+ return inter_area / union
54
+
55
+ def merge_overlapping_boxes(boxes, iou_threshold=0.93):
56
+ merged = []
57
+ boxes = sorted(boxes, key=lambda x: x["conf"], reverse=True)
58
+
59
+ while boxes:
60
+ current = boxes.pop(0)
61
+ new_boxes = []
62
+
63
+ for other in boxes:
64
+ iou = calculate_iou(current["coords"], other["coords"])
65
+ if iou >= iou_threshold:
66
+ x1 = int((current["coords"][0] + other["coords"][0]) / 2)
67
+ y1 = int((current["coords"][1] + other["coords"][1]) / 2)
68
+ x2 = int((current["coords"][2] + other["coords"][2]) / 2)
69
+ y2 = int((current["coords"][3] + other["coords"][3]) / 2)
70
+
71
+ current["coords"] = [x1, y1, x2, y2]
72
+ current["conf"] = max(current["conf"], other["conf"])
73
+ else:
74
+ new_boxes.append(other)
75
+
76
+ merged.append(current)
77
+ boxes = new_boxes
78
+
79
+ return merged
80
+
81
+ # =========================================================
82
+ # ENDPOINT DE PREDIÇÃO
83
+ # =========================================================
84
+ @app.post("/predict")
85
+ async def predict(file: UploadFile = File(...)):
86
+ # 1. Recebe a imagem enviada pela aplicação web
87
+ image_bytes = await file.read()
88
+ image = Image.open(io.BytesIO(image_bytes))
89
+
90
+ # 2. Executa a inferência forçando o tamanho 640 e os parâmetros do Colab
91
+ results = model.predict(
92
+ source=image,
93
+ imgsz=IMG_SIZE,
94
+ conf=CONFIDENCE_THRESHOLD,
95
+ device="cpu",
96
+ verbose=False
97
+ )
98
+
99
+ boxes_data = []
100
+
101
+ # 3. Varre os resultados aplicando os filtros
102
+ for r in results:
103
+ if r.boxes:
104
+ for box in r.boxes:
105
+ cls = int(box.cls[0])
106
+
107
+ # Filtro de classe: Apenas classe 0 (igual ao seu Colab)
108
+ if cls != 0:
109
+ continue
110
+
111
+ xyxy = box.xyxy[0].cpu().numpy().astype(int)
112
+ x1, y1, x2, y2 = xyxy
113
+ conf = float(box.conf[0])
114
+
115
+ w = x2 - x1
116
+ h = y2 - y1
117
+
118
+ # Filtro de tamanho mínimo e máximo
119
+ if not ((MIN_SIZE <= w <= MAX_SIZE) and (MIN_SIZE <= h <= MAX_SIZE)):
120
+ continue
121
+
122
+ boxes_data.append({
123
+ "coords": [int(x1), int(y1), int(x2), int(y2)],
124
+ "conf": conf,
125
+ "cls": cls
126
+ })
127
+
128
+ # 4. Remove duplicadas usando o seu filtro IoU
129
+ filtered_boxes = merge_overlapping_boxes(boxes_data, iou_threshold=IOU_THRESHOLD)
130
+
131
+ # 5. Retorna o JSON limpo e idêntico ao esperado pelo seu front-end
132
+ return {
133
+ "total_detections": len(filtered_boxes),
134
+ "predictions": filtered_boxes
135
+ }