Slideshowsteuerung mit einer 3D Kamera

Aus toolbox_interaktion
Wechseln zu: Navigation, Suche

Die Steuerung der Windows-Slideshow basiert auf dem Grundsatz einer zuvor durchgeführten Pojektarbeit (Rahmenprogramm zur PMD Kamera), bei welcher verschiedene Funktionen (z.B. eine einfache Maussteuerung) realisiert wurden. Die Weiterführung beinhaltet unter anderem eine gedrehte und vergrößerte Bildausgabe, das Ermitteln der zwei nähesten Pixel und deren farbliche Visualisierung für den Nutzer. Auf diesen Funktionen baut die Steuerung der Slideshow auf, wobei hier die räumliche Lage der nähesten Pixel mit Keyboard-Events gekoppelt ist (siehe Abschnitt 5.4). Die Ergebnisse dieser Funktionen können dann über die standardisierte Schnittstelle des OSC Protokolls versendet werden. Die Funktionen sind in C++ realisiert, wobei abschließend ein Installer erschaffen wurde, der alle Features - einschließlich der Einbindung der OpenCV-Bibliothek - beinhaltet und somit dem Anwender einen einfachen Einstieg ermöglicht.

Nachfolgend sind die Kernbestandteile und die implementierten Funktionen sowie deren Einbindung in das Programm beschrieben.

Vorstellung der Kamera

Verschiedene Ansichten der PMD-Kamera

Die Kamera basiert auf einem PMD-Sensor (Photo-Misch-Detektor), der nach dem Lichtlaufzeit-Messprinzip arbeitet und zusätzlich zu konventionellen Helligkeits-Informationen das Amplitudenbild einer aktiven Infrarot-Beleuchtung und die Abstandsinformation zu betrachtender Objekte zu jedem Pixel des Kamerabildes liefert.

Siehe PMD Kamera

Übersicht über das Projekt

Flussdiagramm des Projektes

Flussdiagramm des Gesamtsystems

Zur Veranschaulichung unseres Projektes haben wir ein Flussdiagramm eingefügt. Es zeigt die wichtigsten Funktionen und den Überblick über die Verarbeitung und Weitergabe von Daten im Umfeld der Kamera. Auf die einzelnen Funktionsweisen der Programmteile wird in den jeweiligen Kapiteln eingegangen. In der Abbildung kann man erkennen das die “main routine” eine Windows Forms Application aufruft, welche mit der Kamera kommuniziert und die aktualisierten Werte abfragt. Diese Bildwerte werden dann über Methoden verarbeitet und ausgegeben. Als Besonderheit ist eine OSC Schnittstelle eingebunden, welche es uns ermöglicht die Ergebnisse der Datenauswertung der Kamera theorethisch weltweit auszulesen.

Erstellen einer geeigneten Windows Forms Applikation

Um für den Benutzer einen leichten Einstieg und eine leichte Bedienung des Programmes zu ermöglichen haben wir uns dazu entschlossen eine Windows Forms Applikation einzusetzen. Diese bietet für den User eine sehr einfache Handhabe mit der Kamera. Mann muss sich nicht mit technischen Details befassen um die Funktionen zu nutzen. Das Design richtet sich im Wesentlichen nach der Forms Applikation unserer Vorgruppe, haben sie aber nach unseren Bedürfnissen angepasst und so eine eigenständige Bedienoberfläche geschaffen. Mit Hilfe von Windows Forms lassen sich einfach per Drag&Drop graphische Oberflächen erstellen, die dennoch anspruchsvolle Anwendungen beherbergen können. Visual Studio stellt hier eine umfangreiche Bibliothek zur Verfügung um verschiedenste Anwendungen zu erstellen. Windows Forms enthält eine Vielzahl von Steuerelementen wie zur Anzeige von Textfeldern, Schaltflächen oder Optionsfeldern. Wenn ein Benutzer Aktionen ausführt, wird ein Ereignis generiert. Die Anwendung kann dann auf Ereignisse mithilfe von Code reagieren und diese verarbeiten. Wir haben uns für die Windows Forms Anwendung entschieden, da diese die einfachste Möglichkeit bietet, eine Benutzeroberfläche mit einfachen In- und Output Befehlen zu erstellen und diese mit eingebundenem Code zu verarbeiten.

(Ausschnitte von Microsoft)

Einbindung der Open Source Bibliothek OpenCV

Eine ausführliche Anleitung zur Bindung der OpenCV-Bibliothek in die Windows Forms Anwendung findet sich unter OpenCV mit Windows Forms in Visual Studio

Darstellung der Funktionen des Programmes

Die Bildausgabe

Diese Funktion verwertet die ankommenden Bilddaten zu einem Ipl-Image, das nach Drehung und Vergrößerung für den Benutzer eine optische Kontrolle im Hinblick auf die Bedienung der Kamera und des Programmes bietet. OpenCV nutzt für Bilder die Datenstruktur IplImage aus der Intel Image Processing Library.

Zunächst werden die Variablen angelegt:
		float gray[3200], rot_mat[3200], divisor = 1;
        	IplImage  *rotated = cvCreateImage(cvSize(50, 64), 32, 1);
		IplImage  *great = cvCreateImage(cvSize(250, 320), 32, 1);
		int x = 50, j = 0;
		pKamera->GetAmplitudes(gray, sizeof(gray));

Die Kameradaten werden anfangs an das Array gray[3200] übergeben und auf den entsprechenden Wertebereich skaliert. Hierzu benötigen wir einen Divisor, der vorerst mit dem Wert 1 belegt werden muss, da ansonsten ein Programmabsturz durch eine mögliche Division mit 0 drohen könnte.

		for(int i = 0; i < sizeof(gray) / sizeof(float); i++){
			if(gray[i] > divisor)
				divisor = gray[i];			
		}

Nachfolgend wird mittels einer for-Schleife der höchste Wert ermittelt, um den Wertebereich der eingegangen Bilddaten zu erfassen. Der höchste Wert wird in der Variablen divisor gespeichert.

		for(int i = 0; i < sizeof(gray)/sizeof(float); i++){
			gray[i] = gray[i]/divisor;

			if(gray[i] > 1)
				gray[i] = 1;
			if(gray[i] < 0)
				gray[i] = 0;
		}

Die Bildmatrix wird abgefragt und die einzelnen float-Werte durch den ermittelten Divisor geteilt, wodurch eine Aufspaltung der Werte von 0 bis 1 passiert. Nachdem der Wertebereich des Datenstrom der Kamera auch negative Werte beinhaltet, stellen wir mit den if-Bedingung sicher, dass die Zahlenwerte tatsächlich zwischen 0 und 1 liegen.

 		for(int i = 0; i < 3200; i++){
			if( (i % 64) == 0 ){
				x -= 1;
				j = x;
			}
			rot_mat[j] = gray[i];
			j += 50;
		}

Die so entstandene, für ein Bild geeignete float-Matrix muss nun noch um 90° im Uhrzeigersinn gedreht werden, da das Bild normalerweise den PC “gekippt” erreicht. Dies geschieht, indem das Daten-Array mit modulo 64 abgefragt wird und jeder 64te Array-Wert in der jeweiligen Bildzeile gespeichert wird (siehe Abbildung). So wird nach und nach das komplette Bild um 90° gedreht und in das Array rot_mat überführt.

Veraunschaulichung der Drehung der Bildmatrix
		rotated->imageData = (char*)rot_mat;
		cvResize(rotated, great, 1);


Im Anschluss werden die Bilddaten dann an IplImage übergeben und mit der Funktion cvResize das Image noch vergrößert, in diesem Fall um den Faktor 5 (siehe Variable great).

		cvShowImage( "ROTATED_GREAT_32bit", great );

Die endgültige Bildausgabe erfolgt unter dem Namen ROTATED_GREAT_32bit.

Funktion koord

Die Funktion koord dient dazu, die nähesten Pixel zu finden und deren Koordinaten zu berechnen. Dies geschieht indem wir das von der Kamera gelieferte Daten Array nach den niedrigsten Werten absuchen. Somit erhält man deren Position in diesem Array, so dass sich anhand der Position im Array die dazugehörigen Koordinaten im Bild errechnen lassen.

Die folgenden Codeausschnitte verdeutlichen unser Vorgehen:

		int koord(cam *pKamera, double *erg_cx, double *erg_cy,double *erg_cz, double *erg_cx_2,
			double *erg_cy_2, double *erg_cz_2){ ...

Der Funktionsaufruf findet in form1.h statt. Übergabeparameter sind neben einem Zeiger auf ein Kameraobjekt, Referenzen von Variablen, welche in form1.h deklariert wurden.

		float dist[3200], z_1 = 0, z_2 = 0, nearestPoint;
		int index_1, index_2, res = 0;

		res = pKamera->GetDistances(dist, sizeof(dist));
		if(res != PMD_OK)
		{
			return res;
		 }

Mit GetDistances(dist, sizeof(dist)) holen wir uns die Daten eines einzelnen Bildes ab und speichern sie in dem als Argument übergegebenen Array dist[] ab. Die Daten repräsentieren die Entfernungen der einzelnen Pixel zur Kamera. Dabei entspricht ein Pixel des Bildes einer Stelle im Datenarray. Die Zählweise beginnt oben links im Bild und wird zeilenweise fortgesetzt, so dass der letzte Wert im Array dem Pixelwert unten rechts im Bild entspricht. Die Variable res dient zur Sicherstellung, dass die Datenübertragung von der Kamera problemlos verlief.


		nearestPoint = 2000;
		for(int i = 0; i < 1600; i++){
 			if( dist[i] < nearestPoint){
				nearestPoint = dist[i];
				 index_1 = i;
			 }
		}

		nearestPoint = 2000;
		for(int i = 1600; i < 3200; i++){

			if( dist[i] < nearestPoint){
				nearestPoint = dist[i];
				index_2 = i;
			}
		}

Sind die Daten sicher im dist[]-Array gespeichert, beginnt die Suche nach den nähesten Pixeln. Nachdem sich die Suche nach dem zweiten nähesten Pixel als äußerst schwierig erwies, haben wir beschlossen das Bild zu “teilen”. Damit erreichen wir, dass pro Bildhälfte zu 100% der richtige Pixel erkannt wird. Zur endgültigen Berechnung der x- und y-Koordinaten benötigen wir nur die Positionen der gefundenen Pixel im Array. Diese speichern wir unter index_1 und index_2. Aus der Arrayposition lassen sich wie folgt die dazugehörigen x- und y- Koordinaten errechnen:

		float ax, ay, az;
		ax = (float) (-(index_*) / 64 + 50); 
		ay = (float) (-(index_*) % 64 + 50);
		az = floor(dist[index_*] * 100);


		*erg_cx = abs(ax);
		*erg_cy = abs(ay);
		*erg_cz = abs(az);

Die fertig berechneten Werte übergeben wir schließlich den Funtkionsparametern, damit diese über form1.h in unserer Applikation ausgegeben werden können. Die Bildung des Absolutwertes ist nötig, da die Kamera hin und wieder tatsächlich negative Werte liefert.

		track_me(pKamera);          
		steuerung(pKamera, az, az_2);

Abschließend werden weitere Funktionen aufgerufen, die sowohl die in koord.cpp gewonnenen Daten auswerten, als auch weitere Operationen am Array durchführen. Deren genaue Funtkionsweise wird in den folgenden Kapiteln erklärt.

Damit die Daten nicht nur intern, also innerhalb des Programmes genutzt werden, sondern auch innerhalb des Systems bzw sogar weltweit über das Internet, haben wir in koord.cpp noch eine OSC-Schnittstelle eingebunden.

		char *hostName = "localhost";	 
		int port = 7000;
		IpEndpointName host( hostName, port );
		char hostIpAddress[ IpEndpointName::ADDRESS_STRING_LENGTH ];
		host.AddressAsString( hostIpAddress );
		char buffer[IP_MTU_SIZE];
		 osc::OutboundPacketStream p( buffer, IP_MTU_SIZE ); 
		UdpTransmitSocket socket( host );

Und können somit unsere Ergebnisse verschicken:

		p.Clear();
		p << osc::BeginMessage( "x_1" ) << (float)ax  << osc::EndMessage;
		socket.Send( p.Data(), p.Size() ); 

Hier wird - allgemein dargestellt - gezeigt, wie wir eine OSC Nachricht verschicken. Der Inhalt des Objektes p der Klasse OSC muss zuvor gelöscht werden, damit eventuelle Vorbelegungen nicht mitgesendet werden. p wird auf einfache Art und Weise mit dem ‘<<’ – Operator “beschrieben” und anschließend versendet.

Funktion trackMe

Damit die unter der Funktion koord() gefundenen Punkte für den Nutzer optisch besser dargestellt werden, entschieden wir uns eine Funktion zu implementieren, die dies übernimmt. Wir nannten sie trackMe. Diese Funktion arbeitet wie koord, mit dem Unterschied, dass die Daten intern verarbeitet bleiben und für den Nutzer ein Bild erzeugt wird, das nur diese 2 Punkte darstellt.

		float gray[3200], nearestPoint, divisor = 1, dist[3200];
		IplImage *original = cvCreateImage(cvSize(48, 64), IPL_DEPTH_8U, 3);	
		IplImage *original_big = cvCreateImage(cvSize(240, 320), IPL_DEPTH_8U, 3);

Als erstes werden ein float Array für die übergebenen Bildpunkte der Kamera und zwei Bilder angelegt. Wir verwenden für die Bilddarstellung die Open Source Bibliothek OpenCv um die Bilder für den Benutzer darzustellen. Die Bilder sind vom Typ IPLImage und haben eine Bittiefe von 8 bit pro Pixel sowie 3 Farbkanäle, die benötigt werden, um ein Bild farbig darzustellen. Das entspricht einem Wertebereich von 0 bis 255. Die Auflösung der Kamera beträgt nur 64*50Pixel, was zur Folge hat, dass das dargestellte Bild für den Nutzer viel zu klein ist. Aus diesem Grund wird ein zweites Bild erzeugt, das 25 mal größer ist als das Original.

		pKamera->GetAmplitudes(gray, sizeof(gray));

Mit der Funktion GetAmplitudes(gray, sizeof(gray)) werden von der Kamera die Grauwerte des jeweiligen Bildes in im übergebenen Array gray[] gespeichert.

		for(int i = 0; i < sizeof(gray)/sizeof(float); i++){
			if(gray[i] > divisor)
				divisor = gray[i];	
		}
	 
		for(int i = 0; i < sizeof(gray)/sizeof(float); i++){
			gray[i] = gray[i]/divisor;
			if(gray[i] > 1)
				gray[i] = 1;
			if(gray[i] < 0)
				gray[i] = 0;
		}

		for (int i = 0; i < sizeof(gray)/sizeof(float); i++){
			z = (unsigned char)(gray[i]);
			gray_8[i] = (unsigned char)z;
		}


Da wir für die graphische Ausgabe 8bit-Bilder erzeugen wollen, von der Kamera allerdings float Werte geliefert werden, die zu dem in einem völlig unüberschaubaren Wertebereich liegen, skalieren wir diese zuerst auf einen für uns geeigneten Wertebereich. Dies erreichen wir, indem wir alle Werte durch den größten im Array gray[] vorkommenden Wert dividieren. Dadurch erhalten wir einen Wertebereich von 0 bis 1. Diesen belassen wir erstmal so und kopieren ihn in unser unsigned char gray_8 Array.

	gray_8[index_1] = 255; 
	gray_8[index_2] = 255;

Unser Einfärben bewältigen wir, indem wir den Array-Positionen unserer beiden nähesten Pixel den höchstmöglichen Wert zuweisen. So sind nur diese beiden im Array auffällig. Die restlichen Positionen im Array haben alle einen Wert von 0 oder 1. Wie sich das im Bild auswirkt zeigt sich etwas später in diesem Kapitel.

	for(int i = 0; i < 3072; i++){
		gray_8_besch[i] = gray_8[i];
	}

Unter Kapitel 7 haben wir das 4 Byte Alignment Problem angesprochen. Um diesem zuvorzukommen, kürzen wir unser Array um “zwei Zeilen”, also um 128 Grauwerte. Diesen Verlust nehmen wir bewusst in Kauf, da es sehr unwahrscheinlich ist, dass in diesen zwei Zeilen relevante Bewegungen stattfinden.

		for(int i = 0; i < 3072; i++){
			if( (i % 64) == 0 ){
				x -= 1;
				y = x;
			}
			gray_8_besch_rot[y] = gray_8_besch[i];
			y += 48;
		}

Dieses „beschnittene“ Array, mit jeweils 8 bit pro Arrayposition, lässt sich nun problemlos so bearbeiten, dass sich das Bild um 90° im Uhrzeigersinn dreht. Man sollte hierbei erwähnen, dass die Kamera tatsächlich ein 64*50 Pixel Bild liefert und nicht die in sämtlichen Datenblättern und Aufklebern auf der Kamera angegebenen 64*48 Pixel. Nach längerem E-Mail Verkehr mit Mitarbeitern der Herstellerfirma der Kamera PMDTec wurde uns dann mitgeteilt, dass die Kamera 64*50 Pixel liefert. Somit wurde eine Drehung für uns im “8-Bit-Bereich” ohne stark verzerrte Fragmente unmöglich. Bei den 3 Farbkanälen unserer IPLImages handelt es sich um die Aufsplittung der Farbe in einen RGB-Farbraum bzw. bei OpenCV in einen BGR-Farbraum. Der erste Kanal entspricht dem blauen Grundton, der zweite dem grünen und der letzte Kanal dem roten Farbton.

	for(int i = 0; i < 3072; i++){
			original->imageData[i*3]  = 0;
			original->imageData[i*3+1]= 0;
			original->imageData[i*3+2]= gray_8_besch_rot[i];	
	}

Bei der Übergabe des Graustufenarrays gray_8_besch_rot an das IPLImage original weisen wir zwei Farbkanälen den Wert 0 zu, nur einem die Werte des Arrays. Da aber nun nur zwei Werte des Arrays den höchstmöglichen Wert von 255 annehmen, die restlichen 0 oder 1 haben, erhalten wir ein schwarzes Bild mit zwei auffallend gefärbten Pixeln. Da wir diese in den dritten Kanal geschrieben haben, werden sie uns rot angezeigt.

	cvResize(original, original_big, 1);
	cvShowImage("track", original_big);


Mit der Funktion cvResize vergrößern wir das ursprüngliche Bild und geben es mit cvShowImage aus.

Funktion steuerung

Die Funktion steuerung implementierten wir, um die aus koord gewonnen Daten weiter zu verarbeiten. Unser Ziel war es, hier eine Anwendungssteurung zu kreieren, damit der Nutzer berührungslos die Windows Diashow steuern kann. Da die Funktion steuerung nur den „Auslöser“ für weitere Ereignisse darstellt, haben wir die „Auslöser“ Funktion von der „Ereignis“ Funktion getrennt. Die Funktion steuerung besteht also aus mehreren Dateien. Dadurch haben auch nachfolgende Gruppen die Möglichkeit leicht, durch Inkludieren der erforderlichen Datei, Ereignisse wie z.B. das Drücken einer Pfeiltaste zu simulieren. Die gesamte Einheit besteht aus steurung.cpp, slideshow.h und slideshow.cpp.

slideshow.h In dieser Headerdatei werden lediglich die Funktionen deklariert, die bestimmte Ereignisse realisieren. Um Zugriff auf diese zu haben, muss diese Datei entsprechend inkludiert werden. Slideshow.cpp Die Definition der „Ereignissfunktionen“ wurde hier realisiert. Sie simulieren Tastendrücke bzw ganze Tastenkombinationen.

void forward ()
{
	keybd_event (VK_RIGHT, 0x27 , KEYEVENTF_KEYDOWN , 0);
	keybd_event (VK_RIGHT, 0x27, KEYEVENTF_KEYUP, 0);
}

Hier wird z.B. das Drücken der rechten Pfeiltaste simuliert. keybd_event() ist eine von Windows bereitgestellte Funktion, die durch inkludieren von windows.h verwendbar wird. Sie verlangt als Argumente einen „virtual key”, den dazugehörigen HEX-Code, sowie die Position, die die Taste haben soll. Das letzte Argument dient zur weiteren Wertübergabe im Falle eines Tastendrucks. Die Werte für den „virtual key“ und den HEX-Code kann man aus der Tabelle zu den virtuellen Key-Events übernehmen. Mit “KEYEVENTF_KEYDOWN” bzw „KEYEVENTF_KEYUP“ lässt sich nun die gewünschte Taste drücken. Im Prinzip lässt sich somit jeder Tastendruck bis hin zu Tastenkombinationen simulieren.

steuerung.cpp In dieser Datei werden für die Steuerung die Daten der Kamera ausgewertet, Schwellwerte gesetzt und bei Übertreten der Schwellwerte Funktionen aufgerufen.

		if(zaehler_x == 0){
			alt_x_l = x_links;
			alt_x_r = x_rechts;
		}
		diff = alt_z_l - z_links;
		if(abs(diff) < 48)
			summe_z_l += diff;
		alt_z_l = z_links;
		diff = alt_z_r - z_rechts;
		if(abs(diff) < 48)
			summe_z_r += diff;
		alt_z_r = z_rechts;
		zaehler_x++;
		if(zaehler_x == 20){
			zaehler_x = 0;
			summe_x_l = 0;
			summe_x_r = 0;
		}

Hier am Beispiel der x-Koordinaten. Wir bilden eine Differenz der vorherigen und der aktuellen x-Koordinaten. Liegt diese Differenz in einem gültigen Bereich, wird diese aufsummiert.

		cvNamedWindow("Manuelle Justierung");
		cvCreateTrackbar("x-Schwelle","Manuelle Justierung",&x, 20, NULL);
		cvCreateTrackbar("y-Schwelle","Manuelle Justierung",&y, 20, NULL);
		cvCreateTrackbar("z-Schwelle","Manuelle Justierung",&z, 20, NULL);

Unter dem Kapitel Probleme sprechen wir das unterschiedliche Verhalten der Kamera auf verschiedene Lichtquellen an. Um diesem Verhalten entgegenzukommen, haben wir Trackbars eingebunden. Mit diesen Trackbars kann der Nutzer die Schwellwerte, die es zum Auslösen eines Ereignisses zu übertreten gilt, den individuellen Begebenheiten selbst bestimmen.

		if( summe_x_r < -x && summe_x_l > x){
				forward();
				summe_x_r = 0;
				summe_x_l = 0;
				zaehler_x = 0;
		}

Sobald diese Summe einen geeigneten Schwellwert erreicht, rufen wir die gewünschte Funktion auf und setzen die Summe der Differenzen zur erneuten Detektion auf 0. Während dieser gesamten Auswertung laufen Timer, die nach einer bestimmten Zeit die Berechnungen wieder auf 0 setzen. Damit erreichen wir, dass erkannte Bewegungen verworfen werden, falls diese die Schwellwerte nicht überschreiten. Dadurch kompensieren wir natürliche Körperbewegungen des Nutzers.

Kamerabild mit zwei nähesten Pixeln und Übersicht über verlinkte Funktionen

Erstellung eines NSI-Installers

Im Endstadium des Projektes wurde ein Windows Installer erstellt, der nachfolgenden Gruppen einen relativ leichten Einstieg in das Projekt ermöglichen soll. Der Vorteil liegt darin, dass der Installer alle nötigen Treiber mitliefert, gemäß dem Motto "Plug & Play" lässt sich die Kamera dann nach der Installation gleich in Betrieb nehmen. Somit sind keinerlei Vorkenntnisse wie das Einbinden von OpenCV 2.1 erforderlich.

Für den Installer wurde das Open Source Nullsoft Scriptable Install System (NSIS) verwendet. Mit Hilfe eines Skriptes lassen sich so aus der Release Version unseres Projektes durch Visual Studio 2008 mit einfachen Befehlen ein kompletter Installer zusammensetzen. Für die optische Aufwertung wird das Modern User Interface (MUI) herangezogen, welches zu Beginn eines Skriptes inkludiert werden muss.

		!include "MUI2.nsh"
		Name "Project3D 1.4.3"
		OutFile "install_Project3D.exe"
		...	.
		!define MUI_HEADERIMAGE_BITMAP "welcome_banner.bmp" ; optional
		!define MUI_ICON "setup.ico"
		...
		InstallDir "$PROGRAMFILES\Project_3D"
		...
		!insertmacro MUI_PAGE_WELCOME
		!insertmacro MUI_PAGE_LICENSE "readme.txt"
		...
		!insertmacro MUI_LANGUAGE "German"

In den darauffolgenden Zeilen lassen sich noch einige weitere Parameter eingeben, wie gewünschte Sprache, Icon, Einbinden von Copyrights etc. Ist das geschehen, gibt man die Reihenfolge der aufzutretenen Seiten an, die den Nutzer durch die Installation führen.

		SetOutPath $INSTDIR \bin
		FILE "pmdaccess2.dll"
		FILE "Interop.WMPLib.1.0.dll"
		FILE "Hauptprogramm.exe"...
		...
		SetOutPath $INSTDIR\doc
		FILE "readme.txt"
		...

Wichtig ist, dass man sämtliche programmrelevanten Dateien aus dem erstellten Release Ordner in den gewünschten Installationspfad kopiert, wie im Release-Ordner abgelegt.

		writeUninstaller "$INSTDIR\bin\uninstall.exe"

Mit dem Durchlaufen der Installation wird noch ein entsprechender De-Installer installiert, mit welchem das Programm restlos deinstalliert werden kann.

		Delete "$INSTDIR\bin\pmdaccess2.dll"
		Delete "$INSTDIR\bin\Interop.WMPLib.1.0.dll"
		Delete "$INSTDIR\bin\Hauptprogramm.exe"
		…
		RMDir "$INSTDIR"

Der De-Installer löscht zuerst sämtliche Dateien, angelegte Verküpfungen und anschließend die erstellten Programmordner.

		WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\projekt3d" 					 
               "DisplayName" "Project3D"

Um bei der Installation zu prüfen, ob bereits eine Version unseres Programmes installiert ist, tragen wir einen Schlüssel in die Registry unter der HKEY_LOCAL_MACHINE ein.

		function checkLegacyInstallation
		ClearErrors
		readregstr $1 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\project3d"  "DisplayName"
		IfErrors  done

		MessageBox MB_ICONEXCLAMATION "Another version of Project3D is already installed. 
                Remove all other installations of Project3D."
         	Abortd
		done:
		functionend

Zu Beginn der Setup-Routine wird nach diesem Schlüssel gesucht. Wird er gefunden, gibt es eine Fehlermeldung, dass bereits eine ältere Version installiert ist. Durch diesen einfachen Trick erreichen wir, dass Redundanzbildung unseres Programmes vermieden wird.

Eine komplette Anleitung sowie Tutorien findet man unter NSIS Users Manual

Das 4-Byte Alignment

Ein großes Problem bereitet das 4Byte Alignment bei der Drehung der vorhandenen 8-bit Bildmatrix.

"In der Rechnerarchitektur (Computer) bezeichnet man ein Datenelement (oder einen Operanden) mit n Bytes als im Speicher ausgerichtet (engl. Data Alignment), wenn dessen Adresse A ein ganzzahliges Vielfaches von n ist ( A mod n = 0). Falls n jedoch keine Potenz von 2 ist, muss für die Berechnung n auf die nächsthöhere Potenz von 2 aufgerundet werden. Beispiel: Ein 5-Byte-Datenelement muss entsprechend einem 8-Byte-Datenelement ausgerichtet werden." Wikipedia


Da auf einem 32-bit System entwickelt wurde, ist ein 4 Byte Alignment gegeben, entsprechend wäre das bei einem 64-bit System ein 8 Byte Alignment. Genau hier lag unser Problem, auf dessen Lösung wir erst durch Frieder Weiss kamen. In diesem Zusammenhang möchten wir hier unseren großen Dank zum Ausdruck bringen. Er war es, welcher uns auf das 4Byte Alignment hinwies.

Man benötigt pro Pixel 8 bit, das bedeutet auf eine Bildzeile von 50 Pixeln 400 bit, also 50 Byte. Nachdem man sich allerdings in einem 32-bit System befindet, wird dieses Datenelement als "nicht ausgerichtet (engl. Data Misalignment)" betrachtet. Um das Datenelement auszurichten, werden von der darauffolgenden Zeile 2 Byte "geklaut", somit ist das Datenelement 52Byte groß, also ein ganzes Vielfaches unseres 4-Byte Kriteriums. Nachdem allerdings 2Bytes der darauffolgenden Zeile "geklaut" werden, erhalten wir ein zeilenverschobenes Bild, das leider nicht weiterverwendbar ist. Frieder Weiss gab den Rat entweder die Bildmatrix entsprechend zu vergrößen, also auf 3328 Stellen, oder eben die Matrix so zu kürzen, dass sie von sich aus wieder ausgerichtet ist. In diesem Fall auf 3072 Stellen. Nach Einigung auf eine Kürzung des Arrays erhält man somit eine Kameraauflösung von 64x48 Pixel. Diese Auflösung ist auch die, die von PMDTec angegeben wird. Wir vermuten, dass das 4 Byte Alignment der Beweggrund des Unternehemens war, eine falsche Auflösung anzugeben. Aus rechnerischen Gründen ist die geringere Auflösung leichter zu handhaben als die eigentliche physikalische Auflösung, die vermutlich mit dem verwendeten Detektor zusammenhängt.

Downloadbereich

  • Zum Editieren des Skripts eignet sich besonders notepad++, Download unter: Notepad++