Friendly, browser-based CFU (colony-forming unit) counter for petri-dish photos. Drag images in, get auto-counted plates with each colony numbered, click-review (add / delete), then export a CSV.
WeCFU is the software half of a full imaging pipeline; the hardware half —
an overhead USB camera with integrated lighting plus a 3D-printed petri-dish
jig — is documented (with the printable model and purchase link) in
hardware/.
https://huggingface.co/spaces/WeCFU/wecfu
Just open the link. Each visitor gets a private session (≤ 100 images / 400 MB, auto-cleaned after 1 h of inactivity).
conda install westraingroup::wecfu
wecfu serveNo image limits, fully offline. Detailed walkthrough in USAGE.md; other install methods (fresh env, pip wheel, source clone) in INSTALL.md.
wecfu batch /path/to/plate/photos --out /path/to/resultsProduces results.csv plus an annotated overlay per image.
- Drop image files or a folder onto the left side. Files are uploaded (or symlinked in local mode) — your originals are never modified.
- Count all runs the algorithm on every image. Opening an un-counted image also auto-runs once, so the "drop and click around" flow Just Works.
- On any image:
- Left-click empty plate area → add a manual colony (green).
- Right-click a circle → delete it.
- Cmd / Ctrl-Z → undo. ← / → → previous / next image.
- Wheel → zoom. Cmd / Ctrl-drag → pan.
- Pick a colour preset (white / cream / yellow / any), or tune the seven algorithm parameters by hand. Tick Live threshold preview to see in real time which pixels are being treated as colonies.
- Sidebar dot states: grey = not counted, blue = counted, green = viewed or edited.
- Export CSV writes the slim 5-column table; Export bundle writes a zip with the CSV, annotated overlay PNGs, and a JSON state file per image.
| column | meaning |
|---|---|
filename |
the source image |
total_count |
final colony count after any human edits |
machine_count |
what the algorithm produced on its most recent run |
n_removed |
machine detections the user deleted |
n_added |
manual detections the user added |
notes |
per-image free-text note from the GUI |
total_count = machine_count − n_removed + n_added.
A single classical-CV pipeline; no deep-learning dependency, no GPU.
- Plate localisation.
cv2.HoughCircleson a downsampled grey image, with a largest-circular-contour fallback if Hough finds nothing. - ROI mask. Disk of radius
plate_inset · r(default 0.82) — drops the glass rim and the agar-glass glare ring. - White-colony gate. HSV: keep pixels with
V ≥ Min value(default 200) andS ≤ Max saturation(default 60). Bright, near-white colonies pass; yellow agar streaks don't. - Cleanup. Morphological open then close to remove specks and fill saturated-highlight holes at colony centres.
- Touching-colony split. Distance transform + watershed seeded by
peak_local_maxwithmin_distance = Peak min distance(default 15 px). - Shape filter. Area ∈ [
Min area frac, max_area_frac] × plate-area, circularity ≥Min circularity(0.55), solidity ≥ 0.80, eccentricity ≤ 0.88.
All seven parameter names match the GUI labels one-for-one.
| Slider | Range | When to nudge it |
|---|---|---|
| Min value | 0–255 | Empty plate over-counting? Raise. Real colonies missed? Lower. |
| Max saturation | 0–255 | Want to include yellow / orange colonies? Raise. Yellow agar bleed counted? Lower. |
| Plate inset | 0–1 | Rim glare counted? Lower (e.g. 0.78). |
| Min circularity | 0–1 | Tighten to filter irregular debris; loosen to keep oddly-shaped colonies. |
| Min area frac | small float | Tiny pinpoint colonies missed? Lower. Specks counted? Raise. |
| Peak min distance | px | Touching colonies merged? Lower. One colony split in two? Raise. |
The bottom row of the params bar has two UI helpers that don't affect counts:
| Field | What it does |
|---|---|
| New colony radius | Size (in pixels) of the green circle drawn when you left-click to add a manual colony. Visual only. |
| Live threshold preview | While ticked, the canvas overlays a green tint on every pixel currently passing the Min value / Max saturation gate, so you can see what the algorithm "sees" before clicking Apply and recount. |
WeCFU/
├── wecfu/
│ ├── plate.py # plate detection
│ ├── segment.py # CV pipeline
│ ├── naming.py # filename metadata parser
│ ├── pipeline.py # orchestration
│ ├── overlay.py # annotated-image rendering
│ ├── cli.py # 'wecfu batch / serve / web' entrypoints
│ └── server/
│ ├── app.py # FastAPI routes (single-user + web mode)
│ ├── web.py # per-visitor session middleware (web mode)
│ └── static/ # vanilla HTML + JS + CSS frontend
├── hardware/ # 3D-printed petri-dish jig + build notes
│ ├── README.md
│ ├── model.3mf
│ └── preview.png
├── Dockerfile # Hugging Face Spaces image
├── meta.yaml # conda-build recipe
├── pyproject.toml # pip / wheel build
└── tests/
To be added once the accompanying paper is published.
- Jianghua Zhang (张江华) —
zhangjianghua@westlake.edu.cn - Xinyu Wang (王欣宇) —
wangxinyu30@westlake.edu.cn - Wenhao Zhou (周文浩) —
zhouwenhao@westlake.edu.cn - Zhanyi Zhu (朱展翼) —
zhuzhanyi@westlake.edu.cn
MIT — see LICENSE.