Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A helper tool to produce QR codes to be used on website and elsewhere #509

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 133 additions & 0 deletions docs/assets/img/logos/BIDS_logo_black_square_in_circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
94 changes: 94 additions & 0 deletions tools/mkqrcode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env python3

import argparse
import logging
import re

from pyzbar.pyzbar import decode
from PIL import Image

import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers.pil import GappedSquareModuleDrawer

# Create a dedicated logger
lgr = logging.getLogger(__name__)

def sanitize_filename(filename):
sanitized = re.sub(r'[^\w]', '_', filename)
return sanitized

def validate_compression_qualities(qualities):
valid_qualities = {'L', 'M', 'Q', 'H'}
if not all(q in valid_qualities for q in qualities):
raise ValueError(f"Invalid compression qualities specified. Use any combination of: {', '.join(valid_qualities)}.")
return qualities

def main(text, input_image, output_image, compression_qualities):
lgr.debug(f"Processing text: {text}")
kws = {}

if input_image:
lgr.debug(f"Embedding image: {input_image}")
kws['embeded_image_path'] = input_image

for q in compression_qualities:
compress_quality = getattr(qrcode.constants, f'ERROR_CORRECT_{q}')

qr = qrcode.QRCode(error_correction=compress_quality)
qr.add_data(text)

img = qr.make_image(image_factory=StyledPilImage,
module_drawer=GappedSquareModuleDrawer(),
**kws)
img.save(output_image)
lgr.debug(f"Saved QR code image to: {output_image}")

decoded = decode(Image.open(output_image))

if len(decoded) != 1:
lgr.debug(f"With error correction {q} got {len(decoded)} results.")
continue

decoded_data = decoded[0]
lgr.debug(f"DEBUG: error correction {q} - quality: {decoded_data.quality}")

if decoded_data.quality < 0.5:
lgr.warning(f"With error correction {q} got too low quality {decoded_data.quality}.")
continue

text_decoded = decoded_data.data.decode()
if text_decoded != text:
lgr.warning(f"With error correction {q} decoded text does not match: {text_decoded!r} instead of {text!r}")
continue

lgr.info(f"Successfully decoded text: {text_decoded!r} encoded with compress quality {q}. File {output_image}")
break
else:
lgr.error(f"Neither of error correction levels was good enough. File {output_image} might be unreadable.")

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate and decode QR codes.")
parser.add_argument("text", help="Text to encode in the QR code.")
parser.add_argument("-i", "--input-image", help="Path to the input image (optional).")
parser.add_argument("-o", "--output-image", help="Path to the output image (optional).")
parser.add_argument("-c", "--compression-qualities", default="LMQH",
help="Compression qualities (default: LMQH). Choose any combination of L, M, Q, H.")
parser.add_argument("-l", "--log-level", default="INFO", help="Set the logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL).")

args = parser.parse_args()

# Setup logging
logging.basicConfig(level=args.log_level.upper(), format='%(asctime)s - %(levelname)s - %(message)s')

# Validate compression qualities
try:
compression_qualities = validate_compression_qualities(args.compression_qualities)
except ValueError as e:
lgr.error(e)
exit(1)

# Sanitize output filename
output_filename = args.output_image if args.output_image else f"{sanitize_filename(args.text)}.png"

main(args.text, args.input_image, output_filename, compression_qualities)
9 changes: 9 additions & 0 deletions tools/mkqrcode_bids
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash

set -eu

bids_logo=$(dirname $0)/../docs/assets/img/logos/BIDS_logo_black_square_in_circle.png

set -x

$(dirname "$0")/mkqrcode.py -i "$bids_logo" "$@"