YOLO11n cigarette-butt detector

A small object detector that finds cigarette butts on city pavement in high-resolution ground photos. It is part of an open project working towards a robot that collects cigarette litter. The model is YOLO11n (2.6 M parameters), chosen over a larger YOLO11s because its speed makes it the closest to running on a robot.

What it was trained for

  • Images: phone photos of pavement at about robot camera height, 4000Γ—1800 px, daylight without rain.
  • Objects: cigarette butts, 25–254 px long in those photos (90% between 35 and 170 px). Cigar and cigarillo ends are out of scope.
  • How it runs: on 640 px tiles cut from the full photo every 384 px, with boxes merged across tiles. Resizing a whole photo down to 640 px makes butts a few pixels long and the model will miss them; use the tiling below.

Use

from huggingface_hub import hf_hub_download
from PIL import Image
import torch, torchvision
from ultralytics import YOLO

model = YOLO(hf_hub_download('lojony/yolo11n-cigarette-butt-detector', 'yolo11n-cigarette-butts.onnx'))
TILE, STRIDE = 640, 384          # the tiling the model was trained and scored with

def starts(n):
    s = list(range(0, n - TILE + 1, STRIDE))
    return s + [n - TILE] if s[-1] + TILE < n else s   # last tile ends at the border

def detect(path, conf=0.25):
    im = Image.open(path).convert('RGB')
    boxes, scores = [], []
    for y in starts(im.height):
        for x in starts(im.width):
            r = model.predict(im.crop((x, y, x + TILE, y + TILE)), imgsz=TILE, conf=conf, verbose=False)[0].boxes
            boxes += [[a + x, b + y, c + x, d + y] for a, b, c, d in r.xyxy.tolist()]
            scores += r.conf.tolist()
    if not boxes:
        return []
    keep = torchvision.ops.nms(torch.tensor(boxes), torch.tensor(scores), 0.3).tolist()
    return sorted(((scores[i], *boxes[i]) for i in keep), reverse=True)

for score, x0, y0, x1, y1 in detect('photo.jpg'):
    print(f'{score:.2f}  ({x0:.0f}, {y0:.0f}) – ({x1:.0f}, {y1:.0f})')

The photo must be at least 640 px on each side. A lower conf finds more butts and raises more false alarms (see the curve below).

Evaluation

Scored on 19 validation photos (43 butts) that were labelled without any detector suggestions on screen and never used in training, and once on 19 test photos (29 butts), labelled the same way and never used for any choice before. Each photo is scored whole: a butt is found when a detection's centre lies inside its box or the boxes overlap with IoU β‰₯ 0.3; a false positive is a detection on no labelled box. Bracketed ranges are 95% intervals.

Recall at threshold 0.25 81.4% (35 of 43; 67.4–90.3%)
False positives per photo at 0.25 0.74 (0.37–1.16)
False positives per photo at 72% / 81% / 86% recall 0.00 / 0.37 / 1.95
Highest recall reached 88.4%
Test photos (19 photos, 29 butts, scored once), threshold 0.25 recall 75.9% (22 of 29; 57.9–87.8%), 1.37 (0.63–2.21) false positives per photo
5-fold cross-validation, 106 photos, 260 butts, threshold 0.25 recall 81.9% (76.8–86.1%), 1.01 (0.77–1.22) false positives per photo
Time per 4000Γ—1800 photo (50 tiles), laptop CPU (i5-1145G7) 2.1 s
Compute per photo 322 GFLOPs

For comparison on the same 19 photos: YOLO11s trained the same way finds 83.7% at 0.84 false positives per photo and takes 6.5 s; the zero-shot OWLv2 model reaches 97.7% recall but with 7.3 false positives per photo at its operating point and 119 s per photo. The interactive curve shows all three at every threshold. One butt is about 2 points of recall on the validation photos and about 3.5 on the test photos, so small differences are noise: the test result is lower than the validation result, and their intervals overlap.

Training

  • Data: 87 training photos with 217 labelled butts (Valencia, Spain, one phone, one session on 2026-10-01), cut into 640 px tiles every 384 px; a butt counts in a tile when 80% of it is inside.
  • Recipe (06-cv-yolo11n.json): boxes the annotator was unsure of are painted grey, 10% of the training tiles hold no butt, and extra tiles are centred on lookalikes the annotator rejected (leaves, paper, stones). Ultralytics 8.4.184, COCO-pretrained YOLO11n, 100 epochs, default augmentation, final epoch kept.
  • Reproduce: pipeline/train.ipynb and pipeline/evaluate.ipynb in the GitHub repository.

Limitations

  • One capture session: one phone, one area of one city, daylight without rain. Validation photos come from the same walk as the training photos, held out by time, not location. Performance at new places is unmeasured and probably lower.
  • Not a robot camera: the photos come from a phone. A robot's sensor, motion blur and mounting are untested, and no robot or GPU board has run the model.
  • Small data: 87 training photos and one training seed; the intervals above are wide.
  • Lookalikes: in an error analysis of the YOLO11s model trained the same way, most false positives were local lookalikes (leaves and paper the largest group), and crushed or flattened butts were the most common misses, then blurred and small ones. This model was not analysed separately.
  • Labels: one annotator; small, unsaturated butts are probably under-labelled.

Intended use

Research and prototyping of litter detection, especially for robots and for counting litter in field photos, with the limits above stated alongside any result. It is not suited to enforcement or to any decision about a person.

Contributing

The project is open and looking for help. The open questions list what is untested: more varied photos, lookalike-focused training, context at tile edges, larger models, robot hardware. Photos taken with the capture protocol, reports of how the model does on your own photos, and pull requests are all welcome.

Files

  • yolo11n-cigarette-butts.onnx: the model for ONNX Runtime (no Ultralytics pickle); runs with the example above.
  • yolo11n-cigarette-butts.pt: the same weights in Ultralytics' format (last.pt of the training run).
  • run_config.json: every setting of the training run and of its evaluation, as used. With the .pt file, the evaluation notebook (RUN_CONFIG and WEIGHTS settings) reproduces the validation result above.

Licence

Model: AGPL-3.0. Ultralytics states that models trained with its library fall under AGPL-3.0 (Ultralytics licensing): if you build this model into a product, service or device, that project must be open-sourced under AGPL-3.0, or you need an Enterprise licence from Ultralytics. The training code is open source under AGPL-3.0 on GitHub. Training data: CC BY 4.0.

Downloads last month
25
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support