H5摄像头扫描多个二维码,怎么解决帧数抓取慢的问题?

  1. 利用getUserMedia获取摄像头权限

  2. 将摄像头返回的媒体流扔到video标签

  3. 使用canvas抓取video的某一帧画面

  4. 将其canvas转为base64通过wechat-qrcode-ocr-wasm解析其中是否包含二维码(这步很慢)

    • 当这步完成后才会执行video下一次画面帧抓取/或直接绘制二维码所在区域的图形,导致抓取的画面很慢,往往画布上的每秒帧数不会超过3帧,需要对着二维码区域停留很久才能抓到比较清晰的二维码照片
  // 解析二维码
  const video = this.$refs.video
  const canvas = this.$refs.canvas
  const ctx = canvas.getContext('2d')

  ctx.drawImage(video,0,0,canvas.width,canvas.height)

  const img = canvas.toDataURL()
 
  // 第一种
  // 到这一步就很慢了,画布上的帧数可以说是PPT也不为过
  // 因为必须等该接口返回才能抓下一帧
  this.getCode(img).then(result=>{
    conast {size,data,points} = result
    if(size === 0){
        this.animationTimer = window.requestAnimationFrame(()=>{
            //继续调用这个解析二维码函数
        })
        return
    }
    // 绘制扫码完成后的动画特效
  })

  // 第二种
  // 这种很快就会RuntimeError: Aborted(). Build with -sASSERTIONS for more info
  // 在不停的抓取video图片,并发起解析的请求,就会开始加载图片
  // 如果通过节流器控制应该会好点,但没试过
  this.animationTimer = window.requestAnimationFrame(()=>{
      //继续调用这个解析二维码函数
  })
  this.getCode(img).then(result=>{
    conast {size,data,points} = result
    if(size === 0){
        return
    }
    this.clearTimer();
    // 绘制扫码完成后的动画特效
  })
阅读 2.5k
1 个回答

思路没啥问题,主要是对视频的解析。
如果是纯前端来做,建议在play事件中做好节流,识别这块你用的wechat-qrcode-ocr-wasm没听过,不过可以试一下jsqrzxing.

另外建议用Web Worker开子线程做解析。

要想更快更准确的解析出二维码,这里给几个思路和建议:

  1. 通过调整二维码插件参数
  2. 优化图片,比如提高图片对比度、减少图片的噪点
  3. 减小扫描的区域:可以减小图片的解析
  4. 调整视频流的分辨率,分辨率太高也会导致图片解析慢
  5. 提高CPU和GPU,这是硬件方面的了

如果有条件,建议第2、5两条在服务器上完成,甚至可以把视频流全给服务端,让服务端去生成。如果不行只能前端自己搞,关键就是优化,优化的好识别的就快、准。

另外给个减少噪点和增加对比度的代码吧,你根据自己的需求去调试:

// 获取图片元素
const img = document.getElementById('image');

// 创建Canvas元素
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

// 将图片绘制到Canvas中
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);

// 获取像素数据
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;

// 减少噪点
for (let i = 0; i < data.length; i += 4) {
  const gray = (data[i] + data[i + 1] + data[i + 2]) / 3; // 转换为灰度值
  const threshold = 128; // 阈值
  const value = gray < threshold ? 0 : 255; // 二值化
  data[i] = data[i + 1] = data[i + 2] = value;
}

// 提高对比度
const contrast = 1.5;
const intercept = -(0.5 * contrast) + 0.5;
for (let i = 0; i < data.length; i += 4) {
  const value = data[i] * contrast + 255 * intercept;
  data[i] = data[i + 1] = data[i + 2] = value;
}

// 将处理后的像素数据绘制回Canvas中
ctx.putImageData(imageData, 0, 0);

// 将Canvas转换为图片元素
const newImg = new Image();
newImg.src = canvas.toDataURL();
document.body.appendChild(newImg);

记得这种计算类的代码尽量不要在主线程跑,可能会阻塞甚至卡死。
希望能对你有帮助吧

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题