Asynchrones Lesen und Verarbeiten eines Bildes in Python

Kontext

Ich habe mich oft in der folgenden Situation gefunden:

  • Ich habe eine Liste von Bild-Dateinamen, die ich verarbeiten muss
  • Ich lese jedes Bild nacheinander mit zB scipy.misc.imread
  • Dann mache ich irgendeine Art von Verarbeitung auf jedem Bild und gib ein Ergebnis zurück
  • Ich speichere das Ergebnis entlang des Bilddateinamens in ein Regal

Das Problem ist, dass einfach das Lesen des Bildes nimmt eine nicht vernachlässigbare Zeit, irgendwann vergleichbar oder sogar länger als die Bildverarbeitung.

Frage

Also dachte ich, dass ich idealerweise das Bild n + 1 beim Verarbeiten von Bild n lesen konnte. Oder noch besser verarbeiten und mehrere Bilder gleichzeitig in einer automatisch optimierten Weise lesen?

Ich habe über Multiprocessing, Threads, Twisted, Gevent und dergleichen gelesen, aber ich kann nicht herausfinden, welche zu verwenden und wie man diese Idee implementiert. Hat jemand eine Lösung für diese Art von Problem?

Minimales Beispiel

# generate a list of images scipy.misc.imsave("lena.png", scipy.misc.lena()) files = ['lena.png'] * 100 # a simple image processing task def process_image(im, threshold=128): label, n = scipy.ndimage.label(im > threshold) return n # my current main loop for f in files: im = scipy.misc.imread(f) print process_image(im) 

2 Solutions collect form web for “Asynchrones Lesen und Verarbeiten eines Bildes in Python”

Die Antwort von Philip ist gut, aber nur ein paar Prozesse (eine Lektüre, ein Computer), die kaum ein modernes> 2-Kernsystem ausmachen wird. Hier ist eine Alternative mit multiprocessing.Pool (speziell seine Kartenmethode), die Prozesse erzeugt, die sowohl die Lese- als auch die Berechnungsaspekte ausführen, aber die alle verfügbaren Kerne besser nutzen sollen (vorausgesetzt, es gibt mehr Dateien als Kerne).

 #!/usr/bin/env python import multiprocessing import scipy import scipy.misc import scipy.ndimage class Processor: def __init__(self,threshold): self._threshold=threshold def __call__(self,filename): im = scipy.misc.imread(filename) label,n = scipy.ndimage.label(im > self._threshold) return n def main(): scipy.misc.imsave("lena.png", scipy.misc.lena()) files = ['lena.png'] * 100 proc=Processor(128) pool=multiprocessing.Pool() results=pool.map(proc,files) print results if __name__ == "__main__": main() 

Wenn ich die Anzahl der Bilder auf 500 erhöht und die processes=N Argument zum Pool , dann bekomme ich

 Processes Runtime 1 6.2s 2 3.2s 4 1.8s 8 1.5s 

Auf meinem Quad-Core-Hypertrohre i7.

Wenn Sie in realistischere Anwendungsfälle (dh tatsächliche verschiedene Bilder) gelangen, können Ihre Prozesse mehr Zeit damit verbringen, auf die Bilddaten zu warten, die aus dem Speicher geladen werden sollen (bei meinem Test, sie laden praktisch sofort aus dem Cache-Datenträger) und dann könnte es sein Lohnt sich explizit mehr Prozesse als Kerne, um etwas mehr Überschneidungen von Rechnen und Last zu bekommen. Nur deine eigene Skalierbarkeitsprüfung auf eine realistische Last und HW kann dir sagen, was eigentlich für dich am besten ist.

Das Multiprocessing-Paket ist ziemlich einfach zu bedienen. Schauen Sie sich die Warteschlangen Beispiel für einen Führer. Du folgst dem Produzenten-Konsumentenmodell. Sie wollen eine (oder mehrere) Produzentenprozesse, die Bilder lesen, und eine (oder mehrere) Konsumentenprozesse, die die Bildverarbeitung durchführen.

Ihr Beispiel würde so aussehen:

 from multiprocessing import Process, Queue import scipy def process_images(q): while not q.empty: im = q.get() # Do stuff def read_images(q, files): for f in files: q.put(scipy.misc.imread(f)) if __name__ == '__main__': q = Queue() producer = Process(target=read_images, args=(q, files)) producer.start() consumer = Process(target=process_images, args=(q)) consumer.start() 

Das ist ein bisschen einfacher als deine ursprüngliche Idee. In diesem Beispiel fügt der Produzent die Warteschlange so schnell hinzu, wie es kann, anstatt nur noch einen vor dem Verbraucher zu bleiben. Das könnte ein Problem sein, wenn der Produzent so weit voraus ist, dass man nicht genug Speicher hat, um die Warteschlange zu halten. Wenn Probleme auftreten, kannst du tiefer in die Multiprocessing-Dokumente einsteigen, aber das sollte genug sein, um dich zu starten.

  • Wie bekomme ich Nicht-Singleton-Cluster-IDs in scipy hierachical clustering
  • Wie man zwei-Probe-One-tailed-T-Test mit numpy / scipy durchführt
  • Ist die Ordnung eines Python-Wörterbuchs über Iterationen garantiert?
  • Effizientes Aufschneiden von Matrizen mittels Matrixmultiplikation mit Python, NumPy, SciPy
  • Curve Fitting mit bekannten Integralen Python
  • Passenden exponentiellen Zerfall ohne anfängliche Vermutung
  • Finde die meisten entfernten Punkte in der Konturkurve
  • Wie man einen adaptiven Filter in Python anwendet
  • Spline-Darstellung mit scipy.interpolate: Schlechte Interpolation für niederamplituden, schnell oszillierende Funktionen
  • Berechnen des Korrelationskoeffizienten zwischen zwei mehrdimensionalen Arrays
  • Scipy.optimize.curve_fit, TypeError: nicht unterstützter Operanden-Typ
  • Python ist die beste Programmiersprache der Welt.