博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
opencv3 图像检索以及基于图像描述符的搜索
阅读量:1860 次
发布时间:2019-04-26

本文共 9220 字,大约阅读时间需要 30 分钟。

本章主要介绍如何检测图像特征以及如何为描述符提取特征

特征检测算法

       图像特征就是有意义的图像区域,该区域具有独特性或易于识别性。因此,角点高密度区域是很好的特征,而大量重复的模式或低密度区域则不是好的特征。边缘可以将图像分为两个区域,因此也可以看作好的特征。斑点(与周围有很大差点别的图像区域)也是有意义的特征。

       大多数特征检测算法都会涉及图像的角点,边和斑点的识别,也有涉及脊向的概念,可以认为脊向的概念,可以认为脊向是细长物体的对称轴(例如:图像识别中的一条路)

       检测国际象棋角点特征:

import cv2import numpy as npimport sysimg = cv2.imread('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, 0, 255] #while (True):cv2.imshow('corners', img)# if cv2.waitKey(int(1000 / 12)) & 0xff == ord("q"):#     breakcv2.waitKey(0)cv2.destroyAllWindows()

cornerHarris函数中最重要的参数是第三个,该参数限定了sobel算子的中孔,sobel算子通过对图像行列的变化来检测边缘,sobel算子会通过核kernel来完成检测。该参数定义了角点检测的敏感度,其取值必须介于3和31之间的奇数。如果将参数设置为3,当检测到方块的边界时,棋盘中褐色方块的所有对角线都会被认为时角点。它的第二个参数被设置可以改变标记焦点的记号大小

输出:

参数=23时:

参数=3时:

通过sift得到充满角点和特征的图像

import cv2import sysimport numpy as npimgpath = 'varese.jpg'img = cv2.imread(imgpath)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#创建一个sift对象sift = cv2.xfeatures2d.SIFT_create()keypoints, descriptor = sift.detectAndCompute(gray,None)"""这里的标志4穿个drawKeypoints函数,标志值4其实是下面这个cv2模块的属性值 cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINT这个代码对图像的每个关键点都绘制了圆圈和方向"""img = cv2.drawKeypoints(image=img, outImage=img, keypoints = keypoints, flags = 4, color = (51, 163, 236))cv2.imshow('sift_keypoints', img)cv2.waitKey(0)cv2.destroyAllWindows()

SIFT对象会使用DoG检测关键点,并且对每个关键点周围的区域计算特征向量,由方法名称我们就可以知道,它主要包括两个操作:检测和计算,操作的返回值是关键点信息和描述符,最后在图像上绘制关键点

SURF采用快速Hessian算法检测关键点

SURF会提取特征

import cv2import sysimport numpy as np#imgpath = sys.argv[1]imgpath = 'varese.jpg'img = cv2.imread(imgpath)#alg = sys.argv[2]alg = 'SURF'def fd(algorithm):  algorithms = {    "SIFT": cv2.xfeatures2d.SIFT_create(),    "SURF": cv2.xfeatures2d.SURF_create(float(sys.argv[3]) if len(sys.argv) == 4 else 8000),    "ORB": cv2.ORB_create()  }  return algorithms[algorithm]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('keypoints', img)cv2.waitKey(0)cv2.destroyAllWindows()

这幅图像是由SURF算法处理出来的,所采用的hessian阈值为8000,阈值越高,能识别的特征就越少

输出:

基于ORB的特征检测和特征匹配:

SIFT(1999年提出)是一种新算法,SURF(2006年提出)是一种更加新的算法,ORB(2011)是刚起步 用来代替前两者的,ORB是将基于FAST关键点检测技术和基于BRIEF描述符的技术相结合

下面分别介绍一下

FAST算法:

其中匹配算法用的是暴力匹配Brute-Force

import numpy as npimport cv2from matplotlib import pyplot as plt# query and test imagesimg1 = cv2.imread('manowar_logo.png',0)img2 = cv2.imread('manowar_single.jpg',0)# create the ORB detectororb = cv2.ORB_create()kp1, des1 = orb.detectAndCompute(img1,None)kp2, des2 = orb.detectAndCompute(img2,None)# brute force matchingbf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)matches = bf.match(des1,des2)# Sort by distance.matches = sorted(matches, key = lambda x:x.distance)img3 = cv2.drawMatches(img1,kp1,img2,kp2, matches[:25], img2,flags=2)plt.imshow(img3),plt.show()

输出:

KNN匹配

与上面那个例子不同的是,这边用knn来进行匹配,

import cv2import numpy as npimport matplotlib.pyplot as pltp1 = cv2.imread('manowar_logo.png')p2 = cv2.imread('manowar_single.jpg')grayp1 = cv2.cvtColor(p1, cv2.IMREAD_GRAYSCALE)grayp2 = cv2.cvtColor(p2, cv2.IMREAD_GRAYSCALE)orb = cv2.ORB_create()keyp1 ,desp1= orb.detectAndCompute(grayp1, None)keyp2 ,desp2= orb.detectAndCompute(grayp2, None) bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) KnnMatches = bf.knnMatch(desp1, desp2, k=1)KnnMatchImg = cv2.drawMatchesKnn(p1, keyp1, p2, keyp2, KnnMatches[:50],                               p2, flags=2)plt.figure(figsize=(15,8))plt.imshow(KnnMatchImg)plt.show()

输出:

FLANN匹配

    FLANN具有一种内部机制,该机制可以根据数据本身选择最合适的算法来处理数据,经验证,FLANN比其他的最近邻搜索软件快10倍。

    FLANN内部可以选择LinearIndex、KTreeIndex、KMeansIndex、CompositeIndex和AutotuneIndex用来配置索引,这里选择KTreeIndex配置索引(只需指定待处理密度书的数量、最理想的数量在1~16之间),并且KtreeIndex非常灵活(kd-trees)可被并行处理,searchParams字典只包含一个字段checks,用来指定索引树要被遍历的次数,该值越高,计算匹配花费的时间就越长,但也会越准确。

    实际上 匹配效果很大程度取决于输入,5 kd-trees和50 checks总能取得具有合理精度的结果,而且在很短时间内就能完成

import numpy as npimport cv2from matplotlib import pyplot as pltqueryImage = cv2.imread('bathory_album.jpg',0)trainingImage = cv2.imread('bathory_vinyls.jpg',0)# create SIFT and detect/computesift = cv2.xfeatures2d.SIFT_create()kp1, des1 = sift.detectAndCompute(queryImage,None)kp2, des2 = sift.detectAndCompute(trainingImage,None)# FLANN matcher parameters#FLANN_INDEX_KDTREE = 0indexParams = dict(algorithm = 0, trees = 5)searchParams = dict(checks=50)   # or pass empty dictionaryflann = cv2.FlannBasedMatcher(indexParams,searchParams)matches = flann.knnMatch(des1,des2,k=2)# prepare an empty mask to draw good matchesmatchesMask = [[0,0] for i in range(len(matches))]# David G. Lowe's ratio test, populate the maskfor 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()

输出:

FLANN的单应性匹配:

    正确识别出右侧图像,画出了关键点的匹配线段,而且还花了一个白色边框,用来展示图像seed目标在右侧发生投影畸变的效果

import numpy as npimport cv2from matplotlib import pyplot as pltMIN_MATCH_COUNT = 10img1 = cv2.imread('bb.jpg',0)img2 = cv2.imread('color2.jpg',0)# Initiate SIFT detectorsift = cv2.xfeatures2d.SIFT_create()# find the keypoints and descriptors with SIFTkp1, 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)# store all the good matches as per Lowe's ratio test.good = []for m,n in matches:    if m.distance < 0.7*n.distance:        good.append(m)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)    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 = 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 = Nonedraw_params = dict(matchColor = (0,255,0), # draw matches in green color                   singlePointColor = None,                   matchesMask = matchesMask, # draw only inliers                   flags = 2)img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)plt.imshow(img3, 'gray'),plt.show()

输出:

基于纹身取证的应用程序示例:

首先得将图像描述符保存到文件中,好处是,当两幅图像进行匹配和单应性分析时,不用每次都重建描述符,应用程序每次都会扫描保存图像的文件夹,并创建相应的描述符文件,可供后面搜索时使用,

文件存放:

from os.path import joinfrom os import walkimport numpy as npimport cv2from sys import argv# create an array of filenamesfolder = './image'query = cv2.imread(join(folder, "tattoo_seed.jpg"), 0)# create files, images, descriptors globalsfiles = []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 detectorsift = cv2.xfeatures2d.SIFT_create()query_kp, query_ds = sift.detectAndCompute(query, None)# create FLANN matcherFLANN_INDEX_KDTREE = 0index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)search_params = dict(checks = 50)flann = cv2.FlannBasedMatcher(index_params, search_params)# minimum number of matchesMIN_MATCH_COUNT = 10potential_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 < 0.7*n.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 = Nonepotential_suspect = Nonefor culprit, matches in potential_culprits.items():    if max_matches == None or matches > max_matches:        max_matches = matches        potential_suspect = culpritprint ("potential suspect is %s" % potential_suspect.replace("npy", "").upper())

输出:

['bane.npy', 'dr-hurt.npy', 'hush.npy', 'penguin.npy', 'posion-ivy.npy', 'riddler.npy', 'two-face.npy']>> Initiating picture scan...--------- analyzing bane.npy for matches ------------bane.npy is not a match--------- analyzing dr-hurt.npy for matches ------------dr-hurt.npy is a match! (298)--------- analyzing hush.npy for matches ------------hush.npy is a match! (301)--------- analyzing penguin.npy for matches ------------penguin.npy is not a match--------- analyzing posion-ivy.npy for matches ------------posion-ivy.npy is not a match--------- analyzing riddler.npy for matches ------------riddler.npy is not a match--------- analyzing two-face.npy for matches ------------two-face.npy is not a matchpotential suspect is HUSH.
你可能感兴趣的文章
如何准备电赛?19年电赛经验总结!
查看>>
蓝牙:为啥叫“蓝”牙,不叫“白”牙?
查看>>
干货 | 如何系统学习 C 语言?
查看>>
多层PCB内部长啥样? 3D大图解析高端PCB板的设计工艺
查看>>
鸿蒙2.0都来了,快搭个环境玩起来吧!
查看>>
PCB散热的10种方法!
查看>>
值得收藏!268条PCB layout设计规范
查看>>
Keil升级了,Keil Studio 来了!
查看>>
关于RS-485总线,这篇很详细
查看>>
关于2021年电赛的一些想法,看到就是赚到!
查看>>
教你一秒分辨真假芯片!
查看>>
抽奖 | 送STM32开发板
查看>>
光立方,永远的神!
查看>>
学习STM32很简单?
查看>>
电赛 | 电源题软件如何准备?
查看>>
手把手教你DIY一款属于自己的万能红外遥控器!
查看>>
速看 | 电子元器件如何确定好坏?
查看>>
485通信自动收发电路,历史上最详细的解释
查看>>
【视觉盛宴三】不好意思,这些线材接口的横截面真的没见过
查看>>
一位头发发白的神人教你怎么写程序,运维,买电脑,写文章,平面设计!
查看>>