本博文基于FLANN算法介绍一个基于纹身取证的应用程序示例,参考《OpenCV3计算机视觉:Python语言实现(原书第2版)》第6章。
1. 保存图像描述符到文件
应用程序扫描保存图像的文件夹,并创建相应的描述符文件,供后面搜索使用,整个过程包括:加载图像、创建特征检测器、检测并计算、保存描述符到文件。# -*- coding: utf-8 -*-
"""
Created on Sun Jun 24 14:17:31 2018
@author: lu
"""
import cv2
import numpy as np
from os import walk
from os.path import join
def create_descriptors(folder):
files = []
for (dirpath, dirnames, filenames) in walk(folder):
files.extend(filenames)
for f in files:
save_descriptor(folder, f, cv2.xfeatures2d.SIFT_create()) #采用SIFT算法获取特征点
def save_descriptor(folder, image_path, feature_detector):
print ("reading %s" % image_path)
if image_path.endswith("npy"):
return
img = cv2.imread(join(folder, image_path), 0)
keypoints, descriptors = feature_detector.detectAndCompute(img, None)
descriptor_file = image_path.replace("jpg", "npy") #保存为特征描述符文件,后缀为.npy
np.save(join(folder, descriptor_file), descriptors) #save()函数将数据保存为一个文件
#定义图像存放的目录
picDir = "./anchors"
#创建描述图像,用于FLANN检测
create_descriptors(picDir)
2. 扫描查找匹配
将描述符保存为文件后,接下来对所有描述符进行单应性处理,步骤如下:
<1> 加载查询图像,并为其创建描述符(如:tattoo_seed.jpg);
<2> 扫描加载文件夹中的描述符;
<3> 对于每一个描述符,通过FLANN算法进行匹配;
<4> 获取特征符匹配的数目,包括潜在罪犯的档案;
<5> 对于匹配的罪犯,认为匹配最多的图像是潜在的犯罪嫌疑人;# -*- coding: utf-8 -*-
"""
Created on Sun Jun 24 14:18:26 2018
@author: lu
"""
from os.path import join
from os import walk
import numpy as np
import cv2
# create an array of filenames
folder = "./anchors"
query = cv2.imread(join(folder, "tattoo_seed.jpg"), 0)
# create files, images, descriptors globals
files = []
images = []
descriptors = []
for (dirpath, dirnames, filenames) in walk(folder):
files.extend(filenames)
for f in files:
if f.endswith("npy") and f != "tattoo_seed.npy":
descriptors.append(f)
print (descriptors)
# create the sift detector
sift = cv2.xfeatures2d.SIFT_create()
query_kp, query_ds = sift.detectAndCompute(query, None)
# create FLANN matcher
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)
# minimum number of matches
MIN_MATCH_COUNT = 10
potential_culprits = {}
print (">> Initiating picture scan...")
for d in descriptors:
print ("--------- analyzing %s for matches ------------" % d)
matches = flann.knnMatch(query_ds, np.load(join(folder, d)), k =2)
good = []
for m,n in matches:
if m.distance
good.append(m)
if len(good) > MIN_MATCH_COUNT:
print ("%s is a match! (%d)" % (d, len(good)))
else:
print ("%s is not a match" % 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 ("potential suspect is %s" % potential_suspect.replace("npy", "").upper())
3. 形象化显示匹配
根据步骤2中方法,可获取最佳匹配图像。实际应用中,需要更形象化标识出源图像与目标图像之间的匹配情况,此时可通过如下代码实现形象化显示。# -*- coding: utf-8 -*-
"""
Created on Sun Jun 24 13:12:00 2018
@author: lu
"""
import numpy as np
import cv2
from matplotlib import pyplot as plt
'''
简单来说:
单应性就是当两幅图像中的一幅出现投影畸变(perspective distortion)时,它们还能彼此匹配
'''
#至少10个点匹配
MIN_MATCH_COUNT = 10
##电影光盘
#img1 = cv2.imread('./bb.jpg',0)
#img2 = cv2.imread('./bb_small.jpg',0)
#纹身检测
img1 = cv2.imread('./anchors/tattoo_seed.jpg',0)
img2 = cv2.imread('./anchors/dr-hurt.jpg',0)
# Initiate SIFT detector
sift = cv2.xfeatures2d.SIFT_create()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,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)
matches = flann.knnMatch(des1,des2,k=2)
# store all the good matches as per Lowe's ratio test.
good = []
for m,n in matches:
if m.distance
good.append(m)
'''
https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_feature_homography/py_feature_homography.html?highlight=findhomography
Now we set a condition that atleast 10 matches (defined by MIN_MATCH_COUNT) are to be there to find the object. Otherwise simply show a message saying not enough matches are present.
If enough matches are found, we extract the locations of matched keypoints in both the images.
They are passed to find the perpective transformation. Once we get this 3x3 transformation matrix, we use it to transform the corners of queryImage to corresponding points in trainImage. Then we draw it.
'''
if len(good)>MIN_MATCH_COUNT:
src_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)
'''
We have seen that there can be some possible errors while matching which may affect the result. To solve this problem, algorithm uses RANSAC or LEAST_MEDIAN (which can be decided by the flags).
So good matches which provide correct estimation are called inliers and remaining are called outliers.
cv2.findHomography() returns a mask which specifies the inlier and outlier points.
'''
M, mask = cv2.findHomography(src_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为灰度图像,因此设置为255对应边框为白色
img2 = cv2.polylines(img2,[np.int32(dst)],True,255,3, cv2.LINE_AA)
else:
print ("Not enough matches are found - %d/%d" % (len(good),MIN_MATCH_COUNT))
matchesMask = None
draw_params = dict(matchColor = (51, 163, 236),
singlePointColor = None,
matchesMask = matchesMask, # draw only inliers
flags = 2)
# **表示接收的参数作为字典来处理
img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
cv2.imshow("HomographyMatch",img3)
cv2.waitKey()
cv2.destroyAllWindows()
注意:本站所有文章除特别说明外,均为原创,转载请务必以超链接方式并注明作者出处。