目录

问题描述

问题分析

解决思路

存在的问题

问题分析


问题描述

用opencv的

cv2.VideoWriter()

去将图片实时保存为视频,总会出现视频变快或变慢。

问题分析

初步理解是因为该命令不能按照设置的帧率自动按照一定时间间隔保存视频,也就是说要求你的输入帧数和保存帧数要一致才能保存。

但是我们在应用中经常要对获得的视频图像做处理,之后再保存,处理的时间是不一定的。因此,如果直接用这个命令会出现视频时快时慢的问题。

从道理上讲应该是获得一秒需要的帧数后保存一秒的视频,opcv内部应该有相应的定时器,但我没找到,就自己探索了一个方法。

解决思路

由于我这边只是需要完成保存模块,保存的代码要在一个进程里实现。所以这里我用两个进程,一个进程获取摄像机图像信息,一个进程进行保存。

获取涉嫌头视频代码如下

import cv2
import time
import threading
import ffmpeg
import numpy as np
from pandas import DataFrame
import chardet
class Get_rtsp():
    def __init__(self, cam_adrs, pipe):
        self.count = 0
        self.cam_rul = cam_adrs
        self.pipe = pipe

    # 用ffmpeg将摄像机图像拉下来
    def img_get(self):
        out = (
            ffmpeg
            .input(self.cam_rul, rtsp_transport='tcp')
            .output('pipe:', format='rawvideo', pix_fmt='bgr24', loglevel="quiet", g=0, r=60)
            .run_async(pipe_stdout=True)
        )

        while True:
            a = time.perf_counter()
            # 接到的图像是bytes,需要转换为frame
            in_bytes = out.stdout.read(1080 * 1920 * 3)
            frame = np.frombuffer(in_bytes, dtype=np.uint8).reshape(1080, 1920, 3)
            # 放入管道
            self.pipe.send(frame)
            # print('a', round((time.perf_counter() - a) * 1000))
            # 记录获得帧数
            self.count += 1
            df = DataFrame([[self.count, round((time.perf_counter() - a) * 1000)]], columns=['count', 'img_get_time'])
            df.to_csv('time_waste_getrtsp.csv', encoding='gbk', mode='a', index=False,
                      header=False)

两个进程之间用管道通信

下载的代码如下

import cv2
import time
import multiprocessing as mp
import subprocess as sp
from pandas.core.frame import DataFrame
import ffmpeg
import threading
import gc

class DLPrces():
    def __init__(self):
        self.localtime = time.strftime("%Y%m%d%H%M%S", time.localtime())
        self.frame_hub = []
        self.fps = 20
        self.time_iter = round((1 / self.fps), 4)
        self.lock_1 = threading.Lock()
    def down(self, q_dl_img):
        # 这个线程就不停的读管道写进列表
        count = 0
        while True:
            t_start = time.time()
            a = q_dl_img.recv()
            self.lock_1.acquire()
            self.frame_hub.append(a)
            # 如果读的太快,就清空列表,一般没有这么快
            if len(self.frame_hub) > 100:
                self.frame_hub.clear()
                # print('获得帧数超过保存帧数')
            self.lock_1.release()
            count += 1
            time_consume = round((time.time() - t_start) * 1000)
            t_1 = time.strftime("%Y%m%d%H%M%S", time.localtime())
            df = DataFrame([[count, t_1, time_consume]], columns=['count', 't_tmp', 'dl_img_time'])
            df.to_csv('time_get_pipe.csv', encoding='gbk', mode='a', index=False,
                      header=False)


    def write(self):
        out_radpg = cv2.VideoWriter('./' + '/rad_video_' + str(self.localtime) + '.avi',
                                    cv2.VideoWriter_fourcc(*'XVID'), self.fps, (1920, 1080), True)
        print("——————————DLPrces————————————  开始压视频")
        t_loop = 0
        count = 0
        while True:
            tmp_img = time.perf_counter()
            while True:
                # 开始循环计时
                if (time.perf_counter()-tmp_img) - (self.time_iter - t_loop) <= 0:
                    continue
                else:
                    t_lostart = time.time()
                    # 如果列表为空就继续等待
                    if len(self.frame_hub) != 0:
                        t_start = time.time()
                        # 利用opencv保存视频
                        self.lock_1.acquire()
                        out_radpg.write(self.frame_hub.pop(-1))
                        # 清空列表避免获得过去的帧数
                        self.frame_hub.clear()
                        self.lock_1.release()

                        count += 1
                        # print(time.perf_counter())
                        time_consume = time.time()
                        time_consume_save = round((time_consume - t_start) * 1000)
                        t_1 = time.strftime("%Y%m%d%H%M%S", time.localtime())
                        df = DataFrame([[count, t_1, time_consume_save]], columns=['count', 't_tmp', 'dl_img_time'])
                        df.to_csv('time_save.csv', encoding='gbk', mode='a', index=False,
                                  header=False)
                        t_loop = time.time() - t_lostart
                    else:
                        continue
                break

    def run(self, q_dl_img):

        threads = [threading.Thread(target=DLPrces.down, args=(self, q_dl_img)),
                   threading.Thread(target=DLPrces.write, args=(self,))]

        [thread.start() for thread in threads]
        [thread.join() for thread in threads]



   

开启了两个线程,去按照20帧的时间间隔保存图像

存在的问题

用ffmpeg取20,40,60帧的视频,保存也基本是20帧。但是取20帧的保存的视频很流畅,帧数越高越不流畅,优点是不流畅的很稳定........不会因为获得帧数高保存帧数低就慢速很多

存取20帧的时候会出现帧数变少,平均只有18帧。丢的还是很多的。原因是ffmpeg取流时不会按照50ms的间隔去拿,他是平均50ms的拿,就是说有时候100ms拿一次,有时候10ms拿一次。我们保存的固定计时器在100ms的时候保存一帧,在10ms的时候还要等50ms,这时候再来一帧20ms的我们存了前面的10ms的就被清除了。

问题分析

应该是跳帧的问题,一卡一卡的。不知道怎么解决

最终结论

请直接使用ffmpeg进行拉流,干净又卫生

GitHub 加速计划 / opencv31 / opencv
217
19
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:19 天前 )
edfa999b Added option to wrap opencv_contrib into JS too. 1 天前
5755d4f2 - 2 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐