图像处理--OpenCV学习笔记

内容简介

OpenCV是应用广泛的开源图像处理库,包括基本的图像处理方法:几何变换,形态学变换,图像平滑,直方图操作,模板匹配,霍夫变换;特征提取和描述方法:理解角点特征,Haeeis和Shi-Tomes算法,SIFT.SURF算法,Fast算法,ORB算法;以及openCV在视频操作中的应用,最后实践案例为人脸检测。

图像起源

图像是人类视觉的基础,是自然景物的客观反映。“图”是物体反射或透射光的分布,“像”是人的视觉系统所接受的图在人脑中所形成的印象或认识。

模拟图像和数字图像

模拟图像由某种物理量(光,电)的强弱变化来记录图像亮度信息,所以是连续变化的,易受干扰,已被数字图像替代。
数字图像:亮度由离散数值表示,是二维图像用有限数字数值像素的表示。分类有:二值图像,灰度图,彩色图。

二值图像

一维二值图像的二维矩阵仅由0,1两个值构成,“0”代表黑色,“1”代表白色。每个像素取值有0,1两种可能,所以计算机中二值图像的数据类型通常为一个二进制位。二值图通常用于文字,线条图和扫描识别(OCR)和掩膜图像的存储。

灰度图

。灰度数字图像是每个像素只有一个采样颜色的图像。. 这类图像通常显示为从最暗黑色到最亮的白色的灰度,尽管理论上这个采样可以任何颜色的不同深浅,甚至可以是不同亮度上的不同颜色。. 灰度图像与黑白图像不同,在计算机图像领域中黑白图像只有黑白两种颜色,灰度图像在黑色与白色之间还有许多级的颜色深度

索引图像

索引图像的文件结构比较复杂,除了存放图像的二维矩阵外,还包括一个称之为颜色索引矩阵MAP的二维数组。MAP的大小由存放图像的矩阵元素值域决定,如矩阵元素值域为[0,255],则MAP矩阵的大小为256Ⅹ3,用MAP=[RGB]表示。MAP中每一行的三个元素分别指定该行对应颜色的红、绿、蓝单色值,MAP中每一行对应图像矩阵像素的一个灰度值,如某一像素的灰度值为64,则该像素就与MAP中的第64行建立了映射关系,该像素在屏幕上的实际颜色由第64行的[RGB]组合决定。也就是说,图像在屏幕上显示时,每一像素的颜色由存放在矩阵中该像素的灰度值作为索引通过检索颜色索引矩阵MAP得到。索引图像的数据类型一般为8位无符号整型(unsigned int8),相应索引矩阵MAP的大小为256Ⅹ3,因此一般索引图像只能同时显示256种颜色,但通过改变索引矩阵,颜色的类型可以调整。索引图像的数据类型也可采用双精度浮点型(double)。索引图像一般用于存放色彩要求比较简单的图像,如Windows中色彩构成比较简单的壁纸多采用索引图像存放,如果图像的色彩比较复杂,就要用到RGB真彩色图像。

彩色图

每个像素通常由红,绿,蓝,三个分量来表示的。分量介于(0,255).RGB图像与索引图像一样都可用来表示彩色图像。与索引图象一样,由R,G,B三种颜色组合来表示每个像素的颜色。但与索引图像不同的是,RGB图像每一个像素的颜色值直接存放在颜色矩阵中,由于每一像素的颜色由RGB三个分量表示,M,N分别表示图像的行列数,三个M*N的二维矩阵分别表示每个像素的R,G,B三个颜色分量。RGB图像的数据类型一般为8位无符号整型,通常用于表示和存放真彩色图像。

OpenCV

计算机视觉开源软件库,支持与计算机视觉和机器学习的众多算法,并日益扩展。

优势

1:基于C++实现,提供python,ruby,matlab等语言接口。OpenCV-python是OpenCV的python API,结合OpenCV C++ API和python语言特性。
2:可以在不同系统平台上进行使用,包括Windows,Linux,OS X,Android和IOS.
3:丰富的API,完善的传统计算机视觉算法,涵盖主流的机器学习算法,同时添加了对深度学习的支持。

OpenCV-python

是一个python绑定库,旨在解决计算机视觉问题。
python语言简单易读,速度较慢。python使用c\c++扩展,在c/c++中编写计算密集型代码,并创建用作python的python包装器。好处是运行速度依旧很快,以及编写代码容易。
OpenCV-Python使用Numpy,这是一个高度优化的数据库操作库,具有MATLAB风格的语法。所有的OPENCV数组结构都转化为Numpy数组,与其他库集成更容易。

OPENCV模块

core :最核心的模块
highgui:视频与图像的读取,显示,存储
imgproc:图像处理的基础方法
features2d:图像特征以及特征匹配

OPENCV基本操作

读取图像:

import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
#读取图片
imge=cv.imread('E:\\code-python\\pythonProject1\\1.jpg',1)

显示图像:

cv.imshow("image",imge)

显示图像之后调用

cv.waitKey(0)

给图像绘制留下时间,否则窗口会出现无响应情况,图像也无法显示出来。
也可使用matplotlib来进行展示

plt.imshow(imge[:,:,::-1])

以灰度图形式进行保存

plt.imshow(imge,cmap=plt.cm.gray)

图像保存:

#图像保存
cv.imwrite("E:\\code-python\\pythonProject1\\1.png",imge)

绘制几何图形
绘制直线:

cv.line(img,start,end,color,thickness)
#       图像,起点,终点,颜色,线条宽度

绘制圆形:

cv.circle(img,centerpoint,r,color,thinkness)
# 图像,圆心,半径,颜色,线条宽度,为-1时生成闭合图案并填充颜色。

绘制矩形:

cv.rectangle(img,leftupper,rightdown,color,thinkness)
#图像,左上角,右下角,颜色,宽度

添加文字:

cv.putText(img,text,station,font,fontsize,color,thinkness,cv.LINE_AA)
#图像,文本数据,文本放置位置,字体,大小

效果显示:

import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
#创建一个空白的图像
img = np.zeros((512,512,3),np.uint8)
#绘制图形
cv.line(img,(0,0),(511,511),(255,0,0),5)
cv.rectangle(img,(384,0),(510,128),(0,255,0),3)
cv.circle(img,(447,63),63,(0,0,255),-1)
font=cv.FONT_HERSHEY_SIMPLEX
cv.putText(img,'OpenCV',(10,500),font,4,(255,255,255),2,cv.LINE_AA)
#图像展示
plt.imshow(img[:,:,::-1])
plt.title('匹配结果'),plt.xticks([]),plt.yticks([])
plt.show()

图像处理--OpenCV学习笔记_第1张图片
获取像素点:
可以通过行和列的坐标获得该像素点的像素值。对于BGR图像,它返回一个蓝,绿,红值的数组。对于灰度图像,仅返回相应的强度值。使用相同的方法对像素进行修改。

import numpy as np
import cv2 as cv
img=cv.imread('E:\\code-python\\pythonProject1\\1.jpg')
#获取像素点的值
px=img[100,100]
#仅获取蓝色通道的强度值
blue=img[100,100,0]
#修改某个位置的像素值
img[100,100]=[255,255,255]

获取图像属性:
形状:img.shape
图像大小:img.size
数据类型:img.dtype

print(img.shape)
print(img.dtype)
print(img.size)

图像通道的拆分与合并:

b,g,r=cv.split(img)
img=cv.merge((b,g,r))

色彩空间的改变:

cv.cvtColor(input_image,flag)
gray=cv.cvtColor(dili,cv.COLOR_BGR2GRAY)
plt.imshow(gray,cmap=plt.cm.gray)

OPENCV算数操作

图像的加法:
可以使用OpenCV的cv.add函数把两个图像相加,或者可以简单的通过numpy操作添加两个图像,如res=img1+img2,两个图象应该具有相同的类型和大小,或者第二个图像为标量值。
注意:OpenCV加法和numpy加法之间存在差异。OpenCV的加法为饱和操作,而Numpy操作为模操作。
具体如下:

x=np.uint8([250])
y=np.uint8([10])
print(cv.add(x,y))#250+10=260=>255
print(x+y)#250+10=260%256=4

图像的混合:
按照不同的权重进行加法,会给人一种混合或者透明的感觉。公式如下:
g(x)=(1-a)f0(x)+af1(x)a取值为(0->1)

img1=cv.imread('E:\\code-python\\pythonProject1\\picture\\1.jpg',1)
img2=cv.imread('E:\\code-python\\pythonProject1\\picture\\2.jpg',1)
img3=cv.addWeighted(img1,0.7,img2,0.3,0)
cv.imshow("img3",img3)
cv.waitKey(0)

OPENCV图像处理

几何变换

图像缩放,平移
#获取行数列数
rows,cols=img1.shape[:2]
#绝对尺寸
res=cv.resize(img1,(2*cols,2*rows),interpolation=cv.INTER_CUBIC)
#相对尺寸
res1=cv.resize(img1,None,fx=0.5,fy=0.5)
plt.imshow(img1[:,:,::-1])
plt.show()
rows,cols=img1.shape[:2]
res=cv.resize(img1,(2*cols,2*rows))
plt.imshow(res[:,:,::-1])
plt.show()

图像平移;

cv.warpAffine(img,M,dsize)
            图像,矩阵,大小

图像显示:
M为一个2*3的变换矩阵
M = [[x,y,z],[x1,y1,z1]]
M = np.float32([[1,0,100], [0,1,100]])
z为以左上角为坐标原点的x,z1为左上角为坐标原点的y,将图片进行平移
图片向x,y轴方向平移100距离

M=np.float32([[1,0,100],[0,1,100]])
res2=cv.warpAffine(img1,M,(cols,rows))

M为一个2*3的变换矩阵
M = [[x,y,z],[x1,y1,z1]]
M = np.float32([[1,0,0], [0,1,0]])
最初效果:
图像处理--OpenCV学习笔记_第2张图片

z为以左上角为坐标原点的x,z1为左上角为坐标原点的y,将图片进行平移
M = np.float32([[1,0,100], [0,1,100]])
图片效果:
图像处理--OpenCV学习笔记_第3张图片

图片向x,y轴方向平移100距离
当y为0,y1为1.5即图片以y轴方向拉长1.5倍,若y1为0.5即y轴方向图片压缩0.5
M = np.float32([[1,0,0], [0,1.5,0]]), y方向拉长1.5倍
图像处理--OpenCV学习笔记_第4张图片

,y方向缩放0.5倍

图像处理--OpenCV学习笔记_第5张图片

当x1为0,x为1.5即图片以x轴方向拉长1.5倍,若x为0.5即以x轴方向图片压缩0.5
M = np.float32([[1.5,0,0], [0,1,0]])
x轴方向拉长1.5倍

图像处理--OpenCV学习笔记_第6张图片

M = np.float32([[0.5,0,0], [0,1,0]]),x轴缩放0.5倍

图像处理--OpenCV学习笔记_第7张图片

当x,x1均不为0时,当x为1.2,x1为0.5时,即推向发生顺时针偏转,偏转角度tan& = x1/x 并且图像在x轴方向拉长1.5倍.M = np.float32([[1.2,0,0], [0.5,1,0]])

图像处理--OpenCV学习笔记_第8张图片

当y,y1均不为0时,当y为0.5,y1为1.2时,即图像发生逆时针偏转,偏转角为tan& = y/y1,并且图像在y轴方向拉长1.2倍

图像处理--OpenCV学习笔记_第9张图片

当x,x1,y,y1均不为0时,图片发生偏转。x,y均拉长1.2倍效果如下:
M = np.float32([[1.2,0.5,0], [0.5,1.2,0]])

图像处理--OpenCV学习笔记_第10张图片

图像旋转

图像旋转是指图像按照某个位置转动一定角度的过程,旋转中图像仍然保持着原始尺寸。图像旋转后图像的水平对称轴,垂直对称轴,及中心坐标原点都有可能发生变化,因此需要对图像旋转中的坐标进行相应转换。

cv.getRotationMatrix2D((cols/2,rows/2),90,1)
#进行旋转变换
dst=cv.warpAffine(img,M,(cols,rows))

#图像展示
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img1[:,:,::-1])
axes[0].set_title("原图")
axes[1].imshow(dst[:,:,::-1])
axes[1].set_title("旋转后结果")

图像处理--OpenCV学习笔记_第11张图片

M=cv.getRotationMatrix2D((cols/2,rows/2),45,0.5)
res3=cv.warpAffine(img1,M,(cols,rows))

图像处理--OpenCV学习笔记_第12张图片

图像仿射变换

图像的仿射变换涉及到图像的形状位置角度的变化,是深度学习的常用功能,仿射变换主要是对图像的缩放。旋转,翻转和平移等操作的结合。
图像上的仿射变换, 其实就是图片中的一个像素点,通过某种变换,移动到另外一个地方。
从数学上来讲, 就是一个向量空间进行一次线形变换并加上平移向量, 从而变换到另外一个向量空间的过程。

对于二维坐标系的一个坐标点(x,y),可以使用一个2x2矩阵来调整x,y的值,而通过调整x,y可以实现二维形状的线性变换(旋转,缩放),所以整个转换过程就是对(x,y)调整的过程。
仿射变换(Affine Transformation)是指在向量空间中进行一次线性变换(乘以一个矩阵)和一次平移(加上一个向量),变换到另一个向量空间的过程。

仿射变换代表的是两幅图之间的映射关系,仿射变换矩阵为2x3的矩阵,如下图中的矩阵M,其中的B起着 平移 的作用,而A中的对角线决定 缩放,反对角线决定 旋转 或 错切。
所以仿射变换可以由一个矩阵A和一个向量B给出:
图像处理--OpenCV学习笔记_第13张图片
原像素点坐标(x,y),经过仿射变换后的点的坐标是T,则矩阵仿射变换基本算法原理:
图像处理--OpenCV学习笔记_第14张图片
所以仿射变换是一种二维坐标(x, y)到二维坐标(u, v)的线性变换,其数学表达式如下:
图像处理--OpenCV学习笔记_第15张图片

缩放和旋转通过矩阵乘法来实现,而平移是通过矩阵加法来实现的,为了将这几个操作都通过一个矩阵来实现,所以构造出了上面那个 2x3 的矩阵。但是这个会改变图像的尺寸,比如一个 2x2 的图像,乘以 2x3 的矩阵,会得到 2x3 的图像,所以为了解决这个问题,我们就增加一个维度,也就是构造齐次坐标矩阵。
最终得到的齐次坐标矩阵表示形式为:

图像处理--OpenCV学习笔记_第16张图片

仿射变换保持了二维图像的“平直性”和“平行性”:

平直性:
直线经仿射变换后还是直线
圆弧经仿射变换后还是圆弧

平行性:
直线之间的相对位置关系保持不变
平行线经仿射变换后依然为平行线
直线上点的位置顺序不会发生变化
向量间夹角可能会发生变化

#解决中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False

#创建变换矩阵
pst1=np.float32([[50,50],[200,50],[50,200]])
pst2=np.float32([[100,100],[200,50],[100,250]])
M=cv.getAffineTransform(pst1,pst2)
#完成仿射变换
dst=cv.warpAffine(img1,M,(cols,rows))

图像处理--OpenCV学习笔记_第17张图片

图像透射变换

透射变换是视角变化的结果,是指利用投射中点,像点,目标点三点共线的条件,按透视旋转定律使透视面(承影面)绕透视轴(迹线)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。
图像处理--OpenCV学习笔记_第18张图片

#透射变换#

pst1=np.float32([[56,65],[368,52],[28,387],[389,390]])
pst2=np.float32([[100,145],[300,100],[80,290],[310,300]])
T=cv.getPerspectiveTransform(pst1,pst2)
#完成透射变换
dst=cv.warpPerspective(img1,T,(cols,rows))

图像处理--OpenCV学习笔记_第19张图片

图像金字塔–多尺度表达

尺度,顾名思义就是说图像的尺寸和分辨率。在我们进行图像处理的时候,会经常对源图像的尺寸进行放大或者缩小的变换,进而转换为我们指定尺寸的目标图像。在对图像进行放大和缩小的变换的这个过程,我们称为尺度调整。

而图像金字塔则是图像多尺度调整表达的一种重要的方式,图像金字塔方法的原理是:将参加融合的的每幅图像分解为多尺度的金字塔图像序列,将低分辨率的图像在上层,高分辨率的图像在下层,上层图像的大小为前一层图像大小的1/4。图像金字塔用于机器视觉和图像压缩,一幅图像的金字塔型状排列分辨率依次降低,且来源于同一张原始图的图像集合。通过梯次向下采样获得,直到达到某个终止条件才停止采样。
图像的底部是待处理图像的高分辨率显示,而顶部是低分辨率近似,层级越高,图像越小,分辨率越低。

cv.pyrUp(img)上采样
cv.pyrDown(img)下采样
up_img=cv.pyrUp(img1)
img_1=cv.pyrDown(img1)

cv.imshow('enlarge',up_img)
cv.imshow('original',img1)
cv.imshow('shrink',img_1)
cv.waitKey(0)
cv.destroyAllWindows()

形态学变换

形态学转换是基于图像形状的一些简单操作。通常在二进制图像上执行。腐蚀和膨胀是两个最基本的形态学运算符。变体形式如开运算,闭运算,礼帽黑帽等。

连通性

在图像中,最小的单位是像素,每个像素周围有8个邻接像素,常见的邻接关系有三种:4邻接,8邻接,和D邻接。
连通性:4连通,8连通,m连通
4邻接:p在q的上下左右4个像素点内。

图像处理--OpenCV学习笔记_第20张图片

如图,灰色部分就是p的4邻域,那么灰色部分和p就是4邻接关系。

8邻接:p在q的周围的8个像素点内。

图像处理--OpenCV学习笔记_第21张图片
如图,灰色部分就是p的8邻域,灰色部分和p就是8邻接关系。

按照以上的定义,其实4邻接和8邻接是很好区分的,但是在实际问题处理上,这样的定义不够用了(二义性),大牛们就想办法解决问题。

图像处理--OpenCV学习笔记_第22张图片

如图,从 右上1 到 中间1 有2条通路,这种情况人们非常不愿意面对,就提出了m邻接来解决问题。

m邻接(混合邻接):(只要满足1个就是m邻接)

1. p和q是4邻接

2. q在p的ND中且p的N4相交q的N4为空,则q和p是m连接的

当像素间同时存在4邻接和8邻接时,优先采用4邻接。

腐蚀和膨胀

腐蚀和膨胀是最基本的形态学操作,腐蚀和膨胀都是针对白色部分(高亮部分)而言的。
膨胀就是使图像中高亮部分扩张,效果图拥有比原图更大的高亮区域,效果图拥有比原图更小的高亮区域。膨胀是求局部最大值的操作,腐蚀是求局部最小值的操作。

腐蚀

具体操作是用一个结构元素扫描图像中的每一个像素,用结构元素中的每一个像素做’与‘操作,若都为1,则该像素为1.否则为零。如图所示,A被B腐蚀后:
图像处理--OpenCV学习笔记_第23张图片

腐蚀的作用是消除物体边界点,使目标缩小,可以消除小于噪声元素的噪声点。
API:

cv.erode(img,kernel,iterations)

img:要处理的图像
kernel:核结构
iteration:处理次数。默认为1.

膨胀

具体操作是用一个结构元素扫描图像中的每一个像素,用结构元素中的每一个像素做’与‘操作,若都为0,则该像素为0.否则为1。如图所示,A被B膨胀后:
图像处理--OpenCV学习笔记_第24张图片
作用是将与物体接触的所有背景点合并到物体中,使目标增大,可填补目标中的孔洞。
API:

cv.dilate(img,kernel,iterations)

img:要处理的图像
kernel:核结构
iteration:处理次数。默认为1.

kernel=np.ones((5,5),np.uint8)
erosion=cv.erode(img1,kernel)
dilate=cv.dilate(img1,kernel)

fig,axes=plt.subplots(nrows=1,ncols=3,figsize=(10,8),dpi=100)
axes[0].imshow(img1)
axes[0].set_title('原图')
axes[1].imshow(erosion)
axes[1].set_title("腐蚀后结果")
axes[2].imshow(dilate)
axes[2].set_title("膨胀后结果")

图像处理--OpenCV学习笔记_第25张图片

开闭运算

开运算和闭运算是将腐蚀和膨胀按照一定的次序进行处理。但这两者并不是可逆的,即先开后闭并不能得到原来的图像。

开运算

开运算是先腐蚀后膨胀,其作用是:分离物体,消除小区域。特点:消除噪点,去除小的干扰块,而不影响原来的图像。
图像处理--OpenCV学习笔记_第26张图片

闭运算

闭运算和开运算相反,先膨胀后腐蚀,其作用是消除’闭合‘物体里面的孔洞,特点:可以填充闭合区域。
图像处理--OpenCV学习笔记_第27张图片
API:
cv.morphologyEx(img,op,kernel)
img:要处理的图像
op:开运算为cv.MORPH_OPEN,闭运算为cv.MORPH_CLOSE
kernel:核结构

kernel=np.ones((10,10),np.uint8)
cvOpen=cv.morphologyEx(img1,cv.MORPH_OPEN,kernel)#开运算
cvClose=cv.morphologyEx(img2,cv.MORPH_CLOSE,kernel)#闭运算
fig,axes=plt.subplots(nrows=2,ncols=2,figsize=(10,8))
axes[0,0].imshow(img1)
axes[0,0].set_title('原图')
axes[0,1].imshow(cvOpen)
axes[0,1].set_title("开运算结果")
axes[1,0].imshow(img2)
axes[1,0].set_title("原图")
axes[1,1].imshow(cvClose)
axes[1,1].set_title("闭运算结果")

图像处理--OpenCV学习笔记_第28张图片

礼帽和黑帽
礼帽运算

原图像与开运算的结果之差,如下所示:
dst=tophat(src,element)=src-open(src,element)
因为开运算的结果是放大了裂缝或者局部低亮度区域,因此,从原图中减去开运算后的图,得到的效果突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。礼帽操作用来分离比临近点亮一些的斑块。当一幅图像具有大幅背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

黑帽运算

为闭运算的结果图与原图像之差。数学表达为:

dst=blackhat(src,element)=close(src,element)-src
黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且之一操作和选择的核的大小相关。黑帽操作用来分离比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。黑帽操作用来分离比临近点暗一些的斑块。

cv.morphologyEx(img,op,kernel)

img:要处理的图像
op:处理方式
礼帽运算----cv.MORPH_TOPHAT
黑帽操作---- cv.MORPH_BLACKHAT

图像处理--OpenCV学习笔记_第29张图片

图像平滑

图像噪声

由于图像采集,处理,传输的过程中不可避免的受到噪声的污染,妨碍人们的图像理解及分析处理。常见的图像噪声有高斯噪声,椒盐噪声等。

椒盐噪声

椒盐噪声也成为脉冲噪声,是图像中经常见到的一种噪声,它是一种随机出现的白点或黑点,可能是亮的区域有黑色像素或是在暗的地方有白色色素(或者两者皆有)。椒盐噪声的成因可能是影像讯号收到突如其来的强烈干扰而产生,类比数位转换器或位元传输错误等。例如失效的感应器导致像素值为最小值,饱和的感应器导致像素值为最大值。

高斯噪声

噪声的概率密度分布是正态分布。
高斯噪声是指噪声密度函数服从高斯分布的一类噪声。由于高斯噪声在空间和频域中数学上的易处理性,这种噪声模型经常用于实践中。
在这里插入图片描述
高斯函数曲线为
图像处理--OpenCV学习笔记_第30张图片

图像平滑

图像平滑从信号处理的角度看就是去除其中的高频信息,保留低频信息。因此我们可以对图像实施低通滤波。低通滤波可以去除图像中的噪声,对图像进行平滑。
根据滤波器的不同可以分为均值滤波,高斯滤波,中值滤波,双边滤波。

均值滤波

采用均值滤波模板对图像噪声进行滤除,令中心在(x,y)点,尺寸为m×n的矩形子图像窗口的坐标组。均值滤波器可表示为:
在这里插入图片描述
由一个归一化卷积框完成的。它只是用卷积框覆盖区域所有像素的平均值来代替中心元素。例如:3×3标准化的平均滤波器如下所示:
K=
均值滤波的优点是算法简单,计算速度快,缺点是在去噪的同时去除了很多细节部分,将图像变得模糊。
API:

cv.blur(src,ksize,anchor,borderType)

高斯滤波

二维高斯是构建高斯滤波器的基础,其概率分布函数如下所示:
图像处理--OpenCV学习笔记_第31张图片
图像处理--OpenCV学习笔记_第32张图片
当
高斯平滑的过程:
首先确定权重矩阵
假定中心点的坐标为(0,0),那么距离他最近的八个点的坐标如下:
图像处理--OpenCV学习笔记_第33张图片
为了计算权重矩阵,需要设定值为1.5,则模糊半径为1的权重矩阵如下:
图像处理--OpenCV学习笔记_第34张图片
这九个点的权重总和等于0.4787147,如果只计算这九个点的加权平均,还必须让他们权重之和等于1,因此上面9个值还要分别除以0.4787147,得到最终的权重矩阵。
图像处理--OpenCV学习笔记_第35张图片
计算高斯模糊:
有了权重矩阵,就可以计算高斯模糊的值了。
假设现有9个像素点,灰度图(0-255)如下:
图像处理--OpenCV学习笔记_第36张图片
每个点乘以权重值
图像处理--OpenCV学习笔记_第37张图片
图像处理--OpenCV学习笔记_第38张图片
将这9个值加起来,就是中心点的高斯模糊值。
对所有点重复这个过程,就得到了高斯模糊后的图像。如果原图是彩色图片,可以对RGB三个通道分别做高斯平滑。
API:

cv.GaussianBlur(src,ksize,sigmaX,sigmay,borderType)
blur=cv.GaussianBlur(imgx,(3,3),1)
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(imgx),plt.title('原图')
plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title('高斯滤波后结果')
plt.xticks([]),plt.yticks([])

中值滤波

中值滤波是一种典型的非线性滤波技术,基本思想是用像素点邻域灰度值的中值来代替该点像素点的灰度值。中值滤波对椒盐噪声来说尤其有用,因为他不依赖邻域内那些与典型值差别很大的值。
API:

cv.medianBlur(src,ksize)
#            图像,卷积核大小
blur=cv.medianBlur(imgx,5)

直方图

直方图是对数据进行统计的一种方法,并且将统计值组织到一系列事先定义好的bin中,其中,bin为直方图中经常用到的一个概念,可以译为“直条”或“组距”,其数值是从数据中计算出的特征统计量,这些数据可以是诸如梯度,方向,色彩,或者其他特征。
图像直方图是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素个数。这种直方图中,横坐标的左侧为较暗区域,而右侧为较亮的区域。因此1张较暗的图片直方图数据多集中在左侧和中间部分,而整体明亮,只有少量阴影的图像则相反。
图像处理--OpenCV学习笔记_第39张图片
直方图是根据灰度图进行绘制的,而不是彩色图。一张图像的信息是(灰度值0-255,已知数子的范围包含256个值,于是可以按照一定规律将这个范围分割为子区域(也就是bins))
[0,255]=[0,15]∪[16,30]…[240,255]
然后再统计每一个bins的像素数目。可以得到下图(x轴表示bin,y轴表示每个bin中的像素个数)
图像处理--OpenCV学习笔记_第40张图片
dims:需要统计的特征数目。上例中,dims=1,因为仅仅统计了灰度值。
bins:每个特征空间子区段的数目,可译为“直条”或组距,bins=16.
range要统计特征的取值范围,上例中range=[0,255]
直方图的意义:图像中像素强度分布的图形表达方式。
统计了每一个像素值所具有的像素个数。

直方图的绘制

API:

cv.calcHist(img,channel,mask,hisssize,range[,hist[,accumulate]])

img:原图像
channel:输入的图像为灰度图,它的值为[0];彩色图像的话,传入的参数是[0],[1],[2]。它们分别是B,G,R。
mask:淹模图像
hisSize:BIN数目。
ranges:像素值范围[0,256].
图像处理--OpenCV学习笔记_第41张图片

imgx=cv.imread('C:\\Users\\DELL\\Desktop\\111\\1.png',0)
histr=cv.calcHist([imgx],[0],None,[256],[0,256])
plt.figure(figsize=(10,6),dpi=100)
plt.plot(histr)
plt.grid()
plt.show()

图像处理--OpenCV学习笔记_第42张图片

掩膜的应用

掩膜是指用选定的图像,图形,或物体,对要处理的图像进行遮挡,来控制图像处理的区域。
在数字图像中我们通常使用二维矩阵数组进行掩膜。掩膜是由0和1组合的一个二进制图像,利用该掩膜图像要处理的图像进行掩膜,1值被处理,0值屏蔽。
主要用途:提取感兴趣区域,用预先制作的感兴趣掩膜与待处理图像进行与操作,得到感兴趣图像,感兴趣区内图像值保持不变,而区外图像值都为0.
屏蔽作用:用掩膜对图像上的某些区域做屏蔽,使其不参与或不参加处理参数的计算,或仅对屏蔽区做处理或统计。
结构特征提取:用相似性变量或图像匹配方法检测和提取图像中与掩膜相似的结构特征。
特殊形状图像制作。
掩膜在遥感影像处理中使用较多,当提取道路或者河流,房屋时,通过一个掩膜矩阵对图像进行像素过滤。然后将我们需要的地物或者标志突出显示。我们使用cv.calcHist()来查找完整图像的直方图。要查找图像某些区域的直方图,在查找直方图的区域上创建一个白色的掩膜,否则黑色,将其作为掩码mask传递。

#直接以灰度图的形式读入
imgx=cv.imread('C:\\Users\\DELL\\Desktop\\111\\1.png',0)
#创建模板
mask=np.zeros(imgx.shape[:2],np.uint8)
mask[400:650,200:500]=255

mask_img=cv.bitwise_and(imgx,imgx,mask=mask)
#掩膜
mask_histr=cv.calcHist([imgx],[0],mask,[256],[1,256])
fig,axes=plt.subplots(nrows=2,ncols=2,figsize=(10,8))
axes[0,0].imshow(imgx,cmap=plt.cm.gray)
axes[0,0].set_title("原图")

axes[0,1].imshow(mask,cmap=plt.cm.gray)
axes[0,1].set_title("蒙版数据")

axes[1,0].imshow(mask_img,cmap=plt.cm.gray)
axes[1,0].set_title("掩膜后数据")

axes[1,1].plot(mask_histr)
axes[1,1].grid()
axes[1,1].imshow(imgx,cmap=plt.cm.gray)
axes[1,1].set_title("灰度直方图")
plt.show()

图像处理--OpenCV学习笔记_第43张图片

直方图均衡化

一幅图像大多数像素点都集中在某一个小的灰度值取值范围内,当图像整体明亮,那所有的像素值的取值都会很高,如图左。将该直方图做横向拉伸,就可以扩大图像像素值的分布范围,提高图像对比度。
图像处理--OpenCV学习笔记_第44张图片
“直方图均衡化”是把原始图像的灰度直方图从比较集中的某个灰度区间变成更广泛灰度范围内的分布。直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定灰度范围内的像素数量大致一致。
这种方法提高图像整体的对比度,特别是有用数据的像素值分布比较接近时,在X光图像中使用广泛,可以提高骨架结构的显示,另外在曝光过度或不足的图像中可以更好地突出细节。
API:

dst=cv.equalizeHist(img)
#均衡化处理
dst=cv.equalizeHist(img1)
#结果展示
fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img1)
axes[0].set_title('原图')
axes[1].imshow(dst)
axes[1].set_title("均衡化之后")
plt.show()

图像处理--OpenCV学习笔记_第45张图片

自适应的直方图均衡化

在直方图中,考虑的是全局对比度。在进行直方图均衡化之后,图片背景的对比度被改变了,丢失了很多信息,在许多情况下,这种效果不好。为减少信息损失,需要使用自适应直方图均衡化。整幅图被分解为很多小块,这些小块成为tiles(默认8×8),在对每一个小块进行直方图均衡化。在每一个区域中,直方图会集中在某一个小的区域中。如果有噪声的话,噪声会被放大,为避免这种情况出现,要采用对比度限制。对每个小块来说,如果直方图bin超过上限的话,就把其中的像素点均匀分布到其他bins,然后再进行直方图均衡化。
图像处理--OpenCV学习笔记_第46张图片

API:
cv.createCLAHE(clipLimit,tileGridSize)

clipLimit:对比度默认40
tileGridSize:分块大小,默认8×8

#自适应直方图均衡化
clahe=cv.createCLAHE(clipLimit=2.0,tileGridSize=(8,8))
cli=clahe.apply(img1)

fig,axes=plt.subplots(nrows=1,ncols=2,figsize=(10,8),dpi=100)
axes[0].imshow(img1,cmap=plt.cm.gray)
axes[0].set_title('原图')
axes[1].imshow(cli,cmap=plt.cm.gray)
axes[1].set_title("均衡化之后")
plt.show()

图像处理--OpenCV学习笔记_第47张图片

边缘检测

边缘检测是图像处理和计算机视觉中基本问题,边缘检测是表示数字图像中亮度变化明显的点。图像属性中的显著变化是反映了属性的重要事件和变化明显的点。图像属性的显著变化通常反映了属性的重要事件和变化。形式如下:
图像处理--OpenCV学习笔记_第48张图片
图像边缘检测大幅度减少了数据量,并且删除了可以认为不相关的信息,保留了图像重要的结构属性。边缘检测分为两类:基于搜索和基于零穿越。
基于搜索:寻找一阶导数中的最大值来检测边界,然后利用计算结果估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值,代表算法有:sobel算子和Scharr算子。
图像处理--OpenCV学习笔记_第49张图片

基于零穿越:通过寻找图像的二阶导数零穿越来寻找边界,代表算法为Laplacian算子。
图像处理--OpenCV学习笔记_第50张图片

Sobel检测算子

边缘检测算法比较简单,实际应用比canny边缘检测效率高,但是边缘不如Canny检测的准确,在很多实际应用场合,sobel算子是高斯平滑与微分操作的结合体,对抗噪声能力很强,用途较多,尤其是效率较高,对纹理不太关心时。
方法:对于不连续的函数,一阶导数可以写作
图像处理--OpenCV学习笔记_第51张图片
假设要处理的图像为I,在两个方向求导
水平方向:将图像I与奇数大小的模版进行卷积,结果为Gx.比如当模板大小为3时,Gx为
在这里插入图片描述
垂直方向:将图像I与奇数大小的模版进行卷积,结果为Gy.比如当模板大小为3时,Gy为
在这里插入图片描述
在图像每一点,结合以上两个结果求出:
在这里插入图片描述
统计极大值所在的位置,就是图像的边缘。
当内核为3时,内核会产生明显的误差,为解决这一问题,我们使用Scharr函数,但该函数仅作用于大小为三的内核。该函数的运算与Sobel函数一样快,但结果更加准确,计算方法为:
图像处理--OpenCV学习笔记_第52张图片
API:Sobel_x_or_y=cv.Sobel(src,ddepth,dx,dy,dst,ksize,scale,delta,bor))
src:传入的图像
ddepth:图像的深度
dx,dy:求导的阶数,0表示没有求导,取值为0,1
ksize:算子大小即卷积核大小,须为奇数,1,3,5,7,9
scale缩放倒数的比例常数,默认没有伸缩系数
borderType:图像边界的模式,默认cv.BORDER_DEFAULT.
Sobel函数求导后会有负值,还有大于255的值。原图像是uint8,即8位无符号数,Sobel建立的图像位数不够,会有截断。要使用16位有符号数据类型,从v。CV_16S。处理完后,再使用cv.addweighted(src1,alpha,src2,beta)

#边缘检测
#计算卷积结果
x=cv.Sobel(img1,cv.CV_16S,1,0)
y=cv.Sobel(img1,cv.CV_16S,0,1)
#将数据进行转换
Scale_absX=cv.convertScaleAbs(x)#convert转换scale缩放
Scale_absY=cv.convertScaleAbs(y)
#计算结果
result=cv.addWeighted(Scale_absX,0.5,Scale_absY,0.5,0)
#图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img1,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap=plt.cm.gray),plt.title('滤波后结果')
plt.xticks([]),plt.yticks([])
plt.show()

图像处理--OpenCV学习笔记_第53张图片

将Sobel算子ksize设为-1,就是利用Scharr进行边缘检测

x=cv.Sobel(img1,cv.CV_16S,1,0,ksize=-1)
y=cv.Sobel(img1,cv.CV_16S,0,1,ksize=-1)

图像处理--OpenCV学习笔记_第54张图片

Laplacian检测算子

Laplacian是利用二阶导数来检测边缘。因为图像是2维,我们需要在两个方向求导,如下图所示
图像处理--OpenCV学习笔记_第55张图片
API:

laplacian=cv.laplacian(src,ddepth[,dst[,ksize[,scale[,deltal[,borderType]]]]])

src:需要处理的图像
Depth:图像的深度,-1表示采用的是原图像相同的深度,目标图像的深度大于等于原图像的深度
ksize:算子大小

result=cv.Laplacian(img1,cv.CV_16S)
Scale_abs=cv.convertScaleAbs(result)
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img1,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(result,cmap=plt.cm.gray),plt.title('滤波后结果')
plt.xticks([]),plt.yticks([])
plt.show()

图像处理--OpenCV学习笔记_第56张图片

Canny检测算子

原理:
第一步:噪声去除
由于边缘检测很容易受到噪声的影响,所以首先使用5×5高斯滤波去除噪声。
第二步:计算图像梯度
对平滑后的图像使用Sobel算子计算水平方向和竖直方向的一阶导数(Gx和Gy)。根据得到的这两幅梯度图(Gx和Gy),找到边界的梯度和方向,公式如下:
在这里插入图片描述

第三步:非极大值抑制
在获得梯度的方向和大小后,对整幅图进行扫描,去除那些非边界线上的点。对每一个像素进行检查,看这个点的梯度是不是周围具有相同梯度方向的点中最大的。如下图:
图像处理--OpenCV学习笔记_第57张图片
A点位于图像的边缘,在梯度变化方向,选择像素点B和C,用来检测A点的梯度是否为极大值,若为极大值进行保留,否则A点被抑制,最后结果为具有细边的二进制图像。
第四步:滞后阈值
现在确定真正的边界,设置两个阈值:minval和maxval,当图像的灰度梯度高于maxval被认为是真正的边界点相连,如果是就认为它也是边界点,如果不是抛弃。如下图:
图像处理--OpenCV学习笔记_第58张图片
如上图所示,A高于maxval所以是真正的边界点,C虽然低于maxval但高于minval并且与A相连,所以也被认为是真正的边界点。B会被抛弃,因为低于maxval而且不与真正的边界点相连。所以选择合适的maxval,minval决定结果好坏。
API:

canny=cv.Canny(img,threshold1,threshold2)

img:灰度图
threshold1:minval,以较小的阈值将间断的边缘连接起来
threshold2:minval,以较小的阈值将间断的边缘连接起来

#边缘检测
lowThreshold=0
max_lowThreshold=100
canny=cv.Canny(img1,lowThreshold,max_lowThreshold)
#图像显示
plt.figure(figsize=(10,8),dpi=100)
plt.subplot(121),plt.imshow(img1,cmap=plt.cm.gray),plt.title('原图')
plt.xticks([]),plt.yticks([])
plt.subplot(122),plt.imshow(canny,cmap=plt.cm.gray),plt.title('边缘检测结果')
plt.xticks([]),plt.yticks([])
plt.show()

图像处理--OpenCV学习笔记_第59张图片

模板匹配

所谓的模板匹配,就是在给定的图片中查找和模板最相似的区域,该算法的输入包括模板和图片,整个任务的思路就是按照滑窗的思路不断地移动模板图片,计算其与图像中对应区域的匹配度,最终将匹配度最高的区域选择为最终的结果。
实现流程:
1,原图像:在这幅图中,找到与模板匹配的区域。
2,模版:与原图像进行比对的图像块
3,滑动模板图像与原图像进行对比,将模板每次移动一个元素(从左到右,从上到下),在每一个位置,都计算与模板图像的相似程度。对于每个位置将计算的相似结果保存在结果矩阵中,如果输入图像的大小(WxH)且模板图像的大小(wxh),则输出的矩阵R的大小(W-w+1,H-h+1)

图像处理--OpenCV学习笔记_第60张图片

图像处理--OpenCV学习笔记_第61张图片

res=cv.matchTemplate(img,template,method)

img:要进行模板匹配的图像
Template:模板
method:
平方差匹配:(CV_TM_SQDIFF):利用模板与图像之间的平方差进行匹配,最好的匹配为0,匹配越差,匹配的值越大。
相关匹配:(CV_TM_CCORR):利用模板与图像间的乘法进行匹配,数值越大表示匹配程度越高,越小表示匹配效果差。
相关系数匹配:(CV_TM_CCOEFF):利用模板与图像间的相关系数匹配,1表示完美的匹配,-1表示最差的匹配。
完成匹配后,使用cv.minMaxLoc()方法查找最大值的位置即可。如果使用平方差作为比较方法,则最小值位置是最佳匹配位置。

示例如下:

图像处理--OpenCV学习笔记_第62张图片

在这里插入图片描述

图像处理--OpenCV学习笔记_第63张图片

###模板匹配
h,w,l=img2.shape
res=cv.matchTemplate(img1,img2,cv.TM_CCOEFF_NORMED)
plt.imshow(res,cmap=plt.cm.gray)
min_val,max_val,min_loc,max_loc=cv.minMaxLoc(res)
top_left=max_loc
bottom_right=(top_left[0]+w,top_left[1]+h)
cv.rectangle(img1,top_left,bottom_right,(0,255,0),2)
plt.imshow(img1[:,:,::-1])
plt.title('匹配结果'),plt.xticks([]),plt.yticks([])
plt.show()

模板匹配不适用尺度变换,视角变换后的图像,这是我们就要使用关键点匹配算法,比较经典的关键点检测算法包括SIFT和SURF等,主要的思路是通过关键点检测算法获取模板和测试图片的关键;然后使用关键点匹配算法处理即可,这些关键点可以很好的处理尺度变换,视角变换,旋转变换,光照变换等,具有很好的不变性。

霍夫变换

常用来提取图像中的直线和园等几何形状。
在笛卡尔坐标系中,一条直线有两个点A(x1,y1)和B(x2,y2)确定,如下图所示:
图像处理--OpenCV学习笔记_第64张图片
图像处理--OpenCV学习笔记_第65张图片
图像处理--OpenCV学习笔记_第66张图片

对应的变换可以由图形直观表现
图像处理--OpenCV学习笔记_第67张图片

变换后的空间我们叫做霍夫空间,即:笛卡尔坐标系的一条直线,对应霍夫空间的一个点。反过来同样成立,霍夫空间中的一条线,对应的坐标系的一个点,如下图所示:
图像处理--OpenCV学习笔记_第68张图片

即在笛卡尔坐标中的点是共线的,则在霍夫空间中交于一点。
直角坐标与极坐标的表示形式是一样的
在这里插入图片描述

在极坐标下,极坐标中的点对应霍夫空间的线,这时的霍夫空间是不在参数(k,q)的空间,而是(p,θ)的空间,p是原点到直线的垂直距离,θ表示直线的垂线与横轴顺时针方向的夹角,垂直线的角度为0度,水平线角度为180度。
图像处理--OpenCV学习笔记_第69张图片

只要求得霍夫空间的交点的位置,即可得到原坐标系下的直线。
实现流程:
假设有一个大小为100×100的图片,使用霍夫变换检测图片中的直线,步骤如下:
直线可以用(p,θ)表示,首先创建一个2D数组,称作累加器,初始化所有的值为0,行为p,列为θ。
该数组大小决定结果准确性,若希望角度的精度为1度,那就需要180列,对于p,最大值为图片对角线的距离,如果希望达到像素级别,行数应该与图像的对角线的距离相等。
取直线上的第一个点,将其带入在直线在极坐标中的公式中,然后遍历θ的取值;0,1,2,,180,分别求出对应的p值,如果这个数值在上述累加器中存在相应的位置,则在该位置加一。
取第二个点,重复上述步骤,更新累加器中的值。
搜索累加器中的最大值,并找到其对应的(p,θ),就可将图像上的直线表示出来。
霍夫线检测API:

cv.HoughLines(img,rho,theta,threshold)

img:检测的图像,要求是二值化的图像
rho,theta:p,θ精确度
threshold:阈值,只有累加器中的值高于该阈值才被认为直线。

#霍夫线检测
img1=cv.imread('C:\\Users\\DELL\\Desktop\\2.jpg')

# cv.imshow('img1',img1)
# cv.waitKey(0)
edges=cv.Canny(img1,50,150)
plt.imshow(edges,cmap=plt.cm.gray)

gray=cv.cvtColor(img1,cv.IMREAD_GRAYSCALE)


lines=cv.HoughLines(edges,0.8,np.pi/180,150)
line=None
for line in lines:
    rho,theta=line[0]
    a=np.cos(theta)
    b=np.sin(theta)
    x0=a*rho
    y0=b*rho
    x1=int(x0+1000*(-b))
    y1=int(y0+1000*a)
    x2=int(x0-1000*(-b))
    y2=int(y0-1000*a)
    cv.line(img,(x1,y1),(x2,y2),(0,255,0))

plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img1),plt.title('霍夫变换线检测')
plt.xticks([]),plt.yticks([])
plt.show()

图像处理--OpenCV学习笔记_第70张图片
霍夫圆检测:
圆的表示式为
在这里插入图片描述
a,b代表圆点的圆心坐标,圆的半径为r,因此标准的霍夫圆的检测就是在这三个参数组成的三维空间累加器上进行圆形检测,此时效率就会很低,所以OpenCV中使用霍夫梯度法进行圆形检测。
第一阶段:检测圆心
1.1、对输入图像边缘检测;
1.2、计算图形的梯度,并确定圆周线,其中圆周的梯度就是它的法线;
1.3、在二维霍夫空间内,绘出所有图形的梯度直线,某坐标点上累加和的值越大,说明在该点上直线相交的次数越多,也就是越有可能是圆心;(备注:这只是直观的想法,实际源码并没有划线)
1.4、在霍夫空间的4邻域内进行非最大值抑制;
1.5、设定一个阈值,霍夫空间内累加和大于该阈值的点就对应于圆心。

第二阶段:检测圆半径
2.1、计算某一个圆心到所有圆周线的距离,这些距离中就有该圆心所对应的圆的半径的值,这些半径值当然是相等的,并且这些圆半径的数量要远远大于其他距离值相等的数量
2.2、设定两个阈值,定义为最大半径和最小半径,保留距离在这两个半径之间的值,这意味着我们检测的圆不能太大,也不能太小
2.3、对保留下来的距离进行排序
2.4、找到距离相同的那些值,并计算相同值的数量
2.5、设定一个阈值,只有相同值的数量大于该阈值,才认为该值是该圆心对应的半径
2.6、对每一个圆心,完成上面的2.1~2.5步骤,得到所有的圆半径。

原则上霍夫变换可以检测任何形状,但复杂的形状需要参数多,霍夫空间维数就多,因此在程序实现上所需的内存空间以及运行效率上都不利于把标准霍夫变换应用于实际复杂图形的检测中。霍夫梯度法是霍夫变换的改进,目的是降低空间维度,提高效率。
API

ircles=cv.HoughCircles(img,method,dp,minDist,param1=100,param2=100,minRadius=0,maxRadius=0)

img:输入图像
method:霍夫变换圆检测算法
dp:霍夫空间分辨率,dp=1表示霍夫空间与输入空间大小一致,dp=2表示霍夫空间为输入空间一半
minDist:圆心之间最小距离
param1:边缘检测时使用Canny算子的高阈值,低阈值是高阈值的一半
param2:检测圆心与确定半径时共有阈值
minRadius,maxRadius:检测到圆半径的最小值和最大值。
图像处理--OpenCV学习笔记_第71张图片

gay=cv.cvtColor(img1,cv.COLOR_BGRA2GRAY)
im=cv.medianBlur(gay,7)
circles=cv.HoughCircles(im,cv.HOUGH_GRADIENT,1,200,param1=100,param2=30,minRadius=0,maxRadius=100)
# print(circles)
# # circles = circles.astype(np.float32) / 255
circles = circles.astype(int)
# print(circles.dtype)
# print(circles)
for i in circles[0,:]:
    cv.circle(im,(i[0],i[1]),i[2],(0,255,0),2)
    cv.circle(im,(i[0],i[1]),2,(0,0,255),3)


plt.figure(figsize=(10,8),dpi=100)
plt.imshow(im),plt.title('霍夫变换圆检测')
plt.xticks([]),plt.yticks([])
plt.show()

图像处理--OpenCV学习笔记_第72张图片

图像特征提取描述

角点特征

图像处理--OpenCV学习笔记_第73张图片
1:图像特征

图像处理--OpenCV学习笔记_第74张图片

Harris和Shi-Tomas算法

具有旋转不变性,不具有尺度不变性。

Harris

1,原理
Harris角点检测的思想是通过图像的局部的小窗口观察图像,角点的特征是窗口沿任意方向移动都会导致图像灰度的明显变化,如图
图像处理--OpenCV学习笔记_第75张图片

在这里插入图片描述

图像处理--OpenCV学习笔记_第76张图片

图像处理--OpenCV学习笔记_第77张图片

图像处理--OpenCV学习笔记_第78张图片

图像处理--OpenCV学习笔记_第79张图片

图像处理--OpenCV学习笔记_第80张图片

图像处理--OpenCV学习笔记_第81张图片

dst=cv.cornerHarris(img,blockSize,ksize,k)

img:float32的原函数
blockSize:角点检测中要考虑的邻域大小
ksize:sobel使用的核大小
k:角点检测方程中自由参数,取值为【0.04,0.06】

图像处理--OpenCV学习笔记_第82张图片

img1=cv.imread('C:\\Users\\DELL\\Desktop\\jiaodian.png',1)
img2=cv.imread('C:\\Users\\DELL\\Desktop\\pipei.jpg',1)
gray=cv.cvtColor(img1,cv.COLOR_BGR2GRAY)
gray=np.float32(gray)

dst=cv.cornerHarris(gray,2,3,0.04)
img1[dst>0.001*dst.max()]=[0,0,255]
plt.figure(figsize=(10,8),dpi=100)
plt.imshow(img1),plt.title('Harris角点检测')
plt.xticks([]),plt.yticks([])
plt.show()

在这里插入图片描述

Shi-Tomas

SHi-Tomasi算法是对harris算法的改进,一般会比Harris算法得到更好的角点,Harris算法的角点响应函数是将矩阵M的行列式与M的迹相减,利用差值判断是否为角点,后来Shi和Tomasi提出改进的方法是,若矩阵M的两个特征值中较小的一个大于阈值,则认为他是角点:
图像处理--OpenCV学习笔记_第83张图片
从这幅图中,可以看出来只有当λ1和λ2都大于最小值时,才认为是角点。
实现:

corner=cv.goodFeaturesToTrack(img,maxcorner,qualitylevel,mindistance)

img:输入灰度图像
maxcorner:角点数的数目
qualitylevel:最低可接受的角点质量水平
mindistance:角点间的欧式距离,避免相邻特征点
返回:在所有低于质量水平的角点被排挤后,然后把合格的角点按距离排序,然后将质量较好的角点附近的角点删掉,最后找到max个角点返回

图像处理--OpenCV学习笔记_第84张图片

检测后:
图像处理--OpenCV学习笔记_第85张图片

SIFT/SURF

Harris和Shi-Tomas算法具有旋转不变性,不具有尺度不变性。当图片进行放大缩小后,使用同样的窗口,就检测不到角点。如图:
图像处理--OpenCV学习笔记_第86张图片
尺度不变特征转换即SIFT(Scale-invariant fearure transform),用来侦测与描述影像中的局部性特征,在空间尺度中寻找极值点,并提取出位置,尺度,旋转不变量。

基本流程:
1:尺度空间极值检测:搜索所有尺度的图像位置。通过高斯反差函数来识别潜在的对于尺度和旋转不变的关键点。
2:关键点定位:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于他们的稳定程度。
3:关键点方向:基于图像局部的梯度方向,分配给每个关键点位置一个或者多个方向。所有后面的对图像数据的操作都相对于关键点的方向,尺度和位置进行变换,从而保证了对于这些变换的不变性。
4:关键点描述:在每个关键点周围的邻域内,在选定的尺度上测量图像局部的梯度,这些梯度作为关键点的描述符,允许比较大的局部形状的变形或者光照变换。

尺度空间极值检测

在不同尺度空间不能使用相同的窗口检测极值点,对小的关键点使用小的窗口,大的关键点使用大的窗口,为达到上述目的,需要使用尺度空间滤波器。

高斯核是唯一可以产生多尺度空间的核函数。

一个图像的尺度空间L(x,y,σ),定义为原始图像I(x,y)与一个可变尺度的二维高斯函数G(x,y,σ)卷积运算,即图像处理--OpenCV学习笔记_第87张图片
σ为尺度空间因子,它决定了图像的模糊程度。在大尺度下表现的是图像的概貌信息,小尺度下表现的是图像的细节信息。

你可能感兴趣的:(图像处理,opencv,图像处理,计算机视觉)