热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

OpenCV(10)特征检测

特征点检测与匹配特征检测概述OpenCV特征的场景图像搜索,如以图搜图拼图游戏拼图方法:寻找特征特征是唯一的可追踪的能比较的图像拼接,将


特征点检测与匹配

特征检测概述



OpenCV特征的场景



  • 图像搜索,如以图搜图
  • 拼图游戏
    • 拼图方法:
      • 寻找特征
      • 特征是唯一的
      • 可追踪的
      • 能比较的
  • 图像拼接,将两张有关联的图拼接到一起


实例:寻找目标图在原图中的位置



  • 平坦部分很难找到它在原图中的位置
  • 边缘相比平坦要好找一些,但也不能一下确定具体位置
  • 角点可以一下就能找到其在原图中的位置


图像特征定义



  • 图像特征就是指有意义的图像区域,具有独特性、易于识别性,比如角点、斑点以及高密度区


角点



  • 在特征中最重要的是角点
  • 灰度梯度的最大值对应的像素
  • 两条线的交点
  • 极值点(一阶导数最大值,但二阶导数为0)

Harris角点检测


  • 用一个方块移动进行衡量

  • 光滑地区,无论向哪里移动,衡量系数不变

  • 边缘地区,垂直边缘移动时,衡量系数变化剧烈

  • 角点处,无论向哪里移动,衡量系数都变化剧烈

  • cornerHarris(img,dst,blockSize,ksize,k)


    • blockSize:检测窗口大小,窗口越大,敏感度越高
    • ksize:Sobel的卷积核
    • k:权重系数,经验值,一般取0.02-0.04之间

#灰度化
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#处理的是灰度化后的图片
dst = cv2.cornerHarris(gray,2.3.0.04)
#Harris角点的展示
img[dst>0.01 * dst.max()] = [0,0,255]

Shi-Tomasi角点检测


  • Shi-Tomasi是Harris角点检测的改进,尤其是不用设置k值
  • goodFeaturesToTrack(img,maxCorners,…)
    • maxCorners:角点的最大数,值为0表示无限制
    • qualityLevel:小于1.0的正数,一般在0.01-0.1之间
    • minDistance:角之间最小欧式距离,忽略小于此距离的点,距离越大检测角数越少,反之越大
    • mask:感兴趣的区域,不设置则会对整张图片进行检测
    • blockSize:检测窗口
    • useHarrisDetector:是否用Harris算法
      • true:使用原始Harris算法,k默认为0.04
      • flase:使用Shi-Tomasi,默认为flase

corners = cv2.goodfeaturesToTrack(gray,1000,0.01,10)
corners = np.int0(corners)
for i in corners:
x,y = i.ravel()
cv2.circle(img,(x,y),3,(255,0,0),-1)

SIFT关键点检测


  • SIFT:Scale-Invariant Feature Transform 与缩放无关的特征转换

  • SIFT出现的原因:


    • Harris角点具有旋转不变的特性
    • 但缩放后,原有的角点可能不会被检测出来
  • 使用SIFT步骤:


    • 创建SIFT对象,sift = cv2.xfeatures2d.SIFT_create()
    • 进行检测,kp = sift.detect(img,…)
    • 绘制关键点,drawKeypoints(gray,kp,img)

#灰度化
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#创建SIFT对象
sift = cv2.xfeatures2d.SIFT_create()
#进行检测
kp = sift.detect(gray,None)
#绘制关键点
cv2.drawKeypoints(gray,kp,img)

  • 关键点:位置,大小和方向
  • 关键点描述子:记录里关键点周围对其有贡献的像素点的一组向量值,其不受仿射变换、光照变换等影响
  • 计算描述子:
    • kp,des = sift.compute(img,kp)
    • 其作用时进行特征匹配
  • 同时计算关键点和描述子:
    • kp,des = sift.detectAndcompute(img,mask)
    • mask:指明对img中那个区域进行计算,未设置/设置成None即对整个图片进行计算

SURF特征检测


  • SURF: Speeded-Up Robust Features 加速的鲁棒性特征检测
  • SURF优点:SIFT最大的问题是速度慢,因此才有SURF,SURF检测速度快
  • 使用SURF步骤
    • surf= cv2.xfeatures2d.SURF_create()
    • kp,des = surf.detectAndcompute(img,mask)

#创建SURF对象
surf= cv2.xfeatures2d.SURF_create()
#进行检测
kp,des = surf.detectAndcompute(gray,None)
#绘制关键点
cv2.drawKeypoints(gray,kp,img)

OBR特征检测


  • OBR:Oriented FAST and Rotated BRIEF

  • OBR优势:可以做到实时监测

  • 抛弃部分数据以提升速度,因此准确性不如SIFT、SURF

  • FAST:可以做到特征点的实时监测

  • BRIEF是对已检测到的特征点进行描述,它加快了特征描述符建立的速度,同时也极大的降低了特征匹配的时间

  • 使用ORB步骤:


    • obr = cv2.ORB_create()
    • kp,des = obr.detectAndcompute(img,mask)

obr = cv2.ORB_create()
kp,des = obr.detectAndcompute(gray,None)

暴力特征匹配BF


  • BF(Brute-Force) 暴力特征匹配

  • 暴力特征匹配原理:它使用第一组中的每个特征的描述子与第二组中的所有特征描述子进行匹配,计算它们之间的差距,然后将最接近的一个匹配值返回



OpenCV特征匹配步骤:



  • 创建匹配器,BFMatchaer(normType,crossCheck)


    • normType:NORM_L1,NORM_L2,HAMMING1…
    • crossCheck:是否进行交叉匹配,默认为flase
  • 进行特征匹配,bf.match(des1,des2)


    • 参数为SIFT、SURF、OBR等计算的描述子
    • 对两幅图的描述子进行计算
  • 绘制匹配点,cv2.drawMatches(img1,kp1,img2,kp2,match


    • 搜索图img,kp
    • 匹配图img,kp
    • match()方法返回的匹配结果

bf = BFMatchaer(cv2.NORM_L1)
match = bf.match(des1,des2)
cv2.drawMatches(img1,kp1,img2,kp2,match,None)

FLANN特征匹配


  • 优缺点:
    • 在进行批量特征匹配时,FLANN速度更快

    • 由于它使用的是临近近似值,所以精度较差



使用FLANN特征匹配的步骤:



  • 创建FLANN匹配器,FlannBasedMatcher()
    • index_params字典:匹配算法KDTREE(SIFT、SURF使用)、LSH(orb使用)
  • 进行特征匹配,flann.match/knnMatch()
  • 绘制匹配点,cv2.drawMatches/drawMatchesKnn()


KDTREE



  • search_params字典:知道KDTREE算法中遍历树的次数
  • 一般KDTREE设为5,search_params设为50,经验值,准确率高,速度快
  • index_params = dict(algorithm = FLANN_INDEX_KDTREE,trees = 5)
  • search_params = dict(checks = 50)


knnMatch()



  • 参数为SIFT、SURF、OBR等计算的描述子
  • k,表示取欧式距离最近的前k个关键点
  • 返回的是匹配的结果DMatch对象:
    • distance,描述子之间的距离,值越低越好
    • queryIdx,第一个图像的描述子索引值
    • trainIdx,第二个图的描述子索引值
    • imgIdx,第二个图的索引值


drawMatchesKnn()



  • 搜索img,kp
  • 匹配图img,kp
  • match()方法返回的匹配结果

#创建匹配器
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)
for i,(m,n) in enumerate(matches):
if m.distance < 0.7 * n.distance:
good.append(m)
ret &#61; cv2.drawMatchesKnn(img1,kp1,img2,kp2,[good],None)

图像查找


  • 特征匹配 &#43; 单应性矩阵

srcPts &#61; np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
dstPts &#61; np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
H,_ &#61; cv2.findHomography(srcPts,dstPts,cv2.RANSAC,5.0)
h,w &#61; img1.shape[:2]
pts &#61; np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2)
dst &#61; cv2.perspectiveTransform(pts,H)
cv2.polylines(img2,[np.int32(dst)],True,(0,0,255))

【实战】图像拼接

  • 图像合并的步骤&#xff1a;
    • 读文件并重置尺寸
    • 根据特征点和计算描述子&#xff0c;得到单应性矩阵
    • 图像变换
    • 图像拼接并输出图像

import cv2
import numpy as np
#第一步&#xff0c;读取文件&#xff0c;将图片设置成一样的大小 640*480
#第二步&#xff0c;找特征点&#xff0c;描述子&#xff0c;计算单应性矩阵
#第三步&#xff0c;根据单应性矩阵对图像进行变换&#xff0c;然后平移
#第四步&#xff0c;拼接并输出最终结果
def get_homo(img1,img2):
#1.创建特征转换对象
#2.通过调整转换对象获得特征点和描述子
#3.创建特征匹配器
#4.进行特征匹配
#5.过滤特征&#xff0c;找出有效的特征匹配点
sift &#61; cv2.xfeatures2d.SIFT_create()

k1, d1 &#61; sift.detectAndCompute(img1, None)
k2, d2 &#61; sift.detectAndCompute(img2, None)

bf &#61; cv2.BFMatcher()

matches &#61; bf.knnMatch(d1, d2, k&#61;2)

verify_ratio &#61; 0.8
for m1,m2 in matches:
if m1.distance < 0.8 * m2.distance :
verify_matches.append(m1)

min_matcher &#61; 8
if len(verify_matches) > min_matcher:
img1_pts &#61; []
img2_pts &#61; []

for m in verify_matches:
img1_pts.append(k1[m.queryIdx].pt)
img2_pts.append(k2[m.queryIdx].pt)

img1_pts &#61; np.float(img1_pts).reshape(-1,1,2)
img2_pts &#61; np.float(img2_pts).reshape(-1,1,2)

H, mask &#61; cv2.findHomography(img1_pts, img2_pts, cv2.RANSAC, 5.0)
return H
else:
print(&#39;error&#39;)
exit()

def stitch_image(img1, img2, H):
#1.获得每张图片的四个角点
#2.对图片进行变换&#xff08;单应性矩阵使图进行旋转&#xff0c;平移&#xff09;
#3.创建一张大图&#xff0c;将两张图拼接到一起
#4.将结果输出

#获得原始图的高/宽
h1, w1 &#61; img1.shape[:2]
h2, w2 &#61; img2.shape[:2]
img1_dims &#61; np.float([[0,0],[0,h1],[w1,h1],[w1,0]]).reshape(-1,1,2)
img2_dims &#61; np.float([[0,0],[0,h2],[w2,h2],[w2,0]]).reshape(-1,1,2)
img1_transform &#61; cv2.perspectiveTransform(img1_dims, H)

result_dims &#61; np.concatenate((img2_dims, img1_transform), axis &#61; 0)
[x_min, y_min] &#61; np.int32(result_dims.min(axis &#61; 0).ravel() - 0.5)
[x_max, y_max] &#61; np.int32(result_dims.min(axis &#61; 0).ravel() &#43; 0.5)
#平移的距离
transform_dist &#61; [-x_min, -y_min]
transform_array &#61; np.array[1,0,transform_dist[0],[0,1,transform_dist[1]],[0,0,1]]

result_img &#61; cv2.warpPerspective(img1, transform_array.dot(H), (x_max - x_min, y_max - y_min))
result_img[transform_dist[1]:transform_dist[1] &#43; h2,transform_dist[0]:transform_dist[0] &#43; w2] &#61; img2
return result_img
#读取两张图片
img1 &#61; cv2.imread(&#39;map1.png&#39;)
img2 &#61; cv2.imread(&#39;map2.png&#39;)
#将两张图片设置成同样大小
img1 &#61; cv2.resize(img1,(640,480))
img2 &#61; cv2.resize(img2,(640,480))
inputs &#61; np.hstack((img1,img2))
#获取单应性矩阵
H &#61; get_homo(img1,img2)
#进行图像拼接
result_image &#61; stitch_image(img1, img2, H)
cv2.imshow(&#39;input img&#39;,inputs)
cv2.waitKey(0)






推荐阅读
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • Ihaveaworkfolderdirectory.我有一个工作文件夹目录。holderDir.glob(*)>holder[ProjectOne, ... [详细]
  • 基于移动平台的会展导游系统APP设计与实现的技术介绍与需求分析
    本文介绍了基于移动平台的会展导游系统APP的设计与实现过程。首先,对会展经济和移动互联网的概念进行了简要介绍,并阐述了将会展引入移动互联网的意义。接着,对基础技术进行了介绍,包括百度云开发环境、安卓系统和近场通讯技术。然后,进行了用户需求分析和系统需求分析,并提出了系统界面运行流畅和第三方授权等需求。最后,对系统的概要设计进行了详细阐述,包括系统前端设计和交互与原型设计。本文对基于移动平台的会展导游系统APP的设计与实现提供了技术支持和需求分析。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 颜色迁移(reinhard VS welsh)
    不要谈什么天分,运气,你需要的是一个截稿日,以及一个不交稿就能打爆你狗头的人,然后你就会被自己的才华吓到。------ ... [详细]
  • 概述H.323是由ITU制定的通信控制协议,用于在分组交换网中提供多媒体业务。呼叫控制是其中的重要组成部分,它可用来建立点到点的媒体会话和多点间媒体会议 ... [详细]
author-avatar
蓝善凡_407
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有