使用
imageData = context.getImageData(0, 0, width, height);
JSON.stringify(imageData.data);
我抓取像素数据,将其转换为字符串,然后通过websockets通过电线发送。但是,此字符串可能非常大,具体取决于画布对象的大小。我尝试使用此处的压缩技术:JavaScript implementation of Gzip但socket.io抛出错误Websocket message contains invalid character(s).
是否有一种有效的方法来压缩这些数据,以便可以通过websockets发送?
答案 0 :(得分:6)
根据您想要的效率轴(带宽与CPU效率),我会推荐几种方法。
选项1:您可以使用canvas 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 arraybuffer
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解码为arraybuffer或blob并通过二进制WebSocket发送,这样就不会获得33%base64带宽开销,但这会增加CPU开销。
如果您想要有效的带宽而不会丢失任何图像质量,那么选项#2是您最好的选择。
一些注释/警告:
toDataURL为base64数据加前缀如下:
"data:image/png;base64,iVBORw0KGgoAAAA..."
数据URL格式的一个好处是,您可以将整个内容粘贴到浏览器地址栏中,浏览器将呈现图像。
有关toDataURL的更多信息,请参阅MDN Canvas page。
答案 1 :(得分: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
您使用不同浏览器的里程可能会有所不同。
答案 2 :(得分:3)
由于您要求更有效的方法,我不同意关闭的尝试。我们至少可以做的是帮助你提出一种更有效的方式。
这实际上取决于你在做什么。你能让客户做更多工作吗?
你真的必须发送所有画布像素数据吗?您可以只发送已更改的像素吗? (或几乎所有人都是?)
只发送来回更改会将其推入计算问题而不是大量的数据线上问题。
根据您的应用,您可以跟踪已更改的区域吗?如果你在画布上有2-3个已经改变的小矩形,那么它应该比整个画布要小得多。
与任何效率问题一样,如果你在一开始就做正确的事情,那就值得一提。你真的需要通过线路投入大量的像素数据吗?通常使用canvas,通过发送已更改场景的命令比通过位图本身发送的命令更容易在服务器上重新创建场景。 Websockets应该非常适合这个。对于很多绘图应用程序和游戏来说,这可能是一个很好的解决方案,但这又取决于你在这里要完成的任务。
答案 3 :(得分: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%以上,从而显着提升了性能。