隔行扫描 YUYV 到灰度的霓虹灯优化

Posted

技术标签:

【中文标题】隔行扫描 YUYV 到灰度的霓虹灯优化【英文标题】:Neon optimization of interlaced YUYV to gray 【发布时间】:2013-10-28 19:30:28 【问题描述】:

我有以下将隔行扫描网络摄像头 YUYV 转换为灰色的 C 代码:

void convert_yuyv_to_y(const void *src, char *dest) 
    int x, y;
    char *Y, *gray;
    //get only Y component for grayscale from (Y1)(U1,2)(Y2)(V1,2)
    for (y = 0; y < CAM_HEIGHT; y++) 
        Y = src + (CAM_WIDTH * 2 * y);
        gray = dest + (CAM_WIDTH * y);
        for (x=0; x < CAM_WIDTH; x += 2) 
            gray[x] = *Y;
            Y += 2;
            gray[x + 1] = *Y;
            Y += 2;
        
    

有没有办法通过一些霓虹灯指令来优化这样的功能?

【问题讨论】:

您是否有一个基准框架,以便您可以衡量任何更改的好处? 基本方法:一次交错读取2个寄存器,只存储一个:vld2.8 d0, d1, [r0]!; vst1.8 d0, [r1]!;高级的东西:对齐到 16 个字节,使用多个单独的路径,确保这些路径不会从相同的缓存行竞争,使用多个线程。 @Aki Suihkonen:您能否总结一下您的基本方法的答案,包括一些代码? 你也可以通过 gcc 使用 "-Wall -mfpu=neon -ftree-vectorize -ftree-vectorizer-verbose=8 -O2 -c" 来编译它。 我还将删除code.google.com/p/libyuv 作为另一个参考。 【参考方案1】:

这是一个起点。从这里您可以进行缓存预加载、循环展开等。当涉及更多 NEON 寄存器以防止数据停顿时,性能最佳。

 .equ CAM_HEIGHT, 480 @ fill in the correct values
 .equ CAM_WIDTH,  640

@
@ Call from C as convert_yuyv_to_y(const void *src, char *dest);
@
convert_yuyv_to_y:
  mov r2,#CAM_HEIGHT  
cvtyuyv_top_y:
  mov r3,#CAM_WIDTH
cvtyuyv_top_x:
  vld2.8 d0,d1,[r0]!   @ assumes source width is a multiple of 8
  vst1.8 d0,[r1]!      @ work with 8 pixels at a time
  subs r3,r3,#8          @ x+=8
  bgt cvtyuyv_top_x
  subs r2,r2,#1          @ y++
  bgt cvtyuyv_top_y
  bx lr

【讨论】:

(+1) 我认为应该是“subs r2,r2,#2”,OP 提到 interlaced 并且 Y 计算为 (CAM_WIDTH * 2 * y). subs r2,r2,#1 是正确的。它正在读取 16 字节的 YUYV 并存储 8 字节的 Y。在垂直轴(r2 计数器)中,没有隔行扫描。【参考方案2】:

(促进我的评论回答)

NEON 架构中对数据进行解交织的最少指令可通过以下顺序实现:

vld2.8  d0, d1 , [r0]!
vst1.8  d0 , [r1]!

这里r0是源指针,每次前进16,r1是目的指针,每次前进8。

循环展开、检索多达 4 个寄存器并将寄存器偏移 2 的能力可以提供稍大的最大吞吐量。再加上16字节对齐:

start:
vld4.8  d0, d1, d2, d3 , [r0:256]
subs r3, r3, #1
vld4.8  d4, d5, d6, d7 , [r1:256]
add r0, r0, #64
add r1, r0, #64
vst2.8  d0, d2 , [r2:256]!
vst2.8  d4, d6 , [r2:128]!
bgt start

(我不记得vstx.y regs, [rx, ro] 的格式是否存在——这里的 ro 是偏移寄存器,后递增 rx)

虽然内存传输优化很有用,但最好还是考虑一下,如果它可以一起跳过,或者与一些计算合并。这也是考虑平面像素格式的地方,可以完全避免复制任务。

【讨论】:

以上是关于隔行扫描 YUYV 到灰度的霓虹灯优化的主要内容,如果未能解决你的问题,请参考以下文章

霓虹灯和手臂组装优化

霓虹灯代码没有优化

使用 ARM 霓虹灯

向量中的内在霓虹灯交换元素

为啥乘法、加法的霓虹内在函数比运算符慢?

SSE 到霓虹灯 (_mm_movelh_ps)