赞
踩
Scale-Invariant Feature Transform尺度不变形特征变换,旨在于从图像中提取若干个特征点。
这些点的特征具有尺度不变性和旋转不变性,因而可以用于匹配不同尺度和旋转方向的图像内容。
物体以图像方式呈现时,往往会呈现较大的尺度变化
在机器视觉中,如何对尺度变化不敏感,是决定一个特征好坏的关键问题。
深度学习时代,通过多层卷积和池化处理,可以得到尺度不变形较高的深度特征;
而在前深度学习时代,如何能够提高特征的尺度不变性?
演示站点: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依赖于数据集的训练,如果数据集中全都是大尺度图片,则训练出来的对小尺度图片提取特征不好)
- 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)
- 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特征来说:
sift特征提取算法中,关键点的提取和筛选需要耗费大量的计算资源,surf则是对关键点的提取方法提出了改进,从而获得更加快速和稳健的特征表达。
利用hessian矩阵,特征点检测过程
通过不同尺寸的box filter来构建金字塔
使用haar小波变换来确定特征点的方式
图像积分就是对一定区域内的像素求和。
积分图中每个点的值,为该点至(0, 0)点的像素值之和。
为了节省计算资源,积分图可以用增量的方式计算。
即每个像素点不需要计算重合区域。
积分图的好处在于:积分图中任意矩形内的像素值之和,只需要确定四个角的像素即可(两个也)
对固定大小的矩形区域,计算该区域内所有像素的平均值,并作为中心像素的值。
高斯滤波需要计算每个像素值的加权和,但结合积分图和盒子滤波,就能通过查表运算来完成对不同尺度图像的滤波。
对不同尺度的图像构建hessian变换
hessian变换是指,通过三种不同的滤波模板,对图像进行滤波
而后以行列式的方式将其融合,得到最终的hessian矩阵图像
在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]
先对图像采用不同尺度的高斯模糊,而后采用不同尺度的hessian变换获取图像的差分金字塔
其中高斯模糊的过程用box filter获取,并通过积分图进行加速。
只改变尺度不改变图像的shape,得到的不同尺度图像的尺寸是一致的。
载sift中,同一个octave中的图像是逐渐缩小的,但surf中,同一个octave的图像是一样大的。
在这个过程中,积分图用来加速
同sift一样,特征点检测依然在同一个octave中的上下三层矩阵进行
选取27个相临像素后,如果当前中心点是一个极大值,那么就保留,否则去除
不同点在于:
sift使用差分高斯图像作为输入
surf使用hessian变换作为输入
相比sift,此处surf有三个主要优势:
- 1. 计算效率更高
-
- 2. 方向选择更加准确
-
- 3. 更加鲁棒
同样地,surf也会产生产生较多的噪声点,因此同样需要筛选。
为了保证特征点具有旋转不变形,也需要为每个特征点分配一个主要方向。
具体上,以特征点为中心,六倍标准差为半径的圆形区域,对图像进行haar小波响应运算。
Harr特征值反应了图像灰度变化的情况,那么这个主方向就是描述那些灰度变化特别剧烈的区域方向。
以特征点为中心,张角为π/3的扇形滑动,计算窗口内的Harr小波响应值dx、dy的累加
具体上:
- 1. 确定一个尺度,并确定关键点的方向区间。
- 2. 将关键点周围的区域划分为多个子区域,例如4x4的矩形子区域。
- 3. 在每个子区域内,通过Haar小波响应计算dx、dy的累加值,并通过高斯加权对每个子区域内的像素进行加权。
- 4. 根据计算得到的所有累加值,统计不同方向的响应值,并计算加权和,以确定关键点的主方向。
生成特征描述需要两个步骤:
对每个特征点,先获取20×20的区域,然后进行旋转
在图像上使用水平和垂直的haar模板求的响应,然后根据主方向旋转dx和dy与主方向保持一致
而后,将图像划分16个区域,分别计算haar响应。
每个haar响应为4个数值,因此surf算子得到的特征共有64个特征。
需要安装低版本的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好
平移:差不多
速度:surf比sift快3倍
ORB: Oriented FAST Rotated Brief
FAST算法 + brief算法
什么是关键点?
只要与周围足够多的像素相差较大,即可能为关键点
基本步骤:
- 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算法的基本作用:描述特征点周围的像素变化
基本步骤
高斯滤波平滑
以特征点为中心,选取S大小的窗口
2.1 随机选取窗口内的一对点,比较二者像素的大小,进行如下二进制赋值
- 如果第一个像素大于第二个像素,则为1
-
- 如果第二个像素小于第一个像素,则为0
-
2.2 重复上述过程,选择n个点进行描述比对,即可得到N个二进制的描述
其中,比较重要的是如何选取像素对。
选择多少对,就有多个个二进制数,也就是描述特征的维度。
通常,特征是512,256,128,64维等等(自己选择,与前两个不同:sift128维,surf64维)
算法虽然在速度和存储上优势明显,但是不具有旋转不变性、对噪声不够鲁棒等问题。
对噪声问题上,可以对图像进行平滑滤波;
或者是,在随机取点的时候,不再取单个点,而是取一个区域进行计算。从而可以克服噪声问题。
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()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。