目录
目录
2.3.2 实验部分
2.1 Harris角点检测器的原理
Harris 角点检测器是一种用于寻找图像中角点的算法。它的原理是基于角点与边缘的差异性,利用图像局部灰度值的变化信息来检测角点。
Harris 角点检测器的基本思想是:在图像中的某个像素位置,如果在任何方向上移动一个小的窗口,窗口内的像素都发生了明显的灰度变化,那么这个像素位置就是一个角点。Harris 角点检测器通过计算每个像素的灰度变化来评估角点的可能性,从而找到图像中的角点。
角点特征:
- 轮廓之间的交点。
- 对于同一场景,即使视角发生变化,通常具备稳定性质的特征。
- 该点附近区域的像素点无论在梯度方向上还是其梯度幅值上有着较大变化。
- 角点处的一阶导数最大,二阶导数为0。
- 角点指示了物体边缘变化不连续的方向。
Harris 角点检测算子:
Harris 算子是一种简单的点特征提取算子,这种算子受信号处理中自相关函数的启发,给出与自相关函数相联系的矩阵M。M阵的特征值是自相关函数的一阶曲率,如果两个曲率值都高,那么就认为该点是特征点。为了消除噪声对于角点检测的影响,可以使用一个高斯滤波器来平滑图像。
数学公式:
把图像域中点 x 上的对称半正定矩阵定义为:
其中为包含导数
和
的图像梯度,由于该定义,
的秩为 1,特征值为
,
,现在对于图像的每一个 像素,我们可以计算出该矩阵。
选择权重矩阵 W(通常为高斯滤波器 Gσ),我们可以得到卷积:
该卷积的目的是得到在周围像素上的局部平均。计算出的矩阵
又称为 Harris矩阵。
Harris角点检测算法优点:
旋转不变性,椭圆转过一定角度但是其形状保持不变(特征值保持不变)。
对于图像灰度的仿射变化具有部分的不变性,由于仅仅使用了图像的一介导数,对于图像灰度平移变化不变;对于图像灰度尺度变化不变。
Harris角点检测算法缺点:
它对尺度很敏感,不具备几何尺度不变性。
提取的角点是像素级的。
2.1.1 实验部分
代码:
from pylab import *
from PIL import Image
from PCV.localdescriptors import harris
# # 读入图像
im = array(Image.open('J12.jpg').convert('L'))
# 检测harris角点
harrisim = harris.compute_harris_response(im)
# Harris响应函数
harrisim1 = 255 - harrisim
figure()
gray()
# 画出Harris响应图
subplot(141)
imshow(harrisim1)
print(harrisim1.shape)
axis('off')
axis('equal')
threshold = [0.01, 0.05, 0.1]
for i, thres in enumerate(threshold):
filtered_coords = harris.get_harris_points(harrisim, 6, thres)
subplot(1, 4, i + 2)
imshow(im)
print(im.shape)
plot([p[1] for p in filtered_coords], [p[0] for p in filtered_coords], '*')
axis('off')
show()
运行效果图如下:
分析:
增大α的值,将减小角点响应值R,降低角点检测的灵性,减少被检测角点的数量;减小α值,将增大角点响应值R,增加角点检测的灵敏性,增加被检测角点的数量。使用阈值 0.01、0.05 和 0.1 检测出的角点依次减少。
改进:
使用函数cornerHarris()识别角点:它使用 Harris 角点检测算法来寻找图像中的角点。
cv2.cornerHarris()
函数的语法如下:
dst = cv2.cornerHarris(src, blockSize, ksize, k[, dst[, borderType]])
其中,参数的含义如下:
src
:输入图像,单通道灰度图像,数据类型为float32
或float64
。blockSize
:角点检测中使用的窗口大小(即局部邻域),通常取值为 2、3、4、5、6 等。ksize
:Sobel 滤波器的内核大小,必须是奇数。通常取值为 3、5、7 等。k
:Harris 角点检测算法中的自由参数,取值范围为 0.04 到 0.06。dst
:输出图像,与src
具有相同的大小和数据类型。borderType
:可选的边界处理方式,与cv2.filter2D()
函数的borderType
参数相同。
cv2.cornerHarris()
函数的返回值 dst
是一个与输入图像 src
具有相同大小的单通道浮点数图像,其中每个像素的值表示该像素是否为角点的响应值。通常,需要对 dst
进行阈值处理,将响应值较大的像素标记为角点。
当ksize参数设为3时:
代码:
import cv2
import numpy as np
filename = 'J11.jpg'
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 6, 3, 0.04)
img[dst > 0.01 * dst.max()] = [0, 0, 255]
cv2.imshow('dst', img)
if cv2.waitKey(0) & 0xff == 27:
cv2.destroyAllWindows()
代码运行效果如下:
当ksize参数设为21时:
代码:
import cv2
import numpy as np
filename = 'J11.jpg'
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv2.cornerHarris(gray, 6, 21, 0.04)
img[dst > 0.01 * dst.max()] = [0, 0, 255]
cv2.imshow('dst', img)
if cv2.waitKey(0) & 0xff == 27:
cv2.destroyAllWindows()
代码运行效果图如下:
可以看到,如果将参数设置为3,当检测到大楼方块的边界时,大楼中方块的所有对角线都会被认为是角点。如果参数设置为21,只有大楼方块的角点才能被检测为角点。
2.2 SIFT(尺度不变特征变换)
SIFT(Scale-Invariant Feature Transform,尺度不变特征变换)是一种计算机视觉算法,用于在图像中检测和描述局部特征。SIFT 算法由 David Lowe 在 1999 年提出,是一种非常经典的特征匹配算法,被广泛应用于图像拼接、目标识别、三维重建等领域。
SIFT 算法的主要步骤包括:
- 尺度空间极值检测:通过高斯差分金字塔来检测不同尺度下的局部极值点,用于检测图像中的关键点。
- 关键点定位:对尺度空间极值点进行精确定位,得到关键点的位置和尺度。
- 方向确定:在关键点周围的邻域内,根据梯度方向和大小来确定关键点的主方向。
- 关键点描述:根据关键点的主方向,在关键点周围的邻域内计算局部特征向量,用于描述关键点的特征。
SIFT(尺度不变特征变换)优点:
- SIFT 算法的主要优点是具有尺度不变性、旋转不变性和光照不变性,可以在不同的尺度、旋转角度和光照条件下提取到稳定的特征。因此,SIFT 算法被广泛应用于计算机视觉领域中的图像匹配、目标跟踪、三维重建等任务中。
SIFT算法的特点:
- 图像的局部特征,对旋转、尺度缩放、亮度变化保持不变,对视角变化、仿射变换、噪声也保持一定程度的稳定性。
- 独特性好,信息量丰富,适用于海量特征库进行快速、准确的匹配。
- 多量性,即使是很少几个物体也可以产生大量的SIFT特征
- 高速性,经优化的SIFT匹配算法甚至可以达到实时性
- 扩招性,可以很方便的与其他的特征向量进行联合。
SIFT 算法可以用于解决以下问题:
- 图像拼接:在拼接多张图片时,需要找到它们之间的相同特征点来进行对齐。SIFT 算法可以提取出稳定的关键点和特征向量,用于匹配不同图片之间的相同特征点。
- 目标识别:在计算机视觉中,需要对目标进行识别和跟踪。SIFT 算法可以提取出特征向量,用于判断目标是否出现在图像中,并跟踪目标的位置和姿态。
- 三维重建:在三维重建中,需要从多张图片中提取出相同的特征点来进行匹配和三维重建。SIFT 算法可以提取出稳定的关键点和特征向量,用于匹配不同图片之间的相同特征点,并进行三维重建。
- 相机姿态估计:在计算机视觉中,需要对相机的姿态进行估计。SIFT 算法可以提取出关键点和特征向量,用于计算相机的姿态参数。
总之,SIFT 算法可以用于解决许多计算机视觉领域中的问题,包括图像拼接、目标识别、三维重建、相机姿态估计等任务。
2.2.1 兴趣点
SIFT 特征使用高斯差分函数来定位兴趣点:
其中,Gσ 是上一章中介绍的二维高斯核,Iσ 是使用Gσ 模糊的灰度图像,κ 是决定相差尺度的常数。兴趣点是在图像位置和尺度变化下 D(x,σ) 的最大值和最小值点。这些候选位置点通过滤波去除不稳定点。基于一些准则,比如认为低对比度和位于边上的点不是兴趣点,我们可以去除一些候选兴趣点。
2.2.2 描述子
在计算机视觉中,描述子(Descriptor)通常是指一种用于描述图像或者图像中某个区域的局部特征的向量或矩阵。描述子可以用于图像匹配、图像检索、目标识别、物体跟踪等任务中。
描述子的设计通常考虑以下几个因素:
- 不变性:描述子应该具有一定的不变性,即对于图像的旋转、平移、缩放、光照等变化具有一定的鲁棒性。
- 区分性:描述子应该能够区分不同的图像或者图像区域,即不同的图像区域应该有不同的描述子。
- 稳定性:描述子应该是稳定的,即对于图像中的噪声、失真等因素具有一定的抵抗能力。
- 特征维数:描述子的维数应该足够低,避免计算复杂度过高。
常用的描述子包括 SIFT 描述子、SURF 描述子、ORB 描述子、HOG 描述子等。这些描述子都具有不同的特点和适用范围,可以根据具体任务的需求选择合适的描述子进行使用。
2.2.3 检测兴趣点
SIFT(Scale-Invariant Feature Transform,尺度不变特征变换)算法中的检测兴趣点的主要目的是在不同尺度下寻找稳定的关键点,用于描述图像中的局部特征。SIFT 算法中的检测兴趣点的具体实现可以分为以下两个步骤:
- 构建高斯差分金字塔:首先,采用高斯滤波器在不同尺度下对输入图像进行卷积,得到一组高斯模糊图像。然后,通过相邻的高斯模糊图像相减,得到一组差分图像。这些差分图像可以用于检测局部极值点,即在空间和尺度上都是局部最大或最小的点。
- 局部极值点检测:对于每个尺度的差分图像,通过比较每个像素和它周围 26 个像素在同一尺度和相邻尺度上的值,找到局部极值点。然后,通过对局部极值点进行插值和筛选,得到稳定的关键点,包括关键点的位置、尺度和方向。
SIFT 算法中的检测兴趣点具有尺度不变性、旋转不变性和光照不变性,能够在不同的尺度、旋转角度和光照条件下提取稳定的关键点。因此,SIFT 算法在计算机视觉领域中被广泛应用于图像匹配、目标识别、三维重建等任务中。
使用开源工具包 VLFeat 提供的二进制文件来计算图像的 SIFT 特征 。VLFeat 工具包可以从 http://www.vlfeat.org/ 下载,二进制文件可以在所有主要的平台上运行。
2.2.4 实验部分
检测兴趣点
代码:
import cv2
# 读取图像
img = cv2.imread('J13.jpg')
# 创建SIFT对象
sift = cv2.SIFT_create()
# 检测关键点并计算其描述符
keypoints, descriptors = sift.detectAndCompute(img, None)
# 绘制关键点
img_with_keypoints = cv2.drawKeypoints(img, keypoints, None)
# 将图像缩小到适合屏幕显示的大小
height, width = img_with_keypoints.shape[:2]
max_size = max(height, width)
if max_size > 1080:
scale = 1080 / max_size
img_with_keypoints = cv2.resize(img_with_keypoints, None, fx=scale, fy=scale)
# 显示结果
cv2.imshow('image with keypoints', img_with_keypoints)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下图所示:
总结:
在实现角点检测的实验中,我们使用OpenCV中的SIFT算法来检测图像中的关键点,并使用cv2.drawKeypoints函数在图像上绘制关键点。下面是一些实验总结:
- 使用SIFT算法进行角点检测时,需要先创建一个SIFT对象。可以使用cv2.SIFT_create函数创建SIFT对象。
- 在使用SIFT算法进行角点检测之前,需要将图像转换为灰度图像。可以使用cv2.imread函数,cv2.IMREAD_GRAYSCALE标志读取图像并将其转换为灰度图像,但是我们这里是读取的彩色图像。
- 在使用SIFT算法进行角点检测时,可以通过调整SIFT对象的参数来控制检测到的关键点数量和质量。例如,可以通过调整SIFT对象的nfeatures参数来控制检测到的关键点数量,通过调整SIFT对象的contrastThreshold和edgeThreshold参数来控制关键点的质量。
- 在使用cv2.drawKeypoints函数绘制关键点时,可以通过设置参数来控制绘制的关键点的大小、颜色和显示方式。
- 在绘制关键点之前,最好先将图像缩小到适合屏幕显示的大小,以便更好地观察关键点。
- 在实现角点检测的实验中,我们使用了一张包含多个对象的图像进行实验。在实际应用中,我们可能需要针对不同的对象使用不同的参数进行角点检测,以获得更好的检测效果。
总的来说,实现角点检测的实验可以帮助我们了解SIFT算法的基本原理和应用,并提高我们对计算机视觉算法的理解和实现能力。
匹配描述子:
代码:
import cv2
# 读取图像
img1 = cv2.imread('J2.jpg')
img2 = cv2.imread('J3.jpg')
# 创建SIFT对象并计算关键点和描述子
sift = cv2.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)
# 创建FLANN匹配器
matcher = cv2.FlannBasedMatcher()
# 使用knnMatch函数进行匹配
matches = matcher.knnMatch(des1, des2, k=2)
# 过滤匹配点对
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
# 绘制匹配点对
img3 = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)
# 缩放图像以适应屏幕大小
screen_width = 1280
screen_height = 720
img_height, img_width, _ = img3.shape
scale = min(screen_width / img_width, screen_height / img_height)
resized_img = cv2.resize(img3, None, fx=scale, fy=scale)
# 显示图像
cv2.imshow('matches', resized_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行结果如下图所示:
总结:
- 使用SIFT算法检测关键点:SIFT算法是一种常用的关键点检测算法,它可以检测出图像中的关键点,并计算每个关键点的局部特征描述子。
- 使用FLANN匹配器进行特征匹配:FLANN匹配器是一种常用的特征匹配算法,它可以将两个图像中的特征描述子进行匹配,并返回最佳匹配结果。
- 对匹配点对进行筛选:由于特征匹配算法会产生一些错误匹配,我们需要对匹配点对进行筛选,只保留最佳的匹配点对。
- 绘制匹配点对:使用OpenCV库中的cv2.drawMatches函数,可以将匹配点对绘制在两幅图像之间,以便于观察和分析。
- 适应屏幕大小显示图像:在显示图像时,可以使用OpenCV库中的cv2.resize函数,将图像缩小或放大以适应屏幕大小,以便于观察和分析。
总体来说,这个实验是一个基础的计算机视觉实验,它涵盖了计算机视觉中的很多基本概念和常用算法,对于初学者来说是一个很好的入门实验。同时,通过这个实验,我们也可以了解到OpenCV库的基本使用方法,这对于后续的计算机视觉应用开发也是非常有帮助的。
2.3 匹配地理标记图像
2.3.1 实现步骤
- 收集地理标记图像数据集:首先需要收集一定数量的地理标记图像数据集,例如收集多个不同城市的地标建筑物图像、风景图像等等。
- 提取图像特征:使用SIFT或SURF等特征提取算法,对每张地理标记图像进行特征提取,得到每张图像的特征描述子。
- 建立特征库:将每张地理标记图像的特征描述子存储到一个特征库中,以便后续的图像匹配。
- 读取待匹配图像:读取需要匹配的待匹配图像,并使用特征提取算法提取待匹配图像的特征描述子。
- 特征匹配:使用FLANN或BFMatcher等特征匹配算法,将待匹配图像的特征描述子与特征库中的图像特征描述子进行匹配,得到最佳的匹配结果。
- 显示匹配结果:将匹配结果可视化,例如绘制匹配点对或者直接将匹配的地理标记图像显示在屏幕上。
2.3.2 实验部分
代码:
import cv2 as cv
from pylab import *
import os
import pydotplus as pydot
maxsize = (100, 100) # 定义缩略图的大小
path = r'C:\\Users\\86177\\Desktop\\data'
# 读取整个文件夹的图片
def read_path(pathname):
imgname_list = os.listdir(pathname)
img_list = []
i = 0
# 图片列表
for imgname in imgname_list:
if imgname.endswith('.jpg'):
img = cv.imread(pathname + '/' + imgname)
img_n = cv.resize(img, maxsize, cv.INTER_AREA)
filename = path + str(i) + '.png'
cv.imwrite(filename, img_n) # need temporary files of the right size
i = i + 1
print(i)
return img_list
list = read_path(r'C:\\Users\\86177\\Desktop\\data')
print(list)
# 读取整个文件夹的图片
def read_path(pathname):
imgname_list = os.listdir(pathname)
img_list = []
# 图片列表
for imgname in imgname_list:
if imgname.endswith('.jpg'):
img = cv.imread(pathname + '/' + imgname)
img_list.append(img)
return img_list
img_list = read_path(r'C:\\Users\\86177\\Desktop\\data')
nbr_images = len(img_list)
match_scores = zeros((nbr_images, nbr_images))
for i in range(nbr_images):
for j in range(i, nbr_images): # only compute upper triangle
print('comparing ', i, j)
sift = cv.SIFT_create()
kp1, des1 = sift.detectAndCompute(img_list[i], None)
kp2, des2 = sift.detectAndCompute(img_list[j], None)
# BFMatch匹配
bf = cv.BFMatcher(cv.NORM_L2)
matches = bf.knnMatch(des1, des2, k=2)
# 储存差距小的优秀匹配点
goodMatches = []
for m, n in matches:
if m.distance < 0.5 * n.distance:
goodMatches.append(m)
# 计算优秀匹配点的和
nbr_matches = len(goodMatches)
# 向match_scores赋值
print('number of matches = ', nbr_matches)
match_scores[i, j] = nbr_matches
# 复制
for i in range(nbr_images):
for j in range(i + 1, nbr_images): # no need to copy diagonal 不用复制自我匹配的对角线
match_scores[j, i] = match_scores[i, j]
# 可视化
threshold = 2 #至少2个以上匹配点就可以算是联系
# 不需要有向图
g = pydot.Dot(graph_type='graph')
# 定义缩略图的大小
maxsize = (100, 100)
path = r'C:\\Users\\86177\\Desktop\\data'
#两两配对
for i in range(nbr_images):
for j in range(i + 1, nbr_images):
if match_scores[i, j] > threshold:
filename = path + str(i) + '.png'
g.add_node(pydot.Node(str(i), fontcolor='transparent', shape='rectangle', image=filename))
filename = path + str(j) + '.png'
g.add_node(pydot.Node(str(j), fontcolor='transparent', shape='rectangle', image=filename))
g.add_edge(pydot.Edge(str(i), str(j)))
#绘制S地理标记SIFT匹配图
g.write_jpg('sift.jpg')
运行效果图如下: