STARTSEITE OPEN SOURCE MDCONTROL MD PNETZ KONTAKT - IMPR.

Bayer-Pattern zu RGB
von Dirk Schäfer
Die meisten digitalen Farbkameras sind mit CCDs ausgerüstet, welche über eine Farbmaske das Licht filtern.  Diese Bayer-Maske (oder Bayer-pattern) gewichtet Grün stärker als Blau und Rot.
 
RGRG R = Rot G = Grün B = Blau
GBGB
RGRG

Innerhalb des Fertigungsprozesses startet die Maske nicht immer mit einem Filter für Rot, und auch nicht immer in einer Zeile ohne Blau-Filter. Es gibt also 4 Masken.
 

RGRG GRGR GBGB BGBG
GBGB BGBG RGRG GRGR
RGRG GRGR GBGB BGBG

Die Farbanteile eines RGB-Bildes, Rot, Grün und Blau, können also nur durch Mittelung der Nachbarpixel im Graubild gewonnen werden. Das folgende Bild zeigt ein Graubild mit Bayer-Filter und das Farbbild.

Da sich die möglichen Spalten RG oder GR, sowie GB oder BG, wiederholen, und
die Zeilen RG-GB, GR-BG, GB-RG und BG-GR dies ebenfalls tun, ergeben sich folgende Möglichkeiten.
RGRG GRGR GBGB BGBG
GBGB BGBG RGRG GRGR
RGRG GRGR GBGB BGBG
GBGB BGBG RGRG GRGR
Bei DWORD-Zugriffen kann man also in einer Zeile immer zwei Masken gleichzeitig bestimmen, und den DWORD-Zeiger jeweils nur um 2 Byte erhöhen. Auch lassen sich gerade und ungerade Zeilen nacheinander berechnen, da sich beide dann wiederholen.

RGRG  B= 22    B= (22+24)/2
GBGB  G= (12+21+23+32)/4  G= 23
RGRG  R= (11+13+31+33)/4  R= (13+33)/2

GBGB  B= (12+32)/2   B= (12+14+32+34)/4
RGRG  G= 22    G= (13+22+24+33)/4
GBGB  R= (21+23)/2   R= 23

Bei Masken die mit BG oder GR starten, ist die Reihenfolge umzudrehen.

Da die Umrechnung der Bilddaten rechenintensiv ist, wird hier eine Lösung mit MMX vorgestellt. Als Vorraussetzung gilt, dass die Zeilen im Grau- und im Farbbild DWORD-aligned sind.

Der C-Source ist einfach und arbeitet mit Byte-Zeigern.
 

Den C-Quellcode finden Sie als HTML-Dokument in einem separaten Browser-Fenster

Dieser C-Code benötigt auf einem 2,5 Jahre alten PIII 500MHz ca. 33 ms.
In Assembler, und mit MMX, reduziert sich die Zeit auf 13 ms, auf einem PIII 1GHz mit 133 FSB sogar auf 8 ms.
Da es sich bei dem Bild um ein BG-Bild handelt, wurde der Code auch nur für dieses Format geschrieben. Optimierungen über pairing, also gutes Laden der U- und V-Pipe um parallele Ausführung zu ermöglichen, sind nicht vorgenommen worden. Auch lassen sich z.B. MMX-Memory-Befehle nur mit MMX-Befehlen pairen.

Die ersten 4 Pixel, also Bytes, des Graubildes sind wie folgt angeordnet:

BGBG  11,12,13,13
GRGR  21,22,23,24
BGBG  31,32,33,34

Das Farbbild startet also mit dem 1. Rotpixel der zweiten Zeile, R1 = 22. Der Grünwert errechnet sich zu (12+21+23+32)/4 und der Blauwert zu (11+13+31+33)/4. Der zweite Pixel des Farbbildes ist auch in diesen 3 DWORDs enthalten.
R2= (22+24)/2, G2= 23, B2= (13+33)/2

Verschiebt man jetzt die DWORDs um 2 Bytes, die Adressen bleiben gerade, kann man die nächsten 2 Pixel des Farbbildes bestimmen. Die o.g. Masken kommen innerhalb einer Zeile abwechselnd zum Einsatz.

Die Formeln für die Farbwerte sind also:
R1= 22
G1= (12+21+23+32)/4
B1= (11+13+31+33)/4
R2= (22+24)/2
G2= 23
B2= (13+33)/2

In der nächsten Zeile ist dann folgende Maske zu bearbeiten:

GRGR
BGBG
GRGR

Diese beiden Zeilen wechseln sich nun im Bild ab. Die Schleife über die Zeile wird also zweimal hintereinander ausgeführt, zuerst mit der BG-Mittelung, dann mit der GR-Mittelung.
 

Den Assembler-Quellcode finden Sie als HTML-Dokument in einem separaten Browser-Fenster

Eine 3x3-Maske reduziert das Bild um 2 Zeilen und um 2 Spalten. Da pro DWORD-Zugriff zwei Pixel bestimmt werden können, und zwei Schleifen jeweils eine gerade und die folgende Zeile errechnen, werden die Schleifen nur über die halbe Anzahl Zeilen und Spalten geführt.

width -= 2;
width >>= 1; // an jeder 2. Stelle ein DWORD einlesen
height -= 2;
height >>= 1;

Die folgende Zeile zentriert das Bild, ist aber nicht notwendig
pColor= pColor + iColorStep +3;

Der Assemblerteil der doppelten Schleife wird vorrausgesetzt.
Der erste MMX-Befehl liest ein DWORD ein,
movd mm0, [esi], die in den folgenden Zeilen liegenden Pixel werden ebenfalls eingelesen,
wobei esi um die Länge einer Zeile, in Bytes, erhöht wird

add esi, iGrayStep                ; nächste Zeile
movd mm1, [esi]
add esi, iGrayStep
movd mm2, [esi]

Die MMX-Register mm0, mm1 und mm2 enthalten jetzt die Bytes des Graubildes.
mm0: 00 00 00 00 7c 2a 7d 2a
mm1: 00 00 00 00 db 8b df 8d
mm2: 00 00 00 00 82 2b 83 2b

Vom niedrigsten Byte aus sieht die Matrix wie folgt aus:

00 00 00 00 14 13 12 11
00 00 00 00 24 23 22 21
00 00 00 00 34 33 32 31

Da bei der Addition ein möglicher Überlauf zwar per Flag kontrolliert werden kann, hier aber höchstens 4 Bytes addiert werden, ist eine Umwandlung in WORDs sinnvoll.

Der Befehl punpcklbw schiebt das höchste Byte im lowdword des Operanden in das highbyte des höchsten WORD.

mm3: 7c00 2a00 7d00 2a00
mm4: db00 8b00 df00 8d00
mm5: 8200 2b00 8300 2b00

WORD-weise schieben erzeugt passende 16bit-Werte

psrlw mm3, 8

mm3: 007c  002a  007d  002a
mm4: 00db  008b  00df  008d
mm5: 0082  002b  0083  002b

Diese Werte können jetzt addiert werden, ohne das ein Überlauf möglich ist.

Nach Addition und Division durch 2, paddw mm5, mm3, psrlw mm5, 1, enthält mm5 die Terme: (14+34)/2 , (13+33)/2 , (12+32)/2 , (11+31)/2

Die Werte von mm4 nach mm6 kopiert, movq mm6, mm4, und durch das Schieben von 32 nach rechts psrlq mm6, 32, neu angeordnet.

mm6: 0000 0000 00db 008b

Die Addition von mm4 und mm6 ergibt, nach Teilung durch 2,
paddw mm6, mm4
psrlw mm6, 1

folgende Werte in mm6: 006d 0045 00dd 008c.

Die beiden oberen WORDs sind unwichtig, die beiden unteren entsprechen
(22+24)/2 und (21+23)/2.

Um (14+34+12+32)/4 und (13+33+11+31)/4 zu bestimmen wird mm5 in mm7 kopiert, um 32 geschoben, mit mm5 addiert und durch 2 geteilt.
movq mm7, mm5
psrlq mm7, 32
paddw mm7, mm5
psrlw mm7, 1

Die beiden oberen WORDs sind nutzlos.

mm5 in mm0, um 16 geschoben und mit mm6 addiert ergibt,
movq mm0, mm5
psrlq mm0, 16
paddw mm0, mm6
psrlw mm0, 1

, (12+32+21+23)/4 und (13+33+22+24)/4 in mm0.

mm7: 0x003f0015007f002a
mm6: 0x006d004500dd008c
mm5: 0x007f002a0080002a
mm4: 0x00db008b00df008d
mm0: 0x0036006200830086

Da Pixel in Farbbildern immer BGR geschrieben werden, ergibt sich folgende Reihenfolge der 6 Bytes.

B1= (11+13+31+33)/4 -> mm7, Byte(0)
G1= (12+21+23+32)/4 -> mm5, Byte(2)
R1= 22    -> mm4, Byte(2)
B2= (13+33)/2   -> mm5, Byte(3)
G2= 23    -> mm4, Byte(4)
R2= (22+24)/2   -> mm6, Byte(3)

Die MMX-Register werden nun geschoben, nach links und rechts, um alle nicht interessierenden Bytes auf 0 zu setzen. Dies ließe sich auch mit AND und entsprechenden Masken durchführen.
Nachdem in jedem Register nur noch das/die entsprechenden Byte(s) stehen, können alle Register ohne Überlauf addiert werden.

Da die Schleife über die Spalten mit dem ersten Pixel beginnt, und das Farbbild DWORD-aligned ist, kann das gesamte MMX-Register geschrieben werden.
movq [edi], mm4
Erhöhen von edi um 6, also 2 x RGB, sorgt beim nächsten schreiben für das Überschreiben der sinnlosen 2 Byte, da ein MMX-Register 8Byte enthält. Es wurden aber nur die unteren 6 Byte mit den beiden Farbwerten geladen.

Die zweite innere Schleife berechnet jetzt die nächste Zeile. Für die 4 möglichen Masken wird jeweils ein Assembler-Code geschrieben und je nach Maske der entsprechende aufgerufen.

... zum Seiten-Anfang April 2002