Node.js Barcode Reader for Desktop and Web

Last week, I shared how to display webcam video in desktop and web apps. Based on that, I implemented the barcode reading function using Node.js. In this article, I will share how to build Node.js barcode reader apps for desktop and web with Dynamsoft Barcode Reader SDK.

Binding the C/C++ APIs of Dynamsoft Barcode SDK with JavaScript

Let’s get started with the Node.js barcode C/C++ addon.

The source code is available on https://github.com/Dynamsoft/nodejs-barcode.

To support OpenCV Mat, I created a new API decodeBufferAsync(). The C/C++ code is as follows:

void DecodeBufferAsync(const FunctionCallbackInfo<Value>&amp; args) {
    if (!createDBR()) {return;}
    Isolate* isolate = Isolate::GetCurrent();
    Local<Context> context = isolate->GetCurrentContext();

    // get arguments
    unsigned char* buffer = (unsigned char*) node::Buffer::Data(args[0]); // file stream
    int width = args[1]->Int32Value(context).ToChecked();   // image width
    int height = args[2]->Int32Value(context).ToChecked();  // image height
    int stride = args[3]->Int32Value(context).ToChecked(); // stride
    int iFormat = args[4]->Int32Value(context).ToChecked(); // barcode types
    Local<Function> cb = Local<Function>::Cast(args[5]); // javascript callback function
    String::Utf8Value templateName(isolate, args[6]); // template name
    char *pTemplateName = *templateName;

    // initialize BarcodeWorker
    BarcodeWorker *worker = new BarcodeWorker;
    worker->request.data = worker;
    worker->callback.Reset(isolate, cb);
    worker->iFormat = iFormat;
    worker->pResults = NULL;
    worker->buffer = buffer;
    worker->width = width;
    worker->height = height;
    worker->bufferType = RGB_BUFFER;
    worker->stride = stride;
    
    if (hasTemplate(pTemplateName)) {
        // Load the template.
        char szErrorMsg[256];
        DBR_InitRuntimeSettingsWithString(hBarcode, pTemplateName, CM_OVERWRITE, szErrorMsg, 256);
        worker->useTemplate = true;
    }
    else {
        worker->useTemplate = false;
    }

    uv_queue_work(uv_default_loop(), &amp;worker->request, (uv_work_cb)DetectionWorking, (uv_after_work_cb)DetectionDone);
}

The first argument is a Node.js buffer pointer. You can call getData() to get byte array from Mat:

const vCap = new cv.VideoCapture(0);
var img = vCap.read();
dbr.decodeBufferAsync(img.getData(), img.cols, img.rows, img.step, barcodeTypes, function (err, msg) {
        results = msg

    }, "");

Note: the build configuration for macOS is a little bit different from the configuration for Linux in binding.gyp file:

'copies': [
                        {
                            'destination': '/usr/local/lib/',
                            'files': [
                                './platforms/macos/libDynamsoftBarcodeReader.dylib'
                            ]
                        }
                    ]

Not like on Linux, the dynamic library file will be copied to the /usr/local/lib/ directory once the build is done. The reason is the RPATH cannot work on macOS. We can use “otool -L dbr.node” to check the dependent library and then get the path /usr/local/lib/libDynamsoftBarcodeReader.dylib.

If you want to keep the library file and dbr.node under the same folder, you can manually change the library path:

cd build/Release
install_name_tool -change /usr/local/lib/libDynamsoftBarcodeReader.dylib @loader_path/libDynamsoftBarcodeReader.dylib dbr.node

I have published the package to https://www.npmjs.com/package/barcode4nodejs. To install the package, you need to have C++ dev tools installed and then run:

npm install -g node-gyp 
npm install barcode4nodejs

Building Node.js Barcode Reader for Desktop and Web in 5 Minutes

Desktop

Basically, we can use an infinite loop to capture webcam frames and show them in a window:

const cv = require('opencv4nodejs');
const vCap = new cv.VideoCapture(0);
const delay = 10;
while (true) {
  let frame = vCap.read();
  if (frame.empty) {
    vCap.reset();
    frame = vCap.read();
  }
 
  cv.imshow('OpenCV Node.js', frame);
  const key = cv.waitKey(delay); // Press ESC to quit
  if (key == 27) {break;}
}

However, if we invoke the async barcode decoding function in the loop, the callback function will never return. To make it work, we can use setTimeout() instead:

const dbr = require('barcode4nodejs');
const cv = require('opencv4nodejs');
dbr.initLicense("LICENSE-KEY")
barcodeTypes = dbr.barcodeTypes
const vCap = new cv.VideoCapture(0);
const drawParams = { color: new cv.Vec(0, 255, 0), thickness: 2 }
const fontFace = cv.FONT_HERSHEY_SIMPLEX;
const fontScale = 0.5;
const textColor = new cv.Vec(255, 0, 0);
const thickness = 2;

results = null;

function getframe() {
    let img = vCap.read();
    

    dbr.decodeBufferAsync(img.getData(), img.cols, img.rows, img.step, barcodeTypes, function (err, msg) {
        results = msg

    }, "", 1);
    

    cv.imshow('Webcam', img);
    const key = cv.waitKey(10); // Press ESC to quit
    if (key != 27) {
        setTimeout(getframe, 30);
    }

}

getframe()

Because the successive webcam images are similar, it is fine to draw the results on a different frame:

if (results != null) {
        for (index in results) {
            let result = results[index];

            let upperLeft = new cv.Point(result.x1, result.y1)
            let bottomLeft = new cv.Point(result.x2, result.y2)
            let upperRight = new cv.Point(result.x3, result.y3)
            let bottomRight = new cv.Point(result.x4, result.y4)

            img.drawLine(
                upperLeft,
                bottomLeft,
                drawParams
            )
            img.drawLine(
                bottomLeft,
                upperRight,
                drawParams
            )

            img.drawLine(
                upperRight,
                bottomRight,
                drawParams
            )
            img.drawLine(
                bottomRight,
                upperLeft,
                drawParams
            )

            img.putText(result.value, new cv.Point(result.x1, result.y1 + 10), fontFace, fontScale, textColor, thickness);
        }

    }
OpenCV Node.js barcode reader with webcam

Web

Copy the barcode detection code to the web.js file:

function capture() {
    var frame = wCap.read()
    if (frame.empty) {
        wCap.reset();
        frame = wCap.read();
    }

    dbr.decodeBufferAsync(frame.getData(), frame.cols, frame.rows, frame.step, barcodeTypes, function (err, msg) {
        // console.log(results)
        results = msg

    }, "", 1);

    if (results != null) {
        for (index in results) {
            let result = results[index];

            let upperLeft = new cv.Point(result.x1, result.y1)
            let bottomLeft = new cv.Point(result.x2, result.y2)
            let upperRight = new cv.Point(result.x3, result.y3)
            let bottomRight = new cv.Point(result.x4, result.y4)

            frame.drawLine(
                upperLeft,
                bottomLeft,
                drawParams
            )
            frame.drawLine(
                bottomLeft,
                upperRight,
                drawParams
            )

            frame.drawLine(
                upperRight,
                bottomRight,
                drawParams
            )
            frame.drawLine(
                bottomRight,
                upperLeft,
                drawParams
            )

            frame.putText(result.value, new cv.Point(result.x1, result.y1 + 10), fontFace, fontScale, textColor, thickness);
        }


    }

    img = cv.imencode('.jpg', frame);
    setTimeout(capture, 30);
}

capture();

Now we can run the server-side barcode detection. It is totally compatible with any web browser. Here is the screenshot from Microsoft Internet Explorer.

OpenCV Node.js Barcode for web

Source Code

https://github.com/yushulx/nodejs-barcode-reader