pythonopencvvideoavi

cv2 motion capture, .avi files are stretched - Python


So Im trying to make my webcam capture motion, and I how to register motion is going fine but with all these nested while loops my save files are dragged to 30 min long and not 10 sec like intended. I can't figure out what Im doing wrong.

What I mean by dragged out is that I record for 10 sec, but when I go into my files to review the footage it's 30 mins of just some frames.

The idea is to make it register motion and then record for 10 seconds and save the recording as a .avi file.

import cv2
from datetime import datetime
import time


vid_capture = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc(*'XVID')

ret, cur_frame = vid_capture.read()
prev_frame = cur_frame

capture_duration = 10
motion = False

while True:    
    frame_diff = cv2.absdiff(cv2.cvtColor(cur_frame, cv2.COLOR_BGR2GRAY), cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY))

    if frame_diff.max() > 150:
        motion = True

    if motion:
        start_time = time.time()
        name = str(datetime.now().date()) + "_" + str(datetime.now().time().hour) + "-" + str(datetime.now().time().minute) + "-" + str(datetime.now().time().second) + ".avi"
        out = cv2.VideoWriter(name, fourcc, 20.0, (640,480))
        ret, cur_frame = vid_capture.read()

        while time.time() - start_time < capture_duration:
            if ret:
                out.write(cur_frame)
                cv2.imshow('Input', cur_frame)

            else:
                break
        
        out.release()
        cv2.destroyAllWindows()

    motion = False

    prev_frame = cur_frame.copy()
    ret, cur_frame = vid_capture.read()

    if cv2.waitKey(1) == 27:
        break
    
vid_capture.release()

Solution

  • Your code may run very fast and it may create ie. 100 frames every second.

    But 20.0 in VideoWriter doesn't write it video with speed 20 frames per second but it only inform players that they have to display 20 frames per second. But if you create 100 frames per second so finally it wll need 5 seconds instead of 1 second to display it (100frames/20FPS = 5seconds).

    You have to slow down to create new frame every 50ms - (1000ms/20FPS) - you can try waitKey(50).

    EDIT:

    Because code may need some time to create frame so it may need little smaller dealy in waitKey - i.e. 48 - or you may try to measure time inside loop and use

    waitKey( 50 - (loop_end-loop_start) )
    

    You run inner while-loop which all time write the same frame - you should use ret, cur_frame = vid_capture.read() inside this while-loop


    Shorter:

    name = datetime.now().strftime("%Y-%m-%d_%H-%M-%S.avi")
    
    motion = (frame_diff.max() > 150) 
    

    I see one possible problem: cv2.waitKey not work when window is closed - because system sends keys/mouse events only to active window, and when window is closed then cv2 may not get keys/mouse events.


    from datetime import datetime
    import time
    import cv2
    
    vid_capture = cv2.VideoCapture(0)
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    
    ret, cur_frame = vid_capture.read()
    prev_frame = cur_frame
    
    capture_duration = 10
    
    while True:    
        frame_diff = cv2.absdiff(cv2.cvtColor(cur_frame, cv2.COLOR_BGR2GRAY), cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY))
    
        motion = (frame_diff.max() > 150) 
    
        if motion:
            name = datetime.now().strftime("%Y-%m-%d_%H-%M-%S.avi")
            print(name)
            
            out = cv2.VideoWriter(name, fourcc, 20.0, (640,480))
    
            start_time = time.time()
            while time.time() - start_time <= capture_duration:
                ret, cur_frame = vid_capture.read()
                if ret:
                    out.write(cur_frame)
                    cv2.imshow(name, cur_frame)
                    if cv2.waitKey(50) == 27:
                        break
                else:
                    break
            
            out.release()
            cv2.destroyAllWindows()
    
        prev_frame = cur_frame.copy()
        ret, cur_frame = vid_capture.read()
    
        if cv2.waitKey(1) == 27:
            break
        
    vid_capture.release()