基于SIFT特征的图像特征提取
图像检索原理概述
首先,我们需要理解什么是图像检索。简而言之,它就是从图片数据库中提取符合条件的图片。在研究图像检索技术时,默认情况下依据描述图像内容的方式可分为两大类:
一类是基于文本的图像检索技术,简称TBIR,
一类为基于内容的图像检索技术,简称CBIR。
两类图像检索技术
该文本图像检索技术(TBIR),其主要原理在于根据文本描述获取相关数据;
该内容语义图像检索技术(CBIR),借助图片的颜色特征、纹理特征以及所包含的对象类型等细节信息来进行匹配;
基于图像的内容语义的信息 retrieval 包括 same object image retrieval 和 same category image retrieval,在这两类 retrieval 任务中分别执行以下操作:对于 same object image retrieval 任务来说,则是完成同一物体在不同场景或摄像头下获取多幅图片;而对于 same category image retrieval 任务,则是完成同一类别内图片的获取。例如,在 pedestrian retrieval 中所处理的是同一身份但在不同场景和不同摄像头拍摄下的多幅图片属于 same object 的情况;而在 3D shape retrieval 中则是处理同一类物品如飞机等的情况。
基于SFIT的图像特征提取
SIFT被称为尺度不变特征转换(Scale-invariant feature transform),属于一种计算机视觉算法。该算法用于检测和描述图像中的局部特征。该方法通过在空间尺度上寻找极值点,并提取这些点的位置、尺寸以及旋转特性来实现特征描述。该方法由David Lowe于1999年首次提出,在2004年进一步完善并进行了总结。
其涵盖广泛的应用领域包括物体会识别、机器人地图感知系统与导航模块、影像拼接技术、3D模型构建方案、手勢辨識算法研究、影像追蹤技術以及動作對比分析等
通过描述和检测局部影像特征来识别物体,在计算机视觉领域中具有重要应用价值
SIFT算法的核心在于通过多级尺度空间定位关键特征,并确定这些特征的方向。该算法通过精确识别高度稳定的且具有明确几何特性的关键区域(如角质层、边缘带以及暗部亮点与亮部暗纹),从而实现对外界条件干扰下的稳定检测。
opencv中的SIFT算法
下面为一个简单算法样例
import cv2
import numpy as np
img = cv2.imread('home.jpg')
gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
sift = cv2.SIFT()
kp = sift.detect(gray,None)
img=cv2.drawKeypoints(gray,kp)
cv2.imwrite('sift_keypoints.jpg',img)
该函数 sift.detect() 能够识别图像中的关键点。若需仅在图像的一个特定区域搜索,则可创建一个掩模图作为参数传递给该函数。返回的对象是一个具有丰富特性的特殊数据类型体(如 A S.keypoints),其中包含了该特征点的空间位置信息(x, y 坐标)、局部领域的重要程度以及确定其朝向的角度信息等细节信息。OpenCV 提供了名为 cv2.drawKeyPoints() 的绘图功能模块,在调用此函数时会自动将单个特征点标记为一个小圆圈,默认情况下仅显示特征点的基本符号表示形式;若设置 cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS 参数,则会显示特征点的实际尺寸大小的小圆圈外加其指向的方向信息标记(如箭头符号)。

关于计算关键点描述符,OpenCV 提供了两种方法。
基于关键点已被找到,可以通过该函数 sift.compute() 来计算这些关键点的描述符。例如:kp, des = sift.compute(gray, kp)
在尚未识别出关键点的情况下,可以通过调用函数 sift.detectAndCompute() 来完成对图像中特征点的提取与描述符的计算这一操作。该算法能够一次性有效地提取并计算出所有关键点及其描述符。其中 kp 代表提取的关键点列表,在此过程中会被赋值为一个包含所有检测到的关键点的列表。其中 des 代表提取的关键点对应的描述向量,在此实现中将被赋值为一个 Numpy 数组(或向量),其大小为每个关键点的维度乘以128
代码框架
数据集
这里我采用了101_ObjectCategories图像用于本次模型训练资料。该数据集的具体下载链接为:http://www.vision.caltech.edu/Image_Datasets/Caltech101/。
代码运行环境需要
- python 3.7 (Anaconda python)
- Numpy 1.16.2
- Matplotlib 3.1.1
- opencv-contrib-python 3.4.2.16
运行方法
把run模块中的image_path更改为对应的文件路径运行即可
算法流程图

源代码
# Developer:Fazzie
# Time: 2020/6/1420:25
# File name: SIFT.py
# Development environment: Anaconda Python
import cv2
import numpy as np
import scipy
import _pickle as pickle
import random
import os
from matplotlib import pyplot as plt
import scipy.spatial
# 特征提取模块
def feature_extract(image_path,vector_size=32):
img = cv2.imread(image_path,1)#读取图片
# 显示图片
# cv2.imshow('image',img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
# SIFT特征提取
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray, None)
# 画出特征点并保存
img = cv2.drawKeypoints(gray, kp, img)
cv2.imwrite('sift_keypoints.jpg', img)
# 根据关键点的返回值进行排序(越大越好)
kp = sorted(kp, key=lambda x: -x.response)[:vector_size]
# 计算描述符向量
kp, des = sift.compute(gray, kp)
# 将其放在一个大的向量中,作为我们的特征向量
des = des.flatten()
# 使描述符的大小一致
# 描述符向量的大小为128
needed_size = (vector_size * 128)
if des.size < needed_size:
# 如果少于32个描述符,则在特征向量后面补零
des = np.concatenate([des, np.zeros(needed_size - des.size)])
return des
# 数据存储
def batch_extractor(images_path, pickled_db_path="features.pck"):
files = [os.path.join(images_path, p) for p in sorted(os.listdir(images_path))]
result = {}
for f in files:
print('Extracting features from image %s' % f)
name = f.split('/')[-1].lower()
result[name] = feature_extract(f)
# 将特征向量存于pickled 文件
with open(pickled_db_path, 'wb') as fp:
pickle.dump(result, fp)
# 图像特征匹配模块
class Matcher(object):
def __init__(self, pickled_db_path="features.pck"):
with open(pickled_db_path,"rb") as fp:
self.data = pickle.load(fp)
self.names = []
self.matrix = []
for k, v in self.data.items():
self.names.append(k)
self.matrix.append(v)
self.matrix = np.array(self.matrix)
self.names = np.array(self.names)
def cos_cdist(self, vector):
# 计算待搜索图像与数据库图像的余弦距离
v = vector.reshape(1, -1)
return scipy.spatial.distance.cdist(self.matrix, v, 'cosine').reshape(-1)
def match(self, image_path, topn=5):
features = feature_extract(image_path)
img_distances = self.cos_cdist(features)
# 获得前5个记录
nearest_ids = np.argsort(img_distances)[:topn].tolist()
nearest_img_paths = self.names[nearest_ids].tolist()
return nearest_img_paths, img_distances[nearest_ids].tolist()
def show_img(path):
img = cv2.imread(path,1)
plt.imshow(img)
plt.show()
# 主程序
def run():
images_path = 'E:/Code/SFIT/101_ObjectCategories/panda/'
files = [os.path.join(images_path, p) for p in sorted(os.listdir(images_path))]
# 随机获取1张图
sample = random.sample(files, 1)
batch_extractor(images_path)
ma = Matcher('features.pck')
for s in sample:
print('Query image ==========================================')
show_img(s)
names, match = ma.match(s, topn=3)
print('Result images ========================================')
for i in range(3):
# 我们得到了余弦距离,向量之间的余弦距离越小表示它们越相似,因此我们从1中减去它以得到匹配值
print('Match %s' % (1 - match[i]))
show_img(os.path.join(images_path, names[i]))
run()
可能遇见的问题和解决方案
opencv库SFIT函数不可用(Error:cv2 has no attribute SIFT)
根本原因是,在cv2.4版本之后的后续版本中...
解决方案:安装OpenCV contrib模块的步骤如下:首先下载相应的wheel文件;然后将该wheel文件放置到Anaconda目录下的third-party包中的site-packages目录中;最后通过命令行使用pip安装该wheel文件即可完成安装
-
pickle模块不可用
在Python 3中,pickle模块已经集成到_pickle中;可以直接导入_pickle模块。- 可以考虑使用SURF算法代替SIFT,进一步提高运算速率
如果想下载源码,可以访问https://github.com/Fazziekey/opencv_SIFT
