Skip to content

Barcode Reader Node ​

Overview ​

The Barcode Reader is a multi-decoder node that scans barcodes and QR codes from images using four decoders: ZBar, ZXing, Quagga2 and, since 1.4.0, the Rosepetal SDK engine. It features a flexible block-based architecture that allows you to optimize detection for either maximum accuracy or performance.

Part of the @rosepetal/node-red-contrib-barcode-reader package. The node appears under the RP Utils category in the Node-RED palette.

Architecture ​

The package is split into two components:

  • barcode-engine/ -- Native C++ addon (Node-API) that wraps ZBar, ZXing, and OpenCV. As of v1.2.1, all decoding and preprocessing operations run on async workers, so the Node-RED event loop is never blocked.
  • node-red-contrib-barcode-reader/ -- Node-RED wrapper that provides the editor UI, block configuration, execution modes, and result merging/deduplication.
  • node-red-contrib-barcode-reader/lib/rp-mapping.js + the npm dependency @rosepetal/barcode-engine-client -- the rosepetal decoder (v1.4.0+): the client runs rp-barcode serve (a Go binary from the optional @rosepetal/barcode-engine-* packages) as one child process per Node-RED runtime, shared by every barcode-reader node; rp-mapping.js translates a block's options into the engine's decode options and the engine's symbols into the raw results the node already merges.

Prebuilt binaries for barcode-engine are available for linux-x64, linux-arm64, and linuxmusl-x64. On other platforms the addon is compiled from source during npm install.

Key Features ​

  • Multi-decoder support: ZBar, ZXing, Quagga2 and the Rosepetal SDK engine
  • Fully async processing: Native addon uses C++ async workers -- the Node-RED event loop is never blocked (v1.2.1+)
  • Flexible preprocessing: Original, Histogram Equalization, Otsu Threshold, Sharpen
  • Per-block format filter: restrict each block to specific symbologies (e.g. UPC-A only) for speed and to eliminate false positives
  • Block-based configuration: Combine decoders and preprocessing methods
  • Execution modes: Parallel (maximum detection) or Sequential (optimized performance)
  • Batch processing: Process single images or arrays of images
  • Smart deduplication: Automatically merges duplicate detections
  • Performance tracking: Built-in execution time monitoring
  • Rich output format: Includes barcode corners, rotation, and detection metadata

Installation ​

bash
npm install @rosepetal/node-red-contrib-barcode-reader

npm will attempt to fetch a prebuilt native addon for your platform. Prebuilt binaries bundle OpenCV, ZBar, and ZXing; system libraries are only required for source builds or unsupported platforms.

The optional rosepetal decoder needs the Rosepetal SDK engine. The node depends on @rosepetal/barcode-engine-client (npmjs, published by the SDK), which declares the engine binary as its optional dependencies @rosepetal/barcode-engine-linux-x64 and @rosepetal/barcode-engine-linux-arm64 (0.4.x, static binary at bin/rp-barcode, the same package for glibc and musl) on Rosepetal's private Artifact Registry (europe-southwest1-npm.pkg.dev/rosepetal-artifact/private-node-packages). When the registry npm installs from does not carry them (the public npmjs does not) npm skips them with a warning, every other decoder works as before, and a rosepetal block warns Rosepetal engine not available once per node and returns [].

To add the engine to such a Node-RED, fetch the platform package outside the user dir with a token and install it as a tarball kept in the user dir (package.json records its path):

bash
REG=europe-southwest1-npm.pkg.dev/rosepetal-artifact/private-node-packages
npm pack @rosepetal/barcode-engine-linux-x64@0.4 --pack-destination ~/.node-red --registry=https://$REG/ "--//$REG/:_authToken=$(gcloud auth print-access-token)"
cd ~/.node-red && npm install --no-audit --no-fund ./rosepetal-barcode-engine-linux-x64-0.4.*.tgz

Never run npm install --registry=… inside the user dir (it reconciles the whole tree against that registry), and do not point the @rosepetal scope at the private registry (it does not proxy npmjs: the public client and this package would 404). Alternatively RP_BARCODE_ENGINE=/path/to/rp-barcode points the node at another build of the engine (SDK >= 0.4.0, which the block's defaults need: Projection fallback 0.3.0, Projection retries full 0.4.0; when set it must be executable, the package and PATH are then not searched); as a last resort rp-barcode is looked up in PATH.

Build from source (Debian/Ubuntu) ​

bash
cd node-red-contrib-barcode-reader
bash INSTALL.sh

The script will:

  1. Install ZBar, ZXing, and OpenCV libraries
  2. Build ZXing from source if the package is not available
  3. Build the C++ addon and install the Node-RED package dependencies

Since 1.4.0 the inner package (node-red-contrib-barcode-reader/package.json, what INSTALL.sh installs) depends on @rosepetal/barcode-engine-client from npmjs, so its npm install needs that package published; the engine comes from a scope registry that also proxies npmjs (the controller image's node-packages), from the engine tarball or from RP_BARCODE_ENGINE, as above.

Build dependencies (source builds only) ​

PackagePurpose
libzbar-devZBar barcode library
libzxing-devZXing barcode library (or build from source)
libopencv-devOpenCV image processing
build-essentialC++ compiler toolchain
node-gypNative addon build tool

Runtime requirements ​

  • Node.js: >= 18 (engines.node); the test suite (npm test) needs >= 22.9 because of the node-red 5.0.7 dev dependency
  • Node-RED: >= 1.0.0
  • OS: Linux (Ubuntu/Debian recommended)
  • Rosepetal SDK decoder (optional): the engine package or RP_BARCODE_ENGINE, see above

Configuration ​

Properties ​

Name ​

  • Type: String
  • Optional: Yes
  • Description: Custom name for the node instance

Input ​

  • Type: Message property path
  • Default: payload
  • Description: Message property containing the input image(s)

Output ​

  • Type: Message property path
  • Default: payload
  • Description: Message property where results will be stored

Execution Mode ​

  • Type: Select
  • Options:
    • Parallel: Runs all blocks simultaneously and merges results (maximum detection)
    • Sequential: Processes blocks in order, stops at first successful detection (optimized performance)

Decoder Blocks ​

Each block represents a detection attempt with specific configuration. Blocks can be:

  • Added with the "Add Block" button
  • Reordered by dragging (relevant for Sequential mode)
  • Removed individually
  • Configured independently

Decoder Options ​

ZBar

  • Fast and reliable
  • Good for standard barcodes and QR codes
  • Native C++ implementation via async worker
  • Best overall performance

ZXing

  • Comprehensive format support
  • Thorough scan and rotation handling always enabled (matches zxing.org defaults)
  • Good for damaged or low-quality barcodes
  • Slower but more thorough

Quagga2

  • JavaScript-based decoder
  • Good for 1D barcodes
  • Runs in Node.js without native dependencies
  • Moderate performance

Rosepetal SDK (rosepetal, v1.4.0+)

  • Rosepetal's own engine (Go, rosepetal-barcode-sdk 0.4.0 or later — the default Projection retries full needs 0.4.0, a 0.3.0 engine works with retries off —, Rosepetal's internal repository) run as one persistent rp-barcode serve child process per Node-RED runtime, shared by all barcode-reader nodes; pixels go over stdio as raw frames and the decode runs off the event loop
  • 1D only: UPC-A, UPC-E, EAN-13, EAN-8, Code 128 / GS1-128, Code 39, Code 93, Codabar, ITF. Keep a ZBar/ZXing block for 2D codes; a block whose Formats hold only 2D codes returns [] without calling the engine
  • Options: Projection (Off, Fallback, Fallback + sharpen retry, Fallback + full retries default; saved as projection + projectionRetries), Effort (normal default / robust); under Advanced: Add-ons, UPC-A as EAN-13, Code 39 full ASCII, Optional checksum, Quiet zone, Min lines, Timeout (ms, default 5000, at most 2147483647; it does not cover the engine's cold start). directions and tryInvert (flow JSON) only apply with normal (robust scans both directions and the inverted image anyway). tryHarder from older flows is ignored; a value outside the vocabulary fails that block with a warning and []. Flow JSON only (not in the editor, kept when a saved block is edited): directions, edges, tryInvert
  • Retries (edges): how a search of the whole image retries the scan rows the row threshold read nothing on. auto (default, and a flow saved without it) = the engine's recommended retries, today with Effort robust the histogram threshold and then, on the rows it leaves unread, gradient edges refit to the blurred profile of the row (guarded), with normal the histogram threshold alone; histogram = the histogram threshold alone in both efforts, the previous auto (robust auto costs about +44 % on the p95 per crop over it, +134 % over threshold, SDK on BarBeR dev); gradient = the refit gradient alone, without the histogram first, measured without EAN/UPC 1↔7 / 2↔8 flips (a measurement, not a guarantee; with robust it reads less than auto, so use auto unless you need it alone); threshold = no retries, the reads and latency of the 0.2.0 engine. auto sends no key and runs on any engine (nothing changes on 0.2.0); any other value needs rosepetal-barcode-sdk 0.3.0 or later (a dev build from cd3c347 on; the refit gradient from 973d10a on): the 0.2.0 engine rejects the key and the block warns rosepetal option edges: the installed engine … does not support it … and returns [], the other blocks run. Details and costs: readme Rosepetal SDK, "Retries and the engine"
  • Projection (projection): fallback (default, and a flow saved without it) = when the search of the rows and columns reads nothing, the projection search of the engine (the method of the rp-projection decoder ported into it: reading direction at any angle, bands along the bars averaged into profiles, a vote), for noisy, blurred or low-resolution crops of one code (not full frames: it looks for one dominant code); off = no projection search, no key sent, any engine. Free on the images the rows read; on those they do not read it is the slowest search of the engine on large images (bounds and costs: the SDK docs of the engine installed), so check the budget with images of the line that do not read. With fallback the block sends a colour input of at most 1,500,000 pixels (width × height) in colour (3 channels: RGB/BGR as they are, RGBA/BGRA without alpha, an encoded file decoded to BGR) so the projection can also read the R, G and B planes, in either effort. 1,500,000 pixels is the largest image the engine's projection runs on (SDK projMaxPixels, rosepetal-barcode-sdk 0.4.0): a larger frame (a full camera frame such as 2448×2048) goes as the preprocessed gray, with its Preprocessing, since colour there would only triple the bytes. The Preprocessing (Sharpen included) acts on the gray, so it does not apply to a colour frame and detectedBy reads rosepetal without a suffix (rosepetal_original with Original); a gray input, a frame above 1,500,000 pixels and a block at off go preprocessed as before. A colour frame is three times the bytes; through the node on the development host, on frames the rows read, a raw RGB frame costs about what the gray costs with an engine that makes the colour planes only when the projection reads them (SDK plan 03), RGBA adds the alpha drop (+4-9 ms at 1280×1024), and an engine that makes the planes on every request (SDK branch at ff78cd7) cost about 17 ms more at 2640×2400, a size that now goes gray. Needs rosepetal-barcode-sdk 0.3.0 or later, like Retries: on an older engine the block warns rosepetal option projection: the installed engine … does not support it … and returns [], the other blocks run. Details: readme Rosepetal SDK, "Projection and colour"
  • Projection retries (projectionRetries): extra passes of the projection search, one cumulative level. off = none, no key sent, any engine; sharpen = projectionSharpen: "retry", the band profiles decoded once more unsharp-masked in cascade, for blurred crops (the Sharpen preprocessing done per band inside the engine, so it also works on the colour frame); full (default since 2026-10-03, also for a flow saved without it, such as a block of 1.4.0; an explicit off or sharpen stays as saved) = sharpen, then projectionMarginVote (EAN/UPC reads of low decodability voted without the projection's floor), projectionEdges (the profiles split at the threshold of their histogram: faded narrow bars) and projectionGuardZone (EAN-13 / UPC-A from any start guard, the quiet zone in the guard's own module: a label about 4.5 modules from something dark; nothing under Quiet zone spec or Add-ons require). They run only with Projection fallback and only when everything before them read nothing, on the bands the projection already sampled, every value flip-guarded (EAN/UPC 1↔7 / 2↔8); with Projection off no key travels (the editor disables the select, keeping its level), the frame sent follows Projection as before, the Sharpen retry's sigma is not exposed, and the SDK's experimental halves and angle retries are in no level: the block never sends them (lib/rp-mapping.js PROJECTION_RETRIES, one table). Free on the images something reads: they run only when the rows, the columns and the projection read nothing, so a simple read costs nothing extra; on the images nothing reads each level adds time, full the most (reads and cost per level: readme Rosepetal SDK, "Projection retries"; p95 per crop nothing reads 168 ms at full against 92 ms at off). sharpen and full (the default) need rosepetal-barcode-sdk 0.4.0 or later: on an older engine the block warns rosepetal option projectionRetries: the installed engine … does not support it … and returns [], the other blocks run
  • Output adds symbology, identifier (ISO/IEC 15424), orientation (0/90/180/270), lines, confidence, checksum when the base result came from this block; corners are the bar extent (TL, TR, BR, BL) and box.angle is 0 for a horizontal code (ZXing gives ±90 there). format uses ZXing's spelling plus EAN-2/EAN-5 for add-ons read as separate symbols
  • Projection replaces rp-projection (1.5.1+, engine >= 0.4.0): the projection search finds the bar angle (any angle), averages bands of rows along the bars into clean profiles, reads each with the engine's decoders and accepts a value read by at least 3 of them; a few ms on a crop. It replaces the C++ rp-projection decoder of 1.3.0-1.5.0, removed from the addon: an rp-projection block in a flow runs as a Rosepetal SDK block with effort: normal, projection: fallback and the default retries (normal so that a miss of the rows costs ~2 ms before the projection instead of ~80 ms; it sends colour up to 1,500,000 pixels, as the C++ decoder tried each colour channel; its Formats and its detectedBy name rp-projection kept, minVotes / stripWidth ignored), and the editor opens and saves it as that block. Such a block needs rosepetal-barcode-sdk 0.4.0 or later (the retries): with the 0.2.0 engine it warns Block i (rp-projection) failed: rosepetal option projection: the installed engine … does not support it … and returns [], the other blocks run
  • Needs the engine package (@rosepetal/barcode-engine-linux-x64 / -linux-arm64, private registry, optional dependencies of the client) or RP_BARCODE_ENGINE; without it the block warns once per node and outage and returns []. A running engine that dies is restarted 1 s later by the next request (a binary that fails to start is retried with a growing wait, up to 30 s); the crops of the same message inside that second get []
  • Differences against ZBar/ZXing (format spelling, UPC-A digits, UPC-E 8 vs 12 digits, Code 39 pairs, GS1-128 separators, add-ons, geometry) and the two values that change in mixed flows: readme Rosepetal SDK and docs/compat/barcode-reader.md of the SDK repository
  • Timing on the development host: about 11-13 ms per 600×300 crop with robust (20 crops in one array: about 220-255 ms); robust on a large full image with no code costs 0.2-0.8 s (the whole scan runs); the first decode after a (re)deploy also pays the engine start

Preprocessing Options ​

Original

  • Simple grayscale conversion
  • Fastest option
  • Use for high-quality images with good contrast

Histogram Equalization

  • Enhances contrast across the image
  • Good for poor lighting conditions
  • Helps with uneven illumination

Otsu Threshold

  • Converts to binary (black/white) image
  • Best for low-contrast barcodes
  • Effective for faded or worn codes

Sharpen (1.5.0)

  • Unsharp mask: gray + amount × (gray − GaussianBlur(gray, sigma)), computed in float and saturated to 8 bits
  • For blurred or out-of-focus codes (the bottle turning under a line-scan camera, curved labels): it restores the contrast of the narrow bars and spaces that blur flattens below a global threshold
  • Sharpen sigma (default 2 px, (0, 20]): about the width of one narrow bar in pixels; Sharpen amount (default 1.5, (0, 10]): the strength. Saved in the block as preprocessingOptions: { sigma, amount }
  • Applies to ZBar, ZXing, Quagga2 and Rosepetal SDK blocks; not to the colour frame a Rosepetal SDK block sends with Projection fallback for a colour input of at most 1,500,000 pixels (a gray input of that block, or a larger frame, is sharpened)
  • Caution: with ZBar use it only for EAN/UPC (ZBar stops reading clean Code 128, Code 39, Code 93, Codabar and ITF symbols once they are sharpened; ZXing and the Rosepetal SDK keep reading them), and check the wrong reads on your own images before using a stronger mask in production
  • Needs the 1.5.0 addon: with an older addon a Sharpen block fails with a warning that says so

Format Filter (per block) ​

Each block has a Formats allowlist. The list shown in the editor is filtered to formats the selected decoder actually supports — so you'll see different options when the block uses ZBar vs. ZXing vs. Quagga2 (Quagga2 and the Rosepetal SDK are 1D-only).

  • All formats (default): the decoder is unrestricted; current behavior is preserved.
  • Specific formats: only the checked symbologies are decoded. Useful for reducing false positives in production lines that scan a single known format, and to avoid ZBar's UPC-A → EAN-13 leading-zero ambiguity (force UPCA to get 12-digit output).

Selections persist across decoder changes when the new decoder still supports them — for example switching a block from ZBar to ZXing with UPCA checked keeps UPCA checked; switching to Quagga2 from ZXing with Aztec checked drops Aztec (Quagga2 is 1D-only).

Input Format ​

The node accepts images in multiple formats:

Rosepetal Bitmap Format ​

javascript
{
  data: Buffer,            // Raw pixel data
  width: 1920,
  height: 1080,
  colorSpace: "RGB",       // "GRAY", "RGB", "BGR", "RGBA", "BGRA"
  dtype: "uint8"
}

Raw Bitmap ​

javascript
{
  width: 1920,
  height: 1080,
  data: Buffer,
  channels: 3              // 1 (grayscale), 3 (RGB/BGR), 4 (RGBA/BGRA)
}

Image Buffers ​

  • JPEG buffer
  • PNG buffer

Array Input ​

Process multiple images at once:

javascript
msg.payload = [image1, image2, image3];

Output Format ​

format has one spelling whatever block read the code, ZXing's (the Rosepetal SDK block prints it too): EAN-13, EAN-8, UPC-A, UPC-E, Code128, Code39, Code93, Codabar, ITF, QRCode, DataMatrix, PDF417, Aztec, DataBar, DataBarExpanded, and EAN-2 / EAN-5 for add-ons. ZBar (CODE-128, I2/5, QR-Code…) and Quagga2 (code_128, ean_13…) names are mapped onto it.

Single Image Input ​

Returns an array of detected barcodes:

javascript
[
  {
    format: "QRCode",
    value: "decoded_content_here",
    box: {
      center: { x: 0.5, y: 0.5 },        // Relative coordinates (0-1)
      size: { width: 0.2, height: 0.2 },  // Relative to image size
      angle: 15                            // Rotation in degrees
    },
    corners: [                             // Four corners in relative coordinates
      { x: 0.4, y: 0.4 },
      { x: 0.6, y: 0.4 },
      { x: 0.6, y: 0.6 },
      { x: 0.4, y: 0.6 }
    ],
    detectedBy: ["zbar_original", "zxing_original"] // Which decoder/preprocessing found it: one string per block
  }
]

detectedBy is an array of "<decoder>_<preprocessing>" strings, one per block that read the value (rp-projection alone for a legacy projection block, rosepetal alone for a Rosepetal SDK block that sent colour). When the base result came from a rosepetal block (v1.4.0+) the object also carries symbology, identifier, orientation, lines, confidence and checksum; results of the other decoders never have them.

Array Input ​

Returns nested array structure:

javascript
[
  [image1_result1, image1_result2], // Results from first image
  [image2_result1],                  // Results from second image
  []                                 // No results from third image
]

Coordinate System ​

All coordinates are normalized to 0-1 range:

  • x: 0 = left edge, x: 1 = right edge
  • y: 0 = top edge, y: 1 = bottom edge

To convert to pixels: pixelX = x * imageWidth

Performance Tracking ​

The node automatically adds execution time to msg.performance:

javascript
msg.performance = {
  "barcode-reader": {
    startTime: Date,
    milliseconds: 245
  }
}

The execution time is also displayed under the node in the editor.

Usage Examples ​

Example 1: Maximum Detection Rate (Parallel Mode) ​

Use case: Scanning worn, damaged, or low-quality barcodes

Configuration:

  • Mode: Parallel
  • Block 1: ZBar + Original
  • Block 2: ZBar + Histogram Equalization
  • Block 3: ZBar + Otsu Threshold
  • Block 4: ZXing + Original
  • Block 5: ZXing + Histogram

Behavior: All blocks run simultaneously via async workers, results are merged and deduplicated. The Node-RED event loop remains free to handle other messages.

Trade-off: Higher total CPU time, maximum detection rate

Example 2: Fast with Fallback (Sequential Mode) ​

Use case: High-throughput production with mostly good quality codes

Configuration:

  • Mode: Sequential
  • Block 1: ZBar + Original (fastest)
  • Block 2: ZXing + Original (if Block 1 fails)
  • Block 3: ZXing + Histogram (last resort)

Behavior: Stops at first successful detection

Trade-off: Fast for most images, reliable fallback for difficult ones

Example 3: Balanced Approach ​

Use case: Mixed quality images, moderate performance requirements

Configuration:

  • Mode: Parallel
  • Block 1: ZBar + Original
  • Block 2: ZXing + Histogram
  • Block 3: Quagga2 + Otsu

Behavior: Three different approaches run in parallel

Trade-off: Good balance of speed and detection rate

Example 4: Processing Multiple Images ​

javascript
// Input node
msg.payload = [
  imageBuffer1,
  imageBuffer2,
  imageBuffer3
];

// After barcode reader
msg.payload = [
  [{ format: "QRCode", value: "ABC123", ... }],   // Results from image 1
  [{ format: "Code128", value: "XYZ789", ... }],  // Results from image 2
  []                                               // No barcodes in image 3
];

Example 5: Custom Input/Output Paths ​

javascript
// Configuration:
// - Input: "image.data"
// - Output: "barcodes"

msg.image = {
  data: imageBuffer,
  width: 1920,
  height: 1080
};

// After processing:
msg.barcodes = [
  { format: "QRCode", value: "scanned_data", ... }
];

Supported Barcode Formats ​

1D Barcodes ​

  • CODE-128 (logistics, shipping)
  • CODE-39 (automotive, DoD)
  • CODE-93 (Canada Post)
  • EAN-13 (retail products worldwide)
  • EAN-8 (small retail items)
  • UPC-A (North America retail)
  • UPC-E (small packages)
  • CODABAR (libraries, blood banks, FedEx)
  • ITF (Interleaved 2 of 5)

2D Barcodes ​

  • QR Code
  • Data Matrix
  • PDF417
  • Aztec Code

Decoder Format Support ​

The canonical names match the values used in each block's Formats filter.

Canonical nameZBarZXingQuagga2Rosepetal SDK
UPCAYYYY
UPCEYYYY
EAN13YYYY
EAN8YYYY
Code128YYYY
Code39YYYY
Code93YYYY
CodabarYYYY
ITFYYYY
QRCodeYY--
PDF417YY--
DataMatrix-Y--
Aztec-Y--
DataBarYY--

Formats not supported by a decoder are omitted from the editor's checklist when that decoder is selected; if a Format is set programmatically that the decoder doesn't support, it is silently ignored.

Performance Considerations ​

Async Processing (v1.2.1+) ​

All native decoding and preprocessing runs on C++ async workers. This means:

  • The Node-RED event loop is never blocked during barcode processing
  • Multiple barcode operations can be in-flight concurrently
  • Other nodes in the flow continue to process messages while decoding runs

Execution Time Factors ​

  1. Number of blocks: More blocks = longer execution (in Parallel mode)
  2. Preprocessing method: Original < Histogram < Otsu < Sharpen (a float Gaussian: its cost grows with sigma; about 2 ms on a 600×300 crop and 27 ms on a 2448×2048 image at sigma 2)
  3. Decoder choice: ZBar < ZXing < Quagga2 (approximate; Quagga2 is the slow JavaScript path). The Rosepetal SDK is measured per crop in Decoder Options above (about 11-13 ms per 600×300 crop with robust, less with normal): with robust it is slower than ZBar and ZXing (20-25× zxing-cpp per crop in the SDK benchmarks), more so on a large image with no code
  4. Image size: Larger images take longer (the Rosepetal SDK projection, when it runs, scales with crop height × width)

Optimization Strategies ​

For Speed ​

  • Use Sequential mode
  • Start with Original preprocessing
  • Place fastest blocks first (ZBar + Original)
  • Reduce image resolution before processing
  • Use single decoder only

For Accuracy ​

  • Use Parallel mode
  • Include multiple preprocessing options
  • Use multiple decoders
  • Ensure good image quality (1500px max on long side recommended)

Typical Performance ​

Sequential mode (fast path):

  • High-quality barcode: 20-50ms (Block 1 success)
  • Medium quality: 100-200ms (falls to Block 2-3)
  • Low quality: 300-500ms (exhausts all blocks)

Parallel mode (maximum detection):

  • 3 blocks: 150-300ms
  • 5 blocks: 300-600ms
  • 8 blocks: 500-1000ms

Times vary based on image size, hardware, and complexity

Deduplication Logic ​

When multiple blocks detect the same barcode:

  1. Barcodes are considered duplicates if they have the same value (data) at the same place: the boxes overlap along the code and lie within half its length across it. The same value printed twice (two labels side by side) gives two results; a detection without a box (Quagga2 without one) joins the first detection of its value
  2. The detection from the lowest block index is kept as the primary result
  3. All detection methods are tracked in the detectedBy array
  4. Position data (corners, box) comes from the primary detection, and so do the Rosepetal SDK fields (symbology, identifier, orientation, lines, confidence, checksum) when the primary is a rosepetal block
  5. Two different values whose boxes lie on top of each other (spatial deduplication, typically a ghost read on a blurry scan line) are one symbol: the value more blocks agree on wins, else the lowest block's. Codes stacked on a label do not overlap and all come out
  6. The spatial rule also decides between two spellings of one symbol. UPC-E: ZXing and the SDK print the 8 digits, ZBar the 12-digit UPC-A expansion, so in a flow mixing them the block order and count decide which form is printed, and a template saved with the other form stops matching. A GS1-128 keeps only one form (ZXing's HRI or the SDK's \u001d separators, whichever block is lower). A flow without a rosepetal block is unchanged

Example:

javascript
// Block 0: ZBar + Original detects "ABC123"
// Block 2: ZXing + Histogram also detects "ABC123"

// Result:
{
  format: "Code128",
  value: "ABC123",
  box: { /* from Block 0 */ },
  corners: [ /* from Block 0 */ ],
  detectedBy: ["zbar_original", "zxing_histogram"]
}

Programmatic API ​

The barcode-engine addon can be used outside Node-RED. All native functions are async (callback-based) and are promisified by the package:

javascript
const barcode = require('@rosepetal/node-red-contrib-barcode-reader');

// Preprocessing (returns grayscale image as Rosepetal bitmap)
const gray     = await barcode.preprocess_original(inputMat);
const enhanced = await barcode.preprocess_histogram(inputMat);
const binary   = await barcode.preprocess_otsu(inputMat);
const sharp    = await barcode.preprocess_sharpen(inputMat, { sigma: 2, amount: 1.5 });  // options optional; sigma (0, 20] px, amount (0, 10]

// Decoders (require grayscale input, return JSON string)
// Signature: decode_zbar(image, formats)
const zbarAll  = await barcode.decode_zbar(gray, []);                 // all symbologies
const zbarUpc  = await barcode.decode_zbar(gray, ['UPCA']);           // UPC-A only

// Signature: decode_zxing(image, formats)
const zxAll    = await barcode.decode_zxing(gray, []);          // all formats
const zxQrOnly = await barcode.decode_zxing(gray, ['QRCode']);  // QR only

// Parse results
const { results } = JSON.parse(zbarUpc);

// Utilities
const resized   = await barcode.resizeImage(inputMat, 50);   // 50% size
const converted = await barcode.convertToMat(anyInput);      // normalize input

formats argument: an array of canonical format names (see the Decoder Format Support table). Pass [] to leave the decoder unrestricted. Unsupported names for a given decoder are silently ignored.

Decoder Result Format ​

javascript
{
  "results": [
    {
      "type": "QR-Code",
      "data": "decoded content",
      "points": {
        "x1": 100, "y1": 100,
        "x2": 200, "y2": 100,
        "x3": 200, "y3": 200,
        "x4": 100, "y4": 200
      }
    }
  ]
}

Troubleshooting ​

No Barcodes Detected ​

Possible causes:

  • Barcode too small or large in image
  • Poor image quality or low contrast
  • Barcode damaged or partially obscured
  • Unsupported barcode format

Solutions:

  1. Add blocks with stronger preprocessing (Histogram, Otsu)
  2. Add a ZXing block alongside ZBar (different decoders catch different cases)
  3. Switch to Parallel mode for comprehensive scanning
  4. Verify barcode format is supported
  5. Check image quality and resolution
  6. Ensure proper lighting in source images

Build Errors ​

These errors only apply to source builds (when no prebuilt binary is available for your platform).

"Could not load the barcode native addon"

  • If no prebuilt binary is available, install system deps and run cd barcode-engine && npm run rebuild

"Cannot find -lzbar"

bash
sudo apt-get install libzbar-dev

"Cannot find -lZXing"

bash
# Build ZXing from source
git clone https://github.com/zxing-cpp/zxing-cpp.git
cd zxing-cpp && git checkout v2.3.0
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_SHARED_LIBS=ON ..
make -j$(nproc) && sudo make install
sudo ldconfig

"Cannot find opencv2/opencv.hpp"

bash
sudo apt-get install libopencv-dev

node-gyp errors

bash
sudo apt-get install build-essential python3
npm install -g node-gyp

Slow Performance ​

Possible causes:

  • Too many blocks in Parallel mode
  • Large image resolution
  • Many ZXing blocks (slower than ZBar)
  • Processing arrays of many images

Solutions:

  1. Switch to Sequential mode
  2. Reduce number of blocks
  3. Prefer ZBar blocks for cases ZBar can handle on its own
  4. Resize images before processing
  5. Remove blocks with slow preprocessing (Otsu)

Wrong Barcode Values ​

Possible causes:

  • Multiple barcodes in image
  • Barcode partially damaged
  • Similar patterns confusing decoder

Solutions:

  1. Crop image to single barcode region
  2. Use stronger preprocessing
  3. Verify expected barcode format

UPC-A read as EAN-13 with leading 0 ​

ZBar treats UPC-A as a subset of EAN-13. When unrestricted, it may emit a 12-digit UPC-A code as a 13-digit EAN-13 with a leading 0.

Solution: add UPCA to the block's Formats list. ZBar will then label and output the code as UPC-A (12 digits). If you need both, enable both UPCA and EAN13.

Missing Quagga2 Warning ​

Message: Quagga2 not available. Install @ericblade/quagga2 to use Quagga decoder.

Solution:

bash
npm install @ericblade/quagga2

Or remove Quagga2 blocks from configuration.

rosepetal block returns [] with "Rosepetal engine not available" ​

The engine package is missing (no access to the private registry at install time), RP_BARCODE_ENGINE is set to something that is not executable, or the binary speaks another protocol (too old or too new for this node version). Check ~/.node-red/node_modules/@rosepetal/barcode-engine-linux-x64/bin/rp-barcode version (prints rp-barcode 0.4.0 (schema 1.0, GS1 Syntax Dictionary …, build <rev>)), reinstall as described under Installation, and redeploy. The warning is printed once per node and outage; the other blocks of the node keep working.

rosepetal block times out or reports "engine exited" ​

Raise Timeout for very large images or switch Effort to normal. After a crash the block returns [] for 1 s (every crop of the same message inside that wait included) and the first message after the wait is read by the new process. The engine's own messages appear as [rp-barcode] … warnings in the Node-RED log.

Values changed after adding a rosepetal block ​

UPC-E (8 digits instead of the 12-digit UPC-A expansion, depending on which block comes first) and GS1-128 (\u001d separators or ZXing's HRI, depending on block order) are expected: see the deduplication logic above and the readme's Rosepetal SDK section. A downstream filter on detectedBy.length >= 2 drops every read of a Rosepetal-only flow: use lines / confidence instead.

Best Practices ​

Image Preparation ​

  1. Resolution: Keep images around 1500px on the long side for optimal balance
  2. Format: Use JPEG for photos, PNG for generated codes
  3. Lighting: Ensure even lighting across barcode
  4. Focus: Barcodes must be sharp and in focus
  5. Orientation: Any orientation works, rotation is detected

Block Configuration ​

  1. Start simple: Begin with 1-2 blocks and add more if needed
  2. Order matters: In Sequential mode, put fastest/most likely blocks first
  3. Test thoroughly: Verify with representative sample images
  4. Monitor performance: Watch execution times in production
  5. Document choices: Note why specific blocks were chosen

Production Deployment ​

  1. Test all formats: Verify all expected barcode types are detected
  2. Benchmark performance: Measure actual execution times
  3. Handle failures: Implement proper error handling for no-detection cases
  4. Log statistics: Track detection rates and performance
  5. Version control: Document block configuration changes

Advanced Usage ​

Dynamic Block Configuration ​

You can modify block configuration programmatically before the node:

javascript
// In a function node before the barcode reader
msg.barcodeConfig = {
  blocks: [
    { decoder: "zbar",  preprocessing: "original",  options: { formats: ["UPCA"] } },
    { decoder: "zxing", preprocessing: "histogram", options: { formats: ["QRCode"] } }
  ],
  executionMode: "sequential"
};

options.formats is an array of canonical format names (see Decoder Format Support). Use [] or omit the key for "all formats".

Region of Interest Processing ​

Crop image to barcode region before processing:

javascript
// In a function node before the barcode reader
const sharp = require('sharp');

msg.payload = await sharp(msg.payload)
  .extract({ left: 100, top: 100, width: 500, height: 200 })
  .toBuffer();

Quality Validation ​

Filter results by confidence or validation:

javascript
// In a function node after the barcode reader
msg.payload = msg.payload.filter(result => {
  // Only keep results detected by multiple methods
  return result.detectedBy.length >= 2;
});

Performance Monitoring ​

Track detailed performance metrics:

javascript
// In a function node after the barcode reader
const perf = msg.performance["barcode-reader"];
const detectionCount = Array.isArray(msg.payload) ? msg.payload.length : 0;

msg.metrics = {
  executionTime: perf.milliseconds,
  detectionsFound: detectionCount,
  detectionsPerSecond: (detectionCount / perf.milliseconds * 1000).toFixed(2)
};

Migration Guide ​

From node-zbardecoder ​

If migrating from the basic node-zbardecoder package:

Old approach:

javascript
const bardecoder = require('node-zbardecoder');
const result = JSON.parse(bardecoder.decode('image.jpg'));

New approach:

  • Use the Barcode Reader node in your flow
  • Configure with ZBar decoder + Original preprocessing
  • Results are automatically parsed and formatted
  • Additional decoders and preprocessing available

Key differences:

  • Block-based configuration vs. single decoder
  • Relative coordinates instead of absolute pixels
  • Rich metadata (corners, rotation, detection tracking)
  • Built-in performance monitoring
  • Fully async processing -- no event loop blocking

See Also ​

  • ZBar Library - Open source barcode scanner
  • ZXing Library - Multi-format 1D/2D barcode library
  • Quagga2 - JavaScript barcode decoder
  • OpenCV - Computer vision library used for preprocessing
  • @rosepetal/barcode-engine-client - The engine client of the rosepetal decoder (its README describes protocol 1; docs/compat/barcode-reader.md of the SDK repository, internal, describes what changes against ZBar/ZXing)
  • validation.md - How to validate the rosepetal decoder on a Node-RED against the other blocks