0

我想使用均方误差测量两个灰度相同大小的图像之间的相似度。我不能使用任何不属于 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();
4

3 回答 3

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 的速度要快两倍。但实际上,在发生额外内存分配的情况下,循环方法稍微快一些。

于 2020-10-29T17:39:11.150 回答
0

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

int d;

size_t i = imgA.height * imgA.width;

while ( i -- )
{
  d = ( int )(*pxlsA++) - ( int )(*pxlsB++);
  mse += d * d;
}

编辑

哎呀,因为它们是无符号字符,并且因为我们计算了差异,所以我们需要使用有符号整数来做到这一点。

另一个编辑 - 必须pxls...在这里使用,不知道是什么img...

于 2020-10-28T12:44:32.087 回答
0

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

于 2020-10-28T00:37:12.993 回答