Demosaicing

Demosaicing是一种将Bayer格式的图像数据转换为RGB等格式的图像处理算法。

算子效果

输入图像参数输出图像
image-image

原理

此处介绍了一种利用色差恒定原理的自定义插值方式,将Bayer格式的图像数据转换为RGB格式的图像数据。 具体的输入格式为RAW图片数据排布为RGGB、comp模式、每像素12bit。其具体示意图片如下:

raw_rggb

RGB格式的输出为R、G、B三通道,每通道12bit,每种通道信息独立存储。其具体示意图片如下:

rgb_p

下面以上图为基准,介绍插值计算的实现方法。 在插值计算中,Raw图片的每个位置都需要计算出其对应的RGB值,根据不同位置的计算方式将其分为以下几类:

  • Raw格式R计算R G B。
  • Raw格式G计算R G B。
  • Raw格式B计算R G B。

Raw格式R计算R G B

R通道的值为Raw的值,例如Raw图中的R00,R02点。

G通道的值需要根据Raw的值和周围的值进行插值计算,将此插值计算方法命名为green_5x5。 以R24为例,其计算方式如下:

  • 首先判断插值方向,插值方向根据水平与垂直方向的一阶梯度变化加2倍二阶梯度变化的和作为判断依据:
    • 水平方向梯度变化:dh = abs((G23-G25)/2)+abs((R22+R26-2*R24)/2)。
    • 垂直方向梯度变化:dv = abs((G14-G34)/2)+abs((R04+R44-2*R24)/2)。
  • 当dh < dv,沿着水平方向插值:G = (G23+G25)/2。
  • 当dh > dv,沿着垂直方向插值:G = (G14+G34)/2。
  • 当dh == dv,G = (G23+G25+G14+G34)/4。
  • 插值完成,将计算出来的值clip到有效范围内,例如12bit数据,clip到0~4095范围。

将上述插值用代码表示如下:

// G[i][j] -- green_5x5[i][j]
dh = abs(G[i][j-1] - G[i][j+1]) + abs(R[i][j-2] - R[i][j] + R[i][j+2] - R[i][j]);
dv = abs(G[i-1][j] - G[i+1][j]) + abs(R[i-2][j] - R[i][j] + R[i+2][j] - R[i][j]);
if (dh < dv) {
    G[i][j] = (G[i][j-1] + G[i][j+1]) / 2;
} else if (dh > dv) {
    G[i][j] = (G[i-1][j] + G[i+1][j]) / 2;
} else {
    G[i][j] = (G[i][j-1] + G[i][j+1] + G[i-1][j] + G[i+1][j]) / 4;
}

B通道的值需要根据Raw的值和周围的值进行插值计算,以R44为例,具体的计算方式为 B = G + (B33-G33+B35-G35+B53-G53+B55-G55)/4。

其中G33、G35、G53、G55分别为对应位置G通道的值,计算方法命名为green_3x3。 以B33为例,green_3x3计算方式如下:

  • 计算水平方向梯度变化量绝对值,dh = abs(G32-G34)。
  • 计算垂直方向梯度变化量绝对值,dv = abs(G23-G43)。
  • 当dh < dv,沿着水平方向插值:G = (G32+G34)/2。
  • 当dh > dv,沿着垂直方向插值:G = (G23+G43)/2。
  • 当dh == dv,G = (G32+G34+G23+G43)/4。
  • 插值完成,将计算出来的值clip到有效范围内,例如12bit数据,clip到0~4095范围。

将上述插值用代码表示如下:

// G[i][j] -- green_3x3
int dh = abs(G[i][j-1] - G[i][j+1]);
int dv = abs(G[i-1][j] - G[i+1][j]);
if (dh < dv) {
    G[i][j] = (G[i][j-1] + G[i][j+1]) / 2;
} else if (dh > dv) {
    G[i][j] = (G[i-1][j] + G[i+1][j]) / 2;
} else {
    G[i][j] = (G[i][j-1] + G[i][j+1] + G[i-1][j] + G[i+1][j]) / 4;
}

// G[i-1][j-1] -- calculate by green_3x3 
// G[i-1][j+1] -- calculate by green_3x3 
// G[i+1][j-1] -- calculate by green_3x3 
// G[i+1][j+1] -- calculate by green_3x3 
B[i][j] = G[i][j] + 
          (B[i - 1][j - 1] - G[i - 1][j - 1] +
           B[i - 1][j + 1] - G[i - 1][j + 1] +
           B[i + 1][j - 1] - G[i + 1][j - 1] +
           B[i + 1][j + 1] - G[i + 1][j + 1]) / 4;

Raw格式G计算R G B

根据G的位置不同,将G分为G1和G2两种情况,G1为R同行的G通道,G2为B同列的G通道。

R通道的值需要根据Raw的值和周围的值进行插值计算, 以G23为例,具体的计算方式如下:

  • 计算左侧位置的G通道值G22,计算方法为green_3x3。
  • 计算右侧位置的G通道值G24,计算方法为green_3x3。
  • R = G + (R22-G22+R24-G24)/2。

将上述插值用代码表示如下:

// G[i][j-1] -- calculate by green_3x3
// G[i][j+1] -- calculate by green_3x3
R[i][j] = G[i][j] + (R[i][j-1] - G[i][j-1] + R[i][j+1] - G[i][j+1]) / 2;

以G32为例,具体的计算方式如下:

  • 计算上侧位置的G通道值G22,计算方法为green_3x3。
  • 计算下侧位置的G通道值G42,计算方法为green_3x3。
  • R = G + (R22-G22+R42-G42)/2。

将上述插值用代码表示如下:

// G[i-1][j] -- calculate by green_3x3
// G[i+1][j] -- calculate by green_3x3
R[i][j] = G[i][j] + (R[i-1][j] - G[i-1][j] + R[i+1][j] - G[i+1][j]) / 2;

G通道的值为Raw的值,例如Raw图中的R01,R10点。

B通道的值需要根据Raw的值和周围的值进行插值计算, 以G23为例,具体的计算方式如下:

  • 计算上侧位置的G通道值G13,计算方法为green_3x3。
  • 计算下侧位置的G通道值G33,计算方法为green_3x3。
  • B = G + (B13-G13+B33-G33)/2。

将上述插值用代码表示如下:

// G[i-1][j] -- calculate by green_3x3
// G[i+1][j] -- calculate by green_3x3
B[i][j] = G[i][j] + (B[i-1][j] - G[i-1][j] + B[i+1][j] - G[i+1][j]) / 2;

以G32为例,具体的计算方式如下:

  • 计算左侧位置的G通道值G31,计算方法为green_3x3。
  • 计算右侧位置的G通道值G33,计算方法为green_3x3。
  • B = G + (B31-G31+B33-G33)/2。

将上述插值用代码表示如下:

// G[i][j-1] -- calculate by green_3x3
// G[i][j+1] -- calculate by green_3x3
B[i][j] = G[i][j] + (B[i][j-1] - G[i][j-1] + B[i][j+1] - G[i][j+1]) / 2;

Raw格式B计算R G B

G通道的值需要根据Raw的值和周围的值进行插值计算,计算方法为green_5x5。

R通道的值需要根据Raw的值和周围的值进行插值计算,以B33为例,具体的计算方式为 R = G + (R22-G22+R24-G24+R42-G42+R44-G44)/4。

其中G22、G24、G42、G44分别为对应位置G通道的值,计算方法为green_3x3。

将上述插值用代码表示如下:

// G[i-1][j-1] -- calculate by green_3x3 
// G[i-1][j+1] -- calculate by green_3x3 
// G[i+1][j-1] -- calculate by green_3x3 
// G[i+1][j+1] -- calculate by green_3x3 
B[i][j] = G[i][j] + 
          (B[i - 1][j - 1] - G[i - 1][j - 1] +
           B[i - 1][j + 1] - G[i - 1][j + 1] +
           B[i + 1][j - 1] - G[i + 1][j - 1] +
           B[i + 1][j + 1] - G[i + 1][j + 1]) / 4;

B通道的值为Raw的值,例如Raw图中的B11、B13点。

API接口

int32_t hbVPHBDemosaicing(hbUCPTaskHandle_t *taskHandle, 
                          hbVPImage *dstImg,
                          hbVPImage const *srcImg);

详细接口信息请查看 hbVPHBDemosaicing

使用方法

// Include the header
#include "hobot/hb_ucp.h"
#include "hobot/vp/hb_vp.h"
#include "hobot/vp/hb_vp_hb_demosaicing.h"

// init Image, allocate memory for image data
hbUCPSysMem src_mem;
hbUCPMallocCached(&src_mem, src_stride * src_height, 0);
hbVPImage src_img{HB_VP_IMAGE_FORMAT_RAW_RGGB_COMP_16,
                  HB_VP_IMAGE_TYPE_S16C1,
                  src_width,
                  src_height,
                  src_stride,
                  src_mem.virAddr,
                  src_mem.phyAddr,
                  nullptr,
                  0,
                  0};

hbUCPSysMem dst_mem;
hbUCPMallocCached(&dst_mem, dst_stride * dst_height * 3, 0);
hbVPImage dst_img{HB_VP_IMAGE_FORMAT_RGB_P,
                  HB_VP_IMAGE_TYPE_S16C1,
                  dst_width,
                  dst_height,
                  dst_stride,
                  dst_mem.virAddr,
                  dst_mem.phyAddr,
                  nullptr,
                  0,
                  0};

// init task handle and schedule param
hbUCPTaskHandle_t task_handle{nullptr};
hbUCPSchedParam sched_param;
HB_UCP_INITIALIZE_SCHED_PARAM(&sched_param);
sched_param.backend = HB_UCP_DSP_CORE_0;

// create task
hbVPHBDemosaicing(&task_handle, &dst_img, &src_img);

// submit task
hbUCPSubmitTask(task_handle, &sched_param);

// wait for task done 
hbUCPWaitTaskDone(task_handle, 0);

// release task handle
hbUCPReleaseTask(task_handle);

// release memory
hbUCPFree(&src_mem);
hbUCPFree(&dst_mem);