计算机视觉实验二——角点检测
一、实验目标
- 分别实现Harris角点检测与SIFT特征提取,对比两者的区别
- 利用SIFT算法实现两幅相近图像的特征匹配
- 实现匹配地理标记图像
二、实验内容
1.分别实现Harris角点检测与SIFT特征提取,对比两者的区别
①Harris 角点检测
# 从一幅Harris响应图像中返回角点
def get_harris_points(harrisim, min_dist=10, threshold=0.1):
# min_dist 为分割角点和图像边界的最少像素数目
# 寻找高于阈值的候选角点
corner_threshold = harrisim.max() * threshold
harrisim_t = (harrisim > corner_threshold) * 1
# 得到候选点的坐标
coords = array(harrisim_t.nonzero()).T
# 获取候选点的 Harris 响应值
candidate_values = [harrisim[c[0], c[1]] for c in coords]
# 对候选点按照 Harris 响应值进行排序
index = argsort(candidate_values)
# 将可行点的位置保存到数组中
allowed_locations = zeros(harrisim.shape)
allowed_locations[min_dist:-min_dist, min_dist:-min_dist] = 1
# 按照 min_distance 原则,选择最佳 Harris 点
filtered_coords = []
for i in index:
if allowed_locations[coords[i, 0], coords[i, 1]] == 1:
filtered_coords.append(coords[i])
allowed_locations[(coords[i, 0] - min_dist):(coords[i, 0] + min_dist),
(coords[i, 1] - min_dist): (coords[i, 1] + min_dist)] = 0
return filtered_coords
# 绘制图像中检测到的角点
def plot_harris_points(image, filtered_coords):
figure()
gray()
imshow(image)
plot([p[1] for p in filtered_coords], [p[0] for p in filtered_coords], '*')
axis('off')
show()
打开图像,转换成灰度图像。然后,计算响应函数,基于响应值选择角点。最后,在原始图像中覆盖绘制检测出的角点。
原始图像1:
使用阈值0.01、0.05 和 0.1 检测出的角点:
原始图像2:
使用阈值0.01、0.05 和 0.1 检测出的角点:
可见,阈值增大,角点响应值R减小,降低角点检测的灵敏性,被检测角点数量减少;阈值减小,角点响应值R增大,增加角点检测的灵敏性,被检测角点的数量增加。
②SIFT特征提取
# 处理一幅图像,然后将结果保存在文件中
def process_image(imagename, resultname, params="--edge-thresh 10 --peak-thresh 5"):
if imagename[-3:] != 'pgm':
# 创建一个pgm文件
im = Image.open(imagename).convert('L')
im.save('tmp.pgm')
imagename = 'tmp.pgm'
cmmd = str("sift.exe " + imagename + " --output=" + resultname + " " + params)
os.system(cmmd)
print('processed', imagename, 'to', resultname)
# 读取特征属性值,然后将其以矩阵的形式返回
def read_features_from_file(filename):
f = loadtxt(filename)
return f[:, :4], f[:, 4:] # 特征位置,描述子
# 将特征位置和描述子保存到文件中
def write_featrues_to_file(filename, locs, desc):
savetxt(filename, hstack((locs, desc)))
# 显示带有特征的图像
def plot_features(im, locs, circle=False):
# 输入:im(数组图像),locs(每个特征的行、列、尺度和朝向)
def draw_circle(c, r):
t = arange(0, 1.01, .01) * 2 * pi
x = r * cos(t) + c[0]
y = r * sin(t) + c[1]
plot(x, y, 'b', linewidth=2)
imshow(im)
if circle:
for p in locs:
draw_circle(p[:2], p[2])
else:
plot(locs[:, 0], locs[:, 1], 'ob')
axis('off')
③二者的区别
Harris角点检测是一种基于图像灰度变化的角点检测方法,它通过计算图像中每个像素点的灰度值变化来判断该点是否为角点。Harris角点检测适用于对图像中的角点进行检测和匹配,例如在图像拼接、目标跟踪等应用中。
而SIFT特征提取是一种基于尺度空间和梯度方向的特征提取方法,它通过在不同尺度下计算图像中每个像素点的梯度方向和大小来提取特征点,并生成具有旋转不变性和尺度不变性的特征描述子。SIFT特征提取适用于对图像中的物体进行识别和匹配,例如在图像检索、目标识别等应用中。
因此,Harris角点检测和SIFT特征提取的区别在于它们的原理和应用场景不同。Harris角点检测适用于角点检测和匹配,而SIFT特征提取适用于物体识别和匹配。
2.利用SIFT算法实现两幅相近图像的特征匹配
# 处理一幅图像,然后将结果保存在文件中
def process_image(imagename, resultname, params="--edge-thresh 10 --peak-thresh 5"):
if imagename[-3:] != 'pgm':
# 创建一个pgm文件
im = Image.open(imagename).convert('L')
im.save('tmp.pgm')
imagename = 'tmp.pgm'
cmmd = str("sift.exe " + imagename + " --output=" + resultname + " " + params)
os.system(cmmd)
print('processed', imagename, 'to', resultname)
def read_features_from_file(filename): # 读取特征属性值,然后将其以矩阵的形式返回
f = loadtxt(filename)
return f[:, :4], f[:, 4:] # 特征位置,描述子
# 将特征位置和描述子保存到文件中
def write_featrues_to_file(filename, locs, desc):
savetxt(filename, hstack((locs, desc)))
# 显示带有特征的图像
def plot_features(im, locs, circle=False):
# 输入:im(数组图像),locs(每个特征的行、列、尺度和朝向)
def draw_circle(c, r):
t = arange(0, 1.01, .01) * 2 * pi
x = r * cos(t) + c[0]
y = r * sin(t) + c[1]
plot(x, y, 'b', linewidth=2)
imshow(im)
if circle:
for p in locs:
draw_circle(p[:2], p[2])
else:
plot(locs[:, 0], locs[:, 1], 'ob')
axis('off')
# 对于第一幅图像中的每个描述子,选取其在第二幅图像中的匹配
def match(desc1, desc2):
# 输入:desc1(第一幅图像中的描述子),desc2(第二幅图像中的描述子)
desc1 = array([d / linalg.norm(d) for d in desc1])
desc2 = array([d / linalg.norm(d) for d in desc2])
dist_ratio = 0.6
desc1_size = desc1.shape
matchscores = zeros((desc1_size[0], 1), 'int')
desc2t = desc2.T # 预先计算矩阵转置
for i in range(desc1_size[0]):
dotprods = dot(desc1[i, :], desc2t) # 向量点乘
dotprods = 0.9999 * dotprods
# 反余弦和反排序,返回第二幅图像中特征的索引
indx = argsort(arccos(dotprods))
# 检查最近邻的角度是否小于dist_ratio乘以第二近邻的角度
if arccos(dotprods)[indx[0]] < dist_ratio * arccos(dotprods)[indx[1]]:
matchscores[i] = int(indx[0])
return matchscores
# 双向对称版本的match()
def match_twosided(desc1, desc2):
matches_12 = match(desc1, desc2)
matches_21 = match(desc2, desc1)
ndx_12 = matches_12.nonzero()[0]
# 去除不对称的匹配
for n in ndx_12:
if matches_21[int(matches_12[n])] != n:
matches_12[n] = 0
return matches_12
# 返回将两幅图像并排拼接成的一幅新图像
def appendimages(im1, im2):
# 选取具有最少行数的图像,然后填充足够的空行
rows1 = im1.shape[0]
rows2 = im2.shape[0]
if rows1 < rows2:
im1 = concatenate((im1, zeros((rows2 - rows1, im1.shape[1]))), axis=0)
elif rows1 > rows2:
im2 = concatenate((im2, zeros((rows1 - rows2, im2.shape[1]))), axis=0)
return concatenate((im1, im2), axis=1)
# 显示一幅带有连接匹配之间连线的图片
def plot_matches(im1, im2, locs1, locs2, matchscores, show_below=True):
# 输入:im1, im2(数组图像), locs1,locs2(特征位置),matchscores(match()的输出),
# show_below(如果图像应该显示在匹配的下方)
im3 = appendimages(im1, im2)
if show_below:
im3 = vstack((im3, im3))
imshow(im3)
cols1 = im1.shape[1]
for i in range(len(matchscores)):
if matchscores[i] > 0:
plot([locs1[i, 0], locs2[matchscores[i, 0], 0] + cols1], [locs1[i, 1], locs2[matchscores[i, 0], 1]], 'c')
axis('off')
im1f = '1.jpg'
im2f = '2.jpg'
im1 = array(Image.open(im1f))
im2 = array(Image.open(im2f))
process_image(im1f, 'out_sift_1.txt')
l1, d1 = read_features_from_file('out_sift_1.txt')
figure()
gray()
subplot(121)
plot_features(im1, l1, circle=False)
process_image(im2f, 'out_sift_2.txt')
l2, d2 = read_features_from_file('out_sift_2.txt')
subplot(122)
plot_features(im2, l2, circle=False)
matches = match_twosided(d1, d2)
print('{} matches'.format(len(matches.nonzero()[0])))
figure()
gray()
plot_matches(im1, im2, l1, l2, matches, show_below=True)
show()
两幅相似图像共匹配到2138个特征点。
3.实现匹配地理标记图像
# 获取图像列表
def get_imlist(path):
return [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.jpg')]
download_path = "D:/Computer Vision/ch02/ch02/pics"
path = "D:/Computer Vision/ch02/ch02/pics"
# 获取文件名列表`
imlist = get_imlist(download_path)
nbr_images = len(imlist)
print(nbr_images)
# 提取特征
featlist = [imname[:-3] + 'sift' for imname in imlist]
for i, imname in enumerate(imlist):
sift.process_image(imname, featlist[i])
matchscores = zeros((nbr_images, nbr_images))
for i in range(nbr_images):
for j in range(i, nbr_images): # only compute upper triangle
print('comparing ', imlist[i], imlist[j])
l1, d1 = sift.read_features_from_file(featlist[i])
l2, d2 = sift.read_features_from_file(featlist[j])
matches = sift.match_twosided(d1, d2)
nbr_matches = sum(matches > 0)
print('number of matches = ', nbr_matches)
matchscores[i, j] = nbr_matches
print("The match scores is: \n", matchscores)
# 复制值
for i in range(nbr_images):
for j in range(i + 1, nbr_images): # no need to
matchscores[j, i] = matchscores[i, j]
# 可视化
# min number of matches needed to create link
threshold = 2
# don't want the default directed graph
g = pydot.Dot(graph_type='graph')
for i in range(nbr_images):
for j in range(i + 1, nbr_images):
if matchscores[i, j] > threshold:
# 图像对中的第一幅图像
im = Image.open(imlist[i])
im.thumbnail((100, 100))
filename = path + str(i) + '.jpg'
im.save(filename) # 需要一定大小的临时文件
g.add_node(pydot.Node(str(i), fontcolor='transparent', shape='rectangle', image=filename))
# 图像对中的第二幅图像
im = Image.open(imlist[j])
im.thumbnail((100, 100))
filename = path + str(j) + '.jpg'
print(filename)
im.save(filename) # 需要一定大小的临时文件
g.add_node(pydot.Node(str(j), fontcolor='transparent', shape='rectangle', image=filename))
g.add_edge(pydot.Edge(str(i), str(j)))
g.write_png('output.jpg')
对要匹配的图像提取局部描述子。此时使用 SIFT 特征描述子,对这些图像使用 SIFT 特征提取代码进行处理,并且将特征保存在和图像同名(但文件名后缀是.sift,而不是.jpg)的文件中。
可视化连接结果: