CV QR Encode
Generate scannable QR codes, correctly spaced
- qr_image
- modules
- version
Text in, QR bitmap out, all inside the graph. If your workflow needs to stamp a code onto an image - a link, a caption, a serial number, a per-generation seed encoded for later - this is the node that makes it instead of you leaving ComfyUI for a website.
How it works
cv2.QRCodeEncoder writes the symbol, and the node upscales it with nearest-neighbour so the modules stay crisp - a bilinear-upscaled QR is a QR that scans badly, because the finder pattern's hard edges get smeared. module_size is pixels per module: 1 is the raw bitmap (too small for most scanners), 6–12 is the sweet spot for print or screen.
Two knobs deserve actual thought:
error_correction is the Reed-Solomon level - how much of the code can be damaged, occluded, or obscured and still decode. L is about 7% recovery, M 15%, Q 25%, H 30%. Higher levels leave less room for data, so the symbol grows for the same payload. If you're pasting a logo over the centre or printing onto something that gets handled, go up; if you're engraving pixels, M is a sane default.
quiet_zone is the one that catches people out. OpenCV frames the symbol with only two quiet-zone modules per side where the QR spec asks for four. The default of 2 here adds the missing margin, making the output spec-compliant. If you're compositing a code onto a busy background, raise it - scanners need that white ring, and a code with no breathing room fails for reasons that look like nothing is wrong.
version fixes the symbol size (v = 21 + 4·(v−1) modules square, so v1 is 21x21 and v40 is 177x177); leave it at 0 and OpenCV picks the smallest version that fits your payload at the chosen error-correction level. The mode dropdown controls the payload encoding - auto picks the densest mode that fits; numeric and alphanumeric pack more characters per module at the cost of a restricted alphabet; byte carries arbitrary bytes; ECI tags the payload as UTF-8, which is what you want for non-Latin text; kanji packs Shift-JIS.
Structured Append
Set structure_number above 1 and the message is split across that many codes - the output becomes an IMAGE batch of the parts, white-padded to a common size. This is the trick for payloads too long for one symbol, and the parts decode as a single joined message only when a decoder sees all of them; CV QR Detect writes the joined text on the first part.
Inputs and outputs
text is the payload, and it has to be representable in the chosen mode and fit the chosen version - otherwise the node reports OpenCV's error rather than writing a corrupt code, which is the behaviour you want.
Alongside qr_image you get modules, the encoder bitmap's side length in modules including OpenCV's two-module quiet zone but excluding your extra one (so version 1 reads 25), and version, the version actually used - which is how you find out what auto resolved to. Both are useful sanity checks when a long payload quietly bumps you up several versions and your code comes out much denser than planned.
The output is an ordinary IMAGE with three identical channels, so it composites, scales, rotates and saves like any other image. A common pattern: encode, then warp the code into place with CV Quad Warp in fill mode so it sits on a photographed surface.
Installing
ComfyUI Manager → search "ComfyUI CV", or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
# restart ComfyUI
Python ≥ 3.12, a recent ComfyUI on the V3 node API, and opencv-contrib-python-headless~=5.0.0.93 (plus numpy, torch). No model files - OpenCV's encoder is self-contained, unlike the ONNX-based WeChat detector in the same pack.
Gotchas
Don't upscale the output with a smooth filter. A Lanczos or bicubic resize of a QR adds ringing around module edges. If you must resize, keep it integer-scaled and nearest-neighbour, or generate at the size you need via module_size.
A code that won't scan is usually a margin or contrast problem, not an encoder problem. Check the quiet zone, and make sure the code isn't dark-on-dark or light-on-light after compositing.
Non-Latin text needs the ECI mode. Encode Japanese or Cyrillic with auto and you may get a symbol that decodes to mojibake on a phone.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| text | STRING | https://opencv.org | Payload to encode. Must be representable in the chosen 'mode' (numeric = digits only, alphanumeric = 0-9 A-Z and $%*+-./: and space) and must fit the chosen version - otherwise the node reports the OpenCV error instead of writing a corrupt code. |
| error_correction | COMBO | M (~15% recovery) | Reed-Solomon error-correction level: how much of the code may be damaged/occluded and still decode. Higher levels leave less room for data (the symbol grows) but survive print wear, glare and logos pasted over the centre. |
| mode | COMBO | auto (pick the densest that fits) | Payload encoding. 'auto' lets OpenCV choose the densest mode the text fits. numeric/alphanumeric pack more characters per module but restrict the alphabet; byte carries arbitrary bytes; ECI (UTF-8) tags the payload as UTF-8 for non-Latin text; kanji packs Shift-JIS. |
| version | INT | 00–40 | Symbol version = size in modules (v = 21 + 4*(v-1) modules square). 0 = auto: the smallest version that fits. A fixed version keeps a set of codes the same physical size; too small for the payload is an error. |
| module_size | INT | 81–64 | Pixels per QR module in the output image (nearest-neighbour upscale). 1 = the raw bitmap (one pixel per module) - too small for most scanners; 6-12 is a good print/screen size. |
| quiet_zone | INT | 20–16 | EXTRA white border in modules, added around the TWO quiet-zone modules OpenCV emits. The QR spec asks for four, so 2 (the default) is the spec-compliant margin; raise it when the code is composited onto a busy background. |
| structure_number | INT | 11–16 | Structured Append: number of codes the message is split over (1 = a single code). The parts decode into one message only when a decoder sees all of them; 'CV QR Detect' writes the joined message on the first part. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| qr_image | IMAGE | The QR code as an IMAGE (white background, black modules, 3 identical channels). A batch of 'structure_number' frames in Structured Append mode. |
| modules | INT | Side length of the encoder's bitmap in modules, its 2-module quiet zone included and the extra 'quiet_zone' excluded (version 1 = 25). |
| version | INT | QR version actually used (1-40), i.e. what 'auto' resolved to. 0 if the size is not a valid version. |