梯度方向计算

5
我正在计算机视觉课程中的任务上工作。其中一个子任务是基于图像亮度计算梯度方向。我已经创建了一个包含每个像素的亮度值的矩阵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算子,因为对于我的需求来说简单的导数已经足够了。

问题是,我是否正确地计算了这个梯度,并且我应该如何处理边缘像素(现在函数返回像素本身的值,我不确定它是否精确)?另外,是否有任何计算图像梯度的实用程序? 我想确保我的程序表现良好。


有限差分法 = FED - Adam
https://en.wikipedia.org/wiki/Kernel_(image_processing)#Edge_Handling - Adam
4个回答

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

1

所以,一方面你想保持简单,另一方面你希望程序的性能良好。唉。

实际上,我正在做类似的事情,尽管我不太在意边界。我喜欢以三次B样条为基础来计算系数。如果将离散的二维信号与二维三次B样条进行卷积,你将得到一个非常平滑且具有两次连续可微性的函数。可以在任意点计算出该函数的精确强度和导数。由于三次B样条不是插值器,结果相对原始信号会稍微平滑。但对于许多应用来说,这并不是问题。事实上,在许多情况下,它往往改善了事物(在一定程度上抑制噪音)。如果你不想要这种平滑效果,可以绕过它(见下面的参考资料)。

在一维中,使用三次B样条作为重构滤波器再次对信号进行采样等效于将信号与进行卷积。

1/6 4/6 1/6

这个的精确导数是:

1/2 0 -1/2

而且确切的二阶导数是:

1 -2 1

这些系数源于立方B样条曲线及其导数。在二维中,您可以任意组合它们。一个滤波器用于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

这个方法很好,虽然过滤后的结果不完全对应原始信号,但只是稍微平滑一下,它们仍然相互一致。您可以使用一个简单的预处理技巧来绕过平滑效果,该技巧在此处中有描述。但根据您实际想要做什么,这种预处理可能不合适。

我使用它来计算二次泰勒近似值,以在任意(亚像素)点上找到诸如鞍点和局部极值等内容。

如果您关心边界,您需要选择某种外推方式来满足您的需求。我通常只重复最后几个像素的像素值。对于我的应用程序来说,这样做效果很好*。


0

如何处理边界完全取决于应用程序。镜像、外推、添加零,根据您的需求进行选择。

现在您将亮度值作为边界,我不会这样做,因为它没有意义。就像您的图像在所有边界上都有更多的像素一样,为您的导数计算镜像、外推或将其值设为零。

为什么不只使用Sobel核?它快速且不需要更多的编码工作?

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


我正在尝试实现简化版本的R-HOG描述符,我认为边界像素并不那么重要。现在,对于每个边界像素,我都添加了左右导数的计算,具体取决于它是哪个边界。我从未使用过计算机视觉技术,所以不确定这是否会影响描述符的准确性。 - Anton
关于Sobel核 - 它具有相同的边框问题,等我解决了这个边缘像素问题后,我将使用Sobel核来更改我的函数。 - Anton
想象一下一个均匀的二维图像,充满了亮度值为100。你的图像在边界以外处处都有导数为0,而在边界上则是100。根据应用程序的不同,这可能是可以接受的,也可能不是。 - c_k
你的应用程序是什么类型的,需要处理什么样的图像(分辨率、噪点等)?这可能会对最佳处理方法有所启示。 - c_k
使用左右导数将在任何地方都给出0梯度,我认为这是正确的。我的应用程序只是一个简单的人员检测系统,仅供练习使用。我被赋予了一组图像(256x256 jpeg),其中包含黑色背景上的人物和一组矩形,用于界定图片中的人物(以简化训练)。我正在为每个包含人类的矩形计算HOG描述符。 - Anton
显示剩余2条评论

0

实用工具:

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

边缘像素:

取决于应用程序。以下是一些很好的方法填充您的图像


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接