La clase BufferedImage

Iniciado por Proteus1989, 31 Marzo 2012, 03:13 AM

0 Miembros y 1 Visitante están viendo este tema.

Proteus1989

He desarrollado una aplicación que captura la pantalla de un PC y mediante sockets la envía a un segundo ordenador.
La aplicación en sí funciona perfectamente. A través de los sockets envío como cadena de bytes la imagen capturada y en el receptor recompongo esa información para mostrarla por la pantalla.

El problema es el siguiente, en 1080x1920 la pantalla consta de 2073600 pixeles. Estos pixeles se encuentran dentro de un objeto BufferedImage y los convierto en un array de enteros de 4 bytes de tamaño 2073600.
Una vez obtengo el array de enteros transformo cada entero en 4 bytes, añadiendolo finalmente a un array de tamaño 2073600*4 para enviarlo por el socket.

Total que al final me encuentro con un array de 8MB para enviar por el socket.

Entonces mi pregunta es obvia, ¿como hago para reducir ese tamaño tan exagerado a algo más asequible como por ejemplo 500 kb.

raul_samp

Pues en los algoritmos de compresión con perdida (que son los que se usan para imágenes como JPEG) hay muchas matemáticas de por medio. Pero si quieres algo ya implementado creo que la clase que buscas es ImageWriteParam
o algo por el estilo. Échale un vistazo al Api y ya nos cuentas si te sirve.

Api JDK6 ImageWriteParam: http://docs.oracle.com/javase/6/docs/api/javax/imageio/ImageWriteParam.html
Yeah Mr. White, yes science!!

Proteus1989

Ya pensaba que nadie sabía responderme. Lo miraré, tenlo por seguro.
Gracias!

Proteus1989

He estado mirando la clase pero no la termino de comprender, como le pasaría mi array de enteros para que me lo comprimiese?


Proteus1989

Encontré esto, pero era con jpgs http://www.exampledepot.com/egs/javax.imageio/JpegWrite.html
El problema es que el uso de disco duro no es eficiente.

PD: Lo encontré con el google, que se que no muerde xD

raul_samp

Si a lo que te refieres es que no te renta guardarlos en disco para luego mandarlo, lo veo normal xD, y ahora mismo no se decirte así como se programaría exactamente, me tendría que poner a programar, pero la idea supongo que seria algo así como decirle al ImageWriter que su Output es el mismo que el del socket.

Ya te digo que no estoy muy seguro de que sea exactamente así, habría que profundizar la idea en el API, si no es mucho código y quieres subirlo para que le echemos un vistazo lo haré encantado.

Un saludo!!
Yeah Mr. White, yes science!!

Proteus1989

Exacto, el tener que guardarlos y recuperarlos del HDD es demasiado costoso. El código es algo extenso (sobre unas 7 u 8 clases), quizás demasiado para lo que aquí interesa resolver.

Emisor
Este código recupera una captura de pantalla que transforma a int[] para enviarla por el socket.
Código (java) [Seleccionar]

int ancho = (int) java.awt.Toolkit.getDefaultToolkit().getScreenSize().getWidth();
int alto = (int) java.awt.Toolkit.getDefaultToolkit().getScreenSize().getHeight();
v = new int[ancho*alto];
dataBuffer = robot.createScreenCapture(rectangulo).getData().getDataBuffer(); //Captura la pantalla
for(int i = 0; i < ancho*alto; i++)
v[i] = dataBuffer.getElem(i);




Receptor
Se encarga de recibir el int[] y recomponerlo.
Código (java) [Seleccionar]


// Creo un ByteBuffer donde añado v (byte[])
// v es el int[] que envié y que recibo en forma de 4 bytes por int.
ByteBuffer bb = ByteBuffer.allocate(v.length);
bb.put(v);
bb.order(ByteOrder.BIG_ENDIAN);
bb.rewind();

// Ahora transformo ese ByteBuffer en un IntBuffer
IntBuffer ib = bb.asIntBuffer();
int[] result = new int[v.length / 4];
ib.get(result);
BufferdedImage imagen = generarBufferedImage(result); //este método está implementando en el código de abajo.




Y con este último código copiado de internet y que no comprendo muy bien recupero el BufferedImage inicial

Código (java) [Seleccionar]

private BufferedImage generarBufferedImage(int[] v)
{
DataBufferInt DB = new DataBufferInt(v, (resolucion.getAncho() * resolucion.getAlto()), 0);
int[] BM = new int[]
{ 0xff0000, 0xff00, 0xff };
SinglePixelPackedSampleModel SM = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, resolucion.getAncho(), resolucion.getAlto(), BM);

// creating the raster
Point P = new Point(0, 0);
WritableRaster R = Raster.createWritableRaster(SM, DB, P);
BufferedImage bi = new BufferedImage(resolucion.getAncho(), resolucion.getAlto(), BufferedImage.TYPE_INT_RGB);
BufferedImage BI = new BufferedImage(bi.getColorModel(), R, false, null);
return BI;
}

raul_samp

#8
La verdad no se si esto es una paja mental muy grande pero podrías intentar partir de esta idea a ver que sale:

Código (java) [Seleccionar]

BufferedImage buffer = robot.createScreenCapture(rectangulo);
GZIPOutputStream out;

try {
out = new GZIPOutputStream(socket.getOutputStream());
ImageIO.write(buffer, "jpg", out);
} catch (IOException e1) {
e1.printStackTrace();
}finally{
if(out != null)
out.close();
}


La intención esta clara, es usar un flujo de salida comprimido desde el emisor y descomprimir en el receptor con un GZIPInputStream.

A mi me parece que algo así tendría que funcionar, no se si ves que no coméntanos a ver cual es el problema y que más podemos hacer. Ya te digo que es lo primero que se me ha ocurrido  :D
Yeah Mr. White, yes science!!

Proteus1989

Parece no funcionar. El GZIP no deja apuntar al OutputStream ni casteandolo