Kamera

Aus Kicker
Zur Navigation springenZur Suche springen

Ballerfassung mit Hilfe einer Kamera

Informationen zur verwendeten Hardware

Kamera

Bei der Kamera handelt es sich um eine schwarzweiß-Hochgeschwindigkeitskamera der Firma Mikrotron, die MC1302. Diese Kamera ist mit bis zu 118 Bilder/s bei maximaler Auflösung (1280x1024Pixel) für die Ballerkennung völlig ausreichend, zumal bei geringerer Auflösung die Bildwiederholrate noch gesteigert werden könnte. Als Ansprechpartner bei Mikrotron stellte sich Hr. Ertl, ein ehemaliger Diplomant der FH München, zur Verfügung.

Objektiv

Das zunächst verwendete Objektiv CANON LENS FD 20mm 1:2,8 hat den Nachteil, dass der nötige Abstand zum Objekt, dem Spielfeld, bei einer Auflösung von 640x480 (Grund für diese Auflösung weiter unten!) viel zu groß sein müsste. Bei einem Spielfeld, das 125cm x 68,5cm bemisst wäre ein Abstand von Kamera zu Spielfeld von etwa 3,38m nötig. Diese Annahme ergibt sich aus zwei Messungen und der Tatsache, dass sich der Blickwinkel nicht verändert und somit bei doppeltem Abstand auch eine doppelte Länge bzw. Breite erfasst werden kann (Abweichungen durch Scharfstellen und dem damit verbundenen verändern der Linsen vernachlässigt!).

CANON LENS FD 20mm 1:2,8

  • 1. Messung:

Abstand: ca. 66cm, erfasstes Feld: 25cm x 18,7cm

  • 2. Messung:

Abstand: ca. 110cm, erfasstes Feld: 40,3cm x 31,2cm

  • Nötiger Abstand, um vollständiges Feld zu erfassen:

Der Quotient Abstand/Spielfeldlänge beträgt etwa 2,7. Daher ergibt sich eine notwendige Höhe von etwa 2,7*125cm= 3,38m, was sehr schwer umzusetzen wäre. Es ist also nötig, ein weitwinkligeres Objektiv zu verwenden, mit dem ein geringerer Abstand ausreicht.

Ein besseres Resultat wird mit einem Objektiv mit geringerer Brennweite erzielt. Dafür stehen zum einen ein Objektiv mit 12.5mm (PENTAX TV LENS 12.5mm 1:1.4) oder mit 8mm (MEGAPIXEL CCTV LENS 0814MP f8mm F1.4 A6314) zur Verfügung.

PENTAX TV LENS 12.5mm 1:1.4

Ein durchaus besseres Resultat liefert das Objektiv mit 12.5mm Brennweite. Bei einem Abstand von Objektiv zu Objekt von 113.5cm konnte Feld von 70cm Länge und 51cm Breite aufgenommen werden. Der Quotient 113.5cm/70cm=1,62 ergibt, dass ein Abstand von 1,62*1,25cm=2,03m nötig wäre, um das gesamte Kickerfeld aufzunehmen, also bedeutend weniger als die 3,38m mit dem CANON-Objektiv. Zusätzlich kann noch mit einem Spiegel gearbeitet werden, um die Kamera nicht 2m vom Spielfeld entfernt positionieren zu müssen.

MEGAPIXEL CCTV LENS 0814MP f8mm F1.4 A6314

Dieses Objektiv würde mit der 8mm Brennweite den nötigen Abstand zwischen Objektiv und Spielfeld weiter reduzieren. Leider stellte es sich als inkompatibel zur Kamera heraus, es war nicht möglich das Bild scharf zu stellen.

Rechner

RAM: 1GB CPU: AMD Athlon 64 3200 Framegrapper-Karte ohne eigenen Speicher

Infos zu verwendeter, vorläufiger Software

Kamera konfigurieren

Um die Kamera zu konfigurieren wird die Software MC1302_25 Mikrotron GMbH (korrekter Programmname?) verwendet. Gute Werte wurden erzielt, indem das User Profile 2 geladen wurde, dir 'shutter-mode' auf Async timer gesetzt wurde und die 'exposure time [sec]' den Lichtverhältnissen angepasst wurde. <TODO: genauere Konfiguration angeben!>. Diese Software wurde durch durch einen Download von Mikrotron auf den aktuellen Stand gebracht (firmware: V1.43-F2.45) <Versionsnummer überprüfen!>

Resultate der Kamera betrachten und Bildfolgen abspeichern

Um sofort überprüfen zu können, ob die verwendeten Einstellungen zu gebrauchen sind bietet sich die Verwendung des Programms VCAM von Mikrotron an. Dieses zeigt zum einen die aktuellen Bilder der Kamera wieder, zum anderen gibt es auch die Möglichkeit, Bildfolgen direkt auf die Festplatte abzuspeichern, um sie anschliesend auszuwerten. Dabei fiel allerdings auf, dass selbst bei einer geringen Auflösung von 640x480Pixeln die Geschwindigkeit der Festplatte das Bottleneck des Speichervorgangs darstellt. Folgende Messreien zeigen deutlich, dass bei einer hohen Bildwiederholrate nicht alle Bilder abgespeichert werden können (Speicherung einer Bildreihe von einer Uhr mit Zehntel Sekunden):

4x .7
4x .8
3x .9
4x .0
3x .1
4x .2
3x .3
3x .4
3x .5
4x .6
4x .7
Betrachtet man dabei die fett markierte Sekunde, so erhält man statt den eingestellten 137fps lediglich 35fps.

6x .4
6x .5
6x .6
5x .7
6x .8
4x .9
5x .0
5x .1
5x .2
4x .3
3x .4
5x .5
Auch hier bleibt man mit den 49fps weit unter den eingestellten 260fps. Allerdings fällt auf, dass sich die effektive Bildwiederholrate gegenüber der vorherigen Messung erhöht hat. Grund müsste laut Hr. Ertl die eingeschränkte Schreibgeschwindigkeit auf Festplatte sein. Da das Programm VCAM ein Demonstationsprogramm darstellt ist es nicht darauf ausgelegt, so viele Bilder innerhalb kürzester Zeit abzuspeichern. Da der Grapper keinen eigenen Speicher besitzt und die Bilder auch nicht im RAM zwischengelagert werden verwirft VCAM alle Bilder, die vor der Beendigung des vorherigen Schreibvorgangs geliefert werden. Dies wäre auch eine erklärung, warum in der 2. Messung mehr fps erreicht werden, da sich die Wartezeit zwischen beendetem Schreibvorgang und der Lieferung des nächsten Bildes kleiner wird.

Sollte das der alleinige Grund für die Reduzierte Abspeicherrate sein, so lässt sich dieses Problem vorläufig mit der Verwendung einer RAM-Disk, also einer virtuellen Festplattenpartition im RAM, lösen.

Ramdisk erstellen (Linux):

<source lang="bash">sudo mount -t ramfs ramfs /media/RAM-Disk</source> Geschwindigkeit RAM-Disk an Referenzrechner (AmiloPa2530): Testfile: 700MB <source lang="bash">date1=`date '+%s%N'` && cp testdatei1 testdatei2 && expr '(' `date '+%s%N'` - $date1 ')' / 1000000</source>

  • HDD -> HDD: 42273msec
  • HDD -> RAM: 12886msec
  • RAM -> RAM: 875msec
  • RAM -> HDD: 18567msec

Ramdisk erstellen (Win2000):

Wie in der Messung unter Linux festzustellen sollte das Problem, dass die Bilder nicht schnell genug auf die HDD geschrieben werden können, mit einer Ramdisk gelöst bzw. umgangen werden können. Von den 1GB Arbeitsspeicher wurde dafür eine Ramdisk von 700MB erstellt. Bei einer neuen Aufnahme einer Bildfolge ergab sich dabei folgendes Resultat:

5x .7
5x .8
5x .9
4x .0
5x .1
5x .2
5x .3
5x .4
5x .5
5x .6
Das ergibt eine Rate von 49fps bei einer eingestellten Rate von 205fps, was gegenüber der 2. Messung ohne Ramdisk keinerlei Verbesserung darstellt. Das Problem scheint also an anderer Stelle zu liegen. Nächster Lösungsansatz wird sein, die Bilder direkt über C-Code von der Kamera zu holen, in reserviertem Speicher abzulegen und danach in Dateien zu speichern.

Koordinaten des Balles aus Bild ermitteln

Was später automatisiert in Sourcecode gefasst passieren soll wird zunächst Step by Step in einem eigenem Programm durchgeführt: Das Auswerten jedes Bildes, um die Koordinaten des Balls zu ermitteln. Dafür wird ein Macro für die Software <MCR - genauer Softwarename!> geschrieben, mit dessen Hilfe folgende Schritte für jedes einzelne Bild durchlaufen werden:

  • Bild [bmp] einlesen
  • Bereiche mit bestimmter Farbe / bestimmtem Grauwert als aktiv markieren
  • Aktive Bereiche, die unterhalb eines Grenzwertes sind löschen (z.B. <200px)
  • Schwerpunkt der verbleibenden aktiven Punkte ermitteln (bei Ball -> Mittelpunkt)
  • Koordinaten (Mittelpunkt) zu Liste hinzufügen

Das Macro, das dafür notwendig ist sieht folgendermasen aus: <source lang="text">Macro einfügen</source> Deutlich zu erkennen ist, dass für jeden der Schritte bei größerer Anzahl von Bildern leicht mit Schleifen gearbeitet werden, nur bei dem ersten Schritt, dem einlesen der einzelnen Bilder treten hier durch die unterschiedlichen Dateinamen Probleme auf. Um trotzdem nicht alle Befehle per Hand editieren zu müssen wurde diese Aufgabe von einem kleinen C-Programm übernommen: <source lang="cpp">#include <stdio.h>

  1. include <string.h>
  2. include <stdlib.h>
  1. define MAX_BILDER 10

int main(int argc, char *argv[]) { int i; int start, ende;

if (argc <= 3) return -1;

start = atoi(argv[2]); ende = atoi(argv[3]);

for (i = start; i <= ende; i++) { printf("%s%.4d.bmp\n", argv[1], i); }

return 0; }</source> Das Programm wird mit 3 Parametern aufgerufen. Der erste Parameter ist der Dateipfad bis zu der Stelle der Nummerierung. Der 2. und 3. Parameter ist Anfang und Ende der Schleife. Soll beispielsweise Bild 35 bis 250 eingelesen werden, wird 35 als 2. Parameter und 250 als 3. Parameter übergeben. Die Ausgabe kann man nun direkt in das Makro kopieren.

Ein Testdurchlauf mit diesem Makro ergab folgende Wertetabelle (2. und 3. Spalte):

<source lang="text"> No X/pixel Y/pixel Dist Richtung [px ] 1 599,5 251,83 /[px] x: y: 2 588,51 251,84 10,99 -10,99 -0,01 3 577 251,87 11,51 -11,51 -0,03 4 563,88 251,76 13,12 -13,12 0,11 5 550,94 251,44 12,94 -12,94 0,32 6 537,38 251,35 13,56 -13,56 0,09 7 524,5 251,07 12,88 -12,88 0,28 8 510,85 250,94 13,65 -13,65 0,13 9 497,96 250,79 12,89 -12,89 0,15 10 477,69 250,77 20,27 -20,27 0,02 11 458,23 250,81 19,46 -19,46 -0,04 12 444,73 250,94 13,50 -13,50 -0,13 13 431,89 250,88 12,84 -12,84 0,06 14 418,07 251,07 13,82 -13,82 -0,19 15 398,71 251,18 19,36 -19,36 -0,11 16 384,71 251,41 14,00 -14,00 -0,23 17 372,33 251,49 12,38 -12,38 -0,08 18 357,79 251,78 14,54 -14,54 -0,29 19 338,33 251,94 19,46 -19,46 -0,16 20 323,7 252,27 14,63 -14,63 -0,33 21 310,82 252,55 12,88 -12,88 -0,28 22 296,42 252,9 14,40 -14,40 -0,35 23 276,66 253,26 19,76 -19,76 -0,36 24 262,2 253,57 14,46 -14,46 -0,31 25 249,2 253,8 13,00 -13,00 -0,23 26 234,76 254,15 14,44 -14,44 -0,35 27 221,4 254,54 13,37 -13,36 -0,39 28 207,61 254,94 13,80 -13,79 -0,40 29 193,82 255,39 13,80 -13,79 -0,45 30 180,51 255,92 13,32 -13,31 -0,53 31 166,19 256,46 14,33 -14,32 -0,54 32 153,25 257,02 12,95 -12,94 -0,56 33 138,69 257,72 14,58 -14,56 -0,70 34 125,58 258,31 13,12 -13,11 -0,59 35 111,12 259,01 14,48 -14,46 -0,70 36 97,41 259,77 13,73 -13,71 -0,76 37 83,45 260,4 13,97 -13,96 -0,63 38 69,24 261,14 14,23 -14,21 -0,74 39 55,58 261,77 13,67 -13,66 -0,63 40 42,97 262,5 12,63 -12,61 -0,73 </source>

Aus diesen Koordinaten lässt sich ohne weiteres die zurückgelegte Distanz (Spalte 4) sowie die Änderung in x-Richtung (Spalte 5) wie auch in y-Richtung (Spalte 6) feststellen.

Eigener Sourcecode

Damit das vom Computer gesteuerte Team sofort auf Positionsänderungen des Balls reagieren kann muss die Auswertung der Koordinaten automatisiert werden. Dazu sind im grunde die Selben Schritte wie bei der Software nötig. Diese Schritte werden nach und nach in C-Code implementiert.

  • Kameraprofil laden
  • Bild und Bildinformationen von Kamera holen
  • Binärbild mithilfe der Informationen mit Header versehen und als *.bmp abspeichern (Zwischenschritt um Erfolg zu prüfen
  • Bildfolge abspeichern, fps überprüfen!!!

//TODO:

  • Mit offenen Bibliotheken Koordinaten des Balls auf den Bildern ermitteln
  • Optimierungen (z.B. nur Bildbereiche in bestimmten Umkreis der Koordinaten des letzten Bildes untersuchen o.ä.

Dabei entsteht folgender Code: <source lang="c"> //kamera.c

  1. include <stdio.h>
  2. include <time.h>
  3. include "windows.h"
  4. include "mvfgdrv.h"
  5. include "bitmap.h"

//C:\PROGRA~1\MIKROT~1\Inspecta\Inspecta-4DC.cam int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { LONG ret; char buffer[256]; void *ptr; FORMAT_INFO info; time_t t1, t2; int err = 0, i;

/* open camera in Testmode, number 0 */ ret = mvfg_open("C:\\PROGRA~1\\MIKROT~1\\Inspecta\\Inspecta-4DC.cam;MC1310_11_02_03 640x480 206fps", 0); if (ret != MVFG_OK) { mvfg_errmessage(ret); return -1; }

/* get some camera informations */ ret = mvfg_getparam(MVFGPAR_FORMAT_INFO,&info,0); if (ret != MVFG_OK) { mvfg_errmessage(ret); return -1; } else { wsprintf(buffer, "Pixel breite: %d\nPixel hoehe: %d", info.lImageWidth, info.lImageHeight); MessageBox(NULL, buffer, "Informationen ueber aktuelle Einstellungen", MB_OK); }

/* grab picture */

  1. if 0

time(&t1); for (i = 0; i < 100; i++) { ret = mvfg_grab(GRAB_WAIT, 0); if (ret != MVFG_GRAB_READY) ++err; } time(&t2); if (err > 0) { wsprintf(buffer, "Es sind %d Fehler aufgetreten", err); MessageBox(NULL, buffer, "Informationen ueber Bilder", MB_OK); } else { wsprintf(buffer, "Es wurden 100 Bilder in %d aufgenommen", t2 - t1); MessageBox(NULL, buffer, "Informationen ueber Bilder", MB_OK); }

  1. endif

ret = mvfg_grab(GRAB_WAIT, 0); if (ret != MVFG_GRAB_READY) MessageBox(NULL,"Fehler beim aufnehmen", "Informationen ueber Bilder", MB_OK); /* ptr points to the address of the picture */

ptr = malloc(info.lFrameSize); memcpy(ptr, mvfg_getbufptr(0), info.lFrameSize);

/* * XXX save file as bitmap * we have to build an bitmap header ... */

writeBitmapToFile(ptr, info.lFrameSize);


ret = mvfg_close(0); if (ret != MVFG_OK) { mvfg_errmessage(ret); return -1; } else { MessageBox(NULL, "Bild wurde abgespeichert, Kamera Handle geschlossen", "Fertig", MB_OK); } return 0; } </source>

<source lang="cpp">//bitmap.h zum anhängen des Bitmap-Headers:

  1. ifndef _BITMAP_H
  2. define _BITMAP_H
  1. include <stdio.h>


int writeBitmapToFile(void *, DWORD );


struct bitmapFileHeader { WORD bfType; DWORD bfSize; DWORD bfReserved; DWORD bfOffBits; };

struct bitmapInformationBlock { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; };

  1. endif

</source>

<source lang="cpp">//bitmap.cpp

  1. include <windows.h>
  2. include <stdio.h>
  3. include <time.h>
  4. include "bitmap.h"

int writeBitmapToFile(void *buffer, DWORD bSize) { struct bitmapFileHeader header; struct bitmapInformationBlock infoBlock; FILE *fd;

header.bfOffBits = 54; // Offset vom Beginn der Datei in Byte header.bfReserved = 0; header.bfType = 19778; //Magic Sequenz ascii BM header.bfSize = 300 * 1024;

infoBlock.biBitCount = 8; // Farbtiefe infoBlock.biClrImportant = 0; //Anzahl der im Bild vorkommenden Farben infoBlock.biClrUsed = 0; infoBlock.biCompression = 0; //keine Compression infoBlock.biHeight = 480; infoBlock.biWidth = 640; infoBlock.biPlanes = 1; infoBlock.biSize = 40; infoBlock.biSizeImage = bSize; infoBlock.biXPelsPerMeter = 0; infoBlock.biYPelsPerMeter = 0;

fd = fopen("file.bmp", "wb+"); fwrite(&header, 1, sizeof(header), fd); fwrite(&infoBlock, 1, sizeof(infoBlock), fd); fwrite(buffer, 1, bSize, fd); fclose(fd);

return 1; } </source>

Berechnungen

Nötige Auflösung

Bei der zu verwendenden Auflösung gilt: so groß wie nötig, so klein wie nötig. Je größer jedes auszuwertende Bild ist, desto mehr Rechenaufwand und Zeit wird für die Auswertung benötigt. Allerdings wird eine gewisse minimale Auflösung benötigt, damit der Ball gegenüber anderen Störeinflüssen wie Schatten, Flecken o.ä. eindeutig erkannt werden kann. Außerdem wirkt sich die Auflösung des Bildes auf die Genauigkeit der ermittelten Koordinaten aus.

Um diesen Anforderungen gerecht zu werden wurde eine Auflösung von 640x480 gewählt. Die Spielfeldlänge von 1,25m wird dabei in 480px aufgeteilt bzw. von diesen erfasst, die Breite von 68,5cm hingegen von 350px, es werden also nicht alle der 480 möglichen Pixeln für die Breite benötigt, da das Spielfeld nicht in einem Verhältnis von 4:3 also 1,33:1 sondern von 1,82:1 steht.

x-Achse: 640px/125cm = 5,12px/cm = 1px/2mm y-Achse: 5,12px/cm * 68,5cm = 350,7px

  • Durchmesser des Balls: 3,5cm = 3,5cm * 5,12px/cm = 18px (genauer: 17,92px)
  • Fläche des Balls: A = r² * PI = (17,92px/2)² * PI = 252px

Das heißt, dass bei der Ballerkennung etwa 250px den Ball darstellen, was zum einen ausreichend ist um ihn von Störungen zu unterscheiden, was zum anderen auch eine weit höhere Genauigkeit bringt als die Auflösung selbst (1px/2mm), da die Koordinaten aus dem Mittelpunkt dieser 250px ermittelt werden, also die Koordinaten auf mehrere Pixelkomastellen genau darstellen (was bei Pixeln zwar nicht viel Sinn macht da es nu ganze Pixel geht, aber beim Betrachten der Koordinaten zur verbesserten Genauigkeit beiträgt).

Nötige Exposure Time

Eine weiterer Parameter den man bestimmen muss ist der mögliche Maximalwert der exposure time. Darunter kann man sich die Belichtungszeit vorstellen. Die nötige exposure time ist abhängig von der Beleuchtung: Je geringer die Beleuchtung desto länger die Belichtungszeit, um ein gutes Bild mit ausreichender Helligkeit zu erhalten. Allerdings ist ein Bild mit hoher exposure time bei schnellen Bewegungen sehr verschwommen. Aber auch die Beleuchtung sollte gering gehalten werden, da Hitze, Stromkosten und das Blenden der Spieler Nebenwirkungen sind.

Um die Koordinaten des Balles ohne größere Verfälschung ermitteln zu können wird als Grenze eine exposure time verwendet, in der sich die Position des Balls während der Belichtungszeit maximal um 3px/Bild ändert. Geht man dabei von einer Maximalgeschwindigkeit des Balls von 4m/sec aus, so ergibt sich folgende maximale exposure time:

  • 4m/sec = 4mm/msec = 2px/msec (bei 1px/2mm)
  • Grenze 3px: 1,5msec = 1/667 sec

Dieser Wert ist gegenüber den momentan verwendeten Werten von etwa 1/280 sec sehr gering und setzt daher eine sehr gute Beleuchtung vorraus. Ggf. wäre es möglich, diesen Wert auch etwas zu vergrößern, was zwar zu einem verschwommenen Ball auf dem Bild führen würde, aber evtl. trotzdem ausreichen würde, um gute Koordinaten zu erhalten. Mit einer exposure time von 1/280 sec hätte man in einem Bild eine Positionsänderung des Balls bei einer Geschwindigkeit von 2px/msec von:

  • 1sec/280 * 2px/msec = 7,14px (Frage an Hr. Jaschul: möglich?)