root16285 commited on
Commit
ebfcb4b
·
1 Parent(s): 1f3d9ce

Fix: Use ultralytics YOLO API instead of DetectMultiBackend to avoid export module dependency

Browse files
Files changed (1) hide show
  1. webapp/backend/main.py +50 -72
webapp/backend/main.py CHANGED
@@ -24,24 +24,16 @@ from fastapi.responses import JSONResponse, HTMLResponse, FileResponse
24
  from fastapi.staticfiles import StaticFiles
25
  from PIL import Image
26
 
27
- # YOLOv5 imports - Load from local directory
28
  import sys
29
 
30
- # Add parent yolov5 directory to path
31
- YOLOV5_ROOT = Path(__file__).resolve().parents[2]
32
- if str(YOLOV5_ROOT) not in sys.path:
33
- sys.path.insert(0, str(YOLOV5_ROOT))
34
-
35
- # Import YOLOv5 modules
36
  try:
37
- from models.common import DetectMultiBackend
38
- from utils.general import non_max_suppression, scale_boxes, check_img_size
39
- from utils.torch_utils import select_device
40
- from utils.augmentations import letterbox
41
  YOLOV5_AVAILABLE = True
42
- print("✅ Modules YOLOv5 importés avec succès")
43
  except Exception as e:
44
- print(f"❌ Erreur d'import YOLOv5: {e}")
45
  YOLOV5_AVAILABLE = False
46
 
47
  # Initialize FastAPI app
@@ -188,34 +180,34 @@ manager = ConnectionManager()
188
 
189
 
190
  def load_model(model_name: str = "yolov5s"):
191
- """Load YOLOv5 model from local files"""
192
  global device
193
 
194
  if not YOLOV5_AVAILABLE:
195
- raise Exception("YOLOv5 non disponible - vérifiez l'installation")
196
 
197
  # Initialize device on first load
198
  if device is None:
199
- device = select_device('')
200
  print(f"📊 Device sélectionné: {device}")
201
 
202
  if model_name not in models_cache:
203
  try:
204
  print(f"🔄 Chargement du modèle {model_name}...")
205
 
206
- # Path to model weights
207
- model_path = YOLOV5_ROOT / f"{model_name}.pt"
208
-
209
- if not model_path.exists():
210
- print(f"⚠️ {model_path} non trouvé, utilisation de yolov5s.pt")
211
- model_path = YOLOV5_ROOT / "yolov5s.pt"
212
-
213
- if not model_path.exists():
214
- raise FileNotFoundError(f"Modèle non trouvé: {model_path}. Téléchargez-le depuis https://github.com/ultralytics/yolov5/releases")
215
 
216
- # Load model using DetectMultiBackend
217
- model = DetectMultiBackend(str(model_path), device=device, dnn=False, fp16=False)
218
- model.eval()
 
 
 
 
 
 
 
219
 
220
  models_cache[model_name] = model
221
  print(f"✅ Modèle {model_name} chargé avec succès!")
@@ -234,60 +226,40 @@ def load_model(model_name: str = "yolov5s"):
234
 
235
 
236
  def process_image(image: Image.Image, model_name: str = "yolov5s", conf_threshold: float = 0.25):
237
- """Process image and return detections"""
238
  start_time = time.time()
239
 
240
  try:
241
  # Load model
242
  model = load_model(model_name)
243
 
244
- # Prepare image
245
- img = np.array(image)
246
-
247
- # Handle different image formats
248
- if img.ndim == 2: # Grayscale
249
- img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
250
- elif img.shape[2] == 4: # RGBA
251
- img = cv2.cvtColor(img, cv2.COLOR_RGBA2RGB)
252
-
253
- img_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
254
-
255
- # Resize and letterbox
256
- img_size = 640
257
- h0, w0 = img_bgr.shape[:2]
258
- r = img_size / max(h0, w0)
259
- if r != 1:
260
- img_bgr = cv2.resize(img_bgr, (int(w0 * r), int(h0 * r)), interpolation=cv2.INTER_LINEAR)
261
-
262
- # Letterbox
263
- img_letter, ratio, (dw, dh) = letterbox(img_bgr, img_size, auto=False, scaleup=True)
264
-
265
- # Convert to tensor
266
- img_tensor = torch.from_numpy(img_letter).to(device)
267
- img_tensor = img_tensor.permute(2, 0, 1).float() / 255.0
268
- if img_tensor.ndimension() == 3:
269
- img_tensor = img_tensor.unsqueeze(0)
270
-
271
- # Inference
272
- with torch.no_grad():
273
- pred = model(img_tensor)
274
 
275
- # NMS avec seuil IoU plus élevé pour éviter doublons
276
- pred = non_max_suppression(pred, conf_threshold, 0.50, classes=None, agnostic=False, max_det=300)
277
 
278
- # Process detections
279
  detections = []
280
- img_display = img.copy() # RGB image for display
281
 
282
- if pred[0] is not None and len(pred[0]) > 0:
283
- # Scale boxes back to original image
284
- pred[0][:, :4] = scale_boxes(img_tensor.shape[2:], pred[0][:, :4], img_display.shape).round()
 
 
285
 
286
- for *xyxy, conf, cls in pred[0]:
287
- x1, y1, x2, y2 = [int(x.item()) for x in xyxy]
288
- confidence = float(conf.item())
289
- class_id = int(cls.item())
290
- class_name_en = model.names[class_id]
 
 
 
 
 
 
291
  class_name = CLASS_NAMES_FR.get(class_name_en, class_name_en)
292
 
293
  detections.append({
@@ -297,7 +269,7 @@ def process_image(image: Image.Image, model_name: str = "yolov5s", conf_threshol
297
  "class_id": class_id
298
  })
299
 
300
- # Draw on image (RGB)
301
  cv2.rectangle(img_display, (x1, y1), (x2, y2), (0, 255, 0), 2)
302
  label = f"{class_name} {confidence:.2f}"
303
  (label_w, label_h), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)
@@ -332,6 +304,12 @@ def process_image(image: Image.Image, model_name: str = "yolov5s", conf_threshol
332
  "processing_time": time.time() - start_time,
333
  "timestamp": datetime.now().isoformat()
334
  }
 
 
 
 
 
 
335
 
336
 
337
  def update_statistics(detections: List[dict], processing_time: float):
 
24
  from fastapi.staticfiles import StaticFiles
25
  from PIL import Image
26
 
27
+ # YOLOv5 imports - Use ultralytics package (more reliable for deployment)
28
  import sys
29
 
30
+ # Import YOLOv5 via ultralytics package
 
 
 
 
 
31
  try:
32
+ from ultralytics import YOLO
 
 
 
33
  YOLOV5_AVAILABLE = True
34
+ print("✅ Ultralytics YOLO importé avec succès")
35
  except Exception as e:
36
+ print(f"❌ Erreur d'import Ultralytics: {e}")
37
  YOLOV5_AVAILABLE = False
38
 
39
  # Initialize FastAPI app
 
180
 
181
 
182
  def load_model(model_name: str = "yolov5s"):
183
+ """Load YOLOv5 model using ultralytics"""
184
  global device
185
 
186
  if not YOLOV5_AVAILABLE:
187
+ raise Exception("Ultralytics YOLO non disponible - vérifiez l'installation")
188
 
189
  # Initialize device on first load
190
  if device is None:
191
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
192
  print(f"📊 Device sélectionné: {device}")
193
 
194
  if model_name not in models_cache:
195
  try:
196
  print(f"🔄 Chargement du modèle {model_name}...")
197
 
198
+ # Try to load from local file first
199
+ model_path = f"/app/{model_name}.pt"
 
 
 
 
 
 
 
200
 
201
+ if not os.path.exists(model_path):
202
+ # Try in root directory
203
+ model_path = f"{model_name}.pt"
204
+
205
+ if not os.path.exists(model_path):
206
+ # Download from ultralytics hub
207
+ print(f"⚠️ {model_path} non trouvé, téléchargement depuis ultralytics...")
208
+ model = YOLO(f"{model_name}.pt")
209
+ else:
210
+ model = YOLO(model_path)
211
 
212
  models_cache[model_name] = model
213
  print(f"✅ Modèle {model_name} chargé avec succès!")
 
226
 
227
 
228
  def process_image(image: Image.Image, model_name: str = "yolov5s", conf_threshold: float = 0.25):
229
+ """Process image and return detections using ultralytics API"""
230
  start_time = time.time()
231
 
232
  try:
233
  # Load model
234
  model = load_model(model_name)
235
 
236
+ # Convert PIL to numpy array
237
+ img_array = np.array(image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
+ # Run inference using ultralytics API
240
+ results = model(img_array, conf=conf_threshold, iou=0.45, verbose=False)
241
 
242
+ # Process results
243
  detections = []
244
+ img_display = img_array.copy()
245
 
246
+ # Get the first result (single image)
247
+ result = results[0]
248
+
249
+ if result.boxes is not None and len(result.boxes) > 0:
250
+ boxes = result.boxes
251
 
252
+ for i in range(len(boxes)):
253
+ # Get box coordinates (xyxy format)
254
+ box = boxes.xyxy[i].cpu().numpy()
255
+ x1, y1, x2, y2 = map(int, box)
256
+
257
+ # Get confidence and class
258
+ confidence = float(boxes.conf[i].cpu().numpy())
259
+ class_id = int(boxes.cls[i].cpu().numpy())
260
+
261
+ # Get class name
262
+ class_name_en = result.names[class_id]
263
  class_name = CLASS_NAMES_FR.get(class_name_en, class_name_en)
264
 
265
  detections.append({
 
269
  "class_id": class_id
270
  })
271
 
272
+ # Draw on image
273
  cv2.rectangle(img_display, (x1, y1), (x2, y2), (0, 255, 0), 2)
274
  label = f"{class_name} {confidence:.2f}"
275
  (label_w, label_h), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)
 
304
  "processing_time": time.time() - start_time,
305
  "timestamp": datetime.now().isoformat()
306
  }
307
+ return {
308
+ "detections": [],
309
+ "image": image,
310
+ "processing_time": time.time() - start_time,
311
+ "timestamp": datetime.now().isoformat()
312
+ }
313
 
314
 
315
  def update_statistics(detections: List[dict], processing_time: float):