Haptisches Feedback

Wieder ein Beitrag aus den Projektemacher Labs…

…dank Corona nun endlich abgeschlossen:

Da es in diesem Blog auch darum gehen soll analoge Inhalte innovativ in den digitalen Raum zu transportieren, hier ein Versuch Stoffe fühlbar zu machen…

Vor einiger Zeit habe ich ein Stoffmusterbuch erworben:

Textil-Lexikon: Ein Lehrbuch und Nachschlagebuch für den Textileinzelhandel und die Gewebeverarbeitung von Wilhelm Spitschka, Franckh, Stuttgart 1928.  

Darin sind die Muster nicht einfach abgedruckt, sondern aufwendig ausgeschnitten und eingeklebt. Der entsprechende Eintrag findet sich bei Blaufußtölpel. Diese sollen mit diesem Beitrag auf eine neue Art erfühl und damit erfahrbar gemacht werden.

Vorbereitung der Bilddaten

Der erste Schritt war die Vorbereitung der Bilddaten: Ausgangsbasis für taktiles Feedback sind die Bilddateien, sowie einer manuellen Markierungen für die Abschnitte (Bounding Box) mit Stoff.

Aus ihnen wurden mit etwas automatisierter Nachbearbeitung “Höheninformationen” gewonnen. Wobei es allerdings nur zwei Abstufungen gibt. Diese Schritte sind für jeden einzelnen Bildausschnitt konfigurierbar, nach der Bearbeitung wurde das Ergebnis nochmals gerastert, um eine Auflösung von fühlbarer Punkten zu erhalten, dabei wird eine Kantenlänge von 1mm verwendet, da das ungefähr der Auflösung einer menschlichen Fingerkuppe entspricht.

Als Beispiel dient hier der Cordsamt:

Ausschnitt zur Analyse

Ausschnitt zur Analyse

Kantenerkennung

Kantenerkennung

Kantenverbesserung

Kantenverbesserung

Weichzeichnen 1

Weichzeichnen 1

Weichzeichnen 2

Weichzeichnen 2

Graustufen

Graustufen

Mittelwert

Mittelwert

Binarisieren

Binarisieren

Reduktion auf eine Pixelkantenlänge von 1mm

Reduktion auf eine Pixelkantenlänge von 1mm

Beispiel: Das Ergebnis der Vorverarbeitung als JSON-Array
[
  [1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,0,1,0,0,0,0,1,1,1,1,0,1,1,0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,1,0,1,1,1,0,1,1],
  [0,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,0,0,1,1,0,1,1,1,0,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0],
  [0,0,0,1,1,0,0,1,1,1,1,0,0,1,1,1,1,0,1,1,1,0,1,1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1],
  [0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,0,1,1,0,1,0,1,0,1,1,1,0,1,0,1,1,1,1,1,0,0],
  [1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,1,1,0,0,1,0,1,1,1,0,1,0,0,1,0,0,1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,0,0,1,0,1,1,0,1,1,1,1],
  [1,0,1,1,1,1,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,0,0,1,1,0,1,1,0,1,1,1,0,1,1,0,0,1,0,1,1,1,1,0,1,0,1,1,1,1,0,0,1,1,0,0],
  [1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,0,1,1,0,1,1,1,1,1,1,0,1,1,0,1,1,1,1],
  [1,1,1,1,0,0,1,1,0,0,1,1,1,0,0,1,0,1,1,1,0,0,0,1,0,0,1,0,0,1,0,0,1,1,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1,0,1,1,1,0,1,1,0,0],
  [1,1,1,1,1,0,1,1,1,1,0,0,1,1,0,0,1,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,0,0,1,0,1,1,1,1],
  [1,1,1,1,0,1,1,1,1,1,1,0,1,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,0,0,0,0,1,1,1],
  [1,0,1,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,1,1,1,1,0,0,0,1,1,1,1,0,1,1,0,1,1,0,1,1,1,0,1],
  [0,0,1,1,0,1,1,1,1,1,0,0,1,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,1,1,1,1,1,0,1,1,0,1,1,1,1,1,0,1,1,0,1,1],
  [0,0,1,1,0,1,1,0,1,1,0,0,0,0,0,1,0,1,1,1,0,0,1,0,0,0,0,0,1,0,0,1,1,0,1,1,0,0,1,0,0,1,1,0,1,1,1,0,1,0,1,1,0,1,0,1,0,0],
  [1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,0,1,0],
  [1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,1,0,1,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,0,1,1,0,0,0,1,0,1],
  [1,1,1,1,0,1,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,0],
  [0,1,1,1,0,1,1,0,1,1,0,0,0,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,0,0,1,1,1,0,1,1,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1],
  [0,1,1,1,0,1,1,0,1,1,0,1,1,0,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1],
  [0,0,1,1,0,1,1,0,1,1,0,0,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,1,1,1,0,1,1,0,1,1,1,0,1],
  [1,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,1,1,0,1,1,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,1,0,1,1,0,1,1,0,0,1],
  [0,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,0,1,0,1,1,1,0,1,0,0,1,0,0,1,1],
  [1,1,1,1,0,1,1,0,1,1,0,1,1,0,1,0,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,0,1,1,0,1,1,0,0,1,0,1,1,0,0,1,0,0,1,1,1,1,1,1,1,1],
  [1,0,1,1,0,0,1,0,1,0,0,1,1,0,1,1,0,0,0,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1,1,0,1,1],
  [1,1,1,1,0,0,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,1,1,1,0,1,1],
  [1,1,1,0,0,1,1,0,0,1,0,1,0,0,0,1,1,0,1,0,0,1,1,0,1,1,0,0,1,1,0,1,0,1,1,1,0,0,1,0,1,0,0,1,1,1,0,1,0,0,1,0,0,0,1,0,0,1],
  [1,1,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,0,0,0,1,0,0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,1,1,0,0,1,1,1,1,0,1,1,0,0,0,1],
  [1,1,1,0,1,1,0,0,1,1,0,0,1,1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,1,1,1,0,0,1,1,0,0,0],
  [1,1,1,1,0,1,0,0,1,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1,1],
  [1,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,0,1,1,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,0,1,1,0,1,1,0,1,1,0,1,1,1,0,0,1],
  [1,1,1,0,1,0,0,0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0,1,1,0,1,1,0,0,1,1,0,0,1],
  [1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,0,0,1],
  [1,1,1,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,0,0,1],
  [0,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,1,0,0,0,0,1,1,1,0,0,0,0,1,1,0,1,0,0,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,0,1,0],
  [1,1,1,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,1,0,1,1,1,0,1,0,0,1,1,0,0,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,0,0,0,1],
  [1,1,1,0,1,1,1,0,1,1,0,1,1,0,1,1,0,1,0,0,1,1,0,1,1,0,1,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,0,1,1,1,0,1,1],
  [1,1,1,0,1,1,0,0,1,1,0,1,1,0,1,1,0,1,0,0,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,1,1],
  [1,0,1,1,0,0,0,0,0,1,0,0,1,1,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0,1,1,1,0,1]
]

Präsentation

Damit sich die erzeugte Datenstruktur mit dem Digitalisat, in der gewohnten Funktionalität (z.B. Zoom) einbinden lies, musste sie Teil des IIIF Manifestes werden. In einem Manifest lassen sich über Annotationen Bezüge zwischen beschreibenden Daten und Bildregionen herstellen, das Verfahren kam auch hier zum Einsatz.

Dafür war allerdings ein weiterer Datenaufbereitungsschritt notwendig: Selbst wenn man fast die Hälfte der Punkte verwerfen würde, da sie keine notwendigen Informationen (im Beispiel oben “0”) beinhalten, blieben immer noch 1232 Punkte über. Daher wurden die zusammenhängenden Bereiche zusammengefasst und dabei darauf geachtet, dass keine “Löcher” geschlossen wurden. Am Ende dieser Optimierung blieben 120 Bereiche über, die als SVG Polygone repräsentiert sind:

Darstellung als einzelne Quadrate

Darstellung als einzelne Quadrate

Darstellung Polygone

Darstellung Polygone

Diese SVG Polygone ließen sich dann in Webannotationen übersetzen und so direkt in das IIIF Manifest einbetten. Da ein solcher Inhaltstyp bisher nicht vorgesehen ist, musste er noch definiert werden.

Beispiel: Das zweite Polygon als Annotation
{
  "id": "http://localhost:5173/canvas/page031/annotation/0/touch/1",
  "type": "Annotation",
  "motivation": "sensing",
  "body": {
    "id": "http://localhost:5173/canvas/page031/annotation/0/touch/1/body",
    "type": "InteractiveResource",
    "haptics": { "vibrate": true }
  },
  "target": {
    "type": "SpecificResource",
    "source": "http://localhost:5173/canvas/page031",
    "selector": {
      "type": "SvgSelector",
      "value": "<svg  xmlns=\"http://www.w3.org/2000/svg\"><polygon points=\"805,596 806,596 829,596 853,596 853,572 829,572 806,572 805,572 782,572 758,572 758,596 782,596\" /></svg>"
    }
  }
}

Die notwendige Extension ist hier definiert.

Zur Darstellung kommt hier dann OpenSeadragon zusammen mit Annotorious zum Einsatz.

Ergebnis

“Leider” wird die Vibrate API nicht von allen Browsern unterstützt. Der erfolgversprechendste Kandidat ist sicher Chrome auf Android, aber auch da müssen ein paar Rahmenbedingungen beachtet werden:

  • Vibration muss aktiviert sein
  • Ton muss aktiviert sein
  • Der Stromsparmodus deaktiviert sein

Getestet mit Chrome auf Android 12, 13 und 14.

Weitere potentielle Browser listet Can I Use auf, es existiert auch eine Testseite vom Chrome Projekt auf Github.

Vibrarions-API Überprüfung

IIIF-Manifest

Genutzte Bibliotheken