ultralytics
ONNX
agriculture
weed-detection
precision-agriculture
ugv
yolo
yolo11
hailo
edge-ai
broadleaf-weeds
Eval Results (legacy)
Instructions to use llama-farm/broadleaf-weed-detector with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- ultralytics
How to use llama-farm/broadleaf-weed-detector with ultralytics:
# Couldn't find a valid YOLO version tag. # Replace XX with the correct version. from ultralytics import YOLOvXX model = YOLOvXX.from_pretrained("llama-farm/broadleaf-weed-detector") source = 'http://images.cocodataset.org/val2017/000000039769.jpg' model.predict(source=source, save=True) - Notebooks
- Google Colab
- Kaggle
| #!/usr/bin/env python3 | |
| """Compare full-frame vs TILED inference for the fine-tuned footage model. | |
| Tiled: run the detector on each tile of a grid (weeds appear bigger → in the | |
| model's resolvable range), map boxes back, NMS. Report per-frame detection | |
| rate + recall vs the tiled GT (fraction of GT weeds with an overlapping pred). | |
| """ | |
| import argparse | |
| import glob | |
| from pathlib import Path | |
| from collections import defaultdict | |
| from ultralytics import YOLO | |
| from PIL import Image | |
| GT = Path("/opt/weeds/footage/gt_tiled") | |
| def iou(a, b): | |
| ix1, iy1 = max(a[0], b[0]), max(a[1], b[1]) | |
| ix2, iy2 = min(a[2], b[2]), min(a[3], b[3]) | |
| iw, ih = max(0, ix2-ix1), max(0, iy2-iy1) | |
| inter = iw*ih | |
| ua = (a[2]-a[0])*(a[3]-a[1]) + (b[2]-b[0])*(b[3]-b[1]) - inter | |
| return inter/ua if ua > 0 else 0 | |
| def nms(boxes, thr=0.5): | |
| boxes = sorted(boxes, key=lambda b: -b[4]) | |
| keep = [] | |
| for b in boxes: | |
| if all(iou(b[:4], k[:4]) < thr for k in keep): | |
| keep.append(b) | |
| return keep | |
| def full_preds(model, img, conf, imgsz=1280): | |
| r = model.predict(img, imgsz=imgsz, conf=conf, verbose=False)[0] | |
| return [[*map(float, b.xyxy[0].tolist()), float(b.conf[0])] for b in r.boxes] | |
| def tiled_preds(model, img_path, conf, grid=(3, 2), imgsz=640, overlap=0.12): | |
| im = Image.open(img_path); W, H = im.size | |
| gx, gy = (grid[1], grid[0]) if H > W else grid | |
| tw, th = W//gx, H//gy | |
| ox, oy = int(tw*overlap), int(th*overlap) | |
| boxes = [] | |
| for j in range(gy): | |
| for i in range(gx): | |
| x0 = max(0, i*tw-ox); y0 = max(0, j*th-oy) | |
| x1 = min(W, (i+1)*tw+ox); y1 = min(H, (j+1)*th+oy) | |
| tile = im.crop((x0, y0, x1, y1)) | |
| tmp = Path("/tmp")/f"ti_{Path(img_path).stem}_{i}_{j}.jpg" | |
| tile.convert("RGB").save(tmp, quality=90) | |
| r = model.predict(str(tmp), imgsz=imgsz, conf=conf, verbose=False)[0] | |
| tmp.unlink(missing_ok=True) | |
| for b in r.boxes: | |
| bx = b.xyxy[0].tolist() | |
| boxes.append([x0+bx[0], y0+bx[1], x0+bx[2], y0+bx[3], float(b.conf[0])]) | |
| return nms(boxes, 0.5) | |
| def gt_boxes(stem): | |
| W = H = None | |
| ip = GT/"images"/f"{stem}.jpg" | |
| with Image.open(ip) as im: | |
| W, H = im.size | |
| out = [] | |
| for ln in (GT/"labels"/f"{stem}.txt").read_text().splitlines(): | |
| if not ln.strip(): | |
| continue | |
| _, cx, cy, bw, bh = map(float, ln.split()) | |
| out.append([(cx-bw/2)*W, (cy-bh/2)*H, (cx+bw/2)*W, (cy+bh/2)*H]) | |
| return out | |
| def recall(preds, gts, thr=0.3): | |
| if not gts: | |
| return None | |
| hit = 0 | |
| for g in gts: | |
| if any(iou(p[:4], g) >= thr for p in preds): | |
| hit += 1 | |
| return hit/len(gts) | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--model", required=True) | |
| ap.add_argument("--imgsz-full", type=int, default=1280) | |
| ap.add_argument("--imgsz-tile", type=int, default=640) | |
| ap.add_argument("--clip", default="", help="restrict to one clip prefix; blank = all frames") | |
| ap.add_argument("--grid", default="3x2") | |
| args = ap.parse_args() | |
| gx, gy = (int(v) for v in args.grid.lower().split("x")) | |
| model = YOLO(args.model) | |
| pat = f"{args.clip}*.txt" if args.clip else "*.txt" | |
| stems = [Path(p).stem for p in glob.glob(str(GT/"labels"/pat))] | |
| agg = defaultdict(lambda: [0.0, 0]) | |
| frames_hit = defaultdict(int) | |
| for stem in stems: | |
| gts = gt_boxes(stem) | |
| ip = str(GT/'images'/f'{stem}.jpg') | |
| for mode, preds in (("full", full_preds(model, ip, 0.25, args.imgsz_full)), | |
| ("tiled", tiled_preds(model, ip, 0.25, (gx, gy), args.imgsz_tile))): | |
| r = recall(preds, gts) | |
| if r is not None: | |
| agg[mode][0] += r; agg[mode][1] += 1 | |
| if preds: | |
| frames_hit[mode] += 1 | |
| n = len(stems) | |
| for mode in ("full", "tiled"): | |
| mr = agg[mode][0]/agg[mode][1] if agg[mode][1] else 0 | |
| print(f"{mode:6s} inference: mean per-frame weed-recall={mr:.3f} " | |
| f"frame-detection-rate={frames_hit[mode]/n:.3f} (n={n})") | |
| print("TILED_EVAL_DONE") | |
| if __name__ == "__main__": | |
| main() | |