如何在一段时间后停止 OpenCV 视频 capturing/recording 并在 tkinter 中重新开始

how to stop OpenCV video capturing/recording after sometime and start again in tkinter

我有一个 python 应用程序,其中有很多 frames/pages 当我转到某个相机页面时,我启动了相机,20 秒后我想关闭视频录制并开始录制新文件。基本上将录音分成 20 秒的视频。 现在我有一个 stop_20_seconds() 函数,它会在 20 秒后停止录制并重新开始。但是现在我得到了一个接一个没有 20 秒中断的多个文件。

我的代码

class FrontCameraPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        self.label = tk.Label(self, text="FRONT CAMERA", font=MediumFont, bg="white").grid(row=0, column=0, columnspan=2, sticky="nsew")
        self.cameraFrame = tk.Frame(self, bg=gray)
        self.cameraFrame.grid(row=1, column=0, sticky="nsew")
        self.buttonFrame = tk.Frame(self, bg="white")
        self.buttonFrame.grid(row=1, column=1, sticky="nsew", padx=(10, 0))


        self.endTrip = tk.Button(self.buttonFrame, text="END TRIP", font=small_Font, bg=dark_blue, fg="White")
        self.endTrip.grid(row=2, column=0, ipadx=10, pady=(0, 5))
        self.endTrip['command'] = self.stop_capture

        self.cancelButton = tk.Button(self.buttonFrame, text="Cancel", font=small_Font, bg=dark_blue, fg="white")
        self.cancelButton.grid(row=3, column=0, ipadx=10)
        self.cancelButton['command'] = lambda: controller.show_frame(changepage)

        # setup callbacks for switching in and out events
        self.bind('<<SwitchIn>>', self.start_capture)
        self.bind('<<SwitchOut>>', self.stop_capture)

        self.capture = None 
        self.lmain = tk.Label(self.cameraFrame)
        self.lmain.pack()

    def start_capture(self, event=None):
        if self.capture is None:
            #-----------------------------------
            width, height = 200, 200
            self.cap = cv2.VideoCapture(0)
            self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
            self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
            width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            self.fourcc = cv2.VideoWriter_fourcc(*'XVID')
            self.out = cv2.VideoWriter('output.avi', self.fourcc, 25, (width, height))       
            #-----------------------------------
            self.timer =0
            self.show_frame()
            print('capture started')


    def stop_capture(self, event=None):
        if self.capture:
            self.after_cancel(self.capture)
            #-----------------------------------
            self.cap.release()
            self.out.release()
            #------------------------------
            self.capture = None
            print('capture stopped')


    def show_frame(self):
        ret, frame = self.cap.read()
        if ret:
            self.out.write(frame)
            frame = cv2.flip(frame, 1)
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
            img = Image.fromarray(cv2image)
            self.imgtk = ImageTk.PhotoImage(image=img)
            self.lmain.configure(image=self.imgtk)

        self.stop20Sec =  self.after(5000, self.stop_20_seconds)
        self.capture = self.after(10, self.show_frame)
    
    def stop_20_seconds(self):
        self.timer += 1
        self.out.release()
        width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        self.out = cv2.VideoWriter(f'{str(self.timer)}.avi', self.fourcc, 25, (width, height))
        self.after_cancel(self.capture)
        self.after_cancel(self.stop20Sec)
        self.stop20Sec = None
        self.capture = None
        self.show_frame()

其实你不需要stop_20_seconds()。只需在 show_frame():

内进行输出文件旋转
    def start_capture(self, event=None):
        if self.capture is None:
            #-----------------------------------
            width, height = 200, 200
            self.cap = cv2.VideoCapture(0)
            self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
            self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
            # used instance variables for width and height
            self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            # initialize file rotation and frame counters
            self.timer = 1    # file rotation counter
            self.frames = 0   # frame counter
            self.fourcc = cv2.VideoWriter_fourcc(*'XVID')
            self.out = cv2.VideoWriter(f'{self.timer}.avi', self.fourcc, 25, (self.width, self.height))       
            #-----------------------------------
            self.show_frame()
            print('capture started')

    ...

    def show_frame(self):
        ret, frame = self.cap.read()
        if ret:
            self.out.write(frame)
            self.frames += 1   # update frame counter
            if self.frames >= 500:    # reach 20 seconds (20x25 frames)
                self.out.release()    # close current output file
                self.timer += 1       # update file rotation counter
                self.out.open(f'{self.timer}.avi', self.fourcc, 25, (self.width, self.height))
                self.frames = 0       # reset frame counter
            frame = cv2.flip(frame, 1)
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
            img = Image.fromarray(cv2image)
            self.imgtk = ImageTk.PhotoImage(image=img)
            self.lmain.configure(image=self.imgtk)

        self.capture = self.after(10, self.show_frame)

注意:由于实际采集帧率与输出帧率不同,因此每个视频文件中的实际时间段可能不是实时的20秒。