任意 Sobel 核大小和角度的完整解决方案
tl;dr:跳到“示例”部分
添加另一个解决方案,扩展 this document(它的质量不是特别高,但从第 2 页底部开始显示一些可用的图形和矩阵)。
目标
我们要做的是估计图像在位置 (x,y) 处的局部梯度。梯度是由x和y方向的分量gx和gy组成的向量。
现在,假设我们想根据我们的像素 (x,y) 及其邻居作为核操作(3x3、5x5 或任何大小)来近似梯度。
解决思路
我们可以通过对所有相邻中心对在梯度方向上的投影求和来近似梯度。 (Sobel 的核只是一种特殊的加权不同贡献的方法,Prewitt 基本上也是如此)。
3x3 的显式中间步骤
这是本地图像,中心像素 (x,y) 标记为 'o' (center)
a b c
d o f
g h i
假设我们想要正 x 方向的梯度。 x 正方向的单位向量是 (1,0) [我稍后会使用正 y 方向向下的约定,即 (0,1),并且 (0,0) 是图像的左上角) .]
从o到f(简称'of')的向量是(1,0)。 “of”方向的梯度为 (f - o) / 1(此处像素处的图像值表示 f 减去中心 o 处的值,除以这些像素之间的距离)。如果我们通过点积将特定邻居梯度的单位向量投影到我们想要的梯度方向 (1,0) 上,我们得到 1。这是一个包含所有邻居贡献的小表格,从更简单的情况开始。注意对于对角线,它们的距离是sqrt2,对角线方向的单位向量是1/sqrt2 * (+/-1, +/-1)
f: (f-o)/1 * 1
d: (d-o)/1 * -1 because (-1, 0) dot (1, 0) = -1
b: (b-o)/1 * 0 because (0, -1) dot (1, 0) = 0
h: (h-o)/1 * 0 (as per b)
a: (a-o)/sqrt2 * -1/sqrt2 distance is sqrt2, and 1/sqrt2*(-1,-1) dot (1,0) = -1/sqrt2
c: (c-o)/sqrt2 * +1/sqrt2 ...
g: (g-o)/sqrt2 * -1/sqrt2 ...
i: (i-o)/sqrt2 * +1/sqrt2 ...
编辑澄清:
1/sqrt(2) 有两个因数,原因如下:
我们感兴趣的是在特定方向(这里是x)对梯度的贡献,所以我们需要将从中心像素到相邻像素的方向梯度投影到该方向上我们感兴趣。这是通过在各个方向上取单位向量的标量积来实现的,它引入了第一个因子 1/L(这里是对角线的 1/sqrt(2))。
梯度测量一点的无穷小变化,我们用有限差分来近似。就线性方程而言,m = (y2-y1)/(x2-x1)。出于这个原因,从中心像素到相邻像素 (y2-y1) 的值差必须分布在它们的距离上(对应于 x2-x1),以便获得每个距离单位的上升单位。这产生了 1/L 的第二个因子(这里对角线为 1/sqrt(2))
好的,现在我们知道贡献了。让我们通过组合相反的像素贡献对来简化这个表达式。我将从 d 和 f 开始:
{(f-o)/1 * 1} + {(d-o)/1 * -1}
= f - o - (d - o)
= f - d
现在是第一个对角线:
{(c-o)/sqrt2 * 1/sqrt2} + {(g-o)/sqrt2 * -1/sqrt2}
= (c - o)/2 - (g - o)/2
= (c - g)/2
第二条对角线的贡献是 (i - a)/2。垂直方向贡献为零。请注意,中心像素“o”的所有贡献都消失了。
我们现在已经计算了像素 (x,y) 处所有最近邻对正 x 方向梯度的贡献,因此我们对 x 方向梯度的总近似就是它们的总和:
gx(x,y) = f - d + (c - g)/2 + (i - a)/2
我们可以通过使用卷积核来获得相同的结果,其中系数被写入相应的相邻像素的位置:
-1/2 0 1/2
-1 0 1
-1/2 0 1/2
如果您不想处理分数,请将其乘以 2 并获得著名的 Sobel 3x3 内核。
-1 0 1
G_x = -2 0 2
-1 0 1
乘以二只是为了得到方便的整数。输出图像的缩放基本上是任意的,大多数时候您将其标准化为您的图像范围,无论如何(以获得清晰可见的结果)。
通过与上述相同的推理,您可以通过将邻居贡献投影到正 y 方向 (0,1) 上的单位向量上来获得垂直梯度 gy 的内核
-1 -2 -1
G_y = 0 0 0
1 2 1
任意大小内核的公式
如果你想要 5x5 或更大的内核,你只需要注意距离,例如
A B 2 B A
B C 1 C B
2 1 - 1 2
B C 1 C B
A B 2 B A
在哪里
A = 2 * sqrt2
B = sqrt5
C = sqrt2.
如果连接任意两个像素的向量的长度为 L,则该方向上的单位向量的前置因子为 1/L。出于这个原因,任何像素'k'对(比如说)x梯度(1,0)的贡献可以简化为“(平方距离上的值差)乘以(非归一化方向向量'ok'与梯度向量的DotProduct , 例如 (1,0) )"
gx_k = (k - o)/(pixel distance^2) ['ok' dot (1,0)].
因为连接向量与x单位向量的点积选择了对应的向量入口,所以位置k对应的G_x核入口正好
i / (i*i + j*j)
其中 i 和 j 是从中心像素到像素 k 在 x 和 y 方向上的步数。在上述 3x3 计算中,像素 'a' 将具有 i = -1(左侧 1),j = -1(顶部 1),因此 'a' 内核条目为 -1 / (1 + 1 ) = -1/2。
G_y 内核的条目是
j/(i*i + j*j).
如果我想要内核的整数值,请按照以下步骤操作:
- 检查输出图像的可用范围
- 通过应用浮点内核计算可能的最高结果(即假设所有正内核条目下的最大输入值,因此输出值为(所有正内核值的总和)*(最大可能的输入图像值)。如果您有签名输入,你也需要考虑负值。最坏的情况是所有正值的总和+负条目的所有abs值的总和(如果在正数下的最大输入,在负数下的最大输入)。编辑:所有的总和abs 值也被恰当地称为内核的权重
- 计算内核允许的最大放大倍数(不溢出输出图像范围)
- 对于浮点内核的所有整数倍数(从 2 到最大值以上):检查哪个具有最小的绝对舍入误差总和并使用此内核
总之:
Gx_ij = i / (i*i + j*j)
Gy_ij = j / (i*i + j*j)
其中 i,j 是从中心算起的内核中的位置。根据需要缩放内核条目以获得整数(或至少接近近似值)。
这些公式适用于所有内核大小。
示例
-2/8 -1/5 0 1/5 2/8 -5 -4 0 4 5
-2/5 -1/2 0 1/2 2/5 -8 -10 0 10 8
G_x (5x5) -2/4 -1/1 0 1/1 2/4 (*20) = -10 -20 0 20 10
-2/5 -1/2 0 1/2 2/5 -8 -10 0 10 8
-2/8 -1/5 0 1/5 2/8 -5 -4 0 4 5
请注意,浮点表示法中 5x5 内核的中心 3x3 像素只是 3x3 内核,即较大的内核表示具有附加但权重较低的数据的持续近似值。这继续到更大的内核大小:
-3/18 -2/13 -1/10 0 1/10 2/13 3/18
-3/13 -2/8 -1/5 0 1/5 2/8 3/13
-3/10 -2/5 -1/2 0 1/2 2/5 3/10
G_x (7x7) -3/9 -2/4 -1/1 0 1/1 2/4 3/9
-3/10 -2/5 -1/2 0 1/2 2/5 3/10
-3/13 -2/8 -1/5 0 1/5 2/8 3/13
-3/18 -2/13 -1/10 0 1/10 2/13 3/18
此时,精确的整数表示变得不切实际。
据我所知(无法访问原始论文),“Sobel”部分对贡献进行了适当的加权。 Prewitt解可以省略距离权重,只在内核中适当输入i和j。
奖励:任意方向的 Sobel 内核
所以我们可以近似图像梯度的 x 和 y 分量(它实际上是一个向量,如开头所述)。通过将梯度向量投影到 alpha-梯度单位向量上,可以获得任意方向 alpha 上的梯度(在数学上测量为正,在这种情况下为顺时针方向,因为正 y 向下)。
alpha 单位向量是 (cos alpha, sin alpha)。对于 alpha = 0°,您可以获得 gx 的结果,对于 alpha = 90°,您可以获得 gy。
g_alpha = (alpha-unit vector) dot (gx, gy)
= (cos a, sin a) dot (gx, gy)
= cos a * gx + sin a * gy
如果您费心将 gx 和 gy 写为邻居贡献的总和,您会意识到您可以通过适用于同一邻居像素的术语对生成的长表达式进行分组,然后将其重写为带有条目的单个卷积核
G_alpha_ij = (i * cos a + j * sin a)/(i*i + j*j)
如果您想要最接近的整数近似值,请按照上述步骤操作。