- OpenCV 4机器学习算法原理与编程实战
- 朱斌编著
- 1684字
- 2021-04-30 21:23:50
2.2 基本视频操作
如果说单张图像是二维数据,则视频是三维数据,它增加了时间维度z,单张图像与视频序列如图2-18所示。在机器视觉和机器学习结合的许多应用中都需要处理视频,本节将介绍OpenCV的基本视频操作。
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-72-2.jpg?sign=1739632702-40O0IWHeV6vqnxAPvO35LXGNUjLJoqj9-0-0d13a08fc12236bbe1fe23ec439c0ad4)
图2-18 单张图像与视频序列
2.2.1 读取和播放视频文件
1. 实现方法
使用OpenCV读取和播放视频文件几乎与显示图像一样简单,只需设置一个循环结构,每次循环读取视频文件中的一帧图像用于显示即可,当然还需要设置退出循环的条件。读取和播放视频文件如示例代码2-6所示。
示例代码2-6 读取和播放视频文件
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-73-1.jpg?sign=1739632702-Sd27hiamqTAm3RQhesVjwy1jKGMgNx3J-0-0e7da9aaba1d3c7f63a2036354abf47f)
2. 代码分析
首先,实例化一个视频控制类函数cv::VideoCapture的对象cap,用于读取和操作视频文件:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-73-2.jpg?sign=1739632702-CIy7U2l2ZTZWqMy2aXYkjQvCGAQEWhkg-0-d674b31fc3b6991ea5e40e49737cd7b8)
其次,使用cap.open方法从源文件所在目录读取视频文件bike.avi:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-73-3.jpg?sign=1739632702-NCK4zfQeBvw9ZjfaMXkSk1xJZiuTAZrE-0-c2b70836da5fd27171e0c33857245d14)
然后,创建一个用于从视频文件中读取单帧图像的cv::Mat变量frame:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-73-4.jpg?sign=1739632702-ZV52wZYmxMfBfukH0hjxnvNd8Pazx5QZ-0-8ba524e026fc3f8909599dac210e0afb)
接下来是关键部分,创建for循环从视频文件中依次读取单帧图像并存储在frame中:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-73-5.jpg?sign=1739632702-icnaIq2yZb722wEjV9bMmlr6EEX7y4w4-0-7cc9b43625de7e043f2a083ec8961fa7)
最后,当读取的frame是空图像时,即已读取到视频文件的最后一帧,退出循环:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-74-1.jpg?sign=1739632702-44d26xWw21PvvHDZ3jxwUjtvRSZuXSbK-0-b317bc4c418de343cc2d69d2de3f00f1)
如果读取的frame有数据,则显示frame:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-74-2.jpg?sign=1739632702-l5AxIaXTQdW1q4URSzg6mHjLFpzVPDMw-0-1f8477c8bc87c2da645cc17516fe006f)
另外,增加一个退出机制,即在播放过程中当有按键被按下时,cv::waitKey函数将返回按键的ASCII码(>0),并退出循环,停止播放,否则等待33ms后继续读取下一帧图像:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-74-3.jpg?sign=1739632702-cxyoPmqWfGOcQjflCxB6XzO4BwXDKuNq-0-3fe0307bb8bc150c8145e429070144ec)
当然,也可以使用while循环控制视频文件的播放。
3. 运行结果
示例代码2-6的运行结果如图2-19所示,视频中的小女孩骑车从左至右经过画面。
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-74-4.jpg?sign=1739632702-cs1c8jvjm1jokRtEVAjycvYXNd6WAwBj-0-bf8064f7f13a128513ae8e24d2f17377)
图2-19 示例代码2-6的运行结果
2.2.2 处理视频文件
1. 实现方法
下面通过例子展示如何使用OpenCV调用摄像头并处理视频文件。如果计算机自带摄像头或已将USB摄像头连接至计算机,则只需将示例代码2-7中的cap.open("bike.avi")改为cap.open(0)即可打开摄像头。
示例代码2-7 调用摄像头并处理视频
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-75-1.jpg?sign=1739632702-mK8TDnPoQRJ9KHt8EO8bGD6R3Ojd44kJ-0-fde0e8806d10e8c8715d061c00971bfa)
2. 代码分析
首先,使用cap.open(0)方法打开默认摄像头,新建两个cv::Mat变量——frame和edge_img,分别用于存放摄像头捕获的图像与边缘检测后的图像:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-75-2.jpg?sign=1739632702-EsRCxKxMooKEl4XZ12USaCI95oQNpZHG-0-a52c92d8c0b14a6890b4f6c32ea2f7d7)
然后,与示例代码2-6一样,在循环中获取一帧图像:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-75-3.jpg?sign=1739632702-JMUbYttyfBko2JbuSPTAPj8hfKcz3pma-0-bfe4207d96d58574f22b2837e7796b93)
接下来,使用cv::Canny函数检测图像的边缘图像,并将其存储于edge_img变量中:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-75-4.jpg?sign=1739632702-grwRQfJnGed2o87c2YiYuEfXKmL0SQi7-0-cbe3a0e811d5c81e2b4daa65c67169f6)
在两个窗口中分别显示源图与边缘图像:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-75-5.jpg?sign=1739632702-dZhZchcHFNBqlhZxYFgluHY7ytzCV2VR-0-998364b0409bc600cea7b5b11b411f66)
设置循环退出条件,当按下“ESC”、“q”或者“Q”键时退出循环:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-76-1.jpg?sign=1739632702-i2SZsA9YqZ2UUqU826RnFD9D62klaKD7-0-9c86a8a7f7c58162e8a969e30db65766)
前面从整体上分析了示例代码2-7,下面简单介绍Canny边缘检测算法。Canny边缘检测算法结合了高低阈值计算出的两个边缘分布图,可生成最优边缘分布图。具体做法是在低阈值的边缘分布图中只保留有连续路径的边缘点,同时把这些边缘点连接到属于高阈值边缘分布图的边缘上。这样一来,高阈值分布图上的所有边缘点就都保留了下来,而低阈值分布图上的边缘点的孤立链被全部移除。这是一种很好的折中方案,只要指定适当的阈值,就能获得高质量的轮廓。这种基于两个阈值获得二值分布图的策略,称为滞后化阈值,适用于任何需要用阈值获得二值分布图的场景。
cv::Canny函数是OpenCV提供的边缘检测函数。
cv::Canny函数:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-76-2.jpg?sign=1739632702-f6FX2GPyw59QohZwKOKG8fPvmHUCQk8M-0-12759f8ca307ad3bf51f06af6346d016)
函数参数:
◎ image:8bit输入图像。
◎ edges:输出边缘图像,单通道8bit图像,尺寸与输入图像一致。
◎ threshold1:滞后阈值化的第1阈值。
◎ threshold2:滞后阈值化的第2阈值。
◎ apertureSize:Sobel操作员的孔径大小。
◎ L2gradient:标志,指示是使用更精确的L2范数计算图像梯度的大小(L2gradient =true),还是使用默认的L1范数(L2gradient = false)计算图像梯度的大小。
3. 运行结果
读取摄像头并处理示例代码2-7的运行显示结果如图2-20所示。
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-77-1.jpg?sign=1739632702-WPzsVevtmzCbQeCAHt6ENAjlix0nH2Wh-0-2227b9950b6703f09c7c4cb7c6fff2e1)
图2-20 读取摄像头并处理示例代码2-7的运行显示结果
2.2.3 存储视频文件
1. 实现方法
使用cv::VideoWrite类可以方便地在硬盘中写入不同编码方式的视频文件。在示例代码2-7的基础上,修改代码如下。
示例代码2-8 写视频文件
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-77-2.jpg?sign=1739632702-t8zhYco8Qv8VSAFNpaob73PACGZXNZ4h-0-201417540bbc3e7eca861c320b8b7354)
2. 代码分析
与示例代码2-7相比,在增加的代码中首先使用了cv::VideoCapture类的get方法来获取摄像头图像的宽和高:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-78-1.jpg?sign=1739632702-e9PFeWoLHmEvxBKzVochlXBCl6RyNU2I-0-3e917e1391abe08c0da8cb0308435ac3)
然后根据这些参数,实例化一个cv::VideoWriter类的对象out,并调用open方法完成对out的初始化:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-78-2.jpg?sign=1739632702-okU9LR6vxEqVAPSod3CG2Ny0CZZ8U5U9-0-b40c5a4a1dee84a76c1ee051b579847f)
最后,在while循环中计算图像边缘后,使用cv::VideoWriter类的write方法将边缘图像edge_img一帧接一帧地写入视频文件my_video.avi中:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-78-3.jpg?sign=1739632702-otG2ygHV5OveuT4iY8vr16Fel0eji3pD-0-6543ac6d4518d878cee3a7d3e70ddd31)
另一种写入视频的操作如下:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-78-4.jpg?sign=1739632702-RyDygSS8t9sNiwxmTDvtbK8RQI1V4uCM-0-57418977e47e9d0cf1cda138e49f7008)
cv::VideoWriter::VideoWriter函数:
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-78-5.jpg?sign=1739632702-KBYdzXlrRNK2lNa2GIxkEGmTrroCymb8-0-94fdd722cb9158754bc315d8cb79e792)
函数参数:
◎ filename:输出视频文件名。
◎ fourcc:由4个字母组成,表示帧压缩的编码方式。例如:
VideoWriter::fourcc('P', 'I', 'M', '1') 表示MPEG-1编码。
VideoWriter::fourcc('D', 'I', 'V', 'X') 表示MPEG-4编码。
VideoWriter::fourcc('M', 'J', 'P', 'G') 表示主运动jpeg编码。
◎ fps:每秒传输视频帧数,简称帧率。
◎ frameSize:视频帧的宽和高。
◎ isColor:如果不为零,则编码器如预期一样编码彩色帧,否则编码灰度帧(该标记当前仅在Windows上受支持)。
3. 运行结果
在源文件的目录下生成一个my_video.avi的视频文件,它存储了边缘检测结果视频,存储的视频文件缩略图如图2-21所示。
![](https://epubservercos.yuewen.com/EC2541/19938710301518806/epubprivate/OEBPS/Images/40830-00-79-1.jpg?sign=1739632702-YkrWR10ipP1Fw4pM4ptFOqMM7jVMfqIn-0-d2f63344abbf5ad2fe46801aef873e83)
图2-21 存储的视频文件缩略图