二.使用 OpenCV 读写视频
Posted 哇风
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二.使用 OpenCV 读写视频相关的知识,希望对你有一定的参考价值。
在 OpenCV 中读取和写入视频与读取和写入图像非常相似。视频只不过是一系列通常称为帧的图像。因此,您需要做的就是遍历视频序列中的所有帧,然后一次处理一帧。在这篇文章中,我们将演示如何从文件、图像序列和网络摄像头读取、显示和写入视频。我们还将调查过程中可能出现的一些错误,并帮助了解如何解决它们。
让我们先看一下读取视频文件的代码示例。它本质上包含从磁盘读取视频并显示它的功能。随着您的深入,我们将详细讨论此实现中使用的函数。
Python
import cv2
# Create a video capture object, in this case we are reading the video from a file
vid_capture = cv2.VideoCapture('person_test.mp4')
if (vid_capture.isOpened() == False):
print("Error opening the video file")
# Read fps and frame count
else:
# Get frame rate information
# You can replace 5 with CAP_PROP_FPS as well, they are enumerations
fps = vid_capture.get(5)
print('Frames per second : ', fps,'FPS')
# Get frame count
# You can replace 7 with CAP_PROP_FRAME_COUNT as well, they are enumerations
frame_count = vid_capture.get(7)
print('Frame count : ', frame_count)
while(vid_capture.isOpened()):
# vid_capture.read() methods returns a tuple, first element is a bool
# and the second is frame
ret, frame = vid_capture.read()
if ret == True:
cv2.imshow('Frame',frame)
# 20 is in milliseconds, try to increase the value, say 50 and observe
key = cv2.waitKey(20)
if key == ord('q'):
break
else:
break
# Release the video capture object
vid_capture.release()
cv2.destroyAllWindows()
这些是我们将在这篇博文中讨论的 OpenCV 视频 I/O 中的主要功能:
- cv2.VideoCapture– 创建一个视频捕获对象,这将有助于流式传输或显示视频。
- cv2.VideoWriter– 将输出视频保存到目录。
- 此外,我们还讨论了其他需要的函数cv2.imshow(),cv2.waitKey()以及get()用于读取帧高度、宽度、fps 等视频元数据的方法。
从文件中读取视频
下面的下一个代码块使用VideoCapture()
该类创建一个VideoCapture对象,然后我们将使用它来读取视频文件。使用此类的语法如下所示:
VideoCapture(path, apiPreference)
第一个参数是视频文件的文件名/路径。第二个是可选参数,表示API
偏好。与此可选参数相关的一些选项将在下面进一步讨论。要了解更多信息apiPreference
,请访问官方文档链接VideoCaptureAPIs。
# Create a video capture object, in this case we are reading the video from a file
vid_capture = cv2.VideoCapture('person_test.mp4')
现在我们有了一个视频捕获对象,我们可以使用该isOpened()
方法来确认视频文件是否已成功打开。该isOpened()
方法返回一个布尔值,指示视频流是否有效。否则,您将收到一条错误消息。错误消息可能暗示很多事情。其中之一是整个视频已损坏,或某些帧已损坏。假设视频文件已成功打开,我们可以使用该get()
方法检索与视频流相关的重要元数据。请注意,此方法不适用于网络摄像头。该方法从此处get()
记录的选项枚举列表中获取单个参数。在下面的示例中,我们提供了数值 5 和 7,它们对应于帧速率 (CAP_PROP_FPS
) 和帧数 ( CAP_PROP_FRAME_COUNT
)。可以提供数值或名称。
Python
if (vid_capture.isOpened() == False):
print("Error opening the video file")
# Read fps and frame count
else:
# Get frame rate information
# You can replace 5 with CAP_PROP_FPS as well, they are enumerations
fps = vid_capture.get(5)
print('Frames per second : ', fps,'FPS')
# Get frame count
# You can replace 7 with CAP_PROP_FRAME_COUNT as well, they are enumerations
frame_count = vid_capture.get(7)
print('Frame count : ', frame_count)
在检索到与视频文件关联的所需元数据后,我们现在准备从文件中读取每个图像帧。这是通过创建一个循环并使用该vid_capture.read()方法从视频流中一次读取一帧来完成的。
该vid_capture.read()方法返回一个元组,其中第一个元素是布尔值,下一个元素是实际的视频帧。当第一个元素为 True 时,表示视频流包含要读取的帧。
如果有要读取的帧,则可以使用imshow()在窗口中显示当前帧,否则退出循环。请注意,您还使用该waitKey()函数在视频帧之间暂停 20 毫秒。调用该waitKey()函数可让您监视键盘以获取用户输入。在这种情况下,例如,如果用户按下 ' q' 键,则退出循环。
while(vid_capture.isOpened()):
# vid_capture.read() methods returns a tuple, first element is a bool
# and the second is frame
ret, frame = vid_capture.read()
if ret == True:
cv2.imshow('Frame',frame)
# 20 is in milliseconds, try to increase the value, say 50 and observe
key = cv2.waitKey(20)
if key == ord('q'):
break
else:
break
一旦视频流被完全处理或用户过早退出循环,您释放视频捕获对象 ( vid_capture
) 并关闭窗口,使用以下代码:
Python
# Release the video capture object
vid_capture.release()
cv2.destroyAllWindows()
从网络摄像头读取视频
从网络摄像头读取视频流也与上面讨论的示例非常相似。这怎么可能?这一切都归功于 OpenCV 中视频捕获类的灵活性,为了方便起见,它有几个重载函数可以接受不同的输入参数。无需指定视频文件或图像序列的源位置,您只需提供视频捕获设备索引,如下所示。
- 如果您的系统具有内置网络摄像头,则摄像头的设备索引将为“
0
”。 - 如果您有多个摄像头连接到您的系统,则与每个附加摄像头关联的设备索引会递增(例如
1
、2
等
Python
vid_capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
您可能想知道 flag CAP_DSHOW
。这是一个可选参数,因此不是必需的。CAP_DSHOW
只是另一个视频捕获 API 首选项,它是通过视频输入直接显示的缩写。
写视频
现在让我们看看如何编写视频。就像视频阅读一样,我们可以编写来自任何来源(视频文件、图像序列或网络摄像头)的视频。要写入视频文件:
- 使用 方法检索图像帧的高度和宽度
get()
。 - 使用前面描述的任何源初始化视频捕获对象(如前几节所述),以将视频流读入内存。
- 创建一个视频编写器对象。
- 使用 video writer 对象将视频流保存到磁盘。
继续我们的运行示例,让我们从使用get()
方法获取视频帧的宽度和高度开始。
Python
# Obtain frame size information using get() method
frame_width = int(vid_capture.get(3))
frame_height = int(vid_capture.get(4))
frame_size = (frame_width,frame_height)
fps = 20
如前所述,类中的get()
方法VideoCapture()
需要:
- 枚举列表中的单个参数,允许您检索与视频帧关联的各种元数据。
可用的元数据非常广泛,可以在此处找到。
- 在这种情况下,您通过指定
3
(CAP_PROP_FRAME_WIDTH
) 和4
(CAP_PROP_FRAME_HEIGHT
) 来检索框架的宽度和高度。在将视频文件写入磁盘时,您将在下面进一步使用这些尺寸。
为了编写视频文件,您需要首先从VideoWriter()
该类中创建一个 video-writer 对象,如下面的代码所示。
这是VideoWriter()
的语法:
VideoWriter(filename, apiPreference, fourcc, fps, frameSize[, isColor])
该类VideoWriter()
采用以下参数:
filename
: 输出视频文件的路径名apiPreference
: API 后端标识符fourcc
:编解码器的4字符代码,用于压缩帧(fourcc)fps
: 创建的视频流的帧率frame_size
:视频帧的大小isColor
:如果不为零,编码器将期望并编码彩色帧。否则它将适用于灰度帧(该标志目前仅在 Windows 上受支持)。
以下代码output
从VideoWriter()
类创建视频编写器对象。一个特殊的便利函数用于检索四字符编解码器,需要作为视频编写器对象的第二个参数,cv2
.
VideoWriter_fourcc('M', 'J', 'P', 'G')
在 Python 中。VideoWriter::fourcc('M', 'J', 'P', 'G')
在 C++ 中。
视频编解码器指定如何压缩视频流。它将未压缩的视频转换为压缩格式,反之亦然。要创建 AVI 或 MP4 格式,请使用以下 Fourcc 规范:
视频:cv2.VideoWriter_fourcc('M','J','P','G')
MP4:cv2.VideoWriter_fourcc(*'XVID')
接下来的两个输入参数指定以 FPS 为单位的帧速率和帧大小(宽度、高度)。
Python
# Initialize video writer object
output = cv2.VideoWriter('Resources/output_video_from_file.mp4', cv2.VideoWriter_fourcc(*'XVID'), 20, frame_size)
现在您已经创建了一个视频写入器对象,使用它来将视频文件写入磁盘,一次一帧,如下面的代码所示。在这里,您正在以每秒 20 帧的速度将 MP4视频文件写入磁盘。请注意我们如何从前面的示例中简化为循环。
Python
while(vid_capture.isOpened()):
# vid_capture.read() methods returns a tuple, first element is a bool
# and the second is frame
ret, frame = vid_capture.read()
if ret == True:
output.write(frame)
最后,在下面的代码中,释放 video capture 和 video-writer 对象。
Python
# Release the video capture object
vid_capture.release()
output.release()
阅读或编写视频时可能遇到的错误
视频阅读
在读取帧时,如果路径错误或文件损坏或帧丢失,它可能会引发错误。这就是我们在循环中使用if
语句的原因。while
可以在该行中看到,如果ret == True
。这样它只会在帧存在时处理它。以下是在这种情况下观察到的错误日志示例。它不是完整的日志,仅包含关键参数。
cap_gstreamer.cpp:890: error: (-2) GStreamer: unable to start pipeline in function
对于错误的路径:
VideoCapture()
当您提供错误的视频路径时,使用该类将不会显示任何错误或警告。当您尝试对视频帧进行任何操作时,就会出现问题。为此,您可以使用一个简单的 if 块来检查您是否已阅读视频文件,就像我们在示例中所做的那样。那应该打印以下消息。
Error opening the video file
视频写作
在此步骤中可能会出现各种错误。最常见的是帧大小错误和api 首选项错误。如果帧大小与视频不相似,那么即使我们在输出目录中得到一个视频文件,它也会是空白的。如果您使用 NumPy 形状方法来检索帧大小,请记住反转输出,因为 OpenCV 将返回height x width x channels。如果它抛出 api 首选项错误,我们可能需要在参数中传递CAP_ANY
标志。VideoCapture()
它可以在网络摄像头示例中看到,我们使用它CAP_DHOW
来避免生成警告。
以下是错误日志的示例:
当 CAP_DSHOW 未通过时:
[WARN:0]...cap_msmf.cpp(438) …. terminating async callback
当帧大小不正确时:
cv2.error: OpenCV(4.5.2) :-1: error: (-5:Bad argument) in function 'VideoWriter'
> Overload resolution failed:
> - Can't parse 'frameSize'. Sequence item with index 0 has a wrong type
> - VideoWriter() missing required argument 'frameSize' (pos 5)
> - VideoWriter() missing required argument 'params' (pos 5)
> - VideoWriter() missing required argument 'frameSize' (pos 5)
扩展你的知识
概括
在本博客中,您学习了使用视频捕获对象读取和显示来自三个不同来源的视频流。甚至看到了如何使用视频捕获对象从视频流中检索重要的元数据。我们还演示了如何使用 video-writer 对象将视频流写入磁盘。您可能还会发现调整视频帧的大小或使用形状和文本对其进行注释很有帮助。为此,只需修改单个图像帧。
既然您知道如何阅读和编写视频,并且熟悉使用 OpenCV,请跟上步伐。并不断学习。
python+openCV (入门级)车道线检测 学习笔记
文章目录
- 前言
- 一、openCV安装
- 二、尝试使用cv2中库函数
- 三、Canny边缘检测
- 四、ROI and mask
- 五、霍夫变换
- 六、离群值过滤
- 七、最小二乘法拟合
- 八、车道线标注
- 九、视频读写
- 十、其他
- 总结
- 附视频检测完整程序
前言
本文使用python+openCV
用到的算法:
高斯滤波
Canny边缘检测
ROI和mask
霍夫变换
离群值过滤
最小二乘法拟合
一、openCV安装
win+x选择运行,然后输入cmd
输入pip install opencv-python
等待安装
在IDLE中新建脚本.py文件,输入import cv2
,如果无报错即为安装成功。
再输入print(cv2.__version__)
可查看openCV版本。
二、尝试使用cv2中库函数
1.读取图片
cv2.imread(const String & filename, int flags = IMREAD_COLOR)
第一个参数是窗口的名字,第二个参数是读取格式(彩色或灰度),函数返回值是图片转换格式后所对应的列表。
如img = cv2.imread('img.jpg',cv2.IMREAD_GRAYSCALE)
2.图片显示
cv2.imshow(window_name, img)
第一个参数是窗口的名字,可以创建多个窗口,但是必须给他们不同的名字。第二个参数是显示格式,可以是img图片格式,也可以是mat数组格式。
3.延时/暂停
cv2.waitKey(int x)
相当于程序延时,调整
cv2.imshow()
的显示时间。延时时间为x毫秒。返回值为在延时期间按下键盘上按键所对应的ASCII值。
x=0时,即cv2.waitKey(0)
表示程序停在此处,直到手动关闭cv2.imshow()
的窗口。
例如设计一个,无穷延时,当键盘按下q
时退出。
if cv2.waitKey(1) & 0xFF == ord(‘q’): break
#ord()是将字符转换为ASCII码
4.保存图片
cv2.imwrite(newfile_name, img)
第一个参数是保存的新图片的文件名,第二个参数是应保存的图片对应的列表。
5.清楚所有窗口
cv2.destroyAllWindows()
关闭所有openCV打开的窗口。
三、Canny边缘检测
CV2提供了提取图像边缘的函数canny。其算法思想如下:
1. 使用高斯模糊,去除噪音点(cv2.GaussianBlur)
2. 灰度转换(cv2.cvtColor)
3. 使用sobel算子,计算出每个点的梯度大小和梯度方向
4. 使用非极大值抑制(只有最大的保留),消除边缘检测带来的杂散效应
5. 应用双阈值,来确定真实和潜在的边缘
6. 通过抑制弱边缘来完成最终的边缘检测
1.高斯滤波
gaussian = cv2.GaussianBlur(color_img, (gaussian_ksize,gaussian_ksize), gaussian_sigmax)
color_img 输入图片
gaussian_ksize 高斯核大小,可以为方形矩阵,也可以为矩形
gaussian_sigmax X方向上的高斯核标准偏差
没看太懂这个函数,目前仅局限于搬来直接用,下次一定
2.图片转换
gray_img = cv2.cvtColor(input_image, flag)
用于颜色空间转换。input_image为需要转换的图片,flag为转换的类型,返回值为颜色空间转换后的图片矩阵。flag对应:
cv2.COLOR_BGR2GRAY BGR -> Gray
cv2.COLOR_BGR2RGB BGR -> RGB
cv2.COLOR_BGR2HSV BGR -> HSV
可以了解一下该函数和cv2.imread()的区别
3.边缘检测
edge_img = cv2.Canny(gray_img,canny_threshold1,canny_threshold2)
imag为所操作的图片,threshold1为下阈值,threshold2为上阈值,返回值为边缘图。
四、ROI and mask
利用数组选取ROI(region of interest,感性趣的区域,然后与原图进行布尔运算(与运算)。
poly_pts = numpy.array([[[0,368],[300,210],[340,210],[640,368]]])
四个数组创建后保存在变量poly_pts中。
mask = np.zeros_like(gray_img)
构建一个与gray_img同维度的数组,并初始化所有变量为零。
cv2.fillPoly(mask, pts, color)
绘制多边形函数。mask为绘制对像,pts为绘制范围,color为绘制颜色。
img_mask = cv2.bitwise_and(gray_img, mask)
与运算
五、霍夫变换
得到一系列的直线
lines = cv2.HoughLinesP(edge_img, 1, np.pi / 180, 15, minLineLength=40, maxLineGap=20)
edge_img: 要检测的图片矩阵
参数2: 距离r的精度,值越大,考虑越多的线
参数3: 距离theta的精度,值越大,考虑越多的线
参数4: 累加数阈值,值越小,考虑越多的线
minLineLength: 最短长度阈值,短于这个长度的线会被排除
maxLineGap:同一直线两点之间的最大距离
返回值::
[np.array([[x_1,y_1, x_2, y_2]]),
np.array([[x_1, y_1, x_2, y_2]]),
…,
np.array([[x_1, y_1, x_2, y_2]])]
六、离群值过滤
循环查找超出设定范围的斜率,并去除。
idx = np.argmax(diff)
当diff中存放的是一维时,找到diff中最大的数值,并返回该值所在位置。
示例:
one_dim_array = np.array([1, 4, 5, 3, 7, 2, 6])
print(np.argmax(one_dim_array))
打印结果为 4
lines.pop(idx)
从列表lines中把第idx数去除
七、最小二乘法拟合
获得左车道线和右车道线的理想直线
x_coords = np.ravel([[line[0][0], line[0][2]] for line in lines])
将多维数组降为一维
还未想明白转换关系,下次一定
poly = np.polyfit(x, y, deg)
x, y为各个点的横纵坐标,deg为拟合曲线的次数
y_e = np.polyval(poly, x)
拟合曲线之后,x在曲线上对应的y值
八、车道线标注
cv2.line(img, tuple(line[0]), tuple(line[1]), color,thickness)
在图片上画线,img为所标注的目标图片,ine[0]为起点坐标,line[1]为终点坐标,thickness为线宽。
注意两个坐标需要为tuple元组格式,即tuple(line[0])
九、视频读写
1.打开视频
capture = cv2.VideoCapture('video.mp4')
如果为
cv2.VideoCapture(0)
,表示打开笔记本的内置摄像头。
2.按帧读取视频
ret, frame = capture.read()
其中ret是布尔值,如果读取帧是正确的则返回True,如果文件读取到结尾,它的返回值就为False。
frame就是每一帧的图像,是个三维矩阵。
3.视频编码格式设置
fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
fourcc意为四字符代码(Four-Character Codes),顾名思义,该编码由四个字符组成,下面是VideoWriter_fourcc对象一些常用的参数,注意:字符顺序不能弄混
cv2.VideoWriter_fourcc(‘I’, ‘4’, ‘2’, ‘0’),该参数是YUV编码类型,文件名后缀为.avi
cv2.VideoWriter_fourcc(‘P’, ‘I’, ‘M’, ‘I’),该参数是MPEG-1编码类型,文件名后缀为.avi
cv2.VideoWriter_fourcc(‘X’, ‘V’, ‘I’, ‘D’),该参数是MPEG-4编码类型,文件名后缀为.avi
cv2.VideoWriter_fourcc(‘T’, ‘H’, ‘E’, ‘O’),该参数是Ogg Vorbis,文件名后缀为.ogv
cv2.VideoWriter_fourcc(‘F’, ‘L’, ‘V’, ‘1’),该参数是Flash视频,文件名后缀为.flv
4.写视频参数设置
outfile = cv2.VideoWriter(filename, fourcc, 25, (1280,368))
filename为写入视频的文件名,fourcc为视频编码格式cv2.VideoWriter_fourcc()的返回值,25为帧率,(1280,368)为视频大小。
ret, frame = capture.read()
第一个参数ret 为布尔值即True 或者False,代表有没有读取到图片,第二个参数frame表示截取到一帧的图片
output = np.concatenate((origin, frame), axis=1)
能够一次完成多个数组的拼接
不写axis参数的话,默认为按列拼接;若axis = 1的话,即为按行拼接;
axis参数意义没搞太明白,下次一定
十、其他
程序存在小bug但不影响检测视频生成,如果哪位大佬知道bug怎么解决记得踢我一脚。
cv2.imshow()
总是卡住无响应,无法自己关闭窗口,使用cv2.destroyAllWindows()
也还是卡住;
而且报告错误:
error: OpenCV(4.5.5) D:\\a\\opencv-python\\opencv-python\\opencv\\modules\\imgproc\\src\\smooth.dispatch.cpp:617: error: (-215:Assertion failed) !_src.empty() in function 'cv::GaussianBlur'
> 卡住的问题有人说是openCV对一部分硬件的适配不好,我比较在赞成这种观点。 报错 cv2.error error:
> (-215:Assertion failed) dst.data == (uchar*)dst_ptr in function
> 'cvShowImage'
>
> 同样的代码,我发现在公司电脑1060上,cv2.imshow()就可以显示,但在自己2080ti上就总是报错如上。于是锁定在cv2版本问题,果然卸掉最新的4.0版本,换上3.4.4.19就好了。
>
> 可能是4.0版本与2080ti的卡还不兼容。
> 来源:https://blog.csdn.net/qq_39938666/article/details/88179966
报错的问题目前未解决,也修改不掉。
总结
完全实现对视频形式的单车道直线行驶时对车道线检测和标注。
附视频检测完整程序
import cv2
import numpy as np
#高斯滤波+canny边缘检测
def get_edge_img(color_img, gaussian_ksize=5, gaussian_sigmax=1,
canny_threshold1=50, canny_threshold2=100):
#param intoduction
#color_img 输入图片 gaussian_ksize 高斯核大小
#gaussian_sigmax X方向上的高斯核标准偏差
gaussian = cv2.GaussianBlur(color_img, (gaussian_ksize, gaussian_ksize),
gaussian_sigmax)
gray_img = cv2.cvtColor(gaussian,cv2.IMREAD_GRAYSCALE)
edge_img = cv2.Canny(gray_img,canny_threshold1,canny_threshold2);
return edge_img
def roi_mask(gray_img):
poly_pts = np.array([[[0,368],[300,210],[340,210],[640,368]]])
mask = np.zeros_like(gray_img)
cv2.fillPoly(mask, pts=poly_pts, color=255)
img_mask = cv2.bitwise_and(gray_img, mask)
return img_mask
def get_lines(edge_img):
#斜率计算
def calculate_slope(line):
x_1, y_1, x_2, y_2 = line[0]
slope = (y_2-y_1)/(x_2-x_1)
return slope
#离群值过滤
def reject_abnormal_lines(lines, threshold):
slopes = [calculate_slope(line) for line in lines]
while len(lines)>0:
mean = np.mean(slopes)
diff = [abs(s-mean) for s in slopes]
idx = np.argmax(diff)
if diff[idx]>threshold:
slopes.pop(idx)
lines.pop(idx)
else:
break
return lines
#最小二乘拟合
def least_squares_fit(lines):
x_coords = np.ravel([[line[0][0], line[0][2]] for line in lines])
y_coords = np.ravel([[line[0][1], line[0][3]] for line in lines])
# 2. 进行直线拟合.得到多项式系数
poly = np.polyfit(x_coords, y_coords, deg=1)
# 3. 根据多项式系数,计算两个直线上的点,用于唯一确定这条直线
point_min = (np.min(x_coords), np.polyval(poly, np.min(x_coords)))
point_max = (np.max(x_coords), np.polyval(poly, np.max(x_coords)))
return np.array([point_min, point_max],dtype=np.int0)
lines = cv2.HoughLinesP(edge_img, 1, np.pi/180, 15, minLineLength=40, maxLineGap=20)
left_lines = [line for line in lines if calculate_slope(line)<0]
right_lines = [line for line in lines if calculate_slope(line)>0]
left_lines = reject_abnormal_lines(left_lines, threshold=0.2)
right_lines = reject_abnormal_lines(right_lines, threshold=0.2)
return least_squares_fit(left_lines),least_squares_fit(right_lines)
def draw_line(img, lines):
left_line, right_line = lines
cv2.line(img, tuple(left_line[0]), tuple(left_line[1]), color=(0,255,255),thickness=5)
cv2.line(img, tuple(right_line[0]), tuple(right_line[1]), color=(0,255,255),thickness=5)
def show_lane(color_img):
edge_img = get_edge_img(color_img)
mask_gray_img = roi_mask(edge_img)
lines = get_lines(mask_gray_img)
draw_line(color_img, lines)
return color_img
if __name__ == '__main__':
capture = cv2.VideoCapture('video.mp4')
fourcc = cv2.VideoWriter_fourcc('X', 'V', 'I', 'D')
outfile = cv2.VideoWriter('output.avi', fourcc, 25, (1280,368))
while True:
ret, frame = capture.read()
origin = np.copy(frame)
frame = show_lane(frame)
output = np.concatenate((origin, frame), axis=1)
outfile.write(output)
cv2.imshow('video', output)
cv2.waitKey(10)
分步操作检测图片和检测视频的源码、测试图片和测试视频连接:https://download.csdn.net/download/qq_27839923/75831347
以上是关于二.使用 OpenCV 读写视频的主要内容,如果未能解决你的问题,请参考以下文章
实现抖音视频抖动效果---OpenCV-Python开发指南(53)