 
 
 
 
 
 
 
  
Dieses Kapitel stellt den internen Ablauf der Scanner-Applikation näher vor: die parallele Architektur, die Klassenhierarchie sowie das JAVA-Interface zur benutzerfreundlichen Bedienung des Programmes.
Zu Beginn werden für den weiteren Programmablauf relevante Einstellungen aus der Konfigurationsdatei scanner.cfg gelesen. Diese Parameter lassen sich grob in zwei Gruppen unterteilen:
 , die das Matching zwischen zwei Linien bestimmen; weiterhin diverse Variablen zur Steuerung der Linienerkennung
sowie Boolesche Parameter, die u.a. die Online-Anzeige während des Scannens ein- oder ausschalten.
, die das Matching zwischen zwei Linien bestimmen; weiterhin diverse Variablen zur Steuerung der Linienerkennung
sowie Boolesche Parameter, die u.a. die Online-Anzeige während des Scannens ein- oder ausschalten.Die nächsten Schritte dienen der Vorbereitung der Parallelisierung des Programmes (siehe dazu auch Abbildung 5.1): es werden zwei Pipes geöffnet, die die Kommunikation zwischen den parallelen Prozessen gewährleisten. Über die erste Pipe werden während eines 3D-Scans die laufenden Daten geschickt, so daß die Scannpunkte online angezeigt werden können. Der Benutzer kann somit den Scan online auf dem Bildschirm verfolgen. Das Anzeigeprogramm 2show (näher beschrieben in Abschnitt 6), welches diese Daten im Empfang nimmt, wird daraufhin mittels fork als paralleler Prozeß gestartet.
Die zweite Pipe dient der Kommunikation zwischen dem Hauptprozeß, der die Daten verarbeitet (siehe unten), und dem Kindprozeß, der die Daten liefert. Haupt- und Kindprozeß werden daraufhin gestartet, sobald die Pipe bereitsteht. Per Kommandozeilenparameter kann dem Programm mitgeteilt werden, aus welcher Quelle die Daten stammen sollen (intern wird dazu lediglich ein anderer Kindprozeß geforkt):
Ferner muß der Motor gesteuert werden, der den Scanner um seine horizontale Achse dreht, und es muß die Kamera ausgelöst werden, deren Bilder später als Quellen der Texturen in dem Anzeigeprogramm dienen. Schließlich hat der Prozeß dafür Sorge zu tragen, ,,Ausrutscher`` zu eliminieren, indem er bei einem Scan die Anzahl der Punkte, die eindeutig als Meßfehler klassifizierbar sind, mitzählt, korrigiert bzw., falls dies nicht mehr möglich ist, diesen Scan wiederholt.
Der Hauptprozeß übernimmt die Verarbeitung der Daten. Dies sind im Einzelnen:
 -Scans werden aus der Pipe gelesen:
-Scans werden aus der Pipe gelesen:
 - die Scanpunkte (
 - die Scanpunkte ( konstant). Letztere werden sofort in einem festen zweidimensionalen Array vom Typ Point-Pointer gespeichert,
da auf die Originaldaten von nahezu allen anderen Datenstrukturen aus referenziert wird und sie während des gesamten Programmablaufes bestehen bleiben.
 konstant). Letztere werden sofort in einem festen zweidimensionalen Array vom Typ Point-Pointer gespeichert,
da auf die Originaldaten von nahezu allen anderen Datenstrukturen aus referenziert wird und sie während des gesamten Programmablaufes bestehen bleiben.Das bereits im Abschnitt 3.2.4 angesprochene Kriterium zum Matchen von zwei Linien ist wie folgt realisiert:
Seien  die zwei zu untersuchenden Linien.Wenn nun gilt:
 die zwei zu untersuchenden Linien.Wenn nun gilt:
 und
 und  als hinreichend ähnlich angenommen. Eine Fläche, die beispielsweise
 als hinreichend ähnlich angenommen. Eine Fläche, die beispielsweise  als obere Grenzlinie besitzt, kann nun bis zu
 als obere Grenzlinie besitzt, kann nun bis zu  erweitert werden.
 erweitert werden. , der den Mittelpunkt des Objektes bezeichnet.
, der den Mittelpunkt des Objektes bezeichnet. ausgehend existieren 3 orthogonale Linien (parallel zur
 ausgehend existieren 3 orthogonale Linien (parallel zur  -,
-,  - bzw.
- bzw.  -Achse des Koordinatensystems),
die somit einen eindeutigen Würfel aufspannen.
-Achse des Koordinatensystems),
die somit einen eindeutigen Würfel aufspannen.
 mit sich: Das Paar
 mit sich: Das Paar  beschreibt eine Kugel, welche das gesamte Objekt (inklusive Bounding Box) einfaßt.
Auf diese Weise ist es möglich, einen sehr schnellen Test durchzuführen,
ob überhaupt eine Möglichkeit besteht, daß das zu untersuchende Element sich nahe genug an der Bounding Box des Objektes befindet, um aufgenommen zu werden.
 beschreibt eine Kugel, welche das gesamte Objekt (inklusive Bounding Box) einfaßt.
Auf diese Weise ist es möglich, einen sehr schnellen Test durchzuführen,
ob überhaupt eine Möglichkeit besteht, daß das zu untersuchende Element sich nahe genug an der Bounding Box des Objektes befindet, um aufgenommen zu werden.
   int distance = (int)((o->center - *p).norm()) - 
                  o->radius;
   if (distance > 0) o->radius += distance;
   // difference of border- and point coordinate
   int delta;   
   // is the point right of the right border, but not 
   // too far away?
   if (delta = (p->x - o->xAxis.to->x),
       delta > 0 && delta < maxObjectDistance) {
      // move the leftmost border;
      o->xAxis.to->x    = p->x;             
      // correct the center, according to the x-Axis
      o->center.x      += (int)(delta/2);
      // now we have to correct all the x-coordinates 
      // of all other Axis! (otherwise, the axis  
      // wouldn't cross in the center anymore)
      o->yAxis.from->x += (int)(delta/2);   
      o->yAxis.to->x   += (int)(delta/2);   
      o->zAxis.from->x += (int)(delta/2);
      o->zAxis.to->x   += (int)(delta/2);
   }
   // ..... (repeat for the other side of the xAxis; 
   // afterwards, for y- and zAxis!)
 Element) in Zeit
Element) in Zeit  anstatt der durchaus üblichen Laufzeit
 anstatt der durchaus üblichen Laufzeit  durchzuführen.
durchzuführen.
An dieser Stelle soll das Benutzer-Interface kurz dargestellt werden. Zwar ist es möglich, sowohl das OpenGL- als auch das Scanner-Programm von der Kommandozeile aus zu starten. Die vom Benutzer modifizierbaren Parameter werden dann - wie bereits in Abschnitt 5.1 beschrieben - aus der Konfigurationsdatei scanner.cfg gelesen.
Eine sehr viel komfortablere Möglichkeit der Modifikation besagter Parameter und Kontrolle der externen Programme bietet jedoch das graphische Benutzerinterface, geschrieben in der Sprache JAVA.
Innerhalb des Scan-Bereiches lassen sich all jene Parameter einstellen, welche den eigentliche Scan-Vorgang beeinflussen. Die Betätigung des Scan-Buttons startet die Scanner-Applikation unter den spezifizierten Parametern.
Der Display-Bereich beinhaltet dementsprechend Parameter, welche die Anzeige beeinflussen. Desweiteren existiert ein Textfenster, in welchem der textuelle Output der Scanner-Applikation ausgegeben wird (siehe auch Abbildung 5.4). Dies sind Informationen, welche die einzelnen Algorithmen als Statusmeldungen dem Benutzer übermitteln, wie beispielsweise die Anzahl erkannter Linien pro Scan. Diese Statusmeldungen werden bei manuellem Start der Scanner-Applikation auf der Textkonsole ausgegeben. Der Display-Button startet das Anzeigeprogramm 2show, welches den zuletzt durchgeführten Scan visualisiert.
Die Betätigung des Motor-Buttons gibt einen Schieberegler frei, mit dessen Hilfe sich die Scanner-Hardware manuell direkt um den eingestellten Winkel drehen läßt.
Bei der Wahl File werden eine Reihe von Parametern (wie beispielsweise die Schrittweite des Motors) deaktiviert, die sich ausschließlich auf das Verhalten der Scanner-Hardware während eines 3D-Scans auswirken.
Hier werden bei der Wahl von dump only alle Parameter deaktiviert, die sich auf Algorithmen zur Linien-, Flächen- oder Objekterkennung auswirken. Desweiteren ist es nicht möglich, eine Kombination zu wählen, so daß die Daten aus Dateien gelesen und unbearbeitet als Datei abgespeichert werden.
 aus Formel (5.1), der angibt, wie weit die Endpunkte zweier 
Linien voneinander entfernt liegen dürfen, um von dem Flächenerkennungs-Algorithmus gematcht zu werden.
 aus Formel (5.1), der angibt, wie weit die Endpunkte zweier 
Linien voneinander entfernt liegen dürfen, um von dem Flächenerkennungs-Algorithmus gematcht zu werden. (Formel (5.3) und gibt den maximalen Winkel zwischen zwei zu matchenden Linien ab.
 (Formel (5.3) und gibt den maximalen Winkel zwischen zwei zu matchenden Linien ab. ) werden von vornherein als eigenständige Objekte angesehen. Alle weiteren 
Elemente werden versucht zu Objekten zu segmentieren.
) werden von vornherein als eigenständige Objekte angesehen. Alle weiteren 
Elemente werden versucht zu Objekten zu segmentieren.
Sobald das Demonstrationsfenster geschlossen ist, werden die vor dem Demobetrieb geltenden Einstellungen wieder hergestellt, die Applikation kann wie gewohnt bedient werden.
Das externe Programm scanner wird mittels Runtime.getRuntime().exec() gestartet. Dabei ist es möglich, die Ausgaben des aufgerufenen Programmes auf den Standardausgabestream abzufangen und als InputStream weiterzuverarbeiten: wie in Abbildung 5.5 zu sehen, wird die Ausgabe in das Textfenster der Applikation geschrieben. Aus Performancegründen wird dabei über den Inputstream noch ein BufferedReader gelegt.
Damit die Ausgabe schon während der Laufzeit des aufgerufenen Programmes angezeigt wird - und nicht erst nach Verlassen der Funktion, wenn die Bildschirmanzeige neu gezeichnet wird - war es notwendig, diese Funktion als eigenständigen Thread zu realisieren.
class startScanner extends Thread {
   Frame father;
   startScanner(Frame f) { father = f; }
   public void run() {
      Process scanner;
      try {
         // save the current values
         father.writeValues(new File(System.getProperty(üser.dir") +
                                     "/bin/scanner.cfg"),true);
         // compose command string
         String exec = "bin/scanner " + 
            ("File".equals(father.InputComboBox.getSelectedItem()) ? 
            "f" : ("dump only".equals(father.OutputComboBox.
	          getSelectedItem()) ? "d" : ß"));
         // start the scanner
         scanner = Runtime.getRuntime().exec(exec);
         // catch scanner output via 'getInputStream()'
         BufferedReader inStrm = new BufferedReader(
                                 new InputStreamReader(
                                     scanner.getInputStream()));
         String c;
         while ( (c = inStrm.readLine()) != null) {
            father.jTextArea1.append(c + "\n");
         };
      } catch (Exception e) { 
         System.err.println("Error: " + e); 
         e.printStackTrace();
      }
   }
}
 
 
 
 
 
 
