Image Manipulation – mit PIL Bilddateien verändern

Nicht immer will man die Bilder, welche die Webcam aufnimmt, genauso im Web anzeigen, wie sie aufgenommen wurden; z.B. soll der Ausschnitt verändert werden oder die Auflösung. Auch will man vielleicht das Webcam Image noch beschriften, z.B. mit den Wetterdaten aus meinem Post Wetterbericht und Astrodaten auslesen.

Wie immer führen viele Wege nach Rom. Ich zeige euch, wie man das recht einfach mit der Python Image Library [PIL] bewerkstelligt.

PIL ist eine sehr leistungsfähige Library mit vielen Modulen und bietet fast jede nur denkbare Methode, ein Bild zu manipulieren.

Voraussetzungen

Diese Anleitung setzt voraus, dass ihr ein aktualisiertes Raspbian auf eurem Pi habt.

Ferner ist diese Anleitung für Python 2.7 geschrieben. Bei Python 3 gibt es eventuell ein paar Abweichungen. Ferner gehe ich davon aus, dass ihr das Raspberry Pi Camera Module verwendet. Die meisten Tipps funktionieren aber auch mit einer gewöhnlichen USB Cam, sofern ihr die unter Python zum Laufen bekommt.

Ob ihr headless arbeitet – d.h. mit Putty o.ä. auf euren Pi zugreift – oder mit direkt angeschlossener Tastatur und Monitor ist egal.

Installieren von PIL

Auf einem frisch installierten Raspbian ist PIL noch nicht enthalten und muss deshalb installiert werden.

Zu allererst einmal das System überhaupt mit

updaten und dann mit

die Python Image Library installieren. Fertig!

Wir schauen uns hier nur das Image Modul sowie ImageDraw und ImageFont an. Die Links führen zu einer sehr guten, englischen Anleitung bei effbot.org.

Image Manipulation mit PIL

Wir haben mindestens 2 Möglichkeiten, an das Bild zu kommen, das wir bearbeiten wollen:

1a Bild aus Datei laden

Von PIL brauchen wir für unser erstes Experiment nur das Modul Image.

Im Objekt img steckt jetzt das von euch gewünschte Bild image.jpg.  Natürlich könnt ihr zwischen die Anführungszeichen auch einen Pfad zu einem anderen Verzeichnis eintragen. Achtet auf die korrekte Schreibweise: der erste Buchstabe von Image.open wird groß geschrieben.

1b Bild direkt von der Kamera laden

Natürlich könnte man erst mit Picamera ein Bild erzeugen und abspeichern um es dann anschließend wie oben wieder zu laden. Ich versuche aber, mit möglichst wenigen Speichervorgängen auszukommen, das spart Zeit und stresst die SD Speicherkarte vom Pi nicht so sehr.

Wir müssen zusätzlich noch die Picamera, die IO Library sowie Sleep aus der Time Library importieren.

Gewohnheitsmäßig kommentiere ich meine Programme in Englisch – kein Problem hoffe ich…

Die Methode über ein Streamobjekt zu gehen, findet sich in der gut gemachten Dokumentation der Picamera Library.

Anbgeblich macht die Kamera bessere Bilder, wenn man sie warmlaufen lässt. Der Befehl camera.still_stats = 'true' hilft bei neueren Firmware Releases, den blöden roten Fleck in der Bildmitte zu vermeiden.

Auch hier enthält img das gewünschte Imageobjekt, welches wir nun mit PIL weiterverarbeiten können:

Bild beschneiden

Das geschieht ganz einfach mit "crop".

Überlegt euch, welches Rechteck ihr aus dem Bild behalten wollt, der Rest fliegt raus. Ursprung des Koordinatensystems ist mit [0,0] links oben.

Die ersten beiden Werte des 4er Tupels box beschreiben die obere linke Ecke (hier mit 0 Pixeln Abstand von links und 60 Pixeln Abstand vom oberen Rand), die beiden letzten Werte definieren die rechte untere Ecke des Bildes.

img ethält jetzt das beschnittene Bild.

 Auflösung ändern

Jetzt wollen wir das für Webseiten wahrscheinlich zu große Bild resizen: Das Breiten-/Höhenverhältnis könnt ihr dabei frei wählen und so auch ein Bild verzerren. Will man das Seitenverhältnis beibehalten, muss man etwas rechnen:

Unser Originalbild  aus der Kamera hatte ein Breiten-/Höhenverhältnis von 4 zu 3. Ebenso – aber eher zufällig – hat unser beschnittenes Bild dasselbe Seitenverhältnis (2400/1800 entspricht 4/3).
Wollen wir das Bild auf eine Breite von 1024 Pixeln verkleinern, muss die Höhe 3/4 dieser Wertes, also 768 Pixel betragen

Einfach, oder? PIL hält auch noch Filterparameter bereit, mit dem das Umrechnungsverfahren beeinflusst werden kann. Wer tiefer graben will kann sich hier infomieren.

img ethält jetzt das klein gerechnete Bild.

Bild beschriften

Hierzu brauchen wir auch noch die beiden PIL Module Imagefont und ImageDraw. In unser Python Programm müssen wir oben noch mit

die entsprechenden Libraries importieren. Dann überlegen wir uns zum Ausprobieren einen Text, den wir ins Bild schreiben wollen:

und wir definieren die Schriftart:

Dieser Font ist einer von vielen, die standardmäßig auf dem Raspberry Pi installiert sind.

Wer etwas herumspielen will: eine Übersicht der installierten Fonts gibt es mit dem Shell Kommando

fc-list
bzw.
fc-list|less
sollte die Liste zu schnell durchrauschen.

Die Zahl am Ende der Font Zeile (hier 21) sagt, welche Fontgröße verwendet werden soll. Anschließend wird das Draw Objekt erzeugt und in dieses der Text mit den Farbattributen und dem definierten Font hineingeschrieben.

(10,40) steht für die Position der linken obere Ecke des Textes im Bild.
(255,255,255) steht für die Farbe weiß.
Leider kann ImageDraw keine Zeilenumbrüche. Man muss also jede Zeile einzeln positionieren oder einen Text variabler Länge z.B. mit splitlines aufbrechen und dann in einer Schleife jede Zeile an ihre Stelle schreiben.

 Die picamera Library hat auch eine etwas rudimentäre Möglichkeit mit dem Attribut annotate_text ein Bild zu beschriften. PIL bietet aber wesentlich mehr Möglichkeiten.

Speichern

Auch das ist ganz easy:

In diesem Beispiel habe ich das neue Bild in das Webserver Verzeichnis geschrieben. Damit kann man es dann das Bild mit einem Browser betrachten, indem man die IP-Adresse des Pi und den Dateinamen in die Adresszeile des Browsers einträgt:

Das setzt natürlich voraus, dass ein Webserver (ich empfehle für den Pi den Webserver Lighttpd) installiert ist und der User, mit dem ihr gerade angemeldet seid, Schreibrechte auf /var/www hat. Natürlich könnt ihr auch jeden anderen Pfad nehmen.

Das ganze Programm

Bei meiner Webcam sieht das Ergebnis dann so aus: http://rusticam.eu

4 Gedanken zu „Image Manipulation – mit PIL Bilddateien verändern

  1. Kleiner Nachteil, wenn man das Bild als Stream und nicht als Datei speichert: die EXIF-Daten gehen verloren.

    Hier kurzer Code, um EXIF-Daten zu speichern, bevor man mit PIL das Bild ändert:
    http://stackoverflow.com/questions/4764932/in-python-how-do-i-read-the-exif-data-for-an-image

    Jetzt muß ich nur rausfinden, wie ich die EXIF-Dateien manuell speichern kann, damit ich die Bilder als Stream bearbeiten kann bevor ich sie (mit EXIF) abspeichere….

    1. Hallo,
      ja da hast du recht! Ich glaube auch, dass die EXIF Werte erst mit camera.capture("name.jpg") erzeugt werden. Da man aber bei der Erzeugung des Images als Stream nichts hat, was man abgreifen könnte, bleibt man ohne EXIF Info.
      Wie geschildert, arbeite ich mit einem Stream Objekt, weil ich die SD Karte nicht unnötig stressen will. Eine andere Möglichkeit wäre natürlich, doch ein JPG Objekt zu erzeugen und dieses auf eine RAM Disk im Arbeitsspeicher abzulegen.
      Damit hat man zwar etwas weniger Arbeitspeicher (je nach dem wie das jpg komprimiert ist) aber das, was übrig bleibt reicht allemal für eine Webcam. Mit tmpfs wird immer nur so viel Speicher belegt, wie gebraucht wird. Meine Cam erzeugt ca. 250kB große JPG Dateien. Da bleicbt noch genug Arbeitsspeicher übrig.

    1. Hallo Dan,
      danke für deine Nachfrage.
      Wenn du einen Lighttpd Webserver auf dem Pi aufgesetzt hast, hast du sicher auch php mit installiert. Wenn nein, solltest du das noch nachholen. Php ist eine aktive Serverkomponente, welche z.b. alle jpg Dateien in einem Verzeichnis auslesen kann und dann in eine „vor/zurück“ Blätterlogik einbindet. Dazu gibt es eine ganze Reihe von Anleitungen im Web. Einfach mal nach „PHP Slideshow“ suchen.
      Die von mir gebaute Slideshow auf meinem Webspace http://rusticam.eu funktioniert anders, da ich auf meinem Webhosting Paket bisher keine aktiven Komponenten installieren wollte.
      Grob formuliert funktioniert das so:
      · Ein Python Script auf dem Pi macht alle halbe Stunde ein Foto und lädt es mit FTP in den Webspace hoch.
      · Dasselbe Script liest anschließend das Bilderverzeichnis aus dem Webserver aus und baut daraus eine XML Datei, welche alle *.jpg Dateinamen enthält.
      · Die XML Datei wird dann auch noch auf den Webspace geladen.
      · Im Webspace befindet sich eine html Seite (index.html) welche auch ein JavaScript enthält, das die XML Datei bei Aufruf einliest. Javascript ist eine Client Komponente, d.h. die eigentliche Verarbeitung findet im Browser und nicht auf dem Server statt.
      · Mit CSS werden noch die Navigationsbuttons eingeblendet
      Mit einem Analysetool (ich verwende das Firefox Plugin Firebug) kannst du dir die Website mit allen Komponenten anschauen.
      Ich hoffe, ich komme irgendwann bald dazu meine Lösung richtig zu dokumentieren.
      Gruß
      Chris

Schreibe einen Kommentar

Ich freue mich über Lob und Kritik.
Falls du Probleme mit der hier vorgestellten Anleitung hast und nicht weiter kommst:
Bitte das Problem oder die Fehlermeldung(en) möglichst genau beschreiben, auch an welcher Stelle (z.B. in welchem Node oder Befehl) und unter welchen Umständen der Fehler auftritt.
Gerne kannst du mir auch ein Mail schreiben. Die Adresse findest du im Impressum.
Ich gebe mir viel Mühe, meinen Lesern weiterzuhelfen. Je konkreter du bist, desto einfacher und schneller kann ich versuchen zu helfen.
Deine E-Mail-Adresse wird nicht veröffentlicht.
Erforderliche Felder sind mit * markiert