![]() |
Webcam am Linux-Rechnervon Prof. Jürgen Plate, Michael Prechtl |
Nun fehlt nur noch die entsprechende Software, um die Devices nutzen zu können. Hat man Debian, Ubuntu oder Mint und eine Webcam, dann kann man mit einfachen Mitteln Snapshots oder Streams von der Kamera holen. Das gilt auch für den Raspberry Pi, sofern die Kamera unterstützt wird. Generell sollte man wie folgt vorgehen.
# dmesg ... [ 12.621009] Linux video capture interface: v2.00 [ 12.685253] uvcvideo: Found UVC 1.00 device USB 2.0 Camera (0c45:6340) [ 12.712522] input: USB 2.0 Camera as /devices/pci0000:00/0000:00:1d.7/usb1/1-1/1-1:1.0/input/input8 [ 12.713513] uvcvideo: Found UVC 1.00 device HD 720P Webcam (0603:8f01) [ 12.718307] input: HD 720P Webcam as /devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4:1.0/input/input9 [ 12.718700] usbcore: registered new interface driver uvcvideo [ 12.718709] USB Video Class driver (1.1.1) ...In unserem Fall sind es zwar zwei verschiedene Kameras, aber bei beiden der Treiber uvcvideo. Falls der Befehl dmesg keine derartige Meldung zeigt, kann man mittels lsmod die Liste aller geladenen Module ansehen und dort den zu der Webcam passenden Treiber herausfinden. Dazu die Webcam abstecken, lsmod aufrufen und die Ausgabe ansehen. Dann die Webcam anstecken und nochmals lsmod aufrufen. Das neu aufgetauchte Modul ist der Treiber der Webcam.
Achtung: Falls man einen Laptop mit eingebauter Webcam besitzt sind eventuell zwei Module für Webcams geladen (außer interne und externe Webcam haben den gleiche Chipsatz).
Sieht man etwas Änliches wie oben, kann man zufrieden sein (insbesondere scheint UVC inzwischen zuverlässig zu funktionieren - und es wird kein spezieller Treiber benötigt). Es wird eine Kamera erkannt und über input0 angebunden. Das trifft inzwischen auf eine ganze Reihe von Modellen zu. Normalerweise wird auch das Device /dev/video0 (bzw. /dev/video1 ...) installiert, über das die Kamera angesprochen wird. Zur Sicherheit (oder etwa vor einem Kauf) sollte man den Namen der Webcam googeln und nachsehen, ob in den techischen Daten etwas von UVC steht.
apt-get install fswebcam gpicview fbi fswebcam -v -r "640×480" test.jpg gpicview test.jpgIm Beispiel entsteht ein Bild namens test.jpg im aktuellen Verzeichnis und wird anschliessend angesehen (640x480 Pixel sollte jede Kamera können). Hat der Rechner keinen X-Server laufen und kein grafisches Interface, dann kann das Bild mit dem "Frame Buffer Imager" fbi betrachtet werden:
fbi test.jpgLetzteres geht natürlich nur auf einer realen Console am Rechner und nicht über eine ssh-Verbindung o. ä.
Unterstützt das Gerät V4L/V4L2 und eben UVC, so gibt es eine Reihe von weiteren Tools für die Weiterverarbeitung der Bilder, z. B. das motion-Paket oder auch luvcview. Mein aktuelles Projekt überträgt nur Einzelbilder zu bestimmten Zeiten (ffmpeg fuer einen Stream wird daher nicht benötigt).
Das eingesetzte Tool fswebcam ist ein kleines und ausgefeiltes Webcam-Programm. Es kann eine Anzahl von Frames von den meisten Geräten lesen, die V4L2-kompatibel sind, deren Mittelwert bilden, um das Rauschen zu reduzieren, und einen Titel auf das Resultat zeichnen. Dazu wird die Grafikbibliothek GD verwendet, die auch die Kompression des Bildes in PNG oder JPEG vornimmt. Letztere ist bei den meisten Distributionen per default installiert, sonst muss man sie nachinstallieren. Homepage: http://www.firestorm.cx/fswebcam/.
Für die Snapshots kann eine Reihe von Einstellungen gesetzt werden. Bei einer Kamera für Aussenaufnahmen können auch proprietäre Parameter der Kamera mittels -s-Option durchgereicht werden (siehe unten). Für Innenaufnahmen ist die Parameterzahl überschaubarer, ein Aufruf könnte lauten:
fswebcam -v -S 5 -F 2 -r "1280x720" –d /dev/video0 –-no-banner snap.jpgDie Optionen des Befehls sind relativ einfach erklärt:
-v "verbose mode": bietet eine detailiertere Ausgabe -F 2 2 Frames speichern -S 5 5 Frames überspringen und erst die folgenden speichern (die Kameras müssen oft erst "einrasten") -r legt die Auflösung des Bildes fest, in unserem Fall HD 1280 x 720 Pixel -d gibt an, welches Device verwendet werden soll, in unserem Fall /dev/video0Zum Schluss wird noch angegeben wie das aufgenommene Bild heißen soll.
Zur S-Option sei noch bemerkt, dass die ersten vorbeisausenden Frames manchmal nicht akzeptabel sind, da eine kurz zuvor eingestellte Kameraempfindlichkeit noch nicht gegriffen hat. Deswegen ist es sinnvoll, einige Frames zu überspringen. Man kann auch noch mittels -D n eine Wartezeit von n Sekunden einschieben.
Hinweis: Wenn die Auflösung nicht mithilfe der Option -r manuell eingestellt wird, nimmt das Programm die Standardauflösung (meistens nicht die optimale Auflösung). In der Regel beträgt die Standardauflösung 352 x 288 Pixel. Man erhält aber viel bessere Bilder wenn man als Auflösung z. B. 800 x 600 Pixel angibt. Oft ist aber auch die maximal mögliche Auflösung (z. B. 1200 x 1600) auch nicht optimal und man fährt mit einer etwas geringeren Auflösung besser.
fswebcam kann fast alles selbst, man muss es nur konfigurieren. Dazu gehört beispielsweise auch das Einfangen eines Bildes in regelmäßigen Zeitabständen. Persönlich neige ich jedoch dazu, solche Aufgaben dem cron-Dämon zu überlassen. Interessant ist u. a. die Möglichkeit, das Bild automatisch mit einer Beschriftung am unteren oder oberen Rand zu versehen, wobei Hintergrund- und Schriftfarbe, Transparenz, Schriftgröße usw. frei wählbar sind. Standardmäßig erfolgt die Beschriftung am unteren Bildrand. Folgende Optionen sind möglich:
--title "xxx" Titel (1. Zeile links) --subtitle "xxx" Untertitel (2. Zeile links) --timestamp "xxx" Datum/Uhrzeit (1. Zeile rechts), z. B. "%d.%m.%Y %H:%M" --info "xxx" Zusatzinfo (2. Zeile rechts)Die Timestamp-Option beherrscht alle Formen der Zeitformatierung, die von der Funktion strftime() unterstützt werden. Wenn kein Font gefunden wird (Fehlermeldung von fswebcam) muss der Pfad zu einem Font angegeben werden, z. B.:
--font "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono-Bold.ttf:12"
Die Option --flip <richtung[,richtung]> erlaubt das (h)orizontale oder (v)ertikale Spiegeln des Bildes (etwa bei Deckenmontage der Kamera). Zum Beispiel --flip v oder --flip h,v. --rotate www rotiert das Bild um den angegebenen Winkel www, wobei nur die Werte 90, 180 und 270 möglich sind.
--crop <groesse[,offset]> erlaubt das Herstellen von Bildausschnitten. Beide Angaben erfolgen in der Form BxH (Breite mal Höhe). So extrahiert --crop 320x240 einen zentralen Ausschnitt von 320x240 Pixel Größe. --crop 100x100,0x0 produziert dagegen einen 100 x 100 Pixel großen Ausschnitt der linken oberen Ecke.
Mittels --scale <groesse> kann das Bild auf die angegebene Größe skaliert werden. Die Angabe erfolgt wieder in der Form BxH (Breite mal Höhe), z. B. --scale 640x480.
Weitere Optionen sind in der ausführlichen Manual-Page des Programms aufgeführt.
Damit die Kommandozeile nicht zu unübersichtlich wird, kann diese aufgeteilt werden, wie folgendes Beispiel zeigt (Achtung: Nach dem '\' muss das Zeilenende kommen; kein weiteres Zeichen!):
fswebcam -D 3 -S 10 -F 10 -r 1280x720 -d /dev/video0 \ --font "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono-Bold.ttf:12" \ --title "Kamera 1" --subtitle "http://www.netzmafia.de" \ --timestamp "%d.%m.%Y %H:%M" --info "Zusatzinfo" $FILEFür Titel, Untertitel, Info etc. können auch variable Werte verwendet werden, beispielsweise durch Übergabe in Shell-Variablen. Wenn es keine variablen Parameter gibt, die sich bei jedem Aufruf ändern, kann die gesamte Konfiguration auch in einer Datei untergebracht werden, was den Aufruf auf der Kommandozeile entsprechend verkürzt.
fswebcam -d /dev/video0 --list-controls --- Opening /dev/video0... Trying source module v4l2... /dev/video0 opened. No input was specified, using the first. Available Controls Current Value Range ------------------ ------------- ----- Brightness 10 (57%) -64 - 64 Contrast 21 (32%) 0 - 64 Saturation 64 (49%) 1 - 128 Hue 0 (50%) -40 - 40 Gamma 72 (0%) 72 - 500 Gain 0 (0%) 0 - 100 Power Line Frequency 50 Hz Disabled | 50 Hz | 60 Hz Sharpness 4 0 - 6 Backlight Compensation 1 0 - 2 Adjusting resolution from 384x288 to 352x288. --- Capturing frame... Captured frame in 0.00 seconds. --- Processing captured image... There are unsaved changes to the image.
fswebcam -d /dev/video1 --list-controls --- Opening /dev/video1... Trying source module v4l2... /dev/video1 opened. No input was specified, using the first. Available Controls Current Value Range ------------------ ------------- ----- Brightness -128 (0%) -128 - 127 Contrast 124 (32%) 60 - 255 Saturation 70 (35%) 0 - 200 Hue 5 (52%) -128 - 127 White Balance Temperature, Auto True True | False Gamma 9 0 - 10 Power Line Frequency 50 Hz Disabled | 50 Hz | 60 Hz White Balance Temperature 4500 (45%) 2800 - 6500 Sharpness 150 (75%) 0 - 200 Backlight Compensation 6 0 - 10 Adjusting resolution from 384x288 to 432x240. --- Capturing frame... Captured frame in 0.00 seconds. --- Processing captured image... There are unsaved changes to the image.Man sieht schon im Vergleich, dass die numerische Spannweite sehr unterschiedlich ist (z. B. reicht die Helligkeit bei der "UA0155" von -64 bis +64, bei der "Digital Eye II" jedoch von -128 bis +127).
Um der Gefahr zu entgehen, gegebenenfalls falsche Werte zu übergeben, erlaubt fswebcam in jedem Fall auch Prozentangaben. Wird z. B. die Helligkeit mit 20% angegeben, rechnet das Programm den entsprechenden Zahlenwert aus (gerundet auf ganze Zahlen). die 20% ergäben dann -51 (UA0155) oder -77 (Digital Eye II).
Die Einstellung erfolgt über den Parameter -s und immer in der Form "Bezeichnung=Wert", zum Beispiel:
... -s "Backlight Compensation=75%" \ -s "Brightness=35%" \ -s "Contrast=45%" \ ...
Getestet wurden noch zwei weitere Kameras: Die Logitech C270 USB Webcam und die Microsoft LifeCam HD-3000. Beide Kameras liefen sofort unter Linux, obwohl in den technischen Daten (wie üblich) immer nur Windows als Betriebssystem angegeben war. Beide Kameras haben HD-Auflösung (1280 x 720 Pixel) und ein integriertes Mikrofon mit Rauschunterdrückung. Die Logitech C270 erlaubt daneben noch Fotos, die per Software-Interpolation etwa einer 3-Megapixel-Kamera entsprechen (Windows-Software-Version). Beim Capturen von Bildern meldete fswebcam zwar eine kleine Unsauberkeit bei den angelieferten Daten, konnte aber ein korrektes JPG-Bild abspeichern. Bei schwacher Beleuchtung wirken die Bilder aber etwas flau.
Eine Überraschung bot die Microsoft LifeCam HD-3000. Abgesehen davon, dass sie unter Linux das gewünschte leistete, lieferte sie auch noch die besten Bilder von allen vier USB-Kameras. Außerdem kann sie auch noch 16:9-Breitbildformat für Videoaufzeichnungen im Kinoformat. Des Weiteren darf jegliche Helligkeitsanpassung entfallen, denn die sogenannte TrueColor-Technologie sorgt automatisch für kräftige Farben bei nahezu allen Lichtverhältnissen. Die Software-Interpolation entspricht bei der HD-300 sogar einer 4-Megapixel-Kamera. Insgesamt eine angenehme Überraschung.
www.axis.com/products/
www.trendnet.com/products/
www.logilink.eu/showproduct/WC0040.htm
www.grand.com.tw/sur_mega_pixel.php
Die ersten drei Kameras liefern ein Farbbild von maximal 640 x 480 Pixeln (sind also nicht mehr die neuesten Modelle), die Grandtech schafft 1280 x 1024 Pixel. An dieser Stelle kommt es jedoch nicht so sehr auf die Auflösung oder andere Kameraeigenschaften an, sondern die vier Modelle sollen einfach die Möglichkeiten illustrieren, wie auf solche Kameras zugegriffen wird. Oft lassen sich die Methoden dann auf andere Modelle übertragen.
Über ein Webinterface oder ein mitgeliefertes Windows-Tool sind nicht nur der Videostrom oder Einzelbilder abrufbar, sondern es lassen sich auch beliebige Einstellungen vornehmen. Bei Inbetriebnahme muss die Kamera zunächst eine IP-Adresse erhalten. Bei der Gelegenheit kann man auch gleich die anderen Grundeinstellungen vornehmen. Dabei sollten die Kameras selbst nie direkt "von aussen" erreichbar sein, sondern deren Bilder über einen lokalen Webserver gefiltert werden. Beim Abgreifen der Bilder wird die Kamera natürlich über das Netzwerk angesprochen. Zum Herunterladen der Bilder können auf der Kommandozeile die Programme wget oder curl verwendet werden.
Die Axis-Kamera hat ein Fixfocus-Objektiv, bei den anderen muss man die Schärfe manuell regulieren.
Bei der Trendnet TV-IP100 war etwas Forschungsarbeit nötig, da per Default das Abliefern der Bilder nur per E-Mail oder FTP offeriert wird. Es ging dann doch recht einfach: beim Browser wird Ansicht → Seitenquelltext aufgerufen und im HTML-Wust nach einem IMG-Tag gesucht. Es präsentiert sich hübsch verpackt in einer Tabelle, aber mit etwas seltsamer Dateiangabe, z. B:
<IMG SRC="IMAGE.JPG?cidx=2009117184335312242" BORDER="0">Der Schwanz "?cidx=200911..." ist eigentlich unnötig, es handelt sich um einen Trick der Kamerasoftware, um die Browser auszutricksen: da die Webseite immer gleich aussieht, würde ein schlauer Browser beim Klicken auf "Reload" nicht das aktuelle Bild der Kamera abrufen, sondern das im Browser-Cache befindliche abliefern. Durch das Anhängen einer Pseudo-Formulareingabe mit sich ständig ändernden Werten wird dafür gesorgt, dass immer das aktuelle Bild geholt wird. Bei der Trendnet TV-IP100 erhält man bei folgendem Aufruf ein Bild und nichts sonst:
curl -o snap.jpg http://192.168.2.100/IMAGE.JPG
Bei der Logilink WC0040 funktioniert der Abruf eines Standbildes genauso einfach, im Beispiel mit User- und Passwortangabe:
curl -o snap.jpg http://user:pass@192.168.2.102/snapshot.jpgÄhnlich einfach kann hier übrigens auch eim MPEG-Stream abgerufen werden.
Die Axis-Kamera macht etwas Ähnliches wie die Trendnet. Sie liefert das Bild scheinbar über ein CGI-Script aus, was ebenfalls dafür sorgt, dass sich der Browser immer ein frisches Bild holt. Bei der Axis 207 können Parameter wie Helligkeit, Bildgröße usw. mitgegeben werden. Im folgenden Beispiel gebe ich die Bildgröße vor:
curl -o snap.jpg http://192.168.2.101/axis-cgi/jpg/image.cgi?resolution=640x480Es steht zwar nicht im Handbuch, aber wenn man nur ein Bild ohne Einstellungen will, genügt auch:
curl -o snap.jpg http://192.168.2.101/axis-cgi/jpg/image.jpg(Für Video verwenden Sie stattdessen .../mjpg/video.mjpg.)
Die "Grandtec Megapixel IP Camera" sträubte sich hingegen etwas, obwohl die Reklame ausdrücklich auch die Linux-Kompatibilität aufführte. Deren Windows-Software (zu Linux gab's nix) kommt mit massenhaft Active X und dererlei Gefickel. Etwas Forschungsarbeit liess aber den Verdacht aufkommen, dass bei der Kamera "unter der Haube" ein ARM-Linux werkelte - wenn auch mit gewöhnungsbedingtem Interface.
Jedenfalls fand sich innerhalb des Mini-Werservers, der auf der Kamera lief, auch ein Verzeichnis namens /cgi-bin mit einigen vielversprechenden Kommandos. Leider zeigte sich, dass auch ein Kommando namens still.cgi keineswegs ein Standbild lieferte, sondern einen MJPEG-Stream. Immerhin ohne Active X oder anderen Dingen, aber mit Abfrage von Username (immer "root") und Passwort. Darauf ließ sich aufbauen. Mit dem Programm curl (oder auch wget) kann das Video geladen und als JPG-Datei gespeichert werden. curl wurde eingesetzt, weil sich hier im Gegensatz zu wget die Downloadzeit begrenzen läßt. Also fischt man sich eine kurze Videsequenz heraus, aus der dann mittels avconv, das früher ffmpeg hieß, ein Frame extrahiert wird - und schon hat man ein Standbild:
# Kamerabild (Stream) holen curl -o video.jpg -m 3 http://root:geheim@192.168.2.103/cgi-bin/still.cgi # ein Bild extrahieren mit avconv (früher ffmpeg) avconv -i video.jpg -s 1280x1024 snap.jpg rm video.jpgSie sehen, ich gibt immer eine Möglichkeit, eine störrische Netzwerk-Kamera zu zähmen.
Für einen eindeutigen Dateinamen eines jeden Bildes werden Datum und Uhrzeit herangezogen. Dann wird auch noch das Bild mit Datum und Uhrzeit beschriftet - je nach Bildhelligkeit schwarz oder weiss.
# Timestamp fuer Bild TS=$(date "+%d.%m.%Y %H:%M") # Timestamp fuer Dateinamen FILE=$(date "+%d%m%Y%H%M") # abhaengig von der Helligkeit Schrift schwarz oder weiss einfuegen # Funktion Brightness siehe unten (Vergleich "groesser" wegen Korrekturwert) col=$(Brightness snap.jpg) if [ $col -gt 50 ] then convert -fill white -gravity SouthEast -pointsize 20 \ -draw "text 15,15 '$TS'" snap.jpg ${FILE}.jpg else convert -fill black -gravity SouthEast -pointsize 20 \ -draw "text 15,15 '$TS'" snap.jpg ${FILE}.jpg fi rm snap.jpgDie Größe eines Bildes liegt, je nach Auflösung, um die 30 bis 150 KByte. Ein Tag hat 86.400 Sekunden, ein Monat (30 Tage) 2.592.000 Sekunden. Wenn Sie alle 10 Sekunden ein Bild speichern, um später einen Zeitraffer-Film daraus zu machen, sind das ungefähr 7,8 bis 39 GByte pro Monat. Also auf den Plattenplatz achten und rechzeitig alte Dateien löschen.
convert ist ja ein Tausendsassa, so kann man das Bild auch gleich noch verkleinern und mit höherer Kompressionsrate (aber geringerer Qualität) abspeichern, um Platz zu sparen. Oder man macht Schwarzweiss-Bilder daraus, oder, oder ...
# Kamerabild holen, Helligkeit bestimmen fswebcam -q -D 3 -S 10 -F 10 -r 1280x720 -d $KAMERA \ -s "Backlight Compensation=75%" \ --no-banner $TMP/$FILE HELL=$(Brightness $TMP/$FILE) # Kamerabild holen, Helligkeit anpassen fswebcam -D 3 -S 10 -F 10 -r 1280x720 -d $KAMERA \ -s "Backlight Compensation=75%" \ -s "Brightness=${HELL}%" \ -s "Contrast=45%" \ ... $TMP/$FILEDie Shell-Funktion Brightness (siehe unten) liefert den Helligkeitswert in Prozent. Er ergibt sich zu 100 - (ermittelte Helligkeit) [Prozent]. Ist das Bild recht dunkel, wird der Wert für den Kamera-Parameter Brightness relativ hoch gesetzt und umgekehrt.
Wie ermittelt man eigentlich die Helligkeit eines Bildes? Mathematisch kann die Bildhelligkeit eines Grauwertbildes als Mittelwert aller Grauwerte, der Kontrast als Varianz aller Grauwerte definiert werden. Die einfachste Möglichkeit zur Bestimmung der Helligkeit eines Pixels (bzw. dessen Grauwert) ergibt sich daher zu (R+G+B)/3, wobei R, G und B die Werte für die drei Grundfarben Rot, Grün und Blau sind. Da jede Farbe einen Wert zwischen 0 und 255 haben kann, ergibt sich der gleiche Bereich für den Helligkeitswert. Die Gesamthelligkeit des Bildes errechnet sich aus der Helligkeit aller Bildpunkte, wie es das folgende C-Programmfragment illustriert (t_img sei eine Struktur, die alle Bildinfo enthält):
int brightness(t_img *bild) { int x, y, br, avg; t_color val; double sum; for(x = 0; x < bild->xsize; x++) { for(y = 0; y < bild->ysize; y++) { getpixel(bild, x, y, &val); grau = (val.R + val.G + val.B)/3; sum += grau; } } avg = sum / (bild->xsize * bild->ysize); return avg; }Nur lässt diese Form der Helligkeitsbestimmung die Wahrnehmungseigenschaften unsers Auges außer Acht. Wir nehmen den gelb-grünen Farbbereich wesentlich heller wahr als die anderen Farben. Das wird in anderen Farbmodellen berücksichtigt. So könnten Sie anstelle der linearen Umrechnung auch den RGB-Farbraum nach YUV umrechen. YUV verwendet zur Darstellung der Farbinformation zwei Komponenten, die Luminanz Y und die Chrominanz (Farbanteil), wobei diese wiederum aus den zwei Unterkomponenten U und V besteht:
y = r * 0.299 + g * 0.587 + b * 0.114
u = (b - y) * 0.493
v = (r - y) * 0.877
Für die Helligkeitsbestimmung wird nur der Y-Wert benötigt. Zum Glück kann man die Arbeit dem Universaltool ImageMagick überlassen und man muss nicht selbst programmieren. Das Programm convert kann nicht nur alle möglichen Bildmanipulationen vornehmen, sondern auch diverse Infos über ein Bild liefern. Das Problem ist eher, aus dem Datenwust den richtigen Wert herauszufischen. Dazu wird das Bild erst in Graustufen umgewandelt. Damit hat schon jeder Bildpunkt nicht mehr drei, sondern nur noch einen Helligkeitswert. Dann wird mittels sed der Helligkeitswert ("Mean") herausgefischt. Diser Wert liegt zwischen 0 und 1 und muss mit 100 multipliziert werden, um eine Prozentangabe zu bekommen. Der Prozentwert wird dann noch von 100 subtrahiert (siehe oben). Das erledigt bc, der Kommandozeilen-Rechner. Zum Schluss werden noch die Nachkommastellen abgeschnitten:
Brightness() { # Bildhelligkeit des Schriftfeldes ermitteln (Prozent, ganzzahlig) # Helligkeit von $1 ermitteln local data=`convert $1 -colorspace gray -verbose info: ` local mean=`echo "$data" | sed -n '/^.*[Mm]ean:.*[(]\([0-9.]*\).*$/{ s//\1/; p; q; }'` echo "100-$mean*100" | bc | sed -e 's/\..*$//' }
Eine sehr einfache Methode Bewegungen zu erkennen ist, zwei zeitlich aufeinander folgende Bilder zu vergleichen, um die Änderung von Pixeln zu erkennen. Überschreitet die Zahl (oder zusätzlich der Betrag) der Änderungen einen Schwellenwert, wird beispielsweise ein Alarm ausgelöst. Bei statischen Motiven und konstanter Beleuchtung genügt eventuell auch der Vergleich mit einem Referenzbild. Wenn man in diesem Fall das aktuelle Bild vom Referenzbild subtrahiert, liefern alle Pixel, die sich nicht verändern haben, das Ergebnis Null zurück (ein schwarzes Pixel). Dieses Verfahren funktioniert ganz gut, jedoch stösst es bei Beleuchtungsänderung und bei kleinen Veränderungen im Bild an seine Grenzen.
Hebt sich das bewegende Objekt vom Hintergrund ab (ein sich vor der Kamera bewegendes Objekt mit einer weissen Wand dahinter), ist auch die langsamste Bewegung detektierbar. Variiert der Hintergrund sehr stark und erfolgt die Bewegung sehr langsam, kann man die Bewegungserkennung austricksen. Bei Aussenaufnahmen kommen noch Effekte wie vom Wind bewegte Äste oder die Katze vom Nachbarn hinzu. Der Stubentiger soll ja im Gegensatz zu einem Einbrecher keinen Alarm auslösen. In solchen Fällen ist es günstig, nur einen Teilausschnitt des Bildes zu untersuchen.
Das Prinzip des Differenz-Algorithmus zeigt das folgende Programmfragment. Die Funktion changed liefert die Anzahl der Pixel zurück, die sich jeweils in beiden Bildern um mehr als den Wert diff unterscheiden (t_img ist wieder Struktur, die alle Bildinfo enthält):
int changed(t_img *bild1, t_img *bild2, int diff) { int y, z, chan, diffcount; t_color val1, val2; /* Zaehler fuer die Unterschiede */ diffcount = 0; /* Iteration ueber alle Pixel (x, y) */ for(x = 0; x < bild1->xsize; x++) { for(y = 0; y < bild1->ysize; y++) { /* akt. Pixel beider Bilder einlesen */ getpixel(bild1, x, y, &val1); getpixel(bild2, x, y, &val2); /* Grauwerte beider Pixel berechnen */ grau1 = (val1.R + val1.G + val1.B)/3; grau2 = (val2.R + val2.G + val2.B)/3; /* ist der Unterschied groesser diff, den Zaehler inkrementieren */ if(abs(grau1 - grau2) > diff) diffcount++; } } return diffcount; }Vergessen Sie auch nicht, dass sich die Bilder mit den Imagemagick-Tools (convert, mogrify) vor- und nachbereiten lassen. Neben diversen Filterfunktionen lassen sich auch Ober- und Untergrenzen für die Helligkeit angeben, bei deren Über- bzw. Unterschreitung die Pixel weiß bzw. schwarz eingefärbt werden.
Sehr viel ausgefeilter kann das alles aber ein Programm zur Bewegungs-Erkennung (Motion Detection, MD). Bei manchen Kameras ist MD sogar in der Kamera selbst implementiert. Oft wird auch ein Tools für Windows mit der Webcam geliefert. Natürlich gibt es dergleichen auch für Linux. Einer der bekanntesten Vertreter dieser Programmgattung kann auch wieder über die Kommandozeile gestartet werden.
Das Programm motion empfängt nonstop Bilder von beliebig vielen Webcams oder Netzwerkkameras. Ändert sich dabei von einem Bild zum anderen eine definierte Anzahl Pixel, postuliert das Programm, dass sich etwas im zu überwachenden Bereich bewegt. In diesem Fall nimmt motion mit Hilfe von ffmpeg einen Videostream oder eine Serie von Einzelbildern auf und speichert sie auf einem Server. Des Weiteren ist es möglich, Bereiche in Bildern zu maskieren, um Bewegungen darin zu ignorieren. Letzteres hilft vor allem dann, wenn sich beispielsweise Bäume oder Sträucher im Erfassungsbereich befinden, die im Wind schaukeln - oder eben die Katze, die ihr Revier kontrolliert.
Neben der Funktion als Bewegungsmelder eignet sich motion auch, um innerhalb bestimmter Intervalle Schnappschüsse zu speichern oder fortlaufend Videos aufzunehmen. Das Werkzeug verfügt zudem über eine Möglichkeit, die aktuell empfangenen Bilder von überall mit einem Browser abzurufen. Bei der Installation von motion wird auch eine Gruppe gleichen Namens erzeugt. Alle Nutzer, die motion verwenden, müssen zu dieser Gruppe hinzugefügt werden, damit die Konfigurationsdatei von motion gelesen werden kann.
In der Konfigurationsdatei /etc/motion/motion.conf lassen sich zahlreiche Einstellungen vornehmen. Die wichtigsten für erste Versuche sind:
Falls das nicht erfolgreich ist, läßt sich der Bildausschnitt maskieren. Dazu erzeugt man mit dem Grafikprogramm seiner Wahl ein Bild von der Größe des Kamerabildes (Angabe von width und height. Das Bild hat nur schwarze und weiße Flächen. Alles was schwarz ist, wird ausgeblendet. Im einfachsten Fall wäre dies also ein einfacher schwarzer Rahmen. Die Datei wird dann im Format "pgm" (Portable Gray Map) gespeichert. In der Konfigurationsdatei tragen Sie nun eine weitere Zeile ein:
mask_file /pfad/zur/maskendatei.pgm
Ein weiteres Feature von motion ist, dass man auf bestimmte Ereignisse reagieren kann. Mit der Konfigurationsoption on_event_start wird ein Programm oder Script angegeben, das motion ausführt, sobald eine Bewegung erkannt wurde. Auf diese Weise kann man sich dann Nachricht per SMS, Twitter oder E-Mail zusenden lassen. Für erste Tests können Sie beispielsweise einen Zeitstempel an eine Datei anhängen:
on_event_start 'date "+%d%m%Y%H%M" >> /home/testuser/event'
Weitere Infos enthalten die Homepage von motion und ein Zeitschriftenartikel:
www.lavrsen.dk/foswiki/bin/view/Motion
www.lavrsen.dk/foswiki/bin/view/Motion/FrequentlyAskedQuestions
Objekte mit Motion per Video überwachen
Als Alternative zu "Motion" würde sich noch "Zoneminder" anbieten, der aus etlichen Modulen besteht und über ein Web-Interface bedient wird. Neben Video4Linux für die Unterstützung der Kameras werden ein Apache-Webserver, MySQL, PHP und Perl benötigt. Für die Aufnahme von Stand- und Bewegtbildern kommen noch die die Ffmpeg- und Libjpeg-Pakete hinzu. Der Aufwand gegenüber Motion ist also beträchtlich. Um die wegen der vielen Pakete und Codecs etwas umständliche Installation bzw. Konfiguration zu erleichtern, kann das im Zoneminder-Forum stehende Script heruntergeladen und verwendet werden. Mehr über Zoneminder erfahren Sie auf dessen Homepage www.zoneminder.com. Das Installationsscript findet man unter www.zoneminder.com/forums/viewtopic.php?t=16628 und weitere Informationen für den Einstieg liefert ein Artikel in LinuxUser 09/2011.
#!/usr/bin/perl use strict; use warnings; use Net::FTP; # ------------------ Configuration Section ---------------------------- my $scandir = "/tmp/bilder"; # da liegen die Bilder my $server = "ftp.sonstwo-in.de"; # Adresse des FTP-Servers my $ftpuser = "camuser"; # Benutzername auf dem FTP-Server my $ftppass = "geheim"; # Passwort des FTP-Benutzers # --------------------------------------------------------------------- my (@files,$file); opendir(DIR,$scandir) or die("Verzeichnis nicht lesbar."); @files = grep {(/\.jpg$/) && -f "$scandir/$_"} readdir(DIR); closedir (DIR); exit(0) if ($#files < 0); # keine Bild-Dateien vorhanden # Connect zum Server und Einloggen my $ftp = Net::FTP->new($server, Debug => 0, Passive => 1) or die("Keine Verbindung mit $server."); $ftp->login($ftpuser, $ftppass) or die("Fehler beim Einloggen."); # Uebertragungsmodus 'binaer' $ftp->binary(); for $file(@files) { $file = $scandir . '/' . $file; # Datei senden $ftp->put($file); # lokale Datei loeschen unlink ($file); } # FTP-Verbindung beenden $ftp->quit();Bei FTP werden jedoch alle Daten inklusive Username und Passwort im Klartext übertragen, was mir nicht so sympatisch ist. Besser erscheint mir daher der Transfer per SSH/SCP, wo alles schön verschlüsselt ist. Hier kommt das Perl-Modul Net::SCP::Expect zum Ensatz. Es bildet nicht nur die Schnittstelle zu SCP, sondern kann auch per 'Expect' den User- und Passwort-Dialog automatisieren. Im übrigen gleicht das Programm dem vorhergehenden, beim Senden wird zusätzlich das Zielverzeichnis angegeben:
#!/usr/bin/perl use strict; use warnings; use Net::SCP::Expect; # ------------------ Configuration Section ---------------------------- my $scandir = "/tmp/bilder"; # da liegen die Bilder my $server = 'server.irgendwo-in.de'; # Adresse des SCP-Servers my $scpuser = "camuser"; # Benutzername auf Netzmafia my $scppass = "geheim"; # Passwort des SCP-Benutzers my $destination = "/home/camuser/"; # Zielverzeichnis des Servers # --------------------------------------------------------------------- my (@files,$file); opendir(DIR,$scandir) or die("Verzeichnis nicht lesbar."); @files = grep {(/\.jpg$/) && -f "$scandir/$_"} readdir(DIR); closedir (DIR); exit(0) if ($#files < 0); # keine Bild-Dateien vorhanden # Connect zum Server und Einloggen my $scp = Net::SCP::Expect->new(host => $server, user => $scpuser, password => $scppass) or die("Keine Verbindung mit $server."); for $file(@files) { $file = $scandir . '/' . $file; # Datei senden $scp->scp($file, $destination); # lokale Datei loeschen unlink ($file); } # SCP-Verbindung beenden ist nicht notwendigBeim SCP-Zugriff kann auch ein Login ohne Angabe von User/Passwort erfolgen. Dafür muss auf dem Kamerarechner ein Zertifikat erstellt und auf den Webserver übertragen werden, was mit Hilfe der beiden Kommandos ssh-keygen und ssh-copy-id erledigt werden kann. Zuerst wird ein Schlüssel-Paar (Public- und Private-Key) erstellt und dann der öffentliche Schüssel zum Webserver übertragen.
Der Private-Key ist dann dem normalen Passwort gleichgestellt. Im Gegensatz zum Passwort existiert er aber als Datei, die vor fremdem Zugriff zu schützen ist. Deswegen besteht die Möglichkeit, den Private Key mit einer Passphrase zu schützen. Die Passphrase muss in unserem Fall leer bleiben, da sonst bei Verbindungsaufnahme diese Passphrase abgefragt würde und so kein automatischer Dateitransfer möglich wäre.
Das folgende Shellscript erzeugt die Schlüssel und übertägt sie dann (den Account "camuser@www.netzmafia.de" gibt es natürlich nicht, er dient nur als Beispiel):
#!/bin/bash # Generiert Public/Private-Keys und kopiert sie zur Netzmafia # damit ein Login bzw. Aufbau eines SSH-Tunnels dorthin ohne # Passwort-Eingabe moeglich ist # # zuerst werden die Keys generiert ssh-keygen -t rsa ssh-keygen -t dsa # nun befinden sich im Verzeichnis ~/.ssh vier Dateien: # id_rsa id_dsa id_rsa.pub id_dsa.pub # # nun werden die public keys zur Netzmafia kopiert und dort # an die Datei ~/.ssh/authorized_keys angehaengt ssh-copy-id -i ~/.ssh/id_rsa.pub camuser@www.netzmafia.de ssh-copy-id -i ~/.ssh/id_dsa.pub camuser@www.netzmafia.deAb jetzt kann sich der lokale user vom Cam-Server als "camuser@www.netzmafia.de" ohne Passworteingabe auf Netzmafia einloggen. Der Transfer der Datei(en) kann mit dem Kommandozeilenprogrmm scp erfolgen (oder in Perl mit dem Modul Net::SCP nach obigem Muster).
Wenn es nicht klappen sollte und trotzdem noch ein Passwort angefordert wird, stimmen in fast immer die Zugriffsrechte nicht. Das Verzeichnis .ssh und die Datei authorized_keys darin dürfen nur für den User zugreifbar sein (→ chmod 700 .ssh .ssh/authorized_keys). Das Home-Directory des jeweiligen Users darf auch nur für diesen beschreibbar sein.
Für eine Webcam-Anwendung genügt es, in regelmäßigen Abständen immer dasselbe Bild zu speichern und im passenden Verzeichnis des Webservers abzulegen. Gegebenenfalls müssen Sie zur Überlistung des Browsers verfahren wie die Trendnet-Kamera. Alternativ könnte das aktuelle Bild auch per CGI-Programm abgerufen werden. Auch können Sie eine bestimmte Anzahl von Bildern rotierend speichern (es fällt immer das älteste Bild weg und ein neues kommt hinzu), wie es in folgendem Perl-Listing skizziert wird ($MAX enthält die Anzahl der zu speichernden Bilder), Die Dateien heißen dann 1.jpg, 2.jpg, 3.jpg usw.:
use strict; use warnings; use LWP::Simple; my $MAX = 10; # Maximalzahl Bilder my $file_base = '/var/www/cam/'; # Bilderverzeichnis # z. B. Abruf TV-IP100 my $url = 'http://192.168.2.100/IMAGE.JPG'; ... # Bild-Dateien "rotieren" my $id = $MAX; while (my $id > 1) { my $prev = $id - 1; my $old = $file_base . $id . '.jpg'; my $pred = $file_base . $prev . '.jpg'; unlink($old); rename($pred, $old); $id--; } # Die neue Datei holen $file = $file_base . "1" . '.jpg'; my $res = getstore($url, $file); ...
Hat jede Datei einen individuellen Namen (z. B. aus Datum und Uhrzeit gebildet) sammeln sich zwar viele Dateien an, die gelegentlich altersabhängig gelöscht werden sollten, aber mit der Präsentation gibt es keine direkten Probleme. Es gibt ja genug fertige Tools für Bildergalerien. Wenn der Name aber immer gleich bleibt oder, wie im Beispiel weiter oben, nur wenige Dateien "rotiert" werden, tauchen plötzlich Probleme mit dem Browser auf. Der Browser speichert das Bild lokal in seinem Cache. Beim Firefox kann man beispielsweise den Cache erforschen, indem man "about:cache" in der URL-Zeile eingibt.
Das generele Problem liegt daran, dass der Browser beim erneuten Laden der Webseite (oder einem Refresh über Meta-Anweisung) feststellt, dass sich ein Bild gleichen Namens bereits im Cache befindet und statt das neue Bild über das Netz zu laden, zeigt er das aus dem Cache an. So ist auch Mittags noch der Sonnenaufgang zu sehen. Eine Lösungsmöglichkeit wäre, das Bild über ein CGI-Script auszuliefern - da ist der Browser so schlau, zu wissen, dass er jedemal neu darauf zugreifen muss. Wir können aber auch den gleichen Trick anwenden, wie die Trendnet-Kamera:
Der Browser wird getäuscht, indem der Name der Bilddatei manipuliert wird. Das kann mit etwas Javascript auf der Client-Seite geschehen. Einerseits wird im Body-Tag ein Refresh-Intervall definiert, damit die Seite regelmäßig neu gelanden wird und entsprechend auch ein neues Bild kommt. Andererseits sorgt ein Javascript-Zweizeiler dafür, dass mit einem sinnlosen, zufälligen Zusatz versehen, der dem Browser vorgaukelt, es handle sich um etwas Neues. Daraufhin lädt der Browser brav das frische Bild. Wichtig ist, dass beim Bilderlink eine ID hinzugefügt wird, damit das Javascript das Element auf der Webseite finden kann.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"> <html> <head> <title>Das Bild</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <script type="text/javascript"> RefreshImage = function() // macht neuen Bilderlink { // wo ist das Bild auf der Seite? img = document.getElementById("cam"); // mit "neuem" Namen laden img.src = "/webcam/bild.jpg?rand=" + Math.random(); } </script> </head> <!-- Hier kommt alle 30 Sekunden der Refresh --> <body onload="window.setInterval(RefreshImage, 30*1000);"> <!-- Hier ist das Bild, das automatisch nachgeladen wird --> <div align="center"> <img id="cam" src="/webcam/bild.jpg" /> </div> </body> </html>
Der Browser sieht nur folgendes:
<body onload="window.setInterval(refreshImage, 30*1000);"> <div align="center"> <img id="cam" src="bild.jpg?rand=0.9121381653654791"></img> </div> </body>
Die beschriebene Lösung ist sehr einfach und klappt mit nahezu jedem Browser - sofern Javascript eingeschaltet ist (nachdem aber kaum noch eine Webseite ohne Javascript daherkommt, ist das in der Regel der Fall). Natürlich gibt es noch weitere Methoden, entweder das Nachladen des Bildes alleine per Javascript und Ajax oder, wie oben erwähnt, Auslieferung des Bildes per Script oder komplett dynamische Webseiten mittels PHP und Content Management System.
Der hier vorgestellte Eigenbau-Infrarot-Scheinwerfer besteht aus 40 preiswerten Infrarot-LEDs und acht Widerständen. Er lässt sich auch vom Lötanfänger ganz einfach auf einer Lochrasterplatte aufbauen. Profis machen sich natürlich eine Platine, insbesondere wenn mehr als ein Scheinwerfer gebraucht wird. Bei 12 V Versorgungsspannung nimmt der Scheinwerfer ca. 200 mA auf, er sollte also ggf. vom Computer aus ein- und ausgeschaltet werden können (per Relais oder MOSFET-Schalttransistor). Die Schaltung des Scheinwerfers ist so einfach, das im folgenden Bild nur die Verdrahtung gezeigt werden muss. Es werden jeweils 5 LEDs und ein 100-Ohm-Widerstand in Reihe geschaltet.
Für den Außeneinsatz muss dann noch ein passendes Gehäuse mit Klarsichtdeckel spendiert werden.
Die alternative ist ein (unbeheiztes) Low-Cost-Gehäuse für die USB-Webcam. Hier dient als Wetterschutzgehäuse ein 100-W-Halogenstrahler aus dem das gesamte Innenleben entfernt wurde. Das Gehäuse bietet genügend Platz für die Kamera. Für größere Kameras muss man sich dann das jeweils passende Gehäuse suchen (150- oder 500-W-Lampe).
Bei der "Logilink UA0155" wurde der hintere Teil der Halterung abgesägt, wobei der Teil mit dem Kugelgelenk unversehrt blieb. Dann kann man die Kamera einfach ins Gehäuse kleben, wobei die Möglichkeit, die Kamera über das Kugelgelenk auszurichten, erhalten bleibt. Um den USB-Stecker aus dem Gehäuse herausführen zu können, musste der kleine Anschlusskasten entfernt und die Kabeldurchführung mit einer Rundfeile etwas vergrößert werden. Anschließend wird die Öffnung wieder verschlossen und ggf. mit Silikon abgedichtet.
Gegebenenfalls kann das Gehäuse mit einem Heizwiderstand ausgerüstet werden, der im Winter das Vereisen der Glasscheibe verhindert. Dafür sollte aber immer eine getrennte Stromversorgung verwendet werden. Einerseits muss ja nur bei Minustemperaturen geheizt werden und andererseits liefert die USB-Schnittstelle nicht wirklich genügend Strom zum Heizen.
Dabei ergibt sich allerdings ein schwerwiegender Nachteil: die interne Flash-Speicherkarte, SD-Karte oder SSD des Rechners wird belastet, da die Bilder erst auf der Platte zwischengespeichert werden, bevor sie auf den Webserver übertragen werden. Mit der oben geschilderten Helligkeitsanpassung ggf. sogar zweimal. Das ständige Löschen und Wiederbeschreiben ist trotz aller schlauer Algorithmen der Controllerbausteine Gift für die Lebensdauer der Solid-State-Speicher. Deshalb sollte für alle Dateien eine RAM-Disk eingerichtet werden - bei der sogar ein noch schnellerer Zugriff als Nebeneffekt hinzukommt. Dazu gibt es zwei Möglichkeiten:
Die Verwendung des tmpfs-Dateisystems:
Das tmpfs-Dateisystem ist eigentlich kein reines RAM-Dateisystem, sondern
die Daten landen im Festplatten-Swap, sobald der Speicherplatz im RAM knapp wird.
Mit dem folgenden Shell-Befehl wird das Verzeichnis /root/tmp zur RAM-Disk:
# mount -t tmpfs none /root/tmpoder, falls die Größe festgelegt werden soll:
# mount -t tmpfs -o size=20M none /root/tmpEs werden dynamisch immer so viele Ressourcen abgezweigt, wie gerade benötigt werden, auch wenn eine Größe angegeben wurde. Ist das Laufwerk also leer, belegt es auch keinen Platz im RAM. Es ist möglich, diese Partition standardmäßig beim Systemstart einzubinden, indem man eine Zeile in die Datei /etc/fstab einfügt:
tmpfs /root/tmp tmpfs defaults,size=20M 0 0Die Verwendung des ramfs-Dateisystems:
sudo mount -t ramfs ramfs /root/tmpDamit erhält man eine RAM-Disk, die sich ebenfalls dynamisch der benötigten Größe anpasst. Um die Partition beim Systemstart automatisch einzuhängen, fügt folgende Zeile in der Datei /etc/fstab hinzu:
ramfs /root/tmp ramfs defaults 0 0Das ramfs-Dateisystem hat im Gegensatz zu tmpfs keine Mountoptionen und bietet somit auch keine Möglichkeit, die Größe zu limitieren. Eventuell hat das System dann keinen freien Hauptspeicher mehr zur Verfügung und kann nur noch auf die Festplatte auslagern.