Bewegung im Farbraum

Allgemeines:
Der Farbraum ist physikalisch gesehen nichts als ein Würfel mit zwei Koordinatensystemen, die je nach Betrachtungsweise in zwei gegenüberliegenden Ecken ihren Ursprung haben.
Diese beiden Ecken stehen für die Farben schwarz und weiß.
Liegt die Farbe Schwarz (keine Wellenlänge) im Ursprung, spricht man von additiver Farbmischung, da man sich durch Hinzufügen von Farbanteilen der Farben rot, grün, blau sich in Richtung auf die Farbe weiß (alle Wellenlängen) zubewegt.
Die drei Farbkomponenten sind rot,grün,blau (red,green,blue). Man spricht hier von den RGB-Farben.
Typisches Beispiel für additive Farbmischung ist der Bildschirm, auf dem man mit einer Lupe mit hinreichender Vergrößerung auf einer weißen Fläche die roten, grünen und blauen Linien oder Farbpunkte (Pixel) erkennen kann.

Farbraum

Bewegt man sich von weiß weg in die Gegenrichtung, spricht man von subtraktiver Farbmischung. Druck und Malerei funktionieren nach diesem Prinzip, da die aufgetragenen Farbpigmente wie Farbfilter wirken, die aus dem weißen reflektierten Licht der Unterlage die entsprechenden Wellenlängen entfernen. Wegen der Basisfarben Cyan, Magenta und Gelb (cyan, magenta, yellow) werden diese Farben als CMY-Farben bezeichnet.

Darstellung der Farben im C(++)-Programm (Little Endian)

Für jede der drei RGB-Farben wird ein nicht vorzeichenbehaftetes Byte reserviert und damit eine Werteskala von null bis 255. Die daraus resultierenden 256*256*256 (16.777.216) Farben sind so feingranular unterteilt, dass sie für das menschliche Auge den unterscheidbaren Bereich mit Sicherheit abdecken.
typedef unsigned  long DWORD;
typedef unsigned  long DWORD;
typedef DWORD COLORREF;
#define RGB(r,g,b) ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))

Auf Little-Endian-Systemen liegt der Rotanteil am niederwertigsten Byte des DWORDs – entsprechend höherwertiger der Grünanteil und der Blauanteil.

Daher wird der einzelne Anteil der Farbe folgendermaßen ermittelt:

#define GetRValue(rgb)      ((BYTE)(rgb))
#define GetGValue(rgb)      ((BYTE)(((WORD)(rgb)) >> 8))
#define GetBValue(rgb)      ((BYTE)((rgb)>>16))

Das freibleibende höchstwertige BYTE kann dabei für eine weitere Eigenschaft des Pixels (wie zum Beispiel eine Kodierung der Transparenz) verwendet werden.
(Vorsicht mit der Darstellung beim Rückermitteln!)

Damit ergeben sich einige Grundfarben folgendermaßen:

#define BLACK       0x000000 // 0
#define BROWN       0x000080 // 1
#define DGREEN      0x008000 // 2
#define OCHRE       0x008080 // 3
#define DBLUE       0x800000 // 4
#define VIOLET      0x800080 // 5
#define BGREEN      0x808000 // 6
#define LGRAY       0xc0c0c0 // 7
#define MGRAY       0x808080 // 8
#define RED         0x0000ff // 9
#define GREEN       0x00ff00 // A
#define YELLOW      0x00ffff // B
#define BLUE        0xff0000 // C
#define PINK        0xff00ff // D
#define LBLUE       0xffff00 // E
#define WHITE       0xffffff // F
#define LVIOLET     PINK
#define LGBLUE      0xff8080
#define SKY         0xffff80
#define LRED        0x8080ff
#define LLRED       0xc0c0ff
#define DGRAY       0x404040
#define DRED        BROWN
#define DDGREEN     0x004000
#define DMGREEN     0x006000
#define ORANGE      0x20A0ff
#define WOOD        0x4080C0
#define LWOOD       0x3060ff
#define LGREEN      0x80ff80
Dabei sind die angeführten ersten sechzehn dieser Farben die Windows-Grundfarben, die zum Beispiel für bestimmte Windows-Icons verwendet werden.
Für sehr komplexe Berechnungen mit Farbwerten mit vielen Einzelschritten empfiehlt es sich, vor der Berechnung die Farbe in eine Struktur wie zum Beispiel:
typedef struct  { double r,g,b; } DOUB_COLOR;
zu wandeln, und nach der Berechnung in die Systemdarstellung rückzuwandeln.

Interpolation einer Farbe aus zwei Grundfarben
Will man eine neue Farbe so errechnen, dass sie die erste Farbe verschoben um einen gewissen Prozentsatz zur zweiten Farbe darstellt, gilt die Vektordarstellung

Vres = V1 + p * V1V2
wobei mit V1V2 der Vektor von V1 zu V2 gemeint ist farbwertinterpolation

Die Interpolation ist für jede der drei Koordinaten r,g,b anzusetzen.
Der dazu passende C++-Code:
Die Farbe c1 wird um den Faktor f in Richtung auf c2 verschoben.

static void Move(CLR c2,CLR&c1,double f) // 0<=f<=1
{ 
    c1=RGB(GetRValue(c1)+f*(GetRValue(c2)-GetRValue(c1)),
    GetGValue(c1)+f*(GetGValue(c2)-GetGValue(c1)),
    GetBValue(c1)+f*(GetBValue(c2)-GetBValue(c1))); 
}

Interpolation einer Farbe aus mehr als zwei Grundfarben

Will man zum Beispiel eine Farbe c aus drei Grundfarben c1, c2 und c3 so ermitteln, dass deren Einfluss im Verhältnis 60%:30%:10% aufgeteilt wird, so ist zunächst eine (Zwischenergebnis-) Farbe cm aus c1 und c2 im Verhältnis 60:30 zu bilden, die dann mit c3 im Verhältnis 90:10 die Ergebnisfarbe c liefert. Bei mehr als drei Grundfarben c1, c2, . . . cn ist dieser Vorgang n-2 mal zur Bildung einer Zwischenfarbe ci anzuwenden, bis sich im letzten Schritt die Ergebnisfarbe c aus ci und cn ergibt.

Farbverläufe entlang Kurven zwischen zwei Punkten

Bild mit Farbverläufen

Kontinuierliche Farbverläufe zwischen zwei Extremwerten ergeben sich, wenn man in obiger Interpolationsgleichung den Faktor f entsprechend der Weglänge vom Punkt P1 zum Punkt P2 wachsen lässt.
Bessere Ergebnisse bei rhythmischen Verlaufswiederholungen erhält man, wenn man den Faktor f nicht entsprechend der Weglänge bestimmt, sondern als Kosinusfunktion:

static void Move(CLR c2,CLR&c1,double f,int ifact)
{
    const DB HLF=0.5; Move(c2,c1,HLF+HLF*cos(PI*f*(DB)ifact));
}

ifact ist dabei die Anzahl der Wiederholungen auf der Weglänge.

Cosinusfunktion

Projektion einer Fläche auf ein neues Koordinatensystem

Verschiedene Koordinatensysteme

Die im folgenden beschriebenen Interpolationsmethoden sind je nach dem verwendeten Verfahren:
- linear
- quadratisch
- kubischa
Dabei wird interpoliert
linear:            zwischen 4 Farben
quadratisch: zwischen 9 Farben
kubisch:        zwischen 16 Farben
Durch Koordinatentransformation wird zunächst die Lage des Punktes P (Pi) des im Urspungsraster ermittelt.
Im nächsten Schritt werden folgendermassen die beteiligten Interpolationspunkte gebildet:
linear:
4 Punkte desjenigen Quadrates des Urspungsrasters in dem P liegt: P1,P2,P3,P4 quadratisch: zunächst Ermitteln des zu P nächstliegenden Punktes P1. Dann Ermitteln der umliegenden Quadratkanten P2, P3, P4, P5, . . . , P9.
kubisch:

Zuerst Ermitteln von P1,P2,P3,P4 wie bei der linearen Vorgangsweise.
Dann Ermitteln der umliegenden Quadratkanten P5, . . . , P9, P10, . . . , P16.
Nun werden die im Ursprungsraster orthogonalen Geraden gA und gB gebildet und mit den jeweiligen Quadratkanten und inneren Quadratlinien geschnitten. Daraus ergeben sich die Schnittpunkte der Strecken lA und lB.
Durch die Rot-, Grün- und Blauwerte der äusseren und inneren Quadratlinien werden folgende Interpolationskurven gelegt:

linear:
rA = a0A + a1A*x
rB = a0B + a1B*y
analog für gA und gB bzw. bA und bB

quadratisch:
rA = a0A + a1A*x + a2A*x2
rB = a0B + a1B*y + a2B*y2
analog für gA und gB bzw. bA und bB

kubisch:
rA = a0A + a1A*x + a2A*x2 + a3A*x3
rB = a0B + a1B*y + a2B*y2 + a3B*y3
analog für gA und gB bzw. bA und bB

Jetzt werden die Ursprungsrasterkoordianten der Schnittpunkte der Strecken lA und lB in obige Interpolationsgleichungen eingesetzt und damit die Interpolationswerte rAi, rBi, gAi, gBi, bAi, bBi gebildet.
Daraus ergeben sich jeweils zwei neue lineare, quadratische oder kubische Interpolationskurven auf den Strecken lA und lB. Nun erfolgt die Interpolation durch Einsetzen der Ursprungsrasterkoordianten des Punktes P. Wenn man die zwei nun ermittelten Gleitkommawerte (double) mittelt und rückprojiziert auf die möglichen RGB-Werte, ist bei Anwendung des quadratischen oder des kubischen Verfahrens zu beachten, dass die Ergebnisse ausserhalb von
0 <= r <= 255
0 <= g <= 255
0 <= b <= 255
liegen können und in diesem Fall korrigiert werden müssen!
Nach meiner Erfahrung kann nicht allgemeingültig vorausgesagt werden, welches der Verfahren für einen bestimmten Bildausschnitt die besten Ergebnisse liefert und es ist daher im Einzelfall zu entscheiden, welche Vorgangsweise man wählt.

Zurück