角检测算法为倾斜边缘提供非常高的值?
Corner detection algorithm gives very high value for slanted edges?
我已经尝试实现 shi-tomasi 角点检测算法的基本版本。该算法适用于角落,但我遇到了一个奇怪的问题,该算法还为倾斜(标题)边缘提供了高值。
这是我所做的
- 拍摄灰度图
- 计算机 dx,图像的 dy 与 sobel_x 和 sobel_y
卷积
- 采用 3 尺寸 window 并将其移动到图像上以计算 window 中元素的总和。
- 计算dy图像中window个元素的总和和dx图像中window个元素的总和,并将其保存在sum_xx和sum_yy中。
- 创建了一个新图像(称之为
result
),其中根据 shi-tomasi 算法的要求,计算 window 总和的像素被替换为 min(sum_xx, sum_yy)
。
我预计它会为 dx 和 dy 都很高的角提供最大值,但我发现它甚至为带标题的边提供高值。
这是我收到的图像的一些输出:
结果:
到目前为止一切顺利,角点值很高。
另一张图片:
结果:
问题就出在这里。边缘具有算法不期望的高值。我无法理解边缘如何对 x 和 y 梯度都具有高值(sobel 是梯度的近似值)。
我想请求你的帮助,如果你能帮我解决边缘的这个问题。我愿意接受任何建议和想法。
这是我的代码(如果有帮助的话):
def st(image, w_size):
v = []
dy, dx = sy(image), sx(image)
dy = dy**2
dx = dx**2
dxdy = dx*dy
dx = cv2.GaussianBlur(dx, (3,3), cv2.BORDER_DEFAULT)
dy = cv2.GaussianBlur(dy, (3,3), cv2.BORDER_DEFAULT)
dxdy = cv2.GaussianBlur(dxdy, (3,3), cv2.BORDER_DEFAULT)
ofset = int(w_size/2)
for y in range(ofset, image.shape[0]-ofset):
for x in range(ofset, image.shape[1]-ofset):
s_y = y - ofset
e_y = y + ofset + 1
s_x = x - ofset
e_x = x + ofset + 1
w_Ixx = dx[s_y: e_y, s_x: e_x]
w_Iyy = dy[s_y: e_y, s_x: e_x]
w_Ixy = dxdy[s_y: e_y, s_x: e_x]
sum_xx = w_Ixx.sum()
sum_yy = w_Iyy.sum()
sum_xy = w_Ixy.sum()
#sum_r = w_r.sum()
m = np.matrix([[sum_xx, sum_xy],
[sum_xy, sum_yy]])
eg = np.linalg.eigvals(m)
v.append((min(eg[0], eg[1]), y, x))
return v
def sy(img):
t = cv2.Sobel(img,cv2.CV_8U,0,1,ksize=3)
return t
def sx(img):
t = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=3)
return t
如果你在寻找角点,为什么不看看 Harris 角点检测?
该检测器不仅查看一阶导数,还查看二阶导数以及它们在 loca 邻域中的排列方式。
ans = cv2.cornerHarris(np.float32(gray)/255., 2, 3, 0.03)
查看像素 ans > 0.001
:
如您所见,检测到大部分角,但未检测到倾斜边缘。
您可能需要稍微调整一下 Harris 检测器的参数以获得更好的结果。
我强烈建议您阅读此检测器背后的解释和基本原理,以及它如何能够可靠地区分拐角和倾斜边缘。确保您了解这与您发布的方法有何不同。
你误解了Shi-Tomasi method. You are computing the two derivatives dx
and dy
, locally averaging them (the sum is different from the local average by a constant factor which we can ignore), and then taking the lowest value. The Shi-Tomasi equation refers to the Structure Tensor,它使用了这个矩阵的两个特征值中的最小值。
Structure张量是梯度与其自身外积,然后平滑后形成的矩阵:
[ smooth(dx*dx) smooth(dx*dy) ]
[ smooth(dx*dy) smooth(dy*dy) ]
也就是我们取x导数dx
和y导数dy
,形成三个图像dx*dx
,dy*dy
和dx*dy
,并对这三幅图像进行平滑处理。现在对于每个像素,我们有三个值一起形成一个对称矩阵。这叫做结构张量。
这个结构张量的特征值说明了局部边缘。如果两者都很小,则邻域中没有边。如果一个很大,那么在局部邻域中只有一个边缘方向。如果两者都很大,则说明发生了更复杂的事情,可能是一个角落。平滑度越大 window,我们检查的局部邻域就越大。 select 与我们正在查看的结构大小相匹配的邻域大小很重要。
结构张量的特征向量说明了局部结构的方向。如果有一条边(一个特征值很大),那么对应的特征向量就是这条边的法线。
Shi-Tomasi 使用两个特征值中的最小值。如果两个特征值中最小的一个很大,那么在局部邻域中发生的事情比边缘更复杂。
Harris corner detector同样使用了Structure tensor,但是它结合了行列式和trace,以更少的计算成本获得了相似的结果。 Shi-Tomasi 更好但计算成本更高,因为特征值计算需要计算平方根。 Harris 检测器是 Shi-Tomasi 检测器的近似值。
这是对 Shi-Tomasi(上)和 Harris(下)的比较。我将两者都削减了最大值的一半,因为最大值出现在文本区域,这让我们可以更好地看到对相关角的较弱响应。正如您所看到的,Shi-Tomasi 对图像中的所有角落都有更均匀的响应。
对于这两个,我使用了 sigma=2 的高斯 window 进行局部平均(使用 3 sigma 的截止值,导致 13x13 平均 window) .
查看您更新后的代码,我发现了几个问题。我在这里用评论注释了这些:
def st(image, w_size):
v = []
dy, dx = sy(image), sx(image)
dy = dy**2
dx = dx**2
dxdy = dx*dy
# Here you have dxdy=dx**2 * dy**2, because dx and dy were changed
# in the lines above.
dx = cv2.GaussianBlur(dx, (3,3), cv2.BORDER_DEFAULT)
dy = cv2.GaussianBlur(dy, (3,3), cv2.BORDER_DEFAULT)
dxdy = cv2.GaussianBlur(dxdy, (3,3), cv2.BORDER_DEFAULT)
# Gaussian blur size should be indicated with the sigma of the Gaussian,
# not with the size of the kernel. A 3x3 kernel corresponds, in OpenCV,
# to a Gaussian with sigma = 0.8, which is way too small. Use sigma=2.
ofset = int(w_size/2)
for y in range(ofset, image.shape[0]-ofset):
for x in range(ofset, image.shape[1]-ofset):
s_y = y - ofset
e_y = y + ofset + 1
s_x = x - ofset
e_x = x + ofset + 1
w_Ixx = dx[s_y: e_y, s_x: e_x]
w_Iyy = dy[s_y: e_y, s_x: e_x]
w_Ixy = dxdy[s_y: e_y, s_x: e_x]
sum_xx = w_Ixx.sum()
sum_yy = w_Iyy.sum()
sum_xy = w_Ixy.sum()
# We've already done the local averaging using GaussianBlur,
# this summing is now no longer necessary.
m = np.matrix([[sum_xx, sum_xy],
[sum_xy, sum_yy]])
eg = np.linalg.eigvals(m)
v.append((min(eg[0], eg[1]), y, x))
return v
def sy(img):
t = cv2.Sobel(img,cv2.CV_8U,0,1,ksize=3)
# The output of Sobel has positive and negative values. By writing it
# into a 8-bit unsigned integer array, you lose all these negative
# values, they become 0. This is half your edges that you lose!
return t
def sx(img):
t = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=3)
return t
我是这样修改你的代码的:
import cv2
import numpy as np
def st(image):
dy, dx = sy(image), sx(image)
dxdx = cv2.GaussianBlur(dx**2, ksize = None, sigmaX=2)
dydy = cv2.GaussianBlur(dy**2, ksize = None, sigmaX=2)
dxdy = cv2.GaussianBlur(dx*dy, ksize = None, sigmaX=2)
for y in range(image.shape[0]):
for x in range(image.shape[1]):
m = np.matrix([[dxdx[y,x], dxdy[y,x]],
[dxdy[y,x], dydy[y,x]]])
eg = np.linalg.eigvals(m)
image[y,x] = min(eg[0], eg[1]) # Write into the input image.
# Better would be to create a new
# array as output. Make sure it is
# a floating-point type!
def sy(img):
t = cv2.Sobel(img,cv2.CV_32F,0,1,ksize=3)
return t
def sx(img):
t = cv2.Sobel(img,cv2.CV_32F,1,0,ksize=3)
return t
image = cv2.imread('fu4r5.png', 0)
output = image.astype(np.float32) # I'm writing the result of the detector in here
st(output)
pp.imshow(output); pp.show()
我已经尝试实现 shi-tomasi 角点检测算法的基本版本。该算法适用于角落,但我遇到了一个奇怪的问题,该算法还为倾斜(标题)边缘提供了高值。
这是我所做的
- 拍摄灰度图
- 计算机 dx,图像的 dy 与 sobel_x 和 sobel_y 卷积
- 采用 3 尺寸 window 并将其移动到图像上以计算 window 中元素的总和。
- 计算dy图像中window个元素的总和和dx图像中window个元素的总和,并将其保存在sum_xx和sum_yy中。
- 创建了一个新图像(称之为
result
),其中根据 shi-tomasi 算法的要求,计算 window 总和的像素被替换为min(sum_xx, sum_yy)
。
我预计它会为 dx 和 dy 都很高的角提供最大值,但我发现它甚至为带标题的边提供高值。
这是我收到的图像的一些输出:
结果:
到目前为止一切顺利,角点值很高。
另一张图片:
结果:
问题就出在这里。边缘具有算法不期望的高值。我无法理解边缘如何对 x 和 y 梯度都具有高值(sobel 是梯度的近似值)。
我想请求你的帮助,如果你能帮我解决边缘的这个问题。我愿意接受任何建议和想法。
这是我的代码(如果有帮助的话):
def st(image, w_size):
v = []
dy, dx = sy(image), sx(image)
dy = dy**2
dx = dx**2
dxdy = dx*dy
dx = cv2.GaussianBlur(dx, (3,3), cv2.BORDER_DEFAULT)
dy = cv2.GaussianBlur(dy, (3,3), cv2.BORDER_DEFAULT)
dxdy = cv2.GaussianBlur(dxdy, (3,3), cv2.BORDER_DEFAULT)
ofset = int(w_size/2)
for y in range(ofset, image.shape[0]-ofset):
for x in range(ofset, image.shape[1]-ofset):
s_y = y - ofset
e_y = y + ofset + 1
s_x = x - ofset
e_x = x + ofset + 1
w_Ixx = dx[s_y: e_y, s_x: e_x]
w_Iyy = dy[s_y: e_y, s_x: e_x]
w_Ixy = dxdy[s_y: e_y, s_x: e_x]
sum_xx = w_Ixx.sum()
sum_yy = w_Iyy.sum()
sum_xy = w_Ixy.sum()
#sum_r = w_r.sum()
m = np.matrix([[sum_xx, sum_xy],
[sum_xy, sum_yy]])
eg = np.linalg.eigvals(m)
v.append((min(eg[0], eg[1]), y, x))
return v
def sy(img):
t = cv2.Sobel(img,cv2.CV_8U,0,1,ksize=3)
return t
def sx(img):
t = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=3)
return t
如果你在寻找角点,为什么不看看 Harris 角点检测?
该检测器不仅查看一阶导数,还查看二阶导数以及它们在 loca 邻域中的排列方式。
ans = cv2.cornerHarris(np.float32(gray)/255., 2, 3, 0.03)
查看像素 ans > 0.001
:
如您所见,检测到大部分角,但未检测到倾斜边缘。
您可能需要稍微调整一下 Harris 检测器的参数以获得更好的结果。
我强烈建议您阅读此检测器背后的解释和基本原理,以及它如何能够可靠地区分拐角和倾斜边缘。确保您了解这与您发布的方法有何不同。
你误解了Shi-Tomasi method. You are computing the two derivatives dx
and dy
, locally averaging them (the sum is different from the local average by a constant factor which we can ignore), and then taking the lowest value. The Shi-Tomasi equation refers to the Structure Tensor,它使用了这个矩阵的两个特征值中的最小值。
Structure张量是梯度与其自身外积,然后平滑后形成的矩阵:
[ smooth(dx*dx) smooth(dx*dy) ]
[ smooth(dx*dy) smooth(dy*dy) ]
也就是我们取x导数dx
和y导数dy
,形成三个图像dx*dx
,dy*dy
和dx*dy
,并对这三幅图像进行平滑处理。现在对于每个像素,我们有三个值一起形成一个对称矩阵。这叫做结构张量。
这个结构张量的特征值说明了局部边缘。如果两者都很小,则邻域中没有边。如果一个很大,那么在局部邻域中只有一个边缘方向。如果两者都很大,则说明发生了更复杂的事情,可能是一个角落。平滑度越大 window,我们检查的局部邻域就越大。 select 与我们正在查看的结构大小相匹配的邻域大小很重要。
结构张量的特征向量说明了局部结构的方向。如果有一条边(一个特征值很大),那么对应的特征向量就是这条边的法线。
Shi-Tomasi 使用两个特征值中的最小值。如果两个特征值中最小的一个很大,那么在局部邻域中发生的事情比边缘更复杂。
Harris corner detector同样使用了Structure tensor,但是它结合了行列式和trace,以更少的计算成本获得了相似的结果。 Shi-Tomasi 更好但计算成本更高,因为特征值计算需要计算平方根。 Harris 检测器是 Shi-Tomasi 检测器的近似值。
这是对 Shi-Tomasi(上)和 Harris(下)的比较。我将两者都削减了最大值的一半,因为最大值出现在文本区域,这让我们可以更好地看到对相关角的较弱响应。正如您所看到的,Shi-Tomasi 对图像中的所有角落都有更均匀的响应。
对于这两个,我使用了 sigma=2 的高斯 window 进行局部平均(使用 3 sigma 的截止值,导致 13x13 平均 window) .
查看您更新后的代码,我发现了几个问题。我在这里用评论注释了这些:
def st(image, w_size):
v = []
dy, dx = sy(image), sx(image)
dy = dy**2
dx = dx**2
dxdy = dx*dy
# Here you have dxdy=dx**2 * dy**2, because dx and dy were changed
# in the lines above.
dx = cv2.GaussianBlur(dx, (3,3), cv2.BORDER_DEFAULT)
dy = cv2.GaussianBlur(dy, (3,3), cv2.BORDER_DEFAULT)
dxdy = cv2.GaussianBlur(dxdy, (3,3), cv2.BORDER_DEFAULT)
# Gaussian blur size should be indicated with the sigma of the Gaussian,
# not with the size of the kernel. A 3x3 kernel corresponds, in OpenCV,
# to a Gaussian with sigma = 0.8, which is way too small. Use sigma=2.
ofset = int(w_size/2)
for y in range(ofset, image.shape[0]-ofset):
for x in range(ofset, image.shape[1]-ofset):
s_y = y - ofset
e_y = y + ofset + 1
s_x = x - ofset
e_x = x + ofset + 1
w_Ixx = dx[s_y: e_y, s_x: e_x]
w_Iyy = dy[s_y: e_y, s_x: e_x]
w_Ixy = dxdy[s_y: e_y, s_x: e_x]
sum_xx = w_Ixx.sum()
sum_yy = w_Iyy.sum()
sum_xy = w_Ixy.sum()
# We've already done the local averaging using GaussianBlur,
# this summing is now no longer necessary.
m = np.matrix([[sum_xx, sum_xy],
[sum_xy, sum_yy]])
eg = np.linalg.eigvals(m)
v.append((min(eg[0], eg[1]), y, x))
return v
def sy(img):
t = cv2.Sobel(img,cv2.CV_8U,0,1,ksize=3)
# The output of Sobel has positive and negative values. By writing it
# into a 8-bit unsigned integer array, you lose all these negative
# values, they become 0. This is half your edges that you lose!
return t
def sx(img):
t = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=3)
return t
我是这样修改你的代码的:
import cv2
import numpy as np
def st(image):
dy, dx = sy(image), sx(image)
dxdx = cv2.GaussianBlur(dx**2, ksize = None, sigmaX=2)
dydy = cv2.GaussianBlur(dy**2, ksize = None, sigmaX=2)
dxdy = cv2.GaussianBlur(dx*dy, ksize = None, sigmaX=2)
for y in range(image.shape[0]):
for x in range(image.shape[1]):
m = np.matrix([[dxdx[y,x], dxdy[y,x]],
[dxdy[y,x], dydy[y,x]]])
eg = np.linalg.eigvals(m)
image[y,x] = min(eg[0], eg[1]) # Write into the input image.
# Better would be to create a new
# array as output. Make sure it is
# a floating-point type!
def sy(img):
t = cv2.Sobel(img,cv2.CV_32F,0,1,ksize=3)
return t
def sx(img):
t = cv2.Sobel(img,cv2.CV_32F,1,0,ksize=3)
return t
image = cv2.imread('fu4r5.png', 0)
output = image.astype(np.float32) # I'm writing the result of the detector in here
st(output)
pp.imshow(output); pp.show()