{"id":301,"date":"2015-01-21T21:25:51","date_gmt":"2015-01-21T20:25:51","guid":{"rendered":"http:\/\/chriskrz.selfhost.bz\/?p=301"},"modified":"2025-03-06T08:50:02","modified_gmt":"2025-03-06T07:50:02","slug":"image-manipulation-mit-pil-bilddateien-veraendern","status":"publish","type":"post","link":"https:\/\/www.rustimation.eu\/index.php\/image-manipulation-mit-pil-bilddateien-veraendern\/","title":{"rendered":"Image Manipulation &#8211; mit PIL Bilddateien ver\u00e4ndern"},"content":{"rendered":"<p>Nicht immer will man die Bilder, welche die Webcam aufnimmt, genauso im Web anzeigen, wie sie aufgenommen wurden; z.B. soll der Ausschnitt ver\u00e4ndert werden oder die Aufl\u00f6sung. Auch will man vielleicht das Webcam Image noch beschriften, z.B. mit den Wetterdaten aus meinem Post <a title=\"Wetterbericht und Astrodaten als JSON auslesen und verarbeiten\" href=\"https:\/\/www.rustimation.eu\/index.php\/nochmal-update-wetterbericht-im-json-format-auslesen-und-verarbeiten\/\" target=\"_blank\" rel=\"noopener noreferrer\">Wetterbericht und Astrodaten auslesen<\/a>.<\/p>\n<p>Wie immer f\u00fchren viele Wege nach Rom. Ich zeige euch, wie man das recht einfach mit der Python Image Library [PIL] bewerkstelligt.<\/p>\n<p><!--more--><\/p>\n<p>PIL ist eine sehr leistungsf\u00e4hige Library mit vielen Modulen und bietet fast jede nur denkbare Methode, ein Bild zu manipulieren.<\/p>\n<h2>Voraussetzungen<\/h2>\n<p>Diese Anleitung setzt voraus, dass ihr ein aktualisiertes Raspbian auf eurem Pi habt.<\/p>\n<p>Diese Anleitung ist auf Python 3.x upgedated. Ferner gehe ich davon aus, dass ihr irgend ein\u00a0 Raspberry Pi Camera Module verwendet. Die meisten Tipps funktionieren aber auch mit einer gew\u00f6hnlichen USB Cam, sofern ihr die unter Python zum Laufen bekommt.<\/p>\n<p>Ob ihr headless arbeitet &#8211; d.h. mit Putty o.\u00e4. auf euren Pi zugreift &#8211; oder mit direkt angeschlossener Tastatur und Monitor ist egal.<\/p>\n<h2>Installieren von PIL<\/h2>\n<p>Auf einem frisch installierten Raspbian ist PIL noch nicht enthalten und muss deshalb installiert werden.<\/p>\n<p>Zu allererst einmal das System \u00fcberhaupt mit<\/p>\n<pre class=\"lang:sh decode:true \">sudo apt-get update\r\nsudo apt-get upgrade<\/pre>\n<p>updaten und dann mit<\/p>\n<pre class=\"lang:sh decode:true \">sudo apt-get install python-imaging<\/pre>\n<p>die Python Image Library installieren. Fertig!<\/p>\n<p>Wir schauen uns hier nur das <a href=\"http:\/\/www.effbot.org\/imagingbook\/image.htm\" target=\"_blank\" rel=\"noopener noreferrer\">Image Modul <\/a>sowie <a href=\"http:\/\/www.effbot.org\/imagingbook\/imagedraw.htm\" target=\"_blank\" rel=\"noopener noreferrer\">ImageDraw <\/a>und <a href=\"http:\/\/www.effbot.org\/imagingbook\/imagefont.htm\" target=\"_blank\" rel=\"noopener noreferrer\">ImageFont<\/a> an. Die Links f\u00fchren zu einer sehr guten, englischen Anleitung bei <a href=\"http:\/\/www.effbot.org\/imagingbook\/\" target=\"_blank\" rel=\"noopener noreferrer\">effbot.org<\/a>.<\/p>\n<h2>Image Manipulation mit PIL<\/h2>\n<p>Wir haben mindestens 2 M\u00f6glichkeiten, an das Bild zu kommen, das wir bearbeiten wollen:<\/p>\n<h3>1a Bild aus Datei laden<\/h3>\n<pre class=\"lang:python mark:9 decode:true\">#!\/usr\/bin\/python\r\n# coding=UTF-8\r\n\r\nfrom PIL import Image\r\n\r\n#  open ImageObject\r\nimg=Image.open('image.jpg')<\/pre>\n<p>Von PIL brauchen wir f\u00fcr unser erstes Experiment nur das Modul Image.<\/p>\n<p>Im Objekt <code>img<\/code> steckt jetzt das von euch gew\u00fcnschte Bild <code>image.jpg<\/code>.\u00a0 Nat\u00fcrlich k\u00f6nnt ihr zwischen die Anf\u00fchrungszeichen auch einen Pfad zu einem anderen Verzeichnis eintragen. Achtet auf die korrekte Schreibweise: der erste Buchstabe von <code>Image.open<\/code> wird gro\u00df geschrieben.<\/p>\n<h3>1b Bild direkt von der Kamera laden<\/h3>\n<p>Nat\u00fcrlich k\u00f6nnte man erst mit Picamera ein Bild erzeugen und abspeichern um es dann anschlie\u00dfend wie oben wieder zu laden. Ich versuche aber, mit m\u00f6glichst wenigen Speichervorg\u00e4ngen auszukommen, das spart Zeit und stresst die SD Speicherkarte vom Pi nicht so sehr.<\/p>\n<p>Wir m\u00fcssen zus\u00e4tzlich noch die <em>picamera2<\/em> Library installieren. Die alte Library <em>picamera<\/em> ist f\u00fcr Python 3.x obsolet und durch das neue picamera2 Librarymonster ersetzt worden. Leider ist die Doku dazu nicht besonders erhellen und ausf\u00fchrlich. Zudem gibt es nur wenige, abstrakte Beispiele. Ich empfehle hier, ChatGPT zu befragen.<\/p>\n<p>Auf \u00e4lteren Raspis (vor allem dem Raspberry Pi 1B) dauert die Ausf\u00fchrung auf dem Einkern Prozessor recht lange, also nicht die Geduld verlieren.<\/p>\n<pre class=\"expand:true lang:python decode:true\">#!\/usr\/bin\/python\r\n# coding=UTF-8\r\n\r\n#########################\r\n# portiert auf Python 3.x\r\n#########################\r\n\r\nfrom picamera2 import Picamera2\r\nfrom PIL import Image\r\n\r\npicam2 = Picamera2()                         # erzeuge Objekt\r\nconfig = picam2.create_still_configuration() #Voreinstellung f\u00fcr Fotos\r\npicam2.configure(config)                     #Konfiguration anwenden\r\n\r\npicam2.start()\r\nimage_array = picam2.capture_array()         # foto aufnehmen und in Array laden\r\npicam2.stop()\r\n\r\n# weitere Bildverarbeitung\r\nimg = Image.fromarray(image_array)           # img Objekt erzeugen\r\nimg= img.convert(\"RGB\")                      # in RGB konvertieren\r\n# in img steckt das Bild das weiter verarbeiten wollen\r\n<\/pre>\n<p>Auch hier enth\u00e4lt <code>img<\/code> das gew\u00fcnschte Imageobjekt, welches wir nun mit PIL weiterverarbeiten k\u00f6nnen:<\/p>\n<h3>Bild beschneiden<\/h3>\n<p>Das geschieht ganz einfach mit \"crop\".<\/p>\n<p>\u00dcberlegt euch, welches Rechteck ihr aus dem Bild behalten wollt, der Rest fliegt raus. Ursprung des Koordinatensystems ist mit [0,0] links oben.<\/p>\n<pre class=\"lang:python decode:true\">#crop\r\nbox=[0,60,2400,1860]\r\nimg=img.crop(box)<\/pre>\n<p>Die ersten beiden Werte des 4er Tupels <code>box<\/code> 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.<\/p>\n<p><code>img<\/code> eth\u00e4lt jetzt das beschnittene Bild.<\/p>\n<h3>\u00a0Aufl\u00f6sung \u00e4ndern<\/h3>\n<p>Jetzt wollen wir das f\u00fcr Webseiten wahrscheinlich zu gro\u00dfe Bild resizen: Das Breiten-\/H\u00f6henverh\u00e4ltnis k\u00f6nnt ihr dabei frei w\u00e4hlen und so auch ein Bild verzerren. Will man das Seitenverh\u00e4ltnis beibehalten, muss man etwas rechnen:<\/p>\n<p style=\"padding-left: 30px;\">Unser Originalbild\u00a0 aus der Kamera hatte ein Breiten-\/H\u00f6henverh\u00e4ltnis von 4 zu 3. Ebenso &#8211; aber eher zuf\u00e4llig &#8211; hat unser beschnittenes Bild dasselbe Seitenverh\u00e4ltnis (2400\/1800 entspricht 4\/3).<br \/>\nWollen wir das Bild auf eine Breite von 1024 Pixeln verkleinern, muss die H\u00f6he 3\/4 dieser Wertes, also 768 Pixel betragen<\/p>\n<pre class=\"lang:python decode:true\"># resize\r\nsize=[1024, 768]\r\nimg=img.resize(size)<\/pre>\n<p>Einfach, oder? PIL h\u00e4lt auch noch Filterparameter bereit, mit dem das Umrechnungsverfahren beeinflusst werden kann. Wer tiefer graben will kann sich <a href=\"http:\/\/www.effbot.org\/imagingbook\/image.htm#tag-Image.Image.resize\">hier<\/a> infomieren.<\/p>\n<p><code>img<\/code> eth\u00e4lt jetzt das klein gerechnete Bild.<\/p>\n<h3>Bild beschriften<\/h3>\n<p>Hierzu brauchen wir auch noch die beiden PIL Module <em>Imagefont<\/em> und <em>ImageDraw<\/em>. In unser Python Programm m\u00fcssen wir oben noch mit<\/p>\n<pre class=\"lang:python decode:true\">from PIL import ImageFont\r\nfrom PIL import ImageDraw<\/pre>\n<p>die entsprechenden Libraries importieren. Dann \u00fcberlegen wir uns zum Ausprobieren einen Text, den wir ins Bild schreiben wollen:<\/p>\n<pre class=\"lang:python decode:true \">text1=\"This is the overlay text\"<\/pre>\n<p>und wir definieren die Schriftart:<\/p>\n<pre class=\"expand:true lang:python decode:true\">font_normal = ImageFont.truetype(\"\/usr\/share\/fonts\/truetype\/dejavu\/DejaVuSans.ttf\",21)<\/pre>\n<p>Dieser Font ist einer von vielen, die standardm\u00e4\u00dfig auf dem Raspberry Pi installiert sind.<\/p>\n<p style=\"padding-left: 30px;\"><span style=\"color: #808080;\">Wer etwas herumspielen will: eine \u00dcbersicht der installierten Fonts gibt es mit dem Shell Kommando <\/span><\/p>\n<pre class=\"lang:sh decode:1 inline:1 \">fc-list<\/pre>\n<p>bzw.<\/p>\n<pre class=\"lang:sh decode:1 inline:1 \">fc-list|less<\/pre>\n<p>sollte die Liste zu schnell durchrauschen.<\/p>\n<p>Die Zahl am Ende der Font Zeile (hier 21) sagt, welche Fontgr\u00f6\u00dfe verwendet werden soll. Anschlie\u00dfend wird das Draw Objekt erzeugt und in dieses der Text mit den Farbattributen und dem definierten Font hineingeschrieben.<\/p>\n<pre class=\"lang:python decode:true\">draw = ImageDraw.Draw(img)\r\ndraw.text((10, 40),text1,(255,255,255),font=font_normal)<\/pre>\n<p>(10,40) steht f\u00fcr die Position der linken obere Ecke des Textes im Bild.<br \/>\n(255,255,255) steht f\u00fcr die Farbe wei\u00df.<br \/>\nLeider kann ImageDraw keine Zeilenumbr\u00fcche. Man muss also jede Zeile einzeln positionieren oder einen Text variabler L\u00e4nge z.B. mit <code>splitlines<\/code> aufbrechen und dann in einer Schleife jede Zeile an ihre Stelle schreiben.<\/p>\n<p style=\"padding-left: 30px;\">\u00a0<span style=\"color: #808080;\">Die picamera Library hat auch eine etwas rudiment\u00e4re M\u00f6glichkeit mit dem Attribut annotate_text ein Bild zu beschriften. PIL bietet aber wesentlich mehr M\u00f6glichkeiten.<\/span><\/p>\n<h3>Speichern<\/h3>\n<p>Auch das ist ganz easy:<\/p>\n<pre class=\"lang:python decode:true\">#save\r\nimg.save(\"\/var\/www\/html\/new.jpg\")\r\n<\/pre>\n<p>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\u00e4gt:<\/p>\n<pre class=\"lang:sh decode:true \">http:\/\/192.168.178.100\/new.jpg<\/pre>\n<p>Das setzt nat\u00fcrlich voraus, dass ein Webserver (ich empfehle f\u00fcr den Pi den Webserver Lighttpd) installiert ist und der User, mit dem ihr gerade angemeldet seid, Schreibrechte auf \/var\/www\/html hat. Nat\u00fcrlich k\u00f6nnt ihr auch jeden anderen Pfad nehmen.<\/p>\n<pre class=\"lang:default decode:true \" title=\"User Pi f\u00fcr Webserververzeichnis zulassen\">sudo usermod -aG www-data pi       # User Pi der Gruppe des Webservers www-data hinzuf\u00fcgen\r\nsudo chmod -R 775 \/var\/www\/html    # Schreibrechte f\u00fcr die Gruppenmitglieder setzen<\/pre>\n<p>&nbsp;<\/p>\n<h3>Das ganze Programm<\/h3>\n<pre class=\"lang:python decode:true\">#!\/usr\/bin\/python\r\n# coding=UTF-8\r\n\r\n#################################################\r\n# portiert auf Python 3.x mit Raspbian \"bookworm\"\r\n#################################################\r\n\r\nfrom picamera2 import Picamera2\r\nfrom PIL import Image\r\nfrom PIL import ImageFont\r\nfrom PIL import ImageDraw\r\n\r\npicam2 = Picamera2()                         # erzeuge Objekt\r\nconfig = picam2.create_still_configuration() #Voreinstellung f\u00fcr Fotos\r\npicam2.configure(config)                     #Konfiguration anwenden\r\n\r\npicam2.start()\r\nimage_array = picam2.capture_array()         # foto aufnehmen und in Array laden\r\npicam2.stop()\r\n\r\n# weitere Bildverarbeitung\r\nimg = Image.fromarray(image_array)           # img Objekt erzeugen\r\nimg= img.convert(\"RGB\")                      # in RGB konvertieren\r\n# in img steckt das Bild das weiter verarbeiten wollen\r\n\r\n#crop\r\nbox=[0,60,2400,1860]\r\nimg=img.crop(box)\r\n# resize\r\nsize=[1024, 768]\r\nimg=img.resize(size)\r\n# annotate\r\ntext1=\"This is the overlay text\"\r\nfont_normal = ImageFont.truetype(\"\/usr\/share\/fonts\/truetype\/dejavu\/DejaVuSans.ttf\",21)\r\ndraw = ImageDraw.Draw(img)\r\ndraw.text((10, 40),text1,(255,255,255),font=font_normal)\r\n#save\r\nimg.save(\"\/var\/www\/html\/new.jpg\")\r\n<\/pre>\n<p>Bei meiner Webcam sieht das Ergebnis dann so aus: <a href=\"http:\/\/rusticam.eu\">http:\/\/rusticam.eu<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Nicht immer will man die Bilder, welche die Webcam aufnimmt, genauso im Web anzeigen, wie sie aufgenommen wurden; z.B. soll der Ausschnitt ver\u00e4ndert werden oder die Aufl\u00f6sung. Auch will man vielleicht das Webcam Image noch beschriften, z.B. mit den Wetterdaten aus meinem Post Wetterbericht und Astrodaten auslesen. Wie immer f\u00fchren viele Wege nach Rom. Ich &hellip; <a href=\"https:\/\/www.rustimation.eu\/index.php\/image-manipulation-mit-pil-bilddateien-veraendern\/\" class=\"more-link\"><span class=\"screen-reader-text\">Image Manipulation &#8211; mit PIL Bilddateien ver\u00e4ndern<\/span> weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,9,10],"tags":[16,35,38],"class_list":["post-301","post","type-post","status-publish","format-standard","hentry","category-kamera","category-python","category-raspberry-pi","tag-crop-beschneiden","tag-python-image-library","tag-resize-groesse-aendern"],"_links":{"self":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts\/301","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/comments?post=301"}],"version-history":[{"count":1,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts\/301\/revisions"}],"predecessor-version":[{"id":3374,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/posts\/301\/revisions\/3374"}],"wp:attachment":[{"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/media?parent=301"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/categories?post=301"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rustimation.eu\/index.php\/wp-json\/wp\/v2\/tags?post=301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}