Barcode Detection

Note

This API requires a valid software license.

The barcode detection API allows you to detect and decode barcodes in a 2D image.

Barcodes are a visual, machine-readable representation of data, typically in a pattern of parallel lines, spaces or shapes that encode information such as product numbers. Zivid SDK supports the following linear (1D) and matrix (2D) barcode formats:

  • Linear: EAN-13, EAN-8, Code 128, Code 93, Code 39, UPC-A [1], UPC-E, ITF

  • Matrix: QR Code, Data Matrix

EAN-13 EAN-13
EAN-8 EAN-8
Code 128 Code 128
Code 93 Code 93
Code 39 Code 39
UPC-A UPC-A
UPC-E UPC-E
QR Code QR Code
Data Matrix Data Matrix

Examples of supported barcode formats.

Barcodes may come in various sizes, where the width of the smallest bar or space is the main sizing parameter. This smallest feature is called the module size (or X-dimension), and it is commonly expressed in mils, where one mil is 1/1000 inch (0.0254 mm). For example, standard retail linear barcodes typically vary between 7 mil (0.178 mm) and 13 mil (0.330 mm). Smaller barcodes require higher resolution to be detected and decoded reliably.

8 mil 8 mil
10 mil 10 mil
12 mil 12 mil

Examples of different Code 128 barcode sizes captured from the same distance.

Barcode Detection API

Start by initializing the barcode detector. Note that this should only be done once and not for every capture.

Go to source

source

const auto barcodeDetector = Zivid::Experimental::Toolbox::BarcodeDetector();
Go to source

source

barcode_detector = BarcodeDetector()

Then specify which of the supported barcode formats to detect, rather than looking for all formats. This will speed up detection and make decoding more robust. In this example we generalize and select all supported formats for simplicity.

Go to source

source

// Select your specific barcode formats for optimal performance
const auto linearFormatFilter = LinearBarcodeFormat::code128 | LinearBarcodeFormat::code93
                                | LinearBarcodeFormat::code39 | LinearBarcodeFormat::ean13
                                | LinearBarcodeFormat::ean8 | LinearBarcodeFormat::upcA
                                | LinearBarcodeFormat::upcE | LinearBarcodeFormat::itf;
const auto matrixFormatFilter = MatrixBarcodeFormat::qrcode | MatrixBarcodeFormat::dataMatrix;
Go to source

source

# Select your specific barcode formats for optimal performance
linear_format_filter = {
    LinearBarcodeFormat.code128,
    LinearBarcodeFormat.code93,
    LinearBarcodeFormat.code39,
    LinearBarcodeFormat.ean13,
    LinearBarcodeFormat.ean8,
    LinearBarcodeFormat.upcA,
    LinearBarcodeFormat.upcE,
    LinearBarcodeFormat.itf,
}
matrix_format_filter = {MatrixBarcodeFormat.qrcode, MatrixBarcodeFormat.dataMatrix}

Successfully detecting and decoding barcodes depends heavily on a sharp and well-exposed 2D image. Use the suggested 2D settings from the barcode detector and capture a 2D frame.

Go to source

source

const auto settings2d = barcodeDetector.suggestSettings(camera);
const auto frame2d = camera.capture2D(settings2d);
Go to source

source

settings_2d = barcode_detector.suggest_settings(camera)
frame_2d = camera.capture_2d(settings_2d)

Linear barcodes are detected and decoded in two steps. Detection identifies candidate regions in the image that are likely to contain a barcode. Since decoding has not yet been attempted, the list may include false positives. These are regions that resemble a barcode but are not. Decoding reads the actual code from each candidate. If a candidate could not be decoded, the corresponding result is empty.

Note

For a simpler one-call alternative, use readLinearCodes, which combines detection and decoding into a single function call. The two-phase approach gives you explicit access to candidate regions and their bounding boxes before decoding.

Detect linear barcode candidate regions:

Go to source

source

const auto detectionResults = barcodeDetector.detectLinearCodes(frame2d);
Go to source

source

detection_results = barcode_detector.detect_linear_codes(frame_2d)

Then decode the detected candidates:

Go to source

source

const auto decodingResults = barcodeDetector.decodeLinearCodes(detectionResults, linearFormatFilter);
Go to source

source

decoding_results = barcode_detector.decode_linear_codes(detection_results, linear_format_filter)

Print the candidates alongside their decoded results. Each detection candidate exposes its bounding box, and each decoded result exposes the code, format, and its own bounding box:

Go to source

source

if(!detectionResults.empty())
{
    std::cout << "Detected " << detectionResults.size() << " linear barcode candidates:" << std::endl;
    for(size_t i = 0; i < detectionResults.size(); ++i)
    {
        std::cout << "-- Candidate " << (i + 1) << ":" << std::endl;
        std::cout << "   Bounding box: " << detectionResults[i].boundingBox() << std::endl;
        const auto &decodingResult = decodingResults[i];
        if(decodingResult.has_value())
        {
            const auto &decoded = decodingResult.value();
            std::cout << "   Code:         " << decoded.code() << std::endl;
            std::cout << "   Format:       " << toString(decoded.codeFormat()) << std::endl;
            std::cout << "   Bounding box: " << decoded.boundingBox() << std::endl;
        }
        else
        {
            std::cout << "   Failed to decode" << std::endl;
        }
    }
}
else
{
    std::cout << "No linear barcode candidates detected" << std::endl;
}
Go to source

source

if detection_results:
    print(f"Detected {len(detection_results)} linear barcode candidates:")
    for i, (candidate, decoded) in enumerate(zip(detection_results, decoding_results, strict=False)):
        print(f"-- Candidate {i + 1}:")
        print(f"   Bounding box: {candidate.bounding_box()}")
        if decoded is not None:
            print(f"   Code:         {decoded.code()}")
            print(f"   Format:       {decoded.code_format()}")
            print(f"   Bounding box: {decoded.bounding_box()}")
        else:
            print("   Failed to decode")
else:
    print("No linear barcode candidates detected")

Matrix barcodes do not have a two-phase API. Use readMatrixCodes to detect and decode in a single call:

Go to source

source

const auto matrixBarcodeResults = barcodeDetector.readMatrixCodes(frame2d, matrixFormatFilter);
Go to source

source

matrix_barcode_results = barcode_detector.read_matrix_codes(frame_2d, matrix_format_filter)

Print the decoded matrix barcodes with their bounding boxes:

Go to source

source

if(!matrixBarcodeResults.empty())
{
    std::cout << "Detected " << matrixBarcodeResults.size() << " matrix barcodes:" << std::endl;
    for(const auto &result : matrixBarcodeResults)
    {
        std::cout << "-- Code:         " << result.code() << std::endl;
        std::cout << "   Format:       " << toString(result.codeFormat()) << std::endl;
        std::cout << "   Bounding box: " << result.boundingBox() << std::endl;
    }
}
else
{
    std::cout << "No matrix barcodes detected" << std::endl;
}
Go to source

source

if matrix_barcode_results:
    print(f"Detected {len(matrix_barcode_results)} matrix barcodes:")
    for result in matrix_barcode_results:
        print(f"-- Code:         {result.code()}")
        print(f"   Format:       {result.code_format()}")
        print(f"   Bounding box: {result.bounding_box()}")
else:
    print("No matrix barcodes detected")

Barcode Detection in Zivid Studio

Tip

Barcode detection in Zivid Studio does not require a software license.

You can easily test barcode detection using Zivid Studio. Connect to a camera and perform a 2D capture of a scene containing barcodes by selecting the barcode preset. Then click on ViewShow barcodes to visualize decoded barcodes in the image.

Barcode detection in Zivid Studio

Detected barcodes visualized in Zivid Studio.

You can speed up the detection by selecting your specific barcode format in ViewConfigure barcodes to detect.

Barcode Detection Performance

The performance of detection and decoding depends on multiple factors, including:

  • Barcode size

  • Distance to barcode

  • Image resolution

  • Image focus

  • Horizontal and vertical FOV

These factors effectively make up the spatial resolution on the barcode surface, which is important to maximize for reliable detection and decoding.

Image resolution and focus can be controlled by the camera settings, where we recommend BarcodeDetector::suggestSettings(camera) for optimal capture settings. The camera’s horizontal and vertical FOV are fixed for each camera model, where larger FOVs lead to lower spatial resolution. Distance to the barcode can be controlled by the application within some constraints, but is also limited by the camera’s working range.

The recommended cameras for barcode detection are therefore Zivid 2+ MR130 and MR60, as these provide sufficient spatial resolution within their working ranges. Below are some benchmarks for a variety of static barcodes qualified at different distances, orientations and placements within the FOV.

Tip

Stay within the green area for reliable decoding results.

Version History

SDK

Changes

2.18.0

Linear barcode detection and decoding are now separated into two steps. detectLinearCodes returns candidate regions as LinearBarcodeDetectionResult. decodeLinearCodes takes detection results and returns decoded codes as LinearBarcodeDecodingResult. MatrixBarcodeDetectionResult is renamed to MatrixBarcodeDecodingResult. All result types now expose a bounding box. ITF (Interleaved 2 of 5) is now a supported linear barcode format. UPC-A codes are now returned as EAN-13 (UPC-A is a valid subset of EAN-13), except when only UPC-A is in the format filter.

2.17.0

Barcode detection API is added.