您可以在渲染之前从 websocket 中对图像进行下采样吗

Posted

技术标签:

【中文标题】您可以在渲染之前从 websocket 中对图像进行下采样吗【英文标题】:Can you downsample an image from a websocket prior to rendering 【发布时间】:2021-05-22 20:34:10 【问题描述】:

我有一个应用程序通过 websocket 大约每秒从服务器提取图像几次。应用程序负载很大,图像有时会跳过渲染。

图像本身的分辨率远高于所需的分辨率,因此在渲染之前对其进行下采样会有很大帮助。这可能吗?

这里是 websocket 代码:

const ws = new WebSocket(config.videoWebSocketURL);
<img className=styles.img src=`data:image/jpeg;base64,$data` />

欢迎任何其他加速渲染的理想。

【问题讨论】:

您是否尝试从静止图像(来自网络摄像头或视频源)进行实时视频流传输?我已经多次沿着这条路线走 - 它没有扩展。是什么产生了这些帧? 图片。它不是为了扩展或用于商业用途。这是一个概念验证 我所说的“无法缩放”的意思是,几乎不可能实时传输大型 JPEG 图像。 img 标签本身会为您缩小图像。但我怀疑真正的问题出在服务器端——你将帧推入套接字的速度比你的网络应用程序消耗的速度要快。你能在服务器端下采样吗? 另外,所有这些都是推测,因为您提供的架构细节很少。 如果您的应用程序已经很难处理大图像,那么添加一个下采样过程不会进一步削弱它吗?如果可能,这应该在服务器端完成。 【参考方案1】:

每秒大约几次通过 websocket 拉入图像

你正在让自己走上痛苦的道路。

使用旨在显示动画内容的正确协议和接口:HTTP + &lt;video&gt; 标签。

如果我有一堆 jpeg 文件作为源文件,我会将它们在服务器端(发送之前)转换为视频。然后我会将流直接发送到 html5 video 标签。这解决了许多问题,例如背压和缓冲问题、跳帧等。

对于转换,可能的解决方案是使用 ffmpeg,例如:

ffmpeg -framerate 10 -i frame-%d.jpg output.mp4


如果您真的想在浏览器中手动进行此渲染, 那么可能的探索方向是:

使用canvas 和requestAnimationFrame() API - 画布在immediate graphics mode 中工作,它可以渲染高达每秒60 帧; 使用createImageBitmap() 进行图像处理(注意:下采样和调整大小尚不兼容跨浏览器); 使用网络工作者和OffscreenCanvas 对象,可以potentially speed up operations(注意:这是实验性功能)。

演示

在运行它之前,将一些名为 frame-0.jpegframe-6.jpeg 的图像放在此应用文件旁边。

我使用了从https://www.thispersondoesnotexist.com/ 生成的人脸图像,这些图像是 1024x1024 像素。 不是故意使用 React 来实现的,以了解最小解决方案的速度。

package.json


  "name": "images-on-canvas",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "engines": 
    "node": "14.15.5"
  ,
  "scripts": 
    "start": "nodemon app.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  ,
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": 
    "express": "^4.17.1",
    "express-ws": "^4.0.0"
  ,
  "devDependencies": 
    "nodemon": "^2.0.7"
  

app.js

const port = process.env.PORT || 3000
const path = require('path')
const fs = require('fs')
const app = require('express')()
require('express-ws')(app)

app.get('/', (req, res) => res.sendFile(path.resolve(__dirname, 'index.html')))
app.ws('/', async function (socket) 
  // send frame files 0 to 6
  for (let i = 0, count = 6; i <= count; i += 1) 
    try 
      let data = await fs.promises.readFile(path.resolve(__dirname, `frame-$i.jpeg`))
      socket.send(data)
     catch (e) 
      console.error(e)
    
  
)
app.listen(port, () => console.log(`Listening on port $port`))

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Rendering Test</title>
    <meta charset="utf-8">
  </head>
  <body>
    <canvas id="mycanvas"  ></canvas>
    <script type="text/javascript">
      var cv = document.getElementById('mycanvas')
      var ctx = cv.getContext('2d')
      var ws = new WebSocket(location.origin.replace(/^http/, 'ws'))
      var sprites = []

      ws.onmessage = function onMessage(event) 
        // NOTE: check compatibility for options object, works in Chrome 52+
        // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap#browser_compatibility
        var opts = 
          resizeWidth: 200,
          resizeHeight: 200,
          resizeQuality: 'pixelated'
        
        createImageBitmap(event.data, 0, 0, 1024, 1024, opts).then(function (sprite) 
          sprites.push(sprite)
        )

        // NOTE: classic way to draw on canvas
        // var img = new Image()
        // img.addEventListener('load', function onLoad(event) 
        //   ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
        //   ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height)
        // )
        // img.src = URL.createObjectURL(event.data)
      

      function draw(high_res_timestamp) 
        if (sprites.length) 
          console.log(high_res_timestamp)
          var sprite = sprites.shift()
          ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
          ctx.drawImage(sprite, 0, 0, ctx.canvas.width, ctx.canvas.height)
        
        requestAnimationFrame(draw)
      
      draw()
    </script>
  </body>
</html>

写完所有内容后,我真的认为更好的方法是在服务器端执行此操作,正如我在上面所写的那样。

为什么?您有可预测的输出,并且提供视频是一个已解决的问题。这样一来,您就可以避免使用实验性 API 或没有得到很好支持的 API 带来的所有麻烦,并且可能需要编写、测试和维护的代码更少。

【讨论】:

有趣。这正是我的想法。我不知道为什么它最初是这样完成的。服务器上的代码需要特定图像是有原因的,但我们没有理由在浏览器中以这种方式显示它。 该项目是在 React 中完成的,我正在放置一个类组件以将渲染限制为每秒一次或两次。我希望这可以将开销降至最低。 我已经更新了探索客户端渲染的答案,也许有帮助!

以上是关于您可以在渲染之前从 websocket 中对图像进行下采样吗的主要内容,如果未能解决你的问题,请参考以下文章

如何从文档进纸器异步扫描和传输图像

如何在opencv中对鱼眼图像生成的去扭曲?

如何在反应中从对象映射动态设置 sessionStorage

有没有办法在 qml 中从网上下载图像

Xcode - IB Designable:无法渲染和更新自动布局(找不到合适的图像)

将剪切图像之一分配给精灵渲染器