Gittercollage erstellen?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
heiliga horsd

Moin,

erstmal Entschuldigung für den Titel, aber mir fällt echt nix besseres ein. Ich versuche dafür das Problem umso detaillierter zu schildern.

Ich möchte für ein Projekt in dem Bilder (Räume) verfremdet werden müssen diese ansprechend präsentieren (abgeben) und habe mir überlegt, dass ich die Einzelbilder zu einer Art Collage zusammen setze. Konkret möchte ich die Bilder einfach alle nebeneinander/untereinander legen, sodass ich am Ende ein riesiges Bild habe in das man erst hineinzoomen muss und in dem die Bilder neben-/untereinandereinander liegen (Also das ganze soll die Form eines Rechtsecks/Quadrats haben, also bspw. 50 Bilder lang und 30 tief oder so, nicht alle nur nebeneinander oder untereinander). Meine Frage: Kann ich das mit Python2 realisieren und welche Bibliotheken nehme ich hierfür am Besten her um das automatisiert zu machen, ich habe nämlich keine Ahnung wie ich das Problem am einfachsten und effizientesten angehe. (Nicht falsch verstehen, ich suche hier keinen fertigen Quelltext sondern eher ein paar Denkanstöße zur Lösung des Problems sowie Dinge, die ich evtl. von Anfang an beachten sollte. Snippets akzeptiere ich natürlich trotzdem ;))


Lg heiliga horsd
BlackJack

Kann man nicht einfach mit der Python Imaging Library (`PIL`) ein entsprechend grosses Bild erstellen und dann die Einzelbilder dort an die passenden Stellen hinein kopieren?
heiliga horsd

Ich habe damit keinerlei Erfahrung, deswegen wollte ich lieber mal die Profis zuerst fragen wie man das am Besten angeht. Von PIL habe ich lediglich irgendwo gelesen dass da die Entwicklung kaum vorwärts geht, aber ich schau mir das morgen mal ein wenig an. Wie ist das denn so von der Performance, muss ich mir da Gedanken machen oder läuft das ratzfatz durch?
BlackJack

@heiliga horsd: PIL ist schnell, das ist in C implementiert. Problematisch könnte höchstens die Grösse des Bildes werden, aber das ist unabhängig von der Programmiersprache.
heiliga horsd

OK, Danke für den Hinweis. Welche Bildgröße wäre denn kritisch? Erfahrungsgemäß schieße ich nämlich anfangs gerne über das Ziel hinaus und da bin ich ehrlich gesagt nicht scharf drauf ;)
BlackJack

@heiliga horsd: Den Speicherbedarf kannst Du doch recht einfach ausrechnen. Drei bis vier Bytes pro Pixel.
heiliga horsd

OK dann rechne ich das mal durch und probiers mit PIL aus, Danke!
Benutzeravatar
pixewakb
User
Beiträge: 1411
Registriert: Sonntag 24. April 2011, 19:43

Ein Problem, auf das du stoßen könntest, dürften unterschiedliche Formate deiner Bilder sein. Besonders einfach wäre es, wenn alle Bilder quadratisch sind und du somit ein Mosaik aus Quadraten setzen kannst, dann wäre nämlich die neue Position des nächsten Bildes die Summe der Breiten der vorigen Bilder ;) usw.

Ansonsten wie BlackJack schrieb: Ein entsprechend großes Bild ("Leinwand") erzeugen mit Breite = Breite des Einzelbildes * Ihre Anzahl und Höhe = Höhe des Einzelbildes * Ihre Anzahl; alle Bilder in eine Liste packen (vorher skalieren und zuschneiden???) und gut mischen (random.shuffle) und dann mit einer for-Schleife die Bilder auf die Leinwand bringen.

__ Edit __
Zu "alle Bilder in eine Liste packen": Das meint die Namen der Bilder in eine Liste packen. Dann lädst du erst einmal nur das Bild, was du gerade ändern und auf die Leinwand bringen willst...
heiliga horsd

Moin,

hab das ganze mal zusammengebaut und will hier mein Ergebnis posten falls mal jemand anderes ein ähnliches Problem hat. Hab mir nicht allzuviel Mühe gegeben, alles sauber zu machen, daher läuft der Code ohne eigene Funktionen/Klassen einfach auf Modulebene durch.

Code: Alles auswählen

from PIL import Image
import glob, os, random

# Konstanten 
SOURCEDIR = "Quelle" # Name des Quellordners
SIZE_X = 1080 # Anzahl der horizontalen Pixel des Bildes
SIZE_Y = 810 # Anzahl der vertikalen Pixel des Bilders
FORMAT = (8,8) # Format der Collage, also hier 8 Bilder neben- und untereinander
DESTFILENAME = "Ausgabe.jpg" # Ausgabedatei

tempList = [] # Hier werden die offenen Bildobjekte gespeichert

# Alle jpg-Bilder im SOURCEDIR-Verzeichnis laden; Könnte man zwar später auch
# einzeln bei Bedarf laden, wodurch sie nicht dauernd im RAM hängen würden, aber
# aus Faulheit und weil es auch so funktioniert lass ich das alles mal so
for infile in glob.glob("{0}/*.jpg".format(SOURCEDIR)):
    file, ext = os.path.splitext(infile)
    im = Image.open(infile)
    tempList.append(im)

# Zum Format passende Bildanzahl prüfen kann vermutlich einige Probleme ersparen
if not (len(tempList) == FORMAT[0] * FORMAT[1]): 
	print "Format und Bildanzahl passen nicht zusammen, Abbruch"
	exit()     

random.shuffle(tempList) # optional, mischt die Bilder nur ein wenig durch

# Leeres Bild mit passender Größe erzeugen
out = Image.new("RGB", (FORMAT[0]*SIZE_X, FORMAT[1]*SIZE_Y), "white")

# Die Positionierung erzeugt durch primitives Zählen; hier werden die 
# Zählvariablen initialisiert
x = y = 0
counter = 1

# Die Schleife, die das neue große Bild mit den kleinen füllt
for element in tempList:
	if counter > FORMAT[0]:
		counter -= FORMAT[0]
		x = 0
		y += SIZE_Y
	out.paste(element, (x,y))
	x += SIZE_X
	counter += 1

		
out.save(DESTFILENAME) # Bild speichern
@pixewakb: Hab den Beitrag erst jetzt gesehen wo ich mein Script posten wollte, aber die von dir erläuterten Probleme sind natürlich alle in gewisser Form behandelt worden, bei mir werden zwar gleich alle Bilder in den RAM geladen (siehe entsprechender Kommentar), aber das hat mich nicht gestört. Falls das Script jemand mal benötigen wird, kann er es ja umschreiben wenn ihn der Umstand stört.
BlackJack

@heiliga horsd: Das sind so ca. 336 MB für die Quellbilder und das Zielbild.

Neben den von Dir selbst schon genannten Punkten:

`tempList` sollte `images` heissen, denn das ist ja was drin ist.

Pfade mit `os.path.join()` zusammensetzen.

`file` und `ext` werden nicht verwendet, die Zeile kann also ersatzlos raus.

Die letzte ``for``-Schleife lässt sich einfacher schreiben, in dem man nur den `counter` mittels `enumerate()` mit den Bildern mitzählt und `x` und `y` aus dem `counter` und `FORMAT` berechnet. Beispielhaft mit einer Liste von Dateinamen statt `Image`-Objekten:

Code: Alles auswählen

# 
# Die Schleife, die das neue große Bild mit den kleinen füllt.
# 
for i, image_name in enumerate(image_names):
    y, x = divmod(i, FORMAT[0])
    result.paste(Image.open(image_name), (x * FORMAT[0], y * FORMAT[1]))
heiliga horsd

Einmal wurds bisschen eng mit dem RAM, das waren 13*13 Bilder die auch noch bisschen größer waren, da wär mir die Kiste fast abgeraucht (hab keinen /swap). Ansonsten war das alles gar kein Stress, wird ja alles anschließend gleich wieder freigegeben.
Ansonsten: Danke für deine Verbesserungen, werds mir noch zu Herzen nehmen!
Antworten