Compress-Or-Die

PNG endlich verstehen

Christoph Erdmann Autor: Christoph Erdmann (@McSodbrenner)
Letztes Update: 2017-08-02
English version | Deutsche Version

Dieser Artikel ist der Nachfolgeartikel zu JPG endlich verstehen, den ich jedem ans Herz legen möchte, der ein bisschen tiefer – aber in allgemeinverständlicher Weise – in die JPG-Kompression einsteigen möchte. Diesmal möchte ich aber die Funktionsweise eines PNGs darstellen und am Ende des Artikels zeigen, mit welchen Tricks ihr noch ein bisschen mehr an Dateigröße aus euren PNGs herauskitzeln könnt.

Wie auch beim vorherigen Artikel werde ich einige Aspekte vereinfacht darstellen, solange sie dem Verständnis dienen. Mit Mathematiker-Buzzword-Bingo ist schließlich keinem geholfen, denn die wenigsten von euch, die ihre Bilder verkleinern wollen/müssen, werden Mathematiker sein, sondern vermutlich eher Webworker oder Digital-Advertising-Spezialisten.

Um die entstehenden Dateigrößen der verschiedenen Tools (compress-or-die.com, Photoshop, TinyPNG und Co.) richtig vergleichen zu können, möchte ich euch noch auf den Abschnitt Vergleichbare Dateigrößen im JPG-Artikel hinweisen.

Jetzt kann es losgehen. Schaut euch mal diese beiden Bälle-Bilder an:

4.492 B 128.025 B

Beide Bilder sehen gleich aus, haben die gleiche Breite und Höhe, beide sind PNGs, aber dennoch ist die Dateigröße deutlich unterschiedlich. Warum?

Im Laufe des Artikels werde ich weiter mit diesem Bild arbeiten und euch die Feinheiten näherbringen, die zu diesem Umstand führen.

Farbtiefen

Die meisten eurer Bilder werden im RGB-Farbmodell vorliegen (wie auch unser Beispielbild), und somit besteht ein Pixel eures Bildes aus den drei Farben (oder Farbkanälen) Rot, Grün und Blau, welche jeweils 256 Abstufungen haben können. 256 Abstufungen, weil sich jede Farbe eines Pixels über den Speichververbrauch von 1 Byte darstellen lassen soll. Und mit einem Byte lassen sich exakt 256 Werte ausdrücken. Mit einem Speicherverbrauch von 3 Byte lässt sich also ein gesamtes Pixel wiedergeben (jeweils 1 Byte für Rot, Grün und Blau).

Hat man jetzt ein 800 x 600 px großes Bild, ergibt das einen Speicherverbrauch von 800 x 600 x 3 Byte = 1.440.000 B = 1,44 MB. Das entspricht auch ziemlich genau der Größe eines unkomprimierten BMP-Bildes (und lustigerweise der Größe einer 3,5"-HD-Diskette... für alle, die sich noch erinnern können).

Benennt man jetzt die Farbtiefe, könnte man von einem 3-Byte-Bild sprechen. Da es aber noch andere Farbtiefen als 1 Byte pro Kanal gibt, hat sich für so ein Bild die Bezeichnung 24-Bit-Bild (also 3x8-Bit-Bild) eingebürgert, weil 1 Byte halt aus 8 Bits besteht. Die Farbtiefe bezeichnet also den Speicherverbrauch eines Pixels.

Die zwei anderen Farbtiefen, über die man beim Auseinandersetzen mit dem PNG-Bild-Format noch stolpert, sind 32-Bit und 8-Bit:

Ein 32-Bit-PNG gleicht einem 24-Bit-PNG, enthält aber noch einen zusätzlichen 8-Bit-Kanal für Transparenz-Informationen (meistens Alpha-Kanal genannt). Somit kann ein Bild auch transparente Bereiche in 256 Abstufungen beinhalten.

Übrigens: Photoshop zeigt im "Für Web speichern..."-Dialog keine Auswahlbox-für 32-Bit-PNGs. Ihr müsst dafür "PNG-24" auswählen und die Checkbox mit dem Namen "Transparenz" markieren.

Ein 8-Bit-PNG funktioniert dagegen ein wenig anders. Hier werden bis zu 256 Farben mit ihren Rot-, Grün-, Blau- und Transparenz-Informationen in einer sogenannten Palette festgelegt, und für jedes Pixel wird dann nur noch definiert, an welcher Stelle der Palette die entsprechende Farbe steht. Somit braucht man zum Speichern eines Pixels nur noch 1 Byte an Speicher, und zwar für die Position der Farbe in der Palette. Dazu kommt dann noch das Speichern der Palette. Allerdings ist man bei so einem Bild auf maximal 256 Farben beschränkt, was jedoch für die Mehrzahl der Bilder, die sinnvollerweise als PNG gespeichert werden, mehr als genug ist. Daher ist diese Farbtiefe im Bereich der PNG-Kompression die bedeutendste.

Die drei Farbtiefen hier noch einmal in einer Kurzübersicht:

8-Bit-PNG 24-Bit-PNG 32-Bit-PNG
Paletten-Bild RGB-Bild RGBA-Bild
maximal 256 Farben 16,8 Mio. Farben 16,8 Mio. Farben + Transparenz

Das ist übrigens der erste Grund, warum das eine PNG mit den Bällen kleiner ist als das andere. Das erste, kleinere PNG ist ein 8-Bit-PNG, während das größere PNG ein 32-Bit-PNG ist.

Jetzt wissen wir, was Farbtiefen bedeuten und wie wir die Dateigröße eines unkomprimierten Bildes berechnen können, aber die Daten belegen noch sehr viel Platz und müssen dementsprechend komprimiert werden.

Die Kompression

Ein PNG wird in drei Schritten komprimiert: Dem Vorfiltern, der Wörterbuch-basierten Kodierung per LZ77 und schließlich der Häufigkeits-Kodierung (auch Entropiekodierung genannt) nach einem gewissen Herrn Huffman.

Schritt 1: Das Vorfiltern

Daten können am besten komprimiert werden, wenn sie sich möglichst oft wiederholen. Also versucht man durch das Vorfiltern, aus unterschiedlichen Daten möglichst gleichartige zu generieren.

Schauen wir uns mal im folgenden die Rotwerte eines 5x5 px großen Bildes an:

Die Rottöne ... ... und ihre numerische Darstellung

Auf jede Zeile könnte man hier den Filter "Sub" (einer von 5 möglichen Filtern) anwenden, welcher besagt, dass wir uns einfach nur die Differenz zum vorherigen Wert merken. Dieser Schritt lässt sich später einwandfrei umdrehen, so dass uns keine Daten verlorengehen. In unserem Beispiel wird sich also für jede Zeile gemerkt, dass der Filter "Sub" verwendet wurde. Unsere zu speichernden Werte sehen jetzt so aus und sind jetzt sehr viel besser zu komprimieren:

Die per "Sub" gefilterten Werte

Diese Filter sind der Grund, warum das PNG dem GIF eine Nasenlänge voraus ist und daher fast immer kleiner ist. Und sie sind ein wichtiger Angriffspunkt, wenn man die Kompression eines PNGs optimieren möchte.

Jeder PNG-Algorithmus hat die undankbare Aufgabe, herauszufinden, welcher Filter für welche Zeile wohl optimal ist, damit sich möglichst viele Daten wiederholen. Aber wenn er jetzt den Filter für Zeile 3 so anpasst, dass die Daten möglichst denen aus Zeile 2 ähneln, könnte es sein, dass sie aber nicht mehr denen aus Zeile 5 ähneln. Welche Lösung ist da jetzt die bessere?

Die optimalen Filter pro Zeile herauszufinden ist eine Wissenschaft für sich, die man natürlich mit entsprechender Computer-Power angehen könnte. Da ihr aber vermutlich nicht eine Woche vor eurem Computer warten möchtet, bis das PNG endlich berechnet ist, werden die Filter aus Erfahrungswerten und Vermutungen heraus gewählt. Von manchen Programmen besser und von anderen schlechter.

Eine andere Möglichkeit wäre, eure Bilddaten insofern anzupassen, dass die Filter besser wirken können. Dieses ist dann ein verlustbehafteter Vorgang, weil ja eure originalen Bilddaten nicht mehr errechenbar sind.

Habt ihr z.B. eine schwarze Fläche, welche aber in Wirklichkeit gar nicht rein schwarz ist, sondern aus ganz vielen fast schwarzen Pixeln mit unterschiedlichen Intensitäts-Werten besteht, ist das äußerst nachteilig für die Kompression. Bereinigt ihr diesen Zustand, indem ihr allen Pixeln den gleichen Schwarzwert gebt, werdet ihr sehen, dass eure Dateigröße signifikant schrumpft, obwohl euer Auge keinen Unterschied sieht.

Schritt 2: Wörterbuch-basierte Kodierung per LZ77

LZ77 ist ein verlustfreies Verfahren zur Datenkompression, welches sich wiederholende Sequenzen von Daten sucht. Trifft der Algorithmus auf eine Sequenz von Daten, die er schon einmal weiter vorn in der Datei gesehen hat, ersetzt er diese mit einem Hinweis auf die erste Sequenz: "Hier bitte die Sequenz an Daten einsetzen, die 1300 Zeichen vorher auftaucht und 200 Zeichen lang ist".

Aus diesem Grund kann ein PNG hervorragend sich wiederholende Daten speichern. Habt ihr gleiche Abbildungen von Objekten in einer PNG-Datei, belegt nur die erste Darstellung Platz. Die anderen werden euch praktisch geschenkt.

Erstellt ihr ein PNG in COD, werdet ihr wahrscheinlich schon das Bild mit der Überschrift compression view (in einem Forum auch liebevoll Predator vision genannt) gesehen haben. Dieses Bild zeigt euch, wieviel Speicher-Aufwand in einen bestimmen Bereich des Bildes investiert werden musste. Die beiden compression views für die Bälle-Bilder sehen folgendermaßen aus:

4.492 B 128.025 B

Dunkelblau bedeutet dabei, dass dieser Bereich praktisch keinen Speicher belegt, Hellblau ein bisschen Speicher, Gelb mehr Speicher und Rot viel Speicher.

Wie ihr im ersten Bild deutlich sehen könnt, kostete nur das jeweilige erste Auftreten der farbigen Bälle Speicherplatz. Alle anderen wurden euch geschenkt, da deren Abbildungen exakt dem jeweils ersten Ball der gleichen Farbe entspricht.

Und im zweiten Bild könnt ihr jetzt deutlich sehen, dass die Bälle halt nur gleich aussehen, aber in Wirklich kein Ball einem anderen entspricht. Es muss also jeder Ball einzeln gespeichert werden. Das liegt daran, dass ich zu Demonstationszwecken im zweiten Bild einen leichten Verlauf und ein leichtes Bildrauschen über das komplette Bild gelegt habe.

Vorsicht vor dem rutschenden Fenster

Eigentlich sollte ihr eben über etwas gestolpert sein... warum belegt eigentlich der dritte rote Ball Speicherplatz, obwohl doch vorher schon zwei identische rote Bälle aufgetreten sind?

Dieses ist begründet durch die Tatsache, dass bei der Spezifikation des PNGs aus Performance-Gründen ein sliding window von 32 kB festgelegt wurde. Verständlich ausgedrückt heißt das, dass nur in den vorherigen ca. 32.000 Zeichen geschaut wird, ob eine bestimmte Sequenz schon aufgetreten ist. Ist der Abstand zwischen zwei Objekten also zu groß, wird nicht mehr erkannt, dass beide Objekte gleich sind, und es werden beide Objekte einzeln gespeichert.

Zeigen kann ich euch das, indem ich eine "Brücke" baue und den Ball zwischen dem zweiten und dritten Ball auch rot einfärbe.

4.492 B 4.050 B

Schon verschwindet der dritte rote Ball aus der compression view und die Datei wird folgerichtig noch kleiner.

Schritt 3: Entropiekodierung per Huffman

Dieses ist der letzte Schritt der PNG-Kompression. Hierbei wird versucht, jedes Zeichen mit einer möglichst kleinen Bitfolge darzustellen, um die Datei weiter zu schrumpfen. Mit entsprechendem Computer-Power-Aufwand kann hier noch etwas herausgeholt werden. Das wird z.B. versucht, wenn ihr in COD den Haken bei "Extreme compression" setzt. Hier passiert aber sonst nichts, was für das Verständnis von PNGs wichtig wäre.

7 Tricks zur Verringerung der Dateigröße

Jetzt möchte ich euch gern einige Hinweise geben, worauf ihr beim Erstellen euer PNGs achten solltet, um möglichst viel Dateigröße einsparen zu können.

1. Original-Daten komprimieren

Achtet darauf, die Original-Daten zu komprimieren, und nicht Bild-Daten, die vielleicht selbst schon Opfer eines Nicht-PNG-Bild-Komprimierungs-Algorithmus geworden sind.

12.322 B

Das Ursprungsbild ist hier ein JPG, welches Artefakte erzeugt, die für die PNG-Kompression "tödlich" sind. Erstens entstehen Unmengen an Farben, zweitens entstehen Bildmuster, die im Original gar nicht enthalten sind und jetzt auch gespeichert werden müssen.

Interessanterweise könnt ihr an der compression view auch erkennen, ob ein Bild schon einmal zum JPG konvertiert wurde. Achtet einfach darauf, ob die compression view wie in diesem Beispiel "blockig" aussieht (bedingt durch das 8x8 px Raster der JPG-Kompression). Sollte dieses der Fall sein, versucht ihr besser, an die Original-Daten des Bildes zu kommen.

Das Ergebnis sollte dann nämlich eher so aussehen und um einiges kleiner sein:

2.118 B

2. Sauberes Klonen von Objekten

In vielen Grafik-Programmen ist es möglich, nicht nur auf ganzen Pixel zu arbeiten, sondern auch Objekte um halbe Pixel zu verschieben. Wollt ihr aber eine Grafik aus geklonten Objekten perfekt als PNG speichern, müssen die Objekte absolut identisch sein, damit nur das erste Objekt Speicherplatz belegt. Glücklicherweise könnt ihr das in der compression view ja sehr schnell erkennen.

Achtet also darauf, Objekte, die mehrfach in eurem Bild vorkommen, nicht ausversehen zu verschieben (gerade in Nicht-100%-Zoom-Abstufen), zu skalieren, zu verzerren, zu drehen usw.

3. Dirty transparency

Als "dirty transparency" werden Bildbereiche bezeichnet, welche 100% transparent sind, aber in den anderen Farbkanälen noch alle Farbinformationen beinhalten. Dieses kommt häufig bei maskierten Bildern vor und sorgt dafür, dass Speicherplatz durch nicht sichtbare Bildinformationen verschwendet wird.

Bereiche mit 100% Transparenz werden von COD automatisch gesäubert, allerdings kann es durch unsauberes Freistellen mit dem Pinsel auch mal dazu kommen, dass Bereiche mit 98% oder 99% Transparenz übrig bleiben, welche Speicherplatz beanspruchen, weil der ursprüngliche Bildinhalt immer noch vorhanden ist.

Der Bereich um Lena sollte eigentlich transparent sein, aber in der compression view sieht man deutlich, dass dort Daten komprimiert werden:

36.948 B

Das gesäuberte Bild ist 11% kleiner bei gleichem Bildeindruck:

32.990 B

4. 8-Bit-Farbtiefe verwenden

Im Normalfall sollte die Qualität eines 8-Bit-PNGs immer ausreichend sein. Deshalb ist das Generieren eines 8-Bit-PNGs in COD auch als Standard eingestellt. Dabei wird euer Bild auf maximal 256 Farben reduziert. Fehlende Farben werden per Dithering simuliert.

Spielt dabei gern mal mit dem Regler für die Anzahl der Farben herum. Gerade flächige Bilder wie z.B. Logos brauchen fast nie 256 Farben. Eine gute Daumenregel ist hierbei, pro Farbe in dem Bild acht weitere Farben zu rechnen, damit das Antialiasing sauber aussieht. Besteht ein Logo also aus den Farben Rot, Blau und Schwarz, sollte es reichen, den Farb-Regler auf 24 zu stellen.

5. Posterization

Posterization bezeichnet den Vorgang der Farbreduktion innerhalb eines Bildes. Dieses ist nützlich, weil es in einem RGB-Bild oft mehr Farben gibt, als das menschliche Auge unterscheiden kann. Durch die Posterization werden jetzt sehr ähnliche Farben zu einer zusammengefasst, was wir erst als störend wahrnehmen, wenn sich die Farben wirklich deutlich unterscheiden.

Dadurch, dass es dann mehr gleichartige Farbwerte in einem Bild gibt, arbeiten die PNG-Filter besser und die Datei wird kleiner. Einstellen könnt ihr das in COD über den "Quality"-Regler bei 24-Bit-PNGs.

156.830 B 82.613 B
24-Bit-PNG mit Quality 50%

6. Selektiv weichzeichnen

Gerade bei körnigen Bildern mit einem leichten Bildrauschen bietet es sich an, mit dem selektiven Weichzeichner zu arbeiten, den ihr in COD im "Preprocessing"-Bereich findet. Er sorgt dafür, dass Details scharf bleiben, Flächen hingegen aber weichgezeichnet werden. Da die entstehenden, sauberen Flächen der Arbeitsweise der PNG-Filter entgegen kommen, wird das Bild kleiner.

156.830 B 131.242 B
Leicht weichgezeichnet mit selektivem Weichzeichner

7. JPG-Transparenz

Normalerweise unterstützt JPG keine Transparenz. Aber durch den Einsatz von Javascript kann dieser Makel ausgeglichen werden, solange das Bild im Webumfeld eingesetzt wird. Ladet in COD mal ein transparentes PNG hoch und ihr bekommt bei Auswahl des Reiters "JPG" ein transparentes JPG sowie den zugehörigen HTML-Code angeboten, um das Bild in eure Website einbinden zu können. Gerade bei großen transparenten Bildern wie z.B. Abbildungen von Produkten liegt man damit weit unter der Dateigröße eines transparenten PNGs. Selbst unter der eines 8-Bit-PNGs.

47.219 B 18.020 B 10.685 B
32-Bit PNG 8-Bit PNG mit 256 Farben JPG mit Transparenz

 

Und schon sind wir wieder am Ende. Falls ihr aber noch Fragen, Anregungen, Wünsche o.ä. haben solltet, immer her damit.

Ansonsten könnt ihr jetzt auch gleich mit dem Bälle-Bild weiter experimentieren:

Bälle-Bild in compress-or-die.com öffnen.

Vergleicht doch COD auch mal mit der Konkurrenz und macht euch euer eigenes Bild davon, wie gut COD wirklich ist:

Ergänzende Lektüre

Magst du diesen Artikel?

Dann wäre es doch klasse, ihn mit anderen zu teilen.