【问题标题】:Image convolution with a Gaussian Blur, speedups possible?具有高斯模糊的图像卷积,可以加速吗?
【发布时间】:2017-10-23 19:57:48
【问题描述】:

作为前言,这是一道作业题,但我已经完成了,只是速度很慢。这对报告无关紧要。但无论如何我都在努力加快速度。

所以我正在尝试对图像应用高斯模糊

const unsigned char KERNAL_SIZE = 5;
const unsigned char KERNAL_MEAN = KERNAL_SIZE / 2;
const unsigned char GaussianBlurKernal[KERNAL_SIZE][KERNAL_SIZE] =
{ { 1, 4, 7, 4, 1 },
{ 4, 16, 26, 16, 4 },
{ 7, 26, 41, 26, 7 },
{ 4, 16, 26, 16, 4 },
{ 1, 4, 7, 4, 1 }
};
const int GAUSSIAN_KERNAL_SIZE = 273;

并通过以下代码:

Mat CustomGuassianBlur(const Mat & inputImage)
{
    Size maxSize = inputImage.size();
    Mat outputImage(maxSize, CV_8UC1, Scalar(0, 0, 0));
    for (int i = 0; i < maxSize.width; i++)
    {
        for (int j = 0; j < maxSize.height; j++)
        {
            unsigned int sum = 0;
            for (int k = -KERNAL_MEAN; k <= KERNAL_MEAN; k++)
            {
                for (int l = -KERNAL_MEAN; l <= KERNAL_MEAN; l++)
                {
                    Point imagePoint = { (i + l + maxSize.width) % maxSize.width, (j + k + maxSize.height) % maxSize.height };
                    sum = sum + inputImage.at<uchar>(imagePoint) * GaussianBlurKernal[k + KERNAL_MEAN][l + KERNAL_MEAN];
                }
            }
            sum = sum / GAUSSIAN_KERNAL_SIZE;
            outputImage.at<uchar>(j, i) = uchar(sum);
        }
    }
return outputImage;
}

过滤器大约需要 8 秒。我试图近似的 OpenCV 代码:

GaussianBlur(src_gray, detected_edges, Size(5, 5), 0, 0);

在同一张图片上耗时不到四分之一秒。

我可以做些什么来加快这个过程?我已经阅读了一些关于应用两个一维过滤器的信息,但老实说,我不能很好地理解它。我还阅读了一些关于对图像和过滤器进行 FFT,将它们相乘,然后进行 IFFT 的内容,但我认为这不是我想要在这里做的:我发现提到的资源只是当涉及到更大的过滤器时,效率会更高。

【问题讨论】:

  • 我对 OpenCV 一无所知,但您的代码将为图像中的每个像素调用 inputImage.at&lt;uchar&gt;(imagePoint) 二十五次。如果 OpenCV 不会为您提供指向像素数组的原始指针,也许您可​​以创建自己的原始数组,并通过调用 inputImage.at&lt;uchar&gt;(imagePoint) 来填充它,每个像素只需 一次 次。
  • 还值得注意的是,OpenCV 将在 GPU 可用时利用它们,这对于卷积等操作可以显着加快。要更直接地与 OpenCV 进行比较,您可以尝试禁用该优化。
  • 在下面查看@CostantinoGrana 的答案。真正要杀死你的是#3。我的建议:切换循环的顺序,使用Mat.ptr 而不是at 获取指向整行的指针,然后重新测量以查看您还需要/想要优化多少。我认为下一步是重写为线性可分离过滤器。即:两次通过:一次在行上,一次在列上。

标签: c++ image-processing opencv3.0


【解决方案1】:

我的几分钱:

  1. 请调用您的索引变量rc,以便您知道什么是行,什么是列。从长远来看,它将对您有所帮助。
  2. 核心?这不应该是内核吗?还是这是另一种语言?
  3. 记住您的缓存:您的矩阵以行优先顺序存储,因此每一行都要遍历每一列。
  4. 如果您的内核大小是固定的,请展开内部双循环。
  5. 如果您的内核大小必须更改,请在一个内部循环中使用cv::Mat::ptr() 获取当前行的地址,以至少在列索引中节省一些计算。
  6. 模数宽度和高度听起来不正确。您正在使用环形图像表示。不寻常。

【讨论】:

  • 1) 好主意,谢谢 2) 我不是最擅长拼写的,请不要因此而杀了我。 3)我会试试看。 4) 将来它将扩展到任意大小的内核。 6) 部分要求,是平铺图像。
【解决方案2】:

除了@CostantinoGrana 的回答,我想指出,2D 高斯平滑是线性可分的。除了使用 2D 内核,您可以使用 2 个 1D 内核 - 第一次遍历行,第二次遍历列。

此外,如果您可以访问多个内核,则可以并行对行/列进行一维过滤。请参阅cv::parallel_for_ 构造。

【讨论】:

  • 您究竟是如何使用 2 个 1D 内核的?我有点困惑。您是否将它们都卷入其中并仅添加结果?还是将结果相乘?
  • 通常这样:1)对源图像的行执行一维过滤并调用输出rowFiltered,2)对rowFiltered图像的列执行一维过滤并写入到你的目的地。
  • @ChrisPy 也看看cv::parallel_for_
猜你喜欢
  • 1970-01-01
  • 2011-03-22
  • 2013-10-25
  • 2015-01-18
  • 1970-01-01
  • 1970-01-01
  • 2015-11-05
  • 1970-01-01
  • 2021-08-11
相关资源
最近更新 更多