# Jpeg performant skalieren



## UlfL (20. Mrz 2009)

Hallo zusammen,

suche seit längerem eine Möglichkeit, grosse Jpeg-Dateien (5-8MB) performant zu skalieren. Ohne Interpolation klappt es mit Graphics2D.drawImage() ganz gut, aber mit Interpolation dauert es zwischen zwei und vier Minuten. So sieht mein Code aus:

[highlight=Java]
    public BufferedImage createScaledBufferedImage(BufferedImage originalImage, String type, boolean useInterpolation) 
    {
// ---- Anfang Ausschnitt 
BufferedImage bi;
            Graphics2D g2;
            bi = createCompatibleImage(thumbnailWidth,thumbnailHeight,BufferedImage.TYPE_INT_RGB);
            g2 = bi.createGraphics();
            if(useInterpolation)
            {
                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            }

            g2.drawImage(originalImage, 0, 0, thumbnailWidth, thumbnailHeight, null);
            g2.dispose();
return bi;
// ---- Ende Ausschnitt
}

    private BufferedImage createCompatibleImage(int width, int height, int type) {    
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
        BufferedImage image = gc.createCompatibleImage(width, height, type);        
        return image;
    }
[/highlight]

Wenn useInterpolation == false, dann geht das Ganze ziemlich fix, aber mit den RenderingHints.VALUE_INTERPOLATION_BILINEAR wird das Warten zur Geduldsprobe. Mein Entwicklunsrechner: WinXP, Centrino Duo 1.83 GHz, 2 GB RAM.
Hat jemand eine Idee, wie dies performanter gemacht werden könnte? Habe die Hardware-Beschleuniging über System.setProperty("sun.java2d.ddscale", "true"); eingeschaltet, ohne spürbare Verbesserungen. Oder ist drawImage() einfach so langsam? Zugegeben, die Files sind ziemlich gross...
Wenn es hier keinen besseren Weg gibt, muss ich vielleicht doch auf native Bibliotheken, wie z.B. ImageMagick, zurückgreifen.

Viele Grüsse,
Ulf


----------



## Marco13 (20. Mrz 2009)

Hmja... abgesehen von diesem Link java.net: The Perils of Image.getScaledInstance() kann ich zwar keine konkreten Tipps geben, aber ... ein 5MB JPG-Bild kann eine Auflösung von 2000x2000 haben, oder eine von 50000x50000 ... Schonmal GROB geschaut, wie lange ein natives, spezialisiertes Programm (von IrfanView bis Photoshop...) für so eine Skalierungsaktion braucht?


----------



## 0x7F800000 (20. Mrz 2009)

UlfL hat gesagt.:


> grosse Jpeg-Dateien (5-8MB)
> ...
> thumbnail
> ...
> RenderingHints.VALUE_INTERPOLATION_BILINEAR


Aus den ersten beiden stichwörtern schließe ich, dass du das Bild _verkleinern _willst.
Da ist der Bilineare Filter absolut unbrauchbar: was soll denn der filter machen? Wenn du zB. in einer reihe hundert pixeln uberspringst, macht es die sache nicht besser, wenn du dann zwischen dem 101 und dem 102 pixel linear interpolierst: du kriegst trotzdem nur verrauschten schrott raus, da kannst du genausogut die koordinaten abrunden und einfach den nächstbesten pixel nehmen, also einfach ohne filter skalieren. 


> Wenn useInterpolation == false, dann geht das Ganze ziemlich fix, aber mit den RenderingHints.VALUE_INTERPOLATION_BILINEAR wird das Warten zur Geduldsprobe.


Nja, lohnt es sich denn? Aus dem oben geschriebenen Grund müsste imho beides ziemlich verrauscht aussehen.
Da müsste man eigentlich mit einem anderen Filter dran, anisotropische wäre natürlich toll, aber ich habe keinen soclhe rendering hint gefunden ???:L Vielleicht muss man einfach antialiasing einschalten, und das bild auf die kleinere Fläche zeichnen, da sollte er auch für jeden Bildpunkt den Mittelwert der drumherum liegenden Pixel ausrechnen: das wäre ja schon genau das was du brauchst.


> Mein Entwicklunsrechner: WinXP, Centrino Duo 1.83 GHz, 2 GB RAM.


Ich weiß es nicht genau, aber das Bild wird wahrscheinlich rein Softwaremäßig skaliert. Und womöglich nur auf der einen Hirnhälfte... Hast du schonmal versucht, das bild einfach in der Mitte aufzuteilen und beide Teile in zwei parallelen Threads zu skalieren? Glaube zwar kaum, dass das was bringen würde, aber man kann's ja mal versuchen, dauert ja nicht lange. 
Vor dem Versuch aber bitte STRG+Alt+Entf -> Taskmanager -> Leistung guggen, wenn da [100%,3%] statt [99%,100%] angezeigt wird, könnte man es mit 2 threads versuchen.



> Habe die Hardware-Beschleuniging über System.setProperty("sun.java2d.ddscale", "true"); eingeschaltet, ohne spürbare Verbesserungen.


Achso... Hardwarebeschleunigt... Hmm... Wer weiß, wenn du so ein reines Business-laptop mit einer spartanischen Grafikkarte hast, dann bist du womöglich mit dem hauptprozessorr besser dran (das sind aber echt nur noch mutmaßungen^^)


----------



## UlfL (20. Mrz 2009)

Hi Marco, und danke für dein Antwort. Der von dir genannten Artikel habe ich schon gelesen, und entsprechend ein paar Anpassungen gemacht, ohne das es viel geändert hat. 
Die meisten Jpegs haben eine Auflösung um die 4000x3000 Pixel. Photoshop braucht für dieselbe Aktion etwa 2 Sekunden im Batchmodus, wo die Datei auch von der Platte gelesen, gespeichert und geschlossen wird...


----------



## UlfL (20. Mrz 2009)

0x7F800000 hat gesagt.:


> Aus den ersten beiden stichwörtern schließe ich, dass du das Bild _verkleinern _willst.
> Da ist der Bilineare Filter absolut unbrauchbar: was soll denn der filter machen? da kannst du genausogut die koordinaten abrunden und einfach den nächstbesten pixel nehmen, also einfach ohne filter skalieren.
> Da müsste man eigentlich mit einem anderen Filter dran, anisotropische wäre natürlich toll, aber ich habe keinen soclhe rendering hint gefunden ???:L Vielleicht muss man einfach antialiasing einschalten,


Du hast Recht, ich möchte verkleinern. Das bilineare Filter erzeugt schon ein glatteres Bild als ohne Filter oder nur Antialias (bei den beiden letzteren sehe ich kein Unterschied), wobei der Unterschied nicht wirklich gross ist. Siehe Anhänge. Vielleicht hast du Recht, womöglich brauche ich einfach eine niedrigere Jpeg-Kompression, um die Kompressionsartefakte zu reduzieren. 


0x7F800000 hat gesagt.:


> Achso... Hardwarebeschleunigt... Hmm... Wer weiß, wenn du so ein reines Business-laptop mit einer spartanischen Grafikkarte hast, dann bist du womöglich mit dem hauptprozessorr besser dran (das sind aber echt nur noch mutmaßungen^^)


Habe eine ATI Radeon X1600 256MB.
Bild 1: Antialias. Bild 2: Bilinear. Bild 3: Ohne Filter


----------



## EgonOlsen (20. Mrz 2009)

Ich hatte hier mal ein wenig Code gepostet: http://www.java-forum.org/allgemeine-java-themen/47158-verkleinerung-von-bildern.html

...vielleicht hilft das was...


----------



## UlfL (24. Mrz 2009)

Hi zusammen,

habe jetzt ein wenig mit JMagic/ImageMagick experimentiert, und bin von den Ergebnissen überzeugt. Der Skalierungsalgorithmus von ImageMagick muss sich nicht hinter der von Photoshop verstecken (siehe angehängte Bilder) und die Geschwindigkeit lässt nichts zu wünschen übrig. 
Ist natürlich schade, das ganze nicht in "pure Java" machen zu können, aber ich finde das schon etwas enttäuschend, wie schlecht drawImage() mit "handelsübliche" Digitalkameraphotos umgeht, sobald man ein bilineares (oder gar bikubisches) Filter hinzufügt.

//Ulf

Bild 1: Photoshop, Bild 2: Imagemagick


----------



## Hansdampf (25. Mrz 2009)

du könntest die Bilder erst ohne Filter etwas kleiner skalieren, dann im zweiten Durchgang MIT Filter weiter verkleinern.
Etwa so:
5000x5000 -> 1000x1000 ohne Filter
1000x1000 -> 200x200 mit Filter

Damit wird das ganze mindestens 10x so schnell und liefert die gleiche Qualität.


----------



## UlfL (26. Mrz 2009)

Hansdampf hat gesagt.:


> Damit wird das ganze mindestens 10x so schnell und liefert die gleiche Qualität.


Danke, Hans, ein guter Vorschlag. Werde 2 Durchläufe probieren, und sehen wie performant das ist.


----------



## tuxedo (2. Apr 2009)

Was kam denn dabei raus? Interessiert mich ...

- Alex


----------



## UlfL (2. Apr 2009)

Sorry, habe zuviel anderes um die Ohren, bin noch nicht dazu gekommen, Hans' Vorschlag auszuprobieren. Melde mich sobald ein Ergebnis vorliegt.

//Ulf


----------



## 0x7F800000 (2. Apr 2009)

könnte feine horizontale bzw. vertikale Strukturen verschlucken... Wenn da jemand ein Photo eines Spinnennetzes hochlädt, könnte es bei diesem verfahren dazu kommen, dass nur die schrägen striche zu sehen sind, und das Netz aus vier Segmenten zu bestehen scheint... Pixel vielleicht nicht zeilen bzw Spaltenweise weglassen, sondern irgendwie ein wenig verstreut


----------



## UlfL (2. Apr 2009)

Interessanter Hinweis. Werde ein feines Raster photographieren, und das als Versuchsbild verwenden.


----------



## eskimo328 (29. Apr 2010)

ist zwar schon ein weilchen her der letzte beitrag aber egal.

Der Kommentar von *mrsteve* (_Submitted On 10-MAR-2004_) unter folgendem Link: Bug ID: 4705399 drawImage can be extremely slow for drawing scaled instances hat mir bei einem ähnlichen Problem geholfen

Hier die wichtige Stelle aus dem Link:



> After extensive research into this problem I've found the
> evaluation correct.  A perfect workaround is to force the
> reader to read in the normal color space.  See below.
> This is 20x quicker for a thumbnail generating
> application than using ImageIO.read().



und hier der Code dazu:


```
public static BufferedImage readImage(Object source) throws IOException {
        ImageInputStream stream = ImageIO.createImageInputStream(source);
        ImageReader reader = (ImageReader) ImageIO.getImageReaders(stream).next();
        reader.setInput(stream);
        ImageReadParam param = reader.getDefaultReadParam();
        
        ImageTypeSpecifier typeToUse = null;

        for (Iterator i = reader.getImageTypes(0); i.hasNext(); ) {
            ImageTypeSpecifier type = (ImageTypeSpecifier) i.next();

            if (type.getColorModel().getColorSpace().isCS_sRGB()) 
                typeToUse = type;
        }

        if (typeToUse!=null) 
            param.setDestinationType(typeToUse);
            
        BufferedImage b = reader.read(0, param);
        
        reader.dispose();
        stream.close();
        return b;
    }
```


----------



## UlfL (11. Mai 2010)

Hi Eskimo,

und vielen Dank für dein Post, es hat tatsächlich die Probleme behoben. Auch große Dateien werden performant und in guter Qualität skaliert.

Unglaublich das es hierzu kein Best-Practices von Sun gibt. Man stochert ja buchstäblich im Nebel bei diesen vielen Klassen und Möglichkeiten in Java2d.

Viele Grüße,
Ulf


----------

