【问题标题】:Fast vectorized pixel-wise operations on images图像上的快速矢量化逐像素操作
【发布时间】:2021-02-10 07:45:39
【问题描述】:

我想使用均方误差测量两个灰度相同大小的图像之间的相似度。我不能使用任何不属于 macOS SDK 的框架(例如 OpenCV、Eigen)。没有向量化的这个算法的简单实现是这样的:

vImage_Buffer imgA;
vImage_Buffer imgB;

NSUInteger mse = 0;

unsigned char *pxlsA = (unsigned char *)imgA.data;
unsigned char *pxlsB = (unsigned char *)imgB.data;

for (size_t i = 0; i < imgA.height * imgA.width; ++i) {
    NSUInteger d = pxlsA[i] - pxlsB[i]);
    mse += d * d;
}

有没有办法以更矢量化的方式在没有循环的情况下做到这一点?可能是这样的:

mse = ((imgA - imgB) ^ 2).sum();

【问题讨论】:

  • 我不会说objective-c,但sqrt(d * d)不只是abs(d)吗?另外,我很确定 MSE 不需要sqrt
  • 您希望这种矢量化能做什么?循环在编译语言中并不慢。像imgA - imgB 这样的语句需要一个重载的减号运算符,其中包含一个和你一样的循环。在解释型语言中,这可能会快很多,因为循环会被编译,但这在这里不起作用。在编译语言中,“向量化”通常是指使用 SIMD 处理器指令。如果您在编译器中打开特定于机器的优化,它将使用那些可用的优化。
  • @VladFeinstein 是的,你是对的)
  • @CrisLuengo 我希望矢量化能够提高执行速度。我不明白矢量化背后发生了什么,但我觉得它必须提高速度。我会阅读有关 SIMD 的信息,谢谢。
  • 如果我要这样做,并且需要高性能,我会使用 Core Image 过滤器来做到这一点——但这不是一条容易走的路。即使使用具有 C 接口的 Accelerate,也需要您付出一些努力。

标签: ios objective-c performance image-processing accelerate


【解决方案1】:

这个问题的答案存储在 vDSP 库中,它是 macOS SDK 的一部分。 https://developer.apple.com/documentation/accelerate/vdsp

vDSP - 对大型向量执行基本的算术运算和常见的数字信号处理例程。

在我的情况下,我没有真正的大向量,但仍然如此。

首先,您需要将unsigned char * 转换为float *,顺便说一句,这是一个重要的时刻,我不知道如何在循环中执行此操作。那么你需要两个vDSP函数:vDSP_vsbsbmvDSP_sve

vDSP_vsbsm - 将两个单精度向量的差乘以两个单精度向量的二阶差。

vDSP_sve - 计算单精度向量中值的总和。

所以最终的代码是这样的:

float *fpxlsA = (float *)malloc(imgA.height * imgA.width * sizeof(float));
float *fpxlsB = (float *)malloc(imgB.height * imgB.width * sizeof(float));
float *output = (float *)malloc(imgB.height * imgB.width * sizeof(float));

for (size_t i = 0; i < imgA.height * imgA.width; ++i) {
    fpxlsA[i] = (float)(pxlsA[i]);
    fpxlsB[i] = (float)(pxlsB[i]);
}    

vDSP_vsbsbm(fpxlsA, 1, fpxlsB, 1, fpxlsA, 1, fpxlsB, 1, output, 1, imgA.height * imgB.width);
float sum;
vDSP_sve(output, 1, &sum, imgA.height * imgB.width);

free(output);
free(fpxlsA);
free(fpxlsB);

所以,这段代码完全符合我的要求,并且采用了更加矢量化的形式。但结果还不够好。比较循环方法和 vDSP 方法的性能,如果没有任何额外的内存分配,vDSP 的速度要快两倍。但实际上,在发生额外内存分配的情况下,循环方法会稍微快一些。

【讨论】:

    【解决方案2】:

    这似乎是 Mac OS 的一部分:https://developer.apple.com/documentation/accelerate

    【讨论】:

      【解决方案3】:

      使用指针算术方式进行循环既好又快,如下...

      int d;
      
      size_t i = imgA.height * imgA.width;
      
      while ( i -- )
      {
        d = ( int )(*pxlsA++) - ( int )(*pxlsB++);
        mse += d * d;
      }
      

      编辑

      糟糕,因为它们是无符号字符,而且我们计算差值需要使用 有符号 整数。

      还有一个编辑——这里必须使用pxls...,不知道img...是什么。

      【讨论】:

      • AFAIK C 数组索引在引擎盖下具有指针算法,因此速度将相同
      • 是的,这将接近最快......我在想也许使用按位运算符可以更快,但我对此表示怀疑。
      • 似乎我正在寻找的一种方法是 SIMD 指令,正如 Cris Luengo 所提到的。我将阅读它们并在此处写下答案。
      猜你喜欢
      • 2016-12-18
      • 2016-01-23
      • 1970-01-01
      • 2013-03-03
      • 1970-01-01
      • 2011-03-24
      • 2016-08-22
      • 2022-06-25
      • 2010-10-05
      相关资源
      最近更新 更多