Foundations
Bayer Filter
The colour filter array over a single-chip colour sensor, seen as a sampling design, from the Bayer pattern and its four phases to X-Trans, RGBW and Condat's 2 × 3 array, with the Fourier view that places luminance at the origin and colour on carriers.
beginner
A Bayer filter is the mosaic of tiny red, green and blue filters laid over a single-chip colour sensor so that each photosite measures one colour band. More generally it is one example of a colour filter array (CFA): a repeating pattern of filters that decides which colour is sampled where. The pattern is a sampling design. It fixes how much light reaches the sensor, how finely each colour is sampled, and which spatial frequencies of the scene can be confused with colour.
The image sensor article introduces the Bayer pattern, its patent and how sensors are characterized per channel; image formation shows the false colour that appears when fine detail aliases; and demosaicing reconstructs the missing colours. This article covers the pattern itself: its phases, the alternative layouts, and the frequency-domain view that compares them.
Definition
A colour filter array assigns to every pixel position (row, column) a filter with transmittances in and repeats a small tile of such filters over the sensor. Condat (2011, Section II) uses this model, in which white is a transparent filter. The Bayer pattern is the tile with green at two diagonal positions and red and blue at the other two. Bayer’s patent calls the green filters “luminance-type” elements, placed at every other position so that luminance is sampled at half the sites, uniformly over the array, and red and blue “chrominance-type” elements in alternate rows (Bayer, 1976).
Intuition
Picture a chessboard whose black squares are green filters. Every other row fills the white squares with red, the remaining rows with blue. Green, which carries most of the detail the eye cares about in the patent’s reasoning, is sampled twice as densely as red or blue. Unlike a striped filter, the checkerboard samples luminance evenly in the horizontal and vertical directions; the patent contrasts its pattern with vertically striped arrangements that do not (Bayer, 1976, FIGS. 2A–2C).
The price is light and resolution. Each filter absorbs the bands it does not pass, and each colour is known only at some sites. Levoy’s course slides list the alternatives and what each trades away: three-chip cameras split the image with a prism and lose no light to filters but cost weight and expense, stacked Foveon sensors trade off noise, and a colour filter array such as Bayer’s trades off spatial resolution (Levoy, 2013, slides 43–44 and 50).
Formal Definition
For a scene with true colour , the sensor records one number per pixel,
which is Condat’s eq. (1) for the noise-free case. Condat writes colours in an orthonormal luminance–chrominance basis, , (green against magenta) and (red against blue), and expands each component of the periodic CFA as a sum of cosines and sines at the frequencies allowed by its period (his eqs. (2)–(5)). The mosaic is then the luminance and the two chrominance channels of the image, each multiplied by its own carrier wave. In the Fourier domain each channel’s spectrum is copied to the carrier frequencies, so a CFA is described by where it puts these copies and with what gains. Condat credits the observation that a Bayer mosaic is luminance and chrominance moved to different places in the frequency plane to Alleysson, Süsstrunk and Hérault (2005), and demosaicking by frequency selection to Dubois (2005).
For the Bayer tile with red at (RGGB) the expansion is short. The indicator of tile position is , and for a scene of constant colour summing over the four positions gives (our derivation, checked by the example below)
The first term sits at zero frequency; the red–blue difference is modulated to the two axis frequencies and cycles per pixel, and the green–magenta term to the diagonal . Condat states the same three chrominance frequencies for Bayer, , and in radians per pixel, and gives Bayer a luminance gain and a green–magenta gain of (Condat, 2011, Sections III-A and IV-A).
Properties
Phase. The same pattern can start at any of four positions, named by the top-left read row by row: RGGB, GRBG, GBRG and BGGR, as demosaicing explains. Sources draw different phases: Condat’s Fig. 1(a) starts with red, Levoy’s slide 45 draws “G R / B G”, and demosaicnet’s bayer() function assumes the same GRBG layout (Gharbi, 2023). In the formula above, changing phase flips the signs of carriers and nothing else, so the raw file must say which phase it uses (digital camera describes the raw pixel formats).
Unequal colours. Bayer passes twice as much green as red or blue. Condat (2011, Section III-A) sets out design rules for a CFA: the same luminance sensitivity at every pixel, equal average amounts of red, green and blue, and both chrominance channels on a single carrier as far from the origin as possible. Bayer meets the first (its three filters sum to one at every pixel) but, as he notes, not the other two.
Alternatives.
- X-Trans. Fujifilm’s X-Trans pattern repeats on a tile; Gharbi et al. (2016, Section 5) note this and trained a separate X-Trans network. In the cell drawn in demosaicnet’s
mosaic.py(Gharbi, 2023), 20 of the 36 positions are green and 8 each red and blue. - Panchromatic (RGBW) arrays. Kodak’s patent application by Compton and Hamilton mixes colour pixels with panchromatic ones that have a wider spectral response, because colour filters “reduce the amount of light reaching each pixel”. Its minimal repeating unit has at least twelve pixels. In its FIG. 4A embodiment, an unit, rows of panchromatic pixels alternate with rows of colour pixels, and four cells each hold eight panchromatic pixels and eight pixels of one colour, so that the cells form a low-resolution Bayer pattern; FIG. 9 keeps the cells but puts the panchromatic pixels on a checkerboard (Compton & Hamilton, 2007). The same-colour pixels of a cell can be combined: combining them in pairs gives each combined pixel twice the sensitivity, as the patent states for FIG. 7A. Levoy’s slide 45 also shows Kodak’s RGB+C, labelled “more dynamic range”, and Sony’s RGB+E, labelled “better color”.
- Designs in the Fourier domain. Hirakawa and Wolfe (2008) proposed choosing the carriers directly, as Condat describes. Condat’s own result is a tile of six filters, , , , , and , with both chrominance channels in quadrature at , luminance gain and chrominance gain (Condat, 2011, Section III-B). He argues that robustness to noise now matters more than robustness to aliasing, and that higher sensitivity lets a camera use a shorter exposure or a lower ISO setting (Section I); the luminance gain is his measure of the array’s average light sensitivity (Section III-A).
Examples
CFA carriers in the Fourier domain
The example builds five layouts as tiles of transmittance vectors: Bayer (all four phases for the check), the X-Trans cell, Condat’s tile, and two RGBW tiles. It lays each over a sensor, which every tile period (2, 3, 6, 8) divides, photographs scenes of constant colour, and takes the 2D discrete Fourier transform of the mosaic, divided by the pixel count so that the zero-frequency term is the mean. Because the scene is constant, every non-zero term away from the origin is a carrier created by the pattern itself.
Conventions and sources of each layout:
- Arrays are indexed [row, column]; frequencies are in cycles per pixel, from
np.fft.fftfreq. That function labels the Nyquist bin , which is the same frequency as (period 2 pixels), so prints as(-0.5, 0.0). - The X-Trans cell is typed as six rows of letters from the docstring of demosaicnet’s
xtrans()at commit 959e9d1 (MIT licence), with green at the top-left; check C2 compares its counts with the position lists ofxtrans_cell(). - Condat’s tile is generated from his eq. (26) with (his eq. (32) for a period of 3), and , with (period 2) along rows. His Fig. 1(c) draws the period-3 direction vertically, which is the same CFA transposed.
- The RGBW tiles are built from the text of the Kodak application (FIG. 4A and FIG. 9), since its figures were not read. The phases are our choice: panchromatic rows on even rows, and cells coloured G R / B G. Check C5 shows that shifting a tile, which is what a different phase amounts to, changes no carrier magnitude.
- The printed measures: “white AC” is the largest non-zero-frequency term of the mosaic of a white scene divided by its mean (0 when white gives a flat mosaic); “carriers” counts the non-zero frequencies at which the spectrum of any of the three channels of the tile is non-zero (above 1e-9); “on axes” counts those with or ; “nearest” is the smallest distance of a carrier from the origin, followed by the carriers at that distance.
- “Light” is the fraction of an equal-energy white passed, in our ideal box-filter model: each of R, G and B passes one third of the light and W passes all of it, so a pixel passes the sum of its transmittances divided by three. Real filters are not boxes.
The script uses only NumPy 2.3.5 and runs in under a second.
import numpy as np
N = 48 # mosaic size: a multiple of every tile period (2, 3, 6, 8)
R, G, B, W = (1.0, 0, 0), (0, 1.0, 0), (0, 0, 1.0), (1.0, 1.0, 1.0)
LETTER = {"R": R, "G": G, "B": B, "W": W}
def tile(rows):
"""Tile of filter transmittances (rows x cols x 3) from rows of letters."""
return np.array([[LETTER[c] for c in row] for row in rows], dtype=float)
# Bayer, four phases, named by the top-left 2 x 2 read row by row.
BAYER = {p: tile([p[:2], p[2:]]) for p in ("RGGB", "GRBG", "GBRG", "BGGR")}
# X-Trans: the 6 x 6 cell in demosaicnet's mosaic.py (commit 959e9d1), as its
# docstring draws it (G at the top-left).
XTRANS = tile(["GBGGRG", "RGRBGB", "GBGGRG", "GRGGBG", "BGBRGR", "GRGGBG"])
# Condat (2011), eq. (26): 2 x 3 tile, k1 = row (period 2), k2 = column (period 3),
# with gamma_L = sqrt(3)/2, gamma_C = 1/2, omega_0 = 2*pi/3, phi = phi_0 = -pi/3.
gL, gC, w0, phi = np.sqrt(3) / 2, 0.5, 2 * np.pi / 3, -np.pi / 3
CONDAT = np.array([[[gL / np.sqrt(3) + 2 * gC / np.sqrt(3) * (-1) ** k1
* np.sin(2 * np.pi / 3 * (1 - i) + w0 * k2 - phi)
for i in range(3)] for k2 in range(3)] for k1 in range(2)])
def kodak(checker):
"""8 x 8 RGBW tile built from the text of US 2007/0024931 A1: four 4 x 4 cells
whose colours form a low-resolution Bayer pattern (our phase: G R / B G);
each cell has 8 panchromatic (W) and 8 colour pixels. FIG. 4A: W in
alternate rows (our phase: even rows). FIG. 9: W on a checkerboard."""
cells = [["G", "R"], ["B", "G"]]
rows = []
for r in range(8):
row = ""
for c in range(8):
w = (r + c) % 2 == 0 if checker else r % 2 == 0
row += "W" if w else cells[r // 4][c // 4]
rows.append(row)
return tile(rows)
CFAS = {"Bayer RGGB": BAYER["RGGB"], "X-Trans": XTRANS, "Condat 2x3": CONDAT,
"RGBW rows (4A)": kodak(False), "RGBW checker (9)": kodak(True)}
COLOURS = [W, R, G, B, (0.8, 0.5, 0.2), (0.3, 0.6, 0.9)]
def mosaic(t, colour):
full = np.tile(t, (N // t.shape[0], N // t.shape[1], 1))
return full @ np.asarray(colour, dtype=float)
def spectrum(v):
return np.fft.fft2(v) / v.size # Fourier coefficients: V[0, 0] is the mean
f = np.fft.fftfreq(N) # cycles per pixel, index [row, col] = (f_y, f_x)
# --- C1: Bayer, every phase and colour, FFT against the closed form -----------
def bayer_closed_form(t, colour):
"""Each 2 x 2 site (a, b) adds c/4 * [1, (-1)^a, (-1)^b, (-1)^(a+b)]
at the bins (0,0), (1/2,0), (0,1/2), (1/2,1/2)."""
V = np.zeros((N, N))
h = N // 2
for a in range(2):
for b in range(2):
c = t[a, b] @ np.asarray(colour, dtype=float) / 4
V[0, 0] += c
V[h, 0] += c * (-1) ** a
V[0, h] += c * (-1) ** b
V[h, h] += c * (-1) ** (a + b)
return V
err = max(np.abs(spectrum(mosaic(t, c)) - bayer_closed_form(t, c)).max()
for t in BAYER.values() for c in COLOURS)
print(f"C1 Bayer FFT vs closed form, 4 phases x {len(COLOURS)} colours: max error {err:.1e}")
print(" coefficient of (R, G, B) at each carrier, per phase:")
for p, t in BAYER.items():
rows = []
for (iy, ix), name in (((N // 2, 0), "(1/2,0)"), ((0, N // 2), "(0,1/2)"),
((N // 2, N // 2), "(1/2,1/2)")):
coef = [spectrum(mosaic(t, c))[iy, ix].real * 4 for c in (R, G, B)]
rows.append(f"{name} {np.round(coef).astype(int).tolist()}/4")
print(f" {p}: " + " ".join(rows))
# --- C2: X-Trans counts -------------------------------------------------------
n = XTRANS.reshape(-1, 3).sum(axis=0)
print(f"C2 X-Trans 6x6 counts R, G, B = {n.astype(int).tolist()} (demosaicnet lists 8, 20, 8)")
# --- C3: Condat tile from eq. (26) ---------------------------------------------
vals = CONDAT.ravel()
dev = np.abs(vals - np.round(vals * 2) / 2).max()
listed = {(1, 0, .5), (1, .5, 0), (0, 1, .5), (0, .5, 1), (.5, 0, 1), (.5, 1, 0)}
got = {tuple(float(x) for x in np.round(c * 2) / 2) for c in CONDAT.reshape(-1, 3)}
print(f"C3 Condat tile: max distance of entries from {{0, 1/2, 1}} {dev:.1e}; "
f"six listed colours reproduced: {got == listed}")
for k1 in range(2):
print(" row", k1, [tuple(float(x) + 0.0 for x in np.round(c * 2) / 2) for c in CONDAT[k1]])
# --- C4: Condat's gains -------------------------------------------------------
L = np.ones(3) / np.sqrt(3)
C1 = np.array([-1, 2, -1]) / np.sqrt(6)
C2 = np.array([1, 0, -1]) / np.sqrt(2)
for name, t in (("Bayer RGGB", BAYER["RGGB"]), ("Condat 2x3", CONDAT)):
full = np.tile(t, (N // t.shape[0], N // t.shape[1], 1))
lum = full @ L
s1, s2 = spectrum(full @ C1), spectrum(full @ C2)
print(f"C4 {name}: gamma_L = {lum.mean():.6f} (spread {lum.max() - lum.min():.1e})")
if name.startswith("Bayer"):
print(f" C1 carrier at (1/2,1/2): |coefficient| = {abs(s1[N // 2, N // 2]):.6f} "
f"(Condat: sqrt(6)/4 = {np.sqrt(6) / 4:.6f})")
else:
rms1 = np.sqrt((np.abs(s1) ** 2).sum() - abs(s1[0, 0]) ** 2)
rms2 = np.sqrt((np.abs(s2) ** 2).sum() - abs(s2[0, 0]) ** 2)
print(f" gamma_C from C1 = {rms1:.6f}, from C2 = {rms2:.6f} (Condat: 0.5); "
f"means {abs(s1[0, 0]):.1e}, {abs(s2[0, 0]):.1e}")
# --- C5: shifting the tile changes no carrier magnitude ------------------------
shifts = [(0, 1), (1, 0), (1, 1), (0, 4), (4, 0)]
err = 0.0
for t in CFAS.values():
for c in COLOURS:
v = mosaic(t, c)
a = np.abs(spectrum(v))
for s in shifts:
err = max(err, np.abs(np.abs(spectrum(np.roll(v, s, axis=(0, 1)))) - a).max())
print(f"C5 carrier magnitudes under tile shifts {shifts}: max change {err:.1e}")
# --- C6: data checks -------------------------------------------------------------
for name, t in CFAS.items():
ok = bool((t.min() >= -1e-12) and (t.max() <= 1 + 1e-12)) # float tolerance
if name.startswith("RGBW"):
wmask = t.sum(axis=2) == 3
per_cell = [int(wmask[4 * i:4 * i + 4, 4 * j:4 * j + 4].sum()) for i in range(2) for j in range(2)]
colours_per_cell = [len({tuple(p) for p in t[4 * i:4 * i + 4, 4 * j:4 * j + 4][~wmask[4 * i:4 * i + 4, 4 * j:4 * j + 4]]})
for i in range(2) for j in range(2)]
print(f"C6 {name}: entries in [0,1] {ok}; W per 4x4 cell {per_cell}; colours per cell {colours_per_cell}")
else:
print(f"C6 {name}: entries in [0,1] {ok}; tile {t.shape[0]}x{t.shape[1]}")
# --- Measures: carriers, white response, light --------------------------------
print()
print(f"{'CFA':18s} {'light':>6s} {'white AC':>8s} {'carriers':>8s} {'on axes':>7s} {'nearest':>7s} nearest carriers (f_y, f_x)")
for name, t in CFAS.items():
full = np.tile(t, (N // t.shape[0], N // t.shape[1], 1))
light = full.sum(axis=2).mean() / 3 # white light passed, our model
Vw = spectrum(mosaic(t, W))
ac = np.abs(Vw).copy(); ac[0, 0] = 0
white_ac = ac.max() / abs(Vw[0, 0]) # largest white carrier / mean
mag = np.max([np.abs(spectrum(full[..., ch])) for ch in range(3)], axis=0)
mag[0, 0] = 0
iy, ix = np.nonzero(mag > 1e-9)
fy, fx = f[iy], f[ix]
rad = np.hypot(fy, fx)
near = sorted({(round(float(a), 4), round(float(b), 4)) for a, b, r in zip(fy, fx, rad)
if r < rad.min() + 1e-9})
axes = int(((fy == 0) | (fx == 0)).sum())
print(f"{name:18s} {light:6.4f} {white_ac:8.4f} {len(rad):8d} {axes:7d} {rad.min():7.4f} {near}")
wy, wx = np.nonzero(ac > 1e-9)
if len(wy):
print(f"{'':18s} white carriers at {sorted({(round(float(f[a]), 4), round(float(f[b]), 4)) for a, b in zip(wy, wx)})}")
Output (NumPy 2.3.5):
C1 Bayer FFT vs closed form, 4 phases x 6 colours: max error 1.1e-16
coefficient of (R, G, B) at each carrier, per phase:
RGGB: (1/2,0) [1, 0, -1]/4 (0,1/2) [1, 0, -1]/4 (1/2,1/2) [1, -2, 1]/4
GRBG: (1/2,0) [1, 0, -1]/4 (0,1/2) [-1, 0, 1]/4 (1/2,1/2) [-1, 2, -1]/4
GBRG: (1/2,0) [-1, 0, 1]/4 (0,1/2) [1, 0, -1]/4 (1/2,1/2) [-1, 2, -1]/4
BGGR: (1/2,0) [-1, 0, 1]/4 (0,1/2) [-1, 0, 1]/4 (1/2,1/2) [1, -2, 1]/4
C2 X-Trans 6x6 counts R, G, B = [8, 20, 8] (demosaicnet lists 8, 20, 8)
C3 Condat tile: max distance of entries from {0, 1/2, 1} 4.4e-16; six listed colours reproduced: True
row 0 [(0.5, 1.0, 0.0), (0.0, 0.5, 1.0), (1.0, 0.0, 0.5)]
row 1 [(0.5, 0.0, 1.0), (1.0, 0.5, 0.0), (0.0, 1.0, 0.5)]
C4 Bayer RGGB: gamma_L = 0.577350 (spread 0.0e+00)
C1 carrier at (1/2,1/2): |coefficient| = 0.612372 (Condat: sqrt(6)/4 = 0.612372)
C4 Condat 2x3: gamma_L = 0.866025 (spread 5.6e-16)
gamma_C from C1 = 0.500000, from C2 = 0.500000 (Condat: 0.5); means 0.0e+00, 9.3e-18
C5 carrier magnitudes under tile shifts [(0, 1), (1, 0), (1, 1), (0, 4), (4, 0)]: max change 1.5e-16
C6 Bayer RGGB: entries in [0,1] True; tile 2x2
C6 X-Trans: entries in [0,1] True; tile 6x6
C6 Condat 2x3: entries in [0,1] True; tile 2x3
C6 RGBW rows (4A): entries in [0,1] True; W per 4x4 cell [8, 8, 8, 8]; colours per cell [1, 1, 1, 1]
C6 RGBW checker (9): entries in [0,1] True; W per 4x4 cell [8, 8, 8, 8]; colours per cell [1, 1, 1, 1]
CFA light white AC carriers on axes nearest nearest carriers (f_y, f_x)
Bayer RGGB 0.3333 0.0000 3 2 0.5000 [(-0.5, 0.0), (0.0, -0.5)]
X-Trans 0.3333 0.0000 12 4 0.3333 [(-0.3333, 0.0), (0.0, -0.3333), (0.0, 0.3333), (0.3333, 0.0)]
Condat 2x3 0.5000 0.0000 2 0 0.6009 [(-0.5, -0.3333), (-0.5, 0.3333)]
RGBW rows (4A) 0.6667 0.5000 29 9 0.1250 [(-0.125, 0.0), (0.0, -0.125), (0.0, 0.125), (0.125, 0.0)]
white carriers at [(-0.5, 0.0)]
RGBW checker (9) 0.6667 0.5000 29 8 0.1250 [(-0.125, 0.0), (0.0, -0.125), (0.0, 0.125), (0.125, 0.0)]
white carriers at [(-0.5, -0.5)]
What the carriers show
Bayer. The FFT matches the closed form to rounding error for every phase and colour. The carriers hold only colour differences: red minus blue on the axes and green minus magenta on the diagonal. A grey scene, for which those differences vanish, gives a constant mosaic, which is why the white column reads 0. Across the four phases only the signs change. Read as integers, each carrier’s coefficients sum to zero. The nearest carriers lie on the axes at 0.5 cycles per pixel, a period of two pixels. Luminance detail at those frequencies lands on top of the red–blue copies; that overlap is the frequency-domain form of the aliasing that sampling and aliasing and demosaicing describe.
Condat’s array. Its tile reproduces the six colours his paper lists, and its measured gains equal his: luminance , constant at every pixel, and chrominance in both channels with zero mean. All its colour sits at the single pair , 0.6009 cycles per pixel from the origin and off both axes, where Condat (Section III-B) notes that natural images have less luminance energy than along the axes. In our box-filter model it passes half of the light, against a third for Bayer. In this model the light passed equals the luminance gain divided by (since is ), so it is his in other units.
X-Trans. Its period of 6 allows carriers at multiples of 1/6. The cell produces 12, the nearest four on the axes at cycle per pixel, closer to the origin than Bayer’s. By the criterion Condat uses, keeping chrominance far from the luminance at the origin, its colour copies meet luminance detail at lower frequencies than Bayer’s. The example measures only where the carriers are, not how a demosaicer copes with them, so it does not compare image quality.
RGBW. These layouts pass the most light in our model, two thirds, because half of their pixels are panchromatic. They give up two things the other layouts keep. Their colour carriers come down to 0.125 cycles per pixel, the period of 8 pixels on which the cells’ low-resolution Bayer pattern repeats. A white scene no longer gives a flat mosaic, because a panchromatic pixel collects three times what a colour pixel does in this model; the white carrier sits at for the row layout and at for the checkerboard. This is the non-constant luminance sensitivity that Condat’s first rule forbids. In the patent’s design, detail comes from the panchromatic image and colour from the combined pixels of each cell (Compton & Hamilton, 2007, FIGS. 6A–6B).
Outcomes against the plan
All six checks held. C1 held: the FFT matched the closed form with a largest error of 1.1e-16 against a tolerance of 1e-12. C2 held: the X-Trans counts were (8, 20, 8). C3 held: Condat’s tile matched his six colours, its entries within 4.4e-16 of 0, ½ or 1. C4 held: every gain matched Condat’s printed value to the six printed decimals, with spreads and chrominance means at or below 5.6e-16. C5 held: tile shifts changed no carrier magnitude by more than 1.5e-16. C6 held: every layout had entries in , and both RGBW tiles had eight panchromatic pixels and one colour per cell.
All five hypotheses held:
- H1 held: Bayer had 3 carriers, 2 of them on the axes; the nearest were at 0.5000, its white AC was 0.0000 and its light 0.3333.
- H2 held: Condat’s array had 2 carriers at , none on the axes, the nearest at 0.6009, white AC 0.0000 and light 0.5000.
- H3 held: X-Trans had more carriers than Bayer (12 against 3) and its nearest carrier was closer to the origin (0.3333 against 0.5000).
- H4 held for both RGBW layouts: nearest carrier at 0.1250, light 0.6667, white AC 0.5000, and a single white carrier, at for the rows and at for the checkerboard.
- H5 held: light ordered RGBW (0.6667) > Condat (0.5000) > Bayer = X-Trans (0.3333).
H1’s statement about how the signs change with phase was already shown by the planning script that computed the checks’ reachable values, which printed the same per-phase coefficients as C1. That part of H1 is therefore an illustration, not a test.
Process. The example departs from the batch plan in five ways:
- D1: the RGBW layouts come from the patent text, not its figures, with phases we chose.
- D2: the X-Trans cell is used as demosaicnet prints it, with green at the top-left; the plan’s note that demosaicnet assumes GRBG applies to its Bayer function.
- D3: Condat’s tile is generated from his eq. (26), not read from his figure.
- D4: the checks against Condat’s printed gains, the shift check and the data checks were added.
- D5: the closed form is compared for Bayer only, and Hirakawa and Wolfe’s array is not built: Condat’s text (Section V) lists its four colours, but their arrangement appears only in his figure.
One planned check failed in the planning script that computed the reachable values, before the pilot. The C6 check that entries lie in printed False for Condat’s tile, because eq. (26) computed in floating point leaves some entries a rounding error outside the interval (within the distance from 0, ½ or 1 that C3 prints); the same rounding made C3’s listing print some zeros as -0.0. In the plan’s words, “the check was changed to [−1e-12, 1 + 1e-12] (C6 tolerance 1e-12), and C3’s listing adds 0.0 so that negative zeros print as 0.0 (formatting)”. Both edits were made before any pilot ran. The first run is the pilot script unchanged, and its output is identical to the pilot’s.
Common Misconceptions
- “A Bayer sensor records half resolution in green and quarter resolution in red and blue, and that is all.” The counts are right, but the three colours are not three separate images at those resolutions. In the frequency view the mosaic is full-resolution luminance plus colour differences on carriers, and reconstruction is about separating them (demosaicing).
- “Filters only cost resolution.” Each filter absorbs the bands it does not pass; three-chip cameras avoid that loss with a prism (Levoy, 2013, slide 43), and panchromatic pixels recover part of it.
Where It Is Used
- Single-chip colour cameras. Most cameras now place a CFA over a single image sensor, as image formation reports, and in a digital camera the raw image is a mosaic that the image signal processing pipeline demosaics, white-balances and converts to a standard colour space.
- Demosaicing and joint denoising. Every method in demosaicing is written for a particular layout and phase; Gharbi et al. (2016) trained separate networks for Bayer and X-Trans.
- Raw-domain processing. Methods that work on raw mosaics need the layout to know which colour each sample measures; in burst photography, HDR+ aligns Bayer frames in steps of two raw pixels so that a red sample always lands on a red sample.
- CFA design. Frequency-domain criteria like Condat’s, together with light-sensitivity arguments like the Kodak patent’s, compare layouts before any demosaicer is chosen, as the example above does.
Related
- Demosaicing
Reconstructing a full-colour image from the single colour that each photosite of a Bayer colour filter array records; bilinear, gradient-corrected (Malvar–He–Cutler), directional (Hamilton–Adams), colour-difference, residual-interpolation and learned methods, with their exactness, false colour and noise gain compared on images with known ground truth.
- Image Formation
How light leaving a scene becomes an array of pixel values, through illumination and reflectance, the lens, the sensor and its colour filters, sampling and quantization, and the camera's own processing.
- Digital Camera
The camera as an imaging system, from lens, shutter and sensor to the image signal processor that demosaics, white-balances, colour-corrects, tone-maps and compresses its output; RAW against processed images, metadata, machine-vision interfaces, camera classes, and where processing breaks the assumptions of vision methods.
- Sampling and Aliasing
How a continuous image becomes a grid of samples, when the samples determine it completely, and how detail too fine for the grid turns into false, coarser patterns.
- Fourier Transform
The decomposition of an image into two-dimensional sinusoidal gratings of different frequencies, orientations, amplitudes, and phases, and the frequency-domain view of filtering, sampling, and registration that it provides.
- Image Signal Processing Pipeline
The camera's processing chain from raw mosaic to finished picture as a designed system, covering why the order of its stages matters, how clipping, white balance, demosaicing and denoising interact, hardware against software pipelines, the HDR+ finishing pipeline, and inverse pipelines that unprocess images into raw data.
References
- Bayer, B. E. (1976). Color Imaging Array. US Patent 3,971,065.
- Compton, J. & Hamilton, J. (2007). Image Sensor with Improved Light Sensitivity. US Patent Application 2007/0024931 A1, granted as US 8,139,130 B2 (text read; figures not read).
- Condat, L. (2011). A New Color Filter Array with Optimal Properties for Noiseless and Noisy Color Image Acquisition. IEEE Transactions on Image Processing, 20(8), 2200–2210 (author's preprint read).
- Gharbi, M., Chaurasia, G., Paris, S. & Durand, F. (2016). Deep Joint Demosaicking and Denoising. ACM Transactions on Graphics, 35(6), 191 (authors' copy read).
- Gharbi, M. (2023). demosaicnet: mosaic.py (GitHub repository, MIT licence, commit 959e9d1 of 23 June 2023, as of November 2026).
- Levoy, M. (2013). Photons and Sensors. Lecture slides, CS 178: Digital Photography, Stanford University, Spring 2013 (cited by slide number).
- Alleysson, D., Süsstrunk, S. & Hérault, J. (2005). Linear Demosaicing Inspired by the Human Visual System. IEEE Transactions on Image Processing, 14(4), 439–449 (not read).
- Dubois, E. (2005). Frequency-Domain Methods for Demosaicking of Bayer-Sampled Color Images. IEEE Signal Processing Letters, 12(12), 847–850 (not read).
- Hirakawa, K. & Wolfe, P. J. (2008). Spatio-Spectral Color Filter Array Design for Optimal Image Recovery. IEEE Transactions on Image Processing, 17(10), 1876–1890 (not read).