Bildpyramiden mit libvips, sharp und Leaflet
Die Darstellung großer Grafikdateien im Internet, wie Bilder oder Pläne, kann in einem Browser entweder langsam (aufgrund hoher Auflösung und großer Datenmenge) oder qualitativ minderwertig (aufgrund von Kompression) sein. Die Lösung? Bildpyramiden. Bei dieser Methode wird ein großes Bild für verschiedene Zoomstufen in quadratische 'Kacheln' zerteilt. Der Browser lädt nur die für den jeweiligen Ausschnitt notwendigen 'Tiles' und fügt sie nahtlos zusammen.
Erstellung der Bildpyramide
Bei der Erstellung von Bildpyramiden wird eine Ordnerstruktur erstellt. Für jede Zoomstufe (Z) wird ein eigener Ordner angelegt. Innerhalb dieser Ordner werden Unterordner für die Bildzeilen (Y) erstellt, in denen sich die Bilddateien (X) befinden. Je mehr Zoomstufen, desto mehr Bilder. Unser Beispiel "Die Geburt der Venus" besteht aus insgesamt 2180 Bilddateien.
venus/ ├─ 0 <- Zoomstufe Z │ ├─ 0 <- Zeile Y │ ├─ 0.jpg <- Tile X ├─ 1 <- Zoomstufe Z │ ├─ 0 <- Zeile Y │ │ ├─ 0.jpg <- Tile X │ │ ├─ 1.jpg <- Tile X │ ├─ 1 <- Zeile Y │ ├─ 0.jpg <- Tile X │ ├─ 1.jpg <- Tile X ...
Die in unserem Beispiel eingesetzte Software:
- libvips - eine schnelle Bildverarbeitungsbibliothek für verschiedene Plattformen, hier verantwortlich für die Erzeugung der Bildpyramiden.
- sharp - ein npm-Paket für node.js, das eine kompilierte Basisversion der libvips (jpeg, png, webp, aviv, tiff) beinhaltet. Findet sharp eine aktuelle und global installierte libvips, wird diese bei der Installation kompiliert. Der Funktionsumfang kann somit deutlich erweitert werden.
- Leaflet ruft am Client je nach Benutzer-Interaktion in Form von /pfad/Z/Y/X.jpg die Tiles ab, fügt sie zusammen und stellt sie dar.
libvips
Da die bereitgestellte Version der libvips in den Linux-Distributionen oft veraltet ist und wir die gewünschten Funktionen selbst bestimmen möchten, werden wir libvips nun eigenhändig kompilieren. Ans Werk!
Das obligatorische Update:
apt update
Die grundlegenden Abhängigkeiten:
apt install build-essential pkg-config libglib2.0-dev libexpat1-dev python3 ninja-build meson
Optionale Abhängigkeiten, siehe Doku:
apt install libarchive-dev libjpeg-dev libexif-dev librsvg2-dev libcgif-dev libtiff-dev libspng-dev libimagequant-dev libpangocairo-1.0-0 libwebp-dev libheif-dev libopenexr-dev libpoppler-glib-dev liblcms2-dev libfftw3-dev
Wir halten Ausschau nach der aktuellen libvips-Version auf Github, ersetzen entsprechend die X-e, laden das Archiv herunter und entpacken es:
curl -fLO https://github.com/libvips/libvips/releases/download/vX.X.X/vips-X.X.X.tar.xz
tar xfJ vips-X.X.X.tar.xz
In das Verzeichnis wechseln und kompilieren:
cd vips-X.X.X
meson setup build --libdir=/usr/lib --buildtype=release
cd build
meson compile
meson test
meson install
Falls alles geklappt hat, sollten wir libvips über die Kommandozeile bedienen können:
vips --help
Dieses war der erste Streich, doch der Zweite folgt sogleich.
sharp
sharp ist ein schnelles und flexibles Node.js-Paket für die Bildmanipulation, das auf libvips basiert.
Falls bei der Installation eine aktuelle libvips-Version gefunden wird, wird diese kompiliert und um deren Funktionalität erweitert. (Bildpyramiden, PDF etc.)
Um uns zu vergewissern, dass sharp libvips finden kann (sollte die libvips-Version zurückgeben):
pkg-config --modversion vips-cpp
Wir wechseln in unser node.js-Projekt und installieren die für die Kompilierung nötigen Pakete:
npm install node-addon-api node-gyp
...und dann sharp:
npm install sharp
Hier das Script zur Erzeugung unserer Pyramide
import sharp from 'sharp'
const tileinfo = await sharp('./riesenbild.jpg', { limitInputPixels: false })
.jpeg({ quality: 70 })
.tile({ size: 512, layout: 'google', background: { r: 63, g: 63, b: 63, alpha: 1 } })
.toFile('./pyramidenverzeichnis')
tileinfo.width // Breite des Originalbildes
tileinfo.height // Höhe des Originalbildes
Leaflet
Leaflet ist eine freie JavaScript-Bibliothek und wird üblicherweise zum Anzeigen von interaktivem Kartenmaterial eingesetzt. In unserem Beispiel verwenden wir das einfache L.CRS.Simple Koordinatenreferenzsystem (quadratischer Raster) - perfekt für Anhänger der Flat-Earth-Theorie.
Hier unser Script, um Leaflet zu starten:const mapContainer = document.getElementById('container');
const maxzoom = 6 // Verzeichnistiefe -1
const picWidth = 30000 // Breite des Originalbildes
const picHeight = 18840 // Höhe des Originalbildes
const picURL = 'https://domain.org/pyramidenverzeichnis' + '/{z}/{y}/{x}.jpg'
//Map mit Leaflet erstellen
map = L.map(mapContainer, {
crs: L.CRS.Simple,
center: [0, 0],
zoom: 1,
minZoom: 1,
maxZoom: maxzoom,
zoomControl: true,
attributionControl: false
})
//Festlegen der Bildgrenzen
const northEast = map.unproject([picWidth, 0], map.getMaxZoom());
const southWest = map.unproject([0, picHeight], map.getMaxZoom());
const bounds = L.latLngBounds(southWest, northEast);
map.fitBounds(bounds, true);
map.options.minZoom = map.getBoundsZoom(bounds);
//Kachellayer zur Map hinzufügen
L.tileLayer(picURL, {
tileSize: 512,
minZoom: map.getMinZoom(),
maxZoom: map.getMaxZoom(),
attributionControl: false,
noWrap: true,
bounds: bounds
}).addTo(map)
Die beschriebene Vorgangsweise mit lipvips und Leaflet ist ein sauberer und effizienter Weg, um Bilder und Pläne mit großen Datenmengen im Browser darzustellen. Viel Spaß beim Ausprobieren!