Seite 1 von 1
Bilder gleichzeitig bearbeiten (Multiprocessing)
Verfasst: Sonntag 6. Dezember 2009, 11:35
von Gabelmensch
Hallo,
ich habe einen Haufen Bilder in einer Liste "liste", diese moechte ich so bearbeiten:
Code: Alles auswählen
def bilderskalieren(datei):
name = os.path.basename(datei)
ausgabe = os.path.join(ziel, name)
im = Image.open(datei)
im.thumbnail(abmessungen, Image.ANTIALIAS)
im.save(ausgabe + '.py.jpg', 'JPEG')
for i in liste:
bilderskalieren(i)
Nun moechte ich immer vier Bilder gleichzeitig verarbeiten, jedoch steige ich bei der Doku von "threading", "multiprocessing"... nicht wirklich durch. Mir fehlt ein Einstieg wo ich suchen sollte. Hat einer soetwas mal gemacht?
Verfasst: Sonntag 6. Dezember 2009, 11:52
von nemomuk
Verfasst: Sonntag 6. Dezember 2009, 12:04
von Gabelmensch
Das lese ich bereits seit einer Stunde durch.
Verfasst: Sonntag 6. Dezember 2009, 12:23
von Gabelmensch
Ok, so geht es schoneinmal:
Code: Alles auswählen
from threading import Thread
*listebauen*
def bilderskalieren(datei):
name = os.path.basename(datei)
ausgabe = os.path.join(ziel, name)
im = Image.open(datei)
im.thumbnail(abmessungen, Image.ANTIALIAS)
im.save(ausgabe + '.py.jpg', 'JPEG')
for datei in liste:
prozess = Thread(target = bilderskalieren, args = (datei,))
prozess.start()
Es funktioniert auch, ich bekomme aber ca. 10 Fehlermeldungen:
Code: Alles auswählen
Exception in thread Thread-15:
Traceback (most recent call last):
File "/usr/lib64/python2.6/threading.py", line 522, in __bootstrap_inner
self.run()
File "/usr/lib64/python2.6/threading.py", line 477, in run
self.__target(*self.__args, **self.__kwargs)
File "z.py", line 60, in bilderskalieren
im = Image.open(datei)
File "/usr/lib64/python2.6/site-packages/PIL/Image.py", line 1916, in open
raise IOError("cannot identify image file")
IOError: cannot identify image file
Aber am Ende werden alle 150 Bilder korrekt generiert. Kann ich die Fehlermeldung ignorieren?
Edit:
OK, die Fehlermeldung resultiert aus der Liste.
Verfasst: Sonntag 6. Dezember 2009, 14:07
von Darii
Funktioniert es denn ohne Threads? Und werden die 150 Bilder wirklich korrekt angezeigt oder sind das noch Reste von einem voherigen Test? Mit der Fehlermeldung so dürfte das dann nämlich nicht funktionieren. Du betreibst da auch ziemliches Kamikaze mit deinen 150 Threads die du da startest.
Zu deinem eigentlichen Problem, das ist nicht so trivial, vermutlich musst du dir einen eigenen Threadpool basteln, der die Aufgaben dann abarbeitet, aber auch da muss man
aufpassen(man muss den Quellcode nicht unbedingt verstehen, der Text reicht).
Verfasst: Sonntag 6. Dezember 2009, 14:28
von DatenMetzgerX
Naja suche dir mal einige Hilfen zu queue heraus, das ist wohl das, was du benötigst.
Das mit den Threads wäre ja schon mal ganz nett, nur würde ich nicht pro Datei einen starten sondern genau 4 stuck die sich dann die Daten selbst aus der Queue (liste).
Achte hierbei unbedingt noch auf die Synchronisierung (ist die Python queue synchronisiert?)
Verfasst: Sonntag 6. Dezember 2009, 15:04
von Gabelmensch
Die Fehlermeldung ruehrte daher, dass einige Eintraege in der Liste keine Bilder waren. Die Seite ist Interessant, einen Queue schaue ich mir noch an.
Ich habe es jetzt ganz dreckig geloest:
Code: Alles auswählen
from threading import Thread
*listebauen*
def bilderskalieren(datei):
name = os.path.basename(datei)
ausgabe = os.path.join(ziel, name)
im = Image.open(datei)
im.thumbnail(abmessungen, Image.ANTIALIAS)
im.save(ausgabe + '.py.jpg', 'JPEG')
z = 0
for datei in liste:
prozess = Thread(target = bilderskalieren, args = (datei,))
prozess.start()
z = z + 1
if z == 10:
prozess.join()
z = 0
So wird bei jedem 10. Thread gewartet, bis der 10. Fertig ist, bevor es weitergeht.
Verfasst: Sonntag 6. Dezember 2009, 15:20
von EyDu
Gabelmensch hat geschrieben:So wird bei jedem 10. Thread gewartet, bis der 10. Fertig ist, bevor es weitergeht.
Das ist genau der Weg, wie man es nicht machen würde. Überlege dir was passiert, wenn die ersten 9 Threads fertig sind, der 10te aber noch (beliebig lang) arbeitet. Wie schon vorher von jemandem vorgeschlagen, solltest du 10 Worker-Threads erzeugen, welche sich ihre Aufgaben aus einer Queue holen oder auf die die Arbeit schon vorher verteilt wird.
Threads laufen in Python übrigens nicht wirklich parallel, da spielt der GIL nicht mit.
Verfasst: Sonntag 6. Dezember 2009, 17:10
von Gabelmensch
So, meine neue Version.
Code: Alles auswählen
import threading
def bilderskalieren(liste):
for datei in liste:
name = os.path.basename(datei)
ausgabe = os.path.join(ziel, name)
im = Image.open(datei)
im.thumbnail(abmessungen, Image.ANTIALIAS)
im.save(ausgabe + '.py.jpg', 'JPEG', quality=95)
laenge = len(liste)
anzahljobs = 10
if laenge > anzahljobs:
teiler = laenge / anzahljobs
for i in range(0, laenge - 1, teiler):
jobliste = []
for j in range(0, teiler):
stelle = j + i
if stelle < laenge:
jobliste.append(liste[stelle])
prozess = threading.Thread(target = bilderskalieren, args = (jobliste,))
prozess.start()
Das reicht mir fuer Heute.
Verfasst: Sonntag 6. Dezember 2009, 19:37
von HWK
Etwas vereinfacht (ungetestet)
Code: Alles auswählen
anzahljobs = min(laenge, 10)
for i in xrange(anzahljobs):
thread = threading.Thread(target=bilderskalieren,
args=(liste[i:laenge:anzahljobs],))
thread.start()
Wobei das Holen der Bilder aus einem Queue sicher effektiver wäre. Bei Deiner Variante wäre es durchaus möglich, dass bis auf einen alle Threads fertig sind, der letzte aber noch einige Bilder umrechnen muss. Diese Arbeit könnten ihm die fertigen Threads abnehmen.
Evtl. soltest Du auch einmal das multiprocessing-Module ausprobieren. Damit kannst Du mehrere Kerne eines Multi-Core-Prozessors gleichzeitig verwenden. Wahrscheinlich ist das schneller.
MfG
HWK
Verfasst: Sonntag 6. Dezember 2009, 19:38
von EyDu
Ein Tip:
Slicing
Edit: Da war HWK wohl etwas schneller.
Verfasst: Sonntag 6. Dezember 2009, 19:52
von Darii
HWK hat geschrieben:Evtl. soltest Du auch einmal das multiprocessing-Module ausprobieren. Damit kannst Du mehrere Kerne eines Multi-Core-Prozessors gleichzeitig verwenden. Wahrscheinlich ist das schneller.
Nicht unbedingt, das Hauptproblem ist das I/O. Vermutlich ist der GIL das einzige, was einem bei der jetzigen Lösung sogar den Hintern rettet(die I/O-Aktivität wird durch den GIL automatisch synchronisiert). Siehe mein Link.
Verfasst: Sonntag 6. Dezember 2009, 20:37
von rayo
Hi
Hier noch eine Version mit dem Multiprocessingmodul. Funktioniert bei mir auf 4 Kernel problemlos.
Code: Alles auswählen
from multiprocessing import Process, Queue
from glob import glob
import os
import Image
def rescale(q, size, target):
while not q.empty():
filename = q.get()
print 'Process %d: create thumbnail of %s' % (os.getpid(), filename)
output = 'thumb_%s' % filename
ausgabe = os.path.join(target, output)
im = Image.open(filename)
im.thumbnail(size, Image.ANTIALIAS)
im.save(output, 'JPEG')
if __name__ == '__main__':
q = Queue()
worker = 4
for x in glob('*.JPG'):
q.put(x)
for x in range(worker):
p = Process(target=rescale, args=(q, (128,128), '.'))
p.start()
for x in range(worker):
p.join()
Gruss
Verfasst: Sonntag 6. Dezember 2009, 21:01
von EyDu
@rayo: Auch wenn es für die Ausführung des Programms keine dramatischen Folgen hat (außer, dass es sich mal nicht beendet), denke mal über deine Zeilen
nach.
Verfasst: Sonntag 6. Dezember 2009, 22:43
von BlackJack
@rayo: Gibt's einen Grund warum Du keinen `Pool` benutzt?
Verfasst: Montag 7. Dezember 2009, 07:35
von rayo
BlackJack hat geschrieben:@rayo: Gibt's einen Grund warum Du keinen `Pool` benutzt?
Nö, war einfach das Beispiel was mir gerade am geläufigsten war.
Verfasst: Montag 7. Dezember 2009, 15:13
von Gabelmensch
Vielen Dank fuer eure Antworten.