cv2 motion capture, .avi files are stretched - Python - 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()

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

Related

cv2 module capturing frame from video every 5 seconds

I have been trying to capture a frame(screenshot) after every 5 seconds from a live .webm video stream which is constantly increasing in duration.
import cv2
import time
TIMER = int(5)
k=0
cap = cv2.VideoCapture('video.webm')
capture=int(1)
while True:
ret, img = cap.read()
cv2.imshow('a', img)
if ret:
prev = time.time()
while TIMER >= 0:
ret, img = cap.read()
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, str(TIMER),
(200, 250), font,
7, (0, 255, 255),
4, cv2.LINE_AA)
cv2.imshow('a', img)
cv2.waitKey(125)
cur = time.time()
if cur-prev >= 1:
prev = cur
TIMER = TIMER-1
if TIMER == 0:
ret, img = cap.read()
cv2.imshow('a', img)
cv2.waitKey(2000)
cv2.imwrite('camera' + str(capture) + '.jpg', img)
capture=capture+1
TIMER = int(5)
else:
break
cap.release()
cv2.destroyAllWindows()
Here my code captures the frame after every 5 frames, I'm not able to figure out the framerate calculation and making it work to capture frames after every 5 seconds rather than 5 frames.
If you want to utilize the fps information of the video an approach might be:
import cv2
cap = cv2.VideoCapture(r'path/to/vid')
fps = int(cap.get(cv2.CAP_PROP_FPS))
save_interval = 5
frame_count = 0
while cap.isOpened():
ret, frame = cap.read()
if ret:
frame_count += 1
if frame_count % (fps * save_interval) == 0:
cv2.imwrite(r'path/to/save/to')
# optional
frame_count = 0
# Break the loop
else:
break
cap.release()
cv2.destroyAllWindows()

Video Recording is too fast in opencv

I'm capturing video from my video file using OpenCV on linux. It works fine but when I try to play my captured video it plays too fast. i.e. I capture from video for 10 seconds but when I play on the video is 8 seconds.
Video capture function
def save_frames(video_file, path_in):
cap = cv2.VideoCapture(video_file)
length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap.get(cv2.CAP_PROP_FPS)
count = 1
while cap.isOpened():
ret, frame = cap.read()
cv2.imwrite(path_in + "frame{}.jpg".format(str(count).zfill(5)), frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
count += 1
cap.release()
cv2.destroyAllWindows()
return fps
Video writer function
def make_video(path_out, outvid, fps=25, size=None, is_color=True,format='mp4v'):
images = [f for f in os.listdir(path_out) if ".png" in f]
from cv2 import VideoWriter, VideoWriter_fourcc, imread, resize
fourcc = VideoWriter_fourcc(*'mp4v')
vid = None
count = 0
for image in images:
if not os.path.exists(path_out + image):
raise FileNotFoundError(image)
img = imread(path_out + image)
if vid is None:
if size is None:
size = img.shape[1], img.shape[0]
vid = VideoWriter(outvid, fourcc, float(fps), size, is_color)
if size[0] != img.shape[1] and size[1] != img.shape[0]:
img = resize(img, size)
vid.write(img)
if count % 100 == 0:
print("Progress: %0.2f%%" % (count / len(images) * 100,), flush=True)
vid.release()
return
I tried different fps (30,25,15,10,5) .These fps didn't works me and for all fps 600 frame captures on 10 sec video .The cv2.CAP_PROP_FPS default capture 30 fps. When I try to change the fps but the frame rate didn't change .please let me know why this happens .Any answers welcome.
1)The problem here is cv2.CAP_PROP_FPS default fps is 30. So, it saves 600 images for 20sec video. To solve this use if count%(1*fps) == 0: before the cv2.imwrite .It saves one frame for each second.
2)Then set the fps = 1 in VideoWriter object. After that video is in normal speed.

Trigger Event using key press?

I am trying to make my program take 8 pictures only when a key is pressed but I cant find a solution for it. I tried the keyboard package and whenever I would do key is pressed it would throw error.
By the way here is my code-
import numpy as np
import cv2
import time
cap = cv2.VideoCapture('morning.mp4')
img_counter = 0
start_time = time.time()
x=0
while(cap.isOpened()):
ret, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame',gray)
if cv2.waitKey(47) & 0xFF == ord('q'):
break
#here is where the image capturing comes into play
if time.time() - start_time > 14: #<---- Check if 15 sec passed
img_name = "frame_{}.png".format(x)
cv2.imwrite(img_name, frame)
print("{} written!".format(img_counter))
start_time = time.time()
x += 1
img_counter += 1
if x == 8:
break
cap.release()
cv2.destroyAllWindows()
After the comment "#here is where the image capturing comes into play" until the break statement is where I take pictures of the video and I want to trigger that part of code with a keypress. Any idea how I should go about it?
The issue is that you break while loop after keypress. You should use some boolean variable to point when script should capture images.
Code:
import numpy as np
import cv2
import time
cap = cv2.VideoCapture('morning.mp4')
img_counter = 0
start_time = time.time()
x=0
capture_images = False
while(cap.isOpened()):
ret, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame',gray)
if cv2.waitKey(47) & 0xFF == ord('q'):
capture_images = True
if capture_images:
#here is where the image capturing comes into play
if time.time() - start_time > 14: #<---- Check if 15 sec passed
img_name = "frame_{}.png".format(x)
cv2.imwrite(img_name, frame)
print("{} written!".format(img_counter))
start_time = time.time()
x += 1
img_counter += 1
if x == 8:
break
cap.release()
cv2.destroyAllWindows()

Extract a frame at a specific time of a video and insert a text OpenCV Python

So i have a video with a duration of 15 seconds and at a specific time i need to insert a text field.
Until now my code just reads a video and displays it. After that we extract frames and calculate the duration of each frame.
import cv2
import numpy as np
# Create a VideoCapture object and read from input file
# If the input is the camera, pass 0 instead of the video file name
cap = cv2.VideoCapture('my_baby_dog.mp4')
# Check if camera opened successfully
if (cap.isOpened() == False):
print("Error opening video stream or file")
# Read until video is completed
while (cap.isOpened()):
# Capture frame-by-frame
ret, frame = cap.read()
if ret == True:
fps = cap.get(cv2.CAP_PROP_FPS) # OpenCV2 version 2 used "CV_CAP_PROP_FPS"
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
duration = frame_count / fps
print('fps = ' + str(fps))
print('number of frames = ' + str(frame_count))
print('duration (S) = ' + str(duration))
minutes = int(duration / 60)
seconds = duration % 60
print('duration (M:S) = ' + str(minutes) + ':' + str(seconds))
# Display the resulting frame
frameWidth = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
frameHeight = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
cv2.putText(img=frame, text='EKO', org=(int(frameWidth / 2 - 20), int(frameHeight / 2)),
fontFace=cv2.FONT_HERSHEY_DUPLEX, fontScale=3,
color=(0, 255, 0))
cv2.imshow('Frame', frame)
# Press Q on keyboard to exit
if cv2.waitKey(25) & 0xFF == ord('q'):
break
# Break the loop
else:
break
# When everything done, release the video capture object
cap.release()
# Closes all the frames
cv2.destroyAllWindows()
It sounds like you want to add a text overlay after 6 seconds. Assuming this overlay will continue until the video is finished, you will want to add an if statement to compare the duration to the start time of your text overlay.
then display it.
import cv2
import numpy as np
start_time = 6
# Create a VideoCapture object and read from input file
# If the input is the camera, pass 0 instead of the video file name
cap = cv2.VideoCapture('my_baby_dog.mp4')
........
while (cap.isOpened()):
........
# ADD OVERLAY TEXT
if start_time < duration:
cv2.putText(img=frame, text='EKO', org=(int(frameWidth / 2 - 20), int(frameHeight / 2)),
fontFace=cv2.FONT_HERSHEY_DUPLEX, fontScale=3,
color=(0, 255, 0))
cv2.imshow('Frame', frame)
Likewise you could start and stop the text overlay like this
import cv2
import numpy as np
start_time = 6
stop_time = 10
........
# ADD OVERLAY TEXT
if start_time < duration < stop_time:
cv2.putText(img=frame, text='EKO', org=(int(frameWidth / 2 - 20), int(frameHeight / 2)),
fontFace=cv2.FONT_HERSHEY_DUPLEX, fontScale=3,
color=(0, 255, 0))
.......

Using timer to capture webcam images

How do I get breaks during the image capture? In fact, I need 5sec in between the image.
This code captures around 15-20 images per sec, But I need to take a picture at an interval of 5sec in between images
import cv2
import time
import numpy
capture = cv2.VideoCapture(0)
capture.set(3,640)
capture.set(4,480)
img_counter = 0
frame_set = []
start_time = time.time()
while(True):
ret , frame = capture.read()
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
frame_set.append(gray)
cv2.imshow('frame',gray)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
end_time = time.time()
elapsed = end_time - start_time
img_name = "opencv_frame_{}.png".format(img_counter)
cv2.imwrite(img_name,frame)
print("{} written!".format(img_counter))
img_counter +=1
if elapsed > 1:
capture.release()
cv2.destroyAllWindows()
time.sleep(3)
You need an if statement to check if the desired time has passed. Using the already imported time package you can do this:
import cv2
import time
capture = cv2.VideoCapture(0)
capture.set(3, 640)
capture.set(4, 480)
img_counter = 0
frame_set = []
start_time = time.time()
while True:
ret, frame = capture.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame', gray)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
if time.time() - start_time >= 5: #<---- Check if 5 sec passed
img_name = "opencv_frame_{}.png".format(img_counter)
cv2.imwrite(img_name, frame)
print("{} written!".format(img_counter))
start_time = time.time()
img_counter += 1
The above script will save the frame in RGB every 5 seconds to the working directory.

Categories