CV QR Decode At Points
When OpenCV's detector misses the code but you can see it
- image
- points
- found
- decoded_count
- text
- texts
- straight_codes
A QR decoder that doesn't bother detecting. You hand it the four corners; it rectifies that quad and reads what's inside. It exists because OpenCV's detector is good on clean codes and falls over on the ones a human would have no trouble reading.
Why you'd separate detection from decoding
The normal path is CV QR Detect, which finds and reads codes in one shot. When that comes back empty on a code that is plainly visible - a small code in a busy photo, a code behind glare, a code at a steep angle, a code in a frame the detector's finder-pattern scan just doesn't like - you're stuck, unless you can outline the thing yourself.
That's the gap this fills. Get four corners by any means: CV Find Quadrilateral on a thresholded region, a contour, a homography you solved, CV Annotate Points clicked by hand on the preview, or a DNN detector. Feed them here and the payload still comes out. It keeps detection and decoding composable instead of welded together, which is a small design decision that makes the whole QR side of this pack usable on real photographs rather than on generated test images.
The trap, stated plainly
Give it the corners of the symbol, not of the white quiet zone around it. The decoder rectifies exactly the quad it is handed, so a quad that includes the margin is a rectified image of "some white space and a slightly shrunken code" - and it decodes to an empty string. This is the single most common failure with this node, and it's silent: you get a quad, found=false, and no hint that you were four pixels too generous.
Points are consumed four at a time in corner order, any winding, so CV QR Detect's corners output or a set of CV Find Quadrilateral corners both drop straight in. Trailing points that don't complete a quad are ignored, and undecodable quads come out as empty strings rather than errors - so a mixed set of good and bad quads gives you a partial result, which is what you want.
Inputs and outputs
image is the photo containing the code (an IMAGE batch uses frame 0). points is (N*4, 2) or (N, 4, 2). detector picks which cv2 decoder implementation reads the rectified patch - the classic one or the ArUco-based one, which can be more tolerant of small or warped symbols. mode picks straight (rectify and read, the normal case) or curved, for a code wrapped around a bottle or cylinder, which the classic detector only.
found is true when at least one quad produced a non-empty string, decoded_count is how many decoded, text is the strings joined one per line for a text preview, and texts is an (N,) string array aligned with the quads you supplied - so an empty entry tells you exactly which quad failed. straight_codes is an IMAGE batch of the rectified, binarised symbols, white-padded to a common size, which is the picture the decoder actually read: when something won't decode, previewing that is how you find out your corners are off by a few pixels.
Installing
ComfyUI Manager → search "ComfyUI CV", or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
# restart ComfyUI
Python ≥ 3.12 and a recent ComfyUI on the V3 node API, plus the pack's dependency set - opencv-contrib-python-headless~=5.0.0.93, numpy, torch. No model files: unlike the pack's CV WeChat QR Detect, this path is pure OpenCV.
Gotchas
Symbol corners, not quiet zone. Worth repeating because it's the failure everyone hits once.
The rectified patch is the debugging surface. straight_codes shows you the decoder's input. If it looks like a skewed or clipped version of the code, your quad is wrong; if it looks perfect and still won't decode, the code is damaged or the resolution is too low for that payload.
Curved mode is not a general fallback. It's for genuinely curved surfaces and fails on some flat codes, so switching modes when a flat code won't read is going the wrong way - fix the corners or the image quality instead.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| image | NPARRAY,IMAGE | Image containing the code(s). An IMAGE batch uses its first frame. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| points | NPARRAY | Corner points, (N*4, 2) or (N, 4, 2), in per-code corner order - e.g. the 'corners' output of 'OpenCV QR Detect' / 'CV Find Quadrilateral', or annotated points. cv2 expects the corners of the SYMBOL ITSELF (the outer finder-pattern corners), not of the white quiet zone around it - a quad that includes the margin decodes to an empty string. | |
| detector | COMBO | Which cv2 decoder implementation reads the rectified patch. Both accept externally supplied corners; the ArUco-based one can be more tolerant of small/warped symbols. | |
| mode | COMBO | straight: decodeMulti - perspective-rectify the quad and read it. curved: decodeCurved per quad, for codes wrapped around a curved surface (classic detector only). |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| found | BOOLEAN | True when at least one quad decoded to a non-empty string. |
| decoded_count | INT | How many of the supplied quads decoded. |
| text | STRING | Decoded strings, one per line - wire into 'Preview as Text'. |
| texts | NPARRAY | (N,) string array aligned with the supplied quads; an empty entry marks a quad that did not decode. |
| straight_codes | IMAGE | IMAGE batch of the rectified binarized symbols, one per decoded quad (white-padded to a common size). |