2015-04-17 25 views
11

Quando si utilizza javax.imageio.ImageIO per caricare un JPEG a risoluzione elevata (9000x9000) dal disco, ci vuole più di 1 minuto nella mia scala applicazione. Ho provato a creare un progetto solo Java, ma ci vuole ancora troppo tempo - circa 30 secondi.La lettura di un JPEG 9000x9000 progressivamente codificato in Java richiede 1 minuto

Questo è come mi caricare l'immagine:

File file = new File("/Users/the21st/slow2.jpg"); 
BufferedImage image = ImageIO.read(file); 

Esiste un modo per migliorare le prestazioni in lettura progressivamente codificati JPEG grandi-res in Java?

L'immagine in questione è this one (moderatori, si prega di non ricaricare per altri siti di hosting di nuovo in modo che la codifica/qualità non cambia)

+0

Il caricamento dell'immagine (da un file locale) utilizzando 'ImageIO' in un semplice programma java richiede circa 1-2 secondi sul mio PC - da dove viene l'immagine? quando lo fai? – BretC

+3

@Bret Non mi sono reso conto che StackOverflow probabilmente ha ridimensionato e/o ricodificato quell'immagine durante il caricamento sul loro server. Questa è l'immagine originale: https://dl.dropboxusercontent.com/u/73774/slow2.jpg (domanda originale modificata con questo collegamento) – the21st

+0

Hai provato a utilizzare 'InputStreamReader' e/o' BufferedReader'? –

risposta

6

Ok, ecco il mio scoperte finora (e ad essere onesti, sono un po 'preoccupanti ...).

Utilizzando il plugin standard JPEG per ImageIO in bundle con l'Oracle JRE:

BufferedImage image = ImageIO.read(file); 

legge l'immagine in circa 18 secondi sul mio computer (un MacBookPro/2.8GHz i7).

Usando il mio JPEG plugin for ImageIO, che utilizza un percorso di codice leggermente diverso (per esempio, probabilmente si può ottenere gli stessi risultati per ottenere il ImageReader e invocando il metodo readRaster(), quindi la creazione di un BufferedImage da questo. Il codice non è banale, in modo da consultare la pagina del progetto tho se volete vedere il codice):

BufferedImage image = ImageIO.read(file); 

legge l'immagine in circa 8 secondi sul mio computer.

Utilizzando la mia classe BufferedImageFactory e AWT Toolkit:

BufferedImage image = new BufferedImageFactory(Toolkit.getDefaultToolkit().createImage(file.getAbsolutePat‌​h())).getBufferedImage(); 

legge l'immagine in ~ 2,5 secondi sul mio computer.

Utilizzando il deprecato JPEGImageDecoder classe da sun.awt.codec:

BufferedImage image = new JPEGImageDecoderImpl(new FileInputStream(file)).decodeAsBufferedImage(); 

legge l'immagine in ~ 1,7 secondi sul mio computer.

Quindi, questo significa che dovremmo essere in grado di leggere questa immagine in meno di 2 secondi, anche in Java. Le prestazioni dello JPEGImageReader sono ridicole in questo caso e mi piace davvero sapere perché. Come già accennato, sembra essere necessario con la progressiva decodifica, ma comunque, dovrebbe essere migliore di questo.

Aggiornamento:

Solo per il gusto di farlo, ho creato un rapido PoC ImageReader plug sostenuto dalla LibJPEG-Turbo Java API. Non è molto sofisticato ma, ma consente il codice come:

BufferedImage image = ImageIO.read(file); 

Per leggere l'immagine in < 1,5 secondi sul mio computer.

PS: I used to maintain ImageIO wrappers for JMagick (simile al codice menzionato da @Jordan Doyle, ma consente di programmare contro l'API ImageIO), tuttavia ho smesso poiché era troppo lavoro. Forse dovrò riconsiderare ... Almeno vale la pena di dare un'occhiata anche alla sua soluzione, se non ti interessa affidarti all'installazione di codice JNI/nativo.

+0

C'è un modo per modificare l'approccio 'BufferedImageFactory' in modo che possa decodificare anche i jpeg CMYK? – the21st

+0

@ the21st Siamo spiacenti, sembra che il metodo 'Toolkit.createImage' non supporti CMYK, quindi non funzionerà (e non c'è davvero molto che possiamo fare per risolvere il problema). Tuttavia, il mio plug-in originale e la versione LibJPEG-Turbo funzionano. ;-) – haraldK

2

Una veloce alternativa al ImageIO è ImageMagick, ci sono vari wrapper per interfacciamento ImageMagick tramite Java come JMagick

Per ottenere un BufferedImage da JMagick è innanzitutto necessario ottenere un'istanza di MagickImage che può essere fatto in questo modo:

ImageInfo info = new ImageInfo(pathToImage); 
MagickImage image = new MagickImage(info); 

Ora è possibile utilizzare il metodo fornito dai nostri molto proprio Jacob Nordfalk 8 anni fa per leggere l'immagine in un BufferedImage here

public static BufferedImage magickImageToBufferedImage(MagickImage magickImage) throws Exception 
{ 
    Dimension dim = magickImage.getDimension(); 
    int size = dim.width * dim.height; 
    byte[] pixels = new byte[size * 3]; 

    magickImage.dispatchImage(0, 0, dim.width, dim.height, "RGB", pixels); 

    BufferedImage bimage = createInterleavedRGBImage(dim.width, dim.height, pixels); 

    ColorModel cm = bimage.getColorModel(); 
    Raster raster = bimage.getData(); 
    WritableRaster writableRaster = null; 

    writableRaster = (raster instanceof WritableRaster) ? (WritableRaster) raster : raster.createCompatibleWritableRaster(); 

    BufferedImage bufferedImage = new BufferedImage(cm, writableRaster, false, null); 

    return bufferedImage; 
} 

Poi il metodo createInterleavedRGBImage:

public static BufferedImage createInterleavedRGBImage(int imageWidth, int imageHeight, byte data[]) 
{ 
    int[] numBits = new int[3]; 
    int[] bandoffsets = new int[3]; 

    for (int i = 0; i < 3; i++) { 
     numBits[i] = 8; 
     bandoffsets[i] = i; 
    } 

    ComponentColorModel ccm = new ComponentColorModel(
     ColorSpace.getInstance(ColorSpace.CS_sRGB), 
     numBits, 
     false, 
     false, //Alpha pre-multiplied 
     Transparency.OPAQUE, 
     DataBuffer.TYPE_BYTE 
    ); 

    PixelInterleavedSampleModel csm = new PixelInterleavedSampleModel(
     DataBuffer.TYPE_BYTE, 
     imageWidth, 
     imageHeight, 
     3, //Pixel stride 
     imageWidth * 3, // Scanline stride 
     bandoffsets 
    ); 

    DataBuffer dataBuf = new DataBufferByte(data, imageWidth * imageHeight * 3); 
    WritableRaster wr = Raster.createWritableRaster(csm, dataBuf, new Point(0, 0)); 
    return new BufferedImage(ccm, wr, false, null); 
}