Frage

Ich habe eine Open-Source-App, die Fotos auf Facebook hochlädt.Um Bandbreite zu sparen, wird die Größe der Fotos vor dem Hochladen automatisch geändert (Facebook legt eine maximale Größenbeschränkung fest).Einige Leute haben sich über die Fotoqualität beschwert, und tatsächlich kann man den Unterschied sehen (siehe dieses Problem für einige Demobilder).

Meine Frage ist also: Was ist die „beste“ Möglichkeit, Bilder zu verkleinern (d. h.Fotos) in Java ohne Qualitätsverlust oder zumindest mit minimalem Qualitätsverlust/Artefakten?

Sie können den aktuellen Code sehen, den ich habe Hier (Größencode ändern über diese Seite).

War es hilfreich?

Lösung

Ich habe alles ausprobiert - einschließlich der Tricks hier, Und alles, was ich sagen kann, dass Sie im ImageMagick mit jeder Schnittstelle besser verwenden können, sind Javas Imaging -Bibliotheken einfach nicht so, dass es dazu ist, wenn es darum geht. Sie müssen so viele Formate und Algorithmen unterstützen, um es richtig zu machen.

Andere Tipps

Phil, ich weiß nicht, mit welcher Lösung Sie schließlich gegangen sind, aber die Skalierung von Bildern in Java können ziemlich gut aussehen, wenn Sie:

  • Vermeiden Sie BufferedImage -Typen, die vom JDK nicht gut unterstützt werden.
  • Verwenden Sie eine inkrementelle Skalierung
  • Halten Sie sich bei der inkrementellen Skalierung an Bicubic

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

Ich habe die veröffentlicht IMGSCALR -Bibliothek (Apache 2) Vor ungefähr 6 Monaten, um das Problem von "Ich möchte gut aussehende skalierte Kopien dieses Bildes, tun Sie es jetzt!" Nach ungefähr 10 Fragen wie diesem.

Standardnutzung sieht aus wie:

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

Das zweite Argument ist die Begrenzungsbreite und die Höhe von IMGSCAlR werden das Bild skalieren - und die Anteile korrekt hält, selbst wenn Sie in ungültigen Abmessungen bestanden haben - gibt es viele detailliertere Methoden, Aber das ist die einfachste Verwendung.

Die Verwendung, die Sie möchten, beispielsweise wenn Facebook Bilder auf 800x600 Pixel begrenzt, würden wie folgt aussehen:

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

Dadurch wird sichergestellt, dass das Bild in den am besten unterstützten Bildtyp bleibt und mit der meist Qualitätsmethode skaliert wird, die Java aufbringen kann.

In meinen eigenen hochauflösenden Tests habe ich keine klaffenden Diskrepanzen mit skalierten Bildern mit dieser Bibliothek/diesen Methoden bemerkt, außer wenn Ihr Bild vom ImageIO Loader in einen schlecht unterstützten Bildtyp eingesetzt wird-zum Beispiel passiert dies viel mit GIFs . Wenn Sie sie so lassen und sie nicht aus diesen schlecht unterstützten Typen herausholen, sieht es wirklich wirklich und schrecklich aus.

Der Grund dafür ist, dass das Java2D -Team für alle verschiedenen Arten von gepufferten Hardware -beschleunigten Pipelines, die das JDK verarbeiten kann Abdeckungen in Java2d, was zu schlechten und manchmal völlig falschen Bildern führt. Dies war eine solche PIA, um zu erklären und herauszufinden, dass ich diese Logik direkt in die Bibliothek geschrieben habe.

Die beiden besten unterstützten Typen sind gepuffert.

Die beiden derzeit beliebtesten Open-Source-Bibliotheken, die sich auf die Größenänderung von Bildern in Java spezialisiert haben, sind:

Zusätzlich gibt es den JDK-Weg mit Java Graphics2D (Sehen Sie sich diese Frage an, wie es geht), dessen Erstellung berüchtigt ist schlechte Ergebnisse, insbesondere beim Downscaling.Da ist auch ein Java-Schnittstelle zu ImageMagick Dies wird hier weggelassen, da hierfür ein externes Tool erforderlich ist.

Visuelle Qualität

Hier ist ein Vergleich der Ergebnisse der Größenänderung/Verkleinerung von a 580x852 png zu 145x213.Als Referenz wird die Größenänderung „Für Web speichern“ von Photoshop CS5 verwendet. Notiz:Die Ergebnisse entsprechen 1:1 dem, was die gerade erstellten Bibliotheken zusammenkopiert haben.Der Zoom verwendet keine Filterung, sondern nur einen einfachen Algorithmus für den nächsten Nachbarn. Hier finden Sie das Originalbild.

comparison

  1. Thumbnailator 0.4.8 mit Standardeinstellungen, keine Dimensionsanpassungen
  2. Photoshop CS5 mit bikubischem Algorithmus
  3. imgscalr 4.2 mit ULTRA_QUALITY-Einstellung, keine Dimensionsanpassungen
  4. Graphics2D (Java 8) mit Renderhinweisen VALUE_INTERPOLATION_BICUBIC, VALUE_RENDER_QUALITY, VALUE_ANTIALIAS_ON

Ich überlasse es dem Leser, das beste Ergebnis auszuwählen, da dies subjektiv ist.Im Allgemeinen haben alle eine gute Ausgabe, außer Graphics2D.Thumbnailator erzeugt schärfere Bilder, die der Photoshop-Ausgabe sehr ähneln, während die Ausgabe von imgscalr deutlich weicher ist.Für Symbole/Text usw.Sie möchten eine schärfere Ausgabe, für Bilder möchten Sie möglicherweise eine weichere Ausgabe.

Rechenzeit

Hier ist ein nichtwissenschaftlicher Benchmark, der dies verwendet Werkzeug und 114 Bilder mit Abmessungen von ca 96x96 bis zu 2560x1440 Behandeln Sie es als 425 % Bilder und erstellen Sie:100 %, 150 %, 200 %, 300 % und 400 % skalierte Versionen davon (also 114 * 5 Skalierungsoperationen).Alle Bibliotheken verwenden die gleichen Einstellungen wie im Qualitätsvergleich (also höchstmögliche Qualität).Die Zeiten skalieren nur, nicht den gesamten Prozess.Auf einem i5-2520M mit 8 GB RAM und 5 Läufen durchgeführt.

  • Miniaturansicht:7003.0ms | 6581.3ms | 6019.1ms | 6375.3ms | 8700,3 ms
  • imgscalr:25218,5 ms | 25786.6ms | 25095.7ms | 25790.4ms | 29296,3 ms
  • Graphics2D:7387,6 ​​ms | 7177.0ms | 7048,2 ms | 7132.3ms | 7510.3ms

Hier ist der in diesem Benchmark verwendete Code.

Interessant Thumbnailator ist mit einer durchschnittlichen Zeit von 6,9 Sekunden auch der schnellste gefolgt von Java2D mit 7,2 Sek Verlassen imgscalr liegt mit mageren 26,2 Sek. zurück.Dies ist wahrscheinlich nicht fair, da imgscalr auf eingestellt ist ULTRA_QUALITY was extrem teuer zu sein scheint;mit dem QUALITY Die Einstellung liegt im Durchschnitt bei konkurrenzfähigeren 11,1 Sekunden.

Um das Bild mit benutzerdefinierter Qualität zu ändern, verwenden Sie Vorschaubild. Jar.

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

Welchen Hinweis verwenden Sie? Normalerweise ist das bicubische Resampling die beste. Auf den Fotos, auf die Sie verknüpfen, sind sie sehr zackig, wodurch ich denken lässt, dass Sie den nächsten Nachbarn als Hinweis verwenden.

In dem PicturesCaler Klasse, auf die Sie verlinken, in der paintComponent Methode verwendet sechs verschiedene Möglichkeiten zur Größe des Bildes. Haben Sie alle sechs ausprobiert, um zu sehen, was das beste Ergebnis erzielt?

Nach ein paar frustrierenden Experimenten fand ich Folgendes Größenänderung Evaluierung, und verwendete den Multi-Pass-Ansatz in meinem Projekt.

Zu diesem Zeitpunkt habe ich die methode getCaledInstance () in meine Miniaturgenerator -Klasse kopiert, meinen Bildleseansatz geändert, um ImageIO zu verwenden (damit man einen gepuffertenImage zurückgibt) und jetzt bin jetzt sehr glücklich!

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

Ich wollte höchste Qualitätsgröße mit dem Seitenverhältnis erhalten. Versuchte einige Dinge und las mehrere Einträge. Zwei Tage verloren und am Ende habe ich das beste Ergebnis mit einfacher Java-Methode (ausprobiert auch ImageMagick und Java-Image-Scaling-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();
    }
  }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top