Skip to content

Commit d384137

Browse files
yarikopticjulia-pfarrpre-commit-ci[bot]
authored
A helper tool to produce QR codes to be used on website and elsewhere (#509)
Co-authored-by: Julia-Katharina Pfarr <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 850bd77 commit d384137

File tree

4 files changed

+277
-0
lines changed

4 files changed

+277
-0
lines changed
35.2 KB
Loading
Lines changed: 133 additions & 0 deletions
Loading

tools/mkqrcode.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import logging
5+
import re
6+
7+
import qrcode
8+
from PIL import Image
9+
from pyzbar.pyzbar import decode
10+
from qrcode.image.styledpil import StyledPilImage
11+
from qrcode.image.styles.moduledrawers.pil import GappedSquareModuleDrawer
12+
13+
# Create a dedicated logger
14+
lgr = logging.getLogger(__name__)
15+
16+
17+
def sanitize_filename(filename):
18+
sanitized = re.sub(r"[^\w]", "_", filename)
19+
return sanitized
20+
21+
22+
def validate_compression_qualities(qualities):
23+
valid_qualities = {"L", "M", "Q", "H"}
24+
if not all(q in valid_qualities for q in qualities):
25+
raise ValueError(
26+
f"Invalid compression qualities specified. Use any combination of: {', '.join(valid_qualities)}."
27+
)
28+
return qualities
29+
30+
31+
def main(text, input_image, output_image, compression_qualities):
32+
lgr.debug(f"Processing text: {text}")
33+
kws = {}
34+
35+
if input_image:
36+
lgr.debug(f"Embedding image: {input_image}")
37+
kws["embeded_image_path"] = input_image
38+
39+
for q in compression_qualities:
40+
compress_quality = getattr(qrcode.constants, f"ERROR_CORRECT_{q}")
41+
42+
qr = qrcode.QRCode(error_correction=compress_quality)
43+
qr.add_data(text)
44+
45+
img = qr.make_image(
46+
image_factory=StyledPilImage,
47+
module_drawer=GappedSquareModuleDrawer(),
48+
**kws,
49+
)
50+
img.save(output_image)
51+
lgr.debug(f"Saved QR code image to: {output_image}")
52+
53+
decoded = decode(Image.open(output_image))
54+
55+
if len(decoded) != 1:
56+
lgr.debug(f"With error correction {q} got {len(decoded)} results.")
57+
continue
58+
59+
decoded_data = decoded[0]
60+
lgr.debug(
61+
f"DEBUG: error correction {q} - quality: {decoded_data.quality}"
62+
)
63+
64+
if decoded_data.quality < 0.5:
65+
lgr.warning(
66+
f"With error correction {q} got too low quality {decoded_data.quality}."
67+
)
68+
continue
69+
70+
text_decoded = decoded_data.data.decode()
71+
if text_decoded != text:
72+
lgr.warning(
73+
f"With error correction {q} decoded text does not match: {text_decoded!r} instead of {text!r}"
74+
)
75+
continue
76+
77+
lgr.info(
78+
f"Successfully decoded text: {text_decoded!r} encoded with compress quality {q}. File {output_image}"
79+
)
80+
break
81+
else:
82+
lgr.error(
83+
f"Neither of error correction levels was good enough. File {output_image} might be unreadable."
84+
)
85+
86+
87+
if __name__ == "__main__":
88+
parser = argparse.ArgumentParser(
89+
description="Generate and decode QR codes."
90+
)
91+
parser.add_argument("text", help="Text to encode in the QR code.")
92+
parser.add_argument(
93+
"-i", "--input-image", help="Path to the input image (optional)."
94+
)
95+
parser.add_argument(
96+
"-o", "--output-image", help="Path to the output image (optional)."
97+
)
98+
parser.add_argument(
99+
"-c",
100+
"--compression-qualities",
101+
default="LMQH",
102+
help="Compression qualities (default: LMQH). Choose any combination of L, M, Q, H.",
103+
)
104+
parser.add_argument(
105+
"-l",
106+
"--log-level",
107+
default="INFO",
108+
help="Set the logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL).",
109+
)
110+
111+
args = parser.parse_args()
112+
113+
# Setup logging
114+
logging.basicConfig(
115+
level=args.log_level.upper(),
116+
format="%(asctime)s - %(levelname)s - %(message)s",
117+
)
118+
119+
# Validate compression qualities
120+
try:
121+
compression_qualities = validate_compression_qualities(
122+
args.compression_qualities
123+
)
124+
except ValueError as e:
125+
lgr.error(e)
126+
exit(1)
127+
128+
# Sanitize output filename
129+
output_filename = (
130+
args.output_image
131+
if args.output_image
132+
else f"{sanitize_filename(args.text)}.png"
133+
)
134+
135+
main(args.text, args.input_image, output_filename, compression_qualities)

tools/mkqrcode_bids

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
3+
set -eu
4+
5+
bids_logo=$(dirname $0)/../docs/assets/img/logos/BIDS_logo_black_square_in_circle.png
6+
7+
set -x
8+
9+
$(dirname "$0")/mkqrcode.py -i "$bids_logo" "$@"

0 commit comments

Comments
 (0)