理解特征
有很多小图像,需要正确组装它们以形成大的真实图像。问题是,要怎么做?将相同的理论投影到计算机程序上,以便计算机可以玩拼图游戏呢?如果计算机可以将多个自然图像缝合在一起,那么如何给建筑物或任何结构提供大量图片并告诉计算机从中创建3D模型呢?
如何将许多被扰的图像片段排列成一个大的单张图像?
寻找独特的,易于跟踪和比较的特定模板或特定特征,如果对这种特征进行定义,可能会发现很难用语言来表达它,但是知道它们是什么;如果要指出一项可以在多张图像中进行比较的良好特征,则可以指出其中一项;在图像中搜索这些特征,找到它们,在其他图像中寻找相同的特征并将它们对齐。
因此,一个基本问题扩展到更多,但变得更加具体,这些特征是什么(答案对于计算机也应该是可以理解的)?
如果深入研究某些图片并搜索不同的模板,会发现一些有趣的东西;例如,看以下的图片:
A和B是散布在很多区域上平坦的表面,很难找到这些补丁的确切位置。C和D是建筑物的边缘,可以找到一个大概的位置,但是准确的位置仍然很困难,因为沿着边缘的每个地方的图案都是相同的(建筑边缘外部情况可能有所不同);因此与平坦区域相比,边缘是更好的特征,但不够好(在拼图游戏中比较边缘的连续性很好)。E和F是建筑物的某些角落,很容易找到它们;因为在拐角处,无论将此修补区域移动到何处,它的外观都将有所不同;因此它们可以被视为很好的特征。
蓝色补丁是平坦区域,很难找到和跟踪,无论将其移到何处,看起来都一样。黑色补丁有一个边缘,如果你沿垂直方向(即沿渐变)移动它,则它会发生变化;沿着边缘(平行于边缘)移动,看起来相同。对于红色补丁,这是一个角落,无论将补丁移动到何处,它看起来都不同,这意味着它是唯一的。
因此基本上,拐点被认为是图像中的良好特征(不仅是角落,在某些情况下,斑点也被认为是不错的特征)。
如何找到不错的特征?寻找图像中在其周围所有区域中移动(少量)变化最大的区域。计算机语言中找到这些图像特征称为特征检测。
在图像中找到了特征,找到它之后,应该能够在其他图像中找到相同的图像,怎么做?围绕该特征采取一个区域,解释它,例如“上部是蓝天,下部是建筑物的区域,在建筑物上有玻璃等”,而在另一个建筑物中搜索相同的区域图片。
同样,计算机应该描述特征周围的区域,以便可以在其他图像中找到它,所谓的描述称为特征描述;获得特征及其描述后,可以在所有图像中找到相同的对象并将它们对齐,缝合在一起或进行所需的操作。
因此在此章节中,寻找OpenCV中的不同算法来查找特征,对其进行描述、匹配等。
哈里斯角检测
理论
角是图像中各个方向上强度变化很大的区域,哈里斯拐角检测器,基本上找到了
(
u
,
v
)
(u, v)
(u,v)在所有方向上位移的强度差异,表示如下:
E
(
u
,
v
)
=
∑
x
,
y
w
(
x
,
y
)
⏟
window function
[
I
(
x
+
u
,
y
+
v
)
⏟
shifted intensity
−
I
(
x
,
y
)
⏟
intensity
]
2
E(u,v) = \sum_{x,y} \underbrace{w(x,y)}_\text{window function} \, [\underbrace{I(x+u,y+v)}_\text{shifted intensity}-\underbrace{I(x,y)}_\text{intensity}]^2
E(u,v)=x,y∑window function
w(x,y)[shifted intensity
I(x+u,y+v)−intensity
I(x,y)]2
窗口函数要么是一个矩形窗口,要么是高斯窗口,它在下面赋予了值。
必须最大化这个函数
E
(
u
,
v
)
E(u, v)
E(u,v)用于角检测,这意味着,必须最大化第二个项,将泰勒扩展应用于上述方程,并使用一些数学步骤,得到最后的等式:
E
(
u
,
v
)
≈
[
u
v
]
M
[
u
v
]
E(u,v) \approx \begin{bmatrix} u & v \end{bmatrix} M \begin{bmatrix} u \\ v \end{bmatrix}
E(u,v)≈[uv]M[uv]
其中
M
=
∑
x
,
y
w
(
x
,
y
)
[
I
x
I
x
I
x
I
y
I
x
I
y
I
y
I
y
]
M = \sum_{x,y} w(x,y) \begin{bmatrix}I_x I_x & I_x I_y \\ I_x I_y & I_y I_y \end{bmatrix}
M=x,y∑w(x,y)[IxIxIxIyIxIyIyIy]
在此,
I
x
和
I
y
I_x和I_y
Ix和Iy分别是在x和y方向上的图像导数(可使用函数cv.Sobel()
找到)。
之后,创建了一个分数,它将确定一个窗口是否可以包含一个角:
R
=
d
e
t
(
M
)
−
k
(
t
r
a
c
e
(
M
)
)
2
R = det(M) - k(trace(M))^2
R=det(M)−k(trace(M))2
其中
- d e t ( M ) = λ 1 λ 2 det(M)=\lambda_1\lambda_2 det(M)=λ1λ2
- t r a c e ( M ) = λ 1 + λ 2 trace(M)=\lambda_1 + \lambda_2 trace(M)=λ1+λ2
-
λ
1
和
λ
2
是
M
的特征值
\lambda_1 和\lambda_2是M的特征值
λ1和λ2是M的特征值
这些特征值的值决定了区域是拐角、边缘还是平坦。 - 当 ∣ R ∣ 较小,这在 λ 1 和 λ 2 较小时发生,该区域平坦。 当|R|较小,这在\lambda_1 和\lambda_2较小时发生,该区域平坦。 当∣R∣较小,这在λ1和λ2较小时发生,该区域平坦。
- 当 R < 0 时,当 λ > > λ 2 时发生,反之亦然,该区域为边。 当R<0时,当\lambda >> \lambda2时发生,反之亦然,该区域为边。 当R<0时,当λ>>λ2时发生,反之亦然,该区域为边。
-
当
R
很大时,这发生在
λ
1
和
λ
2
大、
λ
1
λ
2
时,该区域是角。
当R很大时,这发生在\lambda_1 和\lambda_2大、\lambda_1 ~\lambda_2时,该区域是角。
当R很大时,这发生在λ1和λ2大、λ1 λ2时,该区域是角。
可以用下图来表示:
Harris Corner Detection的结果是具有这些分数的灰度图像,合适的阈值可提供图像的各个角落。
OpenCV中的哈里斯角检测
为此,有函数cv.cornerHarris()
,其参数为: img 输入图像,应为灰度和float32类型;blockSize 是拐角检测考虑的邻域大小 ;ksize 使用的Sobel导数的光圈参数;k 等式中的哈里斯检测器自由参数。示例中用青色标记出找到的哈里斯角
SubPixel精度的转角
有时可能需要找到最精确的角落,OpenCV有函数cv.cornerSubPix()
,它进一步细化了以亚像素精度检测到的角落。下面是一个例子,需要先找到哈里斯角;然后通过这些角的质心(可能在一个角上有一堆像素,取它们的质心)来细化它们;哈里斯角用红色像素标记,精制角用绿色像素标记;在特定的迭代次数或达到一定的精度后停止它;定义它将搜索角落的邻居的大小。
harriscorner_detect.py
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('./OpenCV/blox.png')
img1 = img.copy()
img2 = img.copy()
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv.cornerHarris(gray, 2, 3, 0.04)
# dst用于标记角点,并不重要
dst = cv.dilate(dst, None)
# 最佳值的阈值,可能因图像而异
img1[dst > 0.01 * dst.max()] = [255, 255, 0]
ret, dst = cv.threshold(dst, 0.01 * dst.max(), 255, 0)
dst = np.uint8(dst)
# 寻找质心
ret, labels, stats, centroids = cv.connectedComponentsWithStats(dst)
# 定义停止和完善拐角的条件
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv.cornerSubPix(gray, np.float32(centroids), (5, 5), (-1, -1), criteria)
# 绘制
res = np.hstack((centroids, corners))
res = np.int0(res)
img2[res[:, 1], res[:, 0]] = [0, 0, 255]
img2[res[:, 3], res[:, 2]] = [0, 255, 0]
images = [img, img1, img2]
titles = ['Original', 'CornerHarris', 'SubPixel']
for i in range(3):
plt.subplot(1, 3, i + 1)
plt.imshow(cv.cvtColor(images[i], cv.COLOR_BGR2RGB))
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
Shi-tomas拐角检测器和益于跟踪的特征
理论
Shi和C. Tomasi在他们的论文《有益于跟踪的特征》中做了一个小修改,与Harris Harris Detector相比,显示了更好的结果。
哈里斯拐角检测器的计分功能由下式给出:
R
=
λ
1
λ
2
−
k
(
λ
1
+
λ
2
)
2
R = \lambda_1 \lambda_2 - k(\lambda_1+\lambda_2)^2
R=λ1λ2−k(λ1+λ2)2
取而代之的是,Shi和C. Tomasi提出:
R
=
m
i
n
(
λ
1
,
λ
2
)
R = min(\lambda_1, \lambda_2)
R=min(λ1,λ2)
如果大于阈值,则将其视为拐角,如下图:
从图中可以看到,只有当
λ
1
和
λ
2
都大于最小值
λ
m
i
n
\lambda_1和\lambda_2都大于最小值\lambda_{min}
λ1和λ2都大于最小值λmin时,才将其视为拐角(绿色区域)。
OpenCV实现
OpenCV有一个函数cv.goodFeaturesToTrack()
,它通过Shi-Tomasi方法(或指定哈里斯角检测)找到图像中的N个最强角。
图像应是灰度图像,然后指定要查找的角数;然后,指定质量级别,该值是介于0-1之间的值,该值表示每个角落都被拒绝的最低拐角质量;然后,提供检测到的角之间的最小欧式距离; 利用所有这些信息,该功能可以找到图像中的拐角,低于平均质量的所有拐角点均被拒绝;然后,它会根据质量以降序对剩余的角进行排序,然后函数首先获取最佳拐角,丢弃最小距离范围内的所有附近拐角,然后返回N个最佳拐角。
在下面的示例中,我们将尝试找到25个最佳弯角:
good_features.py
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('./OpenCV/blox.png')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
corners = cv.goodFeaturesToTrack(gray, 25, 0.01, 10)
corners = np.int0(corners)
for i in corners:
x, y = i.ravel()
cv.circle(img, (x, y), 3, 255, -1)
plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.title('GoodFeature')
plt.xticks([]), plt.yticks([])
plt.show()
学习来源:OpenCV-Python中文文档