在本章中,将学习
大多数人都会玩拼图游戏。会得到很多小图像,需要正确组装它们以形成大的真实图像。问题是,你是如何去拼图的?同样地,将相同的理论投影到计算机程序上,以使得计算机也可以玩拼图游戏呢?如果计算机可以玩拼图游戏,为什么不能给计算机提供很多自然风光的真实图像,并告诉计算机将所有这些图像拼接成一个大图像呢?如果计算机可以将多个自然图像缝合在一起,那么如何给建筑物或任何结构提供大量图片并告诉计算机从中创建3D模型呢?
这全都取决于最基本的问题:人们是如何玩拼图游戏?如何将许多杂乱的图像片段排列成一个大的单张图像?如何将许多自然图像拼接到一张图像上?
答案是,在寻找独特的、易于跟踪和比较的特定模板或特定特征。如果我们对这种特征进行定义,可能会发现很难用语言来表达它,但是我们知道它们是什么。如果有人要求你指出一项可以在多张图像中进行比较的良好特征,就可以指出其中一项,这就是为什么即使是小孩也可以玩这些游戏的原因。我们在图像中搜索这些特征,找到它们,在其他图像中寻找相同的特征并将它们对齐。仅此而已。(在拼图游戏中,更多地研究了不同图像的连续性)。所有的这些能力都是我们固有的。
因此,可以将这个基本问题扩展开来,但变得更加具体。这些特征是什么?(答案对于计算机也应该是可以理解的。)
很难说人们如何发现这些特征,因为这已经在我们的大脑中进行了编码。但是,如果深入研究某些图片并搜索不同的模板,我们会发现一些有趣的东西。例如,看以下的图片:
在这里插入图片描述
图像非常简单。在图像的顶部,给出了六个小图像块。问题是在原始图像中找到这些补丁的确切位置。你可以找到多少正确的结果?
A和B是平坦的表面,它们散布在很多区域上,很难找到这些补丁的确切位置。
C和D更简单,它们是建筑物的边缘。可以找到一个大概的位置,但是准确的位置仍然很困难。这是因为沿着边缘的每个地方的图案都是相同的。但是,在边缘,情况有所不同。因此,与平坦区域相比,边缘是更好的特征,但不够好(在拼图游戏中比较边缘的连续性很好)。
最后,E和F是建筑物的某些角落,而且很容易被找到。因为在拐角处,无论将此修补程序移动到何处,它的外观都将有所不同。因此,它们可以被视为很好的特征。因此,现在进入更简单(且被广泛使用的图像)以更好地理解。
在这里插入图片描述
就像上面一样,蓝色补丁是平坦区域,很难找到和跟踪。无论将蓝色补丁移到何处,它看起来都一样。黑色补丁有一个边缘。如果沿垂直方向(即沿渐变)移动它,则它会发生变化。沿着边缘(平行于边缘)移动,看起来相同。对于红色补丁,这是一个角落。无论将补丁移动到何处,它看起来都不同,这意味着它是唯一的。因此,基本上角落点被认为是图像中的良好特征。(不仅是角落,在某些情况下,斑点也被认为是不错的特征)。
因此,现在回答了这个问题,“特征是什么?”。但是出现了下一个问题。如何找到它们?还是如何找到角落?我们以一种直观的方式回答了这一问题,即寻找图像中在其周围所有区域中移动(少量)变化最大的区域。在接下来的章节中,这将被投影到计算机语言中。因此,找到这些图像特征称为特征检测。
在图像中找到了特征之后,应该能够在其他图像中找到相同的图像。这是如何做到的呢?我们围绕该特征采取一个区域,用自己的语言解释它,例如“上部是蓝天,下部是建筑物的区域,在建筑物上有玻璃等”,而在另一个建筑物中搜索相同的区域图片。基本上,这就是在描述特征。同样,计算机还应该描述特征周围的区域,以便可以在其他图像中找到它。所谓的描述称为特征描述。获得特征及其描述后,可以在所有图像中找到相同的特征并将它们对齐,缝合在一起或进行所需的操作。
因此,可以在OpenCV中寻找不同的算法来查找特征,对其进行描述,进行匹配等操作。
在本章中,将学习
cv2.cornerHarris()
,cv.2cornerSubPix()
在上一章中看到,角是图像中各个方向上强度变化很大的区域。Chris Harris和Mike Stephens在1988年的论文《 A Combined Corner and Edge Detector 》中做了一次找到这些角点的早期尝试,所以现在将该方法称为哈里斯角点检测器。他把这个简单的想法变成了数学形式。它基本上找到了
在所有方向上位移的强度差异。表示如下:
窗口函数可以是一个矩形窗口,也可以是一个高斯窗口,它在下面赋予了值。
必须最大化这个函数
用于角点检测。这意味着,必须最大化第二个项。将泰勒扩展应用于上述方程,并使用一些数学步骤,得到最后的等式:
其中
和
分别是在x和y方向上的图像导数。(可以使用cv2.Sobel()
获得)
在此之后,他们定义了一个分数,用等式表示,这将决定窗口是否包含角点。
其中
(行列式值)
(迹,主对角线元素和)
和
是M矩阵的特征值
因此,这些特征值的值决定了区域是拐角,边缘还是平坦。
较小,这在
和
较小时发生,该区域平坦。
时,这是在
时发生,反之亦然),该区域为边
很大时,这是在
和
比较大且
~
时,该区域是角点。
可以用如下图来表示:
因此,Harris Corner Detection的结果是具有这些分数的灰度图像。合适的阈值可提供图像的各个角落。
在OpenCV中有实现哈里斯角点检测,cv2.cornerHarris()
。其参数为:
dst = cv2.cornerHarris(src, blockSize, ksize, k[, dst[, borderType]] )
src
- 输入图像,灰度和float32类型blockSize
- 是拐角检测考虑的邻域大小ksize
- 使用的Sobel导数的光圈参数k
- 等式中的哈里斯检测器自由参数import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('chessboard.png')
img_copy = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
# result is dilated for marking the corners, not important
dst = cv2.dilate(dst, None)
# Threshold for an optimal value, it may vary depending on the image.
img[dst >0.01*dst.max()]=[255,0,0]
# plot
plt.subplot(121)
plt.imshow(img_copy, cmap='gray')
plt.xticks([])
plt.yticks([])
plt.subplot(122)
plt.imshow(img, cmap='gray')
plt.xticks([])
plt.yticks([])
plt.show()
以下是结果:
可以看到,各个角点已经标红。
有时候可能需要找到最精确的角点。OpenCV附带了一个函数cv2.cornerSubPix()
,它进一步细化了以亚像素精度检测到的角点。下面是一个例子。
对于cv2.cornerSubPix()
函数,必须定义停止迭代的条件。我们可以在特定的迭代次数或达到一定的精度后停止它。此外,还需要定义它将搜索角点的邻居的大小。
corners = cv.cornerSubPix( image, corners, winSize, zeroZone, criteria )
的值表示没有这样的尺寸
# sub pixel更精度角点
import cv2
import numpy as np
img = cv2.imread('chessboard2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# find Harris corners
dst = cv2.cornerHarris(gray,2, 3, 0.04)
dst = cv2.dilate(dst, None)
ret, dst = cv2.threshold(dst, 0.01*dst.max(), 255,0)
dst = np.uint8(dst)
# find centroids
ret, labels, stats, centroids = cv2.connectedComponentsWithStats(dst)
# define the criteria to stop and refine the corners
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.001)
corners = cv2.cornerSubPix(gray, np.float32(centroids), (5, 5), (-1, -1), criteria)
# Now draw them
res = np.hstack((centroids,corners))
res = np.int0(res)
img[res[:,1],res[:,0]]=[0,0,255]
img[res[:,3],res[:,2]] = [0,255,0]
cv2.imshow('subpixel', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
以下是结果, 可以看到SubPixel更精确一点:
在本章中,将学习另一个角点检测器:
cv2.goodFeaturesToTrack()
在上一章中,学习了Harris Corner Detector。1994年下半年,J.Shi和C. Tomasi在论文《Good Features to Track》中做了一个小修改,与Harris Harris Detector相比,展示了更好的结果。哈里斯角落探测器的计分功能由下式给出:
取而代之的是,C. Tomasi提出:
如果大于阈值,则将其视为拐角。如果像在Harris Corner Detector中那样在
空间中绘制它,则会得到如下图像:
从图中可以看到,只有当
和
大于最小值
时,才将其视为拐角(绿色区域)。
OpenCV有一个函数cv2.goodFeaturesToTrack()
。它通过Shi-Tomasi方法(或哈里斯角检测,如果指定的话)找到图像中的N个最强角。像往常一样,图像应该是灰度图像。然后,指定要查找的角的数量。然后,指定质量级别,该值是介于0-1之间的值,该值表示每个角落都被拒绝的最低拐角质量。然后,提供检测到的角之间的最小欧式距离。 利用所有这些信息,该函数可以找到图像中的拐角,低于平均质量的所有拐角点均被拒绝。然后,会根据质量以降序的方式对剩余的角进行排序。然后函数首先获取最佳拐角,然后丢弃最小距离范围内的所有附近拐角,然后返回N个最佳拐角。
corners = cv.goodFeaturesToTrack( image, maxCorners, qualityLevel, minDistance, mask, blockSize, gradientSize[, corners[, useHarrisDetector[, k]]] )
在下面的示例中,将尝试找到24个最佳角点:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('shi.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(gray, 24, 0.01, 10) # 四个参数,图像,个数,分数,距离
corners = np.int0(corners)
for i in corners:
x, y = i.ravel()
cv2.circle(img, (x,y), 3, 255, -1)
plt.imshow(img)
plt.show()