建设网站的公司要什么资质,南昌网站空间,WordPress仿虎嗅主题,全球十大互联网企业Kp1,kp2都是list类型#xff0c;两幅图都是500个特征点。这和ORB论文中的数据是一样的。4.4章节
Matches也是list类型#xff0c;找到325个匹配对。
AKAZE文章中提到一个指标#xff1a;MS(matching score)# Correct Matches/# Features,
如果overlap area error 小于40%…
Kp1,kp2都是list类型两幅图都是500个特征点。这和ORB论文中的数据是一样的。4.4章节
Matches也是list类型找到325个匹配对。
AKAZE文章中提到一个指标MS(matching score)# Correct Matches/# Features,
如果overlap area error 小于40%并且经矩阵变换后两个对应像素距离小于2.5个像素就说明是correct match。这里的overlap area error没有搞懂应该在Mikolajczyk的A comparison of affine region dectors中找到答案。
还没搞清楚的还有代码中的matches指的应该是correspondence因为ORB论文中有一句perform brute-force matching to find the best correspondence.代码中使用的正是BFMatcher。但是文章接下来给出的曲线图是The results are given in terms of the percentage of correct matches, against the angle of rotation. the percentage of correctmatches应该等效于图上的纵坐标percentage of Inliers。那么percentage of和Inliers指的都应该是经过RANSAC提纯之后的匹配对。ORB文章中没有提到提纯即剔除误匹配对的过程。
img3 drawMatches(img1, kp1, img2, kp2, matches[:50])#找到50个匹配对
在绘制匹配对的时候代码直接对匹配器中的50个进行连线。但是看到其他代码中还有一个排序的过程
matchessorted(matches,keylambda x:x.distance)
首先Python中有两种排序方法一种是sort一种是Sorted。
L.sort(cmpNone, keyNone, reverseFalse) -- stable sort *IN PLACE*
Sort默认升序排序。要想颠倒排序可将默认的False改为True。In place说明sort是就地排序直接改变原来list
sorted(iterable, cmpNone, keyNone, reverseFalse) -- new sorted list
sorted会新建一个list用来存放排序之后的数据
cmp用于比较的函数比较什么由key决定,有默认值迭代集合中的一项; key用列表元素的某个属性和函数进行作为关键字有默认值迭代集合中的一项;
cmp和key可以使用lambda表达式。
可以理解为是cmp按照key的比较结果排序而key直接按照参数排序。因为列表中的元素很可能是元组。简单来说lambda返回冒号之后的值。下面两种方法是等价的
Sorting cmp:
L [(b,2),(a,1),(c,3),(d,4)]print sorted(L,cmplambda x,y:cmp(x[1],y[1]))[(a, 1), (b, 2), (c, 3), (d, 4)]
Sorting keys:
L [(b,2),(a,1),(c,3),(d,4)]print sorted(L, keylambda x:x[1]))[(a, 1), (b, 2), (c, 3), (d, 4)]
Lamdba是匿名函数有函数体没有函数名常用作回调函数的值。函数对象能维护状态但语法开销大而函数指针语法开销小却没法保存范围内的状态。lambda函数结合了两者的优点。
Lambda还经常和Reduce函数放在一起使用。Reduce函数其实是一个具有递归作用的二元函数对集合中的前两个执行函数得到的结果再与第三个元素做运算以此类推。
Reduce(lambda x,y:x*y,range(1,n1)) #n的阶乘
sumreduce(lambda x,y:xy,(1,2,3,4,5,6,7)) #12…7
bf cv2.BFMatcher(cv2.NORM_HAMMING, crossCheckTrue)#暴力匹配,True判断交叉验证
先使用cv.BFMatcher创造一个BfMatcher的对象。第一个参数normType规定了匹配时使用的距离准则默认是cv2.NORM_L2欧式距离适用于SIFT、SURF等。对于二进制字符串构成的描述子如ORB、BRIEF、BRISK等使用cv2.NORM_HAMMING汉明距离。汉明距离可以通过异或快速求解这也是算法加速的原因。最后If ORB is using WTA_K 3 or 4, cv2.NORM_HAMMING2 should be used.
第二个参数crossCheck是布尔型参数默认为false。如果改为True就开启了交叉验证我理解的就是双向匹配只有双向匹配成功的匹配对才被返回。数学上讲有两个集合A和B对于集合A中的i在B中找到的最近点是j如果对于j在A中搜索找到的最近点也是i那么他们就验证成功。如下图黄色的三角形在圆的集合中找到的最近点是绿色的圆。但是对于绿圆最近的点的粉色的三角即交叉验证失败。就像是找对象必须男女双方互相看对眼才能牵手成功。当然我们可以直接比较两个距离的大小距离小于某个阈值认为匹配正确SIFT则提出了最近邻和次近邻的比值与阈值比较。 It provides consistant result, and is a good alternative to ratio test proposed by D.Lowe in SIFT paper.手册上说这种交叉验证的方法可以是SIFT文章所提的最近邻比率法的一个很好的代替。
在创建BfMatcher的对象之后有两种匹配方法BFMatcher.match() and BFMatcher.knnMatch()。第一种返回的是最好的匹配对第二种是返回k个最好的匹配对k的取值由用户定义。两种匹配对对应的连线方法cv2.drawMatches()和cv2.drawMatchesKnn。这里再记录一下drawmatches的参数
void cv::drawMatches ( InputArray img1,const std::vector KeyPoint keypoints1,InputArray img2,const std::vector KeyPoint keypoints2,const std::vector DMatch matches1to2,InputOutputArray outImg,const Scalar matchColor Scalar::all(-1),const Scalar singlePointColor Scalar::all(-1),const std::vector char matchesMask std::vector char (),int flags DrawMatchesFlags::DEFAULT ) 前四个参数分别是两幅图像和各自的特征点matches1to2表明匹配的方向是从图1到图2keypoints1[i] has a corresponding point in keypoints2[matches[i]] .
outImg是输出的图像。
matchColor决定了连线和特征点的颜色如果matchColorScalar::all(-1) ,颜色随机生成。
singlePointColor决定了没有对应匹配对的孤立特征点的颜色
matchesMask决定哪些匹配对被画出如果掩膜为空则所有匹配对都被画出
flags是所画特征点的标志位 DEFAULT 0,默认会画出所有特征点
BFMatcher.knnMatch()主要用于配合SIFT文章中的最近邻比率法使用。当最近的距离小于次近邻的0.75时这个阈值的取值很重要0.75出自于OpenCV手册Lowe的论文中选取0.7认为是正确匹配对。手册中是以SIFT算法为例展示了knn的用法实际在ORB中也可以使用Knn和近邻比率法进行配对。
比率法在寻找最近距离的过程中综合考虑了次近邻这样只有当点很明显是最接近的情况下才会认为配对。对于一些疑似是对应点但疑似情况差不多的点就不再认为是匹配点即便他们与当前特征点的绝对距离都很近。
由KNN算法还可以引入kd数的构建和BBF算法更快地在搜索空间中寻找k个近邻点。OpenCV中也集成了一个快速搜索最近邻的库FLANN(Fast Library for Approximate Nearest Neighbors),可以在特征点的描述子的维度高特征点数目大的情况下依然快速找到最近邻点。写到这里可以注意到当KNN中的K取1时就是最近邻。所以之前的BFMatcher中的BFMatcher.match() and BFMatcher.knnMatch()都可以通过FLANN实现。
基于FLANN的检测器主要有两个字典类型的参数index_params和search_params。
#index_params与所选取的特征点检测算法有关index_params dict(algorithm FLANN_INDEX_KDTREE, trees 5)#kd树对应SIFT、SURFindex_params dict(algorithm FLANN_INDEX_LSH, table_number 6, key_size 12, # 20multi_probe_level 1) #2 #LSH局部敏感哈希对应ORB
第二个参数search_params决定了迭代的次数。It specifies the number of times the trees in the index should be recursively traversed.迭代次数越多准确度越高但是耗时当然也越长。
Index参数和search参数定义好之后就可以创建搜索器并进行匹配了。
# FLANN parameters
16 FLANN_INDEX_KDTREE 0
17 index_params dict(algorithm FLANN_INDEX_KDTREE, trees 5)
18 search_params dict(checks50) # or pass empty dictionary
19
20 flann cv2.FlannBasedMatcher(index_params,search_params)
21 #根据描述子进行k2的knn搜索
22 matches flann.knnMatch(des1,des2,k2)
23
24 # Need to draw only good matches, so create a mask
#xrange是一个生成器。这里对matches进行标记初始化为0.注意每一个match有最近和次近邻两个匹配点
25 matchesMask [[0,0] for i in xrange(len(matches))]
26
27 # ratio test as per Lowes paper
#对于最近邻m和次近邻n如果最近距离小于次近邻的0.7那么就将m标记成1从而得到0,1组成的掩膜
28 for i,(m,n) in enumerate(matches):
29 if m.distance 0.7*n.distance:
30 matchesMask[i][1,0]
31
32 draw_params dict(matchColor (0,255,0),
33 singlePointColor (255,0,0),
34 matchesMask matchesMask,
35 flags 0)#利用掩膜只画出最好的匹配的对
36
37 img3 cv2.drawMatchesKnn(img1,kp1,img2,kp2,matches,None,**draw_params)Matches是list类型的里面存放了DMatch类型的匹配对每个DMatch有4个成员变量distance(float)、imgIdx(int)、queryIdx(int)、trainIdx(int)表示了匹配对中特征点的距离索引等信息。query是要匹配的描述子train是被匹配的描述子 这是k2时KNN的输出结果对于每一个query会匹配前两个最近邻的特征点。List中的元素是list每一个list元素由两个DMatch构成。
经过最近邻比率法提纯后 可以看到直到query的第67个点才达成第一个正确匹配。
利用经过最近邻比率法匹配得到的匹配对得到新的描述子再通过新的描述子寻找单应矩阵寻找单应矩阵的过程中又可以使用RANSAC等方法提纯。
遇到的问题是函数cv2.findHomography的第一第二个参数要求是array或者scalar类型的srcPoints Coordinates of the points in the original plane, a matrix of the type CV_32FC2. or vector\Point2f\ .。
python中的list是python的内置数据类型list中的数据类不必相同的而array的中的类型必须全部相同the type of objects stored in them is constrainedThe type is specified at object creation time by using a type code, which is a single character.。在list中的数据类型保存的是数据的存放的地址简单的说就是指针并非数据这样保存一个list就太麻烦了例如list1[1,2,3,a]需要4个指针和四个数据增加了存储和消耗cpu。
des1是ndarray类型的包含383个特征点每个特征点的描述子的维度是61AKAZE所以是一个383x61大小的矩阵。
忙活了半天把初始描述子中属于good的部分提取出来并转化成ndarray格式却发现cv2.findHomography的前两个参数是特征点而不是特征点的描述子并且OpenCV又已经在链接5中给出了示例。
这是最终的图像配准代码
#!/usr/bin/env.python
#codingutf-8
## codinggbk 支持中文注释
import numpy as np
import cv2
import math
import logging
orig_imagecv2.imread(beaver.png,0)
skewed_imagecv2.imread(beaver_xform.png,0)cv2.imshow(graf1,orig_image)
cv2.imshow(graf2,skewed_image)#定义函数
def mydrawMatches(orig_image, kp1, skewed_image, kp2, matches):rows1orig_image.shape[0]#height(rows) of imagecols1orig_image.shape[1]#width(colums) of image#shape[2]#the pixels value is made up of three primary colorsrows2skewed_image.shape[0]cols2skewed_image.shape[1]#初始化输出的新图像将两幅实验图像拼接在一起便于画出特征点匹配对outnp.zeros((max([rows1,rows2]),cols1cols2,3),dtypeuint8)out[:rows1,:cols1] np.dstack([orig_image, orig_image, orig_image])#Python切片特性初始化out中orig_imageskewed_image的部分out[:rows2,cols1:] np.dstack([skewed_image, skewed_image, skewed_image])#dstack,对array的depth方向加深for mat in matches:orig_image_idxmat.queryIdxskewed_image_idxmat.trainIdx(x1,y1)kp1[orig_image_idx].pt(x2,y2)kp2[skewed_image_idx].ptcv2.circle(out,(int(x1),int(y1)),4,(255,255,0),1)#蓝绿色点半径是4cv2.circle(out, (int(x2) cols1, int(y2)), 4, (0, 255, 255), 1)#绿加红得黄色点cv2.line(out, (int(x1), int(y1)), (int(x2) cols1, int(y2)), (255, 0, 0), 1)#蓝色连线return out# 检测器。ORB AKAZE可以KAZE需要将BFMatcher中汉明距离换成cv2.NORM_L2
detector cv2.AKAZE_create()#kp1 detector.detect(orig_image, None)
#kp2 detector.detect(skewed_image, None)
#kp1, des1 detector.compute(orig_image, kp1)#计算出描述子
#kp2, des2 detector.compute(skewed_image, kp2)#也可以一步直接同时得到特征点和描述子
# find the keypoints and descriptors with ORBkp1, des1 detector.detectAndCompute(orig_image, None)
kp2, des2 detector.detectAndCompute(skewed_image, None)bf cv2.BFMatcher(cv2.NORM_HAMMING, crossCheckTrue)#暴力匹配,True判断交叉验证
matches bf.match(des1, des2)#利用匹配器得到相近程度
matchessorted(matches,keylambda x:x.distance)#按照描述子之间的距离进行排序
img3cv2.drawMatches(orig_image, kp1, skewed_image, kp2, matches[:50],None,(255, 0, 0),flags2)#找到50个匹配对# Apply ratio test
bf cv2.BFMatcher(cv2.NORM_HAMMING, crossCheckFalse)#为了得到最近邻和次近邻这里交叉验证要设置为false
matches bf.knnMatch(des1,des2, k2)
good []
#lenofgood0
for m, n in matches:if m.distance 0.75 * n.distance:good.append([m])#good.append(m)#lenofgoodlenofgood1#matches2 sorted(good, keylambda x: x.distance) # 按照描述子之间的距离进行排序# cv2.drawMatchesKnn expects list of lists as matches.
#img4 cv2.drawMatches(orig_image, kp1, skewed_image, kp2, matches2[:50],None, flags2)
#img4 cv2.drawMatchesKnn(orig_image, kp1, skewed_image, kp2, good[:50],None, flags2)
img4 cv2.drawMatchesKnn(orig_image, kp1, skewed_image, kp2, good,None, flags2)
lenofgoodlen(good)#good长度为24des1fromgoodnp.ones((lenofgood,61),dtypenp.uint8)
#des2fromgood[]
des2fromgoodnp.ones((lenofgood,61),dtypenp.uint8)
for num in range(0,24):for i in good:for j in i:#good是list组成的list每个子list中是DMatchdes1fromgood[num]des1[j.queryIdx]#将good中的特征点对应的描述子形成新的描述子#des2fromgood.append(des2[j.trainIdx])des2fromgood[num] des2[j.trainIdx]cv2.imwrite(AKAZETest.jpg, img3)
cv2.imshow(AKAZETest, img3)
cv2.imwrite(AKAZETestRatio.jpg, img4)
cv2.imshow(AKAZETestRatio, img4)#H,maskcv2.findHomography(des1fromgood,des2fromgood,cv2.RANSAC,5.0)#,None,None,None,None,None)
good []
for m, n in matches:if m.distance 0.7 * n.distance:good.append(m)
MIN_MATCH_COUNT 10
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)ss M[0, 1]sc M[0, 0]scaleRecovered math.sqrt(ss * ss sc * sc)thetaRecovered math.atan2(ss, sc) * 180 / math.pi#logging打印出通过矩阵计算出的尺度缩放因子和旋转因子LOG_FORMAT %(asctime)s - %(levelname)s - %(message)sDATE_FORMAT %m/%d/%Y %H:%M:%S %plogging.basicConfig(filenamemy.log, levellogging.DEBUG, formatLOG_FORMAT, datefmtDATE_FORMAT)#配置之后将log文件保存到本地logging.info(MAP: Calculated scale difference: %.2f, Calculated rotation difference: %.2f %(scaleRecovered, thetaRecovered))# deskew imageim_out cv2.warpPerspective(skewed_image, np.linalg.inv(M),(orig_image.shape[1], orig_image.shape[0]))resultcv2.warpPerspective(orig_image,M,(skewed_image.shape[1],skewed_image.shape[0]))#注意warpPerspective与warpAffine的区别不能互换
#img_warpcv2.perspectiveTransform(orig_image,M)#,img_warp)
#c.shape[1] 为第一维的长度c.shape[0] 为第二维的长度。
cv2.imshow(fan透视变换,im_out)
cv2.imshow(透视变换,result)
cv2.waitKey(0)
reference:
Lambdahttps://blog.csdn.net/zcy19941015/article/details/47053799OpCVtutorialhttps://docs.opencv.org/3.1.0/dc/dc3/tutorial_py_matcher.html切片特性https://www.jianshu.com/p/5083f8d75439List指针https://blog.csdn.net/liyaohhh/article/details/51055147https://www.programcreek.com/python/example/70413/cv2.RANSAC