二进制图像上的快速像素计数 - ARM neon 内在函数 - iOS 开发
Posted
技术标签:
【中文标题】二进制图像上的快速像素计数 - ARM neon 内在函数 - iOS 开发【英文标题】:Fast Pixel Count on Binary Image- ARM neon intrinsics - iOS Dev 【发布时间】:2012-01-16 22:21:59 【问题描述】:谁能告诉我一个快速的函数来计算二值图像中白色像素的数量。 iOS 应用开发需要它。我正在直接处理定义为的图像的内存
bool *imageData = (bool *) malloc(noOfPixels * sizeof(bool));
我正在实现这个功能
int whiteCount = 0;
for (int q=i; q<i+windowHeight; q++)
for (int w=j; w<j+windowWidth; w++)
if (imageData[q*W + w] == 1)
whiteCount++;
这显然是可能的最慢的功能。我听说 ios 上的 ARM Neon 内在函数 可用于在 1 个周期内进行多项操作。也许这就是要走的路??
问题是我不是很熟悉,目前没有足够的时间学习汇编语言。因此,如果有人可以针对上述问题发布 Neon 内部代码或任何其他 C/C++ 中的快速实现,那就太好了。
我能在网上找到的霓虹内在函数中唯一的代码是 rgb 到灰色的代码 http://computer-vision-talks.com/2011/02/a-very-fast-bgra-to-grayscale-conversion-on-iphone/
【问题讨论】:
我来看看这个,sizeof(bool)
是什么?
另外,imageData[]
中的可能值是什么?它只是0还是1,还是可以有其他非零值?
【参考方案1】:
首先,您可以通过分解乘法并去掉分支来稍微加快原始代码的速度:
int whiteCount = 0;
for (int q = i; q < i + windowHeight; q++)
const bool * const row = &imageData[q * W];
for (int w = j; w < j + windowWidth; w++)
whiteCount += row[w];
(假设 imageData[]
是真正的二进制,即每个元素只能是 0 或 1。)
这是一个简单的 NEON 实现:
#include <arm_neon.h>
// ...
int i, w;
int whiteCount = 0;
uint32x4_t v_count = 0 ;
for (q = i; q < i + windowHeight; q++)
const bool * const row = &imageData[q * W];
uint16x8_t vrow_count = 0 ;
for (w = j; w <= j + windowWidth - 16; w += 16) // SIMD loop
uint8x16_t v = vld1q_u8(&row[j]); // load 16 x 8 bit pixels
vrow_count = vpadalq_u8(vrow_count, v); // accumulate 16 bit row counts
for ( ; w < j + windowWidth; ++w) // scalar clean up loop
whiteCount += row[j];
v_count = vpadalq_u16(v_count, vrow_count); // update 32 bit image counts
// from 16 bit row counts
// add 4 x 32 bit partial counts from SIMD loop to scalar total
whiteCount += vgetq_lane_s32(v_count, 0);
whiteCount += vgetq_lane_s32(v_count, 1);
whiteCount += vgetq_lane_s32(v_count, 2);
whiteCount += vgetq_lane_s32(v_count, 3);
// total is now in whiteCount
(假设imageData[]
是真正的二进制,imageWidth <= 2^19
和sizeof(bool) == 1
。)
unsigned char
的更新版本,白色为 255,黑色为 0:
#include <arm_neon.h>
// ...
int i, w;
int whiteCount = 0;
const uint8x16_t v_mask = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ;
uint32x4_t v_count = 0 ;
for (q = i; q < i + windowHeight; q++)
const uint8_t * const row = &imageData[q * W];
uint16x8_t vrow_count = 0 ;
for (w = j; w <= j + windowWidth - 16; w += 16) // SIMD loop
uint8x16_t v = vld1q_u8(&row[j]); // load 16 x 8 bit pixels
v = vandq_u8(v, v_mask); // mask out all but LS bit
vrow_count = vpadalq_u8(vrow_count, v); // accumulate 16 bit row counts
for ( ; w < j + windowWidth; ++w) // scalar clean up loop
whiteCount += (row[j] == 255);
v_count = vpadalq_u16(v_count, vrow_count); // update 32 bit image counts
// from 16 bit row counts
// add 4 x 32 bit partial counts from SIMD loop to scalar total
whiteCount += vgetq_lane_s32(v_count, 0);
whiteCount += vgetq_lane_s32(v_count, 1);
whiteCount += vgetq_lane_s32(v_count, 2);
whiteCount += vgetq_lane_s32(v_count, 3);
// total is now in whiteCount
(假设imageData[]
的白色值为 255,黑色为 0,imageWidth <= 2^19
。)
请注意,以上所有代码都未经测试,可能需要进一步的工作。
【讨论】:
抱歉 - 里面有几个错别字(现已修复) - 我应该提到这是未经测试的代码,因此可能需要进一步的工作 - 我只是想提出一般想法。跨度> 很抱歉再次打扰.. 但我在 uint8x16_t v = vld1q_u8(&row[j]);说 - 无法使用 const bool * 类型的值初始化 const unit8_t *(又名 unsigned char *)的变量 - 知道问题可能是什么吗?? 我使用 bool* 作为图像数据,因为我认为它会更快,因为每个值只需要 1 位内存 @Shreyas:我在上面问过你 sizeof(bool) 是什么,但你没有回应 - 通常是 sizeof(char) 或 sizeof(int) - 现在我假设它是 sizeof( char)(即 1 byte),但如果不正确,则需要修改代码,或者您需要使用合适的 1 字节类型(例如 uint8_t)。 否 - 这会使实现更容易溢出 - 我在上面添加了一个更新版本,它可以处理白色 == 255 的情况。【参考方案2】:http://gcc.gnu.org/onlinedocs/gcc/ARM-NEON-Intrinsics.html
第 6.55.3.6 节
矢量化算法会进行比较并将它们放入一个结构中,但您仍然需要遍历结构中的每个元素并确定它是否为零。
该循环当前运行的速度有多快,您需要它运行多快?另请记住,NEON 将在与浮点单元相同的寄存器中工作,因此在此处使用 NEON 可能会强制 FPU 上下文切换。
【讨论】:
以上是关于二进制图像上的快速像素计数 - ARM neon 内在函数 - iOS 开发的主要内容,如果未能解决你的问题,请参考以下文章
无符号字符图像上的快速高斯模糊 - ARM Neon Intrinsics - iOS Dev