3

我正在研究一种图像处理算法,并且正在研究使用 NEON 对其进行优化。该算法包括将每个(RGBA,8bit)像素乘以一些权重,进行一些加法并最终转换回 uint8_t 值。我遇到的第一个问题是如何有效地加载单个 uint8_t 像素并将其转换为 NEON 的 float32x4_t。我已经在参考中搜索了合适的转换,但找不到合适的转换,所以我求助于这个丑陋的代码:

const uint8_t* psrc = ...; // pointer to image data
float rgba[4];
for (int c = 0; c < 4; ++c) {
  rgba[c] = *psrc++;
}
float32x4_t srcpix = vld1q_f32(rgba);

任何人都可以为此建议一种“更清洁”的方式吗?

编辑:所以我想出了这个,仍然觉得很麻烦:

uint8x8_t srcu8 = vld1_u8(psrc);
uint16x8_t srcu16x8 = vmovl_u8(srcu8);
uint16x4_t srcu16x4 = vget_low_u16(srcu16x8);
uint32x4_t srcu32x4 = vmovl_u16(srcu16x4);
srcpix = vcvtq_f32_u32(srcu32x4);
4

3 回答 3

1

VTBX查表指令可以在单个操作中进行无符号 8 位到 32 位扩展,但不幸的是,输出是单个霓虹灯寄存器(可能是)uint32x2_t,因此要“填充”a uint32x4_t,您需要调用它两次。对于uint8x8_t源的所有八个字节,您必须执行以下操作:

uint8x8_t bvec = vld1_u8(psrc);

uint8x8x4_t tbl = {
    { 0, -1, -1, -1, 1, -1, -1, -1 },
    { 2, -1, -1, -1, 3, -1, -1, -1 }
    { 4, -1, -1, -1, 5, -1, -1, -1 }
    { 6, -1, -1, -1, 7, -1, -1, -1 }
};

uint32x4_t ivec[2] = {
    {
    vreinterpret_u32_u8(vtbx1_u8(tbl[0], bvec, 0)),
    vreinterpret_u32_u8(vtbx1_u8(tbl[1], bvec, 0))
    },
    {
    vreinterpret_u32_u8(vtbx1_u8(tbl[2], bvec, 0)),
    vreinterpret_u32_u8(vtbx1_u8(tbl[3], bvec, 0))
    }
};

float32x4_t vec[2] = { vcvtq_f32_u32(ivec[0]), vcvtq_f32_u32(ivec[1]) };

我认为它的说明并不比您找到的方法少。查找表也将来自内存,因此它可能会更慢。然后还需要vreinterpret.........这是一个免费的操作,但看起来很脏。

于 2013-06-21T12:02:15.930 回答
1

所以你想将它们转换为浮点数以进行一些算术运算并将结果转换回整数?这与人们所说的优化正好相反。

坚持使用 NEON 真正闪耀的定点算法。

我几乎无法想象在处理每个通道只有 8 位大小(和精度)的 ARGB 格式时转换为浮点数会有意义的任何情况。

显然,您正试图让 NEON 在 ARM 完成浮点运算时来回进行转换,但这正是使用 NEON 的错误方式。

适当的 NEON 优化功能应让 NEON 自行处理数据加载、算术和数据存储。如果操作正确,我相信 NEON 版本的运行速度将比您当前的版本快 20 倍以上,速度接近 memcpy。- NEON 的定点运算功能非常强大。

请透露更多信息,您正在尝试做什么。也许我可以帮忙。

于 2013-06-19T23:38:51.020 回答
0

据我所知,NEON仅支持 32 位转换(使用vcvt_...()您可以在float32x4_tint32x4_t(例如)之间进行转换)。因此,您需要将您的转换为uint8x8_tuint32x4x2_t然后使用.vcvtuint32x4x2_t

编辑: 不幸的是,我无法为您提供代码,因为我没有使用它很多时间并且不记得命令。

于 2013-06-19T17:15:54.423 回答