avant codex
@@ -0,0 +1,37 @@
|
||||
# Test YOLO (feuilles/plantes)
|
||||
|
||||
## 1) Créer et activer un venv
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
## 2) Installer les dépendances
|
||||
|
||||
```bash
|
||||
pip install --upgrade pip
|
||||
pip install ultralyticsplus==0.0.28 ultralytics==8.0.43 opencv-python matplotlib
|
||||
```
|
||||
|
||||
## 3) Lancer un test
|
||||
|
||||
```bash
|
||||
python3 test_yolo/test_yolo_leaf.py --image /chemin/vers/ma_plante.jpg
|
||||
```
|
||||
|
||||
Sorties générées :
|
||||
- `test_yolo/output/detections.json`
|
||||
- `test_yolo/output/annotated.jpg`
|
||||
|
||||
## 4) Paramètres utiles
|
||||
|
||||
```bash
|
||||
python3 test_yolo/test_yolo_leaf.py \
|
||||
--image /chemin/vers/ma_plante.jpg \
|
||||
--conf 0.25 \
|
||||
--iou 0.45 \
|
||||
--max-det 1000 \
|
||||
--json-out test_yolo/output/detections.json \
|
||||
--image-out test_yolo/output/annotated.jpg
|
||||
```
|
||||
|
After Width: | Height: | Size: 810 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 528 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 453 KiB |
|
After Width: | Height: | Size: 842 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 830 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 918 KiB |
|
After Width: | Height: | Size: 943 KiB |
|
After Width: | Height: | Size: 832 KiB |
|
After Width: | Height: | Size: 821 KiB |
|
After Width: | Height: | Size: 895 KiB |
|
After Width: | Height: | Size: 870 KiB |
|
After Width: | Height: | Size: 910 KiB |
|
After Width: | Height: | Size: 936 KiB |
|
After Width: | Height: | Size: 943 KiB |
|
After Width: | Height: | Size: 831 KiB |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 864 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 751 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 870 KiB |
|
After Width: | Height: | Size: 312 KiB |
|
After Width: | Height: | Size: 444 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 5.5 MiB |
|
After Width: | Height: | Size: 40 KiB |
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"image": "image/Image collée (8).png",
|
||||
"model": "foduucom/plant-leaf-detection-and-classification",
|
||||
"model_source": "/home/gilles/.cache/huggingface/hub/models--foduucom--plant-leaf-detection-and-classification/snapshots/fe7b672ca4719fb7eb1b8c677c24631342a24022/best.pt",
|
||||
"params": {
|
||||
"conf": 0.03,
|
||||
"iou": 0.6,
|
||||
"max_det": 1000
|
||||
},
|
||||
"count": 4,
|
||||
"detections": [
|
||||
{
|
||||
"index": 0,
|
||||
"class_id": 8,
|
||||
"class_name": "raspberry",
|
||||
"class_name_fr": "raspberry",
|
||||
"confidence": 0.886375367641449,
|
||||
"bbox_xyxy": [
|
||||
79.77804565429688,
|
||||
5.03753137588501,
|
||||
172.6075897216797,
|
||||
62.644927978515625
|
||||
]
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"class_id": 8,
|
||||
"class_name": "raspberry",
|
||||
"class_name_fr": "raspberry",
|
||||
"confidence": 0.8398752212524414,
|
||||
"bbox_xyxy": [
|
||||
161.0865478515625,
|
||||
54.80467987060547,
|
||||
233.95501708984375,
|
||||
127.42171478271484
|
||||
]
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"class_id": 8,
|
||||
"class_name": "raspberry",
|
||||
"class_name_fr": "raspberry",
|
||||
"confidence": 0.8144381046295166,
|
||||
"bbox_xyxy": [
|
||||
86.82860565185547,
|
||||
82.79867553710938,
|
||||
171.43515014648438,
|
||||
195.80043029785156
|
||||
]
|
||||
},
|
||||
{
|
||||
"index": 3,
|
||||
"class_id": 8,
|
||||
"class_name": "raspberry",
|
||||
"class_name_fr": "raspberry",
|
||||
"confidence": 0.5520069599151611,
|
||||
"bbox_xyxy": [
|
||||
165.154052734375,
|
||||
9.37171459197998,
|
||||
231.52166748046875,
|
||||
60.80621337890625
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
TOKEN_FR = {
|
||||
"healthy": "saine",
|
||||
"leaf": "feuille",
|
||||
"plant": "plante",
|
||||
"disease": "maladie",
|
||||
"blight": "mildiou",
|
||||
"rust": "rouille",
|
||||
"spot": "tache",
|
||||
"scab": "tavelure",
|
||||
"mold": "moisissure",
|
||||
"mildew": "oidium",
|
||||
"powdery": "poudreux",
|
||||
"bacterial": "bacterien",
|
||||
"viral": "viral",
|
||||
"virus": "virus",
|
||||
"yellow": "jaune",
|
||||
"mosaic": "mosaique",
|
||||
"late": "tardif",
|
||||
"early": "precoce",
|
||||
"septoria": "septoriose",
|
||||
"target": "cible",
|
||||
"black": "noire",
|
||||
"rot": "pourriture",
|
||||
}
|
||||
|
||||
|
||||
def translate_label_to_fr(label: str) -> str:
|
||||
tokens = (
|
||||
label.replace("-", " ")
|
||||
.replace("_", " ")
|
||||
.replace(",", " ")
|
||||
.replace("(", " ")
|
||||
.replace(")", " ")
|
||||
.split()
|
||||
)
|
||||
if not tokens:
|
||||
return label
|
||||
translated = [TOKEN_FR.get(tok.lower(), tok.lower()) for tok in tokens]
|
||||
return " ".join(translated)
|
||||
|
||||
|
||||
def resolve_model(model_arg: str) -> tuple[str, str]:
|
||||
model_path = Path(model_arg)
|
||||
if model_path.exists():
|
||||
return str(model_path), model_arg
|
||||
|
||||
lower = model_arg.lower()
|
||||
if "/" in model_arg and not lower.endswith((".pt", ".onnx", ".engine", ".torchscript")):
|
||||
try:
|
||||
from huggingface_hub import hf_hub_download
|
||||
except Exception as exc:
|
||||
raise SystemExit(
|
||||
"Le modèle semble être un repo Hugging Face. Installe:\n"
|
||||
"pip install huggingface_hub\n"
|
||||
f"Détail: {exc}"
|
||||
)
|
||||
|
||||
try:
|
||||
downloaded = hf_hub_download(repo_id=model_arg, filename="best.pt")
|
||||
return downloaded, model_arg
|
||||
except Exception as exc:
|
||||
raise SystemExit(
|
||||
f"Impossible de télécharger best.pt depuis le repo Hugging Face '{model_arg}'.\n"
|
||||
f"Détail: {exc}"
|
||||
)
|
||||
|
||||
return model_arg, model_arg
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Test YOLO feuille/plante avec sortie JSON et image annotée."
|
||||
)
|
||||
parser.add_argument("--image", required=True, help="Chemin image à analyser")
|
||||
parser.add_argument(
|
||||
"--model",
|
||||
default="foduucom/plant-leaf-detection-and-classification",
|
||||
help="Chemin vers .pt/.onnx ou repo Hugging Face (best.pt)",
|
||||
)
|
||||
parser.add_argument("--conf", type=float, default=0.25, help="Seuil confiance")
|
||||
parser.add_argument("--iou", type=float, default=0.45, help="Seuil IoU NMS")
|
||||
parser.add_argument("--max-det", type=int, default=1000, help="Nombre max de détections")
|
||||
parser.add_argument(
|
||||
"--json-out",
|
||||
default="test_yolo/output/detections.json",
|
||||
help="Fichier JSON de sortie",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--image-out",
|
||||
default="test_yolo/output/annotated.jpg",
|
||||
help="Image annotée de sortie",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
image_path = Path(args.image)
|
||||
if not image_path.exists():
|
||||
raise SystemExit(f"Image introuvable: {image_path}")
|
||||
|
||||
try:
|
||||
import cv2
|
||||
from ultralytics import YOLO
|
||||
except Exception as exc:
|
||||
raise SystemExit(
|
||||
"Dépendances manquantes. Installe dans un venv:\n"
|
||||
"pip install ultralytics opencv-python matplotlib huggingface_hub\n"
|
||||
f"Détail: {exc}"
|
||||
)
|
||||
|
||||
model_source, model_display = resolve_model(args.model)
|
||||
model = YOLO(model_source)
|
||||
model.overrides["conf"] = args.conf
|
||||
model.overrides["iou"] = args.iou
|
||||
model.overrides["max_det"] = args.max_det
|
||||
|
||||
results = model.predict(str(image_path))
|
||||
result = results[0]
|
||||
|
||||
boxes = result.boxes
|
||||
names = getattr(result, "names", {})
|
||||
detections: list[dict] = []
|
||||
for i, box in enumerate(boxes):
|
||||
cls_id = int(box.cls.item())
|
||||
conf = float(box.conf.item())
|
||||
xyxy = [float(v) for v in box.xyxy[0].tolist()]
|
||||
detections.append(
|
||||
{
|
||||
"index": i,
|
||||
"class_id": cls_id,
|
||||
"class_name": names.get(cls_id, str(cls_id)),
|
||||
"class_name_fr": translate_label_to_fr(names.get(cls_id, str(cls_id))),
|
||||
"confidence": conf,
|
||||
"bbox_xyxy": xyxy,
|
||||
}
|
||||
)
|
||||
|
||||
payload = {
|
||||
"image": str(image_path),
|
||||
"model": model_display,
|
||||
"model_source": model_source,
|
||||
"params": {"conf": args.conf, "iou": args.iou, "max_det": args.max_det},
|
||||
"count": len(detections),
|
||||
"detections": detections,
|
||||
}
|
||||
|
||||
json_out = Path(args.json_out)
|
||||
json_out.parent.mkdir(parents=True, exist_ok=True)
|
||||
json_out.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
|
||||
annotated = result.plot()
|
||||
image_out = Path(args.image_out)
|
||||
image_out.parent.mkdir(parents=True, exist_ok=True)
|
||||
cv2.imwrite(str(image_out), annotated)
|
||||
|
||||
print(f"Détections: {len(detections)}")
|
||||
print(f"JSON: {json_out}")
|
||||
print(f"Image annotée: {image_out}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||