OpenCV介绍
OpenCV是一款由Intel公司俄罗斯团队发起并参与和维护的一个计算机视觉处理开源软件库,支持与计算机视觉和机器学习相关的众多算法,并且正在日益扩展
OpenCV的优势
1.编程语言:基于C++实现,同时提供python, Ruby, Matlab等语言的接口
2.跨平台
3.活跃的开发团队
4.丰富的API:完善的传统计算机视觉算法,涵盖主流的机器学习算法,同时添加了对深度学习的支持。
OpenCV-Python
OpenCV-Python是一个Python绑定库,旨在解决计算机视觉问题。
OpenCV-Python使用Numpy,这是一个高度优化的数据库操作库,具有MATLAB风格的语法。所有OpenCV数组结构都转换为Numpy数组。这也使得与使用Numpy的其他库(如SciPy和Matplotlib)集成更容易。
安装opencv-python
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python
opencv基础的图像操作
1.读取图像
cv2.imread(path,[读取方式])
import cv2
img = cv2.imread('1.jpg')
print(img)
2.显示图像
cv2.imshow(arg1,arg2) 参数: arg1:显示图像的窗口名称,以字符串类型表示 arg2:要加载的图像
注意:在调用显示图像的API后,要调用cv2.waitKey(0)给图像绘制留下时间,否则窗口会出现无响应情况,并且图像无法显示出来
import cv2
img = cv2.imread('1.jpg')
print(img)
img = cv2.imread('1.jpg')
cv2.imshow('img1',img)
cv2.waitKey(0)
3.保存图像
cv2.imwrite(arg1,arg2)
参数:
-
arg1:文件名,要保存在哪里
-
arg2:要保存的图像
cv2.imwrite('2.jpg',img)
绘制几何图形
1.绘制直线
cv2.line(img,start,end,color,thickness)
参数:
-
img:要绘制直线的图像
-
Start,end: 直线的起点和终点
-
color: 线条的颜色
-
Thickness: 线条宽度
img=cv2.imread('./1.jpg')
cv2.line(img,(0,0),(100,100),(0,255,0),5)
cv2.imshow('img1',img)
cv2.waitKey(0)
2.绘制圆形
cv.circle(img,centerpoint, r, color, thickness)
参数:
-
img:要绘制圆形的图像
-
Centerpoint, r: 圆心和半径
-
color: 线条的颜色
-
Thickness: 线条宽度,为-1时生成闭合图案并填充颜色
img=cv2.imread('./1.jpg')
cv2.circle(img,(300,300),200,(0,255,0),5)
cv2.imshow('img1',img)
cv2.waitKey(0)
3.绘制矩形
cv.rectangle(img,leftupper,rightdown,color,thickness)
参数:
-
img:要绘制矩形的图像
-
Leftupper, rightdown: 矩形的左上角和右下角坐标
-
color: 线条的颜色
-
Thickness: 线条宽度
img=cv2.imread('./1.jpg')
cv2.rectangle(img,(200,200),(400,400),(0,255,0),5)
cv2.imshow('img1',img)
cv2.waitKey(0)
4.向图像中添加文字
cv.putText(img,text,station, font, Fontscale ,color,thickness,cv2.LINE_AA)
参数:
-
img: 图像
-
text:要写入的文本数据
-
station:文本的放置位置
-
font:字体样式
-
Fontscale :字体大小
-
thickness字体线条宽度
-
cv2.LINE_AA
最后一个参数
cv2.LINE_AA
表示使用反走样(Anti-Aliasing)技术来绘制文本边框。-
反走样是一种提高图形质量的技术,它通过混合颜色和像素边缘以减少锯齿状效果,使文本看起来更加平滑、清晰。
-
在 OpenCV 中,
cv2.LINE_AA
是一种高级线条类型,用于实现文本边界的高质量渲染。相比于其他线型如cv2.LINE_8
(默认值),它能提供更好的视觉效果,特别是在文本较小或者需要高精度显示的情况下
-
img=cv2.imread('./1.jpg')
cv2.putText(img,'miao~',(10,200),cv2.FONT_ITALIC,2,(0,255,0),5,cv2.LINE_AA)
cv2.imshow('img1',img)
cv2.waitKey(0)
5.获取并修改图像中的像素点
img=cv2.imread('./1.jpg')
px=img[300,300]
print(px)
img[300,300]=[0,0,0]
cv2.imshow('img2',img)
cv2.waitKey(0)
6.捕获摄像头的实时视频流
cap = cv2.VideoCapture(path)
path视频流资源路径设置为0代表从默认摄像头捕获视频流
ret, frame = cap.read()
返回值cap 调用read()方法可以得到一个布尔值和一帧图像。布尔值表示是否成功读取到帧,如果为False,可能是因为视频结束或读取失败;如果为True,第二项则是当前帧的图像数据。
cap=cv2.VideoCapture('./1.mp4')
while True:
ret,frame=cap.read()
print(ret,frame.shape)
if ret==False or cv2.waitKey(17)==ord('q'):
break
else:
cv2.imshow('img',frame)
#退出程序则要释放资源
cap.release() #释放视频资源
cv2.destroyWindow('img')
计算机眼中的图像
1.像素
像素是图像的基本单元,每个像素存储着图像的颜色、亮度和其他特征。一系列像素组合到一起就形成了完整的图像,在计算机中,图像以像素的形式存在并采用二进制格式进行存储。根据图像的颜色不同,每个像素可以用不同的二进制数表示。
日常生活中常见的图像是RGB三原色图。RGB图上的每个点都是由红(R)、绿(G)、蓝(B)三个颜色按照一定比例混合而成的,几乎所有颜色都可以通过这三种颜色按照不同比例调配而成。在计算机中,RGB三种颜色被称为RGB三通道,根据这三个通道存储的像素值,来对应不同的颜色。例如,在使用“画图”软件进行自定义调色时,其数值单位就是像素。
2.图像
计算机采用0/1编码的系统,数字图像也是利用0/1来记录信息,我们平常接触的图像都是8位数图像,包含0~255灰度,其中0,代表最黑,1,表示最白。
2.1 二值图像
一幅二值图像的二维矩阵仅由0、1两个值构成,“0”代表黑色,“1”代白色。由于每一像素(矩阵中每一元素)取值仅有0、1两种可能,所以计算机中二值图像的数据类型通常为1个二进制位。二值图像通常用于文字、线条图的扫描识别(OCR)和掩膜图像的存储。
2.2 灰度图
每个像素只有一个采样颜色的图像,这类图像通常显示为从最暗黑色到最亮的白色的灰度,尽管理论上这个采样可以任何颜色的不同深浅,甚至可以是不同亮度上的不同颜色。灰度图像与黑白图像不同,在计算机图像领域中黑白图像只有黑色与白色两种颜色;但是,灰度图像在黑色与白色之间还有许多级的颜色深度。灰度图像经常是在单个电磁波频谱如可见光内测量每个像素的亮度得到的,用于显示的灰度图像通常用每个采样像素8位的非线性尺度来保存,这样可以有256级灰度(如果用16位,则有65536级)。
2.3 彩色图
每个像素通常是由红(R)、绿(G)、蓝(B)三个分量来表示的,分量介于(0,255)。RGB图像与索引图像一样都可以用来表示彩色图像。与索引图像一样,它分别用红(R)、绿(G)、蓝(B)三原色的组合来表示每个像素的颜色。但与索引图像不同的是,RGB图像每一个像素的颜色值(由RGB三原色表示)直接存放在图像矩阵中,由于每一像素的颜色需由R、G、B三个分量来表示,M、N分别表示图像的行列数,三个M x N的二维矩阵分别表示各个像素的R、G、B三个颜色分量。RGB图像的数据类型一般为8位无符号整形,通常用于表示和存放真彩色图像。
#生成一个512*512大小的彩色图片 每一个像素点随机颜色
#设置图像尺寸
height,width=512,512
#创建空白的色彩图,深度为3,代表 B G R
img=np.zeros((height,width,3),dtype=np.uint8)
#每个像素生成随机的BGR值,范围是(0~255)
img[:]=np.random.randint(0,256,img.shape)
#显示图像
cv2.imshow('imag1',img)
cv2.waitKey(0)
cv2.destroyWindow()
灰度实验
灰度图与彩色图最大的不同就是:彩色图是由R、G、B三个通道组成,而灰度图只有一个通道,也称为单通道图像,所以彩色图转成灰度图的过程本质上就是将R、G、B三通道合并成一个通道的过程。
1. 最大值法
对于彩色图像的每个像素,它会从R、G、B三个通道的值中选出最大的一个,并将其作为灰度图像中对应位置的像素值。
image_np = cv2.imread('./1.jpg')
img_shape = image_np.shape
print(img_shape)
image_np_gray = np.zeros((img_shape[0], img_shape[1]), dtype=np.uint8)
# 最大值灰度化
for i in range(img_shape[0]): # 按行读取图片的像素bgr
for j in range(img_shape[1]): # 对每一行按照列进行每一个像素格子进行读取
image_np_gray[i, j] = max(image_np[i, j][0], image_np[i, j][1], image_np[i, j][2]) # 求灰度值
cv2.imshow("image_np_gray", image_np_gray)
cv2.waitKey(0)
2. 平均值法
对于彩色图像的每个像素,它会将R、G、B三个通道的像素值全部加起来,然后再除以三,得到的平均值就是灰度图像中对应位置的像素值。
image_np = cv2.imread('./1.jpg')
img_shape = image_np.shape
print(img_shape)
image_np_gray = np.zeros((img_shape[0], img_shape[1]), dtype=np.uint8)
# 最大值灰度化
for i in range(img_shape[0]): # 按行读取图片的像素bgr
for j in range(img_shape[1]): # 对每一行按照列进行每一个像素格子进行读取
image_np_gray[i, j] = (int(image_np[i, j][0])+int(image_np[i, j][1])+int(image_np[i, j][2]))/3# 求灰度值
cv2.imshow("image_np_gray", image_np_gray)
cv2.waitKey(0)
3. 加权均值法
对于彩色图像的每个像素,它会按照一定的权重去乘以每个通道的像素值,并将其相加,得到最后的值就是灰度图像中对应位置的像素值。
image_np = cv2.imread('./1.jpg')
img_shape = image_np.shape
image_np_gray = np.zeros((img_shape[0], img_shape[1]), dtype=np.uint8) # image_np.copy()
# 加权灰度化
wr = 0.2
wg = 0.3
wb = 0.45
for i in range(img_shape[0]):
for j in range(img_shape[1]):
image_np_gray[i, j] = (int(wr * image_np[i, j][2]) + int(wg * image_np[i, j][1]) + int(
wb * image_np[i, j][0]))
cv2.imshow("image_np_gray", image_np_gray)
cv2.waitKey(0)
#最常用的加权均值方式(cv2内置)
# image=cv2.imread('./1.jpg')
# gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
gray = cv2.imread("1.jpg",cv2.IMREAD_GRAYSCALE)
print(gray)
cv2.imshow("gray",gray)
cv2.waitKey(0)
4. 两个极端的灰度值
灰度值为255(纯白)
灰度值为0(纯黑)
二值化实验
1. 阈值法(THRESH_BINARY)
阈值法就是通过设置一个阈值,将灰度图中的每一个像素值与该阈值进行比较,小于等于阈值的像素就被设置为0(黑),大于阈值的像素就被设置为maxval。
image = cv2.imread('1.jpg', cv2.IMREAD_GRAYSCALE)
# #二值化
#image要二值化的灰度图 150代表阈值 255最大值超过就取它 cv2.THRESH_BINARY二值化的类型(简单的二值化)
_, binary = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)
cv2.imshow('Binary Image', binary)
cv2.waitKey(0)
2. 反阈值法(THRESH_BINARY_INV)
顾名思义,就是与阈值法相反。反阈值法是当灰度图的像素值大于阈值时,该像素值将会变成0(黑),当灰度图的像素值小于等于阈值时,该像素值将会变成maxval。
image = cv2.imread('1.jpg', cv2.IMREAD_GRAYSCALE)
# #二值化
#image要二值化的灰度图 150代表阈值超过就取0 255最大值 cv2.THRESH_BINARY_INV二值化的类型
_, binary = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY_INV)
cv2.imshow('Binary Image', binary)
cv2.waitKey(0)
3. 截断阈值法(THRESH_TRUNC)
截断阈值法,指将灰度图中的所有像素与阈值进行比较,像素值大于阈值的部分将会被修改为阈值,小于等于阈值的部分不变。换句话说,经过截断阈值法处理过的二值化图中的最大像素值就是阈值。
image = cv2.imread('1.jpg', cv2.IMREAD_GRAYSCALE)
# #二值化
#image要二值化的灰度图 150代表阈值超过就取0 255最大值 cv2.THRESH_BINARY_INV二值化的类型
_, binary = cv2.threshold(image, 150, 255, cv2.THRESH_TRUNC)
cv2.imshow('Binary Image', binary)
cv2.waitKey(0)
4. 低阈值零处理(THRESH_TOZERO)
低阈值零处理,字面意思,就是像素值小于等于阈值的部分被置为0(也就是黑色),大于阈值的部分不变。
image = cv2.imread('1.jpg', cv2.THRESH_TOZERO)
# #二值化
#image要二值化的灰度图 150代表阈值超过就取0 255最大值 cv2.THRESH_BINARY_INV二值化的类型
_, binary = cv2.threshold(image, 150, 255, cv2.THRESH_TRUNC)
cv2.imshow('Binary Image', binary)
cv2.waitKey(0)
5. 超阈值零处理(THRESH_TOZERO_INV)
超阈值零处理就是将灰度图中的每个像素与阈值进行比较,像素值大于阈值的部分置为0(也就是黑色),像素值小于等于阈值的部分不变。
image = cv2.imread('1.jpg', cv2.THRESH_TOZERO)
# #二值化
#image要二值化的灰度图 150代表阈值超过就取0 255最大值 cv2.THRESH_BINARY_INV二值化的类型
_, binary = cv2.threshold(image, 180, 255, cv2.THRESH_TOZERO_INV)
cv2.imshow('Binary Image', binary)
cv2.waitKey(0
6. OTSU阈值法
OTSU算法是通过一个值将这张图分前景色和背景色(也就是灰度图中小于这个值的是一类,大于这个值的是一类),通过统计学方法(最大类间方差)来验证该值的合理性,当根据该值进行分割时,使用最大类间方差计算得到的值最大时,该值就是二值化算法中所需要的阈值。通常该值是从灰度图中的最小值加1开始进行迭代计算,直到灰度图中的最大像素值减1,然后把得到的最大类间方差值进行比较,来得到二值化的阈值。
方差越大,说明前景和背景的差别就越大,效果就越好。
# 读取图像,以灰度模式
image = cv2.imread('1.jpg', cv2.IMREAD_GRAYSCALE)
# 使用 Otsu 方法进行二值化
# 注意:第二个参数设置为 0,因为我们将使用 Otsu 方法自动计算阈值
_, binary = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 显示二值化图像
cv2.imshow('Binary Image', binary)
cv2.waitKey(0)
cv2.destroyAllWindows() # 不要忘记销毁所有窗口
自适应二值化
与二值化算法相比,自适应二值化更加适合用在明暗分布不均的图片,因为图片的明暗不均,导致图片上的每一小部分都要使用不同的阈值进行二值化处理,这时候传统的二值化算法就无法满足我们的需求了,于是就出现了自适应二值化。
自适应二值化方法会对图像中的所有像素点计算其各自的阈值,这样能够更好的保留图片里的一些信息。
cv2.adaptiveThreshold(image_np_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 7, 10)
其中各个参数的含义如下:
maxval:最大阈值,一般为255
adaptiveMethod:小区域阈值的计算方式:
ADAPTIVE_THRESH_MEAN_C:小区域内取均值
ADAPTIVE_THRESH_GAUSSIAN_C:小区域内加权求和,权重是个高斯核
thresholdType:二值化方法,只能使用THRESH_BINARY、THRESH_BINARY_INV,也就是阈值法和反阈值法
blockSize:选取的小区域的面积,如7就是7*7的小块。
c:最终阈值等于小区域计算出的阈值再减去此值
1. 取均值
假如我们使用的小区域是3*3的,那么就会从图片的左上角开始(也就是像素值为162的地方)计算其邻域内的平均值,如果处于边缘地区就会对边界进行填充,填充值就是边界的像素点,如下图所示:
那么对于左上角像素值为162的这个点,161(也就是上图中括号内的计算结果,结果会进行取整)就是根据平均值计算出来的阈值,接着减去一个固定值C,得到的结果就是左上角这个点的二值化阈值了,接着根据选取的是阈值法还是反阈值法进行二值化操作。紧接着,向右滑动计算每个点的邻域内的平均值,直到计算出右下角的点的阈值为止。我们所用到的不断滑动的小区域被称之为核,比如3*3的小区域叫做3*3的核,并且核的大小都是奇数个,也就是3*3、5*5、7*7等。
image_np = cv2.imread('./1.jpg')
image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) # 灰度化
# 自适应二值化 小区域取值 7*7
image_np_adaptive = cv2.adaptiveThreshold(image_np_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 7, 10)
cv2.imshow("image_np_gray", image_np_gray)
cv2.imshow("image_np_adaptive", image_np_adaptive)
cv2.waitKey(0)
2. 加权求和
对小区域内的像素进行加权求和得到新的阈值,其权重值来自于高斯分布。
二维高斯函数的表达式:
高斯概率函数是相对于二维坐标产生的,其中(x,y)为点坐标,要得到一个高斯滤波器模板,应先对高斯函数进行离散化,将得到的值作为模板的系数。例如:要产生一个3*3的高斯权重核,以核的中心位置为坐标原点进行取样,其周围的坐标如下图所示(x轴水平向右,y轴竖直向上):
将坐标带入上面的公式中,即可得到一个高斯权重核。
在opencv里,当kernel(小区域)的尺寸为1、3、5、7并且用户没有设置sigma的时候(sigma <= 0),核值就会取固定的系数,这是一种默认的值是高斯函数的近似。
kernel尺寸 | 核值 |
---|---|
1 | [1] |
3 | [0.25, 0.5, 0.25] |
5 | [0.0625, 0.25, 0.375, 0.25, 0.0625] |
7 | [0.03125, 0.109375, 0.21875, 0.28125, 0.21875, 0.109375, 0.03125] |
比如kernel的尺寸为3*3时,使用
进行矩阵的乘法,就会得到如下的权重值
通过这个高斯核,即可对图片中的每个像素去计算其阈值,并将该阈值减去固定值得到最终阈值,然后根据二值化规则进行二值化。
image_np = cv2.imread('./1.jpg')
image_np_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) # 灰度化
#cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 自适应阈值类型。使用的是高斯加权的累计分布函数(CDF),并添加一个常数 C 来计算阈值
image_np_adaptive = cv2.adaptiveThreshold(image_np_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 7, 10) # 自适应二值化
cv2.imshow("image_np_gray", image_np_gray)
cv2.imshow("image_np_adaptive", image_np_adaptive)
cv2.waitKey(0)
摄像头实时采集并渲染 自适应二值化图像:
import cv2
cap = cv2.VideoCapture(0)
while True:
# 读取一帧
ret, frame = cap.read()
print(ret,frame.shape)
# 将图像转换为灰度图
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 应用自适应二值化
# 使用 ADAPTIVE_THRESH_MEAN_C 或 ADAPTIVE_THRESH_GAUSSIAN_C
adaptive_thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 11, 2)
# 显示原始图像和自适应二值化图像
cv2.imshow('Original', frame)
cv2.imshow('Adaptive Threshold', adaptive_thresh)
# 按 'q' 键退出循环
if ret==False or cv2.waitKey(17)==ord('q'):
break
# 释放摄像头和关闭所有窗口
cap.release()
cv2.destroyAllWindows()