2015-04-17 9 views
11

Wenn ich javax.imageio.ImageIO verwende, um ein JPEG mit hoher Auflösung (9000x9000) von der Festplatte zu laden, dauert es mehr als 1 Minute in meiner scala-Anwendung. Ich habe versucht, ein Java-Projekt zu erstellen, aber es dauert immer noch zu lange - ungefähr 30 Sekunden.Das Lesen eines progressiv codierten 9000x9000 JPEG in Java dauert 1 Minute

Dies ist, wie ich das Bild laden:

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

Gibt es eine Möglichkeit zur Verbesserung der Leistung auf progressiv codiert groß res JPEGs in Java zu lesen? (Reupload nicht wieder zu anderen Hosting-Website Moderatoren, bitte so, dass die Kodierung/Qualität ändert sich nicht)

Das fragliche Bild ist this one

+0

Das Laden dieses Bildes (aus einer lokalen Datei) mit 'ImageIO' in einem einfachen Java-Programm dauert etwa 1-2 Sekunden auf meinem PC - woher bekommen Sie das Bild? Wenn du es tust? – BretC

+3

@Bret Ich wusste nicht, dass StackOverflow dieses Bild während des Uploads auf ihren Server wahrscheinlich skaliert und/oder neu codiert hat. Dies ist das Originalbild: https://dl.dropboxusercontent.com/u/73774/slow2.jpg (bearbeitete Originalfrage mit diesem Link) – the21st

+0

Haben Sie versucht, 'InputStreamReader' und/oder' BufferedReader' zu verwenden? –

Antwort

6

Ok, hier sind meine Erkenntnisse so weit (und um ehrlich zu sein, sie sind ein wenig beunruhigend ...).

Mit dem Standard JPEG-Plugin für ImageIO mit dem Oracle JRE gebündelt:

BufferedImage image = ImageIO.read(file); 

Liest das Bild in etwa 18 Sekunden auf meinem Computer (ein MacBookPro/2,8 GHz i7).

meinen JPEG plugin for ImageIO verwenden, die einen etwas anderen Codepfad verwendet (dh, können Sie wahrscheinlich die gleichen Ergebnisse erhalten, indem die ImageReader zu erhalten und die readRaster() Methode aufgerufen, dann ein BufferedImage aus, dass zu schaffen. Der Code ist nicht trivial, so bitte tho Projektseite verweisen, wenn Sie den Code sehen möchte):

BufferedImage image = ImageIO.read(file); 

Liest das Bild in etwa 8 Sekunden auf meinem Computer.

Mit meiner BufferedImageFactory Klasse und die AWT Toolkit:

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

Liest das Bild in ~ 2,5 Sekunden auf meinem Computer.

Verwendung der veralteten JPEGImageDecoder Klasse von sun.awt.codec:

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

Liest das Bild in ~ 1,7 Sekunden auf meinem Computer.

Dies bedeutet, dass wir dieses Bild in weniger als 2 Sekunden lesen können, sogar in Java. Die Performance von der JPEGImageReader ist in diesem Fall einfach lächerlich, und ich möchte wirklich wissen warum. Wie bereits erwähnt, scheint es mit der progressiven Decodierung zu tun zu haben, aber trotzdem sollte es besser sein.

Update:

Gerade für den Spaß von ihm, habe ich eine schnelle PoC ImageReader Plugin von den LibJPEG-Turbo Java API gesichert. Es ist nicht sehr anspruchsvoll und doch, aber es ermöglicht Code wie:

BufferedImage image = ImageIO.read(file); 

Um das Bild in < 1,5 Sekunden auf meinem Computer zu lesen.

PS: I used to maintain ImageIO wrappers for JMagick (ähnlich dem von @ Jordan Doyle genannten Code, aber es würde Ihnen erlauben, gegen die ImageIO API zu programmieren), aber ich hörte auf, wie es zu viel Arbeit war. Vielleicht muss ich es noch einmal überdenken ... Zumindest lohnt es sich, auch seine Lösung zu überprüfen, wenn es Ihnen nichts ausmacht, sich auf JNI/native Code-Installation zu verlassen.

+0

Gibt es eine Möglichkeit, Ihre 'BufferedImageFactory'-Methode zu ändern, so dass sie auch CMYK-JPEGs dekodieren kann? – the21st

+0

@ the21st Sorry, es scheint, dass die 'Toolkit.createImage' Methode CMYK nicht unterstützt, also wird es nicht funktionieren (und es gibt wirklich nicht viel, was wir tun können, um es zu reparieren). Sowohl mein Original-Plugin als auch die LibJPEG-Turbo-Version tun dies allerdings. ;-) – haraldK

2

Eine schnellere Alternative zu ImageIO ist ImageMagick, gibt es verschiedene Wickel Schnittstelle ImageMagick über Java wie JMagick

Um einen BufferedImage von JMagick erhalten Sie zuerst eine Instanz von MagickImage bekommen müssen, die wie so getan werden kann:

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

Jetzt können Sie die Methode, mit unseren eigenen Jacob Nordfalk 8 Jahren zur Verfügung gestellt nutzen vor dem Bild in ein 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; 
} 

dann die createInterleavedRGBImage Methode zu lesen:

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); 
}