传统图像处理之几何特征寻找算法

 博主简介

博主是一名大二学生,主攻人工智能研究。感谢让我们在CSDN相遇,博主致力于在这里分享关于人工智能,c++,Python,爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主,博主会继续更新的,如果有错误之处,大家可以指正。

专栏简介:   本专栏主要研究计算机视觉,涉及算法,案例实践,网络模型等知识。包括一些常用的数据处理算法,也会介绍很多的Python第三方库。如果需要,点击这里   订阅专栏

给大家分享一个我很喜欢的一句话:“每天多努力一点,不为别的,只为日后,能够多一些选择,选择舒心的日子,选择自己喜欢的人!”


目录

几何特征

边缘特征

角点

 Harris角点检测

Fast算法

 斑点

局部特征

SIFT算法

 SURF算法

项目实战——图像匹配


几何特征

几何学,相信是每个初高中生的噩梦,做不完的辅助线,各种边边角,成了多少人的恐惧。那么,图像的几何特征除了我们小时候学过的形状、位置、角度、距离等基本特征之外,还有边缘、角点和斑点等。这一节我们就来学习图像几何特征的算法。

边缘特征

这里的边缘不是指图片的四条边,而是指图像中物体的边缘,由于物体的颜色和周围的颜色不同,所以边缘的特性是像素值快速变化,或者说像素值函数一节导数的局部极值点。

如何提取边缘特征?需要先用高斯函数进行滤波(高斯去噪)。高思滤波函数是线性平滑滤波,其对整幅图像像素值进行加权平均,使得新的图像每一个像素的值,都由原图像同一点的像素值和其领域内的其他点的像素值经过加权平均后的到。具体操作:用一个用户指定的卷积核去扫描图像中的每一个像素,然后用卷积核范围内像素的加权平均值去替代卷积核中心像素的值。需要注意的是,高斯滤波只能除去高斯噪声(分布为正态分布的噪声)

 G(x,y)=\frac{1}{2\pi \delta ^{2}}e^{-\frac{x^{2}+y^{2}}{2\delta^{2} }}

由于高斯分布的定义域是无穷大的,理论上需要一个无穷大的高斯核,但是一般我们只需要用到三个标准差以内的函数值即可。当我们决定了标准差之后,就可以计算高斯核了,注意高斯核内所有权重的总和应为1(为了保证总像素不变),所以计算完高斯核的各个权重后还要除以高斯内核内所有权重的总和。

常用的卷积核为3x3的卷积核,然后使用ndimage.convolve()函数进行卷积操作。该函数有两个参数,第一个参数是需要使用卷积核的图片,第二个参数是卷积核矩阵。ndimage函数库功能非常强大,包括但不限于傅里叶变换,图像旋转,图像拉伸,以及图像滤波。

例1:

import numpy as np
import cv2
import scipy.ndimage as ndimage
#生成卷积核
kernel=np.array([[1,1],[1,1]])
#读取图片
img=cv2.imread('F:\Image\\test5.jpeg',0)
#进行卷积操作
img1=ndimage.convolve(img,kernel)
#显示图片
cv2.imshow('1',img)
cv2.imshow('2',img1)
cv2.waitKey(0)

 

高斯核使用相比普通的卷积核更加方便,只需要使用cv2.GaussianBlur()函数即可,此函数有三个参数,分别是输入的图像,高斯核大小和高斯函数标准差。高斯核大小一般为(3,3)或(5,5),高斯函数标准差设置为0,则会根据高斯核大小进行自动计算。

 上面两种卷积操作后,通过对比,第二种的边缘处更加平滑,第一种的卷积核处理后,图像的边缘十分显眼。两种操作,博主更建议使用第二种,简单。而且效果更好。

通过高斯去噪后,再利用梯度获取像素值函数极值,即可得到边缘特征。高斯内核的标准差决定了边缘提取的尺度,对于清晰得边界,标准差可以取得大一些,反之,对于模糊的边界,标准差需要取得小些。这是由于标准差越小,提取到的尺度越小,提取的边缘比较清晰。

角点

与边缘特征 的定义相似,角点不是指图像的四个角,而是指一个局部的小区域与周围的区域不同。常用的角点检测方法有:Harris角点检测和Fast角点检测

Harris角点的定义:向任意方向移动小观察窗,像素值函数都会显著变化。具体的算法是对于图像上的(x,y)坐标点,平移(Δx,Δy)后,计算相似性:

c(x,y)=w(x,y)(I(x,y)-I(x+\Delta x,y+\Delta y))^{2}

其中,w(x,y)为加权函数将I(x+\Delta x,y+\Delta y)在(x,y)处做泰勒展开:

I(x+\Delta x,y+\Delta y)=I(x,y)+\frac{\partial I}{\partial x}\Delta x+\frac{\partial I}{\partial y}\Delta y

代入得到:

c(x,y)=w(x,y)(\frac{\partial I}{\partial x}\Delta x+\frac{\partial I}{\partial y}\Delta y)^{2}

简化后自相关函数变为二元二次函数,或者说是椭圆函数。椭圆形状和M的特征值有关。对于角点,两个特征值都很大,且几乎相等。对于平面,两个特征值都很小,且几乎相等。这是由于在特征值大的方向上,图像的像素值变化也大。实际上,在实际操作中,并不需要将特征值提取出来,只需要对矩阵进行操作即可。计算完自相关函数,得到矩阵后,再计算各像素的Harris响应值R,并进行阈值化处理:

R=det(M)-a*trace(M)^{2}

其中,a的取值范围为0.04~0.06.

最后再进行非极大值抑制,就可以得到图像中的角点。

这里可以用到一个函数cv2.drawKeyPoints()它可以在原途中将检测到的特征部位绘制一个小圆圈。参数为image(原始图像),keypoints(特征点向量),outimage(输出图像),color(颜色),flags(绘制模式)。注意,这个函数,后面的各种特征检测算法都需要用到该函数进行绘制带有特征点的图像。


import cv2
import numpy as np
#读取图片
img=cv2.imread('F:\Image\\test6.jpeg',0)
#关键点存储在 points中
points
#绘制特征图
img=cv2.drawKeypoints(img,points,img,color=(255,0,0))
#显示结果
cv2.imshow('绘制关键点',img)
cv2.waitKey(0)

 Harris角点检测

Harris角点检测使用cv2.cornerHarris()函数,参数为img(需要检测的图像)、blocksize(检测窗口的大小),ksize(求导窗口的大小)、k(角点检测方程中的自由参数a)。

示例代码:


#角点检测
import cv2
import numpy as np
#读取图片
img=cv2.imread('F:\Image\\test7.jpg')
#转化为灰度图像
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#转化为32位浮点
gray=np.float32(gray)
#进行角点检测
R=cv2.cornerHarris(gray,2,3,0.04)
#对于响应值大于一定阈值的点进行标红
img[R>0.05*R.max()]=[0,0,255]
#显示结果
cv2.imshow('Harris角点检测',img)
cv2.waitKey(0)

 这张图片可能不容易分清,我们可以调整参数,比如阈值化处理时对阈值点进行缩小。

 这张图片是不是就很清晰了。这是由于参数a对检测值影响较大,随着a增大,响应值R减小,检测到的角点数量减小,反之,随着a减小,角点检测敏感度增加,检测到的角点数量增加。但是,亮度和对比度对Harris角点检测算法影响较小,这是由于梯度极值点没有发生变化。

Fast算法

Flast角点的定义是如果像素与其领域内足够多的像素不同,那么该像素可能为角点。显然Fast角点要求比Harris角点要低。Fast角点检测的优点就是速度快。其实就是确定一个点,对周围的16个点进行比对,如果其中n(一般为12)个连续的点像素都与该点不同,则为角点。很好理解,直接上代码。

函数是cv2.FastFeatureDetector_create()函数,此函数可选参数为threshold(角点检测阈值)、nonmaxSuppression(是否使用非极大值抑制) ,type(检测类型),有cv2.FAST_FEATURE_DETECTOR_TYPE_5_8、cv2.FAST_FEATURE_DETECTOR_TYPE_7_12,

cv2.FAST_FEATURE_DETECTOR_TYPE_9_16.


import cv2
import numpy as np
#读取图片
img=cv2.imread('F:\Image\\test7.jpg')
#设置窗口
cv2.namedWindow('win',cv2.WINDOW_AUTOSIZE)
#fast角点检测
fast=cv2.FastFeatureDetector_create()
#检测到的角点保存为points
points=fast.detect(img)
#绘制特征图
img=cv2.drawKeypoints(img,points,img,color=(255,0,0))
#显示结果
cv2.imshow('fast角点检测',img)
cv2.waitKey(0)

 结果是夸张了点,不过是可以选择参数来进行控制,下面一幅图就是我选择fast=cv2.FastFeatureDetector_create(type=cv2.FastFeatureDetector_TYPE_5_8)进行识别的,结果就好很多。

 斑点

斑点和我们传统意义上的斑点的理解很相似,即存在边界包围的部分。斑点同样对噪声很敏感,需要先进行高斯滤波。同样需要寻找一阶导数的极值点(或者说二阶导数的零点),这里用到拉普拉斯梯度:

\Delta f=\bigtriangledown ^{2}f

高斯函数的二阶导数:

\Delta G=\frac{-2\delta ^{2}+x^{2}+y^{2}}{2\pi \delta ^{2}}e^{-(x^{2}+y^{2})/2\delta ^{2}}

同样,高斯函数的标准差对斑点检测有影响,较小的标准差可以识别出更加详细的边缘细节。

在OpenCV中,函数cv2.SimpleBlobDetector_create(),此函数的可选参数为minThreshold(检测最小阈值)、maxThreshold(检测最大阈值)、filterByColor(是否通过颜色滤波 )、filterByArea(是否通过面积滤波)、filterByCirculity(是否通过圆度滤波)、filterByConvexity(是否通过凸度滤波)、filterByInertia(是否通过惯性滤波)等。


import cv2
import numpy as np
#读取图片
img=cv2.imread('F:\Image\\test5.jpeg')
#创建检测器
detector=cv2.SimpleBlobDetector_create()
#斑点检测,将检测到的斑点放入bolbs中
blobs=detector.detect(img)
#绘制特征图
img=cv2.drawKeypoints(img,blobs,img,color=(0,0,255),flags=cv2.DrawMatchesFlags_DRAW_RICH_KEYPOINTS)
cv2.imshow('斑点检测',img)
cv2.waitKey(0)

 图片中红色圆圈标注的就是识别到的斑点,检测效果也是比较好的。

局部特征

局部特征也叫特征描述子,其需要满足:对于大小,方向,明暗不同的图像,同一物体的特征描述子应该相似或相同。其特点就是可重复性和显著性。局部特征点常用于图片拼接,目标跟踪和3D重建。所有据局部特征算法都需要注意的问题是旋转不变、仿射不变、光照不变和抵抗噪声。

SIFT算法

尺度不变特征算法(SIFT)由Lowe在2004年提出,同样利用高斯卷积来建立尺度空间,并在高斯差分布空间金字塔上获得拥有尺度不变性的特征描述子。该算法具有仿射不变性、视角不变性、旋转不变性和光照不变性,所以在图像特征提取领域得到了最广泛的应用。优点是信息量丰富和多量性,缺点是速度和还不够快。

①.构建一个线性金字塔结构,让我们可以在连续的高斯核尺度上查找特征点。它比直接用拉普拉斯核的优势在于,他用一阶高斯差分(Dog算子)来近似拉普拉斯算子(Log算子),也就是用差分代替微分,大大减少了运算量。

②.其次,进行极值点的插值。

③.删除边缘效应的点,高斯差分的值会受到边缘的影响,所以边缘上的点,即使不是斑点,tadeDog响应也很强。

④.最后,作出特征描述子的特征描述。

在OpenCV中使用SIFT算法,cv2.xfeatures2d.SIFT_create()即可。


import cv2
import numpy as np
#读取图片
img=cv2.imread('F:\Image\\test5.jpeg')
#创建检测器
sift=cv2.SIFT_create()
points=sift.detect(img,None)
#绘制特征图
img=cv2.drawKeypoints(img,points,img,color=(255,0,255))
cv2.imshow('sift检测',img)
cv2.waitKey(0)

 SURF算法

2006年,Bay和Ess改进了SIFT算法,提出了加速鲁棒特征(SURF),该算法主要优化计算速度,其使用了Hessian行列式的特征检测方法,通过在不同的尺度下的特征图上有效的计算出近似Harrr小波值,简化了二阶微分的计算,从而提高了特征检测算法的效率。

与SIFT算法统计直方图不同,SURF特征点的描述则是利用了积分图,用两个方向上的Harr小波来计算梯度,然后用一个扇形对领域点内的梯度方向进行统计。

函数使用:cv2.SURF_create().

上代码:


import cv2
import numpy as np
#读取图片
img=cv2.imread('F:\Image\\test5.jpeg')
#创建检测器
sift=cv2.SURF_create()
points=sift.detect(img,None)
#绘制特征图
img=cv2.drawKeypoints(img,points,img,color=(255,0,255))
cv2.imshow('sift检测',img)
cv2.waitKey(0)

但是这个代码的环境必须是低版本的opencv,否则没法使用。

项目实战——图像匹配

学习了新的算法,必须去项目上实战一下:

我们需要使用cv2.FlannBaseMatcher()函数,由于博主使用的OpenCV版本比较高,是用不了SURF算法,所以,博主这里就使用SIFT算法,如果你们有低版本的opencv,那就用SURF算法,SURF算法的运算速度是SIFT的3倍,综合性能也比较好。


#基于快速邻近搜索报的SIFT特征描述子匹配算法
import numpy as np
import cv2
#读取图片
img=img=cv2.imread('F:\Image\\test6.jpeg')
#仿射变换矩阵
M=np.array([[0,0.5,-10],[0.5,0,0]])
#旋转缩放图片
img1=cv2.warpAffine(img,M,(750,750))
#改变图片色调,对比度
hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
#改变色调
hsv[:,:,0]=(hsv[:,:,0]+10)%180
#改变饱和度
hsv[:,:,1]=(hsv[:,:,1]+10)%255
#改变明暗度
hsv[:,:,2]=(hsv[:,:,2]+10)%255
#转换为BGR格式
img1=cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR)
#创建检测器
surf=cv2.SURF_create()
#创建搜索器
index_params=dict(algorithm=0,trees=5)
search_params=dict(checks=50)
flann=cv2.FlannBasedMatcher(index_params,search_params)
#特征描述子检测
kp,des=surf.detect(img,None)
kp1,des1=surf.detect(img1,None)
#特征匹配
matches=flann.knnMatch(des1,des,k=2)
#绘制相似特征
good=[]
for m,n in matches:
    if m.distance<0.7*n.distance:
        good.append([m])
img2=cv2.drawMatchesKnn(img1,kp1,img,kp,good,None,flags=2)
#显示图片
cv2.imshow('图片调整',img1)
cv2.waitKey(0)

博主的opencv版本有问题,运行不了该程序,所以就不展示结果图了。理解含义就可以了。好了,本节内容就到此结束了。拜了个拜了!

点赞加关注不迷路

猜你喜欢

转载自blog.csdn.net/qq_59931372/article/details/129461791