【问题标题】:Gradient direction computation梯度方向计算
【发布时间】:2010-10-23 10:56:21
【问题描述】:

我正在完成计算机视觉课程的任务。子任务之一是基于图像亮度的梯度方向计算。我制作了一个矩阵 bright[width][height] 包含图像每个像素的亮度值。我有两个这样的功能:

double Image::grad_x(int x,int y){
    if(x==width-1 || x==0) return bright[x][y];
    return bright[x+1][y]-bright[x-1][y];
}
double Image::grad_y(int x,int y){
    if(y==height-1 || y==0) return bright[x][y];
    return bright[x][y+1]-bright[x][y-1];
}

编辑:修复边界检查

我正在使用简单的导数,而不使用 Sobel 运算符,因为简单的导数足以满足我的需要。

问题是,我是否正确地进行了这个梯度计算,我与边界像素到底有什么关系(现在函数返回像素本身的值,我不确定它是否准确)?而且,顺便说一句,计算图像的梯度有什么用处吗?我想确保我的程序运行良好。

【问题讨论】:

标签: c++ image-processing gradient


【解决方案1】:

你的计算是正确的。这是您正在使用的一种简单的渐变方法,但如果这对您来说没问题,那也没有什么问题。

极端情况是个问题,因为您没有足够的数据来以与其他像素相同的方式计算梯度。处理它们的一种方法是简单地不计算极端情况并使用稍小的图像。

如果这不是一个选项,您还可以推断缺失的数据。如果您假设渐变平滑变化,它的工作原理如下:

在您的 x 梯度计算中,您可能已经计算了像素 1 的导数 A 和像素 2 的导数 B。如果您想推断像素 0(极端情况)的值,可以使用值 a-(b-a) .

一个数值例子:

  pixel1: gradient = 100
  pixel2: gradient = 80

  extrapolate using a-(b-a): 

  pixel0: gradient = 100 - (80-100)) = 120

【讨论】:

    【解决方案2】:

    因此,一方面您希望保持简单,另一方面您希望程序运行良好。呵呵。

    我实际上在做类似的事情,不过,我不太关心边界。我喜欢基于三次 B 样条曲线的系数。如果您将离散的 2D 信号与 2D 三次 B 样条进行卷积,您将获得一个非常平滑且两次连续可微分的函数。可以在任意点计算此函数的精确强度和导数。由于三次 B 样条不是插值器,因此与原始结果相比,结果会有些平滑。但这对许多应用程序来说都不是问题。事实上,它在许多情况下往往会有所改善(在一定程度上抑制噪音)。如果你不想要这种平滑效果,你可以绕过它(见我下面的参考)。

    在一维中,使用三次 B 样条作为重构滤波器进行重构,然后再次对信号进行采样,相当于将信号与

    1/6 4/6 1/6
    

    它的精确导数是:

    1/2 0 -1/2
    

    精确二阶导数是:

    1 -2 1
    

    这些系数来自三次 B 样条曲线及其导数。在 2D 中,您可以任意组合它。一个过滤器用于 x 方向,一个过滤器用于 y 方向。例子:

    "B-Spline reconstruction" (divisor=36)
       1  4  1
       4 16  4
       1  4  1
    
    "B-Spline differentiator in X" (divisor=12)
       1  0 -1
       4  0 -4
       1  0 -1
    
    "B-Spline, 2nd derivative in X, 1st derivative in Y" (divisor=2)
       1 -2  1
       0  0  0
      -1  2 -1
    

    这样做的好处是,即使过滤后的结果与原始信号不完全对应,而只是稍微平滑的版本,但它们仍然彼此一致。您可以使用here 中描述的简单预处理技巧来绕过平滑效果。但是,根据您实际想要做的事情,这种预处理可能是不合适的。

    我用它来计算任意(亚像素)点处的二次泰勒近似值,以找到鞍点和局部极值之类的东西。

    如果您关心边界,则需要以某种方式选择适合您需求的推断。我通常只是重复最后一个像素的像素值。它适用于我的应用程序*。

    【讨论】:

      【解决方案3】:

      如何处理边界完全取决于应用程序。镜像、外推、加零,这取决于你的需要。

      现在你把亮度值作为边框,我不会那样做,它没有任何意义。就像您的图像在所有边框和镜像上都有更多像素一样,为您的导数计算推断或归零它们的值。

      为什么不只采用 Sobel 内核?它速度很快,而且不需要更多的编码工作?

      编辑:您不检查 x 方向的边界。 grad_x(0, 0) 将导致运行时异常。

      【讨论】:

      • 我自己的。我正在尝试实现 R-HOG 描述符的简化版本,在我看来边界像素并不那么重要。现在我添加了左右导数的每个边界像素计算,具体取决于它呈现的边界。我从未使用过计算机视觉技术,所以我不确定它不会影响描述符的准确性。
      • 关于 sobel 内核 - 它有相同的边界问题,在我解决了这个边界像素问题后,我将使用 Sobel 内核更改我的函数。
      • 好吧,想象一个均匀的 2D 图像,其亮度值为 100。除了边界之外,您的图像到处都有导数 0。那里会有100个。根据应用程序,可以,也可以不可以。
      • 您有什么样的应用程序以及什么样的图像(分辨率、噪音等)?这可能说明了最佳方法。
      • 使用左右导数会在任何地方产生 0 梯度,我认为它是正确的。我的应用程序只是一个简单的人员检测系统,仅供练习。我得到了一组图像(256x256 jpeg),其中包含黑色背景上的人和一组矩形,将人限制在这些图片上(以简化培训)。我正在为每个包含人类的矩形计算 HOG 描述符。
      【解决方案4】:

      实用程序:

      OpenCV 与您使用的卷积核 ([1 0 -1])。

      边框像素:

      取决于应用程序。这里有一些pad your image的好方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-21
        • 2016-04-23
        • 2020-09-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-14
        • 1970-01-01
        相关资源
        最近更新 更多