笔记总结。python+opencv_tutorial、CPP+opencv_tutorial
设想两幅图像的颜色直方图几乎相同, 只是互相错开了一个bin, 这时如果采用L1距离或者欧拉距离计算两者的相似度, 会得到很小的相似度值。
为了克服这个缺陷, 需要考虑到相似但不相同的颜色之间的相似度:
一阶导数的极值点就是边缘。
两个方向求导数,再联立起来。
高斯函数的 σ \sigma σ取不同值
从不同的距离, 不同的方向、 角度, 不同的光照条件下观察一个物体时, 物体的大小,形状, 明暗都会有所不同。 但我们依然可以判断它是同一件物体。
理想的特征描述子应该具备这些性质。 即,在大小、 方向、 明暗不同的图像中, 同一特征点应具有足够相似的描述子, 称之为描述子的可复现性。
Harris角点参考
FAST具体计算过程:
斑点:二阶导数取最大值最小值的地方!
两阶两个方向的导数相加,但对噪声很敏感, 首先对图像进行高斯卷积滤波进行降噪处理, 再采用Laplace算子进行边缘检测 : L a p l a c i a n o f G a u s s i a n ( L o G ) \quad:Laplacian of Gaussian (LoG) :LaplacianofGaussian(LoG)当   σ   \,\sigma\, σ较小时, 将识别出更为细节的边缘。
图像尺度空间:
平时生活中,用人眼去看一张照片时,随着观测距离的增加,图像会逐渐变得模糊。那么计算机在“看”一张照片时,会从不同的“尺度”去观测照片,尺度越大,图像越模糊。
那么这里的“尺度”就是二维高斯函数当中的σ值,一张照片与二维高斯函数卷积后得到很多张不同σ值的高斯图像,这就好比你用人眼从不同距离去观测那张照片。所有不同尺度下的图像,构成单个原始图像的尺度空间。“图像尺度空间表达”就是图像在所有尺度下的描述。
尺度是自然客观存在的,不是主观创造的。高斯卷积只是表现尺度空间的一种形式。
基于尺度空间不变的特征
SIFT特征计算步骤
高斯尺度空间 ( G S S − G a u s s S c a l e S p a c e ) : (GSS - Gauss Scale Space): (GSS−GaussScaleSpace):
唯一能产生尺度空间的核为高斯核函数,在低通滤波中,高斯平滑滤波无论是时域还是频域都十分有效。高斯函数具有五个重要性质:
(1)二维高斯具有旋转对称性;
(2)高斯函数是单值函数;
(3)高斯函数的傅立叶变换频谱是单瓣的;
(4)高斯滤波器宽度(决定着平滑程度)是由参数σ表征的,而且σ和平滑程度的关系是非常简单的;
(5)二维高斯滤波的计算量随滤波模板宽度成线性增长而不是成平方增长。
所以我们将图像的尺度空间表示成一个函数 L ( x , y , σ ) L(x,y,σ) L(x,y,σ),它是由一个变尺度的高斯函数 G ( x , y , σ ) G(x,y,σ) G(x,y,σ)与图像 I ( x , y ) I(x,y) I(x,y)卷积产生的。即
L ( x , y , σ ) = G ( x , y , σ ) ∗ I ( x , y ) L(x, y, σ) = G(x, y, σ) ∗ I(x, y) L(x,y,σ)=G(x,y,σ)∗I(x,y)其中 ∗ * ∗代表卷积操作,$G(x, y, σ) $为二维高斯核函数,表示为: G ( x , y , σ ) = 1 2 π σ 2 e − ( x 2 + y 2 ) / 2 σ 2 G(x,y,\sigma) = \frac{1}{2\pi\sigma^2}e^{-(x^2+y^2)/2\sigma^2} G(x,y,σ)=2πσ21e−(x2+y2)/2σ2
高 斯 核 高斯核 高斯核
高斯差分 ( D O G − D i f f e r e n c e o f G a u s s ) : (DOG - Difference of Gauss): (DOG−DifferenceofGauss):
S I F T SIFT SIFT算法建议,在某一尺度上对特征点的检测,可以通过对两个相邻高斯尺度空间的图像相减,得到一个 D o G ( D i f f e r e n c e o f G a u s s i a n s ) DoG (Difference of Gaussians) DoG(DifferenceofGaussians)的响应值图像 D ( x , y , σ ) D(x,y,σ) D(x,y,σ)。然后,仿照 L o G LoG LoG方法,通过对响应值图像 D ( x , y , σ ) D(x,y,σ) D(x,y,σ)进行非最大值抑制(局部极大搜索,正最大和负最大),在位置空间和尺度空间中定位特征点。其中 D ( x , y , σ ) = ( G ( x , y , k σ ) − G ( x , y , σ ) ) ∗ I ( x , y ) = L ( x , y , k σ ) − L ( x , y , σ ) D(x, y, σ) = (G(x, y, kσ) − G(x, y, σ)) ∗ I(x, y) = L(x, y, kσ) − L(x, y, σ) D(x,y,σ)=(G(x,y,kσ)−G(x,y,σ))∗I(x,y)=L(x,y,kσ)−L(x,y,σ)式中, k k k为相邻尺度空间倍数的常数。
为什么用 D O G DOG DOG来检测特征点:
L i n d e b e r g Lindeberg Lindeberg证明用 σ 2 σ^2 σ2标准化的高斯拉普拉斯 ( ∇ 2 G , L O G , L a p l a c i a n o f G a u s s ) (∇^2G, LOG, Laplacian of Gauss) (∇2G,LOG,LaplacianofGauss)有着真正的尺度无关的特性,而 M i k o l a j c z y k Mikolajczyk Mikolajczyk发现,相比于其他一系列函数(比如梯度, H e s s i a n Hessian Hessian, H a r r i s Harris Harris角点函数等),用 σ 2 σ^2 σ2标准化的高斯拉普拉斯 ( σ 2 ∇ 2 G ) (σ^2∇^2G) (σ2∇2G)有着更稳定的图像特征,因此 σ 2 ∇ 2 G σ^2∇^2G σ2∇2G函数是我们理想的寻找特征点的函数。而在此,我们利用 D o G DoG DoG来近似 σ 2 ∇ 2 G σ^2∇^2G σ2∇2G。
通过热传导方程也可以帮助我们理解 D o G DoG DoG与 σ 2 ∇ 2 G σ^2∇^2G σ2∇2G之间的近似关系。由热传导方程可知(通常用 t = σ 2 t=σ^2 t=σ2): ∂ G ∂ σ = σ ∇ 2 G \frac{\partial G}{\partial \sigma} =\sigma \nabla^2G ∂σ∂G=σ∇2G使用 L O G LOG LOG, 则后续计算量较大, 故使用 D O G DOG DOG来代替 L O G LOG LOG, 用差分代替微分. σ ∇ 2 G = ∂ G ∂ σ ≈ G ( x , y , k σ ) − G ( x , y , σ ) k σ − σ \sigma { \nabla }^{ 2 }G =\frac { \partial G }{ \partial \sigma } \approx \frac{G(x,y,k\sigma)-G(x,y,\sigma)}{k\sigma-\sigma} σ∇2G=∂σ∂G≈kσ−σG(x,y,kσ)−G(x,y,σ)
因此:
D o G = G ( x , y , k σ ) − G ( x , y , σ ) ≈ ( k − 1 ) σ 2 ∇ 2 G DoG = G(x, y, kσ) − G(x, y, σ) ≈ (k − 1)σ^2∇^2G DoG=G(x,y,kσ)−G(x,y,σ)≈(k−1)σ2∇2G
而常数项(k - 1)并不会影响极值的位置。所以 D o G DoG DoG是 σ 2 ∇ 2 G σ^2∇^2G σ2∇2G的近似。
对于使用 D o G DoG DoG来近似 σ 2 ∇ 2 G σ^2∇^2G σ2∇2G,有如下优点:
( 1 ) σ 2 ∇ 2 G (1)σ^2∇^2G (1)σ2∇2G需要使用两个方向的高斯二阶微分卷积核,而 D o G DoG DoG直接使用高斯卷积核,省去了对卷积核的生成的运算量。
( 2 ) D o G (2)DoG (2)DoG保留了各个高斯尺度空间的图像,这样,在生成某一空间尺度的特征时,可以直接尺度空间图像,而无需重新再次生成该尺度的图像。
高斯核性质及其在 S I F T SIFT SIFT中的应用:
对于二维高斯卷积,有如下性质:
1、距离高斯核中心 3 σ 3σ 3σ距离外的系数很小,相对于 3 σ 3σ 3σ内的系数值可以忽略不计,所以只用 ( 6 σ + 1 ) ∗ ( 6 σ + 1 ) (6σ + 1)*(6σ + 1) (6σ+1)∗(6σ+1)的面积计算卷积即可。
2、线性可分,二维高斯核卷积(计算次数 O ( n 2 ∗ M ∗ N ) O(n2*M*N) O(n2∗M∗N))效果等于水平和竖直方向的两个一维高斯核 ( 计 算 次 数 O ( n ∗ M ∗ N ) + O ( n ∗ M ∗ N ) ) ) (计算次数O(n*M*N) + O(n*M*N))) (计算次数O(n∗M∗N)+O(n∗M∗N)))累积处理的效果。 n ∗ n n*n n∗n为滤波器大小, M ∗ N M*N M∗N图像大小。
3、对一幅图像进行多次连续高斯模糊的效果与一次更大的高斯模糊可以产生同样的效果,大的高斯模糊的半径是所用多个高斯模糊半径平方和的平方根。例如,使用半径分别为 6 和 8 的两次高斯模糊变换得到的效果等同于一次半径为 10 (勾股定理)的高斯模糊效果。根据这个关系,使用多个连续较小的高斯模糊处理不会比单个高斯较大处理时间要少。
其中,性质3有助我们更快速的生成 G S S GSS GSS。虽然两个小半径处理时间并不会比单个高斯较大半径处理的时间少,但相对于原图像,我们已经有一个小半径模糊过的图像,即可用另一小半径在现成的图像上进行模糊得到较大半径的模糊效果。例如尺度为 σ 0 σ_0 σ0的图像已经存在,我们要得到尺度为 k σ 0 kσ_0 kσ0的图像,即可只使用尺度为 [ ( k σ 0 ) 2 − ( σ 0 ) 2 ] 1 / 2 [(kσ_0)^2-(σ_0)^2]^{1/2} [(kσ0)2−(σ0)2]1/2的核在尺度为 σ 0 σ_0 σ0的图像上进行模糊,即可得到尺度为 k σ 0 kσ_0 kσ0模糊的图像,同理可得到高斯尺度空间各层的图像,使得高斯尺度空间生产得更快。
高斯参数   σ   \,\sigma\, σ越来越大。
亚像素(精确度更高的像素位置) :
D O G : DOG: DOG:
D ( x , y , σ ) = ( G ( x , y , k σ ) − G ( x , y , σ ) ) ∗ I ( x , y ) = L ( x , y , k σ ) − L ( x , y , σ ) D(x, y, σ) = (G(x, y, kσ) − G(x, y, σ)) ∗ I(x, y) = L(x, y, kσ) − L(x, y, σ) D(x,y,σ)=(G(x,y,kσ)−G(x,y,σ))∗I(x,y)=L(x,y,kσ)−L(x,y,σ)
每一个点都有自己的梯度方向,然后再做统计。
为了保证特征矢量具有旋转不变性, 需要以特征点为中心, 将特征点附近邻域内的图像旋转一个方向角θ。
最后一个图就是,对16个格子求主方向,组成一个向量,这个向量就是这个点基于主方向的他的临近区域的特征描述。
SITF的缺点是: 计算太复杂, 如果不借助硬件加速或专门的图像处理器很难实现。sift参考
Harris角点
import numpy as np
import cv2 as cv
filename = 'test_1.jpg'
img = cv.imread(filename)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
gray = np.float32(gray)
dst = cv.cornerHarris(gray,2,3,0.04)
#result is dilated for marking the corners, not important
dst = cv.dilate(dst,None)
# Threshold for an optimal value, it may vary depending on the image.
img[dst>0.01*dst.max()]=[0,0,255]
cv.imshow('dst',img)
if cv.waitKey(0) & 0xff == 27:
cv.destroyAllWindows()
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('test_1.jpg',0)
# Initiate FAST object with default values
fast = cv.FastFeatureDetector_create()
# find and draw the keypoints
kp = fast.detect(img,None)
img2 = cv.drawKeypoints(img, kp, None, color=(255,0,0))
# Print all default params
print( "Threshold: {}".format(fast.getThreshold()) )
print( "nonmaxSuppression:{}".format(fast.getNonmaxSuppression()) )
print( "neighborhood: {}".format(fast.getType()) )
print( "Total Keypoints with nonmaxSuppression: {}".format(len(kp)) )
cv.imwrite('fast_true.png',img2)
# Disable nonmaxSuppression
fast.setNonmaxSuppression(0)
kp = fast.detect(img,None)
print( "Total Keypoints without nonmaxSuppression: {}".format(len(kp)) )
img3 = cv.drawKeypoints(img, kp, None, color=(255,0,0))
cv.imwrite('fast_false.png',img3)
cv.imshow('fastResult',img3)
cv.waitKey(0)
cv.destroyAllWindows()
斑点原教程有一句错误:
detector = cv2.SimpleBlobDetector()===>detector = cv2.SimpleBlobDetector_create()
# Standard imports
import cv2
import numpy as np;
# Read image
im = cv2.imread("test_2.jpg", cv2.IMREAD_GRAYSCALE)
cv2.imshow("src",im)
# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector_create()
# Detect blobs.
keypoints = detector.detect(im)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(im, keypoints, np.array([]), (0, 0, 255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show keypoints
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)
参考sift源码,一步一步按照公式写的代码。
python+opencv,编译错误解决方案参考。
sift = cv.xfeatures2d.SIFT_create()
cv2.error: OpenCV(3.4.3) C:\projects\opencv-python\opencv_contrib\modules\xfeatures2d\src\sift.cpp:1207: error: (-213:The function/feature is not implemented) This algorithm is patented and is excluded in this configuration; Set OPENCV_ENABLE_NONFREE CMake option and rebuild the library in function ‘cv::xfeatures2d::SIFT::create’
import numpy as np
import cv2 as cv
img = cv.imread('test_1.jpg')
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
sift = cv.xfeatures2d.SIFT_create()
kp = sift.detect(gray,None)
img=cv.drawKeypoints(gray,kp,img)
cv.imshow("SIFT", img)
cv.imwrite('sift_keypoints.jpg',img)
cv.waitKey(0)
cv.destroyAllWindows()
可以看到C++源码和python+opencv结果没多大区别,几乎一样,但是python+opencv的速度比C++源码快很多。