Inspiration
Almost every vaccine vial in the world carries a Vaccine Vial Monitor (VVM): a small square inside a circle that darkens with heat. WHO's rule is simple. Once the square is the same colour as the circle, or darker, the vaccine has had too much heat and must be thrown away. Temptime, the company that makes them, shipped over a billion of these labels in 2021 alone.
Yet the check is done by eye. WHO's own Effective Vaccine Management app asks a human to judge the label; it does not scan it. A misread one way gives a child heat-damaged vaccine. A misread the other way throws good vaccine away. That felt like exactly the kind of problem image AI should solve: a real rule, checked millions of times a day, by eye.
Brainstorming
I listed 24 image-AI ideas and cut them against a hard bar: a real rule that must be followed, no existing tool, something you can't solve by pasting a photo into ChatGPT, works with one phone and nobody else adopting it first, measurement plus exact code (not a thin API wrapper), and a wow in the first 10 seconds.
Most ideas died on "already exists" (pill counters, test-strip readers, tyre tread, meter OCR, resistor codes, petri-dish counting) or "common hackathon project" (handwash compliance, CPR rate). The IV drip-rate meter and a library shelf checker were runners-up but research and apps already exist. The VVM reader passed every check: I could find no camera reader for it anywhere, not even in WHO's app.
What it does
- Scan a tray: point the live camera at vials, upload a photo, or try the built-in demo and hard trays. Every label gets a ring: USE (green), USE FIRST (amber), DISCARD (red) or RESCAN (grey), with the measured value. A banner says how many must be discarded.
- Shows its working: tap a vial to see the straightened label, the exact regions measured, the square's and circle's lightness, the difference with a 95% interval, light falloff across the label, glare, sharpness and tilt.
- Refuses to guess: glare, uneven light, blur, tilt or an unstable reading returns RESCAN with the reason ("Uneven light or a reflection on the label. Shade it or change the angle.").
- Live mode tracks each label across frames and averages up to 15 readings.
- Cold-chain log: every reading can be exported as a CSV register.
- Accuracy, reproducible: an in-app test runs 30 synthetic trays (240 labels) with known ground truth.
- Runs in the browser and works offline after the first load. No server makes the decision.
How I built it (design process)
No AI model makes the call. Image processing finds and measures the label, and an exact rule decides.
- Find: adaptive threshold, contours, a circularity check and an ellipse fit (OpenCV.js) locate every VVM circle at any size or rotation.
- Straighten: each ellipse is resampled into a front-on circle, which undoes camera tilt.
- Measure: pixels are converted to CIELAB, a colour space built on human lightness. The square's centre is compared with the ring of the circle. The geometry (square ≈ 0.37 of the circle's width) was measured from the WHO VVM graphic.
- Self-calibrate: the circle is the label's own reference colour. A least-squares plane fitted to the ring's lightness cancels uneven light, so no colour card is needed.
- Decide: ΔL* = square minus circle, with a 95% interval and a ±1.5 L* floor. Discard when the interval reaches within 2 L* of the circle or below, exactly the WHO "same or darker" rule. USE FIRST (lighter by under 12 L*) is a field convention and is labelled as one.
To test it honestly I built a synthetic tray generator: every square is set to a known lightness offset from its circle, then the scene gets uneven light, colour casts, tilt, rotation, glare, sensor noise and blur.
Why not just ask an AI?
I sent the same 8 labels to a state-of-the-art vision-language model (Qwen3-VL-30B, temperature 0). It answered DISCARD on all 8, including squares that are almost white, and described lighter squares as "darker". It got 4 of 8 right only because half really were past the discard point. Reading a few lightness units of difference is a measurement, not a description. The comparison table and a live "ask the AI" button are on the site.
Challenges I ran into
- The first detector found the vial cap, not the label: the threshold traced the cap's edge and hid the label inside it. I switched to checking every contour and keeping the one with the strongest dark-circle contrast.
- Glare almost made it dangerous. The first accuracy run had 14 unsafe calls (labels that must be discarded read as usable), and 13 of them were soft reflections that brighten the square without saturating any pixel. The fix: a clean printed circle measures about 0.5 L* of spread, so unevenness above 3 L* now triggers RESCAN. Unsafe calls went from 14 to 0.
- A purple square in the WHO graphic was read as a label. A circularity check fixed it.
Accomplishments I'm proud of
- 0 unsafe calls and 0 good vials thrown away on 240 synthetic labels with known answers, across normal and hard conditions.
- All three real reference labels in the WHO VVM graphic read correctly (USE, DISCARD, DISCARD).
- Every verdict is explainable with numbers, and the tool says "I'm not sure" instead of guessing.
What I learned
Most of the work in computer vision is not finding the object; it's making the measurement trustworthy. Self-calibration (using the label's own circle as the reference), conservative intervals and refusing to answer under glare mattered more than any model. I also learned that a big vision-language model can describe an image fluently and still be wrong on a measurement.
What's next
- A field trial with real vials and cold-chain staff (empty used vials from a local clinic).
- A small detector trained on synthetic renders for cluttered scenes.
- OCR of expiry date and batch on the vial label.
- The same pipeline for other colour indicators: autoclave tape, sterilisation integrators, food time-temperature labels.
Built With
- canvas
- cielab
- computer-vision
- featherless
- opencv
- opencv.js
- qwen3-vl
- typescript
- vercel
- vite
Log in or sign up for Devpost to join the conversation.