====== Bilder ====== Eingebundene Bilder wurden oft unabhängig vom restlichen Dokument erstellt, und haben ihre eigenen Farbkorrekturinformationen. Darum ist es in PDF möglich, die Farbkorrektur eingebundener Bilder separat einzustellen. Zudem kann man für die allfällige Konversion von RGB nach CMYK festlegen, ob möglichst auf exakte Farbumsetzung, Erhaltung der Farbsättigung, oder Erhaltung des photographischen Eindrucks geachtet werden soll. ===== Farbkorrektur ===== Um eine vom Dokumentstandard abweichende Farbkorrektur für das Bild festzulegen, müssen wir im Bildobjekt den ''/ColorSpace'' Eintrag ändern. Dieser war bisher auf ''/DeviceGray'', ''/DeviceRGB'', ''/DeviceCMYK'' oder ein Array mit einem ''/Indexed'' Farbmodell. ''/DeviceGray'', ''/DeviceRGB'' oder ''/DeviceCMYK'' werden einfach durch eine Umrechnungstabelle oder einen Verweis auf ein ICC-Profil ersetzt. Beispiel: 10 0 obj << /Type /XObject /Subtype /Image /Width 800 /Height 600 /ColorSpace [/ICCBased 11 0 R] /BitsPerComponent 8 /Interpolate true /Filter [/ASCII85Decode /DCTDecode] /Length 35654 >> stream ...~> endstream endobj Bei indizierten Farbräumen ist die Sache ein klein wenig kniffliger. Diese sind bekanntlich als Arrays angegeben, von denen der erste Eintrag ''/Indexed'' ist. Der zweite Eintrag war in unserem Fall bisher immer ''/DeviceRGB''. Diesen Eintrag müssen wir ersetzen. Beispiel: /ColorSpace [/Indexed [/ICCBased 11 0 R] 3 ] **Vorsicht:** Beim [[/grafik/transparenz#alphakanal|Alphakanal]] //muss// ''/ColorSpace'' auf ''/DeviceGray'' gesetzt werden. ''/CalGray'' und ''/ICCBased'' sind hier nicht erlaubt (und machen auch wenig Sinn). ===== Umrechnungspriorität ===== Das Dictionary des Bildobjekts kann einen Eintrag ''/Intent'' haben, der auf die bekannten [[/grafik/farbkorrektur/start#umrechnungsprioritaet|Intents]] gesetzt werden kann. Als Daumenregel kann man sich merken: Bei Formaten, die typischerweise Fotos enthalten (insbesondere JPEG), eignet sich ''/Perceptual'' als Standard. Bei allen anderen Formaten eignet sich ''/RelativeColorimetric'' als Standard. ===== Daten aus Bildern ermitteln ===== Die Umrechnungspriorität müssen wir von Hand einstellen. Farbkorrekturinformationen hingegen sind zuweilen in den Bilddateien abgelegt. Wir müssen sie nur rausfischen. ==== JPEG ==== Bei JPEG herrscht leider ein gewisses Chaos. Das JFIF Format enthält überhaupt keine Farbkorrekturinformationen. Das EXIF Format erlaubt zwar, solche abzulegen, jedoch sind diese unzuverlässig: Zum einen handelt es sich meist nur um Schätzwerte, und zum anderen oft um die Werte der Kamera. Wir brauchen aber die Werte des Bildschirms, an dem das Bild aufbereitet wurde. ICC hat schliesslich eine eigene Erweiterung veröffentlicht, welche es ermöglicht, ein ICC-Profil in eine JPEG-Datei einzubetten. Um diese Information zu ermitteln, müssen wir alle [[/grundlagen/dateien/bilder/jpeg#dateiaufbau|Metadatenblöcke des JPEG]] anschauen. Zur Erinnerung: Sobald wir dabei auf einen Block mit dem Marker stossen, können wir die Verarbeitung abbrechen. Nach diesem Block folgend die komprimierten Bilddaten. Das ICC-Profil ist in einem oder mehreren Metadatenblöcken abgelegt. Diese Blöcke haben den Marker . Der Aufbau ist wie folgt: ^Position ^Grösse ^Typ ^Wert ^ | 0|1 Byte |uint8 |immer 0xFF | | 1|1 Byte |uint8 |immer 0xE2 | | 2|2 Byte |uint16 |Blockgrösse | | 4|11 Byte |char |ASCII-String ''ICC_PROFILE'' | | 15|1 Byte |uint8 |immer 0 | | 16|1 Byte |uint8 |Blocknummer | | 17|1 Byte |uint8 |Blockanzahl | | 18|Blockgrösse - 16 |* |ICC-Profildaten | Der String ''ICC_PROFILE'' am Anfang soll den Block zweifelsfrei als ICC-Block markieren. Es können auch andere Daten in einem Block mit dem Marker abgelegt sein, da es sich nicht um einen offiziellen Standard des JPEG Komitees handelt. Ist die Blockanzahl 1, so können wir die Profildaten aus diesem Block 1:1 übernehmen. Ansonsten müssen wir die Profildaten aller ICC-Blocks aneinanderhängen, und zwar in der Reihenfolge, die mit der Blocknummer vorgegeben ist (aufsteigend ab 1). Die (allenfalls zusammengesetzten) Profildaten sind die unveränderten Daten einer ICC-Datei. Das heisst, die Daten können in einen Stream gepackt werden, der dann mit ''/ICCBased'' referenziert werden kann (den ''/N'' Eintrag im Stream des ICC-Profils nicht vergessen). ==== PNG ==== PNG bietet drei Möglichkeiten, die Farbkorrektur anzugeben: sRGB-Deklaration, ICC-Profile und Chromatizitätsdeklaration. === sRGB-Deklaration === Das ist die einfachste Methode. Enthält das PNG einen Block mit Marker ''sRGB'' (auf Gross- und Kleinschreibung achten), so wird das sRGB-Farbmodell verwendet. Man kann dementsprechend ''/CalRGB'' bzw. ''/CalGray'' mit den zu sRGB passenden Zahlen verwenden. Falls dieser Block vorkommt, sollten ICC-Profile oder Chromatizitätsdeklarationen ignoriert werden. ^Position ^Grösse ^Typ ^Wert ^ | 0|4 Byte |uint32 |Blockgrösse | | 4|4 Byte |char |immer ''sRGB'' | | 8|1 Byte |uint8 |Umrechnungspriorität | | 9|4 Byte |uint32 |Prüfsumme | Wie man sieht, kann in diesem Block (und nur in diesem Block) eine bevorzugte Umrechnungspriorität definiert werden. Folgende vier Werte sind möglich: ^ 0|''/Perceptual'' | ^ 1|''/RelativeColorimetric'' | ^ 2|''/Saturation'' | ^ 3|''/AbsoluteColorimetric'' | === ICC-Profil === Enthält das PNG einen Block mit Marker ''iCCP'', so enthält dieser ein ICC-Profil. Der Blockaufbau ist wie folgt: ^Position ^Grösse ^Typ ^Wert ^ | 0|4 Byte |uint32 |Blockgrösse | | 4|4 Byte |char |immer ''iCCP'' | | 8|* |char |Profilname | | *|1 Byte |uint8 |immer 0 | | *|1 Byte |uint8 |Kompression | | *|* |* |ICC-Profildaten | | *|4 Byte |uint32 |Prüfsumme | Der Profilname ist in ISO Latin-1 kodiert, für uns aber nicht wichtig. Entscheidend ist, dass der Name selbst kein 0-Byte enthalten kann. Das erste 0-Byte innerhalb der Blockdaten ist folglich der oben erwähnte uint8 mit Wert 0. Das ist die einzige Möglichkeit, die Länge des Profilnamens zu ermitteln. Die Kompressionsmethode ist immer 0, was Deflate bedeutet. Das heisst, wir können die Profildaten 1:1 in einen Stream packen, und den ''/FlateDecode'' Filter angeben. Den Stream können wir dann mit ''/ICCBased'' referenzieren. === Chromatizität === Die ist die letzte Möglichkeit, falls weder eine sRGB-Deklaration noch ein ICC-Profil vorhanden sind. In diesem Fall haben wir zwei Blöcke. Einen ''cHRM'' Block mit Weisspunkt und Matrix, und einen ''gAMA'' Block mit dem Gammawert. Falls nur einer der beiden vorhanden ist (was nicht der Fall sein sollte), so kann man die sRGB-Werte für den fehlenden Block verwenden. == gAMA == ^Position ^Grösse ^Typ ^Wert ^ | 0|4 Byte |uint32 |Blockgrösse | | 4|4 Byte |char |immer ''gAMA'' | | 8|4 Byte |uint32 |Gammawert (invers) | | 12|4 Byte |uint32 |Prüfsumme | Der inverse Gammawert ist eine Ganzzahl auf einer Skala von 0 - 100'000. Den Wert für das PDF erhalten wir folglich, indem wir mit einer Fliesskommadivision 100'000 durch den Wert aus dem PNG teilen: $$g_{pdf} = \frac{100000}{g_{png}}$$ == cHRM == Der ''cHRM'' Block enthält die Chromatizität des Weisspunkts und der verwendeten Rot- Grün- und Blautöne. Dies ist eine weitere CIE-Norm, die mit zwei Koordinaten x und y ausgedrückt wird. Um Verwechslungen mit dem XYZ-Farbraum zu vermeiden, werden für Chromatizität immer Kleinbuchstaben, für XYZ immer Grossbuchstaben verwendet. ^Position ^Grösse ^Typ ^Wert ^ | 0|4 Byte |uint32 |Blockgrösse | | 4|4 Byte |char |immer ''cHRM'' | | 8|4 Byte |uint32 |xw | | 12|4 Byte |uint32 |yw | | 16|4 Byte |uint32 |xr | | 20|4 Byte |uint32 |yr | | 24|4 Byte |uint32 |xg | | 28|4 Byte |uint32 |yg | | 32|4 Byte |uint32 |xb | | 36|4 Byte |uint32 |yb | | 40|4 Byte |uint32 |Prüfsumme | Ähnlich wie im ''gAMA'' Block handelt es sich um vorzeichenlose Ganzzahlen auf einer Skala von 0 - 100'000. Anders als bei ''gAMA'' sind sie aber nicht invers. Folglich müssen wir diesmal die Zahlen mit Fliesskommadivisionen durch 100'000 teilen. $$x_{pdf} = \frac{x_{png}}{100000}$$ $$y_{pdf} = \frac{y_{png}}{100000}$$ Wenn wir dies erledigt haben, können wir die Werte in den XYZ-Farbraum umrechnen. Zunächst müssen wir die XYZ-Werte des Weisspunkts berechnen: $$X_w = \frac{x_w}{y_w}$$ $$Y_w = 1$$ $$Z_w = \frac{1 - x_w - y_w}{y_w}$$ Für die Grundfarben brauchen wir folgenden Formelsatz: $$z_r = 1 - x_r - y_r$$ $$z_g = 1 - x_g - y_g$$ $$z_b = 1 - x_b - y_b$$ $$\begin{bmatrix} S_r && S_g && S_b \end{bmatrix} = \begin{bmatrix} x_r && y_r && z_r \\\\ x_g && y_g && z_g \\\\ x_b && y_b && z_b \end{bmatrix}^{-1} \begin{bmatrix} X_w && Y_w && Z_w \end{bmatrix}$$ $$\begin{bmatrix} X_r && Y_r && Z_r \end{bmatrix} = \begin{bmatrix} x_r && y_r && z_r \end{bmatrix} S_r$$ $$\begin{bmatrix} X_g && Y_g && Z_g \end{bmatrix} = \begin{bmatrix} x_g && y_g && z_g \end{bmatrix} S_g$$ $$\begin{bmatrix} X_b && Y_b && Z_b \end{bmatrix} = \begin{bmatrix} x_b && y_b && z_b \end{bmatrix} S_b$$ Wie man sieht, enthalten die Formeln leider eine invertierte Matrix. Das macht die arithmetische Variante recht kompliziert: $$z_r = 1 - x_r - y_r$$ $$z_g = 1 - x_g - y_g$$ $$z_b = 1 - x_b - y_b$$ $$m_{xr} = y_gz_b - z_gy_b$$ $$m_{xg} = z_ry_b - y_rz_b$$ $$m_{xb} = y_rz_g - z_ry_g$$ $$d = m_{xr}x_r + m_{xg}x_g + m_{xb}x_b$$ $$S_r = \frac{m_{xr}X_w + (y_bZ_w - z_b) x_g + (z_g - y_gZ_w) x_b}{d}$$ $$S_g = \frac{m_{xg}X_w + (y_rZ_w - z_r) x_b + (z_b - y_bZ_w) x_r}{d}$$ $$S_b = \frac{m_{xb}X_w + (y_gZ_w - z_g)x_r + (z_r - y_rZ_w) x_g}{d}$$ $$X_r = x_rS_r$$ $$Y_r = y_rS_r$$ $$Z_r = z_rS_r$$ $$X_g = x_gS_g$$ $$Y_g = y_gS_g$$ $$Z_g = z_gS_g$$ $$X_b = x_bS_b$$ $$Y_b = y_bS_b$$ $$Z_b = z_bS_b$$ Damit können wir nun die Werte für ''/CalRGB'' zusammenstellen. Ins ''/WhitePoint'' Array, gehören nacheinander die Werte Xw, Yw und Zw. Ins ''/Matrix'' Array nacheinander die Werte Xr, Yr, Zr, Xg, Yg, Zg, Xb, Yb und Zb.