隔行扫描 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 到灰度的霓虹灯优化的主要内容,如果未能解决你的问题,请参考以下文章