2012-06-25 12 views
9

Possible Duplicate:
Receiving image through websocketCómo tratar eficazmente con una gran cantidad de HTML5 datos de píxeles lona sobre websockets

Usando

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

agarro los datos de píxeles, convertirlo en una cadena, y luego enviarlo a través del cable a través de websockets. Sin embargo, esta cadena puede ser bastante grande, dependiendo del tamaño del objeto canvas. Intenté usar la técnica de compresión que se encuentra aquí: JavaScript implementation of Gzip pero socket.io arroja el error Websocket message contains invalid character(s). ¿Hay alguna forma efectiva de comprimir estos datos para que puedan enviarse a través de websockets?

+0

@Esailija ¿Quizás ... es el consenso para codificar las cadenas base64? – user730569

+0

Qué tal esto http://stackoverflow.com/questions/6869926/websockets-and-binary-data. Realmente, solo estoy buscando google aquí: D – Esailija

+0

@Esailija, sí, pero ninguno de estos es realmente sobre la mejor manera de comprimir una cadena ... cómo enviar imágenes o datos binarios – user730569

Respuesta

3

No estoy de acuerdo con los intentos de cierre ya que me pidió una forma más eficiente. Lo menos que podemos hacer es ayudarlo a encontrar una forma más eficiente.

Realmente depende de lo que estés haciendo. ¿Puedes hacer que el cliente trabaje más?

¿De verdad tiene que enviar todos de los datos del lienzo de píxeles? ¿Puedes enviar solo los píxeles que han cambiado? (¿O eso es casi todos?)

Enviar solo los cambios hacia adelante y hacia atrás lo llevaría a un problema informático en lugar de a un problema de gran cantidad de datos por el cable.


Dependiendo de su aplicación, ¿puede realizar un seguimiento de las regiones que han cambiado? Si tiene 2-3 rectángulos pequeños de los que han cambiado en el lienzo, entonces debería ser mucho más pequeño para enviar que todo el lienzo.


Al igual que con cualquier pregunta sobre eficiencia, vale la pena preguntar si está haciendo lo correcto en primer lugar. ¿De verdad necesitas arrojar grandes cantidades de datos de píxeles sobre el cable? A menudo, con lienzo es más fácil recrear la escena en el servidor enviando los comandos que han cambiado la escena de lo que es enviarlos sobre el mapa de bits. Websockets debería ser adecuado para esto. Esta podría ser una buena solución para muchas aplicaciones de dibujo y juegos, pero nuevamente depende de lo que estás tratando de lograr aquí.

+0

Dentro de las limitaciones de "enviar una imagen completa de lienzo al servidor usando websocket de la forma más eficiente de ancho de banda" hay una respuesta definitiva que he escrito debajo de –

0

Me di cuenta de una manera de recortar significativamente los datos que se enviaban por el cable.

El método getImageData devuelve un objeto con una propiedad data que a su vez es un objeto con claves como el índice del píxel y el valor como el rojo, verde, azul o alfa individual. Las claves hacían que el objeto fuera realmente grande, especialmente dado que un objeto canvas de 300x300 tendría 300x300x4 = 360,000 pares clave/valor de objeto.

Así extrayendo sólo los valores de los colores y empujándolos en una matriz:

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

yo era capaz de reducir los datos de más de un 50%, lo que condujo a una significativa ganancia de rendimiento.

+0

La codificación del lado del cliente a PNG o JPEG reduce la transferencia de datos varias órdenes de magnitud –

+0

@MikkoOhtamaa Claro, pero sería tonto no hacer lo que mencioné en esta respuesta además de la compresión ... – user730569

6

Hay varias maneras que recomendaría dependiendo del eje de eficiencia que desee (ancho de banda frente a la eficiencia de la CPU).

Opción 1: Puede utilizar el método canvas toDataURL. Esto devuelve una imagen codificada en base64 de los datos de imagen del lienzo.Se comprimirá utilizando el formato de imagen que especifique (o PNG para el valor predeterminado) y se codificará previamente en base64 para enviarlo a través de WebSocket.

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

ws.send(b64png); 

Opción 2: Si puede tolerar la compresión con pérdida, entonces puede pedir la imagen como un base64 JPEG a partir del método toDataURL:

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

ws.send(b64jpeg); 

Opción 3: Si está utilizando un navegador que admite datos binarios de WebSocket (Chrome, Firefox, IE 10), puede enviar el lienzo arraybuffer directamente a través de 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 

La opción 3 probablemente sea la menos eficiente en términos de ancho de banda, pero la más eficiente en términos de potencia de procesamiento en el lado del cliente y del servidor porque los datos de imagen se envían en bruto con poco procesamiento pre/post requerido.

La opción más eficiente en ancho de banda probablemente será # 2, pero perderá algo de calidad de imagen durante la conversión de los datos de imagen a formato JPEG. Incluso podría ir más allá y base64 decodificar los datos en un arraybuffer o blob y enviarlos a través de WebSocket binario para que no obtenga el 33% de ancho de banda base64, pero esto agrega aún más sobrecarga de CPU.

Si desea un ancho de banda eficiente sin perder calidad de imagen, la opción n. ° 2 es su mejor opción.

Algunas notas/advertencias:

El toDataURL antepuesta al algo datos base64 como esto:

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

Una cosa agradable sobre el formato de URL datos es que se puede tomar todo el asunto y pegar en la barra de direcciones de su navegador y el navegador representará la imagen.

Consulte el MDN Canvas page para obtener más información sobre toDataURL.

+0

¿Es mejor enviar como codificación binaria en lugar de base64? –

+0

@MikkoOhtamaa, creo que me referí a eso en la respuesta. Es mejor en términos de eficiencia del ancho de banda, pero no conozco ninguna API que le proporcione una imagen comprimida que no esté codificada en base64. Esto significa que tiene que decodificarlo manualmente en un tipo de datos binarios antes de enviarlo, lo que agregará sobrecarga de CPU al lado del cliente. – kanaka

+0

Consulte mi respuesta para obtener una imagen como blob binario –

4

La forma más ancho de banda eficiente es para enviar la foto como datos se JPEG codificado binario como blob

Puede obtener <canvas> de datos en formato binario burbuja JPEG:

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

(Para los que no -como contenido fotográfico también puede obtener blob PNG)

Blob siempre es binario en bruto, no involucrado UTF-8 o basura base64.

WebSocket.send() es compatible con manchas como entrada:

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

HTTP enviando Blob:

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

Su kilometraje con diferentes navegadores puede variar.

+0

A menos que esté ejecutando Firefox con soporte para canvas.mozGetAsFile (que probablemente desaparecerá) este será uno de los métodos menos eficientes de CPU porque tienes que decodificar base64 el resultado de toDataURL a un ArrayBuffer y luego construir un Blob fuera del buffer de la matriz. Además, en este caso, la conversión de Blob es solo por encima y el ArrayBuffer podría enviarse directamente a través del canal WebSocket. – kanaka

+2

-1 por un par de razones: JPEG no siempre es la forma más eficiente de ancho de banda para codificar datos de imagen (a menudo para imágenes de tipo clipart, PNG es mucho más eficiente) y Blob no es más eficiente en ancho de banda que enviar ArrayBuffer sobre WebSockets (y menos CPU eficiente en este caso porque está haciendo una conversión innecesaria de ArrayBuffer a Blob en la mayoría de los navegadores). Repara la declaración principal definitiva (pero incorrecta) y eliminaré la votación negativa. – kanaka

+0

Realmente depende de tantas variables. Para imágenes más pequeñas, esto parece ser cierto. Para imágenes más grandes, mis pruebas ad hoc muestran que una base64-string puede comprimir a menos bytes cuando gzip es su 'JPEG equivalente. Pero eso, por supuesto, depende de que el servidor acepte solicitudes gzipped http. –

Cuestiones relacionadas