第6章 图像检索以及基于图像描述符的搜索
OpenCV可以检测图像的主要特征,然后提取这些特征,使其成为图像描述符,类似于人的眼睛和大脑。
本章介绍如何使用OpenCV检测图像特征,并通过这些特征进行图像匹配和检测。 本章选取一些图像,检测它们的主要特征,并通过单应性来检测这些图像是否存在于另一个图像中。
6.1 特征检测算法
OpenCV中最常用的特征检测和提取算法:
Harris:用于检测角点
SIFT:用于检测斑点(Blob)
SURF:用于检测斑点
FAST:检测角点
BRIEF:检测斑点
ORB:带方向的FAST算法和具有选择不变性的BRIEF算法。
匹配算法:
暴力(Brute-Force)匹配法
基于FLANN的匹配法
6.1.1 特征定义
特征是有意义的图像区域,该区域具有独特性和易于识别性。
因此,角点及高密度区域是很好的特征,而大量重复的模式和低密度区域不是好的特征。边缘可以将图像分成两个区域,因此也可以看作好的特征,
斑点(和周围差别很大的区域)也是有意义的特征。
检测角点特征:
import cv2import numpy as np img = cv2.imread("images/chess_board.png") gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) gray = np.float32(gray) dst = cv2.cornerHarris(gray,2,23,0.04) img[dst>0.01 * dst.max()] = [0,255,0]while(True): cv2.imshow('corners',img) if cv2.waitKey(84) & 0xff == ord('q'): breakcv2.destroyAllWindows()
6.1.2 使用DoG和SIFT进行特征提取与描述
通过SIFT得到充满角点和特征的图像
import cv2import numpy as np imgpath = r".\images\varese.jpg"img = cv2.imread(imgpath) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) sift = cv2.xfeatures2d.SIFT_create() keypoints,descriptor = sift.detectAndCompute(gray,None) img = cv2.drawKeypoints(image=img,outImage=img,keypoints=keypoints,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS, color=(51,163,236)) cv2.imshow('sift_keypoints',img) cv2.waitKey()
6.1.3 使用快速Hessian算法和SURF来提取和检测特征
SURF特征检测算法比SIFT快好几倍,它吸收了SIFT算法的思想。
SURF是OpenCV的一个类,SURF使用快速Hessian算法检测关键点,SURF会提取特征。
import cv2import numpy as np imgpath = r".\images\varese.jpg"img = cv2.imread(imgpath) alg = "SURF"def fd(algorithm): if algorithm == "SIFT": return cv2.xfeatures2d.SIFT_create() if algorithm == "SURF": return cv2.xfeatures2d.SURF_create(float(8000)) gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) fd_alg = fd(alg) keypoints,descriptor = fd_alg.detectAndCompute(gray,None) img = cv2.drawKeypoints(image=img,outImage=img,keypoints=keypoints, flags=4,color=(51,163,236)) cv2.imshow('sift_keypoints',img) cv2.waitKey()
6.1.4 基于ORB的特征检测和特征匹配
ORB是一种新的算法,用来替代SIFT和SURF算法。
ORB将基于FAST关键点检测的技术和基于BRIEF描述符的技术相结合、
1.FAST(Feature from Accelerated Segment Test)
FAST算法会在像素周围绘制一个圆,该圆包括16个像素,然后,FAST将每个像素与加上一个阈值的圆心像素值进行比较,若有连续,比此值还亮或还暗的像素,则认为圆心的角点。
2.BRIEF(Binary Robust Independent Elemetary Features)并不是特征检测算法,它只是一个描述符。
3.暴力匹配
暴力匹配是一种描述符匹配方法,该方法会比较两个描述符,并产生匹配结果的列表,称为暴力匹配的原因是该算法不涉及优化,第一个描述符的所有特征都和第二个描述符的特征进行比较。
6.1.5 ORB特征匹配
import numpy as npimport cv2from matplotlib import pyplot as plt img1 = cv2.imread('images/manowar_logo.png',cv2.IMREAD_GRAYSCALE) img2 = cv2.imread('images/manowar_single.jpg',cv2.IMREAD_GRAYSCALE) orb = cv2.ORB_create() kp1,des1 = orb.detectAndCompute(img1,None) kp2,des2 = orb.detectAndCompute(img2,None) bf = cv2.BFMatcher(cv2.NORM_HAMMING,crossCheck=True) matches = bf.match(des1,des2) matches = sorted(matches,key=lambda x:x.distance) img3 = cv2.drawMatches(img1,kp1,img2,kp2,matches[:40],img2,flags=2) plt.imshow(img3),plt.show() 6.1.6 K-最近邻匹配(运行出错)
只需对前面的匹配操作进行修改:
6.1.7 FLANN匹配
近似最近邻的快速库
import numpy as npimport cv2from matplotlib import pyplot as plt queryImage = cv2.imread('images/bathory_album.jpg',0) trainingImage = cv2.imread('images/vinyls.jpg',0) sift = cv2.xfeatures2d.SIFT_create() kp1, des1 = sift.detectAndCompute(queryImage,None) kp2, des2 = sift.detectAndCompute(trainingImage,None) FLANN_INDEX_KDTREE = 0indexParams = dict(algorithm = FLANN_INDEX_KDTREE, trees=5) searchParams = dict(checks=50) flann = cv2.FlannBasedMatcher(indexParams,searchParams) matches = flann.knnMatch(des1,des2,k=2) matchesMask = [[0,0] for i in range(len(matches))]for i,(m,n) in enumerate(matches): if m.distance < 0.7 *n.distance: matchesMask[i]=[1,0] drawParams = dict(matchColor=(0,255,0),singlePointColor = (255,0,0), matchesMask=matchesMask, flags=0) resultImage = cv2.drawMatchesKnn(queryImage,kp1,trainingImage,kp2,matches,None,**drawParams) plt.imshow(resultImage,),plt.show()
6.1.8 FLANN的单应性匹配
单应性是一个条件,该条件表面当两幅图像中一副出现投影畸变时,还能匹配。
import numpy as npimport cv2from matplotlib import pyplot as plt MIN_MATCH_COUNT = 10img1 = cv2.imread('images/bb.jpg',0) img2 = cv2.imread('images/color2_small.jpg',0) sift = cv2.xfeatures2d.SIFT_create() kp1,des1 = sift.detectAndCompute(img1,None) kp2,des2 = sift.detectAndCompute(img2,None) FLANN_INDEX_KDTREE = 0index_params = dict(algorithm=FLANN_INDEX_KDTREE,trees=5) search_params = dict(checks=50) flann = cv2.FlannBasedMatcher(index_params,search_params) matches = flann.knnMatch(des1,des2,k=2) good = []for m,n in matches: if m.distance < 0.7*n.distance: good.append(m)if len(good)>MIN_MATCH_COUNT: scr_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2) dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2) M,mask = cv2.findHomography(scr_pts,dst_pts,cv2.RANSAC,5.0) matchesMask = mask.ravel().tolist() h,w = img1.shape pts = np.float32([ [0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2) dst = cv2.perspectiveTransform(pts,M) img2 = cv2.polylines(img2,[np.int32(dst)],True,255,3,cv2.LINE_AA)else: print("Not enough matches are found") matchesMask = Nonedraw_params = dict(matchColor=(0,255,0),singlePointColor = None, matchesMask = matchesMask,flags=2) img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params) plt.imshow(img3,'gray'), plt.show()
6.1.9 基于文身取证的应用程序示例
#1.generate_description.py
import cv2import numpy as npfrom os import walkfrom os.path import joinimport sysdef create_descritors(folder): files = [] for(dirpath,dirnames,filenames) in walk(folder): files.extend(filenames) for f in files: save_descriptor(folder,f,cv2.xfeatures2d.SIFT_create())def save_descriptor(folder,image_path,feature_detector): img = cv2.imread(join(folder,image_path),0) keypoints,descriptors = feature_detector.detectAndCompute(img,None) descriptor_file = image_path.replace("jpg","npy") np.save(join(folder,descriptor_file),descriptors) dir = 'anchors'create_descritors(dir)
#2scan_and_match.py from os.path import join from os import walk import numpy as np import cv2 from sys import argv folder = "anchors" query = cv2.imread( join(folder,"tattoo_seed.jpg"),0) files = [] images = [] descriptiors = [] for (dirpath,dirnames,filenames) in walk(folder): files.extend(filenames) for f in files: if f.endswith("npy") and f != "tattoo_seed.npy": descriptiors.append(f) print(descriptiors) sift = cv2.xfeatures2d.SIFT_create() query_kp ,query_ds = sift.detectAndCompute(query,None) FLANN_INDEX_KDTREE = 0 index_params = dict(algorithm=FLANN_INDEX_KDTREE,trees = 5) search_params = dict(checks = 50) flann = cv2.FlannBasedMatcher(index_params,search_params) MIN_MATCH_COUNT = 10 potential_culprits = {} print(">>初始化图像扫描...") for d in descriptiors: print("-----分析%s ---------"%d) matches=flann.knnMatch(query_ds,np.load(join(folder,d)),k=2) good = [] for m,n in matches: if m.distance < 0.7*n.distance: good.append(m) if len(good)>MIN_MATCH_COUNT: print("%s 匹配!(%d)"%(d,len(good))) else: print("%s 不匹配!" % d) potential_culprits[d] = len(good) max_matches = None potential_suspect = None for culprit, matches in potential_culprits.items(): if max_matches == None or matches>max_matches: max_matches = matches potential_suspect = culprit print("嫌疑人 %s" %potential_suspect.replace("npy","").upper())
