CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-google-zxing--core

Core barcode encoding/decoding library supporting 17 formats including QR Code, Data Matrix, Aztec, PDF 417, and various 1D barcodes

Pending
Overview
Eval results
Files

multi-detection.mddocs/reference/

Multiple Barcode Detection

Support for detecting and decoding multiple barcodes from a single image. GenericMultipleBarcodeReader works with any Reader implementation by recursively scanning image regions, while QRCodeMultiReader provides QR-specific multi-detection. Additional utility ByQuadrantReader divides images into quadrants for improved detection in certain scenarios.

Capabilities

MultipleBarcodeReader

Interface for reading multiple barcodes from a single image. Returns an array of Result objects, one for each detected barcode.

/**
 * Interface for reading multiple barcodes from a single image.
 * Implementations detect and decode all barcodes present in the image.
 */
public interface MultipleBarcodeReader {
    /**
     * Decodes multiple barcodes from an image.
     *
     * @param image Binary bitmap representation of the image
     * @return Array of Result objects (one per barcode), may be empty
     * @throws NotFoundException If no barcodes are found
     */
    Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException;

    /**
     * Decodes multiple barcodes from an image with configuration hints.
     *
     * @param image Binary bitmap representation of the image
     * @param hints Map of DecodeHintType to configuration values
     * @return Array of Result objects (one per barcode), may be empty
     * @throws NotFoundException If no barcodes are found
     */
    Result[] decodeMultiple(BinaryBitmap image, Map<DecodeHintType,?> hints) throws NotFoundException;
}

GenericMultipleBarcodeReader

Generic implementation that wraps any single-barcode Reader to detect multiple barcodes. Uses recursive region scanning to find additional barcodes after each successful decode.

/**
 * Generic multiple barcode reader implementation.
 * Wraps any single-barcode Reader (1D or 2D) and recursively scans
 * image regions to find additional barcodes.
 *
 * Algorithm:
 * 1. Decode barcode using delegate reader
 * 2. "Erase" found barcode region by setting bits to white
 * 3. Recursively scan remaining image for more barcodes
 * 4. Repeat until no more barcodes found
 *
 * Works with any barcode format supported by delegate reader.
 */
public final class GenericMultipleBarcodeReader implements MultipleBarcodeReader {
    /**
     * Creates a generic multiple barcode reader.
     *
     * @param delegate Single-barcode reader to use for detection
     *                (e.g., MultiFormatReader, QRCodeReader, etc.)
     */
    public GenericMultipleBarcodeReader(Reader delegate);

    /**
     * Decodes multiple barcodes from image.
     *
     * @param image Binary bitmap to scan
     * @return Array of Result objects (may be empty if none found)
     * @throws NotFoundException If no barcodes found in image
     */
    @Override
    public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException;

    /**
     * Decodes multiple barcodes with hints.
     *
     * @param image Binary bitmap to scan
     * @param hints Decode hints for delegate reader
     * @return Array of Result objects
     * @throws NotFoundException If no barcodes found
     */
    @Override
    public Result[] decodeMultiple(BinaryBitmap image, Map<DecodeHintType,?> hints)
        throws NotFoundException;
}

Usage Example:

import com.google.zxing.*;
import com.google.zxing.common.*;
import com.google.zxing.multi.*;
import java.util.*;

// Create image with multiple barcodes
BinaryBitmap bitmap = ...;

// Create multi-barcode reader with any delegate
Reader delegate = new MultiFormatReader();
MultipleBarcodeReader multiReader = new GenericMultipleBarcodeReader(delegate);

try {
    // Decode all barcodes in image
    Result[] results = multiReader.decodeMultiple(bitmap);

    System.out.println("Found " + results.length + " barcodes:");
    for (int i = 0; i < results.length; i++) {
        Result r = results[i];
        System.out.println("  " + (i+1) + ". " + r.getBarcodeFormat() +
                         ": " + r.getText());

        // Get location of each barcode
        ResultPoint[] points = r.getResultPoints();
        System.out.println("     Location: " + Arrays.toString(points));
    }

} catch (NotFoundException e) {
    System.out.println("No barcodes found");
}

QRCodeMultiReader

QR Code-specific implementation for detecting multiple QR codes. Optimized for QR Code finder patterns and supports structured append sequences.

/**
 * QR Code-specific multiple barcode reader.
 * Extends QRCodeReader to detect multiple QR codes in a single image.
 * More efficient than GenericMultipleBarcodeReader for QR codes because
 * it uses QR-specific finder pattern detection.
 *
 * Supports:
 * - Multiple independent QR codes
 * - Structured append sequences (multiple QR codes forming one message)
 */
public final class QRCodeMultiReader extends QRCodeReader
        implements MultipleBarcodeReader {

    /**
     * Creates a QR Code multi-reader.
     */
    public QRCodeMultiReader();

    /**
     * Decodes multiple QR codes from image.
     *
     * @param image Binary bitmap to scan
     * @return Array of Result objects (one per QR code)
     * @throws NotFoundException If no QR codes found
     */
    @Override
    public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException;

    /**
     * Decodes multiple QR codes with hints.
     *
     * @param image Binary bitmap to scan
     * @param hints Decode hints (PURE_BARCODE, CHARACTER_SET, etc.)
     * @return Array of Result objects
     * @throws NotFoundException If no QR codes found
     */
    @Override
    public Result[] decodeMultiple(BinaryBitmap image, Map<DecodeHintType,?> hints)
        throws NotFoundException;

    // Also inherits single QR code methods from QRCodeReader
    @Override
    public Result decode(BinaryBitmap image) throws NotFoundException, FormatException;

    @Override
    public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
        throws NotFoundException, FormatException;
}

Usage Example:

import com.google.zxing.*;
import com.google.zxing.common.*;
import com.google.zxing.multi.qrcode.QRCodeMultiReader;
import java.util.*;

// Create image with multiple QR codes
BinaryBitmap bitmap = ...;

// Use QR-specific multi-reader for better performance
MultipleBarcodeReader qrMultiReader = new QRCodeMultiReader();

Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);

try {
    Result[] results = qrMultiReader.decodeMultiple(bitmap, hints);

    System.out.println("Found " + results.length + " QR codes:");
    for (Result r : results) {
        System.out.println("  " + r.getText());

        // Check for structured append metadata
        Map<ResultMetadataType, Object> metadata = r.getResultMetadata();
        if (metadata != null &&
            metadata.containsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {

            int sequence = (Integer) metadata.get(
                ResultMetadataType.STRUCTURED_APPEND_SEQUENCE);
            int parity = (Integer) metadata.get(
                ResultMetadataType.STRUCTURED_APPEND_PARITY);

            System.out.println("    Part " + sequence +
                             " of structured append (parity: " + parity + ")");
        }
    }

} catch (NotFoundException e) {
    System.out.println("No QR codes found");
}

ByQuadrantReader

Utility reader that divides the image into quadrants and attempts detection in each. Useful for images with barcodes in specific regions or when standard scanning struggles.

/**
 * Attempts to decode by dividing image into quadrants.
 * Useful when:
 * - Barcode may be in any corner of image
 * - Standard full-image scan is struggling
 * - Image is very large
 *
 * Not a MultipleBarcodeReader - returns first barcode found.
 * Divides image into 4 quadrants and tries each separately.
 */
public final class ByQuadrantReader implements Reader {
    /**
     * Creates a by-quadrant reader.
     *
     * @param delegate Single-barcode reader to use in each quadrant
     */
    public ByQuadrantReader(Reader delegate);

    /**
     * Attempts to decode by scanning quadrants.
     *
     * @param image Binary bitmap to scan
     * @return First barcode found
     * @throws NotFoundException If no barcode found in any quadrant
     * @throws ChecksumException If barcode found but checksum failed
     * @throws FormatException If barcode found but format invalid
     */
    @Override
    public Result decode(BinaryBitmap image)
        throws NotFoundException, ChecksumException, FormatException;

    /**
     * Attempts to decode with hints.
     *
     * @param image Binary bitmap to scan
     * @param hints Decode hints for delegate
     * @return First barcode found
     * @throws NotFoundException If no barcode found
     * @throws ChecksumException If checksum failed
     * @throws FormatException If format invalid
     */
    @Override
    public Result decode(BinaryBitmap image, Map<DecodeHintType,?> hints)
        throws NotFoundException, ChecksumException, FormatException;

    @Override
    public void reset();
}

Usage Example:

import com.google.zxing.*;
import com.google.zxing.common.*;
import com.google.zxing.multi.ByQuadrantReader;

// For difficult images where standard scanning fails
BinaryBitmap bitmap = ...;

// Try quadrant-by-quadrant
Reader quadrantReader = new ByQuadrantReader(new MultiFormatReader());

try {
    Result result = quadrantReader.decode(bitmap);
    System.out.println("Found in quadrant: " + result.getText());

} catch (NotFoundException e) {
    System.out.println("Not found in any quadrant");
}

Usage Patterns

Multiple Barcodes of Same Format

// Scanning sheet with multiple QR codes
QRCodeMultiReader qrMulti = new QRCodeMultiReader();
Result[] qrCodes = qrMulti.decodeMultiple(bitmap);

// Process all QR codes
for (Result qr : qrCodes) {
    processQRCode(qr.getText());
}

Multiple Barcodes of Different Formats

// Scanning document with mixed barcode types
MultiFormatReader singleReader = new MultiFormatReader();
GenericMultipleBarcodeReader multiReader =
    new GenericMultipleBarcodeReader(singleReader);

Result[] allBarcodes = multiReader.decodeMultiple(bitmap);

// Process by format
for (Result result : allBarcodes) {
    switch (result.getBarcodeFormat()) {
        case QR_CODE:
            processQRCode(result.getText());
            break;
        case CODE_128:
            processShippingLabel(result.getText());
            break;
        case EAN_13:
            processProduct(result.getText());
            break;
    }
}

Retry Strategy

public Result[] scanMultipleWithRetry(BinaryBitmap bitmap) {
    // Try 1: Fast scan
    try {
        MultipleBarcodeReader reader = new GenericMultipleBarcodeReader(
            new MultiFormatReader());
        return reader.decodeMultiple(bitmap);

    } catch (NotFoundException e) {
        // Try 2: With TRY_HARDER
        try {
            Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
            return reader.decodeMultiple(bitmap, hints);

        } catch (NotFoundException e2) {
            return new Result[0]; // Empty array
        }
    }
}

Structured Append Reconstruction

// Collect parts of structured append sequence
Map<Integer, Result> parts = new TreeMap<>();

for (Result result : results) {
    Map<ResultMetadataType, Object> metadata = result.getResultMetadata();

    if (metadata != null &&
        metadata.containsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {

        int sequence = (Integer) metadata.get(
            ResultMetadataType.STRUCTURED_APPEND_SEQUENCE);
        parts.put(sequence, result);
    }
}

// Reconstruct complete message
StringBuilder fullMessage = new StringBuilder();
for (Result part : parts.values()) {
    fullMessage.append(part.getText());
}

System.out.println("Complete message: " + fullMessage);

Performance Considerations

Algorithm Complexity:

  • GenericMultipleBarcodeReader: O(n) where n is number of barcodes
  • Each found barcode triggers another full scan of remaining regions
  • Large numbers of barcodes can be slow

Memory Usage:

  • Creates modified copies of BitMatrix for each iteration
  • Memory usage proportional to image size × number of barcodes

Optimization Tips:

  • Use QRCodeMultiReader instead of Generic for QR codes (faster)
  • Limit POSSIBLE_FORMATS to expected types only
  • Consider preprocessing to split image into regions if layout is known
  • For video streams, limit multi-detection to single frame (don't scan every frame)

Limitations

GenericMultipleBarcodeReader:

  • May miss barcodes that overlap visually
  • Erasing found barcodes can affect nearby barcodes
  • No guaranteed ordering of results
  • May find same barcode multiple times in edge cases

QRCodeMultiReader:

  • Only works with QR codes
  • Better at handling close proximity than Generic
  • Still struggles with severe overlap

Best Results:

  • Barcodes should be well-separated (at least 10% of barcode size apart)
  • Good contrast and lighting
  • Consistent barcode sizes (mixing tiny and huge barcodes is difficult)

Best Practices

When to Use:

  • Scanning sheets with multiple product labels
  • Reading packing slips with multiple tracking codes
  • Document processing with mixed barcode types
  • Quality control scanning multiple items

When Not to Use:

  • Real-time video scanning (too slow)
  • Single barcode expected (use regular Reader)
  • Barcodes severely overlapping (not reliable)
  • Performance-critical applications (significant overhead)

Recommendations:

  • Use QRCodeMultiReader for QR-only applications
  • Use GenericMultipleBarcodeReader for mixed formats
  • Set reasonable timeouts for multiple barcode detection
  • Handle empty result arrays gracefully
  • Log and analyze failed detections to improve image quality

See Also

  • Core Reading and Writing - Single barcode Reader interface
  • QR Code Support - QRCodeMultiReader implementation
  • PDF417 Support - PDF417Reader implements MultipleBarcodeReader
  • Result Handling - Processing Result arrays
  • Exception Handling - NotFoundException when no barcodes found

Install with Tessl CLI

npx tessl i tessl/maven-com-google-zxing--core@3.5.1

docs

index.md

tile.json