# Verkleinerung von Bildern



## thesecretboy (29. Mai 2007)

Hallo zusammen,

wir haben in unserer Webseite einen Upload-Button für Bilder integriert. D.h. der User wählt lokal sein Bild aus und kann es dann über einen Upload-Button "hochladen". Nun haben wir User, die uns Ihre Bilder direkt aus der Kamera mit einer großen Auflösung schicken. Daher müssen die Bilder weiter verkleinert werden. Da dies bei uns ewig dauert und das Bild qualitativ sehr leidet, hierzu 2 Fragen:

1.)
In Irfan-View gibt es zum Besipiel ein paar tolle Möglichkeiten, ein Bild zu verkleinern, so dass es danach richtig gut aussieht. Gibt es in Java eine schnelle Möglichkeit, die ein Bild z. B. unter Verwendung des "Hermite"-Filters (weil der laut Irfan-View der schnellste ist) zu verkleinern?


2.)
Mal ganz allgemein gefragt: Gibt es unter Java eine Möglichkeit, Bilder zu verkleinern, so daß die Geschwindigkeit auch bei großen Bildern ab ca. 300k nicht wesentlich langsamer ist als mit Graphics2D.drawImage unter Verwendung von RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR.

Danke für Euere Antworten


----------



## Marco13 (29. Mai 2007)

thesecretboy hat gesagt.:
			
		

> Mal ganz allgemein gefragt: Gibt es unter Java eine Möglichkeit, Bilder zu verkleinern, so daß die Geschwindigkeit auch bei großen Bildern ab ca. 300k nicht wesentlich langsamer ist als mit Graphics2D.drawImage unter Verwendung von RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR.



Ja, mit Graphics2D.drawImage unter Verwendung von RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR. :wink: Du könntest ja einfach ein BufferedImage mit der gewünschten Größe erstellen, dir davon dann das Graphics2D holen, und DORT reinzeichnen. Hab's jetzt nicht getestet, aber in Anlehnung an deine Aussage müßte es schneller sein...


----------



## thesecretboy (29. Mai 2007)

Hmmm... aus meinem Beitrag ging leider offensichtlich nicht deutlich hervor, wo genau unser Problem liegt.

Der bisherige Code lautet im Wesentlichen:


```
// outWidth, outHeight = Zielbreite und -höhe // inImage = das Originalbild, welches verkleinert werden soll BufferedImage outImage = new BufferedImage(outWidth,outHeight,BufferedImage.TYPE_INT_RGB);
Graphics2D g2D = outImage.createGraphics(); g2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_I
NTERPOLATION_NEAREST_NEIGHBOR);
g2D.drawImage(inImage,0,0,outWidth,outHeight,null);
```

Die Qualität des verkleinerten Bildes läßt dabei sehr zu wünschen übrig, andererseits steigt der Zeitaufwand für große Bilder (ab ca. 200k) bei Verwendung von RenderingHints.VALUE_INTERPOLATION_BILINEAR oder gar RenderingHints.VALUE_INTERPOLATION_BICUBIC sehr stark an.

RenderingHints.VALUE_ANTIALIAS_ON scheint überhaupt keine Auswirkung zu haben.

Gibt es also eine Möglichkeit, in Java ein Bild in besserer Qualität zu verkleinern, ohne dabei wesentlich langsamer zu werden als mit obigem Code?


----------



## Gast (1. Jun 2007)

klar, umwander das eingebaute graphics system, schreib dir deine eigene methode, die direkt auf dem bild arbeitet und optimier sie anständig


----------



## Marco13 (1. Jun 2007)

Ach so ... ja, es gibt bei Bildern auch eine Methode getScaledInstance - VIELLEICHT ist die schneller oder besser (ich hatte gedacht, dass du die verwendest, und sie dir zu langsam wäre), Der Methode kann man verschiedene Parameter mitgeben, die die Qualität des Ergebnisses beeinflussen...


----------



## thesecretboy (1. Jun 2007)

Vielen Dank. Werden wir mal ausprobieren.


----------



## thesecretboy (1. Jun 2007)

Ich habe gerade erfahren, dass wir diese Methode bereits getestet haben. Sie ist leider auch nicht schneller. Weiss jemand, ob es vielleicht am Markt fertige Produkte gibt, die das hergeben, was wir benötigen? Auch kommerzielle sind Willkommen.


----------



## EgonOlsen (1. Jun 2007)

Wie langsam ist eigentlich das "Langsam", von dem wir hier sprechen? Und was macht ihr mit den skalierten Bildern? Zeigt ihr die dem User direkt wieder an?


----------



## thesecretboy (1. Jun 2007)

Nachdem das Bild hochgeladen wird, soll es gleich (bei guter Qualität) heruntergerechnet werden auf 1024*768, da wir einige User haben, die uns Bilder direkt aus der Digicam hochladen und diese eine entsprechende Auflösung haben. Hier haben wir nun das Problem, dass die verwendete Routine bei guter Auflösung teilweise bis zu 8 Minuten pro Bild benötigt. Und das ist in Anbetracht, dass das ganze auf einem aktuellen Doppelprozessor-Server mit 4GB Ram passiert einfach zu lange.


----------



## thesecretboy (1. Jun 2007)

Nachtrag: Ja, nach dem herunterrechnen, werden dem User die Bilder sofort wieder angezeigt.


----------



## Hilefoks (1. Jun 2007)

Moin,

JMagick könnte helfen. JMagick ist eine Schnittstelle zu ImageMagick, eine sehr bekannte freie Software zur Erstellung und Bearbeitung von Rastergrafiken. 

JMagick selbst habe ich noch nicht benutzt. Die Geschwindigkeit von ImageMagick war für meine Zwecke bisher aber immer ausreichend.

MfG,
Hilefoks


----------



## EgonOlsen (1. Jun 2007)

thesecretboy hat gesagt.:
			
		

> Hier haben wir nun das Problem, dass die verwendete Routine bei guter Auflösung teilweise bis zu 8 Minuten pro Bild benötigt. Und das ist in Anbetracht, dass das ganze auf einem aktuellen Doppelprozessor-Server mit 4GB Ram passiert einfach zu lange.


Sehr übel...wollte das nicht so recht glauben und habe es selber mal ausprobiert (mit einem 10MP-Bild auf einer P4-2.4Ghz-Gurke). Ich habe den Versuch abgebrochen, so lange hat es gedauert. Das ist nicht sinnvoll benutzbar. Ich schließe mich daher dem obigen Poster an: Baut was eigenes!


----------



## EgonOlsen (1. Jun 2007)

Ein Beispiel für was eigenes (die Interpolationsfunktion ist vorhanden, aber fragwürdig...ist einfach ein geistiger Auswurf von 5min und deshalb sicher nicht so toll). PixelGrabber sind auf großen Bildern übrigens auch zu vermeiden. Sie sind unglaublich langsam. Naja, egal. Also:


```
import java.io.*;
import java.util.*;
import javax.imageio.*;
import javax.imageio.stream.*;

import java.awt.*;
import java.awt.image.*;

public class ScaleTest {
  public static void main(String[] args) throws Exception {
    System.out.println("Lesen...");
    Image img = ImageIO.read(new File("test.jpg"));
    System.out.println("Machen...");
    Image dest = downsample(img,1024,768);
    System.out.println("Schreiben...");
    writeImage(dest,"rescaled.jpg");
  }

  public static Image downsample(Image img,int twidth,int theight) throws Exception {
    int width = img.getWidth(null);
    int height = img.getHeight(null);

    // Pixel zugreifbar machen...PixelGrabber ist viel zu langsam
    BufferedImage sdest = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
    sdest.getGraphics().drawImage(img,0,0,null);
    DataBufferInt data = (DataBufferInt) (sdest.getRaster().getDataBuffer());
    int[] pixels = data.getData();

    // Zielbild erzeugen
    BufferedImage dest = new BufferedImage(twidth,theight,BufferedImage.TYPE_INT_RGB);
    DataBufferInt tdata = (DataBufferInt) (dest.getRaster().getDataBuffer());
    int[] dPixels = tdata.getData();

    float idx = (float) width / (float) twidth;
    float idy = (float) height / (float) theight;

    float px = 0;
    float py = 0;

    for (int y = 0;y < theight;y++) {
      int yp = y * twidth;
      for (int x = 0;x < twidth;x++) {
        int colr = 0;
        int colg = 0;
        int colb = 0;
        int adds = 0;

        // Ziemlich maue Interpolation anwenden...
        for (int ix = 0;ix < idx;ix++) {
          for (int iy = 0;iy < idy;iy++) {
            int pos = ((int) (px)) + ix + ((int) (py) + iy) * width;
            if (pos >= pixels.length) {
              pos = pixels.length - 1;
            }
            int pix = pixels[pos];
            int r = (pix & 0x00ff0000) >> 16;
            int g = (pix & 0x0000ff00) >> 8;
            int b = pix & 0x000000ff;
            colr += r;
            colg += g;
            colb += b;
            adds++;
          }
        }

        // Ergebnispixel schreiben...
        dPixels[yp + x] = (colr / adds) << 16 | (colg / adds) << 8 | (colb / adds);
        px += idx;
        if (px >= width) {
          px = 0;
          py += idy;
        }
      }
    }
    return dest;
  }

  // Als JPEG schreiben
  public static void writeImage(Image image,String path) throws Exception {
    if (image instanceof RenderedImage) {
      Iterator itty = ImageIO.getImageWritersBySuffix("jpeg");
      if (itty.hasNext()) {
        ImageWriter iw = (ImageWriter) itty.next();

        ImageWriteParam iwp = iw.getDefaultWriteParam();
        iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        iwp.setCompressionQuality(0.95f);

        ImageOutputStream ios = ImageIO.createImageOutputStream(new File(path));

        iw.setOutput(ios);
        iw.write(null,new IIOImage((RenderedImage) image,null,null),iwp);
        ios.close();
      }
      else {
        throw new RuntimeException("Keinen Writer gefunden!");
      }
    }
    else {
      throw new RuntimeException("Ja, nee...och...!");
    }
  }
}
```


----------



## uwe75-1 (21. Aug 2007)

Hallo,
ich möchte auch über den Webbrowser mit einem java Applet Bilder auf meine Homepage hochladen, allerdings möchte ich diese 1:1 hochladen, d.h. keine Kompriemierung etc. Das Applet soll absolut keine Veränderung an der datei vornehmen - einfach nur Kopieren. Ich benutze dafür unter anderem die Klasse ImageIO. Das Hochladen funktioniert einwandfrei, aber wenn ich die Eigenschaften der hochgeladenen datei dann vergleiche, dann ist die dpi Anzahl von der Kopie 96 dpi und das Original hat eine andere DPI Anzahl. Wie kann das passieren? Wenn Ihr den Quellcode benötigt, dann sende ich Ihn gern, allerdings ist der 5 Seiten lang - deshalb versuche cih den erstmal zu vermeiden!

Vielen Dank schon mal im Voraus für eure Hilfe


----------



## Wildcard (21. Aug 2007)

Warum kopierst du die Datei nicht einfach anstatt über die ImageIO zu gehen?


----------



## uwe75-1 (21. Aug 2007)

Ja das könnte ich schon machen, aber andere Benutzer sollen ebenfalls ihre dateien auf meinem webspace zur verfügung stellen können - dann funktioniert das kopieren nichtmehr, denn die kennen ja meine zugangsdaten zum webspace nicht - das sollen sie auch nicht. Oder was meinst du genau mit "kopieren"? Gibt es da in java einem simplen copy-befehl?

Vielen dank für die schnelle antwort


----------



## Wildcard (21. Aug 2007)

Einfach den Input/OutputStream rausschreiben, das meine ich damit.


----------



## uwe75-1 (25. Aug 2007)

Hallo,

also mit Java stecke ich noch voll in den Kinderschuhen und habe nun versucht, über Input/Outstream in Büchern und im Internet mehr zu erfahren, aber irgendwie komme ich da nicht weiter. Könntest Du mir bitte noch ein paar mehr Tips geben, wie ich mein 1:1 Upload Problem lösen kann?

Vielen dank schon mal für deine Hilfe


----------



## Wildcard (25. Aug 2007)

Dazu müsste man erstmal wissen wie du das Bild im Moment hochlädst.


----------



## uwe75-1 (25. Aug 2007)

die bilder werden mit ImageIO.read() in ein BufferedImage hochgeladen und dann als ByteArrayOutputStream an ein PHP Skript übergeben mit URLConnection usw.. kann Dir den Quellcode gern mal schicken aber das sind 5 Seiten!


----------



## Wildcard (25. Aug 2007)

Statt ImageIO nimmst du am besten einen einfachen FileInputStream


----------



## uwe75-1 (1. Sep 2007)

Sorry, wenn ich wieder nerven muß - hab es bis jetzt nur hinbekommen, daß ein File im selben lokalen Verzeichnis auf meinem Rechner mit anderen Namen gespeichert wird. Wie kann ich nun den FileOutPutStream über URLConnection an mein PHP-Skript übergeben.
ich habe echt keine Idee mehr, warum die Übergabe ständig scheitert.
Vielen Vielen Dank


----------



## Loep (5. Jun 2009)

Hi,

ich habe den Quellcode von EgonOlsen verwendet um Bilder zu verkleinern und bekomme folgende Exception:


```
Lesen...
Exception in thread "main" javax.imageio.IIOException: Unsupported Image Type
	at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:940)
	at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:915)
	at javax.imageio.ImageIO.read(ImageIO.java:1422)
	at javax.imageio.ImageIO.read(ImageIO.java:1282)
	at ImageResizer.main(ImageResizer.java:12)
```

Gibt es eine Methode, die nicht so empfindlich ist, wenn mal irgendwelche Metadaten falsch sind?
Es ist ein Farbbild und der Konqueror meint auch das Farbschema sei S/W.


----------



## Der Müde Joe (5. Jun 2009)

Ohne jetzt alle durchgelesen zu haben.
Die ist eine sehr schnelle und qualitativ gute Methode um Bilder zu verkleinern:
java.net: The Perils of Image.getScaledInstance()

(Hatte ich schon ein paar mal modifiziert im Einsatz)

EDIT:
Der Thread ist ja Steinalt... grummel...mach ein neues...für solches..time flies...


----------

