9

可能重复:
通过 websocket 接收图像

使用

imageData = context.getImageData(0, 0, width, height);
JSON.stringify(imageData.data);

我抓取像素数据,将其转换为字符串,然后通过 websockets 通过网络发送。但是,此字符串可能非常大,具体取决于画布对象的大小。我尝试使用此处找到的压缩技术:JavaScript implementation of Gzip but socket.io throws the error Websocket message contains invalid character(s).Is there an effective way to compress this data so it can be sent over websockets?

4

4 回答 4

7

根据您想要的效率轴(带宽与 CPU 效率),我会推荐几种方法。

选项 1:您可以使用画布的 toDataURL 方法。这将返回画布图像数据的 base64 编码图像。它将使用您指定的图像格式(或默认为 PNG)进行压缩,并将预编码为 base64 以通过 WebSocket 发送。

canvas = document.getElementById("mycanvas");
b64png = canvas.toDataURL();

ws.send(b64png);

选项 2:如果您可以容忍有损压缩,那么您可以通过 toDataURL 方法将图像请求为 base64 编码的 JPEG:

canvas = document.getElementById("mycanvas");
b64jpeg = canvas.toDataURL("image/jpeg");

ws.send(b64jpeg);

选项 3:如果您使用的浏览器支持二进制 WebSocket 数据(Chrome、Firefox、IE 10),那么您可以直接通过 WebSocket 发送画布数组缓冲区

canvas = document.getElementById("mycanvas");
ctx = canvas.getContext('2d');
imgdata = ctx.getImageData(0,0, width, height).data; // This is a Uint8ClampedArray
ws.send(imgdata.buffer); // Send the ArrayBuffer from the Uint8ClampedArray

选项 3 可能在带宽方面效率最低,但在客户端和服务器端的处理能力方面效率最高,因为图像数据是原始发送的,几乎不需要预处理/后处理。

带宽效率最高的选项可能是 #2,但在将图像数据转换为 JPEG 格式的过程中,您会损失一些图像质量。您甚至可以更进一步,将数据 base64 解码为数组缓冲区或 blob,然后通过二进制 WebSocket 发送,这样您就不会获得 33% 的 base64 带宽开销,但这会增加更多的 CPU 开销。

如果您想要有效的带宽而不损失任何图像质量,那么选项 #2 是您最好的选择。

一些注意事项/注意事项

toDataURL 为 base64 数据添加前缀,如下所示:

"data:image/png;base64,iVBORw0KGgoAAAA..."

数据 URL 格式的一个好处是您可以将整个内容粘贴到浏览器地址栏中,然后浏览器将呈现图像。

有关 toDataURL 的更多信息,请参阅MDN 画布页面

于 2012-06-25T21:45:35.340 回答
4

最节省带宽的方法是发送照片,如数据是 JPEG 编码的二进制作为 blob

您可以将<canvas>数据作为二进制 JPEG blob 获取:

https://github.com/miohtama/Krusovice/blob/master/src/tools/resizer.js#L51

(对于非照片类内容,您还可以获得 PNG blob)

Blob 始终是原始二进制文件,不涉及 UTF-8 或 base64 废话。

WebSocket.send() 支持 blob 作为输入:

https://developer.mozilla.org/en/WebSockets/WebSockets_reference/WebSocket

HTTP Blob 发送:

https://developer.mozilla.org/en/DOM/XMLHttpRequest/Sending_and_Receiving_Binary_Data

您使用不同浏览器的里程可能会有所不同。

于 2012-06-25T21:58:35.453 回答
3

我不同意关闭的尝试,因为您要求采用更有效的方式。我们至少可以帮助您想出一个更有效的方法。

这真的取决于你在做什么。你能让客户做更多的工作吗?

您真的必须发送所有画布像素数据吗?您可以只发送已更改的像素吗?(或者这几乎是全部?)

仅来回发送更改会将其推向计算问题,而不是大量数据传输问题。


根据您的应用程序,您能否跟踪已更改的区域?如果你有 2-3 个小矩形在画布上发生了变化,那么它应该比整个画布要小得多。


与任何效率问题一样,首先要问你是否做对了。您真的需要通过网络传输大量像素数据吗?通常使用画布,通过发送更改场景的命令在服务器上重新创建场景比通过位图本身发送更容易。Websockets 应该很适合这个。对于许多绘图应用程序和游戏来说,这可能是一个很好的解决方案,但同样取决于您在这里要完成的工作。

于 2012-06-25T21:04:02.693 回答
0

我意识到一种方法可以显着减少通过网络发送的数据。

getImageData方法返回一个具有属性的对象,该data属性本身就是一个对象,其中键作为像素的索引,值作为单独的红色、绿色、蓝色或 alpha。键使对象变得非常大,特别是因为 300x300 画布对象将具有 300x300x4 = 360,000 个对象键/值对。

因此,通过仅提取颜色的值并将它们推入数组中:

function extractValues(obj){
  var array = [];
  for (var key in obj){
    array.push(obj[key]);
  }
  return array;
}

我能够将数据减少 50% 以上,从而显着提升了性能。

于 2012-06-25T21:13:27.527 回答