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()

你可能感兴趣的:(python-opencv摄像头读取,保存视频)