gpt4 book ai didi

opencv - 使用 ARM NEON 的快速高斯模糊图像滤波器

转载 作者:太空宇宙 更新时间:2023-11-03 20:56:08 46 4
gpt4 key购买 nike

我正在尝试制作高斯模糊图像过滤器的移动快速版本。

我读过其他问题,例如:Fast Gaussian blur on unsigned char image- ARM Neon Intrinsics- iOS Dev

出于我的目的,我只需要一个固定大小 (7x7) 的固定 sigma (2) 高斯滤波器。

因此,在针对 ARM NEON 进行优化之前,我在 C++ 中实现了 1D Gaussian Kernel,并直接在移动环境(Android 和 NDK)中与 OpenCV GaussianBlur() 方法比较了性能。这样一来,代码的优化就会简单得多。

然而结果是我的实现比 OpenCV4Android 版本慢 10 倍。我读过 OpenCV4 Tegra 优化了 GaussianBlur 实现,但我不认为标准 OpenCV4Android 有这些优化,那么为什么我的代码这么慢?

这是我的实现(注意:在边界附近应用滤镜时,reflect101 用于像素反射):

Mat myGaussianBlur(Mat src){
Mat dst(src.rows, src.cols, CV_8UC1);
Mat temp(src.rows, src.cols, CV_8UC1);
float sum, x1, y1;

// coefficients of 1D gaussian kernel with sigma = 2
double coeffs[] = {0.06475879783, 0.1209853623, 0.1760326634, 0.1994711402, 0.1760326634, 0.1209853623, 0.06475879783};
//Normalize coeffs
float coeffs_sum = 0.9230247873f;
for (int i = 0; i < 7; i++){
coeffs[i] /= coeffs_sum;
}

// filter vertically
for(int y = 0; y < src.rows; y++){
for(int x = 0; x < src.cols; x++){
sum = 0.0;
for(int i = -3; i <= 3; i++){
y1 = reflect101(src.rows, y - i);
sum += coeffs[i + 3]*src.at<uchar>(y1, x);
}
temp.at<uchar>(y,x) = sum;
}
}

// filter horizontally
for(int y = 0; y < src.rows; y++){
for(int x = 0; x < src.cols; x++){
sum = 0.0;
for(int i = -3; i <= 3; i++){
x1 = reflect101(src.rows, x - i);
sum += coeffs[i + 3]*temp.at<uchar>(y, x1);
}
dst.at<uchar>(y,x) = sum;
}
}

return dst;
}

最佳答案

正如@PaulR 指出的那样,这里的问题的很大一部分是算法过于精确。通常最好不要让您的系数表比您的数据更精确。在这种情况下,由于您似乎正在处理 uchar 数据,因此您将大致使用 8 位系数表。

在您的 NEON 实现中保持较小的权重尤其重要,因为您的算法越窄,您一次可以处理的 channel 就越多。

除此之外,第一个突出的主要减速是在主循环中使用图像边缘反射代码。这将使大部分工作的效率降低,因为在这种情况下通常不需要做任何特殊的事情。

如果您在边缘附近使用特殊版本的循环可能会更好,然后当您安全时使用不调用该循环的简化内部循环 reflect101()函数。

其次(与原型(prototype)代码更相关)是可以在应用加权函数之前将窗口的两翼加在一起,因为该表在两侧包含相同的系数。

sum = src.at<uchar>(y1, x) * coeffs[3];
for(int i = -3; i < 0; i++) {
int tmp = src.at<uchar>(y + i, x) + src.at<uchar>(y - i, x);
sum += coeffs[i + 3] * tmp;
}

这为您节省了每个像素的 6 次乘法运算,这是朝着围绕控制溢出条件进行一些其他优化迈出的一步。

然后还有一些与内存系统相关的其他问题。

两次通过的方法在原则上是好的,因为它可以让您免于执行大量的重新计算。不幸的是,它会将有用的数据推出 L1 缓存,这会使一切变慢。这也意味着当您将结果写入内存时,您正在量化中间总和,这会降低精度。

当您将此代码转换为 NEON 时,您需要关注的一件事是尝试将您的工作集保留在寄存器文件中,但不要在它们被完全利用之前丢弃计算。

当人们确实使用两次传递时,中间数据通常会被转置——也就是说,一列输入变成一行输出。

这是因为 CPU 真的不喜欢在输入图像的多行中获取少量数据。如果你收集一堆水平像素并过滤它们,它会更有效率(因为缓存的工作方式)。如果临时缓冲区被转置,那么第二遍也会收集一堆水平点(它们在原始方向上是垂直的)并再次转置其输出以使其以正确的方式出现。

如果您进行优化以保持工作集本地化,那么您可能不需要这种转换技巧,但值得了解它以便您可以为自己设置健康的基准性能。不幸的是,像这样的本地化确实会迫使您返回到非最佳内存获取,但是对于更广泛的数据类型,可以减轻这种损失。

关于opencv - 使用 ARM NEON 的快速高斯模糊图像滤波器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17486025/

46 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com