Asif EjazAI engineering / interactive demo← All demos
YOLOv8 on CPU through ONNX Runtime. No deep learning framework on the server.

Photograph a dashboard, get the warning lights named

Drop in a photo of an instrument cluster. A detector boxes each warning light it recognises and says in plain words what it means and how urgent it is. It began as a training notebook and a phone export. Rebuilding it as a service showed me what its old score had really been measuring, and led me to an earlier checkpoint of the same project that had learned more. Telltale is the car engineer's word for a warning light.

32
kinds of light it named correctly in the test pictures, of the 38 that appear in them
0.80
precision on 129 test pictures: of the lights it names, the share it gets right
0.76
recall on the same pictures: of the lights that are labelled, the share it finds
Try it

Pick a sample, or use your own photo

The samples are plain phone photos from the test part of the dataset. The model did not train on these files, but it did train on other shots of the same dashboards taken seconds apart, so they flatter it. Your own photo is the fairer test. It is sent to this server, processed in memory and never written to disk, and nothing is kept once the answer has been sent back.

Samples
Drop a dashboard photo here
JPEG or PNG, up to 8 MB.
Loading a sample photo.
What I found when I reopened this project

The model on record had only been taught 21 of its 50 symbols

This was a company project from the end of 2023: a model for a phone app that reads dashboard warning lights. The score on record looked healthy. Scoring the exported file again, against the dataset as it was published, told a different story. The first version of this page served that file. What runs here now is an earlier checkpoint of the same project, one that had been shown every symbol.

0.83

The score on record

The training log ends with a mean average precision of 0.83 on the validation pictures, with precision 0.82 and recall 0.78. There were only 91 validation pictures, so each of those figures can move a few points on a different sample.

21 of 50

What that score was measured on

In the project's working copy of the dataset, every label for 29 of the 50 symbols had been deleted: the rare ones, with only a handful of examples each. They were gone from the validation and test files and from all 300 training files that had one. The class list still said 50. So, as far as I can tell, the final training runs never showed the model those symbols, and the score never asked about them.

0.49

The same weights against the published labels

The dataset as published still marks the rare symbols. Scored against it by the same library, the same file reaches 0.49 on the same 91 pictures. Nothing got worse. The 21 symbols it was taught are found about as well as the log says, and the other 29 are never found: 0 of 40 labelled rare lights in the test pictures.

What runs now

An earlier checkpoint that had seen every symbol

The project folder still held the weights of the training runs from before the labels were deleted. Four of them had learned every symbol, and they score about the same. I took the one whose class list matches the published one by name. It trained at the end of 2023 on the full published labels, with the same split of pictures, so none of the test pictures were in its training data.

Through this service, on the same 129 test pictures and at the same settings, it names 32 kinds of light against 21, and finds 18 of 40 rare lights where the final run found none. Its F1 is 0.78 against 0.78: it finds more and is wrong a little more often. On the 21 common symbols alone it is a little behind, an F1 of 0.79 against 0.80. A light the model cannot name at all is a worse failure than one named with slightly less care, so this is the model that runs here now.

What changed

From a notebook and a phone export to a service on a shared CPU

Before

A training notebook and files meant for a phone

The model was trained in a hosted notebook with Ultralytics YOLOv8 and exported for a phone app. Nothing could be tried without the notebook, a graphics card session and the training framework. The PyTorch weights of the final run are no longer in the project folder. What is left of that run is the ONNX file that the exporter writes on its way to a TensorFlow Lite model. The earlier run still has its weights.

The export

Two files, each with its own catch

The final run's file gives its boxes as fractions of the picture instead of pixels and carries no class names. Loaded with the library that made it, every box collapses to a point in the top left corner and its own validator reports a precision and recall of zero. Once the units are corrected the file is fine. The earlier run I exported again from its weights. It learned the first version of the published labels, with 52 classes, and the second version merged a misspelt copy of Service due into the real one and dropped one symbol. So the end of the file has a small added step that maps its 52 scores onto the 50 published classes, keeping the higher score where two became one, checked against the same mapping done in numpy.

Decoding

The framework replaced by a page of numpy

Fitting the photo into a 640 pixel square, reading the 8,400 candidate boxes out of the raw output, class aware non maximum suppression and mapping the boxes back onto the photo are written in numpy and Pillow. The server needs ONNX Runtime and nothing else: no Ultralytics, no PyTorch, no OpenCV.

Serving

Sized for a machine it shares

The model loads on the first request and stays in memory. One photo is processed at a time on three threads, and requests are rate limited per address.

Smaller file

Tested before choosing

I tried storing the weights at half precision and two kinds of 8 bit quantisation, and compared every detection with the original file before deciding which one to serve.

Limits

What it does not do

Coverage

About thirty symbols, the rare ones often missed

It finds the common lights well: engine, oil, battery, brakes, ABS, airbag, seatbelt, tyre pressure, doors, temperature, fuel, stability control and a few more. It also knows some rarer ones, such as fog lights, glow plugs, hill assist and brake wear, but it found fewer than half of the rare lights in the test pictures. An uncommon symbol is still more often missed than found, without any warning, or now and then named as the nearest common one. A light it does not box is not a light that is off.

The photo

Lighting, glare and distance

Reflections on the glass, glare around a light at night, motion blur and symbols that are only a few pixels across all cost it detections. It was trained on pictures shrunk to 416 pixels, most of them screenshots, so a sharp close photo of a real cluster is somewhat outside what it has seen.

Meaning

It names the symbol, not its state

It does not read the colour of a light or whether it is flashing, and both change the meaning. The descriptions are general ones that I wrote, and the same symbol differs between makes. The owner's manual for the car is the authority.

The numbers

Small and not independent

The validation and test pictures are few, and many of them are screenshots taken moments apart from pictures in the training set. I also chose between the two models with these pictures in view, which flatters the one I chose a little. Treat the scores as a check that the service is wired correctly, not as a promise about your car.

Not a diagnosis

A reading aid

This is not a diagnostic tool and it cannot see what is wrong with a car. If a red light is on, stop when it is safe and look it up in the manual or call a mechanic.

Dataset: Enhanced Dashboard DS from Roboflow Universe, licensed CC BY 4.0. The model trained on its version 1; the scores, the class list and the samples use version 2. The sample photos come from its test split, trimmed to remove the black border. Model trained with Ultralytics YOLOv8, which is licensed AGPL 3.0.