OpenCV_11 特征检测与匹配

前言

本节主要介绍了特征检测和匹配的算法,以及OpenCV中相应API的使用,最后实现了图像的查找。


目录

  • 前言
  • 一、特征相关概念
    • 1.特征
    • 2.应用场景
  • 二、特征检测
    • 1.Harris角点
    • 2.Shi-Tomasi角点检测
    • 3.SIFT(scale-Invariant Feature Transform)
      • 使用SIFT的基本步骤:
      • 关键点和描述子
    • 4.SURF(Speeded-up Robust Features)
      • 使用SURF的基本步骤:
    • 5.ORB(Oriented FAST and Rotated BRIEF)
      • 使用ORB的基本步骤:
  • 三、特征匹配
    • 1.BF(Brute-Force)
    • 2.FLANN
  • 四、图像查找
    • 1.齐次坐标
      • 齐次坐标是什么?
      • 齐次坐标的意义
    • 2.单应性
      • 概念
      • 计算
    • 3.图像查找

一、特征相关概念

1.特征

图像的特征是指图像中具有独特性和易于识别性的区域,角、边缘、斑点以及高密度区等都属于有意义的特征。

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

2.应用场景

  • 图像搜索,以图搜图
  • 拼图游戏
  • 图像拼接,将两个有关联的图拼接到一起。

二、特征检测

1.Harris角点

Harris角点检测是通过观察特定窗口内像素的变化情况来判断角点的。

  • 光滑地区,无论向哪里移动,衡量系数不变
  • 边缘处,垂直边缘移动时,衡量系数变化剧烈
  • 角点处,任意方向移动时,衡量系数都变化剧烈
    OpenCV_11 特征检测与匹配_第1张图片

cornerHarris()
声明:void cornerHarris( InputArray src, OutputArray dst, int blockSize, int ksize, double k, int borderType = BORDER_DEFAULT );
参数:
   src:图像(灰度)
   blockSize:检测窗口大小
   ksize:Sobel的卷积核
   k:权重系数,经验值,一般取0.02~0.04之间

代码案例:

import cv2
import numpy as np 

blockSize = 2
ksize = 3
k = 0.04

img = cv2.imread('./picture/chess.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
dst = cv2.cornerHarris(gray, blockSize, ksize, k)

# Harris角点展示
img[dst > 0.01*dst.max()] = [0, 0, 255]

cv2.imshow('Harris', img)
cv2.waitKey(0)

结果展示:
OpenCV_11 特征检测与匹配_第2张图片

2.Shi-Tomasi角点检测

Shi-Tomasi角点检测是由Harris角点检测的改进,不用再设置k值

goodFeaturesToTrack()
声明:void goodFeaturesToTrack( InputArray image, OutputArray corners, int maxCorners, double qualityLevel, double minDistance, InputArray mask = noArray(), int blockSize = 3, bool useHarrisDetector = false, double k = 0.04 );
参数:
   image:图像(灰度)
   maxCorners:角点的最大数,为0时无限制
   qualityLevel:角点的质量,小于1.0的正数,一般在0.01~0.1
   minDistance:角之间最小欧氏距离,忽略小于此距离的点
   mask:感兴趣(检测)区域
   blockSize:检测窗口大小
   useHarrisDetector:是否使用Harris算法
   k:权重系数,默认0.04

代码示例:

corners = cv2.goodFeaturesToTrack(gray, 1000, 0.01, 10)    #Shi-Tomasi角点检测
corners = np.int0(corners)

#Shi-Tomasi角点展示
for i in corners:
    x, y = i.ravel()
    cv2.circle(img, (x, y), 4, (255, 0, 0), -1)

3.SIFT(scale-Invariant Feature Transform)

SIFT出现的原因:Harris角点具有旋转不变的特性,但缩放后,原来的角点可能就不是角点了。
OpenCV_11 特征检测与匹配_第3张图片

使用SIFT的基本步骤:

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

detect()
声明:void detect( InputArray image, CV_OUT std::vector& keypoints, InputArray mask=noArray() );
参数:
   image:图像(灰度)
   mask:检测区域,检测全图设为None

drawKeypoints()
声明:void drawKeypoints( InputArray image, const std::vector& keypoints, InputOutputArray outImage, const Scalar& color=Scalar::all(-1), DrawMatchesFlags flags=DrawMatchesFlags::DEFAULT );
参数:
   image:源图像
   keypoints:detect求出的关键值
   outImage:输出图像
   color:关键点颜色
   flags:图形特征的标志

代码案例:

import cv2
import numpy as np 

img = cv2.imread('./picture/chess.jpeg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

sift = cv2.SIFT_create()

# 得到所有的关键点
keypoints = sift.detect(gray, None)  # 计算关键点

# 将关键点标记的图片上
img2 = cv2.drawKeypoints(image=gray, outImage=img, keypoints=keypoints)

cv2.imshow('sift', img)
cv2.waitKey(0)

结果展示:
OpenCV_11 特征检测与匹配_第4张图片

关键点和描述子

  • 关键点: 位置、大小和方向。
  • 关键点描述子: 记录了关键点周围对其有贡献的像素点的一组向量值,其不受仿射变换、光照变换等影响,主要用于特征匹配。

计算描述子

compute()
声明:void compute( InputArray image, CV_OUT CV_IN_OUT std::vector& keypoints, OutputArray descriptors );
参数:
   image:源图像
   keypoints:detect求出的关键值
返回值:
   keypoints:关键点
   descriptors:描述子

detectAndCompute()
声明:void detectAndCompute( InputArray image, InputArray mask, CV_OUT std::vector& keypoints, OutputArray descriptors, bool useProvidedKeypoints=false );
参数:
   image:源图像
   mask:检测区域,检测全图设为None
返回值:
   keypoints:关键点
   descriptors:描述子

代码示例:

keypoints, descriptors= sift.detectAndCompute(gray, None)

4.SURF(Speeded-up Robust Features)

SURF的优点:
SIFT检测最大的问题是速度慢,因此才有SURF。

使用SURF的基本步骤:

  • 创建SURF对象
  • 进行检测,kp, des = surf.detectAndCompute(img, mask, …)
  • 绘制关键点,drawKeypoints(gray, kp, img)

用法基本与sift一致。

5.ORB(Oriented FAST and Rotated BRIEF)

ORB(Oriented FAST and Rotated BRIEF)是一种快速特征点提取和描述的算法。ORB算法分为两部分,分别是特征点提取和特征点描述。特征提取是由FAST(Features from Accelerated Segment Test)算法发展来的,特征点描述是根据BRIEF(Binary Robust Independent Elementary Features)特征描述算法改进的。ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。

  • ORB可以做到实时检测
  • ORB完全开源
  • ORB检测到的特征点大大减少,依次获得速度上的极大提升。

使用ORB的基本步骤:

  • 创建ORB对象 orb = cv2.ORB_create()
  • 进行检测,kp = orb.detect(img, …)
  • 绘制关键点,drawKeypoints(gray, kp, img)

代码案例:

# 创建orb对象
orb = cv2.ORB_create()
# 计算得到所有的关键点,描述子
keypoints, descriptors= orb.detectAndCompute(gray, None)

# 将关键点标记的图片上
img2 = cv2.drawKeypoints(image=gray, outImage=img, keypoints=keypoints)

结果展示:
OpenCV_11 特征检测与匹配_第5张图片

三、特征匹配

1.BF(Brute-Force)

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

特征匹配步骤:

  • 创建匹配器,BFMatcher( normType, crossCheck );
  • 进行特征匹配,bf.match(des1, des2)
  • 绘制匹配点,cv2.drawMatches(img1, kp1, img2, kp2, ...)

BFMatcher
声明:BFMatcher( int normType=NORM_L2, bool crossCheck=false );
normType:
   NORM_L1和NORM_L2:用于sift和surf
   HAMMING1和HAMMING2:用于ORB
crossCheck:是否进行交叉匹配,默认false

match方法
源码:void match( InputArray queryDescriptors, InputArray trainDescriptors, CV_OUT std::vector& matches, InputArray mask=noArray() ) const;
参数为计算得到的描述子。

drawMatches()
参数:
   搜索img,kp
   匹配图img,kp
   match方法返回的匹配结果

代码案例:

import cv2
import numpy as np 

# img = cv2.imread('./picture/chess.jpeg')
img1 = cv2.imread('./picture/opencv1.jpeg')
img2 = cv2.imread('./picture/opencv2.jpeg')

# gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# 创建sift对象
sift = cv2.SIFT_create()

# 创建surf对象
# surf = cv2.SURF_create()

# 得到所有的关键点,描述子
keypoints1, descriptors1 = sift.detectAndCompute(g1, None)
keypoints2, descriptors2 = sift.detectAndCompute(g2, None)

bf = cv2.BFMatcher(cv2.NORM_L1)     #创建匹配器
match = bf.match(descriptors1, descriptors2)        #进行特征匹配

img3 = cv2.drawMatches(img1, keypoints1, img2, keypoints2, match, None)         #绘制匹配点

cv2.imshow('img3', img3)

cv2.waitKey(0)

结果展示:
OpenCV_11 特征检测与匹配_第6张图片

2.FLANN

FLANN优缺点:

  • 在进行批量特征匹配时,FLANN速度更快。
  • 由于它使用的是邻近近似值,所以精度较差。

特征匹配步骤:

  • 创建FLANN匹配器,FlannBasedMatcher(...);
  • 进行特征匹配,flann.match/knnMatch(...)
  • 绘制匹配点,cv2.drawMatches/drawMatchesKnn(...)

FlannBasedMatcher()
源码:FlannBasedMatcher( const Ptr& indexParams=makePtr(), const Ptr& searchParams =makePtr() );
参数:
   indexParams (字典):匹配算法 KDTREE、LSH
   searchParams (字典):指定KDTREE中遍历次数
   一般KDTREE设为 5,遍历50次
KDTREE:
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks = 50)

knnMatch()
源码:knnMatch( InputArray queryDescriptors, InputArray trainDescriptors, CV_OUT std::vector >& matches, int k, InputArray mask=noArray(), bool compactResult=false ) const;
参数:
   SIFT, SURF, ORB等计算的描述子
   k :表示取欧氏距离最近的前k个关键点
返回值:匹配结果的DMatch对象
   distance:描述子之间的距离,越小越好
   queryIdx:第一个图像的描述子索引值
   trainIdx:第二个图像的描述子索引值
   imgIdx:第二个图的索引值

knnMatch()
源码:knnMatch( InputArray queryDescriptors, InputArray trainDescriptors, CV_OUT std::vector >& matches, int k, InputArray mask=noArray(), bool compactResult=false ) const;
参数:
   SIFT, SURF, ORB等计算的描述子
   k :表示取欧氏距离最近的前k个关键点
返回值:匹配结果的DMatch对象
   distance:描述子之间的距离,越小越好
   queryIdx:第一个图像的描述子索引值
   trainIdx:第二个图像的描述子索引值
   imgIdx:第二个图的索引值

drawMatchesKnn()
参数:
   搜索img,kp
   匹配图img,kp
   match方法返回的匹配结果

代码案例:

import cv2
import numpy as np 

# img = cv2.imread('./picture/chess.jpeg')
img1 = cv2.imread('./picture/opencv1.jpeg')
img2 = cv2.imread('./picture/opencv2.jpeg')

# gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# 创建sift对象
sift = cv2.SIFT_create()

# 得到所有的关键点,描述子
keypoints1, descriptors1 = sift.detectAndCompute(g1, None)
keypoints2, descriptors2 = sift.detectAndCompute(g2, None)

#创建匹配器
index_params = dict(algorithm = 1, trees = 5)
search_params = dict(checks = 50)
flann = cv2.FlannBasedMatcher(index_params, search_params)

match = flann.knnMatch(descriptors1, descriptors2, k = 2)        #进行特征匹配

# 过滤出匹配效果好的点
good = []
for i, (m, n) in enumerate(match):
    if m.distance < 0.7 * n.distance:
        good.append(m)

img3 = cv2.drawMatchesKnn(img1, keypoints1, img2, keypoints2, [good], None)         #绘制匹配点

cv2.imshow('result', img3)

cv2.waitKey(0)

结果展示:
OpenCV_11 特征检测与匹配_第7张图片

四、图像查找

1.齐次坐标

齐次坐标是什么?

  • 齐次坐标就是用N+1维来代表N维坐标:
    我们可以在一个2D笛卡尔坐标(X, Y)末尾加上一个额外的变量w来形成2D齐次坐标(x, y, w),并且有:
       X = x / w
       Y = y / w
  • 平行线在透视空间的无穷远处交于一点,但是在欧氏空间却不能。

齐次坐标的意义

图像的缩放变换和旋转变换可以用矩阵乘法的形式来表达变换后的像素位置映射关系。而对于平移变换却要通过矩阵加法实现,这无疑是不方便处理的,那么有没有办法将图像平移也通过矩阵乘法实现?这个方法就是齐次坐标,这样平面图形人依次的缩放、旋转和平移变换就可以通过矩阵连乘来实现。
齐次坐标系表示平移:
OpenCV_11 特征检测与匹配_第8张图片
上面只是简单介绍了齐次坐标,可以去齐次坐标系详解深入了解。

2.单应性

概念

单应性变换是指从图像中的一个点映射到另一张图像中的对应点的变换。
OpenCV_11 特征检测与匹配_第9张图片
上面左图可以通过单应性变化与右图对齐,通过3 * 3的矩阵实现,两点的坐标使用的是齐次坐标。
OpenCV_11 特征检测与匹配_第10张图片
OpenCV_11 特征检测与匹配_第11张图片
不在平面上的点不会通过单应性对齐,不同平面的单应性矩阵是不一样的。场景是平面,或者近似平面,或者低视差时都可以应用单应性矩阵。

计算

为了计算两个图像之间的单应性,需要知道两个图像之间的至少4个点对应关系,多余4个对应点更好。估计出最适合所有对应点的单应性矩阵。通常,这些点对应是通过图像之间的SIFT或SURF等特征匹配自动找到的。

3.图像查找

当我们通过一张图找另一张图时的步骤是

  • 进行特征匹配,获取单应性矩阵
  • 进行透视变换
  • 就可以找出图中是否有想找的图像

findHomography()
源码:Mat findHomography( InputArray srcPoints, InputArray dstPoints, int method = 0, double ransacReprojThreshold = 3, OutputArray mask=noArray(), const int maxIters = 2000, const double confidence = 0.995);
参数:
   srcPoints:queryIdx第一个图像的描述子索引值
   dstPoints :trainIdx:第二个图像的描述子索引值
   method:对点进行过滤的方法
   ransacReprojThreshold:上述方法的阈值
返回值:
   单应性矩阵
   掩码(不需要)

代码案例:

import cv2
import numpy as np 

# img = cv2.imread('./picture/chess.jpeg')
img1 = cv2.imread('./picture/opencv1.jpeg')
img2 = cv2.imread('./picture/opencv2.jpeg')

# gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
g1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
g2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

# 创建sift对象
sift = cv2.SIFT_create()

# 得到所有的关键点,描述子
keypoints1, descriptors1 = sift.detectAndCompute(g1, None)
keypoints2, descriptors2 = sift.detectAndCompute(g2, None)

#创建匹配器
index_params = dict(algorithm = 1, trees = 5)
search_params = dict(checks = 50)
flann = cv2.FlannBasedMatcher(index_params, search_params)

match = flann.knnMatch(descriptors1, descriptors2, k = 2)        #进行特征匹配

# 过滤出匹配效果好的点
good = []
for i, (m, n) in enumerate(match):
    if m.distance < 0.7 * n.distance:
        good.append(m)



# 计算单应性矩阵(特征点必须多于4个)
if len(good) >= 4:
    srcPoints = np.float32([keypoints1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
    dstPoints = np.float32([keypoints2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
    H, _ = cv2.findHomography(srcPoints, dstPoints, cv2.RANSAC, 5.0)

    # 透视变换
    h, w = img1.shape[:2]
    pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 1, 2)
    dst = cv2.perspectiveTransform(pts, H)
    #框出所找图像
    cv2.polylines(img2, [np.int32(dst)], True, (0, 0, 255))
else:
    print("特征点数量少于四个!")
    exit()

img3 = cv2.drawMatchesKnn(img1, keypoints1, img2, keypoints2, [good], None)         #绘制匹配点
cv2.imshow('result', img3)

cv2.waitKey(0)

结果展示:
OpenCV_11 特征检测与匹配_第12张图片


你可能感兴趣的:(OpenCV,opencv,计算机视觉,python)