BufferedImage performant einlesen

Markey

Mitglied
Hallo zusammen,

ich muss in einem Programm eine ganze Menge von Bildern (JPG) einlesen.
Dafür benutze ich die Klasse ImageIO und speichere die Bilder in einer ArrayList<BufferedImage>.

Mir ist klar dass das Performancetechnisch völliger humbug ist und ich eigentlich die Bilder erst ON-Demand laden sollte. Aber für meine Anwendungszwecke fällt mir leider keine andere Möglichkeit ein.

Selbst das erhöhen des HeapSpace mittels "-Xms1024m -Xmx1024m" hilft nur bedingt.

Zur Erklärung. Ich bin dabei eine Art Photo Mosaik zu programmieren was ein "Zielbild" aus einer Menge von kleinen Bildern zusammensetzt. Ich weiß sowas gibts schon, aber das ist jetzt nicht das Thema.

Dazu ist es erforderlich, dass ich die kleinen Bilder einlese und auf ihren Farb- / Kontrast- und Helligkeitsmittelwert untersuche. Um diese dann in der Folge passend anzuordnen, so dass sie einem vorgegebenen Zielbild entsprechen.

Beispiel: http : // static.gamesports.net/starcraft/picdb/330.jpg
[edit SlaterB: direkte Einbindung eines Mega-Bildes geändert]

Habt ihr ne Idee wie ich der Heap Space Problematik aus dem Weg gehen kann?

Vielen Dank,

Mark
 
Zuletzt bearbeitet von einem Moderator:
Vielleicht hilft es wenn du direkt nach dem einlesen eines Bildes es herunterskalierst auf die Ausgabegröße, bevor du sie in die ArrayList schiebst. Oder willst du ein Mosaik aus Bildern in Originalgröße machen?

Oder du behältst zu jeder Zeit nur 2 Images im Speicher: ein eingelesenes Bild und das Zielbild. In einer Map merkst du dir die errechneten Wertes des zuvor eingelesenen Bildes, sowie den Pfad der Bilddatei - anschliessend verwirst du das Bild und lädst das nächste. Nachdem zu alle Bilder analysiert hast, kannst du die Bilder anhand der errechneten Werte (in der Map) sequentiell laden und in das Zielbild schreiben. Somit hast du immer nur 2 Bilder im Speicher: das große Zielbild und das Bild das du berechnen bzw ins Zielbild einfügen möchtest.
 
Lade die Bilder kachelweise ein. Dafür musst du max. die umgebenden Zellen miteinlesen (also höchstens 9Bilder auf einmal + Zielbild), damit du notfalls auch noch Pixeloperationen an den Rändern durchführen kannst. D.h. du iterierst über jede "Zelle" des Zielbildes, holst dir das benötigte Quellbild (und die umgebenden) aus dem Dateisystem und zeichnest es in dein Zielbild.
 
Danke schonmal für die Antworten!
@HimBromBeere: Das Problem ist nicht das durchlaufen des Zielbildes. Ich erklärs im folgenden nochmal genauer 🙂 Vielleicht verstehe ich es auch nur nicht richtig...

@carcophan
Du meinst das einlesen von den Bildern einfach in mehreren Schüben machen und darauf hoffen dass der Gb bis dahin den Speicher wieder frei geräumt hat? Am Ende hänge ich den aktuellen Code für das einlesen nochmal ran...

Rundumblick nochmal:

Ich habe

Teilbilder (Eine Sammlung von n zufälligen Bildern die später das Mosaik bilden)

Ziebild (Ein einzelnes Bild was durch die Sammlung der Teilbilder abgebildet werden soll)

Ergebnisbild (Das Endprodukt was so aussieht wie das Zielbild und aus der Menge von Teilbildern besteht)

Vom Ablauf her hatte ich mir das wie folgt vorgestellt:

Geplant für das Ergebnisbild sind 40 x 20 Teilbilder die jeweils eine Auflösung von 240 x 120 px haben.
Das Ergebnis verfügt also über insgesamt 800 Teilbilder und hat eine Auflösung von 4800 x 2400 Pixeln.

1. Ich Iterriere durch das Zielbild (40x x 20y Abschnitte)
2. Ich schaue mir den aktuellen Abschnitt des Zielbildes an
und ermittle die vorliegende Durschnittsfarbe (RGB etc.) mit einer Prüfmethode
3. Anhand der ermittelten Farbe soll aus der Liste der Teilbilder ein Bild ausgewählt werden, was am ehesten der vorliegenden Durschnittsfarbe vom aktuellen Zielbildabschnitt entspricht.


Problem:
Das ich das machen kann muss ich doch die Bilder alle schon "eingeladen" und auch durch meine prüfmethode für den Farbmittelwert durchgejagt haben oder?

Jemand ne andere / bessere / funktionierende Idee?

Code-Robau:
Java:
private JFileChooser = new JFileChooser();
[...]


private ArrayList<ImportImage> chooseImageDirectory() {
		ArrayList<BufferedImage> tempList = new ArrayList<BufferedImage>();
		chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		int returnVal = chooser.showOpenDialog(this);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			File file = chooser.getSelectedFile();
			File[] files = file.listFiles();
			MimetypesFileTypeMap typ = new MimetypesFileTypeMap();
			for(File f : files)
			{
				if(f.isFile() && f.getName().matches("jpg$"));
				{
					try {
						BufferedImage image = ImageIO.read(f);
						tempList.add(image);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		} 
		else {
		}
		return null;
	}
 
Oder du behältst zu jeder Zeit nur 2 Images im Speicher: ein eingelesenes Bild und das Zielbild. In einer Map merkst du dir die errechneten Wertes des zuvor eingelesenen Bildes, sowie den Pfad der Bilddatei - anschliessend verwirst du das Bild und lädst das nächste. Nachdem zu alle Bilder analysiert hast, kannst du die Bilder anhand der errechneten Werte (in der Map) sequentiell laden und in das Zielbild schreiben. Somit hast du immer nur 2 Bilder im Speicher: das große Zielbild und das Bild das du berechnen bzw ins Zielbild einfügen möchtest.

ahh okay das würde mir die aufwändige objekterzeugung spaaren! Ich verstehe... Reicht mir ja dann wenn ich für jedes Bild den Pfad und meinen Farbmittelwert in einer Map speicher. Manchmal sieht man vor lauter Bäumen den Wald nicht!
 
Das ich das machen kann muss ich doch die Bilder alle schon "eingeladen" und auch durch meine prüfmethode für den Farbmittelwert durchgejagt haben oder?
Ja, das ist wohl wahr, sonst wirst du auf den Bildern keine Berechnungen machen können. Aber du könntest ja auch erstmal für jedes eingeladene Bild die Durchschnittswerte bestimmen und in einer Map speichern, das eigtl. Bild verwirfst du wieder. Erst danach lädst du jede Kachel sukzessive ein.
 

Zurück
Oben