- Python 81.1%
- Dockerfile 16%
- Shell 2.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| src | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE.txt | ||
| prompt.txt | ||
| README.md | ||
| requirements.txt | ||
| run | ||
docqr - Containerized CPU-Only QR Code Extractor
docqr is a containerized, CPU-optimized CLI tool built to recursively scan a directory of photos, detect and decode any text-based QR codes within them using the qreader library, and output a structured JSON report.
It is designed to run efficiently in batch mode on servers without hardware acceleration (CPU-only).
Features & Highlights
- Robust QR Detection: Leverages the YOLOv8-powered
qreaderlibrary for state-of-the-art QR code detection and decoding (even for skewed, blurry, or low-resolution images). - Format Agnostic: Natively supports
jpg,png, andheic(viapillow-heif) along with standard formats likewebp,bmp,tiff, andgif. - Incremental Runs (Skip Logic): By default, the tool matches absolute paths inside the container against previous runs in
cache.jsonto skip hash computation entirely. If not matched, or if--no-check-filenamesis specified, it falls back to computing and checking aBLAKE2bhash for each scanned photo. Already-processed files are skipped, saving execution time and resources. - Offline & Compact Build:
- Uses the PyTorch CPU-only build to reduce image size (saves gigabytes of CUDA packages).
- Uses OpenCV Headless (
opencv-python-headless) to avoid GUI/X11 system library dependencies. - Pre-downloads (bakes) the default YOLOv8 model weights during build, allowing it to run entirely offline at runtime.
- Safe & Non-Destructive: Mounts the input photo directory as read-only (
ro) to ensure your pictures are never modified.
Project Structure
docqr/
├── Dockerfile # Debian Trixie slim build file
├── docker-compose.yml # Services and volume mount maps
├── requirements.txt # Python dependencies (qreader, pillow-heif, etc.)
├── README.md # Setup and usage guide (this file)
├── run # CLI entrypoint wrapper script
└── src/
└── main.py # Core scanner and detection Python script
Getting Started
1. Prerequisites
Make sure you have Docker and Docker Compose installed on your machine.
2. Run the Container in the Background
Start the container using Compose. This will build the Docker image (which takes a few minutes on the first run as it compiles dependencies and bakes the model weights) and keep it running in the background:
docker compose up -d
3. Run the QR Analysis
Execute the scanner inside the running container:
docker compose exec docqr run
You can optionally disable filename-skipping and force hash computation for all files with the --no-check-filenames flag:
docker compose exec docqr run --no-check-filenames
You can also adjust how often the cache and report are saved to disk during a long run (default is every 50 images) to prevent data loss in case the process is killed:
docker compose exec docqr run -r 100
You will see live logs on stdout showing progress:
Scanning '/data/photos' recursively for supported images...
Found 4 photos matching supported extensions.
Initializing QReader detector...
[1/4] [Analyzed] /data/photos/COW13GAS/20260718_002502.heic (took 0.72s)
[2/4] [Skipped] /data/photos/valid2/20260717_030551.jpg
[3/4] [Analyzed] /data/photos/valid2/Screenshot_20260717_030810_QR & Barcode Scanner.jpg (took 0.35s)
[4/4] [Skipped] /data/photos/SKO4-multiple/20260718_004223.jpg
------------------------------------------------------------
Execution complete.
Total photos found: 4
Analyzed: 2
Skipped: 2
Total time taken: 1.08s
Report written to: /data/output/report.json
------------------------------------------------------------
4. Stopping the Container
If you want to stop the background service, run:
docker compose down
Configuration
To point the tool to your own photos, modify the docker-compose.yml file's volume mounts:
volumes:
# Change "./test-data" to point to your local photo folder (e.g., "/home/user/my-photos")
- /home/user/my-photos:/data/photos:ro
- ./output:/data/output
Output Files
Inside the ./output/ directory, you will find two files:
1. report.json
A pretty-printed report of the current run, detailing every image's container path, status (analyzed, skipped, or failed), and decoded QR contents.
[
{
"path": "/data/photos/COW13GAS/20260718_002502.heic",
"status": "analyzed",
"qr_content": [
"https://example.com/item-123"
]
},
{
"path": "/data/photos/valid2/20260717_030551.jpg",
"status": "skipped",
"qr_content": [
"Sample QR Code Text"
]
}
]
2. cache.json
A persistent file mapping image BLAKE2b hashes to their decoded QR content. Do not delete this file if you wish to keep skipping already processed files.
- Filename checking (default): Matches the path of the file against the
"path"field of cache entries. If a match is found, it skips hash computation entirely. - Hash checking fallback (or when
--no-check-filenamesis active): If the path is not found in the cache or filename checking is disabled, the tool computes the BLAKE2b hash. If the hash exists in the cache, the tool detects it (e.g., if a file is renamed or moved), updates its path in the cache, and skips re-analysis.
Test files
Download a set of 16 test photos at https://vitapavlik.cz/hiddenfiles/docqr-test-data-2026-07-18.zip
Disclaimer
This project was entirely vibe-coded with Gemini 3.5 Flash (high thinking
effort setting). See prompt.txt.