D7WS HOMEPAGE WORKSHOP NEUE XTRAS GALERIE WORKSHOP Automatic Translation LESERSERVICE BUCH NEWS

HowTo: Gamma-Korrekturen an Bitmap-Darstellern
oder: Wie spare ich 50 Euro durch Lingo?

 

 

 

Home
Workshop-Seite

Dieser Artikel ist ein Beitrag von Joachim Baur (baur@medien-werkstatt.com).

Inhaltsübersicht

 

Gammakorrekturen mit Lingo anstelle der schnelleren Filter Factory...

Eigentlich wollte ich mit der Filter Factory (vgl. hier) eine nützliche Gammakorrektur zusammenbauen, die FF kennt aber leider keine Exponentialfunktionen. Um aber dennoch nicht auf kostenpflichtige Xtras (da gibt es u.a. ein Gamma-Xtra für 50 Euro unter http://www.magisterludi.com/xtras/gammaxtra.html) zurückgreifen zu müssen, kann das (wie im Folgenden zu sehen, durchaus zu Recht) undokumentierte getpixel/setpixel weiterhelfen.

Zuerst eine kurze Einführung zum Begriff und Problem der Gamma-Korrektur.




 

 

 

 

Was ist der Gammawert?

Die Gammafunktion

y = x hoch (1 / Gammawert)

ist eine nichtlineare Funktion, mit der die Helligkeitsverteilung von schwarz (x=0) nach weiss (x=1) z.B. eines Röhrenmonitors annähernd korrekt beschrieben werden kann. Der x-Wert steht dabei für den dem Gerät angelieferten Helligkeitswert, der y-Wert ist die Helligkeit, die dann auch wirklich zu sehen ist. Ein gleichmäßig abgestufter Grauverlauf erscheint daher auf einem Monitor (Gammawert so zwischen 2.2 und 2.5) heller als z.B. die eingescannte Fotovorlage. Um das auszugleichen, gibt es eine Gamma-Korrektur in den Bildbearbeitungsprogrammen, und z.T. auch im Betriebssystem selbst. Die Gamma-Korrektur verändert den Weiss- bzw. Schwarzpunkt eines Bilds nicht, sondern nur die Grauverteilung dazwischen!

Grob gesagt, führt ein
Gammawert von 0...1 zu einem dunkleren Bild, ein
Gammawert > 1 zu einem helleren Bild, ein
Gammawert = 1 zu keiner Bildänderung und ein

Gammawert < 0 zu keinem vernünftigen Ergebnis

Eine ausführliche und mathematisch korrektere Erklärung gibt's hier:
ftp://ftp.igd.fhg.de//pub/doc/colour/GammaFAQ.pdf
ftp://ftp.igd.fhg.de//pub/doc/colour/ColorFAQ.pdf
(Achtung: FTP-Übertragungsart auf "Binär" statt "Text"/"Automatisch" stellen...)




 

 

 

 

Sind Mac-Monitore zu hell eingestellt?

Es wird oft behauptet/beklagt, daß Mac-Monitore "heller eingestellt" seien als PC-Monitore. Das ist in sofern nicht richtig, weil ich auf einem Mac-Monitor natürlich genauso ein richtiges, tiefes Schwarz sehe wie am PC-Monitor und außerdem ein am Mac angeschlossener PC-Monitor ohne Änderung der Helligkeits-/Kontrast-Reglereinstellungen sofort ein "helleres" Bild liefert. Der Grund für den Unterschied liegt im Mac-Betriebssystem auf der untersten Ebene, dem sogenannten "QuickDraw", das u.a. für die Übertragung des Bildschirmspeicherinhalts an den Monitor zuständig ist. QuickDraw selbst nimmt (unabhängig von irgendwelchen Benutzereinstellungen, Kontrollfeldern, etc) eine durchgehende Gammakorrektur von ca. 1.4 am Ausgangsbildsignal vor - das Bild wird somit am Monitor heller dargestellt als an einem PC (unahängig vom Monitor selbst).

Um die - heller dargestellte - Grafik wieder "korrekt" zu sehen, macht der (Mac-)Designer sie natürlich dunkler... und prompt ist sie für einen PC zu dunkel...

Der in Newsgroups (von PC-Usern ;-) gerne verbreitete Tip für die Macianer, "doch einfach den Monitor dunkler einzustellen" ist natürlich völliger Quatsch, da der Helligkeitsregler keinen Einfluß auf die Gammakurve hat, sondern im "besten" Fall den Weißwert auf hellgrau herunterregelt... Wer also eine für Mac und PC korrekte Bilddarstellungen erreichen will, muß

  1. entweder einen Gamma-Mittelwert für seine Grafiken wählen (= am Mac etwas zu dunkel, am PC etwas zu hell)
  2. oder zwei Versionen für die verschiedenen Plattformen parallel ausliefern...

Auf jeden Fall ist öfter mal eine nachträgliche Gammakorekktur - auch in Director selbst - nötig, und dazu komme ich jetzt.




 

 

 

 

Gammakorrektur für Darsteller mit 32- und 16-Bit Farbtiefe

Die "getpixel()"-Funktion liefert eine Zahl von 0...16 777 215 zurück, in der die Rot-, Grün- und Blauanteile des Pixels anhand dieser Formel zusammenaddiert wurden:

getPixelWert = Rotwert*256*256 + Grünwert*256 + Blauwert

Jeder Farbkanal (rot, Grün, Blau) umfaßt bei einem

32-Bit-Darsteller (der ja eigentlich nur 24-Bit-Farbtiefe aufweist, plus 8 Bit für einen optionalen Alphakanal) einen
Wertebereich von 0...255 und bei einem

16-Bit-Darsteller (auch nur reale 15-Bit, plus 1 Bit für einen optionalen Alphakanal) einen
Wertebereich von 0...31

Für eine Gammakorrektur liegt es nahe, jeden Pixel des Darstellers einzeln mit getpixel() auszulesen, ihn mit der Gammaformel zu überarbeiten und wieder mit setpixel() zurückzuschreiben.

Eine mögliche Lingo-Umsetzung für 32-Bit Bitmapdarsteller:

 

on gammaKorrektur g, m

  -- g: gammaWert im Bereich von
  -- 0...1: Bild wird dunkler
  -- > 1 : Bild wird heller
  -- < 0 : nicht erlaubt

  -- m: castmember

  farbtiefe = member( m ).depth
  if farbtiefe < 32 then exit --nur bei 32-Bit-Darstellern

  gammaWert = 1 / float( g )
  -- ich rechne ( 1 / Gamma ) schon im voraus aus, dann geht
  -- nachher power( farbwert, ( 1 / Gamma ) ) schneller

  skalierung = 255 / power( 255, gammaWert )
  -- dieser Skalierungsfaktor sorgt dafür, daß nach der
  -- Berechnung wieder Werte von 0...255 rauskommen

  h = member(m).height-1
  w = member(m).width-1

  repeat with y = 0 to h
  -- alle y-Werte (= Zeilen des Bitmaps)...
    repeat with x = 0 to w
    -- ...und innerhalb der Zeilen jeden Pixel auslesen

      RGBalt = getpixel( member( m ), x, y )
  
      Rot = RGBalt / 65536
      Gruen = ( RGBalt - 65536*Rot ) / 256
      Blau = RGBalt mod 256

      -- Rot ist klar,
      -- für Gruen wird zuerst vom Startwert der Rotanteil abgezogen
      -- der Rest kann dann schön einfach durch 256 geteilt werden

      -- Gruen = RGBalt / 256 mod 256
      -- geht genauso, ist aber auf meinem Mac auch nicht schneller.

      -- Bei Blau müßte eigentlich vom Startwert der Rot- und Gruenanteil
      -- abgezogen werden, dann bleibt der Blauwert automatisch übrig:
      -- Blau = RGBalt - 65536*Rot - 256*Gruen
      -- aber das geht einfacher mit "mod"

      -- alle Einzelwerte werden jetzt mit der Gammafunktion überarbeitet

      RotNeu = integer( power( Rot,gammaWert ) * skalierung )
      GruenNeu = integer( power( Gruen,gammaWert ) * skalierung )
      BlauNeu = integer( power( Blau,gammaWert ) * skalierung )

      -- und wieder zu einem einzigen Wert zusammengebaut und zurückgeschrieben

      RGBneu = ( RotNeu*65536 + GruenNeu*256 + BlauNeu )
      setpixel( member( m ), x, y ,RGBneu )

    end repeat
  end repeat
end

Hier die Auswirkung am Beispiel eines Grauverlaufs (Darsteller-Farbtiefe aber trotzdem 32 Bit RGB!) nach Eingabe von

gammaKorrektur 1.8, 1

im Messagefenster:

Member 1 (linearer Grauverlauf in RGB) vorher Member 1 nach Gammakorrektur mit Gamma 1.8

Schwarz- und Weißwert sind gleichgeblieben, nur die Mitteltöne sind heller geworden.




 

 

 

 

Jetzt zu den 16-Bit-Darstellern

Wie oben schon erwähnt, werden für die Farbinformationen insgesamt 15 Bit benutzt, also 5 Bit je Farbe, was insgesamt 32 Abstufungen pro Farbkanal zuläßt. Manche Programme packen diese 15 Bit in 2 Byte (2*8 Bit), Director hingegen nutzt anscheinend intern dieselben 3 Byte wie für die 32-Bit-Darsteller, nur daß nur jeweils die unteren 5 Bit je Byte belegt sind. Ein kleines Diagramm soll das verdeutlichen:

Aufteilung der Farbkanäle bei 24-Bit Farbtiefe:

Byte 1 ROT
Byte 2 GRÜN
Byte 3 BLAU
1 1 1 1 1 1 1 1

= 255

1 1 1 1 1 1 1 1

= 255

1 1 1 1 1 1 1 1

= 255


getpixel-Wert für Weiss: 255*256*256 + 255*256 + 255 = 16 777 215

Aufteilung der Farbkanäle bei 16-Bit Farbtiefe:

Byte 1 ROT
Byte 2 GRÜN
Byte 3 BLAU
- - - 1 1 1 1 1

= 31

- - - 1 1 1 1 1

= 31

- - - 1 1 1 1 1

= 31


getpixel-Wert für Weiss: 31*256*256 + 31*256 + 31 = 2 039 583

 

Ein kurzer Test mit getpixel und verschiedenen Grautönen liefert dann auch die erwarteten Resultate - aber setpixel reagiert völlig unerwartet, indem es den Rotkanal einfach unterschlägt! Ein kleiner Test mit einem Handler, der nur mit setpixel den mit getpixel ausgelesenen Wert zurückschreibt:

on darfSichEigentlichNichtsAendern m
  -- m: castmember
  repeat with y = 0 to ( member( m ).height - 1)
    repeat with x = 0 to ( member( m ).width - 1)
      setpixel( member( m ), x,y ,getpixel( member( 1 ), x, y ) )
    end repeat
  end repeat
end

zeigt dieses Ergebnis:

16-Bit-Darsteller vorher ...und nachher

Das Nachher-Bild mit der Cyan-Färbung zeigt, daß zwar die Blau- und Grünwerte korrekt gesetzt wurden (sonst wäre da gar kein Verlauf mehr), daß aber der Rotwert im ganzen Bild auf 0 steht (deswegen die schöne Einfärbung). Es ist mir nicht gelungen, irgendeinen Weg zu finden, den Rotwert korrekt zurückzuschreiben (weder negative Vorzeichen, das Setzen der unbenutzen 3 Bits, u.ä. haben sichtbare Ergebnisse gezeigt).
Das bedeutet, daß für 16-Bit-Darsteller die setpixel-Funktion - zumindest auf meinem System - leider nicht verwendet werden kann, und somit eine Gammakorrektur für 16-Bit-Darsteller nur über den Umweg der Umwandlung nach 32-Bit, Korrektur und Rückwandlung zu 16-Bit möglich ist. Speicherintensiv, langsamer und schade. Wer eine Lösung hat, her damit!




 

 

 

 

Gammakorrektur für Farbtiefen mit 8 Bit oder weniger

Für alle Farbtiefen von 8-Bit oder weniger ist der oben stehende "GammaKorrektur"-Handler nicht geeignet, da mit eingebauten bzw. selbstdefinierten Paletten gearbeitet wird. Eine praktikable Lösung ist es, die jeweilige Palette zu exportieren, in DeBabelizer o.ä. die Gammakurve der Palette zu ändern und die neu importierte Palette über die ursprüngliche zu kopieren. Alle mit der ursprünglichen Palette verbundenen Bitmap-Darsteller sind dann "auf einen Streich" korrigiert.

 

Kritik, Anregungen und Kommentare (vor allem zu der 16-Bit-Sache) gerne an:

Joachim Baur | medienwerkstatt | grafik-design




 

 

 

 

Ergänzung für Director 8

In Director 8 liefert die Funktion getpixel() in den Farbtiefen 16 und 32 Bit Farbobjekte vom Typ #rgb, in 8 Bit ein Farbobjekt vom Typ #paletteIndex zurück. Ebenso läßt sich setpixel() mit Farbobjekten "füttern". Damit ist es denkbar, den von Joachim Baur für 32-Bit-Bilder beschriebenen Weg für alle drei Farbtiefen anzuwenden.

in 16/32 Bit:
put member(1).image.getpixel(point(1,1))
-- rgb( 255, 255, 0 )

in 8 Bit:
put member(1).image.getpixel(point(1,1))
-- paletteIndex( 5 )
acolor = member(1).image.getpixel(point(1,1))
acolor.colortype = #rgb
put acolor
-- rgb( 255, 255, 0 )

Das 16-Bit-"Gegenbeispiel", das in Director 7 noch zu Verfärbungen führte, funktioniert in Director 8. Ich habe es für den Test folgendermaßen umformuliert:

on darfSichEigentlichNichtsAendern m
  -- m: castmember
  repeat with y = 0 to ( member( m ).height - 1)
    repeat with x = 0 to ( member( m ).width - 1)
      member( m ).image.setpixel( point(x,y) , \
      member( 1 ).image.getpixel( point( x, y ) ))
    end repeat
  end repeat
end

Joachim Gola





Directorworkshop.de ist © Joachim Gola & Gerd Gillmaier 1998-2002. Alle Rechte vorbehalten.