有时候,图像的像素大小对角点存在一定的影响。比如图像越小,角点看上去趋向近似一条直线,这样很容易造成角点的丢失。如果按照上述的检测方法,会造成角点检测结果不相符,因此引入DoG和SIFT算法进行检测。Opencv的SIFT类是DoG和SIFT算法组合。
DoG是对同一图像使用不同高斯滤波器所得的结果。
SIFT是通过一个特征向量来描述关键点周围区域的情况。
对比SURF和SIFT算法,ORB算法更处于起步阶段,在2011年才首次发布。但比前两者的速度更快。ORB基于FAST关键点检测和BRIEF的描述符技术相结合,因此我们先了解FAST和BRIEF。
FAST:特征检测算法。
BRIEF:只是一个描述符,这是图像一种表示方式,可以比较两个图像的关键点描述符,可作为特征匹配的一种方法。
暴力匹配:比较两个描述符并产生匹配结果。
K-最近邻匹配(KNN)
FLANN匹配,相对暴力匹配BFMatcher来讲,这匹配算法比较准确、快速和使用方便。FLANN具有一种内部机制,可以根据数据本身选择最合适的算法来处理数据集。值得注意的是,FLANN匹配器只能使用SURF和SIFT算法。
在上述的例子中,我们只是将检测的关键点进行勾画,在这例子中,将使用SIFT SURF检测关键点之外,还将两图进行匹配,匹配的图像如下:
SIFT FLANN 0.7
SIFT FLANN 0.6
SIFT FLANN 0.4
SURF FLANN 0.4
#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@Copyright: Jihua Lab 2021.
@File Name : 1. SIFT_Feature.py
@Description:
@Create Time : 2021/1/28 0028 10:04
@Author : HaoWANG, Foshan,China
@Email : haowanghk@163.com
@Software : Pycharm
@Version: V1.0
"""
import os
from os import walk
from os.path import join
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
class imgFeature(object):
def __init__(self, img_path):
self.__img_path = img_path
self.__file_info = os.path.split(os.path.abspath(img_path))
self.__file_folder = self.__file_info[0]
self.__file_name = self.__file_info[1]
def get_img_path(self):
# 返回图像路径
return self.__img_path
def get_img_folder(self):
# 返回图像存放绝对路径文件件
return self.__file_folder
def img_path_check(self):
# 读取图像路径合法性检查:输出False or True
img_path = self.get_img_path()
if not (os.path.exists(img_path) and
os.access(img_path, os.R_OK)):
# 判断文件路径是否存在; 检查文件是否可读
print("ERROR : File in path {} is not exists or un-accessible to read"
.format(img_path))
return False
else:
print("--------------------------")
print("File in path {} is accessible to read"
.format(img_path))
return True
# end of img_path_check(img_path)
def cv_img_read(self, check = True):
if check:
if not self.img_path_check():
print("Image Read ERROR : from img_path_check( ) !")
return -1;
else:
# 读取图像数据
return cv.imread(self.get_img_path())
else:
return cv.imread(self.get_img_path())
def create_descriptors(self):
path = self.get_img_path()
files = []
for (dirpath, dirnames, filenames) in walk(path):
files.extend(filenames)
for f in files:
if '.jpg' in f:
self.save_descriptor(path, f, cv.xfeatures2d.SIFT_create())
print("--------------------------")
print("File in path {} is accessible to read"
.format(join(dirnames,filenames)))
return True
def save_descriptor(self,feature_detector):
# 判断图片是否为npy格式
img_path = self.get_img_path()
if img_path.endswith("npy"):
return
# 读取图片并检查特征
img = self.cv_img_read(check=False)
keypoints, descriptors = feature_detector.detectAndCompute(img, None)
# 设置文件名并将特征数据保存到npy文件
descriptor_file = img_path.replace("jpg", "npy")
np.save(descriptor_file, descriptors)
print("--------------------------")
print("Descriptor File in path {} is saved".format(descriptor_file))
def sift_feature(img_src1, img_src2):
'''
:param img_src1: RGB 三通道彩色图像
:param img_src2: RGB三通道彩色图像
:return: None
'''
# 彩色图像转灰度图
img_gray1 = cv.cvtColor(img_src1, cv.COLOR_BGR2GRAY)
img_gray2 = cv.cvtColor(img_src2, cv.COLOR_BGR2GRAY)
# 创建sift特征点对象
# sift = cv.xfeatures2d.SIFT_create()
sift = cv.xfeatures2d.SURF_create(float(4000))
# 将图片进行SURF计算,并找出角点keypoints,keypoints是检测关键点
# descriptor是描述符,这是图像一种表示方式,可以比较两个图像的关键点描述符,可作为特征匹配的一种方法。
keypoints_1, descriptor_1 = sift.detectAndCompute(img_gray1, None)
keypoints_2, descriptor_2 = sift.detectAndCompute(img_gray2, None)
dst_1 = cv.drawKeypoints(image=img_src1,
outImage=img_src1,
keypoints=keypoints_1,
flags=cv.DRAW_MATCHES_FLAGS_DEFAULT,
color=(100, 13, 236))
dst_2 = cv.drawKeypoints(image=img_src2,
outImage=img_src2,
keypoints=keypoints_2,
flags=cv.DRAW_MATCHES_FLAGS_DEFAULT,
color=(51, 163, 236))
# cv2.drawKeypoints() 函数主要包含五个参数:
# image: 原始图片
# keypoints:从原图中获得的关键点,这也是画图时所用到的数据
# outputimage:输出
# color:颜色设置,通过修改(b,g,r)的值,更改画笔的颜色,b=蓝色,g=绿色,r=红色。
# flags:绘图功能的标识设置,标识如下:
# cv2.DRAW_MATCHES_FLAGS_DEFAULT 默认值
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
# cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG
# cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS
# 设置FLANN匹配器参数
# algorithm设置可参考https://docs.opencv.org/3.1.0/dc/d8c/namespacecvflann.html
indexParams = dict(algorithm=0, trees=5)
searchParams = dict(checks=50)
# 定义FLANN匹配器
flann = cv.FlannBasedMatcher(indexParams, searchParams)
# 使用 KNN 算法实现匹配
matches = flann.knnMatch(descriptor_1, descriptor_2, k=2)
# 根据matches生成相同长度的matchesMask列表,列表元素为[0,0]
matchesMask = [[0, 0] for i in range(len(matches))]
# 去除错误匹配
for i, (m, n) in enumerate(matches):
if m.distance < 0.4 * n.distance:
matchesMask[i] = [1, 0]
# 将图像显示
# matchColor是两图的匹配连接线,连接线与matchesMask相关
# singlePointColor是勾画关键点
drawParams = dict(matchColor=(0, 255, 0),
singlePointColor=(255, 0, 0),
matchesMask=matchesMask,
flags=0)
resultImage = cv.drawMatchesKnn(img_src1,
keypoints_1,
img_src2,
keypoints_2,
matches, None, **drawParams)
return resultImage
# end of sift_feature(()
def cv_img_show( resultImage):
# 调用cv.imshow()
window_name = "Input Image"
cv.namedWindow(window_name, cv.WINDOW_NORMAL)
cv.imshow(window_name, resultImage)
while True:
if cv.waitKey(0) & 0xff == ord("q"):
break
cv.destroyAllWindows()
return 0
def plt_img_show(resultImage):
plt.imshow(resultImage)
plt.show()
def main():
# 图像处理主函数
img_path1 = "../pictures/right.jpg" # 路径1
img_path2 = "../pictures/left.jpg" # 路径2
img1 = imgFeature(img_path1)
img2 = imgFeature(img_path2)
if (img1.img_path_check() == False or
img2.img_path_check() == False):
print("ERROR : img_path_check(img_path) !")
return -1;
else:
# 读取图像数据
img_src1 = img1.cv_img_read(check=False)
img_src2 = img2.cv_img_read(check=False)
# 提取并绘制特征点
resultImage = sift_feature(img_src1, img_src2)
cv_img_show(resultImage=resultImage)
# plt_img_show(resultImage)
img1.create_descriptors()
img1.save_descriptor(feature_detector=cv.xfeatures2d.SURF_create(float(4000)))
img2.create_descriptors()
img2.save_descriptor(feature_detector=cv.xfeatures2d.SURF_create(float(4000)))
return 0
# end of main(()
if __name__ == '__main__':
main()
参考:
1.博客以代码实例详述SIFT 、 SURF、 Harris、ORB等常见特征点提取与匹配方法, https://xyhuang.blog.csdn.net/article/details/80660688?utm_medium=distribute.pc_relevant.none-task-blog-searchFromBaidu-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-searchFromBaidu-3.control