What is this?
QRtsy is an experimental Python toolkit for making pretty QR codes. It's pronounced "cue-artsy" which I think captures the spirit of the project pretty well.
QRtsy is designed as a reusable, encoder-neutral Python library. It includes integration with the Segno QR code encoder library, a command-line interface, and a local browser application for interactively trying different settings. The source code for the project can be found at https://codeberg.org/newbery/qrtsy. See the project README for installation and developer instructions. The document you are reading here is more of a user guide and a high-level discussion of how this works.
QRtsy is an experiment. Many artistic choices spend some of the visual redundancy that normally makes QR codes resilient for scanning with less than optimal lighting or equipment, or even if printed on a sheet of paper partially mangled by your cat. Be cautious with the output from this application. Make something nice, then scan it from the devices, distances, print sizes, and lighting conditions that make sense for your use case.
Go ahead and play with it. You won't break anything. Probably.
Acknowledgements
QRtsy owes its biggest debt to Andrew Taylor's Dithered QR Code Generator and his very readable technical explanation. In particular, the idea of shrinking QR samples and diffusing the luminance error caused by forced QR pixels sounded very cool and made me wonder how well that technique would work with full color images (Andrew's implementation converted all images into a dithered greyscale). My first try at this looked promising but, at some point, I decided it would be more fun to lean into my Python strengths and just recreate it in Python.
There already exist plenty of QR code generators written in Python but none of them seemed to check off all the boxes I wanted which gave me enough of an excuse to reinvent this particular wheel (Not that I need much of an excuse). So that's how I ended up building this app that no one asked for. QRtsy is mostly just a tool for me to explore what can be done with QR codes and features are being added as I learn more.
I didn't try to reinvent everything. QRtsy itself doesn't actually create the base QR code onto which most of these artistic effects are applied as post-processing steps. QRtsy is designed instead to make it easy to integrate with existing third-party python libraries that already do the basic QR code generation pretty well, like qrcode, qrcodegen, or segno. QRtsy currently only includes example integration for the Segno library (since I was kind of interested in playing with Segno's plugin architecture and Segno's output gets me very close to a semantic model of the QR code matrix that makes it easier to target the various parts of the QR code in post-processing).
The QRtsy project icon was generated by the QRtsy server app using Painter Artist by Gan Khoon Lay from Noun Project (licensed under CC BY 3.0) as the background image.
The QR Code standard itself comes from DENSO WAVE, whose public documentation is a good first stop to learn about the terminology and the constraints that we are trying to work around to get pretty pictures that still scan.
Settings tour
The application is intentionally a workshop for experimentation, not a polished QR code vending machine. Here is what every current control does, with links into the technical section when you are curious about the machinery underneath.
Content
- Background image
The picture QRtsy fits behind the QR matrix. With no upload, the server uses the selected solid background color. See image preparation.
- Background color
Chooses the solid fallback background used when no image is uploaded. The default is white. See image preparation.
- QR code text
The text, URL, or other content passed to the QR encoder. See the pipeline.
- Error correction
Selects L, M, Q, or H. More correction adds redundancy, usually at the cost of a larger symbol for the same payload. See scannability.
- Version
Selects QR versions 1-40, or lets Segno choose automatically. Higher versions have more modules and more capacity. See QR anatomy.
- Mask
QR optimized lets Segno choose the mask with the lowest standard QR mask penalty. Image optimized renders all eight masks and chooses the one with the lowest image-difference score. All 8 masks displays every candidate side-by-side with both scores, and selecting 0-7 uses that mask explicitly. Different masks rearrange the visible data pattern without changing the decoded content. See QR anatomy.
Image
- Image mode
Uses the source as full color, posterized color, or grayscale/monochrome input. See image preparation.
- Posterize levels
Reduces each color channel to a smaller set of values when Posterized mode is active. See image preparation.
- Dither
Enables constrained Floyd-Steinberg dithering in Monochrome mode. See monochrome dithering.
- Dither cell size
Uses either individual raster pixels or square cells the same size as the QR sampling core. Core-sized cells enable the Andrew Taylor-style subpixel effect. See monochrome dithering.
- Dither intensity source
Uses QRtsy linear-light luminance or the source image green channel. Taylor's implementation uses the green channel. See monochrome dithering.
- Dither gamma
Raises the selected source intensity to this power before contrast and brightness are applied. Taylor uses 2.2. See monochrome dithering.
- Dither contrast
Scales source intensity around the midpoint before diffusion. See monochrome dithering.
- Dither brightness
Shifts source intensity after contrast adjustment. See monochrome dithering.
- Dither threshold
Moves the black/white decision point used by monochrome dithering. See monochrome dithering.
- Minimum dither intensity
Clamps very dark source samples before diffusion, limiting the error introduced by extreme image regions. See monochrome dithering.
- Maximum dither intensity
Clamps very bright source samples before diffusion for the same reason. See monochrome dithering.
- Serpentine diffusion
Alternates the scan direction on successive rows, reducing one-way artifacts in directional diffusion modes. See monochrome dithering.
- Image fit area
Chooses whether the source image is fitted to the encoded QR region or the full decorative canvas. The quiet zone always uses the selected background color. See image preparation.
- Fit
Chooses whether the source image covers the selected fit area, fits inside it, or stretches to it. See image preparation.
QR rendering
- Module size
Sets how many output pixels represent one QR module. See sampling cores.
- Core size
Sets the central square that QRtsy forces to the required QR value for shrinkable modules. See sampling cores.
- Module tint
Lets image-exposing QR cores and texture borrow local background color while preserving their required dark/light polarity. 0 uses canonical black and white; larger values move farther toward the local source color. Tinting is disabled in Monochrome mode. See sampling cores.
- Quiet-zone modules
Adds clear margin around the rendered symbol. The default is four modules, matching the standard QR Code quiet-zone recommendation. See scannability.
- Canvas margin
Adds a decorative module grid outside the quiet zone. Synthetic modules in this margin use the same core style and can participate in tinting, texture, and monochrome dithering without reducing the QR quiet zone. See canvas margin.
- Canvas seed
Selects the repeatable dark/light pattern used by synthetic canvas modules. Changing the seed tries another arrangement; it has no effect when the canvas margin is zero. See canvas margin.
- Module grid overlay
Displays a separate transparent one-pixel grid over the preview without modifying the rendered QR image. QRtsy chooses black or white according to which gives greater contrast with the fitted background. The overlay is retained when hidden, so it can be toggled back on instantly. See QR anatomy.
- Shrink finder patterns
Shrinks the three finder patterns to their sampling cores. See function patterns.
- Shrink separators
Shrinks the light separator modules around the finder patterns. See function patterns.
- Shrink alignment markers
Shrinks alignment-pattern modules to cores. See function patterns.
- Shrink timing patterns
Shrinks timing-row and timing-column modules to cores. See function patterns.
Free-pixel texture
- Texture
Chooses None, ordered dither, seeded noise, randomized error diffusion, or directional Flow diffusion. See free-pixel texture.
- Texture strength
Controls how strongly a visible texture block moves toward the current dark or light QR endpoint color. See polarity & strength.
- Texture density
Sets the target population of visible contrasting texture blocks. See density.
- Texture masking
Controls how strongly texture is restricted to smooth, neutral, near-white background regions. 0 leaves texture unrestricted; 1 applies the full adaptive mask. Values near 1 deliberately have fine-grained response. See masking.
- Texture seed
Makes noise and randomized error diffusion deterministic while letting you try another arrangement. See seeds & diffusion.
Advanced compensation
- Compensation
None leaves the rendered pixels alone. Local compensation adjusts free pixels within each module to recover luminance changed by QR cores and texture. See compensation.
- Strength
Controls how much of the measured local luminance error QRtsy attempts to correct. See compensation.
Presets & browser features
Built-in and custom presets capture renderer settings without baking in the payload, image or fallback background color, QR version, mask, or error-correction choice. The preset display shows the applied preset while the current render settings still match it, and clears when those settings change. New presets are saved with a name and optional description. More on presets.
Browser persistence stores custom presets in version-specific browser storage. "Clear all" removes QRtsy presets from every saved version. The separate Export / Import section can round-trip named render settings as JSON.
Per-field undo appears only when a field differs from the page default and restores that one setting without disturbing its neighbors.
Live preview rerenders as settings change and reports the actual QR version, mask, and error-correction level used.
Download naming derives a friendly PNG filename from the uploaded image, while leaving the name editable before saving.
Export / Import can save or load named render settings as JSON, or produce a fuller reproducibility file containing both a command-line invocation and equivalent Python code, including settings that renderer presets intentionally leave out.
Technical tour
This is an attempt to explain what all these knobs do. It is intentionally modest with just enough detail to describe the features at a high-level and where some of the tradeoffs come from. (Yeah, I know there is maybe too much technical jargon below. I'll work on cleaning this up. Another work-in-progress 🤓)
The pipeline
The core QRtsy renderer does not encode payloads. It accepts a semantic ModuleMatrix: a square grid whose cells know both their required dark/light value and their role: data, finder, timing, alignment, format, version, separator, and so on. The included Segno integration creates that semantic matrix from a Segno QR code. The renderer then combines the matrix with an image and RenderOptions.
Keeping those jobs separate is useful. Segno can stay very good at QR encoding and QRtsy can stay focused on the pretty post-processing. It also means another QR encoder can be adapted later without rewriting the artistic renderer.
QR anatomy
The semantic matrix helps QRtsy to keep track of the different roles for each square (aka module). The large corner finders help a scanner locate and orient the symbol. Timing patterns establish the module grid. Alignment patterns help compensate for distortion. Format modules identify the error-correction level and mask, and version modules appear on larger versions. Data modules carry the encoded payload and its error-correction codewords.
QRtsy treats these roles differently. Data, format, version, and the fixed dark module use central sampling cores by default. Finder, separator, timing, and alignment modules remain full-sized unless their corresponding shrink switch is enabled.
The browser can also compare the eight standard QR masks. QR optimized uses the encoder's normal QR mask-penalty selection. Image optimized instead renders every mask and chooses the one with the lowest normalized mean RGB difference from the fitted source image over the encoded QR region. The All 8 masks view shows both scores for every candidate. The image-difference score is an appearance metric, not a measure of scan reliability.
Sampling cores
A QR module occupies module size × module size output pixels. For shrinkable modules, QRtsy forces only a centered core size × core size square to the required QR endpoint color and leaves the surrounding pixels available for the image and optional texture. Smaller cores reveal more image; larger cores give the scanner a more conventional target.
Module tinting can adapt those forced dark/light colors toward the local source image color. QRtsy limits the resulting luminance so a dark core remains dark and a light core remains light. A custom Python ModuleStyle can also replace the default centered square with another core geometry; the same style is applied to synthetic canvas modules.
Some of these techniques push the boundaries of what is allowed by the standard and what is reliably scanned in the real world. Scanner sampling behavior varies, so the right core size is an empirical choice for the way the final image will actually be used.
Canvas margin
The optional canvas margin extends the output beyond the QR code and its quiet zone in whole-module increments. QRtsy fills that outer area with synthetic dark/light modules generated deterministically from the canvas seed. These are decorative rather than encoded QR modules, but they use the same core style and can participate in module tinting, free-pixel texture, and monochrome dithering.
The QR quiet zone remains between the encoded symbol and this decorative margin. When Image fit area is set to QR code, the source image is fitted only under the encoded QR region and the outer canvas starts from the selected background color. When it is set to Canvas, the fitted source image also extends through the decorative margin; the quiet zone is still replaced by the background color.
Image preparation
When no source image is uploaded, the server starts with a solid white background; the Background color control can replace that with another solid color. A supplied image is fitted either to the encoded QR region or to the full decorative canvas, then resized according to the Fit setting: cover, contain, or stretch. The quiet zone always uses the selected background color. Full-color mode keeps the fitted RGB values. Posterized mode quantizes each channel to the requested number of levels. With dithering disabled, Monochrome mode converts the fitted image to grayscale. With Floyd-Steinberg dithering enabled, QRtsy instead derives a scalar intensity from either linear-light luminance or the source green channel and feeds that value into the dither pipeline.
Monochrome dithering.
Conventional Floyd-Steinberg dithering normally chooses black or white for a pixel, measures the resulting intensity error, and distributes that error to pixels that have not been processed yet. QRtsy's pixel-sized mode keeps that conventional constrained behavior while forcing required QR sampling pixels to their QR values.
The core-sized mode follows the technique used by Andrew Taylor's dithered QR generator. Each dither cell is exactly the same size as the sampling core. With the common module_size = 3 × core_size arrangement, every QR module therefore becomes a 3×3 dither-cell grid whose center cell carries the QR sample. QRtsy first forces those center cells and diffuses each resulting intensity error into all eight neighboring cells. It then makes a second Floyd-Steinberg pass over only the free cells, renormalizing the diffusion kernel whenever a neighboring cell is fixed by the QR code or a protected function pattern. This two-pass treatment is what makes otherwise random-looking QR cores blend much more naturally into the one-bit image.
Core-sized dithering requires module_size / core_size to be an odd integer so the sampling core lands exactly on one dither cell. QRtsy normally derives dither intensity from linear-light luminance. Taylor instead uses the green channel, then applies gamma 2.2, contrast, brightness, and a 0.05-0.95 intensity clamp before diffusion. QRtsy exposes each of those preprocessing choices independently; the andrew-taylor preset selects Taylor's values. The threshold then controls the black/white decision point. Serpentine mode alternates row direction; the preset disables it because Taylor's implementation scans every row left-to-right.
Free-pixel texture.
Free-pixel texture operates on core-sized blocks that do not overlap required QR samples. The goal is to make the regular sampling-core pattern less obvious by adding visually pleasing texture to the background.
The texture controls determine both how candidate free blocks are selected and how visible texture is distributed among them. The texture mode chooses the placement algorithm, while density, masking, and strength control how much texture appears, where it is allowed to appear, and how strongly each visible block contrasts with the underlying image.
More detail...
Modes
Ordered dither uses a deterministic Bayer threshold pattern. Noise dither uses a deterministic pseudo-random threshold. Error diffusion uses a seeded randomized processing order and diffuses quantization error into nearby unprocessed blocks, avoiding a strong left-to-right grain. Flow diffusion deliberately keeps a directional Floyd-Steinberg-like traversal and some pixel color assignment tricks that can produce a cloud-and-tendril texture that looks vaguely like flowing fluid.
For a general overview of dithering techniques, see these Wikipedia articles: Dither and Ordered dithering.
QRtsy's ordered dither mode uses the standard 8×8 Bayer dispersed-dot construction introduced by B. E. Bayer in 1973. The 64 ranked positions repeat across the texture grid and become centered thresholds using (rank + 0.5) / 64.
Polarity & strength
In the default texture modes, local background luminance decides which contrasting QR endpoint is useful: pale regions lean toward visible dark texture, dark regions toward visible light texture. Texture strength controls how far a rendered block moves toward the selected endpoint luminance. When module tinting is active, the painted texture uses the corresponding locally tinted dark/light color.
Density
Texture density controls the target population of visible contrasting blocks. It is deliberately separate from strength: density controls "how many" while strength controls "how far from the original luminance".
Masking
QRtsy samples a small neighborhood around each candidate block and scores how smooth, neutral, and near-white that neighborhood is. Texture masking blends between unrestricted texture and that adaptive background mask. A value of 0 disables the mask, while 1 applies it fully.
Seeds & QR cores
The seed makes randomized layouts repeatable. The required QR cores are protected from being overwritten when these randomized layouts are added in post-processing.
Advanced compensation
Also known as luminance correction or local compensation. I haven't decided on the best terminology for this.
A forced dark core in a pale module removes luminance; a forced light core in a dark module adds it. Texture can change the module's average luminance too. Local compensation runs after the required cores and texture have been rendered, compares each module against the prepared source image, and nudges the remaining free pixels toward the original total luminance. The effect can be subtle and not always so pretty. I'm still experimenting with this algorithm so it might improve in later iterations.
The luminance compensation correction is the slowest part of many high-resolution renders, which is why it is disabled by default. It's generally best to experiment with the other settings first and then try adding luminance compensation as a final tweak before generating the final image to download.
Presets
Most of the rendering options are serialized into RenderOptions which we can save as presets. Presets intentionally do not capture the uploaded image, fallback background color, payload, QR version, mask, or error-correction level (I might change some of this later).
The built-in andrew-taylor preset selects a 3×3 core-cell layout, monochrome Floyd-Steinberg dithering, Taylor-style core-error diffusion, green-channel input with gamma 2.2 and a 0.05-0.95 intensity clamp, fixed left-to-right traversal, full finder/timing/alignment patterns, core-sampled separators, and stretch-to-square image fitting. It also keeps QRtsy's four-module quiet zone; Taylor's generator itself emits no margin and warns that one should be added for real use. The server defaults of Version 6 and High error correction match Taylor's generator defaults, but version, error correction, and mask are encoder choices and are intentionally not part of renderer presets.
Custom presets created in the server UI are application-owned state and live entirely in browser storage under a key containing the QRtsy version. The server does not maintain a custom-preset registry. Custom presets will therefore seem to disappear whenever the server version is updated. Browser storage is a convenience rather than long-term storage.
The preset selector also acts as a simple state indicator: it shows the currently applied preset while the current render settings still match that preset, and is blank after those settings have been changed. The Save action is enabled only in that changed state and opens a dialog for a new preset name and optional description.
The separate Export / Import section can round-trip render settings as named JSON presets. If the current settings match an existing preset, JSON export reuses that preset's name and description. Otherwise it asks for a name and optional description before exporting. Importing one of these files adds it to the Presets section and selects it, but does not change the current render settings until Apply is selected.
The "Export settings as CLI/Python" is intended for reproducibility beyond the qrtsy-server UI: the export includes a CLI invocation and equivalent Python example covering the payload, encoder selections, source/fill information, output name, and all current RenderOptions.
Scannability
QR error correction can recover damaged codewords, but artistic rendering is still spending robustness. Higher error-correction levels provide more recovery capacity, while also reducing payload capacity for a given version.
The standard QR quiet zone around the margin is four modules wide so QRtsy uses four modules as its default. Reducing the margin is allowed, but it is one more thing that can make real-world scanning less forgiving. A code that scans from your monitor under office lighting is just a first test. To test it robustly, print it, shrink it, tilt it, crumple the paper a bit, and maybe borrow somebody else's phone.
The optional decorative canvas margin sits outside this quiet zone; increasing the canvas margin does not reduce the configured quiet-zone width.
More QR resources
Still curious? Go ahead, dive into this rabbit hole. I dare you...
DENSO WAVE - What is a QR Code? - friendly official overview.
DENSO WAVE - Versions and capacity - versions 1 through 40 and how size grows.
DENSO WAVE - Error correction - the L/M/Q/H tradeoff.
DENSO WAVE - Symbol area and quiet zone - including the standard four-module margin.
DENSO WAVE - QR Code standardization - standards history and specification pointers.
ISO/IEC 18004:2024 - the current published QR Code symbology standard.
Segno documentation - the Python encoder used by QRtsy's optional integration.
Thonky QR Code Tutorial - a detailed programmer-friendly walk through encoding, masking, placement, and error correction.
Nayuki QR Code generator library - compact multi-language implementations plus a useful technology overview.
Andrew Taylor - How to make error-diffused QR codes - the direct inspiration for much of QRtsy's image/QR experimentation.
What's next?
There are so many ideas out there on how to make aesthetically pleasing QR codes. My research is just beginning and I plan to document my findings for the recreational reading by all my fellow QR code enthusiasts out there. If you're impatient, here is a sampling of some of the more interesting bits,
Papers
Chu, H.-K., Chang, C.-S., Lee, R.-R., & Mitra, N. J. (2013). Halftone QR codes. ACM Transactions on Graphics, 32(6), Article 217, 1-8. DOI: 10.1145/2508363.2508408
Lin, S.-S., Hu, M.-C., Lee, C.-H., & Lee, T.-Y. (2015). Efficient QR code beautification with high quality visual content. IEEE Transactions on Multimedia, 17(9), 1515-1524. DOI: 10.1109/TMM.2015.2437711
Xu, M., Su, H., Li, Y., Li, X., Liao, J., Niu, J., Lv, P., & Zhou, B. (2019). Stylized aesthetic QR code. IEEE Transactions on Multimedia, 21(8), 1960-1970. DOI: 10.1109/TMM.2019.2891420
Xu, M., Li, Q., Niu, J., Su, H., Liu, X., Xu, W., Lv, P., Zhou, B., & Yang, Y. (2021). ART-UP: A novel method for generating scanning-robust aesthetic QR codes. ACM Transactions on Multimedia Computing, Communications, and Applications, 17(1), Article 3418214. DOI: 10.1145/3418214
Su, H., Niu, J., Liu, X., Li, Q., Wan, J., Xu, M., & Ren, T. (2021). ArtCoder: An end-to-end method for generating scanning-robust stylized QR codes. Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 2277-2286
Su, H., Niu, J., Liu, X., Li, Q., Wan, J., & Xu, M. (2021). Q-Art Code: Generating scanning-robust art-style QR codes by deformable convolution. Proceedings of the 29th ACM International Conference on Multimedia, 722-730. DOI: 10.1145/3474085.3475239
Wu, G., Liu, X., Jia, J., Cui, X., & Zhai, G. (2024). Text2QR: Harmonizing aesthetic customization and scanning robustness for text-guided QR code generation. Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR), 8456-8465.
preprint: https://arxiv.org/abs/2403.06452
project page: https://mulns.github.io/Text2QR/
source code: https://github.com/mulns/Text2QR
Liao, J.-W., Wang, W., Wang, T.-S., Peng, L.-X., Weng, J.-H., Chou, C.-F., & Chen, J.-C. (2025). DiffQRCoder: Diffusion-based aesthetic QR code generation with scanning robustness guided iterative refinement. Proceedings of the IEEE/CVF Winter Conference on Applications of Computer Vision (WACV), 5916-5925.
preprint: https://arxiv.org/abs/2409.06355
project page: https://jwliao1209.github.io/DiffQRCoder/
source code: https://github.com/jwliao1209/DiffQRCoder
Cox, R. (2012). QArt codes. Constrained QR encoding.
source code: https://github.com/rsc/qr
The QArt implementation is in the qart portion of this repository. QArt uses the degrees of freedom in Reed-Solomon encoding and Gauss-Jordan elimination to produce valid QR matrices whose module patterns better approximate a target image.
Demaine, E. D., & Demaine, M. L. (2025). 3D QR codes. The Demaine Laboratory, Massachusetts Institute of Technology.
The "Layered Paper QR Codes with Images" section describes dividing each QR module into a 6 x 6 grid, preserving the center 2 x 2 submodules for QR information and using the remaining submodules for the image.
Interactive QR Designer: https://erikdemaine.org/prints/QR/layers/
Taylor, A. (2025). Making your own dithered QR codes.
online app: https://www.andrewt.net/dithered-qr-codes/
The technical article describes the QR-aware two-pass error-diffusion technique: first diffuse the visual error caused by forced QR data pixels, then perform ordinary error-diffusion dithering on the free image pixels.
Floyd, R. W., & Steinberg, L. (1976). An adaptive algorithm for spatial grey scale. Proceedings of the Society for Information Display, 17(2), 75-77.
https://isgwww.cs.uni-magdeburg.de/~stefans/npr/entry-Floyd-1976-AAS.html
This is the original Floyd-Steinberg error-diffusion algorithm on which Taylor's QR-aware error-diffusion technique is based.
Bayer, B. E. (1973). An Optimum Method for Two-Level Rendition of Continuous-Tone Pictures. IEEE International Conference on Communications. 1: 11-15.
The dispersed-dot ordered-dithering technique used by QRtsy's ordered texture mode.
Software
dithered-qr-codes
source code: https://codeberg.org/andrew-t/dithered-qr-codes
segno
source code: https://github.com/heuer/segno
documentation: https://segno.readthedocs.io/
qrcode-artistic (segno)
source code: https://github.com/heuer/qrcode-artistic
documentation: https://segno.readthedocs.io/en/stable/artistic-qrcodes.html
python-qrcode
source code: https://github.com/lincolnloop/python-qrcode
The StyledPilImage subsystem provides module shapes such as rounded, circular, and gapped modules; color masks and gradients; separate finder-pattern rendering; and embedded images.
qrcodegen
project page: https://www.nayuki.io/page/qr-code-generator-library
source code: https://github.com/nayuki/QR-Code-generator
fuqr
source code: https://github.com/zhengkyl/fuqr
Includes TypeScript and Rust implementations and examples involving image overlays, dithering, constrained pixel-art generation, and logo fitting.
go-qrcode
source code: https://github.com/yeqown/go-qrcode
Includes customizable module shapes, colors, icons, and halftone QR-code rendering.
hitherdither
source code: https://github.com/hbldh/hitherdither
Pillow-oriented Python implementation of Bayer ordered dithering and several error-diffusion algorithms.
dithering
source code: https://github.com/tensorhead/dithering
Python library with a Bayer implementation that does the same centered mean-preserving (index + 0.5) / n^2 threshold normalization used in QRtsy.
"QR Code" is a registered trademark of DENSO WAVE INCORPORATED.