python-opencv摄像头读取,保存视频

由于项目需要进行数据采集,从摄像头读取后,将读取的每帧数据进行保存视频。在使用过程中,发现采集的生成的视频达不到指定的fps,存在丢帧现象。经过排查,在写视频write(frame)存在IO耗时偏高,每帧处理耗时长了,导致掉帧。特别在1080p,30fps的写入视频对CPU占用都较高。
为了解决这个问题,将单线程改为多线程进行 读写分离 处理数据帧,将读出的视频帧,放入FIFO队列,写视频线程从队列中读取数据帧,按照指定帧率进行写入保存视频。
代码如下:

import os
import time

import cv2
import datetime
import configparser
import csv


import queue

import threading

thread_exit = False
ready = False

q = queue.Queue()

class myThread(threading.Thread):
    def __init__(self, camera_id, img_width, img_height, fps):
        super(myThread, self).__init__()
        self.camera_id = camera_id
        self.img_width = img_width
        self.img_height = img_height
        self.fps = fps

        self.cap = cv2.VideoCapture(self.camera_id)

        print("摄像头默认帧率:{}".format(self.cap.get(cv2.CAP_PROP_FPS)))
        print("摄像头设置帧率:{}".format(self.fps))
        ret = self.cap.set(5, self.fps)        # 设置视频读取帧率
        print("摄像头设置后帧率:{},{}".format(ret, self.cap.get(cv2.CAP_PROP_FPS)))

        self.cap.set(3, self.img_width)  # 设置视频的宽
        self.cap.set(4, self.img_height)  # 设置视频的高

        print("摄像头默认编解码:{}".format(self.cap.get(cv2.CAP_PROP_FOURCC)))
        fourcc = cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')
        self.cap.set(6, fourcc)  # 设置解码格式
        print("摄像头设置后编解码:{}".format(self.cap.get(cv2.CAP_PROP_FOURCC)))

        self.img_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # 重新获取最大的分辨率宽
        self.img_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  # 重新获取最大的分辨率高
        self.fps = int(self.cap.get(cv2.CAP_PROP_FPS))                # 获取支持的fps


    def run(self):
        global thread_exit
        global ready
        global q
       
        frame_count = 0
        str_time = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
        f = open(str_time + '.csv', 'w', encoding='utf-8', newline='')
        header = ['Frame', 'DateTime']
        writer = csv.writer(f)
        writer.writerow(header)
        while not thread_exit:
            ret, frame = self.cap.read()
            if ret:
                print(self.cap.get(cv2.CAP_PROP_POS_MSEC))
              
                font = cv2.FONT_HERSHEY_SIMPLEX
                text = 'Width: ' + str(self.img_width) + ' Height:' + str(self.img_height)

                datet = str(datetime.datetime.now())
                str_date = datet.replace('.', ' ')  # .分隔会导致csv文件格式显示不对,需要格式处理
                # datet = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S %f')[:-3] # .分隔会导致csv文件格式显示不对需要格式处理
                frame = cv2.putText(frame, text, (10, 50), font, 0.5,
                                    (0, 255, 255), 2, cv2.LINE_AA)
                frame = cv2.putText(frame, datet, (10, 100), font, 1,
                                    (0, 255, 255), 2, cv2.LINE_AA)
                my_list = [frame_count, str_date]
                writer.writerow(my_list)

                q.put(frame)
                ready = True
                frame_count = frame_count + 1
            else:
                f.close()
                thread_exit = True
                self.cap.release()


def main():
    # 默认设置
    id = 0
    width = 640
    height = 480
    fps = 30

    status = os.path.exists("config.ini")
    if status == True:
        config = configparser.ConfigParser()
        config.read("config.ini")

        config.sections()
        config.options("camera")
        id = config.getint("camera", "id")
        width = config.getint("camera", "width")
        height = config.getint("camera", "height")
        fps = config.getint("camera", "fps")
        print("配置文件中读取配置")

    global thread_exit
    global ready
    global q

    thread = myThread(id, width, height,  fps)
    thread.start()

    tmp_list = []

    # 构建视频保存的对象
    fourcc = cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')  
    str_time = datetime.datetime.now().strftime('%Y%m%d%H%M%S')

    out = cv2.VideoWriter(str_time + ".avi", fourcc, thread.fps, (thread.img_width, thread.img_height))

    interval = 1 / thread.fps   # 毫秒为单位
    print(interval)

    while not thread_exit:
        if ready:
            start = time.time()
            print(start)

            print(q.qsize())
            q_start = time.time()
            frame = q.get()
            q_end = time.time()
            qt = (round(q_end * 1000) - round(q_start * 1000)) / 1000
            print("队列获取数据耗时:{}".format(qt))

            cv2.imshow('Video', frame)

            start_time = time.time()
            out.write(frame)  # 保存视频
            end_time = time.time()
            tt = (round(end_time * 1000) - round(start_time * 1000)) / 1000
            print("写耗时:{}".format(tt))

            end = time.time()
            print(end)

            t = (round(end * 1000) - round(start * 1000)) / 1000
            print(t)
            # if t < interval:
            #     time.sleep(interval-t)

            if cv2.waitKey(1) & 0xFF == ord('q'):
                thread_exit = True
                out.release()
                cv2.destroyAllWindows()


        else:
            continue

    thread.join()



if __name__ == "__main__":
    main()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容