python 之 opencv 阶段总结篇

前言

  • 没想到吧,这就总结了。没错我学习的三分钟热度真的太强了,这几天作业都没好好做,全在看这一套opencv的教程。那么就在大概第四天的时候把这一套教程给全部实验了一遍。
  • 不管怎么说,这一套教程呢,我作为小白学者觉得非常不错,讲的不深也有一些小挑战(略微简单,不过需要细品)。其实可以通过这些知识和技巧来做自己的延申,我还没有多尝试,唯一一次尝试使用对比字母轮廓形状的方法找到正确的字母,用他给的‘A’成功了,自己在网上找了个‘C’,预处理还做了闭运算轮廓只有一条,结果算法结果是像A我真的吐了。
  • 那么鉴于我一下子就学完了,而且每一章都像之前那样复制性的总结其实没多大意义,教程我电脑里也有,想复习了再去看看就可,所以这次来一个总结,目的就是略微的提到写我觉得的重点。

知识点回忆


1. 摄像头 or 视频
2. matplotlib的使用
3. 图像基本操作
4. 颜色空间转换
5. 阈值分割
6. 图形几何变换
7. 绘图功能
8. 图像混合


1. 摄像头 or 视频

  • 我看了下,之前讲完了前三个部分,调用电脑摄像头,可以用来截取一帧图像、可以将摄像头的连续若干帧图像保存成视频的形式
  • 可以打开一个视频,如果你利用cv2来创建一个滑动条cv2.createTrackbar()(滑动就会对应有回调函数)还能实现拖动视频播放进度,借助cv2.setTrackbarPos()cv2.getTrackbarPos()其实看函数名也知道是干什么的,这里不多讲了
  • cv2.VideoCapture()可以打开摄像头或者视频文件cap.get(propId)cap.set(propId,value)得到和修改摄像头的很多属性
  • cv2.imwrite()保存图片或者视频,保存视频的时候要注意提前设置好视频编码方式,我看有挺多的可能根据需要到时候就会了解吧、当然读取图片还是得cv2.imread()

  • 捕获视频中的一帧ret, frame = capture.read()


2. matplotlib的使用

  • 其实这整套教程也没咋用到,他和matlab里的plot用法有点像,都是subplot。不过要注意,他来显示图像时,plt.imshow()中后一个参数cmap的参数选择,灰度图就是cmap = 'gray',而且imshow之后必须要加plt.show(),要不然图片就不显示,可以看一下老师怎么写的
1
2
3
4
5
6
7
8
9
10
11
titles = ['Original', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, th1, th2, th3, th4, th5]

# 使用Matplotlib显示
for i in range(6):
plt.subplot(2, 3, i + 1)
plt.imshow(images[i], 'gray')
plt.title(titles[i], fontsize=8)
plt.xticks([]), plt.yticks([]) # 隐藏坐标轴

plt.show()
  • matplotlib中图像显示RGB顺序,opencv是BGR,显示时需要通道倒置一下

3. 图像基本操作

  • 读取像素点的值直接img[100, 90],修改也是这样。
  • img.shape形状中包括行数、列数和通道数
  • 截取ROI,非常简单一看就懂,face = img[100:200, 115:188],这也只能是个矩形,不过得到轮廓或者利用掩码方式得到一块想要的区域也行
  • 彩色图的BGR三个通道是可以分开单独访问的,b, g, r = cv2.split(img);也可以合并组成图像img = cv2.merge((b, g, r))

4. 颜色空间转换

  • img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)图像形式转换,教程中用他来转换成HSV颜色模型,改成cv2.COLOR_BGR2HSV就行了
  • mask = cv2.inRange(hsv, lower_blue, upper_blue),其实inRange这个函数我感觉不太重要,因为后面有阈值分割,但可能这个是针对真彩图像,与阈值分割多是用于灰度图
  • 然后很重要的一个res = cv2.bitwise_and(frame, frame, mask=mask),这个位操作是图像只存在0和255也就是二值图像的关键操作,可以在以后的二值图像处理里起到作用,特别是应用在掩码图的得到

5. 阈值分割

  • 这一段是之后的基础,全都讲起来有点麻烦了,我就不多说。额,先说一句,这个应该是用在处理灰度图上的
  • 固定阈值ret, th = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY),第二个参数是设定的阈值,第三个是最大值,最后一个阈值的方式,种类主要5种
  • 自适应阈值th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 4),不同之处就在第三个参数,小区域阈值的计算方式;最后一个参数:最终阈值等于小区域计算出的阈值再减去此值(光看看不懂,想试试就自己试试)
  • Otsu阈值:看代码就知道怎么用了,原理自便

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 固定阈值法
    ret1, th1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY)

    # Otsu阈值法
    ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    # 先进行高斯滤波,再使用Otsu阈值法
    blur = cv2.GaussianBlur(img, (5, 5), 0)
    ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
  • 光线不均匀的图片就用自适应阈值就完事了


6. 图形几何变换

  • 其实我觉得这一章是很重要的,因为图像处理应该会有很多时候待处理的目标是需要几何变换特别是立体上的仿射变换
  • cv2.resize()可以自己定义大小,也可以按比例缩放,用处会很多,我不知道和下面这个有什么关系,用处大不大,或者说在哪里使用
1
2
lower = cv2.pyrDown(img)  # 向下采样一级
higher = cv2.pyrUp(img) # 向上采样一级
  • cv2.flip(img, 1)第二个参数决定是镜像、垂直、镜像垂直翻转
  • 平移图片,需要用到矩阵,用仿射变换函数cv2.warpAffine()实现;旋转也是,而且用这个函数前,还要加个得到旋转对应的矩阵的函数cv2.getRotationMatrix2D(),你看这还只是2D的

$$
M = \left[
\begin{matrix}
1 & 0 & t_x \newline
0 & 1 & t_y
\end{matrix}
\right]
$$

仿射变换

  • 二维的,懂了吗,基本的图像变换就是二维坐标的变换:从一种二维坐标(x,y)到另一种二维坐标(u,v)的线性变换,你得先得到一个矩阵,那么就可以转换了,矩阵T(2×3)就称为仿射变换的变换矩阵

$$
\begin{matrix}
u=a_1x+b_1y+c_1 \newline
v=a_2x+b_2y+c_2
\end{matrix}
$$


$$
\left[
\begin{matrix}
u \newline
v
\end{matrix}
\right] = \left[
\begin{matrix}
a_1 & b_1 \newline
a_2 & b_2
\end{matrix}
\right] \left[
\begin{matrix}
x \newline
y
\end{matrix}
\right]+\left[
\begin{matrix}
c_1 \newline
c_2
\end{matrix}
\right]
$$


$$
R=\left[
\begin{matrix}
a_1 & b_1 \newline
a_2 & b_2
\end{matrix}
\right], t=\left[
\begin{matrix}
c_1 \newline
c_2
\end{matrix}
\right],T=\left[
\begin{matrix}
R & t
\end{matrix}
\right]
$$


  • 然后几行代码看懂,现根据三个点和对应的三个点得到变换矩阵,然后直接转整个图像
1
2
3
4
5
6
7
8
# 变换前的三个点
pts1 = np.float32([[50, 65], [150, 65], [210, 210]])
# 变换后的三个点
pts2 = np.float32([[50, 100], [150, 65], [100, 250]])

# 生成变换矩阵
M = cv2.getAffineTransform(pts1, pts2)
dst = cv2.warpAffine(img, M, (cols, rows))

透视变换

  • 三维的,就是立体上如何把一个目标看得顺眼一点,一个身份证你斜着拍的,那我想让他能像平着拍的一样舒服,那就用这个方法

$$
\begin{matrix}
X=a_1 x + b_1 y + c_1 \newline
Y=a_2 x + b_2 y + c_2 \newline
Z=a_3 x + b_3 y + c_3
\end{matrix}
$$


$$
\left[
\begin{matrix}
X \newline
Y \newline
Z
\end{matrix}
\right] = \left[
\begin{matrix}
a_1 & b_1 & c_1 \newline
a_2 & b_2 & c_2 \newline
a_3 & b_3 & c_3
\end{matrix}
\right] \left[
\begin{matrix}
x \newline
y \newline
1
\end{matrix}
\right]
$$

  • OpenCV中首先根据变换前后的四个点用cv2.getPerspectiveTransform()生成3×3的变换矩阵,然后再用cv2.warpPerspective()进行透视变换
1
2
3
4
5
6
7
8
9
# 原图中卡片的四个角点
pts1 = np.float32([[148, 80], [437, 114], [94, 247], [423, 288]])
# 变换后分别在左上、右上、左下、右下四个点
pts2 = np.float32([[0, 0], [320, 0], [0, 178], [320, 178]])

# 生成透视变换矩阵
M = cv2.getPerspectiveTransform(pts1, pts2)
# 进行透视变换,参数3是目标图像大小
dst = cv2.warpPerspective(img, M, (320, 178))
变换 矩阵 自由度 保持性质
平移 [I, t](2×3) 2 方向/长度/夹角/平行性/直线性
刚体 [R, t](2×3) 3 长度/夹角/平行性/直线性
相似 [sR, t](2×3) 4 夹角/平行性/直线性
仿射 [T](2×3) 6 平行性/直线性
透视 [T](3×3) 8 直线性

7. 绘图功能

  • 感觉画图功能不太用得到,不过也是用起来很方便,绘制形状的函数有一些共同的参数

    • color:绘制的颜色,是彩色图就估计是BGR,就输一个三维元组ok;灰度图就一个255以内的数
    • thickness:线宽,默认为1;对于矩形/圆之类的封闭形状而言,传入-1表示填充形状
    • 建议画什么线型都加一个lineType=cv2.LINE_AA比较好看
  • img = np.zeros((512, 512, 3), np.uint8)可以直接用这样一句话来创建一个纯色图

  • cv2.line(img, (0, 0), (512, 512), (255, 0, 0), 5)起点终点
  • cv2.rectangle(img, (384, 0), (510, 128), (0, 255, 0), 3)左上右下
  • cv2.circle(img, (447, 63), 63, (0, 0, 255), -1)圆心半径
  • cv2.ellipse(img, (256, 256), (100, 50), 0, 0, 180, (255, 0, 0), -1)椭圆中心x/y轴长度旋转角度起始角度结束角度,具体自己试试
1
2
3
4
5
# 使用cv2.polylines()画多条直线
line1 = np.array([[100, 20], [300, 20]], np.int32).reshape((-1, 1, 2))
line2 = np.array([[100, 60], [300, 60]], np.int32).reshape((-1, 1, 2))
line3 = np.array([[100, 100], [300, 100]], np.int32).reshape((-1, 1, 2))
cv2.polylines(img, [line1, line2, line3], True, (0, 255, 255))
  • 添加文字cv2.putText(img, 'ex2tron', (10, 500), font,4, (255, 255, 255), 2, lineType=cv2.LINE_AA)

8. 图像混合

👴乏了,找时间再写吧

python 之 opencv-摄像头采集和视频播放

打开摄像头


要使用摄像头,需要使用cv2.VideoCapture(0)创建VideoCapture对象,参数0指的是摄像头的编号,如果你电脑上有两个摄像头的话,访问第2个摄像头就可以传入1,依此类推。

capture.read

函数返回的第1个参数ret(return value缩写)是一个布尔值,表示当前这一帧是否获取正确

cv2.cvtColor

用来转换颜色,这里将彩色图转成灰度图

cap.get(propId)cap.set(propId,value)

通过cap.get(propId)可以获取摄像头的一些属性,比如捕获的分辨率,亮度和对比度等。propId是从0~18的数字,代表不同的属性,完整的属性列表可以参考:VideoCaptureProperties。也可以使用cap.set(propId,value)来修改属性值

经验之谈:某些摄像头设定分辨率等参数时会无效,因为它有固定的分辨率大小支持,一般可在摄像头的资料页中找到。

播放本地视频


跟打开摄像头一样,如果把摄像头的编号换成视频的路径就可以播放本地视频了。回想一下cv2.waitKey(),它的参数表示暂停时间,所以这个值越大,视频播放速度越慢,反之,播放速度越快,通常设置为25或30。

1
2
3
4
5
6
7
8
9
10
# 播放本地视频
capture = cv2.VideoCapture('demo_video.mp4')

while(capture.isOpened()):
ret, frame = capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

cv2.imshow('frame', gray)
if cv2.waitKey(30) == ord('q'):
break

录制视频


之前我们保存图片用的是cv2.imwrite(),要保存视频,我们需要创建一个VideoWriter的对象,需要给它传入四个参数:

  • 输出的文件名,如’output.avi’
  • 编码方式FourCC
  • 帧率FPS
  • 要保存的分辨率大小

FourCC是用来指定视频编码方式的四字节码,所有的编码可参考Video Codecs。如MJPG编码可以这样写: cv2.VideoWriter_fourcc(*'MJPG')cv2.VideoWriter_fourcc('M','J','P','G')

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
capture = cv2.VideoCapture(0)

# 定义编码方式并创建VideoWriter对象
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
outfile = cv2.VideoWriter('output.avi', fourcc, 25., (640, 480))

while(capture.isOpened()):
ret, frame = capture.read()

if ret:
outfile.write(frame) # 写入文件
cv2.imshow('frame', frame)
if cv2.waitKey(1) == ord('q'):
break
else:
break

OpenCV VideoCapture.get()参数中文详解,不太准确供参考

练习


  • 实现一个可以拖动滑块播放视频的功能。(提示:需要用到 cv2.CAP_PROP_FRAME_COUNTcv2.CAP_PROP_POS_FRAMES两个属性)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import cv2


def changetime(x):
return capture.set(cv2.CAP_PROP_POS_FRAMES, x)

# 播放本地视频
capture = cv2.VideoCapture('demo_video.mp4')
#获取视频帧长度
frame_count = capture.get(cv2.CAP_PROP_FRAME_COUNT)
#创建一个窗口
cv2.namedWindow('video')
# 创建视频滑动条
cv2.createTrackbar('video', 'video', 0, int(frame_count), changetime)

height = capture.get(cv2.CAP_PROP_FRAME_HEIGHT)
width = capture.get(cv2.CAP_PROP_FRAME_WIDTH)
print(capture.set(cv2.CAP_PROP_FRAME_HEIGHT, height * 2))
print(capture.set(cv2.CAP_PROP_FRAME_WIDTH, width * 2))

while(capture.isOpened()):
ret, frame = capture.read()

if ret == True:
cv2.setTrackbarPos('video','video',int(capture.get(cv2.CAP_PROP_POS_FRAMES)))
cv2.imshow('video', frame)
if cv2.waitKey(30) == ord('q'):
break
else:
break

python 之 opencv-matplotlib显示图片

Matplotlib


Matplotlib是Python的一个很常用的绘图库,有兴趣的可以去官网学习更多内容。

显示灰度图

1
2
3
4
5
6
7
8
import cv2
import matplotlib.pyplot as plt

img = cv2.imread('lena.jpg', 0)

# 灰度图显示,cmap(color map)设置为gray
plt.imshow(img, cmap='gray')
plt.show()

结果如下:

plt.show()和plt.imshow()的区别

plt.imshowcmap颜色参数选择


显示彩色图

OpenCV中的图像是以BGR的通道顺序存储的,但Matplotlib是以RGB模式显示的,所以直接在Matplotlib中显示OpenCV图像会出现问题,因此需要转换一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import cv2
import matplotlib.pyplot as plt

img = cv2.imread('lena.jpg')
img2 = img[:, :, ::-1]
# 或使用
# img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 显示不正确的图
plt.subplot(121),plt.imshow(img)

# 显示正确的图
plt.subplot(122)
plt.xticks([]), plt.yticks([]) # 隐藏x和y轴
plt.imshow(img2)

plt.show()

img[:,:,0]表示图片的蓝色通道,img[:,:,::-1]就表示BGR翻转,变成RGB,说明一下:

熟悉Python的童鞋应该知道,对一个字符串s翻转可以这样写:s[::-1],’abc’变成’cba’,-1表示逆序。图片是二维的,所以完整地复制一副图像就是:

1
img2 = img[:,:] # 写全就是:img2 = img[0:height,0:width]

而图片是有三个通道,相当于一个长度为3的字符串,所以通道翻转与图片复制组合起来便是img[:,:,::-1]

结果如下:

加载和保存图片

不使用OpenCV,Matplotlib也可以加载和保存图片:

1
2
3
4
5
6
7
8
import matplotlib.image as plt

img = plt.imread('lena.jpg')
plt.imshow(img)

# 保存图片,需放在show()函数之前
plt.savefig('lena2.jpg')
plt.show()
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×