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.
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.
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.
« Zurück
1
2
3
4
Weiter »