4.OpenCV视频处理
Posted 不会写代码的肖恩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.OpenCV视频处理相关的知识,希望对你有一定的参考价值。
1.VideoCapture对象
cv::VideoCapture根据数据来源的不同,有三种构建方式:
cv::VideoCapture::VideoCapture(const string& filename); //从视频文件获取数据
cv::VideoCapture::VideoCapture(int device); //从设备中获取
cv::VideoCapture::VideoCapture();
如果打开成功cv::VideoCapture::isOpened()
会返回true。请在每次打开文件或者设备
时,使用isOpened()检查是否打开成功。
1.1 相机的域
相机的域指示HighGUI应该如何匹配相机。
相机捕捉常数 | 对应数值 |
---|---|
cv::CAP_ANY | 0 |
cv::CAP_MIL | 100 |
cv::CAP_VFM | 200 |
cv::CAP_V4L | 200 |
cv::CAP_V4L2 | 200 |
cv::CAP_FIREWIRE | 300 |
cv::CAP_IEEE1394 | 300 |
cv::CAP_DC1394 | 300 |
cv::CAP_CMU1394 | 300 |
cv::CAP_QT | 500 |
cv::CAP_DSHOW | 700 |
cv::CAP_PVAPI | 800 |
cv::CAP_OPENNI | 900 |
cv::CAP_android | 1000 |
cv::VideoCapture cap;
cap.open("my_video.avi");
//等效于
cv::VideoCapture cap("my_video.avi");
2.从视频流中读取图像
2.1 read()函数读取数据
bool cv::VideoCapture::read(cv::OutputArray image);
- image: 从视频流中读取的一帧数据
- return: 成功返回true,失败或者已读取完文件最后一帧,返回false。
2.2 重载符>>读取数据
cv::VideoCapture& cv::VideoCapture::operator>>(cv::Mat& image);
由于这是一个流操作符,所以无论读取成功与否,它都会返回一个对最初的cv::Capture对象的引用。除此之外,它与read()函数的功能完全相同。
2.3 grab()和retrieve()读取
在多相机数据读取时,我们希望获取每个相机数据时,尽可能的快,从而减少相机直接的时间差。所以出现了捕获(grab)和恢复(retrieve)分开的应用场景。read()
函数就是一次完整的
grab()和retrieve()过程。
bool cv::VideoCapture::grab(void);
bool cv::VideoCapture::retrieve(cv::OutputArray image, int channel=0);
params:
- image 获取的帧数据
- channel 在多相机时,指定相机的设备号
return:
- ture 获取数据成功
- false 获取数据失败
3.相机属性
3.1 获取相机属性
视频文件不仅包含一帧一帧的图像,还有许多元数据(meta data),这些数据会被拷贝到cv::VideoCapture对象内部的数据区。
double cv::VideoCapture::get(int propid); //Property identifier
常见的属性ID表如下:
视频捕获属性 | 是否只有在摄像头模式下可以使用 | 含义 |
---|---|---|
cv::CAP_PROP_POS_MSEC | 视频文件中的当前位置(毫秒)或视频捕获的时间戳 | |
cv::CAP_PROP_POS_FRAMES | 从零开始下一帧的索引 | |
cv::CAP_PROP_POS_AVI_RATIO | 视频中的相对位置(范围0.0到1.0),滑动条调整时需要用到。 | |
cv::CAP_PROP_FRAME_WIDTH | 视频帧的像素宽度 | |
cv::CAP_PROP_FRAME_HEIGHT | 视频帧的像素高度 | |
cv::CAP_PROP_FPS | 录制视频的帧速率 | |
cv::CAP_PROP_FOURCC | 四个字符代码指示编解码 | |
cv::CAP_PROP_FRAME_COUNT | 视频文件中的帧总数,并不完全可靠。 | |
cv::CAP_PROP_FORMAT | 返回的Mat对象格式(如:CV_8UC3) | |
cv::CAP_PROP_PROP_MODE | 表示捕捉模式(如:DC1394) | |
cv::CAP_PROP_PROP_BRIGHTNESS | 是 | 相机的亮度 |
cv::CAP_PROP_PROP_COUNTRAST | 是 | 相机的对比度 |
cv::CAP_PROP_PROP_SATURATION | 是 | 相机的饱和度 |
cv::CAP_PROP_PROP_HUE | 是 | 相机的色调 |
cv::CAP_PROP_PROP_GAIN | 是 | 相机的增益设定 |
cv::CAP_PROP_PROP_EXPOSURE | 是 | 相机曝光设置 |
cv::CAP_PROP_PROP_CONVERT_RGB | 是 | 如果非0,捕获的图像将被转换为3通道数据 |
cv::CAP_PROP_PROP_WHITE_BALANCE | 是 | 相机白平衡设置 |
cv::CAP_PROP_PROP_RECTIFICATION | 是 | 立体相机标志(仅适用于DC1394-2.X) |
FOURCC转换为char类型
cv::VideoCapture cap("my_video.avi");
unsigned f = (unsigned)cap.get(cv::CAP_PROP_FOURCC);
char fourcc[] =
(char)f,
(char)(f >> 8),
(char)(f >> 16),
(char)(f >> 24),
'\\0'
;
3.2 设置相机属性
bool cv::VideoCapture::set(int propid, double value);
PythonPython-OpenCV实时处理视频
利用Python的opencv包实时处理视频(也可以处理视频文件),并输出到虚拟摄像头(类似直播时实时处理视频)。
由于视频处理即帧处理需要一定的时间,全部放在一个进程中会导致进程阻塞、视频卡顿,于是这里采用两个进程分别进行视频处理和推流到虚拟摄像头并通过队列的方式进行通信。
步骤
类与初始化
这里构建一个LIVE类用于实现各种功能,不同的方法可能共用一些相同的参数,因此使用类的实例属性来保存这些参数。
class Live(object):
# 初始化模板与视频(摄像头)参数
def __init__(self, template, file, target):
# 定义opencv所需的模板
self.template = template
# 读取模板并获取模板的高度和宽度
self.template = cv2.imread(self.template, 1)
# 视频路径,0时调用摄像头
self.file = file
# 视频对象,0时调用摄像头
cap = cv2.VideoCapture(self.file)
# 视频高度
self.height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 视频宽度
self.width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
# 视频尺寸
self.size = f'self.widthxself.height'
# 视频帧率
self.fps = int(cap.get(cv2.CAP_PROP_FPS))
del cap
self.target = target # 裁剪坐标和尺寸(x,y,width,height)
self.targetSize = f'self.target[2]xself.target[3]'
这里主要以实时裁剪视频的例子来讲解,所以self.target保存了视频裁剪的坐标和尺寸。
视频捕获预处理
这里使用opencv捕获摄像头的画面(或读取视频文件)为一个个帧,将帧进行处理(裁剪到目标尺寸)后,最后put进队列中。
def image_put(self, queue, filepath):
while 1:
cap = cv2.VideoCapture(filepath)
if filepath == 0: # 摄像头时设置分辨率
cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
print(f'Source Video Parameters: size: self.size fps: self.fps.')
while cap.isOpened():
ret, frame = cap.read()
if not ret:
if filepath == 0:
print("Opening camera is failed!!!")
else:
print("Video is over.")
queue.put(cv2.resize(self.template, (self.target[0], self.target[1]), cv2.INTER_AREA)) # 放一张模板
queue.put('stop')
break
queue.put(frame[self.target[1]:self.target[1] + self.target[3],
self.target[0]:self.target[0] + self.target[2]])
此方法后面会作为一个进程的方法来调用。
视频帧输出到虚拟摄像头
这里使用Unity Video Capture作为虚拟摄像头的插件,插件下载链接我会放在文末,官网也能下载到。
def image_get(self, queue):
cam = pyvirtualcam.Camera(width=self.target[2], height=self.target[3], fps=self.fps)
print(f'Virtual Camera: cam.device.')
print(f'Virtual Camera Parameters: size: self.targetSize fps: self.fps.')
while True:
frame = queue.get()
if type(frame) == type('stop'):
break
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # BGR TO RGB
frame = cv2.flip(frame, 1) # 水平翻转
cam.send(frame)
cam.sleep_until_next_frame()
利用pyvirtualcam包初始化虚拟摄像头,并使用该包的send方法将get到的视频帧推到虚拟摄像头上。
进程设置
首先创建一个队列用于保存视频帧,然后将视频捕获预处理方法和视频帧输出到虚拟摄像头方法分别用一个进程来实现。
def run_single_camera(self):
queue = mp.Queue(maxsize=2) # 队列
processes = [mp.Process(target=self.image_put, args=(queue, self.file)),
mp.Process(target=self.image_get, args=(queue,))]
[process.start() for process in processes]
[process.join() for process in processes]
完整代码
import cv2
import pyvirtualcam
import multiprocessing as mp
class Live(object):
# 初始化模板与视频(摄像头)参数
def __init__(self, template, file, target):
# 定义opencv所需的模板
self.template = template
# 读取模板并获取模板的高度和宽度
self.template = cv2.imread(self.template, 1)
# 视频路径,0时调用摄像头
self.file = file
# 视频对象,0时调用摄像头
cap = cv2.VideoCapture(self.file)
# 视频高度
self.height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 视频宽度
self.width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
# 视频尺寸
self.size = f'self.widthxself.height'
# 视频帧率
self.fps = int(cap.get(cv2.CAP_PROP_FPS))
del cap
self.target = target # 截图尺寸
self.targetSize = f'self.target[2]xself.target[3]'
# 此线程用于视频捕获处理
def image_put(self, queue, filepath):
while 1:
cap = cv2.VideoCapture(filepath)
if filepath == 0: # 摄像头时设置分辨率
cap.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
print(f'Source Video Parameters: size: self.size fps: self.fps.')
while cap.isOpened():
ret, frame = cap.read()
if not ret:
if filepath == 0:
print("Opening camera is failed!!!")
else:
print("Video is over.")
queue.put(cv2.resize(self.template, (self.target[0], self.target[1]), cv2.INTER_AREA)) # 放一张模板
queue.put('stop')
break
queue.put(frame[self.target[1]:self.target[1] + self.target[3],
self.target[0]:self.target[0] + self.target[2]])
# 此线程用于推流
def image_get(self, queue):
cam = pyvirtualcam.Camera(width=self.target[2], height=self.target[3], fps=self.fps)
print(f'Virtual Camera: cam.device.')
print(f'Virtual Camera Parameters: size: self.targetSize fps: self.fps.')
while True:
frame = queue.get()
if type(frame) == type('stop'):
break
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # BGR TO RGB
frame = cv2.flip(frame, 1) # 水平翻转
cam.send(frame)
cam.sleep_until_next_frame()
# 多线程执行一个摄像头
def run_single_camera(self):
queue = mp.Queue(maxsize=2) # 队列
processes = [mp.Process(target=self.image_put, args=(queue, self.file)),
mp.Process(target=self.image_get, args=(queue,))]
[process.start() for process in processes]
[process.join() for process in processes]
if __name__ == '__main__':
mp.freeze_support()
template_path = 'template.jpg'
# file_path = "xxx.mp4"
file_path = 0 # 摄像头
real_camera_size = (640, 480)
target_size = (380, 300)
target = ((real_camera_size[0] - target_size[0]) // 2, (real_camera_size[1] - target_size[1]) // 2, target_size[0],
target_size[1])
live = Live(template=template_path, file=file_path, target=target)
live.run_single_camera()
代码执行时要在代码或者生成的exe目录下放置一张jpg模板(template_path = ‘template.jpg’)。
Unity插件
本文内容分享仅供学习,切勿用于商业或违法用途
本文内容分享仅供学习,切勿用于商业或违法用途
以上是关于4.OpenCV视频处理的主要内容,如果未能解决你的问题,请参考以下文章