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

作者站长头像
站长
· 阅读数 4
  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();
    // 绘制扫码完成后的动画特效
  })
回复
1个回答
avatar
test
2024-07-07

思路没啥问题,主要是对视频的解析。如果是纯前端来做,建议在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);

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

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容