Nodes/ComfyUI CV/CV Barcode Detect
ComfyUI Node

CV Barcode Detect

EAN and UPC decoding in ComfyUI — yes, it reads the curved label on a bottle

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
CV Barcode Detect
  • image
  • found
  • barcode_count
  • text
  • bboxes
  • corners
  • connections
  • texts
  • formats
  • labels
◄gradient_threshold64►
◄downsampling_threshold512►
◄detector_scales0.01, 0.03, 0.06, 0.08►

Product barcodes are the one machine-readable code that shows up in almost every photo of a real object, and this node reads them: EAN-8, EAN-13, UPC-A, UPC-E. It's the linear-barcode sibling of CV QR Detect, and it exists as a curated node for the same structural reason - cv2.barcode.BarcodeDetector is a class, and the pack's ~470 auto-generated raw wrappers can only reach top-level functions.

Where you'd actually use it: cataloguing photos, pulling a SKU out of a product shot to drive prompts or filenames, checking that a generated image still contains a scannable code. On that last point - if your interest is barcodes and QR codes as art, that's a ControlNet trick (feed the pattern as a condition with heavy weight and let the model paint around it), not this. This node reads; it doesn't write.

How it works

It locates the bar region by finding dense parallel gradients, rectifies the region, and reads the relative bar widths. Because it rectifies rather than requiring a square-on shot, it copes with rotation, perspective, and the curve of a label wrapped around a bottle. Decoding itself happens at full resolution.

Inputs

  • image - an IMAGE batch uses its first frame; an NPARRAY (gray or BGR, any dtype) is one frame.
  • gradient_threshold - the minimum gradient magnitude for a pixel to count as part of a bar region. Lower it for faint, low-contrast, or slightly out-of-focus barcodes. Raise it to stop busy background texture from pretending to be bars.
  • downsampling_threshold - images bigger than this on the shorter side get downsampled for the search only. If you're hunting a small barcode in a 4000px photo and getting nothing, raise it.
  • detector_scales - comma-separated relative scales to search at, as a fraction of the image. Blank gives OpenCV's defaults (0.01, 0.03, 0.06, 0.08). Add a bigger value like 0.2 when the barcode fills the frame, a smaller one when it's tiny.

Outputs

There are a lot of them, and they're split the way you'd want: found (branch on it - don't gate nodes on a boolean widget), barcode_count, and text, which is every payload on its own line and drops straight into a core Preview as Text node.

Then the geometry: bboxes is a BOUNDING_BOX list for the core Draw BBoxes node; corners is (N*4, 2) float32 with the true quad corners; connections gives you edge index-pairs closing each quad, so CV Draw Connections can outline the rotated region exactly instead of drawing an axis-aligned box around it. labels puts each barcode's decoded digits on its first corner and blanks on the other three, so pairing corners with labels in CV Draw Labels writes the number once per barcode rather than four times.

texts and formats are (N,) string arrays - payload and symbology (EAN_13, EAN_8, UPC_A, UPC_E, or NONE when something was located but not decodable). That gap is informative: a located-but-NONE result means your bar region was found but the digits were unreadable, which is a focus or resolution problem, not a detection problem.

Zero barcodes is a valid result - empty outputs, found=false, no exception.

Install

Manager → ComfyUI CV, or:

cd ComfyUI/custom_nodes && git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Python ≥ 3.12, current ComfyUI (V3 API). cv2.barcode ships with the contrib wheel, which is the pack's declared dependency - if you later install plain opencv-python on top, the contrib submodules empty out and this node's class goes missing along with them.

Where people get burned

No encoder, unlike QR. OpenCV has QRCodeEncoder; there is no barcode writer in OpenCV. If you need to generate a barcode, that's a different tool.

Print size. A real barcode has a minimum module width, and most web-resolution photos of a bottle label are marginal - if you're building a pipeline around this, shoot at the highest resolution you can. The downsampling and scale inputs exist precisely so you can tune for the small-in-a-big-photo case.

Categoryimage/CV/codes

Inputs (4)

NameTypeDefaultDescription
imageNPARRAY,IMAGEImage to scan. An IMAGE batch uses its first frame; an NPARRAY (gray/BGR, any dtype) is treated as one 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.
gradient_thresholdFLOAT640–255Minimum gradient magnitude for a pixel to count towards a bar region. LOWER it for faint, low-contrast or out-of-focus barcodes; raise it to reject busy background texture that looks like bars.
downsampling_thresholdoptFLOAT51264–8192Images larger than this (in pixels, on the shorter side) are downsampled before the SEARCH - decoding still happens at full resolution. Raise it when a small barcode in a big photo is missed.
detector_scalesoptSTRING0.01, 0.03, 0.06, 0.08Comma-separated relative scales the detector searches at, as a fraction of the image size. Add a larger value (e.g. 0.2) when the barcode fills much of the frame, a smaller one when it is tiny. Blank = OpenCV's defaults.

Outputs (9)

NameTypeDescription
foundBOOLEANTrue when at least one barcode was located. Feed a 'Basic data handling: IfElse' to branch instead of gating a node with a boolean widget.
barcode_countINTNumber of barcodes located.
textSTRINGEvery decoded payload, one per line - wire into the core 'Preview as Text' (PreviewAny) node. Empty when nothing decoded.
bboxesBOUNDING_BOXOne {x, y, width, height, score, label} dict per barcode (axis-aligned box around its 4 corners, label = decoded digits) - feed the core 'Draw BBoxes' node.
cornersNPARRAY(N*4, 2) float32: the 4 corner points of every barcode, in order. Feed 'CV Draw Points', or 'CV Draw Connections'/'Draw Labels' with the matching outputs. Empty (0, 2) when nothing is found.
connectionsNPARRAY(N*4, 2) int32 edge index-pairs closing each barcode's quad, indexed into 'corners' - feed 'CV Draw Connections' to outline the rotated region exactly.
textsNPARRAY(N,) string array of the decoded payloads, one per barcode. An empty entry means the barcode was located but could not be read.
formatsNPARRAY(N,) string array of the symbology per barcode ('EAN_13', 'EAN_8', 'UPC_A', 'UPC_E', or 'NONE' when only located).
labelsNPARRAY(N*4,) string array aligned row-for-row with 'corners': each barcode's text on its FIRST corner, blank on the other three. Feed 'corners' + this to 'CV Draw Labels' to write the digits once per barcode.