Python计算机视觉(二)—局部图像描述子

2.1 Harris角点检测器

Harris角点检测算法的主要思想是 :如果像素周围显示存在多于一个方向的边,认为该点为兴趣点,称为角点。

角点的特征

局部窗口沿各方向移动,均产生明显变化的点;图像局部曲率突变的点;轮廓之间的交点;对于同一场景,即使视角发生变化,通常具备稳定性质的特征;该点邻域的像素点无论在梯度方向上还是其梯度幅值上有着较大变化。

角点检测基本思想

小窗口沿任意方向移动时,角点邻域图像灰度值都有明显变化。
在这里插入片描述

Harris角点检测器数学思想

可以通过一个函数来判断是否为角点:


在这里插入图片描述

w(x,y)表示矩形窗口,I(x,y)表示该点灰度值,u与v表示窗口的偏移量,若在任意方向都存在着较大灰度的变化,则判断该点是角点。我们可以使用高斯权重矩阵来突出变化较大的点,赋予更大的权值。
我们可以使用一阶泰勒公式来简化上述的函数:
在这里插入图片描述
用矩阵表示:
在这里插入图片描述

M=\begin{pmatrix} I_{x}^{2} &I_{x} I_{y}\\ I_{y} I_{x} & I_{y}^{2} \end{pmatrix},求解特征值\left | \lambda E-M \right |=0,可得特征值\lambda _{1}\lambda _{2}

特点:

Harris角点检测算子具有旋转不变性

Harris角点检测算子对灰度平移和灰度尺度变化不敏感

扫描二维码关注公众号,回复: 15924420 查看本文章

Harris角点检测算子不具有尺度不变性

实现角点检测代码 

该算法采用python-opencv实现Harris角点检测

import numpy as np
import cv2 as cv

# 读取图像
im_name = 'jmu/3.jpg'
im = cv.imread(im_name)
# 图像灰度化,并进行浮点数运算
im_gray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
im_gray = np.float32(im_gray)
# Harris算法检测角点
'''
cv2.cornerHarris(src, blockSize, ksize,k)
函数参数含义:
src:输入图像,通常为灰度图像
blockSize:角点检测算法使用的邻域窗口大小
ksize:Sobel 算子的卷积核大小,用于计算图像的梯度信息,一般为奇数
k:角点响应函数中的自由参数,一般取值为[0,0.04,0.06]
'''
# 角点响应图像
dst = cv.cornerHarris(im_gray, 5, 5, 0.06)
# 标准化响应图像
dst_norm = np.empty_like(dst)
cv.normalize(dst, dst_norm, alpha=0, beta=255, norm_type=cv.NORM_MINMAX)

# 将响应图像转换为8位无符号整数类型
dst_norm = np.uint8(dst_norm)

# 显示响应图像
cv.imshow('Corner Response Image', dst_norm)
cv.waitKey(0)

# 将原图像角点响应值大于阈值t的角点标记为红色
im[dst > 0.005* dst.max()] = [0, 0, 255]

# 防止图片输出时过大
cv.namedWindow('t=0.005', cv.WINDOW_KEEPRATIO)
cv.imshow('t=0.005', im)
cv.waitKey(0)
cv.destroyAllWindows()

 结果:

角点响应图像

  

t=0.001
t=0.005
t=0.05

结果分析:
dst = cv.cornerHarris(im_gray, 5, 5, 0.06)中的0.06代表角点响应函数中的自由参数k,改变k值,可以改变被检测角点数量。增大k的值,将减小角点响应值R,降低角点检测的灵敏性,减少被检测角点的数量;减小k的值,将增大角点响应值R,增加角点检测的灵敏性,增加被检测角点的数量。而随着阈值t的增加,被标记的角点数也在减少。

2.2 SIFT(尺度不变特征变换)

SIFT特征包括兴趣点检测器和描述子,对于尺度、旋转和亮度都具有不变性。

兴趣点

SIFT 特征使用高斯差分函数来定位兴趣点:

D(x,\sigma )=\left [ G_{k\sigma }(x)- G_{\sigma }\right(x) ]*I(x)=\left [ G_{k\sigma }- G_{\sigma }\right ]*I(x)=I_{k\sigma }-I_{\sigma }

其中G_{\sigma }是二维高斯核,I_{\sigma }是使用G_{\sigma }模糊的灰度图像, k是决定相差尺度的常数。

兴趣点是在图像位置和尺度变化下 D(x,\sigma ) 的最大值和最小值点。这些候选位置点通过滤波去除不稳定点。

描述子

兴趣点位置描述子给出了兴趣点的位置和尺度信息。为了实现旋转不变性,基于每个点周围图像梯度的方向和大小, SIFT 描述子又引入了参考方向。SIFT 描述子使用主方向描述参考方向。主方向使用方向直方图(以大小为权重)来度量。为了对图像亮度具有稳健性,SIFT 描述子使用图像梯度。 SIFT 描述子在每个像素点附近选取子区域网格,在每个子区域内计算图像梯度方向直方图。每个子区域的直方图拼接起来组成描述子向量。 SIFT 描述子的标准设置使用 4× 4 的子区域,每个子区域使用 8 个小区间的方向直方图,会产生共128 个小区间的直方图( 4× 4× 8=128)。


SIFT算法特征匹配的步骤

检测兴趣点并计算描述子

sift = cv.SIFT_create()
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)

匹配描述子

bf = cv.BFMatcher(cv.NORM_L2)
matches = bf.knnMatch(des1, des2, k=2)

实现特征匹配代码

该算法采用python-opencv实现SIFT特征匹配

import cv2 as cv

# 读取图像
img1 = cv.imread(r'jmu/3.jpg')
img2 = cv.imread(r'jmu/4.jpg')

# 创建SIFT特征点检测
sift = cv.SIFT_create()

# 检测兴趣点并计算描述子
kp1, des1 = sift.detectAndCompute(img1, None)
kp2, des2 = sift.detectAndCompute(img2, None)

# 使用OpenCV中的BFMatcher算法进行特征匹配,并返回最近邻和次近邻匹配的结果
bf = cv.BFMatcher(cv.NORM_L2)
matches = bf.knnMatch(des1, des2, k=2)

# 储存特征匹配最好的优质匹配点对
'''基于距离阈值选择优质匹配点对,如果最近邻m的距离小于0.7倍的次近邻n的距离,
则认为这个匹配点对是优质的,将它存储在goodMatchs列表中。'''
goodMatchs = []
for m, n in matches:
    if m.distance < 0.65 * n.distance:
        goodMatchs.append(m)

# 可视化特征匹配结果,并保存
pic3 = cv.drawMatches(img1=img1, keypoints1=kp1, img2=img2, keypoints2=kp2, matches1to2=goodMatchs, outImg=None)
cv.imwrite(r'/Users/xionglulu/Downloads/project1/m1.jpg', pic3)

结果: 

特征匹配图像

结果分析:根据特征匹配的结果可以看出,相较于Harris算法,SIFT算法对图像特征点的提取更全面,但在匹配时也存在错误,对于相似度过高的点匹配效果并不好。通过改变距离阈值大小,可以改变匹配点对数量,阈值越小,匹配数量越少,同时准确率也越高。

2.3 匹配地理标记图像

输入一组包含三个不同地点的图像,运用SIFT特征匹配算法,对同一地点图像进行匹配并分类。

代码

首先保存略缩图

import os
import cv2 as cv
from pylab import *

maxsize = (100, 100)  # 定义缩略图的大小
path = r'/Users/xionglulu/Downloads/project1/jmu'

# 读取整个文件夹的图片
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) 
            i = i + 1
    return img_list

list = read_path(r'/Users/xionglulu/Downloads/project1/jmu')
print(list)

接下来匹配地理标记图像

import os
import cv2 as cv
from pylab import *
import pydotplus as pydot

# 读取整个文件夹的图片
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'/Users/xionglulu/Downloads/project1/jmu')
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): 
        print('comparing ', i, j)
        sift = cv.xfeatures2d.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):  # 不用复制自我匹配的对角线
        match_scores[j, i] = match_scores[i, j]
# 可视化
threshold = 2  #  至少2个以上匹配点就可以算是联系
g = pydot.Dot(graph_type='graph')  # 不需要有向图
maxsize = (100, 100)  # 定义缩略图的大小
path = r'/Users/xionglulu/Downloads/project1/jmu'
#两两配对
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('jmuv.jpg')

 结果:

匹配地理标记图像

 结果分析:从结果图可以看出,匹配存在一些错误,钟楼和中山纪念馆错误匹配,陆大楼和中山纪念馆也存在错误匹配,这可能是三座建筑物有十分相似的地方。

猜你喜欢

转载自blog.csdn.net/summer_524/article/details/130114804