WebGL彩图转灰度图,提升Web条形码识别性能

图像识别算法通常都要把彩图,转成灰度图,再转成二值图。如果把图像处理部分放到GPU上,就可以减少CPU的时间消耗。这篇文章分享下用WebGL把摄像头传入的彩图转成灰度图,以此提高Web条形码SDK的解码性能。

Web条形码识别

使用Dynamsoft JavaScript Barcode SDK(https://www.npmjs.com/package/dynamsoft-javascript-barcode)可以快速实现一个Web条码扫描App。比如下面的代码,可以直接创建一个带webcam的扫码app:


<html>
<body>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/dbr.js" data-productKeys="PRODUCT-KEYS">script> 
    <script>
        let scanner = null;
        (async()=>{
            scanner = await Dynamsoft.BarcodeScanner.createInstance();
            scanner.onFrameRead = results => {console.log(results);};
            scanner.onUnduplicatedRead = (txt, result) => {alert(txt);};
            await scanner.show();
        })();
    script> 
body>
html>

这里的BarcodeScanner类对camera做了封装,用户不需要手写摄像头相关代码,传入接口的图像是彩图。如果是自己通过canvas来实现,就需要使用BarcodeReader类,然后调用decodeBuffer()接口:

var barcodereader = null;
(async()=>{
    barcodereader = await Dynamsoft.BarcodeReader.createInstance();
    await barcodereader.updateRuntimeSettings('speed');
    let settings = await barcodereader.getRuntimeSettings();
    settings.deblurLevel = 0;
    barcodereader.updateRuntimeSettings(settings);
})();
 
let canvas2d = document.createElement('canvas');
canvas2d.width = width;
canvas2d.height = height;
var ctx2d = canvas2d.getContext('2d');
ctx2d.drawImage(videoElement, 0, 0, width, height);
buffer = ctx2d.getImageData(0, 0, width, height).data;
 
if (barcodereader){
barcodereader
        .decodeBuffer(
            buffer,
            width,
            height,
            width * 4,
            Dynamsoft.EnumImagePixelFormat.IPF_ARGB_8888
        )
        .then((results) => {
            showResults(results);
        });
}

WebGL彩图转灰度图

灰度图是单通道,而彩图是四通道,所以理论上传入灰度图的解码速度要比彩图快。根据上面的解码代码,如果是灰度图,可以改成:

barcodereader
        .decodeBuffer(
            gray,
            width,
            height,
            width,
            Dynamsoft.EnumImagePixelFormat.IPF_GrayScaled
        )
        .then((results) => {
            showResults(results);
        });

关于如何使用WebGL来画图,可以参考学习https://webglfundamentals.org/webgl/lessons/webgl-2d-drawimage.html。

根据网站的示例代码,修改shader来实现灰度转换:

<!-- https://gist.github.com/Volcanoscar/4a9500d240497d3c0228f663593d167a -->
  <script id="drawImage-fragment-shader" type="x-shader/x-fragment">
  precision mediump float;
   
  varying vec2 v_texcoord;
   
  uniform sampler2D u_texture;
  uniform float u_colorFactor;
   
  void main() {
    vec4 sample =  texture2D(u_texture, v_texcoord);
    float grey = 0.21 * sample.r + 0.71 * sample.g + 0.07 * sample.b;
    gl_FragColor = vec4(sample.r * u_colorFactor + grey * (1.0 - u_colorFactor), sample.g * u_colorFactor + grey * (1.0 - u_colorFactor), sample.b * u_colorFactor + grey * (1.0 - u_colorFactor), 1.0);
  }
  </script>

接下来就是通过视频元素来创建绘制纹理:

var drawInfo = {
            x: 0,
            y: 0,
            dx: 1,
            dy: 1,
            textureInfo: loadImageAndCreateTextureInfo(videoElement)
          };
         
        draw(drawInfo);

通过纹理显示,会发现图像是颠倒的,所以需要调用pixelStorei做Y轴翻转:

function drawImage(tex, texWidth, texHeight, dstX, dstY) {
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

从纹理中获取灰度图的数据:

buffer = new Uint8Array(width * height * 4);
gl.readPixels(
            0,
            0,
            gl.drawingBufferWidth,
            gl.drawingBufferHeight,
            gl.RGBA,
            gl.UNSIGNED_BYTE,
            buffer
        );

这里创建的依然是4通道数据,因为不支持单通道数据读取。获取数据之后,要把灰度数据从数组中提取出来:

gray = new Uint8Array(width * height);
let gray_index = 0;
for (i = 0; i < width * height * 4; i += 4) {
    gray[gray_index++] = buffer[i];
}

最后运行程序,可以看到事实的转换效果:

性能比较

通过console观察发现,使用WebGL之后,数据获取的时间5ms左右,而从canvas获取数据的时间只有1ms。WebGL的数据传输会比较耗时。但是灰度图的解码时间和彩图比要快上不少。所以总时间消耗是减少的。
WebGL彩图转灰度图,提升Web条形码识别性能_第1张图片

视频

WebGL彩图转灰度,减少Web扫码耗时,提高性能

源码

https://github.com/yushulx/WebGL-JavaScript-Barcode

你可能感兴趣的:(webgl,html5,条形码,扫码,摄像头)