ISP Pipeline(6): Color Filter Array Interpolation 色彩滤波阵列

发布于:2025-07-02 ⋅ 阅读:(20) ⋅ 点赞:(0)

Color Filter Array Interpolation(CFA插值) 是图像信号处理(ISP)中的核心步骤之一。它的目标是:

将原始 Bayer 图像(只有每个像素一个颜色分量)还原成完整的 RGB 图像,即为每个像素补全缺失的两个颜色通道 —— 这个过程称为 Demosaicing

什么是 Color Filter Array(CFA)?

  • 传感器每个像素只能采集一个颜色通道(R、G、B);

  • 为了同时获取三种颜色信息,我们使用了 Bayer Pattern(最常见的一种 CFA):

 Bayer图的像素排序:

R G R G R G...
G B G B...
R G R G...
G B G B...
...

Bayer 图中的每个像素只有 R、G 或 B 中的一个值,其余两个颜色是空的。

插值的目标

对每个像素位置,估算出缺失的两个颜色值,使其成为完整的 (R, G, B) 三通道像素。

插值方法种类

1.  最近邻插值(Nearest Neighbor)

  • 简单快速:从邻近像素直接复制缺失值;

  • 缺点:容易出现伪影或马赛克纹理。

2.  双线性插值(Bilinear Interpolation)

  • 使用上下左右的加权平均估算;

  • 比最近邻更平滑,但边缘保留能力差。

3.  双立方插值、方向感知插值(Edge-Aware)

  • 更复杂,考虑图像边缘信息;

  • 可以减少色彩混叠和锯齿,质量更高。

 代码实现:

def CFA(awb_img):
  """
  inputs:
    awb_img = bayer domain image after auto white balance gain control

  outputs:
    cfa_img = 8 bit RGB image
  """

  awb_img = awb_img.astype(np.uint32) # change dtype to allow for larger numbers during demosaicing
  max_val = np.max(awb_img)
  r = np.empty(awb_img.shape) # create empty arrays for R, G, and B channels
  g = np.empty(awb_img.shape)
  b = np.empty(awb_img.shape)
  padded_img = np.pad(awb_img, (1,1), 'reflect') # pad image to extract shifted channels

  # R-channel
  r_on_r = padded_img[1:-2:2, 1:-2:2] # red intensity values on red pixels

  r_right = padded_img[1:-2:2, 3::2] # shifted matrix of red intensity values in specified direction, same size as r_on_r
  r_down = padded_img[3::2, 1:-2:2]
  r_down_right = padded_img[3::2, 3::2]

  r_on_gr = np.right_shift((r_on_r + r_right), 1) # calculate red intensity values from green and blue pixels
  r_on_gb = np.right_shift((r_on_r + r_down), 1)
  r_on_b = np.right_shift((r_on_r + r_right + r_down + r_down_right), 2)

  r[::2, ::2] = r_on_r # map red intensity values to the entire red channel array
  r[::2, 1::2] = r_on_gr
  r[1::2, ::2] = r_on_gb
  r[1::2, 1::2] = r_on_b

  #G-channel
  g_on_gr = padded_img[1:-2:2, 2:-1:2] # green intensity values on green pixels
  g_on_gb = padded_img[2:-1:2, 1:-2:2]

  g_up = padded_img[:-3:2, 1:-2:2] # shifted matrix of green intensity values in specified direction, same size as g_on_gr
  g_right = padded_img[2:-1:2, 3::2]
  g_left = padded_img[1:-2:2, :-3:2]
  g_down = padded_img[3::2, 2:-1:2]

  g_on_r = np.right_shift((g_left + g_up + g_on_gr + g_on_gb), 2) # calculate green intensity values from red and blue pixels
  g_on_b = np.right_shift((g_right + g_down + g_on_gr + g_on_gb), 2)

  g[::2, ::2] = g_on_r # map green intensity values to the entire green channel array
  g[::2, 1::2] = g_on_gr
  g[1::2, ::2] = g_on_gb
  g[1::2, 1::2] = g_on_b

  # B-channel
  b_on_b = padded_img[2:-1:2, 2:-1:2] # blue intensity values on green pixels

  b_left = padded_img[2:-1:2, :-3:2] # shifted matrix of blue intensity values in specified direction, same size as b_on_b
  b_up = padded_img[:-3:2, 2:-1:2]
  b_up_left = padded_img[:-3:2, :-3:2]

  b_on_gr = np.right_shift((b_on_b + b_up), 1) # calculate blue intensity values from red and green pixels
  b_on_gb = np.right_shift((b_on_b + b_left), 1)
  b_on_r = np.right_shift((b_on_b + b_left + b_up + b_up_left), 2)

  b[::2, ::2] = b_on_r # map blue intensity values to the entire blue channel array
  b[::2, 1::2] = b_on_gr
  b[1::2, ::2] = b_on_gb
  b[1::2, 1::2] = b_on_b

  r = np.clip(r, 0, max_val) # ensure all values are of the correct and same dtype before stacking
  g = np.clip(g, 0, max_val)
  b = np.clip(b, 0, max_val)

  cfa_img = np.dstack((r, g, b)) # Resize and stack the channels to RGB
  cfa_img = ((cfa_img / max_val) * 255).astype(np.uint8) # rescale the image to 8 bit to skip color correction matrix

  return cfa_img
位置 插值策略
R 使用右、下、右下邻域平均
G on R/B 使用上下左右平均
B 使用上、左、左上邻域平均


网站公告

今日签到

点亮在社区的每一天
去签到