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_ANY0
cv::CAP_MIL100
cv::CAP_VFM200
cv::CAP_V4L200
cv::CAP_V4L2200
cv::CAP_FIREWIRE300
cv::CAP_IEEE1394300
cv::CAP_DC1394300
cv::CAP_CMU1394300
cv::CAP_QT500
cv::CAP_DSHOW700
cv::CAP_PVAPI800
cv::CAP_OPENNI900
cv::CAP_android1000
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插件

Unity插件下载链接

本文内容分享仅供学习,切勿用于商业或违法用途

本文内容分享仅供学习,切勿用于商业或违法用途

以上是关于4.OpenCV视频处理的主要内容,如果未能解决你的问题,请参考以下文章

ApacheCN 计算机视觉译文集 20210212 更新

大伽带你入门OpenCV Python计算机视觉

计算机视觉案例:应用opencv+keras完成视频物体检测

OpenCV实战——OpenCV策略设计模式

OpenCV实战——OpenCV策略设计模式

计算机视觉 : 10分钟入门opencv