Open CV是一个通用,开源,功能强大的图形处理和计算机视觉库
1999年, 加里·布拉德斯基在Intel公司创建了计算机视觉库(Computer Vision Library,CVL)项目,项目旨在提供通用的计算机视觉接口,接口用C++语言实现,以开源方式发布。 2006年10月,OpenCV 1.0版本正式发布。
OpenCV-Python是由原始OpenCV C++实现的Python包装器,是OpenCV库的Python接口。
用户通过OpenCV-Python可获得两大好处:
首先,代码运行速度与原始C/C ++代码一样快(因为它在后台运行的是实际C++代码);
其次,用Python编写代码更容易。
OpenCV-Python需要使用NumPy库,OpenCV在程序中使用NumPy数组存储图像数据。
1 主要模块及功能
1.1 内置基本数据结构和输入输出功能
OpenCV内置了丰富的与图像处理有关的数据结构,如Image、Point、Rectangle等。
core模块实现了各种基本的数据结构。
imgcodecs模块提供了图像文件的读写功能,用户使用简单的命令即可读写图像文件。
1.2 图像基础操作
imgproc模块提供了图像处理操作,如图像过滤、几何图像变换、绘图、色彩空间转换、直方图等。
1.3 图形用户界面操作
highgui模块提供了图像的图形窗口操作功能,如创建窗口显示图像或者视频、令窗口响应键盘和鼠标事件、操作窗口中图像的某个区域等。
1.4 视频分析
video模块提供了视频分析功能,如分析视频中连续帧之间的运动、跟踪视频中的目标。
videostab模块提供了视频稳定处理功能,可解决拍摄视频时的抖动问题。
optflow模块提供了与光流操作相关的算法。
1.5 3D重建
calib3d模块提供了3D重建功能,可根据2D图像创建3D场景。
1.6 特征提取
features2d模块提供了特征提取功能,可以从2D图像中检测和提取对象的特征。
1.7 对象检测
objdetect和xobjdetect模块提供了对象检测功能,可在图像中检测给定图像的位置
1.8 机器学习
ml模块提供了机器学习功能,包含了多种机器学习算法,如k近邻(k-Nearest Neighbors,kNN)、k均值聚类(k-Means Clustering)、支持向量机(Support Vector Machines,SVM)、神经网络(Neural Network)等。
机器学习算法广泛应用于目标识别、图像分类、人脸检测、视觉搜索等。
1.9 深度学习
深度神经网络(Deep Neural Network,DNN)模块提供了深度学习功能。深度学习是机器学习中近几年来快速发展的一个子领域,广泛应用于语音识别、图像识别、自然语言处理、图像修复、人脸识别等。
OpenCV的深度学习支持Caffe、TensorFlow、Torch、Darknet等著名的深度学习框架。
1.10 计算摄影
计算摄影通过图像处理技术来改善相机拍摄的图像,如全景图像、图像补光等。
photo和xphoto模块提供了与计算摄影有关的算法,
stitching模块提供了全景图像算法。
1.11 形态分析
shape模块提供了形态分析功能,可以识别图像中对象的形状、分析形状之间的相似性、转换对象形状等。
1.12 人脸检测和识别
OpenCV已在face模块中实现了人脸检测、人脸特征检测和人脸识别功能。
人脸检测属于对象检测,用于找出图像中人脸的位置和尺寸。
人脸特征检测属于特征检测,用于找出图像中人脸的主要特征。
人脸识别属于对象识别,包括从已知人脸集合中找出与未知人脸最匹配的人脸,以及验证给定人脸是否为某个已知人脸。
OpenCV实现了基于Haar级联分类器和基于深度学习的人脸检测算法,以及EigenFaces、FisherFaces和局部二进制编码直方图(Local Binary Patterns Histograms,LBPH)等人脸识别算法。
1.13 表面匹配
surface_matching模块提供了3D对象识别算法和3D特征的姿态估计算法,用于根据图像的深度和强度信息识别3D对象。
1.14 文本检测和识别
text模块提供了文本检测和识别功能,用于识别和检测图像中的文本,实现车牌识别、道路标志识别、内容数字化等相关应用。
2 OpenCV图像基本操作
2.1 读写,显示图像
1.读取图像
OpenCV的imread()函数用于将文件中的图像读入内存,imread()函数支持各种静态图像文件格式,如jpg、bmp、png、jpeg等。
图像数据格式解析:
1. img.shape的输出结果为(512, 512, 3),说明表示彩色图像的数组是一个三维数组,3个值依次表示图像的高度、宽度和通道数;图像的分辨率为512×512。
2. img.dtypes的输出结果为uint8,说明每个数组元素用一个字节(8位)保存,每个数组元素为一个像素的B、G和R通道的颜色值,颜色值取值范围为[0,255]。
3. img.size的输出结果为786432,等于数组形状的3个维度大小的乘积,即512×512×3。
imread()函数的完整格式如下:
img=cv2.imread(filename,flag) 其中,filename为图像文件名,flag为图像读取格式标志,如下页表格所示。imread()函数在正确读取图像文件时,返回表示图像的NumPy数组;否则返回NULL。
2.写图像
OpenCV的imwrite()函数用于将NumPy数组中保存的图像写入文件
import cv2
import numpy
img=numpy.zeros((50,50), dtype=numpy.uint8) #创建大小为50×50的黑色正方形图像 cv2.imwrite(‘./data/mypic2-1.jpg',img) #将图像存入文件
3.显示图像
OpenCV的imshow()函数用于在窗口中显示图像
import cv2
img=cv2.imread('lena.jpg',cv2.IMREAD_REDUCED_COLOR_2) #读取图像并将图像尺寸减小1/2 cv2.imshow('lena',img) #显示图像
cv2.waitKey(1)
key=0
while key!=27: #按Esc键时终止循环,=113是按q键退出
key=cv2.waitKey() #等待按键
cv2.destroyWindow('lena') #关闭图像窗口
imshow()函数的第1个参数为窗口名称,第2个参数为图像数组
waitKey()函数等待用户输入
rv=cv2.waitKey([delay]) 其中:
rv保存函数返回值,如果没有键被按下,返回-1;如果有键被按下,返回键的ASCII码。
参数delay表示等待按键的时间(单位为毫秒),负数或0表示无限等待,默认值为0;
设置了delay参数时,等待时间结束时结束等待,函数返回-1。
2.2 读写,播放视频
OpenCV的VideoCapture类和VideoWriter类提供了视频处理功能,支持各种格式的视频文件。
视频处理的基本操作步骤如下。
1.将视频文件或者摄像头作为数据源来创建VideoCapture对象。
2.调用VideoCapture对象的read()方法获取视频中的帧,每一帧都是一幅图像。
3.调用VideoWriter对象的write()方法将帧写入视频文件,或者调用cv2.imshow()函数在窗口中显示帧(即播放视频)。
1.捕获摄像头视频
要捕获摄像头视频,需要将摄像头ID作为参数来创建VideoCapture对象。通常,0表示默认摄像头
2.将视频写入文件
将视频写入文件,需要逐帧将视频写入文件
3.播放视频
OpenCV播放视频的实质是逐帧读取和显示帧图像
import cv2
vc=cv2.VideoCapture(0) #创建VideoCapture对象,视频源为默认摄像头
fps=30 #预设视频帧速率
size=(int(vc.get(cv2.CAP_PROP_FRAME_WIDTH)), int(vc.get(cv2.CAP_PROP_FRAME_HEIGHT))) #读取视频大小
vw=cv2.VideoWriter('test2-7out.avi',cv2.VideoWriter_fourcc('X','V','I','D’), fps, size) #设置帧
success,frame=vc.read() #读第1帧
while success: #循环读视频帧,直到视频结束
vw.write(frame) #将帧写入文件
cv2.imshow('MyCamera',frame) #显示帧
key=cv2.waitKey()
if key==27: #按【Esc】键结束
break
success,frame=vc.read() #读下一帧
vc.release() #关闭视频
2.3 操作灰度图像
计算机将灰度处理为256级(0~255),0表示黑色,255表示白色,用一个字节来存储一个像素的值。OpenCV使用单通道的二维数组来表示灰度图像
2.4 操作彩色图像
不同色彩空间中,颜色的表示方法有所不同,但不同色彩空间之间可根据公式进行转换
RGB色彩空间中图像的表示方法
(1)RGB中的R指红色(Red),G指绿色(Green), B指蓝色(Blue)。
(2)在表示图像时,有R、G和B 3个通道,分别对应红色、绿色和蓝色。
(3)每个通道中像素的取值范围为[0,255],用3个通道的像素组合表示彩色图像。
(4)RGB色彩空间中颜色通道依次为R、G、B,但OpenCV默认的图像格式为BGR,即颜色通道依次为B、G、R。
2.5 图像通道操作
1.拆分通道
通过数组索引拆分通道,OpenCV中BGR格式的图像是一个三维数组,可用数组的索引操作拆分3个色彩通道;
import cv2
img=cv2.imread('lena.jpg',cv2.IMREAD_REDUCED_COLOR_2) #读图像,将其尺寸减小为原来的1/2
cv2.imshow('lena',img) #显示原图像
b=img[:,:,0] #获得B通道图像
g=img[:,:,1] #获得G通道图像
r=img[:,:,2] #获得R通道图像
cv2.imshow('lena_B',b) #显示B通道图像
cv2.imshow('lena_G',g) #显示G通道图像
cv2.imshow('lena_R',r) #显示R通道图像
cv2.waitKey(0)
也可以使用cv2.split()函数拆分通道
例如:b,g,r=cv2.split(img)
cv2.split()函数拆分通道的效率不如数组索引
2.合并图像通道
cv2.merge()函数可将3通道图像合并,其基本格式如下。 img=cv2.merge([b,g,r]) 其中,变量img保存生成的图像,b、g、r是3个单通道图像,依次将它们作为B、G和R通道的图像进行合并
import cv2
img=cv2.imread('lena.jpg',cv2.IMREAD_REDUCED_COLOR_2)
cv2.imshow('lena',img) #显示原图像
b,g,r=cv2.split(img) #按通道拆分图像
rgb=cv2.merge([r,g,b]) #按新顺序合并
gbr=cv2.merge([g,b,r]) #按新顺序合并
cv2.imshow('lena_RGB',rgb) #显示合并图像
cv2.imshow('lena_GBR',gbr) #显示合并图像
cv2.waitKey(0)
3 图像处理进阶
3.1 色彩空间
1.GRAY色彩空间
GRAY色彩空间通常指8位灰度图像,其颜色取值范围为[0,255],共256个灰度级
从RGB色彩空间转换为GRAY色彩空间的计算公式如下
Gray = 0.299R+0.587G+0.114B
其中, R 、 G和B为RGB色彩空间中红、绿和蓝通道的图像。
在cv2.cvtColor()函数中使用cv2.COLOR_BGR2GRAY转换码可将图像从BGR色彩空间转换为GRAY色彩空间
import cv2
img=cv2.imread('Lenna.png') #读取图像
cv2.imshow('BGR',img) #显示图像
img2=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转换色彩空间为GRAY
cv2.imshow('GRAY',img2) #显示图像
cv2.waitKey(0)
2.RGB色彩空间
RGB色彩空间使用R(Red,红)、G(Green,绿)和B(Blue,蓝)3种基本颜色表示图像像素。 RGB色彩空间中,图像的每个像素用一个三元组表示,三元组中的3个值依次表示红色、绿色和蓝色,依次对应R、G和B通道。RGB的取值范围是0--255,3个通道不同颜色值组合形成不同的颜色。
OpenCV默认采用BGR色彩空间,它按B、G和R通道顺序表示图像。 例如:一个像素表示(0,255,255) , 它对应的颜色是(B, G, R);这3个颜色混在一起效果是浅黄色。
在cv2.cvtColor()函数中使用cv2.COLOR_BGR2RGB转换码可将图像从BGR色彩空间转换为RGB色彩空间
3.HSV色彩空间
HSV色彩空间是根据直观特性,按色彩、深浅、明暗来描述的;这种方式的表达更接近于人的视觉;通常这种格式用在调色、配色
色调H(Hue) -- 表示颜色,用角度表示,取值范围为[0°,360°],从红色开始按逆时针方向计算。 红色为0°、黄色为60°、绿色为120°、青色为180°、蓝色为240°、紫色为300°等。
饱和度S(Saturation) -- 表示颜色接近光谱色的程度,或者表示光谱色中混入白光的比例。 光谱色中白光的比例越低,饱和度越高,颜色越深、艳。 光谱色中白光比例为0时,饱和度达到最高。饱和度的取值范围为[0,1]。
亮度V(Value) -- 表示颜色明亮的程度,是人眼可感受到的明暗程度,其取值范围为[0,1]。
在cv2.cvtColor()函数中使用cv2.COLOR_BGR2HSV转换码可将图像从BGR色彩空间转换为HSV色彩空间
4.YUV色彩空间
采用这种颜色空间来作为视频压缩的标准,在视频传输中常用的数据格式,
比如:家用电视。
YUV444 -- 无损压缩
YUV422 – UV 色彩损失1/2半
YUV420 – UV 色彩损失3/4半
YUV色彩空间用亮度Y、红色Cr和蓝色Cb表示图像。
Y: 表示明亮度(Luminance 或 Luma),也称灰度图。
U、V: 表示色度(Chrominance 或 Chroma),作用是描述影像的色彩及饱和度。
从RGB色彩空间转换为YUV色彩空间的计算公式如下:
Y = 0.299R + 0.587G + 0.114B
U = 0.713(R – Y) + 128
V = 0.564(B – Y) + 128
在cv2.cvtColor()函数中使用cv2.COLOR_BGR2YCrCb转换码可将图像从BGR色彩空间转换为YUV(或YCrCb)色彩空间
3.2 图像变换
1.几何变换
(1)图像缩放
OpenCV的cv2.resize()函数用于缩放图像;其基本格式如下:
dst=cv2.resize(src,dsize[,dst[,fx[,fy[,interpolation]]]])
参数说明如下:
src表示用于缩放的原图像。
dsize表示转换后的图像大小。
fx, fy表示水平和垂直方向的缩放比例。
当dsize参数不为None时,不管是否设置参数fx和fy,都由dsize来确定目标图像的大小。
dsize是一个二元组,其格式为“(width, height)”;
width表示目标图像的宽度,height表示目标图像的高度。
当dsize参数为None时,参数fx和fy不能设置为0。 此时,目标图像的宽度为“round(原图像的宽度×fx)”,目标图像的高度为“round(原图像的高度×fy)”。
在转换过程中,可能存在一些不能通过转换算法确定值的像素,插值方式决定了如何获得这些像素的值;可用的常见插值方式如下
cv2.INTER_NEAREST:最近邻插值。
cv2.INTER_LINEAR:双线性插值,默认方式。
cv2.INTER_CUBIC:3次样条插值。
cv2.INTER_AREA:区域插值。
cv2.INTER_LANCZOS4:Lanczos插值。
cv2.INTER_LINEAR_EXACT:位精确双线性插值。
示例代码如下:
#缩放图像
import cv2
img=cv2.imread('bee.jpg') #读取图像
sc=[1,0.2,0.5,1.5,2] #设置缩放比例
cv2.imshow('showimg',img) #显示图像
while True:
key=cv2.waitKey()
if 48<=key<=52: #按键【0】【1】【2】【3】或【4】
x=y=sc[key-48] #获得缩放比例
img2=cv2.resize(img,None,fx=x,fy=y) #缩放图像
cv2.imshow('showimg',img2) #显示图像
(2)图像翻转
OpenCV的cv2.flip()函数用于翻转图像,其基本格式如下。 dst=cv2.flip(src, flipCode)
参数说明如下:
dst表示转换后的图像。
src表示原图像。 flipCode表示翻转类型。
flip=0时绕x轴翻转(垂直翻转),
flip>0的整数时绕y轴翻转(水平翻转),
flip<0的整数时同时绕x轴和y轴翻转(水平和垂直翻转)
#翻转图像
import cv2
img=cv2.imread('bee.jpg') #读取图像
cv2.imshow('showimg',img) #显示图像
while True:
key=cv2.waitKey()
if key==48: #按【0】键时显示原图
img2=img
elif key==49: #按【1】键时垂直翻转
img2=cv2.flip(img,0)
elif key==50: #按【2】键时水平翻转
img2=cv2.flip(img,1)
elif key==51: #按【3】键时水平、垂直翻转
img2=cv2.flip(img,-1)
cv2.imshow('showimg',img2)
2.仿射变换
仿射变换包含了平移、旋转、缩放、映射等操作
其主要特点是:原图像中的所有平行线在转换后的图像中仍然平行
OpenCV的cv2.warpAffine()函数用于实现图像的仿射变换 其基本格式如下: dst=cv2.warpAffine(src, M, dsize,[ flags])
参数说明如下:
dst表示转换后的图像,图像类型和原图像一致,大小由dsize决定。
src表示原图像。 M是一个大小为2×3的转换矩阵,使用不同的转换矩阵可实现平移、旋转等多种操作。
dsize为转换后的图像大小。
flags为插值方式,默认值为cv2.INTER_LINEAR。
在cv2.warpAffine()函数省略可选参数时,图像转换的矩阵运算公式如下:
dst(x,y) = src(M11x+M12y+M13, M21x+M22y+M23)
1.平移
平移是指将图像沿水平或垂直方向移动一定的像素。假设将图像水平移动m个像素,垂直移动n个像素,则图像转换的矩阵运算公式如下。
dst(x,y) = src(x+m, y+n)
等价于如下公式:
dst(x,y) = src(1·x + 0·y + m, 0·x + 1·y + n)
所以,转换矩阵
#将图像向右移动100像素,向下移动50像素;
import cv2
import numpy as np
img=cv2.imread('bee.jpg') #读取图像
cv2.imshow('img',img) #显示图像
height=img.shape[0] #获得图像高度
width=img.shape[1] #获得图像宽度
dsize=(width, height)
m=np.float32([[1,0,100],[0,1,50]]) #创建转换矩阵
img2=cv2.warpAffine(img,m,dsize) #平移图像
cv2.imshow('imgx+100y+50',img2) #显示图像
cv2.waitKey(0)
2.缩放
缩放是指将图像沿水平或垂直方向按比例放大或缩小。假设图像水平缩放比例为m,垂直方向缩放比例n,则图像转换的矩阵运算公式如下
dst(x,y) = src(x•m, y•n)
等价于如下公式:
dst(x,y) = src(m·x + 0·y + 0, 0·x + n·y + 0)
所以,转换矩阵
#图像缩放
import cv2
import numpy as np
img=cv2.imread('bee.jpg') #读取图像
cv2.imshow('img',img) #显示图像
height=img.shape[0] #获得图像高度
width=img.shape[1] #获得图像宽度
dsize=(width,height)
m=np.float32([[0.5,0,0],[0,0.5,0]]) #创建转换矩阵
img2=cv2.warpAffine(img,m,dsize) #执行缩放
cv2.imshow('img0.5x+0.5y',img2) #显示图像
cv2.waitKey(0)
3.旋转
OpenCV的cv2.getRotationMatrix2D()函数可用于计算旋转操作的转换矩阵,
其基本格式如下:
m = cv2.getRotationMatrix2D(center, angle, scale)
参数说明如下:
center表示原图像中作为旋转中心的坐标。
angle表示旋转角度,正数表示按逆时针方向旋转,负数表示按顺时针方向旋转。
scale表示目标图像与原图像的大小比例。
假设原图像宽度为width,高度为height,将图像中心作为旋转中心顺时针旋转60°,并将图像缩小50%,则用于计算转换矩阵的语句如下。
m = cv2.getRotationMatrix2D((width/2, height/2), -60, 0.5)
#图像旋转
import cv2
img=cv2.imread('bee.jpg') #读取图像
cv2.imshow('img',img) #显示图像
height=img.shape[0] #获得图像高度
width=img.shape[1] #获得图像宽度
dsize=(width,height)
m=cv2.getRotationMatrix2D((width/2,height/2),-60,0.5) #创建转换矩阵
img2=cv2.warpAffine(img,m,dsize) #执行旋转
cv2.imshow('imgRotation',img2) #显示图像
cv2.waitKey(0)
4.三点映射变换
三点映射变换会将图像转换为任意的平行四边形,cv2.getAffineTransform()函数用于计算其转换矩阵,基本格式如下:
m = cv2.getAffineTransform(src, dst)
参数说明如下:
src为原图像中3个点的坐标。
dst为原图像中3个点在目标图像中的对应坐标。
cv2.getAffineTransform()函数将src和dst中的3个点作为平行四边形左上角、右上角和左下角的3个点,按原图和目标图像与3个点之间的坐标关系计算所有像素的转换矩阵。
#图像的三点映射变换
import cv2
import numpy as np
img=cv2.imread('bee.jpg') #读取图像
cv2.imshow('img',img) #显示图像
height=img.shape[0] #获得图像高度
width=img.shape[1] #获得图像宽度
dsize=(width,height)
src=np.float32([[0,0], [width-10,0], [0,height-1]]) #取原图像中的3个点
dst=np.float32([[50,50], [width-100,80], [100,height-100]]) #设置3个点在目标图像中的坐标
m = cv2.getAffineTransform(src, dst) #创建转换矩阵
img2=cv2.warpAffine(img,m,dsize) #执行转换
cv2.imshow('imgThreePoint',img2) #显示图像
cv2.waitKey(0)
5.透视
透视变换会将图像转换为任意的四边形 其主要特点是:
原始图像中的所有直线在转换后的图像中仍然是直线。
OpenCV的cv2.warpPerspective()函数用于执行透视变换操作;
其基本格式如下:
dst=cv2.warpPerspective(src,M,dsize[,flags]) 其中,
M是大小为3×3的转换矩阵,其他参数含义与cv2.warpAffine()函数中的一致。
OpenCV的cv2.getPerspectiveTransform()函数用于计算透视变换使用的转换矩阵;
其基本格式如下:
M=cv2.getPerspectiveTransform(src,dst)
参数说明如下。
src为原图像中4个点的坐标。
dst为原图像中4个点在转换后的目标图像中的对应坐标。
#图像的四点映射变换
import cv2
import numpy as np
img=cv2.imread('bee.jpg') #读取图像
cv2.imshow('img',img) #显示图像
height=img.shape[0] #获得图像高度
width=img.shape[1] #获得图像宽度
dsize=(width,height)
src=np.float32([[0,0],[width-10,0],[0,height-1] ,[0, height - 2]]) #取原图像中的4个点
dst=np.float32([[50,50],[width-100,80], [100,height-100], [100, height - 200]]) #设置4个点在目标图像中的坐标
m = cv2.getPerspectiveTransform(src, dst) # 创建转换矩阵img2 = cv2.warpPerspective(img, m, dsize) # 执行转换
cv2.imshow('imgfourPoint',img2) #显示图像
cv2.waitKey(0)
3.3 图像平滑
1.图像平滑概念
平滑也称模糊, 是一项简单且使用频率很高的图像处理方法
图像平滑是指受传感器和大气等因素的影响,遥感图像上会出现某些亮度变化过大的区域,或出现一些亮点(也称噪声)。这种为了抑制噪声,使图像亮度趋于平缓的处理方法就是图像平滑。 目的是使图像亮度平缓渐变,减小突变梯度,改善图像质量。
2.图像滤波
1.均值滤波
指以当前点为中心,用其周围N×N个点像素值的平均值来替代当前点的像素值
用于计算平均值的N×N个点称为邻域,用于滤波计算的卷积核大小与邻域大小相同
125 | 129 | 130 |
134 | 253 | 127 |
125 | 133 | 131 |
则卷积核为
则中心点的均值滤波值=(125+129+130+134+253+127+125+133+131)÷9=143, 143比253更接近周围的值
OpenCV的cv2.blur()函数用于实现均值滤波,其基本格式如下。
dst=cv2.blur(src,ksize [,anchor [,borderType]])
参数说明如下。
dst为滤波结果图像。
src为原图像。
ksize为卷积核大小,表示为(width,height),width和height通常设置为相同值,且为正数和奇数。
anchor为锚点,默认值为(-1,-1),表示锚点位于卷积核中心。
borderType为边界值处理方式。
# 均值滤波
import cv2
img=cv2.imread('lena.jpg')
cv2.imshow('img',img)
img2=cv2.blur(img,(5,5)) #可调整卷积核大小以查看不同效果
cv2.imshow('imgBlur',img2)
cv2.waitKey(0)
2.方框滤波
原理:方框滤波以均值滤波为基础,可选择是否对滤波结果进行归一化。如果选择进行归一化,则滤波结果为邻域内点的像素值之和的平均值,否则滤波结果为像素值之和。
例如,一个大小为3×3的邻域如图所示
21 | 32 | 37 |
3 | 56 | 30 |
14 | 20 | 36 |
则卷积核为
则中心点的方框滤波值=(21+32+37+56+3+30+14+20+36)÷9=249(27), 选择归一化操作就是27,否则就是249
OpenCV的cv2.boxFilter()函数用于实现方框滤波,其基本格式如下: dst=cv2.boxFilter(src,ddepth,ksize[,anchor[,normalize[,borderType]]])
参数说明如下:
ddepth为目标图像的深度,一般使用-1表示与原图像的深度一致。
normalize为True(默认值)时执行归一化操作,为False时不执行归一化操作。 其他参数含义和cv2.blur()函数中的一致。
import cv2
img=cv2.imread('lena2.jpg')
cv2.imshow('img',img)
img2=cv2.boxFilter(img,-1,(3,3),normalize=False) #可调整卷积核大小以查看不同效果
cv2.imshow('imgBlur',img2)
cv2.waitKey(0)
3.高斯滤波
高斯滤波-高斯滤波是一种线性平滑滤波,适用于消除高斯噪声
原理:它按像素点与中心点的不同距离,赋予像素点不同的权重值,越靠近中心点权重值越大,越远离中心点权重值越小;然后根据权重值计算邻域内所有像素点的和,将和作为中心点的像素值。
OpenCV的cv2.GaussianBlur()函数用于实现高斯滤波,
其基本格式如下:
dst=cv2.GaussianBlur(src,ksize,sigmaX [,sigmaY [,borderType]])
参数说明如下:
sigmaX为X方向上的高斯核标准差
sigmaY为X方向上的高斯核标准差
其他参数含义和cv2.blur()函数中的一致
如果sigmaY为0,则令其等于sigmaX 如果sigmaX和sigmaY均为0,
则按下面公式计算,其中ksize的宽和高为(width, height)
sigmaX=0.3×((width-1)×0.5-1)+0.8
sigmaY=0.3×((height-1)×0.5-1)+0.8
# 高斯滤波
import cv2
img=cv2.imread('lena2.jpg')
cv2.imshow('img',img)
img2=cv2.GaussianBlur(img,(5,5),0,0) #可调整卷积核大小以查看不同效果
cv2.imshow('imgBlur',img2)
cv2.waitKey(0)
4.中值滤波
将邻域内的所有像素值排序,取中间值作为邻域中心点的像素值
125 | 129 | 130 |
134 | 253 | 127 |
125 | 133 | 131 |
125 | 129 | 130 |
134 | 130 | 127 |
125 | 133 | 131 |
从小到大排序:125 125 127 129 130 131 133 134 253
取到中间值(中位数):130
用130 替换 253
OpenCV的cv2.medianBlur()函数用于实现中值滤波,
其基本格式如下:
dst=cv2.medianBlur(src,ksize) 其中,ksize表示卷积核大小,是一个整型(而不是二元组),必须是大于1的奇数。
# 中值滤波
import cv2
img=cv2.imread('lena2.jpg')
cv2.imshow('img',img)
img2=cv2.medianBlur(img,9) #可调整卷积核大小以查看不同效果
cv2.imshow('imgBlur',img2)
cv2.waitKey(0)
5.双边滤波
在计算像素值的同时会考虑距离和色差信息,从而可在消除噪声的同时保护边缘信息
在执行双边滤波操作时,如果像素点与当前点色差较小,则赋予其较大的权重值,否则赋予其较小的权重值
OpenCV的cv2.bilateralFilter()函数用于实现双边滤波,
其基本格式如下:
dst=cv2.bilateralFilter(src,d,sigmaColor,sigmaSpace[,borderType])
参数说明如下:
d表示以当前点为中心的邻域的直径,一般为5
sigmaColor为像素值域的sigma值,表示双边滤波选择的色差范围
sigmaSpace为空间坐标中的sigma值,值越大表示越多的像素点参与滤波计算。
当d>0时,由d决定邻域大小;否则d由sigmaSpace计算得出,与sigma Space成比例
# 双边滤波
import numpy as np
import cv2
img=cv2.imread('lena2.jpg')
cv2.imshow('img',img)
img2=cv2.bilateralFilter(img,9,100,100) #可调整参数以查看不同效果
cv2.imshow('imgBlur',img2)
cv2.waitKey(0)
6.2D卷积
均值滤波、高斯滤波、方框滤波、中值滤波和双边滤波等可以通过参数来确定卷积核,2D卷积可使用自定义的卷积核来执行滤波操作。
OpenCV的cv2.filter2D()函数用于实现2D卷积,
其基本格式如下:
dst=cv2.filter2D(src,ddepth,kernel[,anchor[,delta[,borderType]]])
参数说明如下:
ddepth表示目标图像的深度,一般使用-1表示与原图像一致
kernel为单通道卷积核(一维数组)
anchor为图像处理的锚点
delta为修正值,未省略时,将加上该值作为最终的滤波结果
borderType为边界值处理方式
# 2D卷积
import numpy as np
import cv2
img=cv2.imread('lena2.jpg')
k1=np.array([[3,3,3,3,3], [3,9,9,9,3], [3,11,12,13,3], [3,8,8,8,3], [3,3,3,3,3],])/25 #自定义卷积核1
k2=np.ones((5,5),np.float32)/25 #自定义卷积核2
img2=cv2.filter2D(img,-1,k1)
cv2.imshow('imgK1',img2)
img2=cv2.filter2D(img,-1,k2)
cv2.imshow('imgK2',img2)
cv2.waitKey(0)
3.4 图像分割
1.图像分割概述
图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。
数字图像,我们往往会对他们中的某一部分感兴趣,这些部分我们称为前景或者目标, 其余部分称为背景。
主要方法有基于阈值的分割、基于边缘、基于区域、基于聚类、基于图论、基于深度学习等。
2.基于阈值的图像分割方法
1.全局阈值分割
介绍:Prewitt 等人于六十年代中期提出的直方图双峰法(也称 mode 法) 是典型的全局单阈值分割取得阈值方法。
基本思想是:假设图像中有明显的目标和背景,则其灰度直方图呈双峰分布,当灰度级直方图具有双峰特性时,选取两峰之间的谷对应的灰度级作为阈值。
算法实现:找到第一个峰值Hmax1和第二个峰值Hmax2, 再找到第一和第二个峰值之间的谷值T对应的值,谷值就是那个阀值了。
matplotlib.pyplot.hist()函数可根据图像绘制直方图,其基本格式如下:
matplotlib.pyplot.hist(src,bins)
参数说明如下:
src为用于绘制直方图的图像数据,必须是一维数组。
通常,OpenCV中的BGR图像是三维数组,可用ravel()函数将其转换为一维数组。
bins为灰度级分组数量。
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('./person.jpg') # 读取图像
cv2.imshow('original', img) # 显示原图像
plt.hist(img.ravel(), 256) # 绘制直方图
plt.show() # 显示直方图
实现方式:全局阈值处理是指将大于阈值的像素值设置为255,将其他像素值设置为0;或者将大于阈值的像素值设置为0,将其他像素值设置为255。
OpenCV的cv2.threshold()函数用于实现全局阈值处理,
其基本格式如下:
retval, dst=cv2.threshold(src, thresh, maxval, type)
参数说明如下:
retval为返回的阈值。
dst为全局阈值处理后的结果图像。
src为原图像。
thresh为设置的阈值,通常使用直方图双峰法获取。
maxval当像素值大于阈值被赋予新的像素值(使用在:THRESH_BINARY和THRESH_BINARY_INV)。
type为阈值类型。
(1)二值化阈值处理
cv2.threshold()函数的type参数值为cv2.THRESH_BINARY时执行二值化阈值处理,将大于阈值的像素值设置为255(maxval),将其他像素值设置为0。
假设图像像素,将阈值设置为150,二值化阈值处理结果如下图所示。
123 | 230 | 187 |
176 | 150 | 200 |
230 | 180 | 135 |
0 | 255 | 255 |
255 | 0 | 255 |
255 | 255 | 0 |
(2)反二值化阈值处理
cv2.threshold()函数的type参数值为cv2.THRESH_BINARY_INV时执行反二值化阈值处理,将大于阈值的像素值设置为0,将其他像素值设置为255(maxval)。
假设图像像素,将阈值设置为150,反二值化阈值处理结果如下所示。
123 | 230 | 187 |
176 | 150 | 200 |
230 | 180 | 135 |
255 | 0 | 0 |
0 | 255 | 0 |
0 | 0 | 255 |
(3)截断阈值处理
cv2.threshold()函数的type参数值为cv2.THRESH_TRUNC时执行截断阈值处理,将大于阈值的像素值设置为阈值,其他像素值保持不变。
假设图像像素,将阈值设置为150,截断阈值处理结果如下图所示。
123 | 230 | 187 |
176 | 150 | 200 |
230 | 180 | 135 |
123 | 150 | 150 |
150 | 150 | 150 |
150 | 150 | 135 |
(4)超阈值0处理
cv2.threshold()函数的type参数值为cv2.THRESH_TOZERO时执行超阈值零处理,将大于阈值的像素值设置为0,其他像素值保持不变。
假设图像像素,将阈值设置为150,超阈值零处理结果如下所示。
123 | 230 | 187 |
176 | 150 | 200 |
230 | 180 | 135 |
123 | 0 | 0 |
0 | 150 | 0 |
0 | 0 | 135 |
(5)低阈值零处理
cv2.threshold()函数的type参数值为cv2.THRESH_TOZERO_INV时执行低阈值零处理,将小于阈值的像素值设置为0,其他像素值保持不变。
假设图像像素,将阈值设置为150,低阈值零处理结果如下所示。
123 | 230 | 187 |
176 | 150 | 200 |
230 | 180 | 135 |
0 | 230 | 187 |
176 | 150 | 200 |
230 | 180 | 0 |
2.自适应阈值处理
介绍:自适应阈值处理也称局部阈值处理,它通过计算每个像素点邻域的加权平均值来确定阈值,并用该阈值处理当前像素点。
OpenCV的cv2.adaptiveThreshold()函数用于实现自适应阈值处理,
其基本格式如下:
dst=cv2.adaptiveThreshold(src,maxValue,adaptiveMethod, thresholdType,blockSize,C)
参数说明如下:
dst为阈值处理的结果图像。
src为原图像。
maxValue为最大值。
adaptiveMethod为自适应方法;
thresholdType为阈值处理方式,其值为
cv2.THRESH_BINARYcv2.THRESH_BINARY_INV。
blockSize为计算局部阈值的邻域的大小。 C为常量,自适应阈值为blockSize指定邻域的加权平均值减去C。
adaptiveMethod为自适应方法,其值为:
cv2.ADAPTIVE_THRESH_MEAN_C:邻域中所有像素点的权重值相同; cv2.ADAPTIVE_THRESH_GAUSSIAN_C:邻域中像素点的权重值与其到中心点的距离有关,通过高斯方程可计算各个点的权重值。
自适应阈值会每次取图片的一小部分计算阈值,这样图片不同区域的阈值不相同,适用于敏感分布不均匀的图片,适用于明暗差异较大的图像。
全局阈值处理适用于色彩均衡的图像。
3.总结
阈值分割处理 -- 把前景跟背景划分
(1)求得阈值
双峰法获取阈值
迭代法阈值分割
自适应阈值分割
(2)处理的方式
二值化阈值处理 : 背景设置为白色,前景黑色
反二值化阈值处理 : 背景设置为黑色,前景白色
截断阈值处理 : 将大于阈值的像素值设置为阈值,其他像素值保持不变
超阈值零处理 : 将大于阈值的像素值设置为0,其他像素值保持不变
低阈值零处理 : 将小于阈值的像素值设置为0,其他像素值保持不变
3.图像边缘检测概述
图像边缘检测的原理是检测出图像中所有灰度值变化较大的点,而且这些点连接起来就构成了若干线条,这些线条就可以称为图像的边缘。
边缘检测的目的是标识数字图像中亮度变化明显的点,大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。
作用:提取物体重要的结构属性(特征)。
常见的边缘检测算法包括:
Soble边缘检测;
拉普拉斯边缘检测;
Canny边缘检测
4.图像边缘检测方法原理
1.Soble边缘检测 :一阶导数算子(Sobel算子)
边缘 – 是像素值发生跃迁的地方(变化率最大处,导数最大处),是图像的显著特征之一,在图像特征提取、对象检测、模式识别等方面都有重要的作用。
图像梯度概念: 连续函数的梯度 把图片想象成连续函数,因为边缘部分的像素值是与旁边像素明显有区别的,所以对图片局部求导数的极值,就可以得到整幅图片的边缘信息了。 不过图片是二维的离散函数,导数就变成了差分,这个差分就称为图像的梯度。
Sobel算子 又被称为一阶微分算子,用来计算图像灰度的近似梯度;在水平和垂直两个方向上求导,得到的是图像在X方向与Y方向梯度图像。
缺点:比较敏感,容易受影响,要通过高斯模糊(平滑)来降噪。
Sobel边缘检测将高斯滤波和微分结合起来执行图像卷积运算,其结果具有一定的抗噪性。
cv2.Sobel()函数用于实现Sobel边缘检测,
其基本格式如下。
dst=cv2.Sobel(src,depth,dx,dy[,ksize[,scale[,delta[,borderType]]]])
参数说明如下。
dst表示边缘检测结果图像。
src为原图像。
depth为目标图像的深度。
dx为导数x的阶数。
dy为导数y的阶数。
ksize为扩展的Sobel内核的大小,必须是1、3、5或7,默认是3。
scale为计算导数的可选比例因子;默认 1,表示不进行缩放。
delta为添加到边缘检测结果中的可选增量值,默认0。
borderType为边界值类型。
2.拉普拉斯边缘检测
Laplacian(拉普拉斯)边缘检测使用图像矩阵与拉普拉斯核进行卷积运算,其本质是计算图像中任意一点与其在水平方向和垂直方向上4个相邻点平均值的差值。
拉普拉斯是用二阶差分计算边缘的,看连续函数的情况下:
在一阶微分图中极大值或极小值处,认为是边缘。
在二阶微分图中极大值和极小值之间的过 0 点,被认为是边缘
cv2.Laplacian()函数用于实现Laplacian边缘检测,
其基本格式如下:
dst=cv2.Laplacian(src,ddepth[,ksize[,scale[,delta[,borderType]]]])
参数说明如下:
dst表示边缘检测结果图像。
src为原图像。
ddepth为目标图像的深度, 即存储像素的位数,如:cv2.CV_8U表示8位无符号整型数据。 ksize为用于计算二阶导数滤波器的系数,必须为正数且为奇数,默认3。
scale为可选比例因子,默认1。
delta为添加到边缘检测结果中的可选增量值,默认0。
borderType为边界值样式类型。
3.Canny边缘检测
Laplacian边缘检测和Sobel边缘检测都是通过卷积运算来计算边缘,它们的算法比较简单,因此结果可能会损失过多的边缘信息或有很多的噪声。
Canny边缘检测的算法比较复杂,是一个多阶段检测方法,它包含下列5个步骤。
(1)使用高斯滤波去除图像噪声。
(2)使用Sobel核进行滤波,计算梯度。
(3)在边缘使用非最大值抑制。
(4)对检测出的边缘使用双阈值以去除假阳性。
(5)分析边缘之间的连接性,保留真正的边缘,消除不明显的边缘。
首先,图像降噪。因为噪声就是灰度变化很大的地方,所以容易被识别为伪边缘。
第二步,计算图像梯度,得到可能边缘。因为灰度变化的地方可能是边缘,也可能不是边缘。这一步就有了所有可能是边缘的集合。常采用sobel核进行梯度计算。
第三步,非极大值抑制。通常灰度变化的地方都比较集中,将局部范围内的梯度方向上,灰度变化最大的保留下来,其它的不保留,这样可以剔除掉一大部分的点。将有多个像素宽的边缘变成一个单像素宽的边缘。即“胖边缘”变成“瘦边缘”。
第四步,双阈值筛选。通过非极大值抑制后,仍然有很多的可能边缘点; 进一步的设置一个双阈值,即低阈值(low),高 阈值(high)。
灰度变化大于high的,设置为强边缘像素,
灰度变化低于low的,剔除。
灰度变化在low和high之间的设置为弱边缘。
进一步判断,如果其邻域内有强边缘像素,保留,如果没有,剔除。
这样做的目的是只保留强边缘轮廓的话,有些边缘可能不闭合,需要从满足low和high之间的点进行补充,使得边缘尽可能的闭合。
cv2.Canny()函数用于实现Canny边缘检测,
其基本格式如下:
dst=cv2.Canny(src,threshold1,threshold2[,apertureSize])
参数说明如下:
dst表示边缘检测结果图像。
src为原图像。
threshold1为第1阈值(low)。
threshold2为第2阈值(high)。
apertureSize为计算梯度时使用的Sobel核大小。
注意:在OpenCV中,Canny函数本身应该没有将图像降噪包含在内。因此,实施Canny边缘检测时,需要在Canny函数外面执行图像降噪的过程
img = cv2.imread("Lenna.png")
# PS:转成灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# canny边缘检测
lenna = cv2.GaussianBlur(gray, (5, 5), 0)
canny = cv2.Canny(lenna, threshold1=50, threshold2=150)
cv2.imshow("canny", canny)
cv2.waitKey()
要是存在比较大的噪声的图,效果很差
增加图像平滑操作可以提升效果:lenna = cv2.GaussianBlur(lenna, (9, 9), 0)
3.5 特征提取
1.计算机视觉任务流程概述
图像识别、目标检测、图像分割
数据,预处理,特征分析,分类回归,结果
2.图像特征
1.纹理特征
(1)LBP局部二值模式
将每个像素点与周围点大小比较
半径为R,均匀采样P个点
大小量化为0或1
多个bit组成一个数,统计每个数的直方图
为解决旋转不变性的问题:将LBP周围的二进制码(如11110001) 按位旋转,取二进制码最小的值为最终LBP值
如: 对于11110001情况,我们按位旋转,得到11100011, 11000111, 10001111, 00011111, 00111110, 01111100, 11111000七个不同的二进制数,最小值为00011111
改进的LBP:
将3x3 邻域扩展到任意邻域,并用圆形邻域代替了正方形邻域,这种LBP特征叫做扩展 LBP 算子
LBP特征具有灰度不变性和旋转不变性等显著优点
2.形状特征
(1)HOG特征
方向梯度直方图:在对象识别与模式匹配中是一种常见的特征提取算法
主要思想:在一副图像中,局部目标的表象和形状能够被梯度或边缘的方向密度分布很好地描述(本质:梯度的统计信息,而梯度主要存在于边缘的地方) HOG特征描述子的定义:局部归一化的梯度方向直方图,是一种对图像局部重叠区域的密集型描述符, 它通过计算局部区域的梯度方向直方图来构成特征
第1步: 灰度图像转换。
由于颜色信息作用不大,通常转化为灰度图。
第2步: 标准化gamma空间(Gamma矫正)。
为了提高检测器对光照等干扰因素的鲁棒性,需要对图像进行Gamma矫正,完成对整个图像的归一化,调整对比度,降低噪声影响;
第3步 将图像分割为小的Cell单元格
由于Cell单元格是HOG特征最小的结构单位;
而且其块Block在检测窗口Win的滑动步长就是一个Cell的宽度或高度,
所以,先把整个图像分割为一个个的Cell单元格(例如:8*8像素)。
第4步 计算图像每个像素的XY方向梯度(包括大小和方向)
计算图像横坐标和纵坐标方向的梯度,并据此计算每个像素位置的梯度方向值;求导操作不仅能够捕获轮廓,人影和一些纹理信息,还能进一步弱化光照的影响。
第5步 为每个单元格构建梯度方向直方图
统计局部图像梯度信息并进行量化(或称为编码),得到局部图像区域的特征描述向量。 流程:首先将图像划分成若干个块(Block),每个块又由若干个细胞单元(cell)组成,细胞单元由更小的单位像素(Pixel)组成,然后在每个细胞单元中对内部的所有像素的梯度方向进行统计。
第6步 把单元格组合成大的块(block),块内归一化梯度直方图【重点】
由于局部光照的变化以及前景-背景对比度的变化,使得梯度强度的变化范围非常大。这就需要对梯度强度做归一化。归一化能够进一步地对光照、阴影和边缘进行压缩。 方法: 将4个临近的cell组合成一个block块,求其梯度方向直方图向量(每个block得到4个9维的向量) 采用L2-Norm with Hysteresis threshold方式进行归一化.
第7步 生成HOG特征描述向量
我们将归一化之后的块描述符(向量)就称之为HOG描述符。 将所有“block”的HOG描述符组合在一起,形成最终的feature vector,该feature vector就描述了detect window的图像内容。
(2)SIFT(Scale-invariant Feature transform) 尺度不变特征变换
SIFT特征计算步骤
在高斯差分(DoG)尺度空间中获取极值点
计算高斯差分空间
SIFT-计算高斯差分 (DOG) 空间
对关键点处理位置插值(获得精确的关键点)
SIFT-DOG空间极值点就是"关键点”
去除边缘点
关键点的方向估计
以特征点为中心,在尺度上计算梯度直方图
获取最高值方向为关键点主方向
关键点描述子的生成
区域坐标旋转
为了保证特征矢量具有旋转不变性,需要以特征点为中心,将特征点附近邻域内的图像旋转一个方向角θ,即将原图像x轴转到与主方向相同的方向。
计算采样区域的直方图
在旋转后的坐标上采样16x16的像素窗