wordpress中文修改,眼科医院网站优化服务商,合肥做网站设计,抖音代运营公司一、角点检测-Harris
1、cv2.cornerHarris角点检测函数
在 cv2.cornerHarris 函数中#xff0c;Sobel 算子用于计算图像的梯度#xff0c;这是 Harris 角点检测的第一步。
cv2.cornerHarris(src, blockSize, ksize, k, dstNone, borderTypeNone)下面是各个参数的详细解释Sobel 算子用于计算图像的梯度这是 Harris 角点检测的第一步。
cv2.cornerHarris(src, blockSize, ksize, k, dstNone, borderTypeNone)下面是各个参数的详细解释
参数含义src输入图像。必须是灰度图像单通道类型为 numpy.float32。blockSize角点检测中窗口的大小表示在计算导数矩阵时的邻域大小。ksizeSobel 算子用于计算图像梯度的核大小通常为 3。kHarris 角点检测方程中的自由参数通常取值范围在 [0.04, 0.06]。dst可选的输出图像。通常不需要指定。borderType可选的边界模式用于处理图像边界像素的方式。默认值为 cv2.BORDER_DEFAULT。
# 使用 Harris 角点检测
dst cv2.cornerHarris(gray, blockSize2, ksize3, k0.04)返回值
函数会返回一个浮点型矩阵单通道矩阵中每个像素点的值表示 Harris 响应函数的响应值响应值越大表明角点的可能性越高。
2、Harris 角点检测过程
具体来说Harris 角点检测的过程包括以下几个步骤 计算图像梯度 Sobel 算子在 x 和 y 方向上计算图像的梯度即像素强度的变化率这是为了找到图像中的边缘信息。cv2.cornerHarris 函数会对输入的灰度图像应用 Sobel 算子来计算两个方向的梯度 I x I_x Ix 和 I y I_y Iy。ksize 参数决定了 Sobel 算子使用的卷积核大小如 3x3 或 5x5它影响计算梯度时的平滑程度。 梯度计算公式 对于水平梯度 Sobel 算子在 x 方向计算 I x ∂ I / ∂ x I_x ∂I/∂x Ix∂I/∂x对于垂直梯度 Sobel 算子在 y 方向计算 I y ∂ I / ∂ y I_y ∂I/∂y Iy∂I/∂y 构建结构张量协方差矩阵 根据计算出的梯度生成图像的结构张量也叫协方差矩阵。这个矩阵在 Harris 算法中用于描述一个像素点的局部邻域特性。结构张量的每个元素依赖于图像的局部梯度平方和 $ M \begin{bmatrix} I_x^2 I_x I_y \ I_x I_y I_y^2 \end{bmatrix} $其中 I x I_x Ix 和 I y I_y Iy 是通过 Sobel 算子计算得到的水平和垂直梯度。 角点响应计算 使用 Harris 角点检测公式计算角点响应 $ R \det(M) - k \cdot (\text{trace}(M))^2 $其中 det(M) 是矩阵的行列式trace(M) 是矩阵的迹对角线元素的和k 是经验参数。 角点检测 根据计算得到的角点响应值 R检测图像中的角点位置。响应值较大的位置被认为是角点。
3、Sobel 在 Harris 角点检测中的作用
Sobel 算子用于 Harris 算法的第一步它计算了图像的梯度这些梯度信息是后续角点检测的基础。具体来说
通过 Sobel 算子得到的梯度图 I x I_x Ix 和 I y I_y Iy反映了图像中每个像素在水平方向和垂直方向上的强度变化边缘信息。Sobel 算子帮助 Harris 算法判断图像中哪些区域具有显著的边缘和角点特征。
4、bsizeksize矩阵M blockSize 在 Harris 角点检测中涉及到对图像的局部区域进行分析这个局部区域的大小由 blockSize 决定。例如blockSize3 表示在每个像素点的检测中会使用一个 3x3 的窗口来计算该区域的协方差矩阵。blockSize 较大时角点检测会对图像的较大区域进行平滑容易忽略小的细节而较小的 blockSize 则能捕捉更多的局部特征。 ksize 这是 Sobel 算子的参数Sobel 算子用于计算图像梯度即像素强度的变化率并且是角点检测的前置步骤。ksize 控制 Sobel 算子的窗口大小例如 ksize3 表示使用 3x3 的 Sobel 核来计算水平和垂直方向的梯度。ksize 较大时计算得到的梯度会更加平滑但可能导致细节损失较小的 ksize 能捕捉更多的细节边缘。 blockSize 指定的区域内的所有像素点的 I x I_x Ix 和 I y I_y Iy 值都需要计算出来。这些梯度值用于构建协方差矩阵结构张量 对于 blockSize 指定的每个窗口计算该区域内所有像素的梯度值 M [ ∑ I x 2 ∑ I x I y ∑ I x I y ∑ I y 2 ] M \begin{bmatrix} \sum I_x^2 \sum I_x I_y \\ \sum I_x I_y \sum I_y^2 \end{bmatrix} M[∑Ix2∑IxIy∑IxIy∑Iy2] 这里的求和是针对整个 b l o c k S i z e × b l o c k S i z e blockSize \times blockSize blockSize×blockSize 的区域进行的。 img cv2.imread(test_1.jpg)
print (img.shape:,img.shape)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# gray np.float32(gray)
dst cv2.cornerHarris(gray, 2, 3, 0.04)
print (dst.shape:,dst.shape)
# img.shape: (800, 1200, 3)
# dst.shape: (800, 1200)
img[dst0.01*dst.max()][0,0,255]
cv2.imshow(dst,img)
cv2.waitKey(0)
cv2.destroyAllWindows()二、关于高斯滤波
高斯滤波的实现 高斯核首先生成一个高斯核即高斯滤波器它是一个二维的权重矩阵形状通常是正方形。高斯核的值由高斯函数决定通常为 G ( x , y ) 1 2 π σ 2 e − x 2 y 2 2 σ 2 G(x, y) \frac{1}{2\pi\sigma^2} e^{-\frac{x^2 y^2}{2\sigma^2}} G(x,y)2πσ21e−2σ2x2y2 其中 σ \sigma σ 是标准差控制高斯分布的宽度。 卷积操作使用这个高斯核与输入图像进行卷积。这一过程将高斯核的每个值与图像对应区域的像素值相乘然后将结果相加得到输出图像中对应位置的值。
控制步长与输出尺寸
步长Stride步长是卷积操作中高斯核滑动的步幅。如果将步长设置为 1则高斯核会每个像素都覆盖到计算出的输出图像会比输入图像小。填充Padding为了保持输出图像与输入图像大小相同通常在输入图像周围增加额外的像素填充。这样在卷积时高斯核能够在图像的边缘上也能进行计算。
三、关于DOG
DOGDifference of Gaussian是一种用于图像处理和特征提取的技术主要用于检测图像中的边缘和角点。其基本思想是通过两个不同标准差的高斯滤波器之间的差异来实现特征检测。
DOG 的主要用途 边缘检测 DOG 可以有效地检测图像中的边缘尤其是在具有高频细节的图像中。通过比较不同尺度下的高斯模糊效果DOG 能够突出显示边缘特征。 特征提取 在计算机视觉中DOG 是许多特征提取算法的基础例如 SIFTScale-Invariant Feature Transform。SIFT 使用 DOG 来检测关键点从而在不同尺度上提取图像特征。 图像平滑与噪声抑制 DOG 通过使用高斯滤波器能够在不同尺度上平滑图像并减少噪声这对于后续的图像处理步骤非常有用。
DOG 的实现步骤 生成两个高斯模糊图像 对原始图像应用两个不同标准差的高斯滤波器得到两个模糊图像。 计算差异 将两个模糊图像相减得到 DOG 图像。这个差异图像突出显示了在这两个尺度之间显著变化的区域通常对应于边缘和角点。
代码示例
以下是使用 OpenCV 实现 DOG 的一个示例
import cv2
import numpy as np# 读取图像
img cv2.imread(image.jpg, cv2.IMREAD_GRAYSCALE)# 应用两个高斯模糊
sigma1 1.0 # 第一个高斯的标准差
sigma2 2.0 # 第二个高斯的标准差
blur1 cv2.GaussianBlur(img, (0, 0), sigma1)
blur2 cv2.GaussianBlur(img, (0, 0), sigma2)# 计算 DOG
dog blur1 - blur2# 显示结果
cv2.imshow(DOG Image, dog)
cv2.waitKey(0)
cv2.destroyAllWindows()总结
DOG 是一种有效的边缘和特征检测方法通过对不同尺度下的高斯滤波结果进行差异计算能够突出显示图像中的重要特征在计算机视觉和图像处理领域有着广泛的应用。
为何差异计算可以得到特征❤️ 通过对不同尺度下的高斯滤波结果进行差异计算DOGDifference of Gaussian能够突出显示图像中的重要特征这主要是因为以下几个原因 1. 尺度空间理论 图像中的特征如边缘和角点在不同的尺度 σ \sigma σ上表现不同。使用两个不同标准差的高斯滤波器可以在多个尺度上分析图像捕捉到不同层次的细节。较小的标准差能够捕捉到图像中的细节特征而较大的标准差则能够平滑掉这些细节关注整体结构。 2. 边缘增强 当图像中存在边缘时边缘两侧的像素值变化较大。应用高斯滤波后边缘的像素会在较小标准差的高斯模糊中保持较高的响应而在较大标准差的高斯模糊中会被平滑掉。通过计算这两者的差异边缘处的响应值会变得显著从而突出显示边缘特征。 3. 抑制噪声 DOG 不仅能够强调特征还可以抑制图像中的噪声。在较大的尺度下噪声通常会被平滑掉因此通过差异计算能够有效减少噪声的影响保留真正的特征信息。 4. 关键点检测 在使用 DOG 进行关键点检测如 SIFT时DOG 可以帮助识别图像中具有显著变化的区域这些区域通常是角点或边缘。通过对 DOG 图像进行阈值处理可以识别出具有局部极值的点进一步提取出重要的特征点。 总结 差异计算能够得到特征的核心原因在于不同尺度的高斯模糊分别保留和抑制了图像的不同特征最终通过计算这两者的差异能够突出显示图像中最显著的变化如边缘和角点从而实现有效的特征检测。这种方法利用了尺度空间的优势使得图像处理在多种应用中更具灵活性和鲁棒性。 四、SIFT原理
使用 DOGDifference of Gaussian进行关键点检测的过程特别是在 SIFTScale-Invariant Feature Transform算法中分为几个主要步骤
1. 构建高斯金字塔与 DOG 金字塔
对图像进行高斯模糊使用不同标准差 ( σ \sigma σ) 的高斯滤波器来模糊图像。模糊后的图像在不同尺度上形成高斯金字塔。计算相邻尺度之间的差分形成 DOG 金字塔。每一层代表不同尺度下的高斯模糊结果之间的差异。
过程
对原始图像进行高斯滤波逐渐增加 ( σ \sigma σ) 值得到多层高斯金字塔。对每一对相邻的高斯模糊图像进行差分得到多层 DOG 金字塔。
2. 在 DOG 金字塔中检测极值点
对于 DOG 金字塔中的每个像素比较它与邻近的 26 个像素即上下两层和当前层的 8 个邻居。如果该像素是这 26 个像素中的最大值或最小值则认为它是一个关键点候选。
过程
每个像素与它在相邻尺度上下层的 8 个像素和当前尺度同一层的 8 个像素进行比较找出局部极值。
3. 关键点精确定位❤️
对找到的极值点进行精确定位。使用泰勒展开在尺度空间中拟合极值点并去除对比度过低或位于边缘的点。
过程
对关键点进行二次拟合以提高检测的精度去掉那些响应不够显著或在边缘的点。 在 SIFT 算法中原始的关键点检测是在 DOG高斯差分金字塔中通过比较邻域像素值找到的局部极值点。这些极值点是在像素级别上通过简单的比较操作得到的因此它们的定位是离散的即受限于图像网格的整数像素位置。 为什么原来的关键点不够精确 像素级别的限制 DOG 算法初步检测到的关键点位置是基于图像的整数像素坐标即在每个像素之间进行比较。由于图像的特征可能位于像素之间而不是恰好在某个像素的整数坐标上因此初步检测的关键点可能并不完全准确。这种粗粒度的关键点检测只能捕捉到特征的大致位置但在亚像素级别上可能会有偏差。 响应不显著 一些初步检测到的关键点可能响应值较弱尤其是当它们处于噪声、低对比度或平滑区域中时。这些点虽然被检测为极值但对实际图像特征的贡献很小容易受到噪声干扰。 边缘响应的不稳定性 在 DOG 金字塔中边缘附近的点也可能表现为局部极值。但这些边缘点通常非常不稳定容易受到图像细微变化的影响。因此虽然这些点可能被初步检测为关键点但它们并不可靠需要进一步精确定位并排除。 为什么需要精确定位 获得亚像素级别的精度 真实世界中的图像特征不一定位于整数像素位置上可能在两个像素之间。因此检测到的关键点如果仅停留在整数像素位置会丢失一些重要的细节。通过泰勒展开和二次拟合SIFT 可以将关键点的定位从整数像素位置扩展到亚像素级别使特征点的精度更高。 提高关键点的稳定性 在 DOG 金字塔中直接检测到的极值点有时可能受噪声干扰或者由于对比度不高而不稳定。通过精确定位可以剔除低对比度或不稳定的关键点保证留下来的都是对图像整体结构有显著贡献的关键点。 消除边缘响应点 边缘上的极值点虽然可能是初步检测到的关键点但它们不够稳定。通过对这些点进行进一步分析如 Hessian 矩阵可以识别出哪些点是边缘响应从而将其剔除确保关键点的稳定性和可靠性。 精确定位的好处 亚像素精度通过二次拟合和泰勒展开关键点的坐标可以在亚像素级别上得到精确确定使得后续的特征匹配更加准确。剔除无效点通过对比度检测和边缘响应排除去掉噪声点和边缘不稳定点确保只保留图像中的重要特征点。提升匹配的鲁棒性更精确的关键点定位使得这些点在不同尺度、旋转和视角下更加稳定从而提高了后续特征描述和匹配的鲁棒性。 总结 初步检测到的关键点虽然是局部极值但由于整数像素坐标的限制、对比度不强的点以及边缘响应的不稳定性这些关键点往往不够精确和稳定。通过关键点精确定位步骤SIFT 可以将关键点的定位提升到亚像素级别并剔除不稳定的点确保后续的特征匹配更加准确和鲁棒。 关键点精确定位的具体步骤 拟合极值点 SIFT 使用泰勒展开在尺度空间中对 DOG 金字塔中的局部极值点进行二次拟合精确确定关键点的位置包括子像素级别的精度、尺度和响应值。使用 DOG 函数的二阶泰勒展开近似将极值点的位置、尺度从整数精度优化到亚像素精度。 泰勒展开公式 D ( x ) D ∂ D ∂ x Δ x 1 2 Δ x T ∂ 2 D ∂ x 2 Δ x D(x) D \frac{\partial D}{\partial x} \Delta x \frac{1}{2} \Delta x^T \frac{\partial^2 D}{\partial x^2} \Delta x D(x)D∂x∂DΔx21ΔxT∂x2∂2DΔx 其中 D 是 DOG 金字塔中的像素值 Δ x \Delta x Δx 是位置的微小变化。 去除低对比度关键点 对于那些响应较弱的关键点即 DOG 函数的值较小的关键点SIFT 将其过滤掉。这是为了确保选出的关键点是图像中的显著特征而非噪声或模糊区域。如果通过拟合得到的关键点响应值低于某个阈值通常是 0.03则该关键点会被丢弃。 去除边缘响应点 边缘响应的点虽然在 DOG 图像中也表现为极值点但它们不稳定且易受噪声影响。为了避免选择边缘上的关键点SIFT 使用 Hessian 矩阵的主曲率来判断边缘点并滤除这些点。SIFT 利用 Hessian 矩阵的特征值来衡量局部区域的曲率。如果一个点在某个方向的主曲率远大于另一个方向的主曲率就认为该点位于边缘从而将其舍弃。 Hessian 矩阵 H [ D x x D x y D x y D y y ] H \begin{bmatrix} D_{xx} D_{xy} \\ D_{xy} D_{yy} \end{bmatrix} H[DxxDxyDxyDyy] 通过计算 Hessian 矩阵的特征值可以判断某个点是否是边缘点。如果主曲率的比值即两个特征值的比值超过某个阈值通常是 10那么该点会被认为是边缘点并被舍弃。 4. 关键点方向分配❤️
为了使得关键点具有旋转不变性SIFT 为每个关键点分配一个方向。通过计算关键点邻域内的梯度方向直方图确定主方向最大值。为了让 SIFT 特征在不同的旋转角度下保持一致必须为每个关键点分配一个主方向这样即使图像发生旋转特征点仍然可以被匹配。 每个特征点可以得到三个信息 ( x , y , σ , θ ) (x,y,σ,θ) (x,y,σ,θ)即位置、尺度和方向。具有多个方向的关键点可以被复制成多份然后将方向值分别赋给复制后的特征点一个特征点就产生了多个坐标、尺度相等但是方向不同的特征点。
过程
在关键点周围计算梯度方向和幅度使用这些信息构建梯度方向直方图。最大值方向被选为该关键点的主方向。 为什么要得到关键点方向 为了让 SIFT 特征在不同的旋转角度下保持一致必须为每个关键点分配一个主方向这样即使图像发生旋转特征点仍然可以被匹配。 原因 旋转不变性图像中的物体可能会因为拍摄角度的变化而旋转。如果不考虑旋转直接使用图像中的关键点图像旋转后相同的物体的特征将变得无法匹配。因此为了让关键点的描述子与旋转无关必须为每个关键点分配一个主方向。统一特征表示通过为关键点分配方向后续生成的特征描述子可以基于这个方向进行计算。这样当图像旋转时特征描述子仍然会指向相同的方向确保特征匹配不受旋转的影响。 2. 如何计算关键点方向 SIFT 在计算关键点方向时会选择一个以关键点为中心的局部邻域通常是一个半径为 3 倍高斯模糊尺度的区域并计算该区域内每个像素的梯度方向和梯度幅值。然后根据梯度信息构建方向直方图直方图的峰值代表主方向。 具体步骤 计算梯度方向和幅值 对关键点邻域内的每个像素计算其梯度方向和幅值。梯度是通过 Sobel 算子或者差分法得到的。 梯度幅值 m ( x , y ) m(x, y) m(x,y) 和梯度方向 θ ( x , y ) \theta(x, y) θ(x,y) 的公式如下 m ( x , y ) ( I ( x 1 , y ) − I ( x − 1 , y ) ) 2 ( I ( x , y 1 ) − I ( x , y − 1 ) ) 2 m(x, y) \sqrt{(I(x1, y) - I(x-1, y))^2 (I(x, y1) - I(x, y-1))^2} m(x,y)(I(x1,y)−I(x−1,y))2(I(x,y1)−I(x,y−1))2 θ ( x , y ) a r c t a n ( I ( x 1 , y ) − I ( x − 1 , y ) I ( x , y 1 ) − I ( x , y − 1 ) ) θ(x,y)arctan(I(x1,y)−I(x−1,y)I(x,y1)−I(x,y−1)) θ(x,y)arctan(I(x1,y)−I(x−1,y)I(x,y1)−I(x,y−1)) 其中 I ( x , y ) I(x, y) I(x,y)是图像在 ( x , y ) (x, y) (x,y) 位置的像素值。 构建方向直方图 将梯度方向分成 36 个方向每个方向覆盖 10°并根据每个像素的梯度幅值为这些方向加权累加生成一个方向直方图。梯度幅值越大说明该方向上变化越剧烈因此该方向在直方图中的权重也越高。在计算梯度时对距离关键点较远的像素进行高斯加权距离越远的像素权重越小。这是为了减少远离关键点的像素对方向计算的影响保证方向主要反映关键点局部的特征。 选择主方向 直方图中的最大值方向被选为该关键点的主方向。有时若某个方向的次峰值也较大通常大于主峰值的 80%则该关键点会分配多个方向从而在多个方向上增强特征点的描述能力。 3. 为什么要计算邻域 计算邻域内像素的梯度方向和幅值是为了确保关键点的方向能够反映局部区域内的图像变化而不仅仅依赖于单个像素。通过这种方式计算出的方向可以更稳定和准确减少噪声或局部异常对方向的影响。 原因 局部稳定性单个像素的梯度方向可能会受到噪声影响或者因为它所处的图像区域比较平滑而不够稳定。通过计算关键点邻域内多个像素的梯度方向SIFT 能够提取出更稳定的方向信息确保关键点方向是对局部图像变化的真实反映。增强鲁棒性邻域内的梯度方向能够更全面地反映该区域内的特征信息。通过在邻域内累加多个像素的梯度信息SIFT 能够捕捉到该区域的主要变化方向增强特征点的鲁棒性尤其是在噪声或光照变化情况下。 4. 关键点方向的重要性总结 实现旋转不变性通过为每个关键点分配主方向确保图像在旋转时关键点的描述子不会受影响。即使图像发生旋转基于关键点主方向构建的描述子仍然可以与其他图像中的对应特征进行正确匹配。稳定关键点描述计算邻域中的梯度信息并构建方向直方图可以确保关键点的方向是基于局部区域的整体变化而不是受单个像素的局部噪声干扰。 总结 得到关键点方向 是为了实现旋转不变性确保图像旋转时特征点仍能正确匹配。计算邻域的梯度信息 是为了增强方向计算的稳定性和准确性确保关键点的方向能够反映局部区域的真实变化。 5. 生成关键点描述子
对每个关键点根据它的主方向在其周围构建一个 8x8 的窗口将窗口划分成 4x4 的子区域。在每个子区域内计算梯度方向的直方图最终生成 32 维的描述子。 过程
每个 4x4 子区域计算 8 个方向的梯度直方图这样 4 个子区域共生成 32 维的描述子。 旋转之后的主方向为中心取8x8的窗口求每个像素的梯度幅值和方向箭头方向代表梯度方向长度代表梯度幅值然后利用高斯窗口对其进行加权运算最后在每个4x4的小块上绘制8个方向的梯度直方图计算每个梯度方向的累加值即可形成一个种子点即每个特征的由4个种子点组成每个种子点有8个方向的向量信息。 (4,4,8) 特征点方向与邻域内的方向有什么区别 SIFT 处理关键点方向和邻域内的方向有两个层次 关键点方向主方向这是在前面步骤中计算的表示的是关键点的主要方向用于实现旋转不变性。通过为每个关键点分配一个方向或者多个方向确保即使图像旋转关键点描述子依然能够被正确匹配。这个方向是通过计算关键点局部区域内像素的梯度信息生成方向直方图并选择主峰值的方向来得到的。 关键点方向的计算是在关键点检测之后构建描述子之前完成的。它用于归一化关键点局部区域的方向使得描述子与图像旋转无关。 邻域内的方向用于生成描述子在生成 SIFT 特征描述子时对关键点邻域的每个小区域如 4x4 子区域都计算梯度方向和梯度幅值并构建方向直方图。这些直方图表示的是该小区域内的局部方向信息。通过将这些局部方向的信息进行组合生成一个反映关键点周围结构的 128 维特征向量。 这些方向不仅仅反映关键点的主方向而是描述了关键点周围的多个方向上的变化。这些信息有助于生成更丰富、细致的特征描述子使其更加鲁棒。 总结 SIFT 特征描述子是通过计算关键点邻域内多个小区域的梯度方向和幅值来生成的每个区域生成一个方向直方图。关键点方向是特征点的主方向用于对邻域方向进行归一化确保特征描述子在旋转不变的情况下依然能够有效匹配。邻域方向是描述子生成过程中对关键点周围的局部方向信息进行编码的结果它描述了关键点周围的图像特征。 6. 匹配关键点
在检测到关键点及其描述子后可以通过对比不同图像中的描述子寻找相似的关键点以进行图像匹配。
过程
通过最近邻匹配算法如欧氏距离将不同图像中的关键点描述子进行匹配。
SIFT 关键点检测总结流程
高斯模糊与 DOG 差分构建高斯金字塔计算相邻尺度之间的差分生成 DOG 金字塔。极值检测在 DOG 金字塔中检测局部极值点。精确定位拟合极值点并去除低对比度或边缘上的关键点。方向分配为每个关键点分配一个主方向使得关键点具有旋转不变性。描述子生成构建 128 维的特征描述子。关键点匹配通过特征描述子匹配不同图像中的关键点。
这个过程让 SIFT 能够在不同尺度、不同旋转角度下进行关键点检测和匹配从而在图像匹配、特征识别中表现出强大的鲁棒性和稳定性。
五、SIFT实例
img cv2.imread(test_1.jpg)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift cv2.xfeatures2d.SIFT_create() # 创建一个 SIFT 特征检测器对象
# 关键点kp对象
# kp 是一个包含多个 KeyPoint 对象的列表每个 KeyPoint 对象包含以下信息
kp sift.detect(gray, None) # 在灰度图像 gray 中检测关键点
img cv2.drawKeypoints(gray, kp, img)
cv2.imshow(drawKeypoints, img)
cv2.waitKey(0)
cv2.destroyAllWindows()cv2.xfeatures2d.SIFT.compute() 和 cv2.xfeatures2d.SIFT.detect() 是 SIFT 特征检测和描述算法中非常重要的两个函数分别用于检测图像中的关键点和计算这些关键点的特征描述符。下面是对这两个函数的详细解释。
1. sift.detect()
kp sift.detect(gray, None)功能用于检测图像中的关键点。参数 gray输入图像必须为单通道灰度图像。None可选的掩码参数指定要检测的区域。如果没有特定区域设置为 None。 返回值返回一个关键点列表cv2.KeyPoint 对象每个对象包含关键点的位置、尺度、方向等信息。
示例
import cv2# 读取图像并转换为灰度图
img cv2.imread(image.jpg)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建 SIFT 特征检测器对象
sift cv2.xfeatures2d.SIFT_create()# 检测关键点
kp sift.detect(gray, None)2. sift.compute()❤️
keypoints, descriptors sift.compute(gray, kp)功能计算给定关键点的特征描述符。参数 gray输入图像通常为灰度图像用于计算特征描述符。kp之前检测到的关键点列表由 sift.detect() 返回的关键点。 返回值 keypoints与输入的关键点列表相同包含所有关键点信息。descriptors特征描述符的数组每个描述符对应一个关键点通常是一个 128 维的浮点数组。
# 计算描述符
keypoints, descriptors sift.compute(gray, kp)使用示例
import cv2# 读取图像并转换为灰度图
img cv2.imread(image.jpg)
gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建 SIFT 特征检测器对象
sift cv2.xfeatures2d.SIFT_create()# 检测关键点
kp sift.detect(gray, None)# 计算描述符
keypoints, descriptors sift.compute(gray, kp)# 可视化关键点
img_kp cv2.drawKeypoints(img, keypoints, None, color(0, 255, 0))# 显示结果
cv2.imshow(SIFT Keypoints, img_kp)
cv2.waitKey(0)
cv2.destroyAllWindows()总结
sift.detect()用于检测图像中的关键点并返回关键点列表。sift.compute()用于计算这些关键点的特征描述符返回描述符数组和关键点信息。
这两个函数通常结合使用以便在图像中提取特征并进行后续的匹配或分析。
六、特征匹配
cv2.BFMatcher
cv2.BFMatcher 是 OpenCV 中用于特征匹配的一个类特别适用于 SIFT、SURF 等特征描述符的匹配。BFMatcher 是“Brute Force Matcher”的缩写表示使用暴力匹配的方法来寻找匹配的特征点。 创建 BFMatcher 对象 bf cv2.BFMatcher(normType, crossCheck)参数详解 normType指定用于计算距离的度量方式 cv2.NORM_L2用于 SIFT 和 SURF 特征描述符的平方欧几里得距离L2 距离。cv2.NORM_HAMMING用于 ORB 特征描述符的汉明距离Hamming 距离适用于二进制描述符。 crossCheck布尔值用于指定是否进行交叉检查 True只有当特征点 A 认为特征点 B 是它的最佳匹配同时特征点 B 也认为特征点 A 是它的最佳匹配时才会保存匹配结果。这可以减少误匹配。False不进行交叉检查匹配更快但可能会有误匹配。 匹配特征 使用 match() 方法可以找到两幅图像之间的匹配特征点。 matches bf.match(descriptors1, descriptors2)返回值 matches返回的匹配对象列表每个对象包含匹配的特征点的信息。 排序和绘制匹配结果 通常在获取匹配后使用 sorted() 对匹配进行排序以便找到最佳匹配然后使用 cv2.drawMatches() 来可视化匹配结果。 示例代码 import cv2# 读取图像并转换为灰度图
img1 cv2.imread(image1.jpg, cv2.IMREAD_GRAYSCALE)
img2 cv2.imread(image2.jpg, cv2.IMREAD_GRAYSCALE)# 创建 SIFT 特征检测器
sift cv2.xfeatures2d.SIFT_create()# 检测关键点和计算描述符
kp1, des1 sift.detectAndCompute(img1, None)
kp2, des2 sift.detectAndCompute(img2, None)# 创建 BFMatcher 对象
bf cv2.BFMatcher(cv2.NORM_L2, crossCheckTrue)# 匹配特征
matches bf.match(des1, des2)# 按距离排序匹配
matches sorted(matches, keylambda x: x.distance)# 可视化匹配结果
img_matches cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flagscv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)# 显示结果
cv2.imshow(Matches, img_matches)
cv2.waitKey(0)
cv2.destroyAllWindows()总结 cv2.BFMatcher 是一个用于特征点匹配的类采用暴力匹配的方法。可以指定距离度量方式如 L2 距离、Hamming 距离和是否进行交叉检查。使用 match() 方法查找匹配返回的匹配结果可以进行排序和可视化以分析图像特征的匹配情况。 k对最佳匹配
matches bf.knnMatch(des1, des2, k2)
# matches将是一个列表其中每个元素都是一个包含两个匹配的列表。每个子列表中的元素表示从des2中找到的最接近的两个描述符。
good []
for m, n in matches:if m.distance 0.75 * n.distance:good.append([m])
img3 cv2.drawMatchesKnn(img1,kp1,img2,kp2,good,None,flags2)KANSAC
算法基本思想和流程
RANSAC是通过反复选择数据集去估计出模型一直迭代到估计出认为比较好的模型。 具体的实现步骤可以分为以下几步
选择出可以估计出模型的最小数据集(对于直线拟合来说就是两个点对于计算Homography矩阵就是4个点)使用这个数据集来计算出数据模型将所有数据带入这个模型计算出“内点”的数目(累加在一定误差范围内的适合当前迭代推出模型的数据)比较当前模型和之前推出的最好的模型的“内点“的数量记录最大“内点”数的模型参数和“内点”数重复1-4步直到迭代结束或者当前模型已经足够好了(“内点数目大于一定数量”)。
迭代次数推导
这里有一点就是迭代的次数我们应该选择多大呢这个值是否可以事先知道应该设为多少呢还是只能凭经验决定呢 这个值其实是可以估算出来的。下面我们就来推算一下。
假设“内点”在数据中的占比为 t t n i n l i e r s n i n l i e r s n o u t l i e r s t\dfrac{n_{inliers}}{ n_{inliers}n_{outliers}} tninliersnoutliersninliers
那么我们每次计算模型使用 N 个点的情况下选取的点至少有一个外点的情况就是 1 − t N 1−t^N 1−tN
也就是说在迭代 k 次的情况下 ( 1 − t n ) k (1−t^n)^k (1−tn)k 就是 k 次迭代计算模型都至少采样到一个“外点”去计算模型的概率。那么能采样到正确的 N 个点去计算出正确模型的概率就是 P 1 − ( 1 − t n ) k P1−(1−t^n)^k P1−(1−tn)k
通过上式可以求得 k l o g ( 1 − P ) l o g ( 1 − t n ) k\dfrac {log(1−P)}{log(1−t^n)} klog(1−tn)log(1−P) “内点”的概率 t 通常是一个先验值。然后 P 是我们希望RANSAC得到正确模型的概率。如果事先不知道 t 的值可以使用自适应迭代次数的方法。也就是一开始设定一个无穷大的迭代次数然后每次更新模型参数估计的时候用当前的“内点”比值当成 t 来估算出迭代次数。 import numpy as np
import matplotlib.pyplot as plt
import random
import math# 数据量。
SIZE 50
# 产生数据。np.linspace 返回一个一维数组SIZE指定数组长度。
# 数组最小值是0最大值是10。所有元素间隔相等。
X np.linspace(0, 10, SIZE)
Y 3 * X 10fig plt.figure()
# 画图区域分成1行1列。选择第一块区域。
ax1 fig.add_subplot(1,1, 1)
# 标题
ax1.set_title(RANSAC)# 让散点图的数据更加随机并且添加一些噪声。
random_x []
random_y []
# 添加直线随机噪声
for i in range(SIZE):random_x.append(X[i] random.uniform(-0.5, 0.5)) random_y.append(Y[i] random.uniform(-0.5, 0.5))
# 添加随机噪声
for i in range(SIZE):random_x.append(random.uniform(0,10))random_y.append(random.uniform(10,40))
RANDOM_X np.array(random_x) # 散点图的横轴。
RANDOM_Y np.array(random_y) # 散点图的纵轴。# 画散点图。
ax1.scatter(RANDOM_X, RANDOM_Y)
# 横轴名称。
ax1.set_xlabel(x)
# 纵轴名称。
ax1.set_ylabel(y)# 使用RANSAC算法估算模型
# 迭代最大次数每次得到更好的估计会优化iters的数值
iters 100000
# 数据和模型之间可接受的差值
sigma 0.25
# 最好模型的参数估计和内点数目
best_a 0
best_b 0
pretotal 0
# 希望的得到正确模型的概率
P 0.99
for i in range(iters):# 随机在数据中红选出两个点去求解模型sample_index random.sample(range(SIZE * 2),2)x_1 RANDOM_X[sample_index[0]]x_2 RANDOM_X[sample_index[1]]y_1 RANDOM_Y[sample_index[0]]y_2 RANDOM_Y[sample_index[1]]# y ax b 求解出aba (y_2 - y_1) / (x_2 - x_1)b y_1 - a * x_1# 算出内点数目total_inlier 0for index in range(SIZE * 2):y_estimate a * RANDOM_X[index] bif abs(y_estimate - RANDOM_Y[index]) sigma:total_inlier total_inlier 1# 判断当前的模型是否比之前估算的模型好if total_inlier pretotal:iters math.log(1 - P) / math.log(1 - pow(total_inlier / (SIZE * 2), 2))pretotal total_inlierbest_a abest_b b# 判断是否当前模型已经符合超过一半的点if total_inlier SIZE:break# 用我们得到的最佳估计画图
Y best_a * RANDOM_X best_b# 直线图
ax1.plot(RANDOM_X, Y)
text best_a str(best_a) \nbest_b str(best_b)
plt.text(5,10, text,fontdict{size: 8, color: r})
plt.show()cv2.findHomography❤️
cv2.findHomography 是 OpenCV 中用于计算两个平面之间的变换矩阵Homography矩阵的函数。以下是它的详细说明
cv2.findHomography 在计算 Homography 矩阵时使用了 RANSAC 或其他方法这些方法会进行迭代以提高估计的鲁棒性。函数内部存在迭代
函数签名
H, status cv2.findHomography(srcPoints, dstPoints, methodcv2.RANSAC, ransacReprojThreshold3)参数说明
srcPoints输入的点集表示源图像中的点应该是一个形状为 ( N × 2 N \times 2 N×2) 的数组。dstPoints目标点集表示目标图像中的点形状同样为 ( N × 2 N \times 2 N×2) 的数组。method可选使用的算法。常用的有 cv2.RANSAC随机采样一致性算法用于鲁棒估计。cv2.LMEDS最小中值平方法。 ransacReprojThreshold可选RANSAC算法的重投影误差阈值用于判断点是否是内点单位像素。
返回值
H计算得到的3x3的Homography矩阵。如果没有足够的点匹配会返回 None。status一个数组表示每个点是否为内点1表示内点0表示外点。
实际应用
cv2.findHomography 通常用于图像拼接、特征匹配、透视变换等场景。例如在拼接两张图像时首先通过特征匹配找到对应点然后使用 cv2.findHomography 计算变换矩阵将一张图像变换到另一张图像的坐标系中。
七、为什么要引入齐次坐标
[计算机视觉] 什么是齐次坐标为什么要引入齐次坐标-CSDN博客
什么是齐次坐标? - 知乎
齐次坐标的基本概念 齐次坐标定义 在二维空间中一个点 ((x, y)) 可以用齐次坐标表示为 ((x, y, 1))。这意味着我们引入了一个额外的维度通常称为齐次坐标的第三个分量称为“尺度因子”。 点的表示 齐次坐标使得我们可以用一个3维向量表示一个二维点。这种表示方式的优点在于我们可以使用矩阵运算来处理平移、旋转和缩放等变换。
齐次坐标的好处 统一变换 在常规二维变换中平移变换通常需要额外的运算例如 旋转 ( ( x ′ , y ′ ) R ⋅ ( x , y ) (x, y) R \cdot (x, y) (x′,y′)R⋅(x,y))平移 ( ( x ′ , y ′ ) ( x t x , y t y ) (x, y) (x t_x, y t_y) (x′,y′)(xtx,yty)) 但在齐次坐标中可以将平移与其他线性变换统一为一个矩阵运算。例如平移和旋转可以用一个3x3的矩阵表示 $ T \begin{bmatrix} 1 0 t_x \ 0 1 t_y \ 0 0 1 \end{bmatrix} $ 旋转矩阵也可以用相同形式表示结合起来后可以通过矩阵乘法得到最终的变换。 避免除法 在传统的二维变换中特别是在透视投影时我们需要进行除法来得到最终的坐标。这可能导致数值不稳定性。使用齐次坐标后我们可以通过交叉相乘消除分母从而避免直接的除法计算。例如 $ x’ \frac{h_{11}x h_{12}y h_{13}}{h_{31}x h_{32}y h_{33}} $ 可以转化为 $(h_{31}x h_{32}y h_{33}) x’ h_{11}x h_{12}y h_{13} $这样就只需要进行乘法和加法避免了除法带来的潜在问题。 表示无穷远点 齐次坐标还允许我们表示无穷远点。在传统的二维坐标中没有一种方式可以表示无穷远的概念而齐次坐标 ((x, y, 0)) 则可以用来表示这一点。这在计算投影变换如透视投影时是非常重要的。
总结
通过使用齐次坐标计算变换变得更为统一和简洁能够有效地处理平移、旋转和缩放等操作。同时避免了计算中的除法问题并可以表示无穷远点。这样在处理图像变换时使用齐次坐标是一个非常有效的选择。