2012-01-05 12 views
10

Tengo un método que convierte BufferedImages cuyo tipo es TYPE_CUSTOM en TYPE_INT_RGB. Estoy usando el siguiente código, sin embargo, me gustaría encontrar una forma más rápida de hacerlo.Alternativa más rápida a ColorConvertOp

BufferedImage newImg = new BufferedImage(
    src.getWidth(), 
    src.getHeight(), 
    BufferedImage.TYPE_INT_RGB); 

ColorConvertOp op = new ColorConvertOp(null); 
op.filter(src, newImg); 

Trabaja muy bien, sin embargo, es bastante lento y me pregunto si hay una manera más rápida de hacer esta conversión.

ColorModel antes de la conversión:

ColorModel: #pixelBits = 24 numComponents = 3 color space = [email protected] transparency = 1 has alpha = false isAlphaPre = false 

ColorModel después de la conversión:

DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0 

Gracias!


Actualización:

Resulta que el trabajo con los datos de píxeles prima era la mejor manera. Dado que TYPE_CUSTOM era en realidad una conversión RGB manualmente, es simple y es aproximadamente un 95% más rápido que ColorConvertOp.

public static BufferedImage makeCompatible(BufferedImage img) throws IOException { 
    // Allocate the new image 
    BufferedImage dstImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB); 

    // Check if the ColorSpace is RGB and the TransferType is BYTE. 
    // Otherwise this fast method does not work as expected 
    ColorModel cm = img.getColorModel(); 
    if (cm.getColorSpace().getType() == ColorSpace.TYPE_RGB && img.getRaster().getTransferType() == DataBuffer.TYPE_BYTE) { 
     //Allocate arrays 
     int len = img.getWidth()*img.getHeight(); 
     byte[] src = new byte[len*3]; 
     int[] dst = new int[len]; 

     // Read the src image data into the array 
     img.getRaster().getDataElements(0, 0, img.getWidth(), img.getHeight(), src); 

     // Convert to INT_RGB 
     int j = 0; 
     for (int i=0; i<len; i++) { 
      dst[i] = (((int)src[j++] & 0xFF) << 16) | 
        (((int)src[j++] & 0xFF) << 8) | 
        (((int)src[j++] & 0xFF)); 
     } 

     // Set the dst image data 
     dstImage.getRaster().setDataElements(0, 0, img.getWidth(), img.getHeight(), dst); 

     return dstImage; 
    } 

    ColorConvertOp op = new ColorConvertOp(null); 
    op.filter(img, dstImage); 

    return dstImage; 
} 

Respuesta

6

Las imágenes almacenadas son muy lentas. Tengo una solución, pero no estoy seguro de que te guste. La forma más rápida de procesar y convertir imágenes almacenadas es extraer la matriz de datos sin procesar desde dentro de la imagen almacenada. Para ello, llame a buffImg.getRaster() y conviértalo en el ráster específico. Luego, llame a raster.getDataStorage(). Una vez que tiene acceso a los datos brutos, es posible escribir un código rápido de procesamiento de imágenes sin que toda la abstracción en BufferedImages lo ralentice. Esta técnica también requiere una comprensión profunda de los formatos de imagen y cierta ingeniería inversa de su parte. Esta es la única forma en que he podido obtener el código de procesamiento de imágenes para que se ejecute lo suficientemente rápido para mis aplicaciones.

Ejemplo:

ByteInterleavedRaster srcRaster = (ByteInterleavedRaster)src.getRaster(); 
byte srcData[] = srcRaster.getDataStorage(); 

IntegerInterleavedRaster dstRaster = (IntegerInterleavedRaster)dst.getRaster(); 
int dstData[] = dstRaster.getDataStorage(); 

dstData[0] = srcData[0] << 16 | srcData[1] << 8 | srcData[2]; 

o algo por el estilo. Espere errores del compilador advirtiéndole que no debe acceder a rásteres de bajo nivel como ese. El único lugar donde he tenido problemas con esta técnica es dentro de los applets donde se producirá una infracción de acceso.

+0

¡Hermoso! Trabajar con los datos sin procesar reduce el tiempo de procesamiento en un 95%. Ver la publicación editada para saber exactamente cómo terminé haciendo esto. –

+0

No terminé usando las clases ByteInterleavedRaster e IntegerInterleavedRaster porque, por alguna razón, no tengo los paquetes Sun. –

+0

Holy crap Acabo de pasar de procesar un archivo de 100MB + psd durante 10 minutos a 8 segundos. ¡Gracias! – EdgeCaseBerg

0

Ha intentado suministrar cualquier RenderingHints? No hay garantías, pero utilizando

ColorConvertOp op = new ColorConvertOp(new RenderingHints(
    RenderingHints.KEY_COLOR_RENDERING, 
    RenderingHints.VALUE_COLOR_RENDER_SPEED)); 

en lugar de la null en el fragmento de código podrían acelerarlo un poco.

+0

intentó, sin suerte :( –

0

Sospecho que el problema podría ser que ColorConvertOp() funciona píxel por píxel (garantizado que es "lento").

Q: ¿Es posible para usted usar gc.createCompatibleImage()?

P: ¿Es el mapa de bits verdadero color verdadero, o utiliza un mapa de colores?

P: En caso de que falle, ¿estaría de acuerdo con escribir una interfaz JNI? ¿Ya sea a su propio código C personalizado o a una biblioteca externa como ImageMagick?

+0

también - tienen ¿miró alguna de las nuevas llamadas de ImageIO en Java 6 ++? http://docs.oracle.com/javase/6/docs/api/javax/imageio/ImageIO.html – paulsm4

+0

gc.createCompatibleImage() no funcionará (máquina sin cabeza), mapa de bits es color verdadero, no me opongo a usar JNI o ​​Ima geMagick pendiente Puedo obtener los mismos resultados que con ColorConvertOp. –

+0

Estoy usando ImageIO con la aceleración nativa para abrir el JPG. –

0

Si tiene JAI instalado, puede intentar desinstalarlo, si puede, o buscar la forma de desactivar codecLib al cargar JPEG. En una vida pasada tuve problemas similares (http://www.java.net/node/660804) y ColorConvertOp fue el más rápido en ese momento.

Como recuerdo, el problema fundamental es que Java2D no está optimizado para las imágenes TYPE_CUSTOM en general. Cuando instala JAI viene con codecLib que tiene un decodificador que devuelve TYPE_CUSTOM y se usa en lugar del predeterminado. La lista JAI puede brindar más ayuda, han pasado varios años.

-1

tal vez intente esto:

Bitmap source = Bitmap.create(width, height, RGB_565);//don't remember exactly... 
Canvas c = new Canvas(source); 
// then 
c.draw(bitmap, 0, 0); 

A continuación, se modificará el mapa de bits fuente.

Más tarde se puede hacer:

onDraw(Canvas canvas){ 
canvas.draw(source, rectSrs,rectDestination, op); 
} 

Si se consigue siempre vuelve a utilizar el mapa de bits por lo que será capaz de obtener un mejor rendimiento.También puede utilizar otras funciones de lienzo para dibujar su mapa de bits

2

He encontrado el renderizado usando Graphics.drawImage() en lugar de ColorConvertOp 50 veces más rápido. Solo puedo suponer que drawImage() es GPU acelerado.

es decir, esto es muy lento, al igual que 50ms una oportunidad para 100x200 rectángulos

public void BufferdImage convert(BufferedImage input) { 
    BufferedImage output= new BufferedImage(input.getWidht(), input.getHeight(), BufferedImage.TYPE_BYTE_BINARY, CUSTOM_PALETTE); 

    ColorConvertOp op = new ColorConvertOp(input.getColorModel().getColorSpace(), 
              output.getColorModel().getColorSpace()); 

    op.filter(input, output); 
    return output; 
} 

es decir, sin embargo, esto registra < 1 ms para la mismas entradas

public void BufferdImage convert(BufferedImage input) { 
    BufferedImage output= new BufferedImage(input.getWidht(), input.getHeight(), BufferedImage.TYPE_BYTE_BINARY, CUSTOM_PALETTE); 

    Graphics graphics = output.getGraphics(); 
    graphics.drawImage(input, 0, 0, null); 
    graphics.dispose(); 
    return output; 
} 
Cuestiones relacionadas