在之前的文章图像处理中常用的相似度评估指标中,我们介绍了通过MSE、PSNR、SSIM以及UQI等指标来计算图像之间的相似度。但是,在使用这些算法计算图像相似的时候两张图像的size必须一致,而且这些算法对于图像的旋转、缩放、平移、仿射变换以及光照强度等都是不鲁棒的。
这篇文章我们来介绍几个更加鲁棒的图像相似度计算的算法,SIFT、SURF以及ORB三种算法,它们都是基于特征点的提取来计算图像之间的相似度。
注意:因为需要用到SHIFT、SURF以及ORB算法,所以需要安装 opencv-python==3.4.2.16和opencv-contrib-python==3.4.2.16,如果是高本版opencv由于license的问题可能会无法使用到这些算法
SIFT (Scale-Invariant Feature Transform):尺度不变特征变换,是用于图像处理中的一种描述。这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征的描述子。在图像的特征匹配和特征提取中,经常使用到。
旋转、尺度缩放、亮度变换保持不变性,对于噪声、视角变换、仿射变换也能保持一定的不变性下面我们用python来提取图像的SIFT特征
import cv2
import numpy as np
from matplotlib import pyplot as plt
def extract_sift_feature(img):
#创建SIFT检测器
sift = cv2.xfeatures2d.SIFT_create()
#提取图像的特征点和描述子信息
keypoints, descriptors = sift.detectAndCompute(img, None)
return keypoints,descriptors
def show_img_sift_dot(img):
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#提取图像的SIFT特征点信息
keypoints, descriptors = extract_sift_feature(img)
#在图像上绘制特征点的位置
img1 = cv2.drawKeypoints(rgb_img, keypoints, img)
plt.imshow(img1)
plt.show()

图像上面的绘制出来的圆圈就是使用SIFT算法提取的特征点
def draw_match_image(img1,img2):
rgb_img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB)
rgb_img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
#提取图像的SIFT关键点信息
keypoints1, descriptors1 = extract_sift_feature(img1)
keypoints2, descriptors2 = extract_sift_feature(img2)
#创建一个关键点匹配器,采用L1距离
bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)
#关键点匹配,通过计算两张图像提取的描述子之间的距离
matches = bf.match(descriptors1, descriptors2)
#根据描述子之间的距离进行排序
matches = sorted(matches, key=lambda x: x.distance)
#只匹配前50个关键点
img3 = cv2.drawMatches(rgb_img1, keypoints1, rgb_img2, keypoints2, matches[:50], rgb_img2, flags=2)
plt.imshow(img3)
plt.show()

上面是两张图片,左图是原图,右图是在原图的基础上增加了高斯噪声,直线连接了两张图像的相似的关键点
def cal_SIFT_sim(img1,img2):
#将图片转换为灰度图
img1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
#提取图片的SIFT特征
keypoints1,descriptors1 = extract_sift_feature(img1)
keypoints2,descriptors2 = extract_sift_feature(img2)
#创建一个匹配器
bf = cv2.BFMatcher()
#记录图1和图2的匹配的关键点
matches1 = bf.knnMatch(descriptors1,descriptors2,k=2)
top_results1 = []
for m,n in matches1:
if m.distance < 0.7 * n.distance:
top_results1.append(m)
#记录图2和图1匹配的关键点
matches2 = bf.knnMatch(descriptors2,descriptors1,k=2)
top_results2 = []
for m,n in matches2:
if m.distance < 0.7 * n.distance:
top_results2.append(m)
#从匹配的关键点中选择出有效的匹配
#确保匹配的关键点信息在图1和图2以及图2和图1是一致的
top_results = []
for m1 in top_results1:
m1_query_idx = m1.queryIdx
m1_train_idx = m1.trainIdx
for m2 in top_results2:
m2_query_idx = m2.queryIdx
m2_train_idx = m2.trainIdx
if m1_query_idx == m2_train_idx and m1_train_idx == m2_query_idx:
top_results.append(m1)
#计算图像之间的相似度
#通过计算两张图片之间的匹配的关键点的个数来计算相似度
image_sim = len(top_results) / min(len(keypoints1),len(keypoints2))
return image_sim
def extract_SIFT_vector(img,vector_size):
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
keypoints = sift.detect(gray_img, None)
# 根据关键点的返回值进行排序,越大越好
keypoints = sorted(keypoints, key=lambda x: -x.response)
img_kps = keypoints[:vector_size]
# 根据关键点来计算描述子向量
kps, des = sift.compute(gray_img, img_kps)
# 将向量展开为1维的
vector = des.flatten()
# 检查提取特征的长度,SIFT特征一个关键点的描述子是128维
vector_len = vector_size * 128
# 对于提取图片特征向量长度不够的使用0进行补充
if vector.size < vector_len:
vector = np.concatenate(vector, np.zeros(vector_len - vector.size))
return vector
import scipy.spatial as T
#设置提取特征向量特征的个数
vector_size = 30
img_path1 = "demo.png"
img_path2 = "demo_gauss_noise.png"
img1 = cv2.imread(img_path1)
img2 = cv2.imread(img_path2)
#提取图片的特征向量
img1_vector = extract_SIFT_vector(img1,vector_size).reshape(-1,128*vector_size)
img2_vector = extract_SIFT_vector(img2,vector_size).reshape(-1,128*vector_size)
#计算图片之间的相似度
sim = T.distance.cdist(img1_vector, img2_vector, 'cosine')
print(sim)#0.411
这篇文章我们主要介绍了如何使用opencv来提取图片的SIFT特征,以及如何来使用SIFT特征点来进行图像匹配和图像的相似度计算