Opencv(Open Source Computer Vision Library)是一个基于开源发行的跨平台计算机视觉库,它实现了图像处理和计算机视觉方面的很多通用算法,已成为计算机视觉领域最有力的研究工具。在这里我们要区分两个概念:图像处理和计算机视觉的区别:图像处理侧重于“处理”图像–如增强,还原,去噪,分割等等;而计算机视觉重点在于使用计算机来模拟人的视觉,因此模拟才是计算机视觉领域的最终目标。
OpenCV用C++语言编写,它具有C ++,Python,Java和MATLAB接口,并支持Windows,Linux,Android和Mac OS, 如今也提供对于C#、Ch、Ruby,GO的支持
1.1 OpenCV发展历史
OpenCV于1999年由Intel建立,如今由Willow Garage公司提供支持。
OpenCV 0.X
1999年1月,CVL项目启动。主要目标是人机界面,能被UI调用的实时计算机视觉库,为Intel处理器做了特定优化。
2000年6月,第一个开源版本OpenCV alpha 3发布。
2000年12月,针对linux平台的OpenCV beta 1发布。
OpenCV 1.X
OpenCV 最初基于C语言开发,API也都是基于C的,面临内存管理、指针等C语言固有的麻烦。
2006年10月, 正式发布OpenCV 1.0版本,同时支持mac os系统和一些基础的机器学习方法,如神经网络、随机森林等,来完善对图像处理的支持。
2009年9月,OpenCV 1.2(beta2.0)发布。
OpenCV 2.X
当C++流行起来,OpenCV 2.x发布,其尽量使用C++而不是C,但是为了向前兼容,仍保留了对C API的支持。
2009年9月2.0 beta发布,主要使用CMake构建。
2010年3月:2.1发布
2010年12月6日,OpenCV 2.2发布
2011年8月,OpenCV 2.3发布。
2012年4月2日,发布OpenCV 2.4.
OpenCV 3.X
随着3.x的发布,1.x的C API将被淘汰不再被支持,以后C API可能通过C++源代码自动生成。3.x与2.x不完全兼容,与2.x相比,主要的不同之处在于OpenCV 3.x 的大部分方法都使用了OpenCL加速。
2014年8月, 3.0 alpha发布,除大部分方法都使用OpenCL加速外,3.x默认包含以及使用IPP(一套跨平台的软件函数库)
2017年8月,发布3.3版本,OpenCV开始支持C++ 11构建,同时加强对神经网络的支持。
OpenCV 4.X
2018年10月4.0.0发布,OpenCV开始需要支持C++11的编译器才能编译,同时对几百个基础函数使用"wide universal intrinsics"重写,极大改善了opencv处理图像的性能。
1.2 OpenCV应用领域
1、人机互动
2、物体识别
3、 图像分割
图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。
4、人脸识别
5、动作识别
6、 运动跟踪
7、机器人
8、运动分析
9、机器视觉
10、结构分析
11、汽车安全驾驶
一般的图像(模拟图像)不能直接用计算机来处理,必须先将图像转化为数字图像。把模拟图像分割成一个个像素,每个像素的亮度或灰度值用一个整数表示——图像的数字化
2.1 灰度图像数字化
所谓的数字化,其实就是化成同行同列的二维数组,而每个坐标存的就是相关的灰度值(0-255)(为什么是0-255?一个字节存放8bit,而图的储存一般都是以uint8类型存放,同时计算机时按照二进制存放数值,也就是2的8次方,也就是256)
实例:
补充:色彩深度与灰阶
色彩深度 | 灰阶 |
---|---|
色彩深度(Depth of Color),色彩深度又叫色彩位数。视频画面中红、绿、蓝三个颜色通道中每种颜色为N位,总的色彩位数则为3N,色彩深度也就是视频设备所能辨析的色彩范围。目前有18bit、24bit、30bit、36bit、42bit和48bit位等多种。24位色被称为真彩色,R、G、B各8bit,常说的8bit,色彩总数为1670万,如手机参数,多少万色素就这个概念。 | 通常来说,液晶屏幕上人们肉眼所见的一个点,即一个像素,它是由红、绿、蓝(RGB)三原色组成的。每一个基色,其背后的光源都可以显现出不同的亮度级别。而灰阶代表了由最暗到最亮之间不同亮度的层次级别。把三基色每一个颜色从纯色(如纯红)不断变暗到黑的过程中的变化级别划分成为色彩的灰阶,并用数字表示,就是最常见的色彩存储原理。这中间层级越多,所能够呈现的画面效果也就越细腻。以8bit 为例,我们就称之为256灰阶。 |
2.2 彩色像数字化
彩色像数字化原理同灰度图像数字化,只不过彩色图像为三通道图像且可以拆分成三张同等像素的灰度图,由下图可知,每三个BGR就组成了一张图片的一列。
数字图像处理的实质就是通过对数字图像中像素数据的判断,依据处理或识别要求,最后逐个像素修改像素的灰度值。
3.1 灰度化处理
#读入原始图像
img=cv2.imread('test.jpg')
#灰度化处理
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
图片的灰度化:将一个像素点的三个颜色变量相等,R=G=B,此时该值称为灰度值,图片灰度化可以有效减少数字图片计算量
3.2 二值化处理
#二值化处理
ret,im_fixed=cv2.threshold(gray,50,255,cv2.THRESH_BINARY)
CV_THRESH_BINARY, 表示如果当前像素点的灰度值大于阈值则将输出图像的对应位置像素值置为255,否则为0。在opencv常用的阈值处理函数除上述演示外还有四种,分别是THRESH_BINARY_INV、THRESH_TRUNC、THRESH_TOZERO、THRESH_TOZERO_INV。
3.3 滤波
滤波是根据原有图像的某个像素的周围像素来确定新的像素值,滤波器主要的作用是用来消去噪声的,消除图像中的不合理的像素点。
3.3.1 均值滤波(通过求与单位矩阵做内积和的平均值做图像处理)
blur = cv2.blur(img,(5,5))
3.3.2 高斯滤波 (根据正态分布处理图像,越靠近中心点,值越接近)
blur = cv2.GaussianBlur(img,(5,5), 0)
3.3.3 中值滤波 (根据正态分布处理图像,越靠近中心点,值越接近)
blur = cv2.cv2.medianBlur(img,5)
3.4 边缘检测
边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。边缘检测是特征提取中的一个研究领域。
图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于查找一类和基于零穿越的一类。基于查找的方法通过寻找图像一阶导数中的最大值和最小值来检测边界,通常是将边界定位在梯度最大的方向。基于零穿越的方法通过寻找图像二阶导数零穿越来寻找边界,通常是Laplacian过零点或者非线性差分表示的过零点。
滤波作为边缘检测的预处理通常是必要的,通常采用高斯滤波。
3.4.1 Sobel边缘检测算子
Sobel边缘检测算法比较简单,实际应用中效率比canny边缘检测效率要高,但是边缘不如Canny检测的准确,但是很多实际应用的场合,sobel边缘却是首选,Sobel算子是高斯平滑与微分操作的结合体,所以其抗噪声能力很强,用途较多。尤其是效率要求较高,而对细纹理不太关系的时候。
算子模板为:
Sobel算子是一种带有方向的过滤器,openCV中Sobel算子的函数为cv2.Sobel ()。
Sobel_x_or_y = cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)
dst及dst之后的参数都是可选参数。
第一个参数是传入的图像,第二个参数是图像的深度,dx和dy指的是求导的阶数,0表示这个方向上没有求导,所填的数一般为0、1、2。ksize是Sobel算子的大小,即卷积核的大小,必须为奇数1、3、5、7。如果ksize=-1,就演变成为3x3的Scharr算子,scale是缩放导数的比例常数,默认情况为没有伸缩系数。borderType是判断图像边界的模式,这个参数默认值为cv2.BORDER_DEFAULT。
# Sobel边缘检测算子
img = cv2.imread('img.jpg', 0)
x = cv2.Sobel(img, cv2.CV_16S, 1, 0)
y = cv2.Sobel(img, cv2.CV_16S, 0, 1)
# cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])
# 可选参数alpha是伸缩系数,beta是加到结果上的一个值,结果返回uint类型的图像
Scale_absX = cv2.convertScaleAbs(x) # convert 转换 scale 缩放
Scale_absY = cv2.convertScaleAbs(y)
result = cv2.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0)
cv2.imshow('img', img)
cv2.imshow('Scale_absX', Scale_absX)
cv2.imshow('Scale_absY', Scale_absY)
cv2.imshow('result', result)
cv2.waitKey(0)
Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此要使用16位有符号的数据类型,即cv2.CV_16S。处理完图像后,再使用cv2.convertScaleAbs()函数将其转回原来的uint8格式,否则图像无法显示。
Sobel算子是在两个方向计算的,最后还需要用cv2.addWeighted( )函数将其组合起来。
其中alpha是第一幅图片中元素的权重,beta是第二个图像的权重,gamma是加到最后结果上的一个值。
3.4.2 Scharr算子
当Sobel()函数的参数ksize=-1时,就演变成了3x3的Scharr算子。算子的模板为:
# Scharr算子
img = cv2.imread('img.jpg', 0)
x = cv2.Sobel(img, cv2.CV_16S, 1, 0, ksize=-1)
y = cv2.Sobel(img, cv2.CV_16S, 0, 1, ksize=-1)
# ksize=-1 Scharr算子
# cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])
# 可选参数alpha是伸缩系数,beta是加到结果上的一个值,结果返回uint类型的图像
Scharr_absX = cv2.convertScaleAbs(x) # convert 转换 scale 缩放
Scharr_absY = cv2.convertScaleAbs(y)
result = cv2.addWeighted(Scharr_absX, 0.5, Scharr_absY, 0.5, 0)
cv2.imshow('img', img)
cv2.imshow('Scharr_absX', Scharr_absX)
cv2.imshow('Scharr_absY', Scharr_absY)
cv2.imshow('result', result)
cv2.waitKey(0)
3.4.3 拉普拉斯(Laplacian)算子
Laplacian函数实现的方法是先用Sobel算子计算二阶x和y导数,再求和。
laplacian = cv2.Laplacian(src, depth, dst, ksize, scale, delta, borderType)
前两个参数是必选参数,其后是可选参数。第一个参数是需要处理的图像,第二个参数是图像的深度,-1表示采用的是原图像相同的深度,目标图像的深度必须大于等于原图像的深度;ksize参数是算子的大小,即卷积核的大小,必须为1,3,5,7。默认为1。scale是缩放导数的比例chan常数,默认情况下没有伸缩系数;borderType是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
# 拉普拉斯算子
img = cv2.imread('img.jpg', 0)
laplacian = cv2.Laplacian(img, cv2.CV_16S, ksize=3)
dst = cv2.convertScaleAbs(laplacian)
cv2.imshow('laplacian', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.4.4 Canny算子
根据对信噪比与定位乘积进行测度,得到最优化逼近算子。这就是Canny边缘检测算子。
算法的基本步骤为:
1.用高斯滤波器平滑图像;
2.用一阶偏导的有限差分来计算梯度的幅值和方向;
3.对梯度幅值进行非极大抑制;
4.用双阈值算法检测和连接边缘。
canny = cv2.Canny(image, threshold1, threshold2, edges, apertureSize, L2gradient)
第一个参数是需要处理的原图像单通道的灰度图,第二个参数是阈值1,第二个参数是阈值2,较大的阈值2用于检测图像中明显的边缘,但一般情况下检测的效果不会那么完美,边缘检测出来是断断续续的。所以这时候应用较小的第一个阈值来将这些间断的边缘连接起来。可选参数中aperaperturesize参数就是卷积核的大,而L2gradient参数就是一个布尔值,如果为true,则就使用更精确的L2范数进行计算(即两个方向的倒数的平方和再开方),否则使用L1范数(直接将两个方向导数的绝对值相加)。
# canny算子
img = cv2.imread('img.jpg', 0)
blur = cv2.GaussianBlur(img, (3, 3), 0) # 用高斯滤波处理原图像降噪
canny = cv2.Canny(blur, 50, 150) # 50是最小阈值,150是最大阈值
cv2.imshow('canny', canny)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.5 腐蚀和膨胀
3.5.1 结构元素
形态学处理的核心就是定义结构元素,在OpenCV中,可以使用其自带的getStructuringElement函数,也可以直接使用NumPy的ndarray来定义一个结构元素。
椭圆:cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
矩形:cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))
3.5.2 腐蚀和膨胀
腐蚀(取局部最小值):腐蚀是原图中的高亮区域被蚕食,效果图拥有比原图更小的高亮区域。
膨胀(取局部最大值):膨胀就是对图像高亮部分进行“领域扩张”,效果图拥有比原图更大的高亮区域;
方法:
# 腐蚀
cv2.erode(src, # 输入图像
kernel, # 卷积核
dst=None, # 输出图
anchor=None, # 结构元素的锚点位置,默认锚点位于结构元素中心
iterations=None, # 迭代次数,默认1
borderType=None, # 推断边缘类型
borderValue=None) # 边缘值
# 膨胀
cv2.dilate(src, # 输入图像
kernel, # 卷积核
dst=None, # 输出图
anchor=None, # 结构元素的锚点位置,默认锚点位于结构元素中心
iterations=None, # 迭代次数,默认1
borderType=None, # 推断边缘类型
borderValue=None) # 边缘值
运行代码:
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
eroded = cv2.erode(img, kernel) # 腐蚀图像
cv2.imshow('eroded.png', eroded)
dilated = cv2.dilate(img, kernel) # 膨胀图像
cv2.imshow('canny.png', dilated)
3.5.3 开运算与闭运算
开运算:先腐蚀后膨胀,用于移除由图像噪音形成的斑点。
闭运算:先膨胀后腐蚀,用来连接被误分为许多小块的对象;
原始图片
图像缩放
适当缩放图片可以有效减少计算量
img = cv2.resize(img, None, fx=0.3, fy=0.3)
rows, cols, channels = img.shape
print(rows, cols, channels)
转换为hsv灰度图更利于背景的识别
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imshow('hsv', hsv)
图片二值化处理
图片的二值化处理,可能会出现噪声(白点),有的图片显示的很明显,这就需要我们进行腐蚀或膨胀。
lower_blue = np.array([90, 70, 70])
upper_blue = np.array([110, 255, 255])
mask = cv2.inRange(hsv, lower_blue, upper_blue) # 蓝色范围内变白,其余之外全部变黑
腐蚀膨胀
去除图片中的噪点
erode = cv2.erode(mask, None, iterations=1)
dilate = cv2.dilate(mask, None, iterations=1)
cv2.imshow('dilate', dilate)
背景变换
遍历每个像素点,进行颜色的替换
for i in range(rows):
for j in range(cols):
if dilate[i, j] == 255: # 像素点为255表示的是白色,此处将白色处的像素点替换为红色
img[i, j] = (0, 0, 255)
————————————————
版权声明:本文为CSDN博主「小小晓晓阳」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bugang4663/article/details/111413850