A Python CLI tool for managing herbarium/fungarium specimen images. VoucherSnap scans images for QR codes linking to iNaturalist observations, optionally adds captions, and uploads images directly to iNaturalist.
Designed to work with inat.label.py, which generates herbarium labels with QR codes linking to iNaturalist observations.
VoucherSnap is designed for observers who:
- Make a field observation - You encounter an organism in nature and create an iNaturalist observation with field photos
- Collect a voucher specimen - You collect the organism (following proper permits and protocols) for preservation in a herbarium or fungarium
- Photograph the preserved specimen - Later, you photograph the dried/preserved specimen alongside its label (which contains a QR code linking to the original observation)
- Add specimen photos to your observation - VoucherSnap uploads these specimen photos to your existing observation, documenting both the living organism and the preserved voucher
This workflow adds scientific value by linking field observations with physical specimens that can be studied, sequenced, or verified.
Important: VoucherSnap uploads photos only to observations you own. Please follow iNaturalist's Terms of Use and community guidelines. Do not use this tool to upload photos of specimens you did not personally observe and collect.
- Scan images for QR codes containing iNaturalist observation URLs
- Fetch observation metadata (taxon name, observer, date, location)
- Display manifest of images to be uploaded with observation details
- Burn captions onto images (e.g., "Packaged for DNA Sequencing 12/11/2025")
- Upload images to iNaturalist observations
- Track upload history to detect and warn about duplicates
- Support for JPEG and HEIC images (common iPhone formats)
- Configurable image resizing and JPEG quality
VoucherSnap requires the zbar library for QR code detection.
macOS (Homebrew):
brew install zbarUbuntu/Debian:
sudo apt-get install libzbar0Windows: Download and install from ZBar Windows releases
pip install vouchersnapOr install from source:
git clone https://github.com/joshuaowalker/VoucherSnap.git
cd vouchersnap
pip install -e .On macOS with Homebrew, you may need to set the library path:
export DYLD_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_LIBRARY_PATHAdd this to your ~/.zshrc or ~/.bashrc for persistence.
VoucherSnap uses OAuth 2.0 with PKCE (Proof Key for Code Exchange) to authenticate with iNaturalist. This is the recommended authentication flow for desktop/CLI applications as it doesn't require storing secrets.
vouchersnap loginThis opens your browser to the iNaturalist login page. After you authorize VoucherSnap, your session is cached locally for future use.
vouchersnap logoutClears your cached session.
- When you run
login(orrunwithout an existing session), VoucherSnap starts a temporary local server onhttp://127.0.0.1:8914 - Your browser opens to iNaturalist's authorization page
- After you log in and authorize, iNaturalist redirects to the local server with an authorization code
- VoucherSnap exchanges the code for an access token using PKCE verification
- The token is cached in
~/.VoucherSnap/token.json
This flow follows RFC 7636 and doesn't require a client secret, making it safe for open-source applications.
The main workflow scans images, shows a manifest, and uploads to iNaturalist:
# Process all images in a directory
vouchersnap run ~/Photos/herbarium/
# Process specific files
vouchersnap run photo1.jpg photo2.heic
# Process with glob pattern
vouchersnap run "~/Photos/*.jpg"
# With caption
vouchersnap run ~/Photos/herbarium/ --caption "Packaged for DNA Sequencing 12/11/2025"
# With custom image settings
vouchersnap run ~/Photos/herbarium/ --max-size 1024 --quality 90To scan images for QR codes without uploading:
vouchersnap scan ~/Photos/herbarium/vouchersnap history
vouchersnap history --limit 50- Take photos of herbarium specimens with voucher slips (labels with QR codes)
- Transfer photos from your phone to your computer
- Run VoucherSnap:
$ vouchersnap run ~/Desktop/specimen_photos/
╭──────────────────────────────────────────────────────────────────────────────╮
│ VoucherSnap │
╰────────────────────── Herbarium Specimen Photo Manager ──────────────────────╯
Found 5 image(s)
Scanning for QR codes...
Found QR codes in 5 image(s)
Fetching observation data...
Upload Manifest
┏━━━┳━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━┓
┃ # ┃ Filename ┃ Obs ID ┃ Taxon ┃ Observer ┃ Status ┃
┡━━━╇━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━┩
│ 1 │ IMG_0001.heic │ 12345678 │ Amanita muscaria │ mycouser │ New │
│ 2 │ IMG_0002.heic │ 12345679 │ Boletus edulis │ mycouser │ New │
│ 3 │ IMG_0003.heic │ 12345680 │ Cantharellus │ mycouser │ New │
│ ... │
└─────────────────────────────────────────────────────────────────────────────┘
Add a caption to the images? [y/N]: y
Enter caption text: Packaged for DNA Sequencing 12/11/2025
Caption: Packaged for DNA Sequencing 12/11/2025
Upload 5 image(s) to iNaturalist? [Y/n]: y
iNaturalist Authentication
Opening browser for iNaturalist login...
Complete the login in your browser to continue.
Authenticated successfully
Uploading images...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
Upload Summary
Total: 5
Successful: 5VoucherSnap stores data in ~/.VoucherSnap/:
token.json- Cached OAuth access tokenhistory.json- Upload history for duplicate detection
| Option | Default | Description |
|---|---|---|
--caption, -c |
None | Caption to overlay on images |
--max-size |
2048 | Max image dimension (pixels) |
--quality |
85 | JPEG quality (1-100) |
--skip-duplicates |
False | Skip duplicates without prompting |
--auto-rotate |
False | Auto-rotate images based on text detection |
| Option | Default | Description |
|---|---|---|
--limit, -n |
20 | Number of records to show |
If your phone's accelerometer doesn't trigger correctly, images may have incorrect EXIF orientation. The --auto-rotate flag uses OCR to detect text orientation and correct it.
Requirements for auto-rotate:
# macOS
brew install tesseract
# Ubuntu/Debian
sudo apt-get install tesseract-ocr
# Install Python bindings
pip install vouchersnap[ocr]
# or: pip install pytesseractUsage:
vouchersnap run ~/Photos/ --auto-rotateThis feature is optional - VoucherSnap works without it, but images with bad EXIF orientation won't be corrected.
- Python 3.10+
- zbar library (system dependency)
- tesseract (optional, for
--auto-rotate)
BSD 2-Clause License