2 years ago

#37862

test-img

tlars25

Python cv2 VideoCapture() in daemon thread turns black randomly

I have a webcam that captures something I want to be able to monitor live. Every 30 seconds an operation needs to be done on the most recent frame of the video. To do this, I've set up a cv2.NamedWindow() in a daemon thread that allows me to see the webcam feed. Then using a scheduler, I can do the operation I need every 30 seconds. The problem is, the camera feed will randomly go black, and save an all black image. Sometimes this happens after 15 minutes of operation, sometimes after 4 hours of operation., so it's very inconsistent and there seems to be no pattern. Here is the code:

import time
import gc
import cv2
import threading
import schedule
import numpy

class operator():
    def __init__(self):
        print("Start")
        
    def start_cam(self):
        is_blurry = True
        while is_blurry == True:
            print("Start of start_cam")
            self.camera = cv2.VideoCapture(0, cv2.CAP_DSHOW)                                                 # Make camera object with correct format, size, and autofocus. All of these appear to be necessary to get 4K resolution in OpenCV
            self.camera.set(cv2.CAP_PROP_FRAME_WIDTH, 3840)  #3840
            self.camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 2160)  #2160
            self.camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter.fourcc('m','j','p','g'))
            self.camera.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter.fourcc('M','J','P','G'))                   
            self.camera.set(cv2.CAP_PROP_AUTOFOCUS, 1)
            self.camera.set(cv2.CAP_PROP_FPS, 60)

            i=10
            while i>0:                                                                                 # Takes 10 pictures and waits 1 sec in between each. This is done to make sure it has time to autofocus
                i=i-1
                cv2.waitKey(1000) #1000
                ret, self.frame = self.camera.read()
                if ret != True:
                    print("image error")                                                                # Check to make sure it actually took a picture and didn't fail
                    if ret != True and i == 1:
                        restart_loop = True
                        print("Image error, restarting loop")                                           #
                    continue
            laplace = cv2.Laplacian(self.frame, cv2.CV_64F).var()                                       # Checks if image is blurry. If it is, it restarts the camera.
            print(laplace)
            if laplace < 20:                        # Blurry
                print("Blurry image, reinitializing")
                self.camera.release()
                del(self.camera)
                cv2.destroyAllWindows()
                gc.collect()
                time.sleep(2)
            else:
                print("image not blurry")
                break


    def live_loop(self):
        loop_bool = True
        cv2.namedWindow("Live Feed", cv2.WINDOW_NORMAL)     # Creates a normal window so that the 4k image is scaled correctly for the live feed (I have a relatively small monitor)
        while loop_bool == True:
            ret, self.frame = self.camera.read()
            if ret != True:
                print("image error")
                break
            self.frame_rotate = cv2.rotate(self.frame, cv2.ROTATE_180)
            cv2.imshow("Live Feed", self.frame_rotate)
            k = cv2.waitKey(10)
        
        gc.collect()

    def data_operation(self):
        print("Start data operation")
        imgpath = "path where image is saved"
        cv2.imwrite(imgpath, self.frame)
        t = time.localtime()                                                                # If the image can't be read as a number, saves the image with a timestamp so that it can be examined later
        timestamp = time.strftime('%b-%d-%Y_%H;%M', t)
        print(timestamp)


if __name__== "__main__":
    op = operator()
    op.start_cam()
    x = threading.Thread(target=op.live_loop, daemon = True)
    x.start()
    schedule.every(30).seconds.do(op.data_operation)
    try:
        while True:
            schedule.run_pending()
            time.sleep(1)
    except KeyboardInterrupt:
        print("Ended Program")

    gc.collect()

This is the smallest amount of code I can reproduce the issue with. There is more going on just this, but this code block has been run and the issue persists. I have found that when I remove the line that saves the image in the data_operation function, which is the cv2.imwrite(imgpath, self.frame), the program seems to work and the camera never goes black and the issue is gone. So I think that is the problem, but I don't know how else to save the frame as an image every 30 seconds while also keeping the live feed up and running. As far as I know, the rest of the code works perfectly except for this, and I do need to be able to save and access the image.

I am not a programmer by trade so I've just been figuring this out as I go. If something is glaringly stupid let me know. If there is a much easier/safer way to display a live webcam feed while also automatically grabbing a picture every 30 seconds and doing stuff with that picture while keeping the live loop going, I would be happy to hear that too.

Any help would be appreciated, and let me know if you have further questions.

EDIT: I have updated the code to simplify and continue shrinking the smallest reproduceable sample. I realized the start_cam function was unnecessary, as all of the cv2 initialization can be done at the start of the live_loop function. The issue persists. I have tried adding a mutex, however that seems to not help (though it's possible I am implementing it wrong). Any info or working examples would be greatly appreciated.

EDIT 2: Another thing to note is that the exact same program running on my laptop (using the built-in webcam rather than the 4k Logitech Brio) doe not seem to have the same issue. Is the frame rate or resolution causing the issue perhaps? Testing is difficult since I need to keep a working program running while working on this one, but I will do more tests when I am able and keep this post updated if anything develops. Still, feel free to toss any ideas you have to me.

python

multithreading

image

opencv

webcam

0 Answers

Your Answer

Accepted video resources