Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Rotten Fruit Detection

Finds apples, bananas and oranges in a webcam feed and says whether each one is fresh or rotten. YOLOv12n trained on 8,162 photos merged from six Roboflow datasets, exported to OpenVINO and run on the NPU of an Intel Core Ultra laptop.

Results

Test split: 593 photos with 870 fruit (170 of the photos have no fruit), used neither for training nor for picking the best epoch.

Model Classes mAP@50 mAP@50-95 Precision Recall
6-class (demo) fresh / rotten apple, banana, orange 0.986 0.885 0.966 0.972
3-class apple, banana, orange 0.989 0.880 0.975 0.978

Of the 870 test fruit, the 6-class model finds 846 with the right class, gives 10 the wrong class and misses 14. It puts 5 boxes on the 170 photos without fruit. The weakest class is rotten banana (0.775 mAP@50-95), mostly tight bunches where the boxes overlap.

Speed of the 6-class model on an Intel Core Ultra 5 226V laptop (OpenVINO, 640x640, batch 1, average of 300 runs):

Device Inference Inference FPS FPS with pre- and post-processing
NPU (Intel AI Boost) 6.35 ms 157.5 105.7
iGPU (Arc 130V) 5.98 ms 167.2 96.5
CPU 77.2 ms 13.0 12.4

The NPU and the integrated GPU are about equally fast, and both are 12 to 13 times faster than the CPU. The webcam demo runs on the NPU. Measured with python demo.py --benchmark 300 on a still frame, so camera capture and display aren't included.

Per-class scores, confusion matrix, sample predictions and an error breakdown are in notebooks/03_evaluation.ipynb.

Dataset

Two fruit datasets relabelled to one 6-class scheme, plus two face datasets and two datasets of everyday scenes with their labels emptied. The face and scene photos teach the model that people and their surroundings, which fill most webcam frames, aren't fruit. merge_datasets.py combines the six folders and rebuilds the dataset exactly.

Split Photos Fruit Photos without fruit
train 6,578 8,545 2,291
validation 991 1,758 144
test 593 870 170

No invalid boxes, and only one test photo is a copy of a training photo. prepare_data.py --classes 3 merges fresh and rotten for the 3-class model. Sources and samples: notebooks/01_dataset.ipynb.

A bigger dataset that didn't make the cut

An earlier dataset had 51,161 studio photos, one fruit per photo on a plain background, with boxes generated by OpenCV. Checking it turned up two problems (notebooks/04_studio_dataset.ipynb):

  • Every box was wrong. The labelling code unpacked cv2.boundingRect as (xmin, ymin, xmax, ymax), but it returns (x, y, width, height). 30% of the boxes had a negative size. The conversion is invertible, so prepare_data.py --fix-boxes rebuilds all of them.
  • About 1 in 5 validation and test photos were copies of training photos. They are left out.

A model trained on the fixed data scores 0.995 mAP@50 on that dataset's own test split, but 0.094 on the test split above, because it only ever saw one fruit on a plain background. The demo uses the model trained on the merged real-world photos.

Training

Fine-tuned from COCO-pretrained YOLOv12n for 50 epochs at 640 px, batch 8, on an RTX 3090. Augmentation is Ultralytics' default: HSV colour jitter, translation, scaling, horizontal flips and mosaic. Curves are in notebooks/02_training.ipynb; the unedited notebook the 6-class model was trained in is notebooks/original_6class_training_run.ipynb.

Running it

pip install -r requirements.txt

python prepare_data.py --classes 3    # 3-class copy of datasets/combined
python train.py                       # 6 classes
python evaluate.py                    # test split, writes results/eval_*.json
python export.py                      # .pt to OpenVINO

python demo.py                        # webcam, NPU
python demo.py --device intel:gpu     # or intel:cpu
python demo.py --model models/fruit-6class.pt --device cpu   # no Intel hardware
python demo.py --benchmark 300        # inference timing, writes results/benchmark_*.json

Datasets go in datasets/ and are only ever read. The NPU needs Intel's NPU driver. demo.py stops if the requested OpenVINO device is missing, because Ultralytics would otherwise fall back to AUTO with only a warning and you'd end up timing the CPU.

Files

notebooks/          dataset, training, evaluation, the studio-photo experiment, original training run
models/             weights (.pt) and OpenVINO exports
results/            dataset stats, training logs, eval and benchmark output
merge_datasets.py   combines fruit datasets and no-fruit datasets into one
prepare_data.py     relabels to 3 or 6 classes (and fixes the studio boxes)
dataset_stats.py    counts per split and class, invalid boxes, duplicate check
train.py            training
evaluate.py         mAP on the val or test split
export.py           .pt to OpenVINO
demo.py             webcam demo and benchmark

Data

Dataset Used for
Rotten Fruit Detector v3 (CC BY 4.0) fruit
Fresh and Rotten Fruits v3 fruit
Face Detection v27, Face Detection v4 no-fruit photos
Room v7, Blind Assistant v9 no-fruit photos

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages