Barcode Reader Node
Overview
The Barcode Reader is a multi-decoder node that scans barcodes and QR codes from images using three powerful libraries: ZBar, ZXing, and Quagga2. 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.
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, and Quagga2
- 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
- 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
npm (recommended)
npm install @rosepetal/node-red-contrib-barcode-readernpm 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.
Build from source (Debian/Ubuntu)
cd node-red-contrib-barcode-reader
bash INSTALL.sh2
The script will:
- Install ZBar, ZXing, and OpenCV libraries
- Build ZXing from source if the package is not available
- Build the C++ addon and install the Node-RED package dependencies
Build dependencies (source builds only)
| Package | Purpose |
|---|---|
libzbar-dev | ZBar barcode library |
libzxing-dev | ZXing barcode library (or build from source) |
libopencv-dev | OpenCV image processing |
build-essential | C++ compiler toolchain |
node-gyp | Native addon build tool |
Runtime requirements
- Node.js: >= 14.x
- Node-RED: >= 1.0.0
- OS: Linux (Ubuntu/Debian recommended)
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
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
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.
- 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
UPCAto 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
{
data: Buffer, // Raw pixel data
width: 1920,
height: 1080,
colorSpace: "RGB", // "GRAY", "RGB", "BGR", "RGBA", "BGRA"
dtype: "uint8"
}2
3
4
5
6
7
Raw Bitmap
{
width: 1920,
height: 1080,
data: Buffer,
channels: 3 // 1 (grayscale), 3 (RGB/BGR), 4 (RGBA/BGRA)
}2
3
4
5
6
Image Buffers
- JPEG buffer
- PNG buffer
Array Input
Process multiple images at once:
msg.payload = [image1, image2, image3];Output Format
Single Image Input
Returns an array of detected barcodes:
[
{
format: "QR-Code",
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
}
]2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Array Input
Returns nested array structure:
[
[image1_result1, image1_result2], // Results from first image
[image2_result1], // Results from second image
[] // No results from third image
]2
3
4
5
Coordinate System
All coordinates are normalized to 0-1 range:
x: 0= left edge,x: 1= right edgey: 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:
msg.performance = {
"barcode-reader": {
startTime: Date,
milliseconds: 245
}
}2
3
4
5
6
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
// Input node
msg.payload = [
imageBuffer1,
imageBuffer2,
imageBuffer3
];
// After barcode reader
msg.payload = [
[{ format: "QR-Code", value: "ABC123", ... }], // Results from image 1
[{ format: "CODE-128", value: "XYZ789", ... }], // Results from image 2
[] // No barcodes in image 3
];2
3
4
5
6
7
8
9
10
11
12
13
Example 5: Custom Input/Output Paths
// Configuration:
// - Input: "image.data"
// - Output: "barcodes"
msg.image = {
data: imageBuffer,
width: 1920,
height: 1080
};
// After processing:
msg.barcodes = [
{ format: "QR-Code", value: "scanned_data", ... }
];2
3
4
5
6
7
8
9
10
11
12
13
14
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 name | ZBar | ZXing | Quagga2 |
|---|---|---|---|
UPCA | Y | Y | Y |
UPCE | Y | Y | Y |
EAN13 | Y | Y | Y |
EAN8 | Y | Y | Y |
Code128 | Y | Y | Y |
Code39 | Y | Y | Y |
Code93 | Y | Y | Y |
Codabar | Y | Y | Y |
ITF | Y | Y | Y |
QRCode | Y | Y | - |
PDF417 | Y | Y | - |
DataMatrix | - | Y | - |
Aztec | - | Y | - |
DataBar | Y | Y | - |
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
- Number of blocks: More blocks = longer execution (in Parallel mode)
- Preprocessing method: Original < Histogram < Otsu
- Decoder choice: Quagga2 < ZBar < ZXing (approximate)
- Image size: Larger images take longer
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:
- Barcodes are considered duplicates if they have the same value (data)
- The detection from the lowest block index is kept as the primary result
- All detection methods are tracked in the
detectedByarray - Position data (corners, box) comes from the primary detection
Example:
// Block 0: ZBar + Original detects "ABC123"
// Block 2: ZXing + Histogram also detects "ABC123"
// Result:
{
format: "CODE-128",
value: "ABC123",
box: { /* from Block 0 */ },
corners: [ /* from Block 0 */ ],
detectedBy: ["zbar_original", "zxing_histogram"]
}2
3
4
5
6
7
8
9
10
11
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:
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);
// 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 input2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
{
"results": [
{
"type": "QR-Code",
"data": "decoded content",
"points": {
"x1": 100, "y1": 100,
"x2": 200, "y2": 100,
"x3": 200, "y3": 200,
"x4": 100, "y4": 200
}
}
]
}2
3
4
5
6
7
8
9
10
11
12
13
14
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:
- Add blocks with stronger preprocessing (Histogram, Otsu)
- Add a ZXing block alongside ZBar (different decoders catch different cases)
- Switch to Parallel mode for comprehensive scanning
- Verify barcode format is supported
- Check image quality and resolution
- 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"
sudo apt-get install libzbar-dev"Cannot find -lZXing"
# 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 ldconfig2
3
4
5
6
7
"Cannot find opencv2/opencv.hpp"
sudo apt-get install libopencv-devnode-gyp errors
sudo apt-get install build-essential python3
npm install -g node-gyp2
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:
- Switch to Sequential mode
- Reduce number of blocks
- Prefer ZBar blocks for cases ZBar can handle on its own
- Resize images before processing
- Remove blocks with slow preprocessing (Otsu)
Wrong Barcode Values
Possible causes:
- Multiple barcodes in image
- Barcode partially damaged
- Similar patterns confusing decoder
Solutions:
- Crop image to single barcode region
- Use stronger preprocessing
- 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:
npm install @ericblade/quagga2Or remove Quagga2 blocks from configuration.
Best Practices
Image Preparation
- Resolution: Keep images around 1500px on the long side for optimal balance
- Format: Use JPEG for photos, PNG for generated codes
- Lighting: Ensure even lighting across barcode
- Focus: Barcodes must be sharp and in focus
- Orientation: Any orientation works, rotation is detected
Block Configuration
- Start simple: Begin with 1-2 blocks and add more if needed
- Order matters: In Sequential mode, put fastest/most likely blocks first
- Test thoroughly: Verify with representative sample images
- Monitor performance: Watch execution times in production
- Document choices: Note why specific blocks were chosen
Production Deployment
- Test all formats: Verify all expected barcode types are detected
- Benchmark performance: Measure actual execution times
- Handle failures: Implement proper error handling for no-detection cases
- Log statistics: Track detection rates and performance
- Version control: Document block configuration changes
Advanced Usage
Dynamic Block Configuration
You can modify block configuration programmatically before the node:
// 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"
};2
3
4
5
6
7
8
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:
// 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();2
3
4
5
6
Quality Validation
Filter results by confidence or validation:
// 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;
});2
3
4
5
Performance Monitoring
Track detailed performance metrics:
// 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)
};2
3
4
5
6
7
8
9
Migration Guide
From node-zbardecoder
If migrating from the basic node-zbardecoder package:
Old approach:
const bardecoder = require('node-zbardecoder');
const result = JSON.parse(bardecoder.decode('image.jpg'));2
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