Dokumentation der Diplomarbeit tell.me von Marcel Senkpiel, betreut durch Prof. Tjark Ihmels entstanden im WS 2005/ 2006 an der Fachhochschule Mainz Grafisch / generative Analyse einer Text Bild Struktur, so lautet offiziell das Thema meiner Diplomarbeit. Zu Anfang wusste ich nur, dass es interessant wäre, wenn ich einen Text in ein schönes Bild umwandeln könnte und dieses Bild dann wieder in den Text zurückverwandeln könnte. In den letzten Monaten habe ich mich mit dem Thema der grafischen Verschlüsselung beschäftigt und das Programm tell.me entwickelt. Was ist tell.me? tell.me ist eine Anwendung, die einen beliebigen Text in eine Grafik umwandeln kann und die mit ihr erstellten Grafiken wiederum in den Ursprungstext zurückverwandeln kann. Man könnte das Prinzip als grafische Verschlüsselung bezeichnen. Die erstellten Grafiken fungieren als Speicher von Informationen jeglicher Art, die mit den Zeichen des ASCII Codes darstellbar sind. Sie sind vergleichbar mit zweidimensionalen Barcodes wie dem Matrix Code, die im Vergleich zu eindimensionalen Barcodes mehr Informationen speichern können. DataMatrix Im Vorfeld wurden bestimmte Anforderungen an die Grafik gestellt. Neben der Eigenschaft, dass sie in den Ursprungstext zurückverwandelbar sein mußte, sollte sie sich durch eine Formensprache mit eigener Ästhetik auszeichnen. Dabei sollte der Aufbau der Grafik möglichst platzsparend in Hinsicht auf Abmessung und, in Verbindung damit, Dateigröße sein. Zudem war eine ablesbare statistische Auswertung des Textes in irgendeiner Form wünschenswert. Zur Funktionsweise Vorüberlegungen 1. Ein unformatierter, ASCII codierter Text ist aus maximal 255 verschiedenen Zeichen, besser Bausteinen, aufgebaut, darunter alphanumerische Zeichen, Steuerzeichen und Sonderzeichen. Jeder Baustein wiederum kann beliebig oft im Text vorkommen. 2. Stellt man sich die komplette Zeichenfolge eines Textes durchnumeriert vor, so hat jedes einzelne Zeichen eine eigene Indexposition innerhalb des Textes. Jedem Baustein ist also eine Reihe von Indexpositionen zugewiesen. 1
Um den Aufbau eines Textes zu beschreiben, genügen also die Angaben: welcher Baustein sitzt an welchen Indexpositionen. Nun ist es auch möglich, den Text nicht in einzelne Zeichen zu zerlegen und diesen dann Indexpositionen zuzuweisen, denkbar ist auch eine Zerlegung in Zeichenpaarungen. Jedes Zeichen, außer dem letzten, hat im Text ein Zeichen, das nach ihm steht: Vorgänger und Nachfolger. Der Nachfolger hat die Indexposition des Vorgängers plus eins. Anstatt die Indexpositionen von beiden Zeichen anzugeben, genügt es also die genaue Zeichenpaarung und die Indexposition des Vorgängers anzugeben. (Somit werden natürlich alle Indexpositionen, bis auf die erste und die letzte, zweimal abgespeichert, einmal als Vorgänger und einmal als Nachfolger.) Ein Baustein ist dann nicht mehr der einzelne ASCII Charakter, sondern die Paarung zweier. (An dieser Stelle ließen sich anhand der Verteilung von Häufigkeiten bestimmter Paarungen erste einfache Aussagen über die Beschaffenheit einer Sprache machen.) Umsetzung Es gilt also, eine Zeichenpaarung und deren Indexpositionen grafisch darzustellen, was sicherlich mit einer Vielzahl unterschiedlicher Methoden erreicht werden kann. Das hier angewandte Verfahren macht Gebrauch vom Binärsystem. Jede Zahl im Dezimalsystem kann in ein Binärsystem umgewandelt werden und stellt sich dann als eine Abfolge von z.b. Nullen und Einsen dar. Die Indexpositionen einer Zeichenpaarung lassen sich also auch im Binärsystem darstellen. Dabei gilt zu beachten: je größer die Dezimalzahl, desto länger die Binärzahl. Bei sehr langen Texten werden die Indexpositionen immer höher, die Binärzahlen also immer länger. Um das zu vermeiden (warum wird später deutlich, Stichwort Platzersparnis), können die Indexpositionen in Distanzen zwischen Indexpositionen umgewandelt werden. Hätte beispielsweise die Zeichenpaarung en unter anderen die Indexpositionen [1034, 1040, 1051, 1095 ], so werden an deren Stelle zunächst die Zahlen 1034, 6, 11 und 44 gespeichert ( weil 0 + 1034 = 1034, 1034 + 6 = 1040 etc.) und diese dann ins Binärsystem umgewandelt. Zu diesem Zeitpunkt liegt ein Text, ganz grob, in folgender Form vor: Zeichenpaarung en : rt : Liste der Indexe, umgeschrieben in Distanzen, binär dargestellt [10, 101, 11, 1011 ] [1001, 10, 1110 ] Das ganze wird wie folgt umgeschrieben: Zeichenpaarung en : rt : Liste der Indexe, umgeschrieben in Distanzen, binär dargestellt [2,1,0,2,1,0,1,2,1,1,2,1,0,1,1,2, ] [2,1,0,0,1,2,1,0,2,1,1,1,0,2, ] 2
Die Zweien trennen die einzelnen Distanzen in einfacher Art und Weise voneinander (streng genommen ist das dann eigentlich kein Binärsystem mehr). Umsetzung in die Grafik Ich will versuchen, das Verfahren zunächst etwas anschaulicher zu erklären. Angenommen auf einem schneebedeckten Fußballfeld wären an dem einen Elfmeterpunkt ein kleines e in den Schnee gemalt, an dem anderen Elfmeterpunkt ein kleines n. Die beiden Zeichen stehen für die Zeichenpaarung en. Am Punkt e steht eine Person, in der Hand einen Zettel mit der Liste der Distanzen (bzw. Indexe) der Paarung en. Nun soll die Person für jede Binärzahl in der Liste einen Schritt in Richtung n machen, für jede Null einen Schritt mit dem linken Fuß, für jede Eins einen mit dem rechten (sie wird auch auf einem Bein hüpfen müssen). Für jede Zwei soll sie ein Kreuz mit der Fußspitze in den Schnee malen. Sind alle Binärzahlen abgelaufen und das n ist noch nicht erreicht, so soll die Person ein n vor sich in den Schnee malen, das andere n wird stattdessen verweht. Betrachtet man sich nun die Fußspur, kann man anhand der Schrittfolge genau nachvollziehen, welche Binärzahlen in welcher Reihenfolge auf dem Zettel der Person standen. Man weiß außerdem, daß die Person vom e zum n gelaufen ist, da die Fußspuren vom e wegführen. Die Person hat am n angekommen alle Indexpositionen der Zeichenpaarung en im Text abgelaufen. Nun könnte die Person zum e zurückkehren (am besten zurückfliegen) und von hier aus eine weitere Zeichenpaarung, die mit e beginnt, beispielsweise e!, in den Schnee laufen. Dabei muß sie beachten, daß sie nicht über die bereits vorhandenen Spuren läuft, da diese sonst unleserlich würden. Außerdem darf sie das Fußballfeld nicht verlassen, da außerhalb kein Schnee liegt und die Spur nicht lesbar wäre. Die Person läuft also wiederum los und achtet bei jedem Schritt darauf, der bereits vorhandenen Spur, ihrer eigenen Spur und dem Spielfeldrand nicht zu nahe zu kommen. Kommt sie einem davon doch einmal zu nahe, so nutzt sie den Zeitpunkt einer Zwei in der Liste (Kreuz mit der Fußspitze) für einen Richtungswechsel hin zu einer unbetretenen Fläche. Sind alle Distanzen der Liste gelaufen, malt die Person das! vor sich in den Schnee. Nun könnte die Person wiederum zum e zurückkehren und eine weitere mit e beginnende Paarung ablaufen, sie könnte aber auch zum n oder zum! gehen und eine Paarung mit diesen als Vorgängern ablaufen. Dieser Prozess setzt sich fort, bis die Distanzlisten aller Zeichenpaarungen abgelaufen wurden. Das ist in etwa das Prinzip. In der konkreten Umsetzung werden anstatt Fußspuren auf Fußballfeldern Linien auf Pixelflächen gezeichnet. Der Läufer bewegt sich in Pixelschritten auf einer mit 1x1 Pixeln gerasterten Fläche und zeichnet das Pixel, auf dem er sich befindet, farbig. Start und Endpunkt bilden die ASCII Zeichen einer Paarung, im Beispiel e und n. Jedes Zeichen wird in dessen ASCII Code (numerisch) umgewandelt. Diese Zahl wird auf der Fläche als Abfolge magentafarbener und weißer Pixel binär dargestellt. Die Ansammlung dieser Punkte beschreibt einen Knoten (Elfmeterpunkt). Von jedem dieser Knoten können vier Linien (bzw. Fußspuren) ausgehen, die wiederum zu einem anderen Knoten führen. Ein Knoten kann also eingehende und ausgehende Linien haben. Eine ausgehende Linie bestimmt das ASCII Zeichen des Knoten als den Vorgänger in einer Zeichenpaarung und umgekehrt. 3
Bei zwei Knoten, die miteinander durch eine Linie verbunden sind, benennen die Knoten die Zeichenpaarung. Die Linie beinhaltet die Information über deren Indexpositionen innerhalb des Textes. Im Unterschied zum Beispiel mit dem Fußballfeld hat der Pixelläufer keinen linken und rechten Fuß. Das würde nämlich bedeuten, dass die Linie nicht hauptsächlich aus schwarzen Pixeln aufgebaut ist. Jeder Fuß hätte seine eigene Farbe und die Linie wäre beispielsweise abwechselnd schwarz und rot. Stattdessen besitzt der Läufer ein bestimmtes Schrittmuster. Der Läufer steht auf einem Pixel von dem aus er einen Schritt in acht verschiedene benachbarte Felder machen könnte. Hat der Läufer z.b. Richtung Westen eingeschlagen wird sein nächster Schritt nicht ostwärts gehen, weil er sonst auf seine eigene Spur zulaufen würde und die wäre dann nicht mehr lesbar. Es bleiben fünf Felder zur Auswahl. Jedem dieser Felder ist entweder eine Null oder eine Eins zugeordnet. Welches der Felder er also betritt gibt die nächste Binärzahl auf seiner Liste an. Prinzipiell hat der Läufer ein einziges Schrittmuster. Dieses wird aber je nach eingeschlagener Richtung gedreht: Hat der Läufer beispielsweise Richtung Norden eingeschlagen und die nächste Binärzahl auf seiner Liste ist eine Eins, so bleiben ihm zwei mögliche benachbarte Pixelfelder, die er betreten kann. Welches er wählt hängt mit davon ab, wieviel Platz ihm zur Verfügung steht (er hält sich von anderen Spuren fern). Die Richtung des Läufers, also Nord, Ost, Süd oder West, muß zur späteren Rückwandlung in den Text angegeben werden. Beim späteren Auslesen des Textes wird das Schrittmuster wie eine Schablone an jedes farbige (zumeist schwarze) Pixel der Linie angelegt. Dabei muß man es immer entsprechend der Richtung gedreht wissen. Im Beispiel Fußballfeld kann man es sich so vorstellen, dass der Läufer für eine Zwei in der Liste nicht nur ein Kreuz mit der Fußspitze in den Schnee malt, sondern einen der Buchstaben N, O, S und W für die Anfangsbuchstaben der nächsten Richtung. Der Läufer auf dem Pixelfeld benutzt stattdessen für jede der vier Richtungen eine eigene Farbe, die später als Richtungsangabe interpretiert wird. Der Läufer muß nicht zwangsläufig seine Wanderung an einem Knotenpunkt beginnen. Auch eine Linie selbst bzw. eine Distanz innerhalb der Linie kann als Knoten dienen. Läuft der Läufer beispielsweise die Distanzen der Paarung en ab, so wird eine dieser Distanzen 4
(bzw. Indexpositionen) vielleicht die erste Distanz (also das erste Auftauchen einer Paarung innerhalb des Textes) einer anderen Paarung, deren Vorgänger n ist, sein. Erste deshalb, weil die Addition aller vorherigen Distanzen in einer Distanzliste die nächste ergibt. Fehlt eine (so z.b. die Erste), so wird der Wert der folgenden Indexpositionen verfälscht. Durch den Einsatz dieser Sprösslinge wird eine höhere Dichte der Grafik erreicht, was der Platzersparnis dient. Die konkrete Umsetzung mit Macromedia Director und Lingo Der Programmcode von tell.me kann in drei Gruppen unterteilt werden: 1. Skripte zur Darstellung der Benutzeroberfläche 2. Skripte zum Umwandeln des Textes in das Bild 3. Skripte zum Umwandeln des Bildes in den Text Ohne allzu sehr auf programmiertechnische Details und die Syntax der angewendeten Programmiersprache Lingo einzugehen, will ich die eigentlichen Herzstücke der zweiten und dritten Gruppe kurz beschreiben. Text zu Bild 1. Erstellung der Distanzlisten: createcharprops() Das Skript erzeugt eine Liste CharProps mit allen im Text vorkommenden Zeichen Paarungen, die wiederum eine Liste mit deren Distanzen/Indexpositionen beinhalten. Die Liste ist nach der Häufigkeit der Vorgänger innerhalb der einzelnen Paarungen geordnet. Sie wird später von den Läufern von der häufigsten zur seltensten Paarung abgearbeitet. 2. Erzeugung eines Knotens: drawmainnode() Für jede Distanzliste (jeden Link) werden zwei Knoten gezeichnet, einen für Vorgänger und einen für Nachfolger (außer der Link selbst dient als Knoten, Sprösslinge). Ein Knoten wird in der Darstellung als Ansammlung magentafarbener Pixel angezeigt. Er beinhaltet binär und farblich codierte Informationen über sein ASCII Zeichen, seinen Registrierungspunkt auf der Zeichenfläche und die vier Positionen zum Andocken einer Linie. 3. Zeichnen einer Linie: drawlink() Das Child Objekt dieses Skripts zeichnet eine Linie von einem Knoten zu einem anderen. Dabei scannt es die Umgebung nach bereits gezeichneten Linien ab, um diese nicht zu berühren. Eine Berührung käme einer Gabelung der Linie gleich, was sie unlesbar machen würde. Weiterhin berechnet das Objekt die tendenzielle Richtung der Linie und erzeugt zudem Sprösslinge. Sind alle Einträge in Charprops gezeichnet, ist das Bild fertig. 5
Weitere Skripte übernehmen hauptsächlich die Aktualisierung der Liste Charprops wenn ein Link gezeichnet oder ein Knoten gesetzt wurde. Bild zu Text 1. Finden der Link Köpfe: createlinklist() Das Child Objekt sucht nach den rot dargestellten Registrierungspunkten der Knoten und stellt fest, welche Links vom den Knoten wegführen. Ein so gefundener Link Kopf wird mit seiner Position in einer Liste aller Links (LinkList) gespeichert. 2. Verfolgen der Spur: scanlink() Führ jeden Eintrag in LinkList wird ein Objekt zum Lesen des Links mittels Schablone erzeugt. Mit Hilfe der farbigen Richtungsmarkierungen kann die Schablone (das Schrittmuster) entsprechend der Richtung der Linie gedreht werden. Hat das Objekt das Linkende erreicht, so wird der ASCII Code des erreichten Knotens bestimmt. Mit Hilfe des bereits am Linkkopf ermittelten ASCII Codes des Vorgängers ergibt sich die Zeichenpaarung. Am Ende wird die Paarung an den ermittelten Indexpositionen in den Text eingesetzt. Sind alle Links erfolgreich gescannt ist der Text komplett. Die Benutzeroberfläche Die Benutzeroberfläche sollte reduziert und funktional sein, um nicht von den eigentlichen Darstellern Text und Bild abzulenken. Außerdem sollte sie eine möglichst große Fläche für das Aufziehen großer Bildformate bieten und damit für das Speichern großer Textmengen. Ein Objekt aus zwei Quadraten stellt zwei Eingabefenster für Text und Bild dar. Durch anklicken mit der Maustaste wird ein Eingabefenster aktiv. Ein Statusbalken am linken Bildrand zeigt dem Benutzer was als nächstes zu tun ist und dient als Steuerpult für Funktionen wie Speichern oder Laden. Ein Text kann getippt, eingefügt oder geladen werden und dann in ein Bild verwandelt werden. Das entstandene Bild kann daraufhin als.png Datei auf dem Rechner des Benutzers gespeichert werden, zudem besteht die Möglichkeit, es im Postkartenformat auszudrucken. In der nächsten Version von tell.me soll eine entsprechende Scan Funktion integriert werden, die auch gedruckte Bilder in einen Text zurückverwandeln kann. Das erstellte Bild wird rechts unten mit einem Wasserzeichen versehen, um es bei erneutem Laden als tell.me Bild zu identifizieren. 6
Hat der Benutzer einen Text ins linke Fenster eingegeben, so kann er das rechte Fenster mittels zweier Skalierungspfeile auf ein gewünschtes Format einstellen. Dabei gilt: je größer das Format, desto höher die maximale Anzahl an Zeichen im Text. Aktuelle sowie maximale Anzahl an Zeichen werden am oberen Bildrand angezeigt, außerdem das eingestellte Bildformat. Werden mehr Zeichen eingegeben als der Maximalwert könnte es sein, dass das Bild nicht zu Ende gezeichnet werden kann. Beim Zeichnen des Bildes besteht für den Benutzer die Möglichkeit, mittels eines Schiebebalkens und dreier Optionsfelder Einfluß auf die Linienführung zu nehmen. Die gesteuerten Parameter werden dabei nicht angezeigt. Was bietet tell.me? tell.me bietet mir zunächst die Ästhetik der Grafik, mit dem Hintergedanken, dass sich hier konkrete Informationen grafisch selbstorganisieren. tell.me eignet sich zur verschlüsselten Kommunikation, sei es durch auf Webseiten eingebauter tell.me Bilder oder durch deren Versand per Email. Die relativ geringe Dateigröße und die Darstellungsfähigkeit der.png Dateien im Internet machen derlei Verbreitung möglich. Durch drucken und scannen von Bildern könnte eine verschlüsselte Kommunikation auch außerhalb des digitalen Raumes stattfinden. Das schafft ein breites Spektrum denkbarer Anwendungen, ähnlich denen bereits existierender Modelle an zweidimensionalen Barcodes. Für mich besitzen die Bilder zudem eine lyrische Qualität. In dem Moment da der Text erscheint wird dem Bild schlagartig eine Bedeutung zugeordnet. Automatisch beginnt der Benutzer zu assoziieren und zu deuten. Geplante Erweiterung des Funktionsumfangs Wie bereits erwähnt soll in der kommenden Version noch die Möglichkeit gegeben sein, ausgedruckte tell.me Bilder einzuscannen und und den Text auszulesen. Außerdem wäre ein integrierter Email Versand sicherlich nützlich. Ursprünglich war tell.me als Internet Anwendung konzipiert, mit der man auf die Schnelle Bilder generieren und auslesen kann. Das käme der Anwendung zu Gute, da bei diesem Projekt der Aspekt der Kommunikation sicherlich ein wichtiger ist. Da es aufgrund von Sicherheitsaspekten nicht einfach ist, Dateien aus dem Shockwave Player heraus zu speichern oder in ihn zu laden, konnte das noch nicht realisiert werden. 7
Anhang A Beispiel für Lingoskript: createcharprops() global gglobalsinterface, gglobals, gworkimg on createcharprops astarttime = the milliseconds --INIT if gglobalsinterface.state = #lfl then thetext = sprite("foldarrow_left").ptextstorage else thetext = member("text").text end if CharProps = [:] gglobals.charprops = [:] NumberOfChars = thetext.char.count CharList = [62,62,32] repeat with i= 1 to NumberOfChars thechar = (thetext.char[i]).chartonum CharList.add(theChar) CharList.add(32) NumberOfChars = CharList.count --CREATE repeat with i= 1 to NumberOfChars -1 charcurrent = CharList[i] strcharcurrent = string(charcurrent) charnext = CharList[i +1] strcharnext = string(charnext) --CREATE A PROP ----charprop tmpcharprop = getaprop(charprops, strcharcurrent) if voidp(tmpcharprop) then acharprop = [#char:charcurrent,\ #indices:[i],\ #successors:[:] ] CharProps.addProp(charCurrent, acharprop) else tmpcharprop.indices.append(i) end if ----successorprops tmpsuccessor = getaprop(charprops[strcharcurrent].successors, strcharnext) if voidp(tmpsuccessor) then asuccessorprop = [#successor: charnext,\ #number: 1,\ #firstaas: i+1,\ #indices: [0,i],\ #distsbin: [],\ #offshoots: [:] ] CharProps[strCharCurrent].successors.addProp(charNext, asuccessorprop) else tmpsuccessor.number = tmpsuccessor[2]+1 tmpsuccessor.indices.append(i) end if --INDICES 2 DISTANCES-- repeat with charprop in CharProps repeat with successor in charprop.successors tmpindices = successor.indices NumberOfIndices = tmpindices.count -1 distsbin = [2] repeat with j = 1 to NumberOfIndices adistbin = convinttobin(tmpindices[j+1]-tmpindices[j], 0) repeat with jj = adistbin.length down to 1 distsbin.append(value(adistbin.char[jj])) distsbin.append(2) successor.distsbin = distsbin 8
--SORT CHARPROPS tmpcharprops = [CharProps[1]] NumberOfCharProps = CharProps.count repeat with k = 2 to NumberOfCharProps NumberOfIndices = CharProps[k].indices.count repeat with kk = 1 to k-1 sortednumberofindices = tmpcharprops[kk].indices.count if NumberOfIndices > sortednumberofindices then tmpcharprops.addat(kk, Charprops[k]) exit repeat else if kk = k-1 then tmpcharprops.append(charprops[k]) end if ----als properties Charprops = [:] repeat with charprop in tmpcharprops charprop.deleteat(2) Charprops.addProp(charProp[#char], charprop) --SORT SUCCESSORS repeat with charprop in Charprops tmpsuccessor = [charprop.successors[1]] NumberOfSuccessors = charprop.successors.count repeat with n = 2 to NumberOfSuccessors repeat with m = 1 to n-1 if charprop.successors[n][#number] >= tmpsuccessor[m][#number] then tmpsuccessor.addat(m, charprop.successors[n]) exit repeat else if m = n-1 then tmpsuccessor.append(charprop.successors[n]) end if ----als properties charprop.successors = [:] repeat with successor in tmpsuccessor charprop.successors.addprop(successor[#successor], successor) --SEARCH OFFSHOOTS repeat with charprop in Charprops repeat with successor in charprop.successors repeat with aindex in successor.indices repeat with s in Charprops[string(successor.successor)].successors if s.firstaas = aindex + 2 then successor.offshoots.addprop(s[1], aindex) gglobals.charprops = Charprops end 9
Anhang B Screenshots 1. inaktiv 2. Maus über Text 3. aktivierte Textbox 4. Text laden 5. Text geladen 6. Bildformat einstellen 7. Zeichenvorgang 8. Zeichnung fertiggestellt 9. Bild laden 10. Bild geladen 10
Hiermit bestätige ich, dass ich die vorliegende Arbeit selbstständig und ohne fremde Hilfe angefertigt habe. Marcel Senkpiel Wiesbaden, den 13.02..2006 11