Open Source im professionellen Einsatz
Linux-Magazin 06/2006
© photocase.com

© photocase.com

Videoüberwachung mit Webcam und Perl

Angeln in der Bilderflut

Eine Webcam bietet die billigste Möglichkeit, die eigene Burg oder die Schwiegermutter zu überwachen. Die Skripte dieses Perl-Snapshots kalibrieren automatisch die Belichtung, wofür sie als Turbo C-Code einspannen, und fischen dann die interessanten Bilder aus dem Datenstrom der Kamera.

920

Die meisten Webcams bringen Windows-Software mit, die unter Linux nicht verwendbar ist. Neuere Linux-Distributionen haben allerdings oft Video4Linux an Bord, das problemarm eine eingestöpselte USB-Kamera ansteuert. Die in diesem Snapshot verwendete Kamera Creative NX Ultra kann neben Standbildern auch Videos liefern und kostet etwa 70 Euro. Für die Benutzung als einfache Webcam ist sie eigentlich zu schade, aber sie lag in einer Schublade des Perlmeister-Labors herum und war somit verfügbar. Sie benötigt keine externe Stromversorgung und das Hot-Plugging erkennt sie, sobald der USB-Stecker im Rechner steckt. Ihre Videodaten erscheinen typischerweise unter »/dev/video0«. Das Perl-Modul Linux::Capture::V4l vom CPAN hält sich am Device-Eintrag fest, greift die Framedaten ab und erlaubt es, Aufnahmeparameter wie die Empfindlichkeit laufend zu verändern.

Listing 1:
»single«

01 #!/usr/bin/perl
02 
03 use strict;
04 use warnings;
05 use Camcap;
06 
07 my $cam = Camcap->new(width  => 640,
08                       height => 480);
09 $cam->cam_bright(42_000);
10 my $img = $cam->capture();
11 $img->write(file => 'buero.jpg')
12   or die "Can't write: $!";

Abbildung 1: Diese Kamera aus der Schublade des Perl-Meisters, eine NX Ultra von Creative, musste für die Versuche herhalten.

Abstrakter Bilderdieb

Listing 1 zeigt eine einfache Anwendung, die erst die Empfindlichkeit der Kamera auf 40000 einstellt, ein Bild aus dem Videostrom abzwackt und es anschließend als Jpeg-Foto auf der Festplatte ablegt (Abbildung 2). Das von »single« verwendete Modul »Camcap« (Listing 2) abstrahiert den Zugriff auf den Videostrom. Der Konstruktor ab Zeile 12 definiert einige Default-Parameter wie die Bildbreite und -höhe und die minimale und maximale Helligkeitseinstellung (»br_min«, »br_max«). Anschließend hängt sich der Code über das CPAN-Modul Video::Capture::V4l an das Videodevice »/dev/video0« an. Lauscht schon ein anderer Interessent daran, schlägt die Verbindung fehl.

Die ab Zeile 34 definierte Methode »cam_bright()« stellt die Empfindlichkeit der Kamera ein. Sie nimmt einen Wert zwischen 0 und 65535 entgegen, holt mit der Methode »picture()« die Datenstruktur Picture der Kamera, setzt in ihr mit »brightness()« den übergebenen Empfindlichkeitswert und übergibt sie anschließend mit der Methode »set()« an die Video4Linux-Schicht.

Die Methode »capture()« ab Zeile 94 nimmt optional eine Empfindlichkeitsvorgabe entgegen und macht sich dann daran, den nächsten Frame aus dem Videostrom abzugreifen. Der erste Frame erhält die Nummer 0. Ein anschließender Aufruf der Methode »sync()« mit der Framenummer stellt sicher, dass die Bilddaten auch gut im Skalar » ankommen.

Abbildung 2: Dieses Bild fischte ein kleines Perl-Programm aus dem Videostrom der Webcam.

Listing 2:
»Camcap.pm«

001 ###########################################
002 package Camcap;
003 ###########################################
004 use strict;
005 use warnings;
006 use Video::Capture::V4l;
007 use Imager;
008 use Imager::Misc;
009 use Log::Log4perl qw(:easy);
010 
011 ###########################################
012 sub new {
013 ###########################################
014     my($class, @options) = @_;
015 
016     my $self = {
017         width   => 320,
018         height  => 240,
019         avg_opt => 128,
020         avg_acc => 20,
021         br_min  => 0,
022         br_max  => 65535,
023         @options,
024     };
025 
026     $self->{video} =
027         Video::Capture::V4l->new() or
028             LOGDIE "Open video failed: $!";
029 
030     bless $self, $class;
031 }
032 
033 ###########################################
034 sub cam_bright {
035 ###########################################
036     my($self, $brightness) = @_;
037 
038     my $pic = $self->{video}->picture();
039     $pic->brightness($brightness);
040     $pic->set();
041 }
042 
043 ###########################################
044 sub img_avg {
045 ###########################################
046   my($img) = @_;
047 
048   my $br = Imager::Misc::brightness($img);
049   DEBUG "Brightness: $br";
050   return $br;
051 }
052 
053 ###########################################
054 sub calibrate {
055 ###########################################
056   my($self) = @_;
057 
058   DEBUG "Calibrating";
059 
060   return if
061    img_avg($self->capture($self->{br_min}))
062    > $self->{avg_opt};
063 
064   return if
065    img_avg($self->capture($self->{br_max}))
066    < $self->{avg_opt};
067 
068       # Binary search
069   my($low, $high) = ($self->{br_min},
070                      $self->{br_max});
071 
072   for(my $max = 5;
073       $low <= $high && $max;
074       $max--) {
075     my $try = int( ($low + $high) / 2);
076 
077     my $i  = $self->capture($try);
078     my $br = img_avg($i);
079 
080     DEBUG "br=$try got avg=$br";
081     return if abs($br-$self->{avg_opt}) <=
082               $self->{avg_acc};
083 
084     if($br < $self->{avg_opt}) {
085         $low = $try + 1;
086     } else {
087         $high = $try - 1;
088     }
089   }
090   # Nothing found, use last setting
091 }
092 
093 ###########################################
094 sub capture {
095 ###########################################
096     my($self, $br) = @_;
097 
098     $self->cam_bright($br) if defined $br;
099 
100     my $frame;
101     for my $frameno (0, 1) {
102        $frame = $self->{video}->capture(
103               $frameno, $self->{width},
104               $self->{height});
105 
106        $self->{video}->sync($frameno) or
107                LOGDIE "Unable to sync";
108     }
109 
110     my $i = Imager->new();
111     $frame = reverse $frame;
112     $i->read(
113       type => "pnm",
114       data => "P6n$self->{width} " .
115               "$self->{height}n255n" .
116               $frame
117     );
118     $i->flip(dir => "hv");
119     return $i;
120 }
121 
122 1;

Vom Kopf auf die Füße

Einige Tests zeigen, dass manchmal der erste vorbeisausende Frame nicht akzeptabel ist, da eine kurz zuvor eingestellte Kameraempfindlichkeit noch nicht gegriffen hat. Deswegen holt »capture« grundsätzlich zwei Frames ab und wirft den ersten weg. Sobald die Methode »sync()« zurückgekehrt ist, liegen in der Variablen » die rohen Bilddaten im BGR-Format. Drei aufeinander folgende Bytes beschreiben jedes Pixel mit den Blau-, Grün- und Rotwerten (jeweils 0 bis 255). Um daraus ein Format zu generieren, das typische Bildverarbeitungsprogramme verstehen, kehrt das Kommando »reverse« den Bytestring zunächst um. Dies hat zur Folge, dass die Daten nun im etwas gängigeren RGB-Format vorliegen.

Wird das Ganze, wie in Zeile 114 geschehen, noch von einem P6-Header für das PPM-Format eingeleitet und die Breite und Höhe des Bildes angegeben, kann die Methode »read()« des CPAN-Moduls Imager daraus ein Bild zaubern. Allerdings hat sich durch die Umkehrung mit »reverse« auch die Reihenfolge der Pixel umgedreht, sodass das Bild nun auf dem Kopf steht. Das macht ein Aufruf der Methode »flip()« sofort rückgängig, die mit dem Parameter »dir => "hv"« eine vertikale 180-Grad-Spiegelung vornimmt. Die Methode »capture()« liefert ein Objekt vom Typ »Imager« zurück, das die aufrufende Funktion dann weiterverarbeiten kann.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 5 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Zufall unter Beobachtung

    In manchen Firmen laufen die Mitarbeiter mit Secur-ID-Tokens der Firma RSA Security herum. Das kleine Authentisierungsgerät berechnet und zeigt jede Minute eine Ziffernkombination, die beim Einloggen temporär gültig ist. Eine mit Perl gestrickte Zeichenerkennung schaut dem Schlüsselgenerator beim Zocken zu.

  • Perl-Snapshot

    Zum Stöbern in vorbeirauschenden Paketen im lokalen Netzwerk leistet Platzhirsch Wireshark gute Dienste. Wer lieber eigene Tools baut, greift auf die Kommandozeilenversion Tshark zurück.

  • Perl und C

    Der Perl-Interpreter perl ist in C geschrieben und bietet recht komfortable Schnittstellen, um ihn mit erstaunlich schnellen Zusatzfunktionen auf Maschinenebene aufzupeppen. Allerdings profitieren nur ganz bestimmte Anwendungen tatsächlich vom Mixed Language Programming.

  • Farbenspiel

    Oft benötigen ganze Serien digitaler Bilder dieselben Korrekturen, was den Fotografen zu immer gleichen Schritten in Gimp zwingt. Viel bequemer kommt zum Ziel, wer die Retusche automatisiert.

  • Runter kommen sie alle

    Spiele-Programmierer rechnen mit physikalischen Formeln und setzen spezielle Tricks ein, um Grafiken realitätsnah zu animieren. Der als Perl-Wrapper verfügbare Simple Direct Media Layer (SDL) bietet ein leistungsfähiges Framework, um einfache 2D-Welten zu schaffen, hier eine Fallschirmsprung-Simulation.

comments powered by Disqus

Ausgabe 01/2017

Digitale Ausgabe: Preis € 6,40
(inkl. 19% MwSt.)

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.