No description
  • Python 36.7%
  • JavaScript 29.6%
  • CSS 18.7%
  • HTML 12.3%
  • Dockerfile 2.3%
  • Other 0.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-27 03:41:43 +02:00
src filter out the nnpack warning 2026-09-27 03:23:57 +02:00
web make the console stream line buffered instead of block buffered 2026-09-27 03:41:43 +02:00
.gitignore add web interface 2026-09-27 02:28:10 +02:00
docker-compose.yml add web interface 2026-09-27 02:28:10 +02:00
Dockerfile add web interface 2026-09-27 02:28:10 +02:00
LICENSE.txt add license 2026-07-18 02:12:39 +02:00
prompt.txt check filenames 2026-07-19 22:01:51 +02:00
README.md add web interface 2026-09-27 02:28:10 +02:00
requirements.txt fix requirements warning for yolov8 2026-07-19 23:27:37 +02:00
run make the console stream line buffered instead of block buffered 2026-09-27 03:41:43 +02:00

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 qreader library for state-of-the-art QR code detection and decoding (even for skewed, blurry, or low-resolution images).
  • Format Agnostic: Natively supports jpg, png, and heic (via pillow-heif) along with standard formats like webp, bmp, tiff, and gif.
  • Incremental Runs (Skip Logic): By default, the tool matches absolute paths inside the container against previous runs in cache.json to skip hash computation entirely. If not matched, or if --no-check-filenames is specified, it falls back to computing and checking a BLAKE2b hash 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 Containers in the Background

Start the containers using Compose. This will start the docqr backend scanner and the web frontend service:

docker compose up -d

3. Using the Web Interface

Once the containers are running, navigate in your browser to:

http://localhost:5001

The web application provides:

  • "Start Analysis" Button: Triggers the scan inside the docqr container and streams live stdout output directly to an interactive terminal console. The button reflects real-time status ("Analysis in progress...").
  • Live Search: Fast, debounced search across decoded QR code texts and photo filenames/folders (searches cache.json with fallback to report.json).
  • Slow Media Storage UX: Per-image loading spinners, lazy loading, and an overall progress bar ("Loading images: X / Y") designed for slower media mounts.
  • HEIC / HEIF / TIFF Transcoding: Transparent on-the-fly conversion of unsupported browser formats like .heic to .jpg.
  • Full EXIF & Metadata Inspector: Click on any photo card to open a lightbox showing the photo, decoded QR codes with copy-to-clipboard, camera settings (Make, Model, Exposure, Aperture, ISO, Focal length), GPS coordinates with interactive map links, and the full searchable raw EXIF table.

4. Running QR Analysis via CLI (Alternative)

You can still execute the scanner directly from the command line 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-filenames is 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.