图像特征提取

SIFT特征

Scale-Invariant Feature Transform尺度不变形特征变换,旨在于从图像中提取若干个特征点。  

这些点的特征具有尺度不变性和旋转不变性,因而可以用于匹配不同尺度和旋转方向的图像内容

动机

物体以图像方式呈现时,往往会呈现较大的尺度变化

在机器视觉中,如何对尺度变化不敏感,是决定一个特征好坏的关键问题。

深度学习时代,通过多层卷积和池化处理,可以得到尺度不变形较高的深度特征;

而在前深度学习时代,如何能够提高特征的尺度不变性?

sift特征提取过程

演示站点:http://weitz.de/sift/index.html?size=large

第一步:构建差分高斯金字塔

* 对一张图像采用不同程度的滤波,实现不同程度的模糊

* 对每一张模糊的图像,连续进行下采样,从而为每张图像都得到不同尺度的表达

import cv2 
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('../../dataset/lena.png', 0)
img = cv2.resize(img, [256, 256])
plt.imshow(img, cmap='gray')
plt.show()
blur1 = cv2.GaussianBlur(img, (5, 5), 0)
blur2 = cv2.GaussianBlur(img, (5, 5), 5)
plt.subplot(1, 2, 1)
plt.imshow(blur1, cmap='gray')
plt.subplot(1, 2, 2)
plt.imshow(blur2, cmap='gray')

然后,对每个图进行金字塔变换。

采用cv2.pyrDown函数进行下采样(即resize为更小的size)

o11 = cv2.pyrDown(blur1)
o12 = cv2.pyrDown(o11)

plt.subplot(1, 2, 1)
plt.imshow(o11, cmap='gray')
plt.subplot(1, 2, 2)
plt.imshow(o12, cmap='gray')

print(blur1.shape, o11.shape, o12.shape)
# (256, 256) (128, 128) (64, 64)

 对于每一个子图像,都与其上一级做差分,得到差的图像。

例如,o12是o11的上级图像,我们需要对这两张图像做差。

尺寸不一致怎么办?先对小图像进行上采样

o12x = cv2.pyrUp(o12)
d12 = cv2.absdiff(o11, o12x)

plt.subplot(1, 4, 1)
plt.imshow(o11, cmap='gray')
plt.subplot(1, 4, 2)
plt.imshow(o12, cmap='gray')
plt.subplot(1, 4, 3)
plt.imshow(o12x, cmap='gray')
plt.subplot(1, 4, 4)
plt.imshow(d12, cmap='gray')

 

那么对每一张图像都进行一次差分,并组成金字塔的形式,则称为了差分高斯金字塔

总结 difference of gaussian, DoG计算过程:

* 对图片采用不同的高斯方差进行滤波,从而得到不同模糊程度的图像

* 对每一张图像,建立一个octave

    - 对图像进行连续多层的下采样

* 对于每个octave,都进行上下之间的做差,得到dog

第二步,寻找关键点

极值点为:在某个区域里,具有最大或者最小值的点。

这里的极值点,不仅仅是同一层级图像中的极值点,也包括了不同尺度图像相同区域中的极值点。

如果我们把一个octvate视为一个tensor,那么我们就需要找出在所有$3*3*3$区域内的极值点。

即对于任意一个点来说,如果他比相临的26个值都大或者小,就认为是一个潜在的极值点。

如果只对单层的图像求取极值点,那么就是获得边缘

对于上下两层的图像求取极值点,则是说明,

**无论放大还是缩小图像,该极值点都会被消除,说明这是一个关键的点**

然而,除了关键点之外,噪声也会作为局部区域的极值存在。

我们将这些极值点(由噪声引起的)视为不稳定的极值点。

如何去除这些噪声?

 

 目前为止,我们已经获取了不同尺度上的关键点。

可以认为我们已经具有了具有尺度不变性的特征。

第三步:获取旋转不变性

首先,对任意关键点,先旋转图像,让关键点的梯度方向和平面直角坐标系的y轴重合 ;

其次,以关键点为圆心,获得16尺寸大小的区域;

在该区域内,划分成4的格子,并对每个像素的梯度方向进行统计,共8个方向;

将每个子区域中的梯度带权叠加,形成一个8维向量;

共16个子区域,因此特征总长度为8\*16=128;

这128维的向量,就是该关键点的sift特征。

与CNN相比:看CNN的训练数据,这种方式的尺度不变性通常更加好(因为cnn依赖于数据集的训练,如果数据集中全都是大尺度图片,则训练出来的对小尺度图片提取特征不好)

sift算子的opencv实现 

img = cv2.imread("../../dataset/lena.png", 0)
# plt.imshow(img)
sift = cv2.SIFT_create()
key_point = sift.detect(img, None)
print(len(key_point))# 1083
img=cv2.drawKeypoints(img,key_point,img)
plt.imshow(img)

 

SIFT算子的匹配和应用 

import cv2
import matplotlib.pyplot as plt
img_a = cv2.imread("../../dataset/box_in_scene.png", 0)
img_b = cv2.imread("../../dataset/box.png", 0)
plt.subplot(1,2,1)
plt.imshow(img_a)
plt.subplot(1,2,2)
plt.imshow(img_b)

sift = cv2.SIFT_create()
kp_a, des_a = sift.detectAndCompute(img_a, None)
print(len(kp_a), len(des_a), des_a[0].shape) # 969 969 (128,)
kp_b, des_b = sift.detectAndCompute(img_b, None)
print(len(kp_b), len(des_b), des_b[0].shape) # 604 604 (128,)

# 暴力匹配
bf = cv2.BFMatcher(crossCheck=True)
matches = bf.match(des_a, des_b)
matches = sorted(matches, key=lambda x: x.distance)
# 标出排在最前面的10个关键点(排在前面表示距离越短,相似度越高)
result = cv2.drawMatches(img_a, kp_a, img_b, kp_b, matches[:10], None, flags=2)
plt.imshow(result)

 

总结:遇到图像匹配等问题时,可以先考虑sift算子,可以避免深度学习繁琐的训练过程!

SIFT特征详解 - 简书

【动手学计算机视觉】第七讲:传统目标检测之SIFT特征 - 知乎

surf特征 

特征提取算法

对图像来说,提取特征的基本要求在于三个要点:

  • 尺度不变性
  • 旋转不变性
  • 平移不变性

那么对于这个特征,应当包含三个方面的内容:

  • 位置信息
  • 方向信息
  • 特征描述

对于sift特征来说:

  • 位置信息表征了图像中的关键匹配点位置
  • 方向信息则为特征添加了旋转不变性
  • 特征描述则是为特征添加了尺度不变性:因为关键点是从不同尺度中图像获取,并且从对应的16*16的对应尺度空间上获取特征。

surf对于sift的改进

sift特征提取算法中,关键点的提取和筛选需要耗费大量的计算资源,surf则是对关键点的提取方法提出了改进,从而获得更加快速和稳健的特征表达。

surf特征的基本步骤

surf特征的基本步骤

基本原理:

  • 利用hessian矩阵,特征点检测过程

  • 通过不同尺寸的box filter来构建金字塔

  • 使用haar小波变换来确定特征点的方式

基本概念

  1. 积分图

图像积分就是对一定区域内的像素求和。

积分图中每个点的值,为该点至(0, 0)点的像素值之和。

为了节省计算资源,积分图可以用增量的方式计算。

即每个像素点不需要计算重合区域。

积分图的好处在于:积分图中任意矩形内的像素值之和,只需要确定四个角的像素即可(两个也)

  1. 盒子滤波

对固定大小的矩形区域,计算该区域内所有像素的平均值,并作为中心像素的值。

高斯滤波需要计算每个像素值的加权和,但结合积分图和盒子滤波,就能通过查表运算来完成对不同尺度图像的滤波。

  1. hessian矩阵构建

对不同尺度的图像构建hessian变换

hessian变换是指,通过三种不同的滤波模板,对图像进行滤波

而后以行列式的方式将其融合,得到最终的hessian矩阵图像

 

  1. haar小波响应

在surf算法中,haar小波响应运算用于计算图像区域的特征向量。

对每个检测到得到特征点,周围4×4的区域将被用于计算该特征点的特征向量。

例如,对于一个20×20的区域,首先将其划分为4×4的子块

1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7

而后,按照行列分别求和,得到

sum_rows: [10 14 18 22]
sum_cols: [10 14 18 22]

可根据差别计算小波响应值

H1 = sum_rows[0] + sum_rows[2] - sum_rows[1] - sum_rows[3] = -4
H2 = sum_cols[0] + sum_cols[2] - sum_cols[1] - sum_cols[3] = -4
H3 = sum_rows[0] + sum_cols[2] - sum_rows[1] - sum_cols[3] 
H4 = sum_cols[0] + sum_rows[2] - sum_cols[1] - sum_rows[3] 

基本步骤

  1. 获取空间金字塔

先对图像采用不同尺度的高斯模糊,而后采用不同尺度的hessian变换获取图像的差分金字塔

其中高斯模糊的过程用box filter获取,并通过积分图进行加速。

只改变尺度不改变图像的shape,得到的不同尺度图像的尺寸是一致的。

载sift中,同一个octave中的图像是逐渐缩小的,但surf中,同一个octave的图像是一样大的。

在这个过程中,积分图用来加速

  1. 特征点检测

同sift一样,特征点检测依然在同一个octave中的上下三层矩阵进行

选取27个相临像素后,如果当前中心点是一个极大值,那么就保留,否则去除

不同点在于:

  • sift使用差分高斯图像作为输入

  • surf使用hessian变换作为输入

相比sift,此处surf有三个主要优势:

1. 计算效率更高

2. 方向选择更加准确

3. 更加鲁棒

同样地,surf也会产生产生较多的噪声点,因此同样需要筛选。

  1. 确定主方向

为了保证特征点具有旋转不变形,也需要为每个特征点分配一个主要方向。

具体上,以特征点为中心,六倍标准差为半径的圆形区域,对图像进行haar小波响应运算。

Harr特征值反应了图像灰度变化的情况,那么这个主方向就是描述那些灰度变化特别剧烈的区域方向。

以特征点为中心,张角为π/3的扇形滑动,计算窗口内的Harr小波响应值dx、dy的累加

具体上:

1. 确定一个尺度,并确定关键点的方向区间。
2. 将关键点周围的区域划分为多个子区域,例如4x4的矩形子区域。
3. 在每个子区域内,通过Haar小波响应计算dx、dy的累加值,并通过高斯加权对每个子区域内的像素进行加权。
4. 根据计算得到的所有累加值,统计不同方向的响应值,并计算加权和,以确定关键点的主方向。
  1. surf特征描述

生成特征描述需要两个步骤:

  • 旋转方向
  • 描述特征

对每个特征点,先获取20×20的区域,然后进行旋转

在图像上使用水平和垂直的haar模板求的响应,然后根据主方向旋转dx和dy与主方向保持一致

而后,将图像划分16个区域,分别计算haar响应。

每个haar响应为4个数值,因此surf算子得到的特征共有64个特征。

surf特征的opencv实现

 需要安装低版本的opencv

import cv2 as cv
import numpy as np
import argparse

img1 = cv.imread("../../dataset/lena.png", cv.IMREAD_GRAYSCALE)
img2 = cv.imread("../../dataset/lena.png", cv.IMREAD_GRAYSCALE)

#-- Step 1: Detect the keypoints using SURF Detector, compute the descriptors
minHessian = 400
detector = cv.xfeatures2d_SURF.create(hessianThreshold=minHessian)
keypoints1, descriptors1 = detector.detectAndCompute(img1, None)
keypoints2, descriptors2 = detector.detectAndCompute(img2, None)
#-- Step 2: Matching descriptor vectors with a brute force matcher
# Since SURF is a floating-point descriptor NORM_L2 is used
matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_BRUTEFORCE)
matches = matcher.match(descriptors1, descriptors2)
#-- Draw matches
img_matches = np.empty((max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1], 3), dtype=np.uint8)
cv.drawMatches(img1, keypoints1, img2, keypoints2, matches, img_matches)
#-- Show detected matches
cv.imshow('Matches', img_matches)
cv.waitKey()

sift和surf优劣:

从尺度、旋转和平移不变性和速度方面评价。

尺度:sift好

旋转:surf好

平移:差不多

速度:surf比sift快3倍

ORB特征

ORB: Oriented FAST Rotated Brief

FAST算法 + brief算法

fast算法

什么是关键点?

只要与周围足够多的像素相差较大,即可能为关键点

基本步骤:

1. 海选:当一个像素和周围大部分相随都不同,则认为是一个候选点。
2. 精选:将聚集在一起的像素选出最不同的那个,作为该小区域的关键点

海选步骤:

对图像中的任意点x(i,j), 像素值为Ix.

以r为圆心,半径为3,确定一个圆,圆的边上,共有16个像素点,记为{Ix1,...,Ix16}

进而,确定一个阈值t,如果存在n个点满足Ix−Ixi>t或Ix−Ixi<−t,则认为是一个候选点。

通常n选择9,11,12等。

先与上下左右四个像素作差,如果超过3个满足条件再与周围16个作差比较(节省时间) 

当然,fast算法按照上述步骤,显然需要大量计算。因此实际中,会选择一种快速算法如下

对比上下左右四个点,如果四个点有3个满足条件,则继续对该点进行16像素检测;

否则,认为该点不是一个关键点。

精选步骤:

海选步骤中会存在很大的问题: 关键点太多

因此,利用极大值抑制算法来实现精选

具体上,

  • 每个点计算一个和周围16个像素之间的差分和为V

  • 在一定范围内的所有点,只保留具有最大值v的点

由上述过程筛选的特征点存在什么缺点?

  • 单一尺度

  • 缺少方向

如何解决尺度不变性问题?

图像金字塔

构建图像金字塔,先模糊,后降采样,对每层金字塔做特征点检测,所有特征点集合作为图像的特征点

Brief算法--特征点的描述

brief算法的核心思想:将特征转化为一个二进制的表达。

brief算法的基本作用:描述特征点周围的像素变化

基本步骤

  1. 高斯滤波平滑

  2. 以特征点为中心,选取S大小的窗口

    2.1 随机选取窗口内的一对点,比较二者像素的大小,进行如下二进制赋值

     如果第一个像素大于第二个像素,则为1
    
     如果第二个像素小于第一个像素,则为0
    
    

    2.2 重复上述过程,选择n个点进行描述比对,即可得到N个二进制的描述

其中,比较重要的是如何选取像素对

选择多少对,就有多个个二进制数,也就是描述特征的维度

通常,特征是512,256,128,64维等等(自己选择,与前两个不同:sift128维,surf64维)

biref的不足及改进

算法虽然在速度和存储上优势明显,但是不具有旋转不变性、对噪声不够鲁棒等问题。

对噪声问题上,可以对图像进行平滑滤波;

或者是,在随机取点的时候,不再取单个点,而是取一个区域进行计算。从而可以克服噪声问题。

steer brief 结合fast中求取的方向,来旋转图像后提取特征。考虑到效率,采用先选择随机坐标,然后进行特征提取的方式进行。可以获得一些旋转不变性。

rBrief 利用机器学习的方法选择数据点。可以获得更高的性能。

关于特征的匹配

既然是二进制特征,如何评价两个特征是否相似?

汉明距离:统计两个二进制特征中不同的比特位数量。

应用:

import cv2
import matplotlib.pyplot as plt

img1 = cv2.imread('E:/notebook/1.jpg', 0)
img2 = cv2.imread('E:/notebook/2.png', 0)
# 参考图片太大了(太过于关注细小的细节,把图片放大)
img2_ = cv2.resize(img2, (100, 200)) 

plt.subplot(1, 3, 1)
plt.imshow(img1, cmap='gray')
plt.subplot(1, 3, 2)
plt.imshow(img2, cmap='gray')
plt.subplot(1, 3, 3)
plt.imshow(img2_, cmap='gray')
plt.show()

 

orb = cv2.ORB_create()
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2_, None)
# img_kp1 = cv2.drawKeypoints(img1, kp1, None, color=(0,255,0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# 显示位置和方向(且大小不同的○)
img_kp1 = cv2.drawKeypoints(img1, kp1, None, color=(0,255,0), flags=0)
img_kp2 = cv2.drawKeypoints(img2_, kp2, None, color=(0,255,0), flags=0)
plt.subplot(1, 2, 1)
plt.imshow(img_kp1, cmap='gray')
plt.subplot(1, 2, 2)
plt.imshow(img_kp2, cmap='gray')
plt.show()

bf = cv2.BFMatcher_create(cv2.NORM_HAMMING, crossCheck=True)
bf = cv2.BFMatcher_create(crossCheck=True)
matches = bf.match(des1, des2)
matchImg = cv2.drawMatches(img1, kp1, img2_, kp2, matches, None)
plt.imshow(matchImg)
plt.show()

 

 

opencv学习笔记(二十三):图像特征检测之SURF、FAST、BRIEF、OBR算法 - 知乎

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值