Automatic bib number detection and EXIF metadata tagging for race photography.
Feed it a folder of race photos, and it will detect bib numbers and write them to each image's IPTC Keywords metadata as BIB:1234. This makes photos instantly searchable by bib number in tools like Lightroom, Photo Mechanic, or any DAM software.
- GUI and CLI - Desktop app or command-line interface
- YOLO-based bib detection - Fast, accurate detection of race bibs
- RapidOCR digit recognition - PP-OCRv4 models for reading bib numbers
- Smart filtering - 50% height threshold rejects secondary numbers (gear check, timing chips)
- IPTC metadata tagging - Writes bib numbers to IPTC Keywords (no external dependencies)
- Batch processing - Process entire folders of images
- Debug visualization - Optional annotated output images showing detections
- Lightweight - ONNX-only inference (~250MB vs 2GB+ with PyTorch)
- Python 3.10+
# Clone the repository
git clone https://github.com/chbornman/RunnerBibTagger.git
cd RunnerBibTagger
# Create virtual environment
python -m venv venv
source venv/bin/activate # Linux/macOS
# or: venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txtpython main.pyThis launches the desktop application where you can:
- Select an image folder
- Choose options:
- Generate CSV report
- Show detection preview (live view as processing runs)
- Save debug images to disk
- Write bib numbers to image metadata (IPTC)
- Include subfolders (recursive processing)
- Click "Start Processing"
- View results in the log and preview panel
- Navigate through processed images with Previous/Next buttons or arrow keys
# Process a single image
python main.py photo.jpg
# Process an entire folder
python main.py ./race_photos/
# With options
python main.py ./race_photos/ --csv --debug --confidence 0.3| Option | Default | Description |
|---|---|---|
--csv |
off | Generate bib_results.csv with all detections |
--debug |
off | Save annotated debug images to debug_images/ |
--no-metadata |
off | Skip writing IPTC metadata to images |
--confidence |
0.25 | Detection confidence threshold (0.0-1.0) |
# Generate CSV report
python main.py ./photos/ --csv
# Save debug images to see what was detected
python main.py ./photos/ --debug
# Higher confidence threshold (fewer false positives)
python main.py --confidence 0.5 ./photos/
# Process without modifying original images
python main.py ./photos/ --no-metadata --csvProcessing 3 image(s)...
[1/3] DSC_0001.jpg -> 1234
[2/3] DSC_0002.jpg -> 567, 890
[3/3] DSC_0003.jpg -> (no bibs)
Processed 3 images, found 3 bib(s)
Filename,Bib Numbers,Detections,Time (ms)
DSC_0001.jpg,1234,1,245
DSC_0002.jpg,"567, 890",2,312
DSC_0003.jpg,,0,89When using --debug, annotated images are saved showing:
- Green bounding boxes around detected bibs
- Detection confidence percentage
- Recognized bib number and OCR confidence
Image → YOLO Detection → RapidOCR → Height Filtering → Metadata Write
- Detection: YOLO ONNX model locates bib regions in the image
- OCR: RapidOCR (PP-OCRv4) extracts text from each detected region
- Digit Filtering: Non-digit characters are removed
- Height Filtering: Text regions smaller than 50% of the tallest are rejected
- Selection: The largest remaining text region is selected as the bib number
- Metadata: Bib numbers are written to IPTC Keywords
Race bibs often contain multiple numbers:
- Main bib number (large, prominent)
- Gear check numbers (small)
- Timing chip codes (small)
- Wave/corral indicators (small)
The 50% height threshold keeps only text that is at least half as tall as the largest detected text, reliably filtering out secondary numbers.
Bib numbers are written to IPTC Keywords:
BIB:1234
This format:
- Is searchable in Lightroom, Photo Mechanic, and most DAM software
- Doesn't conflict with other keywords
- Allows searches like
BIB:1234orBIB:*
To verify metadata, use any IPTC-compatible tool (Lightroom, Photo Mechanic, exiftool, etc.).
- JPEG (
.jpg,.jpeg) - PNG (
.png) - TIFF (
.tif,.tiff)
Files with _debug in the filename are automatically skipped.
- onnxruntime - ONNX model inference
- rapidocr - OCR with PP-OCRv4 models
- opencv-python - Image processing
- py3exiv2 - IPTC/EXIF metadata writing
- Try lowering confidence:
--confidence 0.15 - Check if bibs are clearly visible in frame
- Use
--debugto see what's being detected
- Try increasing confidence:
--confidence 0.5 - The 50% height filter should reject most secondary numbers
- Check file permissions
- Verify image format supports IPTC metadata (JPEG, TIFF)
Run the test suite:
# Install test dependencies
pip install pytest
# Run all tests
python -m pytest tests/ -v
# Run specific test file
python -m pytest tests/test_gui_options.py -vTest images are included in tests/fixtures/images/ (resized to ~1600px for faster testing).
To regenerate test fixtures from full-resolution images:
./scripts/prepare_test_images.shMIT License - See LICENSE for details.
- OCR powered by RapidOCR with PaddleOCR models
- YOLO architecture by Ultralytics
The included bib detection model was trained on combined public datasets from Roboflow Universe:
| Dataset | Author | License |
|---|---|---|
| Bib Number Labeling | Marco Cheung | CC BY 4.0 |
| Bib Number Detection | AI-VSUMN | Roboflow |
| Bib Number | BibNumberDetection | Roboflow |
Marco Cheung's dataset aggregates images from multiple contributors:
- thomas-lamalle/bib-detection
- rbnr/bib-detector
- sputtipa/bip
- bibnumber/bibnumber
- python-vertiefung/python-vertiefung
- hcmus-3p8wh/bib-detection-big-data
- h1-qtgu0/bib-number
Thank you to all the photographers and annotators who made their data publicly available.