2009-10-26 20 views
14

Ich habe eine Open-Source-App, die Fotos auf Facebook hochlädt. Um Bandbreite zu sparen, werden die Fotos vor dem Hochladen automatisch angepasst (Facebook legt eine maximale Größenbeschränkung fest). Ein paar Leute haben sich über die Fotoqualität beschwert, und tatsächlich können Sie den Unterschied sehen (siehe Demo-Bilder unter this issue).Bildgröße (Java)

Also meine Frage ist, was ist die "beste" Möglichkeit, Bilder (d. H. Fotos) in Java herabzusetzen, ohne Qualität zu verlieren, oder zumindest mit minimalem Qualitätsverlust/Artefakten?

Sie können den aktuellen Code ich haben here (Größe ändern über this page).

Antwort

12

Ich habe alles ausprobiert - einschließlich der Tricks here, und alles, was ich sagen kann, dass Sie besser ImageMagick mit welcher Schnittstelle verwenden, sind Javas Imaging-Bibliotheken einfach nicht zu schnupfen, wenn es darum geht. Sie müssen so viele Formate und Algorithmen unterstützen, um es richtig zu machen.

+0

Ich hatte gehofft, einen externen Anruf zu vermeiden, aber ich denke, dass Sie Recht haben können. Java scheint noch nicht ganz richtig Bildverarbeitung zu haben! –

+1

Das ist auch meine Schlussfolgerung! Java-Bild apis nur saugen! Ich kann die Trägheit umgehen, aber die schlechte Bildqualität ist einfach nicht akzeptabel! Ich hoffe, dass die relevanten Apis zusammen mit JavaFX 2.0 ein dringend benötigtes TLC bekommen. – Kimble

+1

Sie sind offensichtlich nicht auf [java-image-scaling] (http://code.google.com/p/java-image-scaling/) gestoßen. –

2

Welchen Rendering-Tipp verwenden Sie? Normalerweise ist das bikubische Resampling das Beste. Auf den Fotos, auf die Sie verlinken, sind sie sehr ungleichmäßig, was mich denken lässt, dass Sie den nächsten Nachbarn als Hinweis verwenden.

In der Klasse PictureScaler, zu der Sie eine Verknüpfung herstellen, werden in der Methode paintComponent sechs verschiedene Mittel zur Größenänderung des Bilds verwendet. Haben Sie alle sechs versucht, um zu sehen, welches das beste Ergebnis gibt?

+0

Ich würde hinzufügen, dass es wie im Code aussieht, dass der Fragesteller einen "hochwertigen" Modus hat, der die Größe des Bildes in mehreren Durchgängen einbezieht. Dies scheint aus dem gleichen Grund eine schlechte Idee zu sein, dass Sie ein Soundsample nicht wiederholt komprimieren und dekomprimieren möchten; die Bildqualität wird tatsächlich schlechter und nicht besser. – jprete

+0

Hallo, eigentlich habe ich nicht alle sechs ausprobiert, die ich wahrscheinlich sollte. Obwohl ich bikubisches Resampling verwende, das, wie Sie sagen, "sollte" das beste sein. Ich verwende auch nicht den "High Quality" -Modus, da es in dieser Situation nicht funktioniert, scheint die Java-Imaging-API anfällig für Deadlocks zu sein! Wahrscheinlich ist etwas falsch mit meinem Code, nicht sicher, was aber. –

0

Nach ein paar frustrierenden Experimenten fand ich die folgenden resize evaluation und verwendete den Multi-Pass-Ansatz in meinem Projekt.

zu tun, dass ich die getScaledInstance() -Methode in meine Thumbnail-Generator-Klasse kopiert, verändern mein Bild Ansatz lesen ImageIO zu verwenden (das man gibt eine BufferedImage) und bin jetzt sehr glücklich !

Ich habe das Ergebnis mit einer Größenanpassung in Photoshop CS3 verglichen und das Ergebnis ist sehr ähnlich.

16

Phil, ich weiß nicht, welche Lösung letztendlich Sie ging mit, aber Skalieren von Bildern in Java kann ziemlich gut aussehen, wenn Sie:

  • Vermeiden BufferedImage Typen, die vom JDK nicht gut unterstützt werden.
  • Verwenden inkrementelle
  • Stick-Skalierung auf Bikubisch wenn inkrementelle mit Skalierung

ich einen fairen Anteil an Tests mit diesen Methoden und die inkrementelle Skalierung zusammen mit Festhalten an gut unterstützten Bildtypen getan haben, ist der Schlüssel - - Ich sehe, dass Alexander erwähnt hat, dass er immer noch kein Glück damit hat, was ein Mist ist.

Ich veröffentlichte die imgscalr library (Apache 2) vor etwa 6 Monaten, um das Problem von "Ich möchte gut aussehende skalierte Kopien dieses Bildes, DO IT NOW!" nach dem Lesen von etwa 10 Fragen wie diese auf SO.

Standardverwendung wie folgt aussieht:

BufferedImage img = ImageIO.read(...); // load image 
BufferedImage scaledImg = Scalr.resize(img, 640); 

Das zweite Argument der Begrenzungsbreite und -höhe ist imgscalr das Bild skaliert verwenden - seine Proportionen zu halten richtig, auch wenn Sie in einem ungültigen Dimensionen bestanden - es gibt viele more detailed methods, aber das ist die einfachste Verwendung.wenn Facebook begrenzte Bilder auf 800x600 Pixel

Der Use-Case Sie würden zum Beispiel wollen, würde wie folgt aussehen:

BufferedImage img = ImageIO.read(...); // load image 
BufferedImage scaledImg = Scalr.resize(img, Method.QUALITY, 800, 600); 

Das wird dafür sorgen, das Bild bleibt im besten unterstützten Bildtyp und mit der skalierte höchste Qualitätsmethode, die Java aufbringen kann.

In meinem eigenen hochauflösenden Test habe ich keine klaffenden Diskrepanzen mit skalierten Bildern mit dieser Bibliothek/diesen Methoden bemerkt, AUSSER wenn Ihr Bild vom ImageIO Loader in einen schlecht unterstützten Bildtyp versetzt wird - zum Beispiel passiert dies viel mit GIFs. Wenn du sie so belässt und sie nicht aus diesen schlecht unterstützten Typen herausholst, sieht es am Ende aus, als ob sie wirklich gechoppt und schrecklich wären. Der Grund dafür ist, dass das Java2D-Team tatsächlich über verschiedene hardwarebeschleunigte Pipelines für alle Arten von BufferedImages verfügt, die das JDK verarbeiten kann - eine Untergruppe der weniger gebräuchlichen Bildtypen verwendet dieselbe Software Rendering-Pipeline unter den Abdeckungen in Java2D, was zu schlechten und manchmal völlig inkorrekt aussehenden Bildern führt. Das war so ein PIA zu erklären und zu versuchen herauszufinden, dass ich diese Logik direkt in die Bibliothek geschrieben habe.

Die beiden besten unterstützten Typen sind BufferedImage.TYPE_INT_RGB und _ARGB wenn Sie neugierig sind.

+0

Hallo Riyad, Vielen Dank für Ihre Bemühungen in diesem zu bauen. Es ist großartig und ich habe es gerade erst benutzt. Ich habe ein Problem mit der Qualität, wenn ich versuche, ein Bild nur auf eine bestimmte Breite und Höhe zu ändern. Ich habe ein Bild, das 240X320 und ich muss es auf 50X75 und 120X180 Größe ändern. Ich habe beide mit folgendem Code ausprobiert (ich benutze imgscalr 4.2). – WowBow

+1

image = Größe ändern (Bild, Methode.ULTRA_QUALITY, 50,75, OP_ANTIALIAS, OP_BRIGHTER); saveImage (Bild, ImageFormatTypes.JPEG, DESTINATION + RESIZED_IMAGE + "." + ImageFormatTypes.JPEG); Ich habe zwei Probleme. # 1 ist eine sehr geringe Qualität im Vergleich zu anderen internen Tools, die ich verwendet habe, um das Bild manuell zu skalieren. # 2. Die Breite steigt von 50 auf 56 und von 120 auf 135, was komisch ist. Haben Sie eine Idee, warum diese passieren? Ich schätze Ihre Hilfe. – WowBow

+2

@WowBow Ah! Ja, ich glaube, ich weiß, was passiert ... Ich hasse es, dass imgscalr das vom Entwickler noch nicht abstrahiert, aber es arbeitet mit rohen Pixelwerten - wenn du das Bild mit ImageIO zurückschreibst, benutzt es DEFAULT JPG-Encoder-Einstellungen, die auf 75% Qualität eingestellt sind, glaube ich - Sie müssen google "Java ImageIO JPEG-Qualität" zum Beispiel Code, wie man das sagen 95%. Der "billige und einfache" Weg, um zu testen, ob dies der Fall ist, schreibe das Bild als PNG (lossless Format) aus - wenn es "viel besser" aussieht, dann ist es das. –

5

Um das Bild mit benutzerdefinierter Qualität zu skalieren, verwenden Sie thumbnailata.jar.

Beispielcode http://code.google.com/p/thumbnailator/wiki/Examples

+1

Getestet sowohl org.imgscalr.Scalr und com.mortennobel.imagescaling in Kombination mit der Änderung der Komprimierungsstufe (http://blog.carsoncheng.ca/2011/02/how-to-change-jpeg-compression-in-java.html) und keiner arbeitete. thumbnailator war genial, zumindest qualitativ hochwertig. Vielen Dank! – daker

0

Ich wollte höchste Qualität erhalten mit einem Seitenverhältnis ändern. Ich habe einige Dinge versucht und mehrere Einträge gelesen. Verlor zwei Tage und am Ende bekam ich das beste Ergebnis mit einfachen Java-Methode (auch versucht, ImageMagick und java-Bild-Skalierung Bibliotheken):

public static boolean resizeUsingJavaAlgo(String source, File dest, int width, int height) throws IOException { 
    BufferedImage sourceImage = ImageIO.read(new FileInputStream(source)); 
    double ratio = (double) sourceImage.getWidth()/sourceImage.getHeight(); 
    if (width < 1) { 
    width = (int) (height * ratio + 0.4); 
    } else if (height < 1) { 
    height = (int) (width /ratio + 0.4); 
    } 

    Image scaled = sourceImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING); 
    BufferedImage bufferedScaled = new BufferedImage(scaled.getWidth(null), scaled.getHeight(null), BufferedImage.TYPE_INT_RGB); 
    Graphics2D g2d = bufferedScaled.createGraphics(); 
    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); 
    g2d.drawImage(scaled, 0, 0, width, height, null); 
    dest.createNewFile(); 
    writeJpeg(bufferedScaled, dest.getCanonicalPath(), 1.0f); 
    return true; 
} 


/** 
* Write a JPEG file setting the compression quality. 
* 
* @param image a BufferedImage to be saved 
* @param destFile destination file (absolute or relative path) 
* @param quality a float between 0 and 1, where 1 means uncompressed. 
* @throws IOException in case of problems writing the file 
*/ 
private static void writeJpeg(BufferedImage image, String destFile, float quality) 
     throws IOException { 
    ImageWriter writer = null; 
    FileImageOutputStream output = null; 
    try { 
    writer = ImageIO.getImageWritersByFormatName("jpeg").next(); 
    ImageWriteParam param = writer.getDefaultWriteParam(); 
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 
    param.setCompressionQuality(quality); 
    output = new FileImageOutputStream(new File(destFile)); 
    writer.setOutput(output); 
    IIOImage iioImage = new IIOImage(image, null, null); 
    writer.write(null, iioImage, param); 
    } catch (IOException ex) { 
    throw ex; 
    } finally { 
    if (writer != null) { 
     writer.dispose(); 
    } 
    if (output != null) { 
     output.close(); 
    } 
    } 
} 
5

Die beiden beliebtesten Open-Source-Bibliotheken in Bild, spezialisiert auf Java-Größenänderung zur Zeit sind:

Additonal ist es die Art und Weise JDK mit Java Graphics2D (see this question on how to do it), die berüchtigt ist, bad results especially with downscaling zu erstellen. Es gibt auch eine Java interface to ImageMagick, die hier weggelassen wird, weil sie ein externes Werkzeug benötigt.

visuelle Qualität

Hier ist ein Vergleich der Ergebnisse der Größe ändern/Downscaling eine 580x852 png zu 145x213. Als Referenz wird Photoshop CS5 "save for web" verwendet. Hinweis: Die Ergebnisse sind 1: 1 was die erstellten libs nur zusammen kopiert haben. Der Zoom verwendet keine Filterung, nur einen einfachen Algorithmus für den nächsten Nachbarn.Here you can find the original image.

comparison

  1. Thumbnailator 0,4.8 mit den Standardeinstellungen, keine Dimension Anpassungen
  2. Photoshop CS5 mit bicubic Algorithmus
  3. imgscalr 4.2 mit ULTRA_QUALITY Einstellung, keine Dimension Anpassungen
  4. Graphics2D (Java 8) mit rendern Hinweise VALUE_INTERPOLATION_BICUBIC, VALUE_RENDER_QUALITY, VALUE_ANTIALIAS_ON

Ich überlasse es dem Leser, das beste Ergebnis zu wählen, da dies subjektiv ist. Im Allgemeinen haben alle eine gute Ausgabe außer Graphics2D. Der Thumbnailator erzeugt schärfere Bilder, die der Photoshop-Ausgabe sehr ähnlich sind, während die Ausgabe von imgscalr wesentlich weicher ist. Für Icons/Text usw. möchten Sie eine schärfere Ausgabe, für Bilder können Sie eine weichere Ausgabe wünschen.

Computational Zeit

Hier wird nicht-wissenschaftliche Benchmark dieses tool und 114 Bilder mit Dimension von etwa 96x96 bis zu 2560x1440 mit der Behandlung als 425% Erstellen von Bildern: 100%, 150%, 200%, 300% und 400% skalierte Versionen davon (also 114 * 5 Skalierungsoperationen). Alle libs verwenden die gleichen Einstellungen wie im Qualitätsvergleich (also höchste Qualität möglich). Zeiten skalieren nicht nur den ganzen Prozess. Fertig auf einem i5-2520M mit 8GB Ram und 5 Läufen.

  • Thumbnailator: 7003.0ms | 6581.3 ms | 6019,1 ms | 6375.3ms | 8700.3ms
  • imgscalr: 25218.5ms | 25786.6 ms | 25095.7ms | 25790.4 ms | 29296.3ms
  • Grafik2D: 7387.6ms | 7177.0ms | 7048,2 ms | 7132.3ms | 7510.3ms

Here is the code used in this benchmark.

Interessanter Thumbnailator ist auch die schnellste mit einer durchschnittlichen Zeit von 6,9 sec von Java2D gefolgt mit 7,2 secimgscalr hinten mit einem schlechten 26,2 sec verlassen. Dies ist wahrscheinlich nicht fair, da imgscalr auf ULTRA_QUALITY gesetzt wird, was extrem teuer erscheint; mit der QUALITY Einstellung beträgt der Durchschnitt 11.1 Sekunden.

+0

Die Leistungsmessungen sind schwierig (ich schrieb einige Worte dazu in [dieser Antwort] (http://stackoverflow.com/a/32278737/3182664)). Trotzdem, 1 für diese und die andere Antwort – Marco13

+0

@ Marco13 wahr, aber ich denke, eine hatte eine große und divarese genug Stichprobengröße, um zumindest Trends zu fangen. – for3st

+0

Ich habe die Tests selbst durchgeführt und ähnliche Muster beobachtet wie Sie (Thumbnailator lieferte die schnellste Leistung). Ich empfehle, die Zeile "imgscalr" zweimal zu schreiben (einmal für ULTRA_QUALITY und einmal für QUALITY), damit die Benchmarks auf einen Blick leichter vergleichbar sind. –