Demosaicing是一种将Bayer格式的图像数据转换为RGB等格式的图像处理算法。
算子效果
原理
此处介绍了一种利用色差恒定原理的自定义插值方式,将Bayer格式的图像数据转换为RGB格式的图像数据。
具体的输入格式为RAW图片数据排布为RGGB、comp模式、每像素12bit。其具体示意图片如下:
RGB格式的输出为R、G、B三通道,每通道12bit,每种通道信息独立存储。其具体示意图片如下:
下面以上图为基准,介绍插值计算的实现方法。
在插值计算中,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);