去马赛克示例介绍
去马赛克是一种图像处理技术,用于将Bayer格式的图像数据转换为RGB格式的图像数据。去马赛克的原理是通过对Bayer格式的图像数据进行插值处理,生成RGB格式的图像数据,以便于后续的图像处理。 为了更好的展示如何使用DSP实现去马赛克的功能,本章从算法原理出发,展示了详细的去马赛克算法实现流程。
原理介绍
Bayer格式的图像数据是一种单通道的图像数据,其中每个像素点只包含一种颜色的信息, 常见的Bayer格式有RGGB、BGGR、GRBG、GBRG等。而RGB格式的图像数据是一种三通道的图像数据,其中每个像素点包含红、绿、蓝三种颜色的信息。 根据插值算法的不同,去马赛克算法可以分为最近邻插值、双线性插值、双三次插值等,在本示例中采用了一种利用色差恒定原理的自定义插值方式。
插值算法分析
在此示例中,RAW图片数据排布为RGGB、comp模式、每像素12bit。其具体示意图片如下:
RGB格式的输出为R、G、B三通道,每通道16bit,每种通道信息独立存储。其具体示意图片如下:
下面以上图为基准,介绍插值计算的实现方法。 在插值计算中,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范围。
将上述插值用代码表示如下:
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范围。
将上述插值用代码表示如下:
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。
将上述插值用代码表示如下:
以G32为例,具体的计算方式如下:
- 计算上侧位置的G通道值G22,计算方法为green_3x3。
- 计算下侧位置的G通道值G42,计算方法为green_3x3。
- R = G + (R22-G22+R42-G42)/2。
将上述插值用代码表示如下:
G通道的值为Raw的值,例如Raw图中的R01,R10点。
B通道的值需要根据Raw的值和周围的值进行插值计算, 以G23为例,具体的计算方式如下:
- 计算上侧位置的G通道值G13,计算方法为green_3x3。
- 计算下侧位置的G通道值G33,计算方法为green_3x3。
- B = G + (B13-G13+B33-G33)/2。
将上述插值用代码表示如下:
以G32为例,具体的计算方式如下:
- 计算左侧位置的G通道值G31,计算方法为green_3x3。
- 计算右侧位置的G通道值G33,计算方法为green_3x3。
- B = G + (B31-G31+B33-G33)/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。
将上述插值用代码表示如下:
B通道的值为Raw的值,例如Raw图中的B11、B13点。
DSP 实现流程
DSP的实现主要围绕着以下几点进行优化:
- 通过使用tile_manager框架,将数据分块处理,并将分块数据的搬运和计算构造pingpong流水线。
- 通过SIMD方法实现green_3x3、green_5x5插值方法。
- 使用数据分类等方法,将相同数据的计算过程进行合并,充分使用宽位寄存器的批量处理能力。
tile manager框架的使用
tile manager是cadence提供的一种算子开发框架,通过将整体数据分块处理,将数据的搬运和计算构造pingpong流水线,提高数据处理效率。 其中frame是框架中对完整输入或者输出的描述,tile是框架中对frame进行分块的描述,是框架中的计算处理单元。 算子开发中通常按照如下步骤使用:
- 创建算子参数结构体
- 实现tile分块函数,将frame数据按照顺序进行分块处理
- 实现每个tile的计算函数,完成单个tile的处理
- 通过tile manager相关接口,构造pingpong流水线,实现完整算子功能
在Demosaicing算子中,Frame结构体中包含一个输入frame和三个输出frame,Tile结构体种包含一个输入tile和三个输出tile。 这样就满足了在每个输入tile中都可以计算出对应的三个RGB tile的目的。 具体参数结构体实现如下:
下一步要实现的是tile的分块函数,按照一定逻辑将frame数据分成逐个处理的块。在Demosaicing算子中,输入输出的每个像素的位置映射是一对一的,位置也不会发生变化。 例如,Raw图片中的G21点,在RGB图片中的每个通道中坐标也是(2,1)。因此,输入tile的坐标和设置和输出一致。 在Demosaicing计算中,Raw图片的边界值计算需要使用的数据超出了原图大小,因此需要填充边界值。 在上文描述的插值算法中,最多使用的是5x5的范围,因此,输入的边界值大小设置为2,输出的边界值大小设置为0。
具体实现如下:
第三步要实现的是每个tile的计算函数,是算子的核心处理逻辑,对应源码中的demosaic_kernel函数。
最后,调用框架相关接口,实现多个tile的流水线处理,对应源码中的demosaic_rggb_comp函数。 代码核心如下:
实现插值算法green_3x3、green_5x5
在上文对插值算法的分析中,可以提取出核心的插值逻辑 green_3x3 和 green_5x5。通过SIMD优化,实现代码如下:
其中,函数的输入参数为计算插值所需要的不同位置的数组,每个元素计算所需的值根据数组下标一一对应。
主体计算逻辑合并和SIMD优化
根据分析,在计算插值过程中,可以将奇数和偶数位置的数据分开处理,比如偶数行元素为RG,可以通过奇偶性将R和G分开进行计算,最后再将计算结果合并。 在插值计算中,每个位置的计算最多涉及到周围5x5的内核大小,因此定义5x6的混合内核如下:
算子的处理按照DSP中int16的最大处理长度进行,因此第一层循环按照宽度,步长为1024/16 = 64,第二层循环按照高度进行,步长为1。 可以看出,在内层的处理逻辑中,混合内核的前4行数据可以复用,可以节省重复的计算逻辑,减少耗时。 流程大致如下:
- 读取数据,并按照奇偶位置将数据进行拆分。
- 将拆分后的数据进行判断,执行不同的去马赛克逻辑。
- 计算结果按照奇偶位置进行合并,并保存到输出数据中。
- 将插值内核中的数据进行更新,充分利用本次计算结果,进行下一轮循环。
第一步,读取数据,并按照奇偶位置将数据进行拆分。DSP实现中将读取数据并按照奇偶分开的过程封装为了LOAD_AND_SELECT_ODD_EVEN_WITH_OFFSET宏函数, 该函数的功能为读取最多64*3=192个数据,并按照奇偶位置拆分,将拆分后的数据按照混合内核的位置进行选择,保存到对应的寄存器中。 其定义及使用代码如下:
第二步,将拆分后的数据进行判断,执行不同的去马赛克逻辑。在计算插值过程中,相同颜色的计算逻辑是一致的,因此将相同颜色的计算逻辑合并。 在具体实现中,行的奇偶判断取y起始坐标加y偏移坐标的最后一位,即(y+ys)%2。列的奇偶判断取x偏移的最后一位,即(x+xs)%2。因为列的处理步长一定是偶数, 所以x偏移不会影响奇偶性。具体实现代码如下:
第三步,将计算结果按照奇偶位置进行合并,并保存到输出数据中。主要实现方法为通过双选操作,将数据合并。 还需要注意的是,在处理结果之前,要将结果限制到合法的输出范围。具体实现代码如下:
第四步,将插值内核中的数据进行更新,充分利用本次计算结果,进行下一轮循环。在内层循环中,很多参数是可以复用的, 5x6的混合内核每次循环向下移动一行,因此只需要更新最后一行的数据,寄存器数组、green_3x3的临时结果都可以使用旧值。具体实现代码如下:
至此,整个Demosaicing算法的实现流程已经完成。此示例仅供参考,在实际应用场景中,可以根据此示例进行修改和优化,或许可以进一步提升算子效果和性能。
