使用 Neon 将图像下采样 3 的算法

Posted

技术标签:

【中文标题】使用 Neon 将图像下采样 3 的算法【英文标题】:algorithm for downsample an image by 3 using Neon 【发布时间】:2013-03-19 17:01:36 【问题描述】:

我想知道是否可以使用霓虹灯向量将图像下采样 3 ? 我正在尝试在纸上为此编写算法,但似乎不可能。因为当你得到例如 8 个字节时,你不能得到 3*3 像素,就没有足够的像素来完成下采样操作。根据2的下采样:Explaining ARM Neon Image Sampling 我考虑加载 16 字节,然后从一行加载 8 字节,然后将它们分配给 32 字节向量,然后处理该向量的 24 字节?

更新: 我已经根据答案编写了示例代码,但是我在 vst1_u8 中遇到了分段错误...

inline void downsample3dOnePass( uint8_t* src, uint8_t *dst, int srcWidth)


    // make sure rows/cols dividable by 8
    int rows = ((srcWidth>>3)<<3);
    // 8 pixels per row
    rows=rows>>3;

    for (int r = 0; r < rows; r++)
    
       // load 24 pixels (grayscale)
       uint8x8x3_t pixels     = vld3_u8(src);
       // first sum = d0 + d1
       uint8x8_t firstSum     = vadd_u8 ( pixels.val[0], pixels.val[1] );
       // second sum = d1+d2;
       uint8x8_t secondSum    = vadd_u8 ( firstSum,  pixels.val[2] );
       // total sum = d0+d1+d2
       uint8x8_t totalSum     = vadd_u8(secondSum, firstSum);
       // average = d0+d1+d2/8 ~9 for test
       uint8x8_t totalAverage = vshr_n_u8(totalSum,3);
       // store 8 bytes
       vst1_u8(dst, totalAverage);
       // move to next 3 rows
       src+=24;
       // move to next row
       dst+=8;

    


【问题讨论】:

我不知道你在问什么。您提供的链接中的代码每行处理 8 个像素,而不是 8 个字节。 @CareyGregory 我假设是灰度图像 【参考方案1】:

对于您处理的每条扫描线,您可以通过vld3.8 使用structure loads。如果你有r0..r2中第一、二、三行像素的起始地址,那么:

vld3.8 d0,d1,d2, [r0]
vld3.8 d3,d4,d5, [r1]
vld3.8 d6,d7,d8, [r2]

给你

d0 具有第一行的字节 [0,3,6,9,12,15,18,21] d1 具有第一行的字节 [1,4,7,10,13,16,19,22] d2 具有第一行的字节 [2,5,8,11,14,17,20,23] 第二行的d3..d5 和第三行的d6..d8 相同

然后对它们进行平均。您可能希望扩展到 16 位,以免降低精度。

编辑:总数看起来有点像(不包括除以九):

//
// load 3x8 bytes from three consecutive scanlines
//
uint8x8x3_t pixels[3] =
     vld3_u8(src), vld3_u8(src + srcwidth), vld3_u8(src + 2*srcwidth) ;

//
// expand them to 16bit so that the addition doesn't overflow
//
uint16x8_t wpix[9] =
     vmovl_u8(pixels[0].val[0]),
      ...
      vmovl_u8(pixels[3].val[2]) ;

//
// nine adds. Don't always add to wpix[0] because of possible dependencies.
//
wpix[0] = vaddq_u16(wpix[0], wpix[1]);
wpix[2] = vaddq_u16(wpix[2], wpix[3]);
wpix[4] = vaddq_u16(wpix[4], wpix[5]);
wpix[6] = vaddq_u16(wpix[6], wpix[7]);
wpix[0] = vaddq_u16(wpix[0], wpix[8]);

wpix[1] = vaddq_u16(wpix[2], wpix[4]);
wpix[3] = vaddq_u16(wpix[6], wpix[0]);
wpix[0] = vaddq_u16(wpix[1], wpix[3]);

[ .. divide-by-nine magic (in 16bit, aka for uint16x8_t), in wpix[0] ... ]
//
// truncate to 8bit and store back
//
vst1_u8(dst, vmovn_u16(wpix[0]);

祝你好运!

【讨论】:

9 平均的指令是什么?之后我应该存储平均值的结果值? 没有单一的说明。将它们相加并近似除以 9。 (d9 + d9>>3 - d9>>6) >> 3;已经很接近了。 这个著名的示例章节来自黑客对按常量除法 的喜悦,hackersdelight.org/divcMore.pdf 有一个示例如何仅使用常量移位和添加来编写div9。如有必要,这可以完全在 NEON 指令中完成。 Aki 的代码是一个近似值。 @FrankH。根据您的回答,我已将我的问题更新为新代码 @Ahmed:vst 内在的分段错误意味着目标 (dst) 指针无效。您可以在调试器中运行它并获取寄存器状态以及精确的PC 值/故障指令吗?

以上是关于使用 Neon 将图像下采样 3 的算法的主要内容,如果未能解决你的问题,请参考以下文章

图像下采样性能

图像上采样和图像下采样

从 C 到 Neon 的双线性插值

对图像进行上采样和下采样的最佳方法

下采样与上采样

重采样Resample 的一些研究记录。