I have the code to get images from the video stream of a laptop camera. I want to reduce the photo saving interval to one photo per minute. The original code looks like this
# Importing all necessary libraries
import cv2
import os
# Read the video from specified path
cam = cv2.VideoCapture(0)
try:
# creating a folder named data
if not os.path.exists('data'):
os.makedirs('data')
# if not created then raise error
except OSError:
print ('Error: Creating directory of data')
# frame
currentframe = 0
while(True):
# reading from frame
ret,frame = cam.read()
if ret:
# if video is still left continue creating images
name = './data/frame' + str(currentframe) + '.jpg'
print ('Creating...' + name)
# writing the extracted images
cv2.imwrite(name, frame)
# increasing counter so that it will
# show how many frames are created
currentframe += 1
else:
break
# Release all space and windows once done
cam.release()
cv2.destroyAllWindows()
For this task I try to use the parameter CAP_PROP_POS_MSEC
[...]
# Read the video from specified path
cam = cv2.VideoCapture(0)
cam.set(cv2.CAP_PROP_POS_MSEC,20000)
[...]
while(True):
[...]
# writing the extracted images
cv2.imwrite(name, frame)
cv2.waitKey()
[...]
But, the saving speed remains the same and I see the following error
videoio error v4l2 property pos_msec is not supported
I use Ubuntu 18.04, Python 3.7, and OpenCV 4.1.
Where do I have a mistake, and whether I chose the right way to minimize the load on my computer's resources?
UPD
Using the recommendation of J.D. this code is working
import cv2
import os
import time
prev_time = time.time()
delay = 1 # in seconds
# Read the video from specified path
cam = cv2.VideoCapture(0)
currentframe = 0
while (True):
# reading from frame
ret, frame = cam.read()
if ret:
if time.time() - prev_time > delay:
# if video is still left continue creating images
name = './data/frame' + str(currentframe) + '.jpg'
print('Creating...' + name)
# writing the extracted images
cv2.imwrite(name, frame)
currentframe += 1
prev_time = time.time()
else:
break
EDIT: this answer is not a good solution - due to the frame buffer, as described in the comments. Because of the relevant information in the comments I will leave the answer.
If you don't plan to expand the code to do other things, you can just use the waitkey:
cv2.waitKey(60000) will freeze code execution 60 secs.
If you want to expand the code, you have to create a time based loop:
import time
prev_time = time.time()
count = 0
delay = 1 # in seconds
while True:
if time.time()-prev_time > delay:
count += 1
print(count)
prev_time = time.time()
you should read separately and set the values again for each image, like this:
while(True):
cam = cv2.VideoCapture(0)
cam.set(cv2.CAP_PROP_POS_MSEC,20000)
# reading from frame
ret,frame = cam.read()
if ret:
# if video is still left continue creating images
name = './data/frame' + str(currentframe) + '.jpg'
print ('Creating...' + name)
# writing the extracted images
cv2.imwrite(name, frame)
cv2.waitKey()
# increasing counter so that it will
# show how many frames are created
currentframe += 1
else:
break
cam.release()
and check your openCV version for python about the error.
Related
This is the process I am trying to achieve :
Live Stream is captured from webcam and the Image frames are stored in a particular folder.
Now, If I give a trigger the frames in that folder at that moment should be converted into a video and get saved with name eg. video1.mp4.
Now again if I press a trigger another video should be saved as video2.mp4.
I have attached the code here . If I press R , it is saving one video as a0.mp4 . But If I press again, nothing seems to happen.
def frametovideo(img_array):
for i in range(len(img_array)):
out.write(img_array[i])
if name == "main":
img_array = []
videono = 0
cap = cv2.VideoCapture(0)
for filename in glob.glob('./output/*.jpg'):
img = cv2.imread(filename)
height, width, layers = img.shape
size = (width,height)
img_array.append(img)
path = 'a' + str(videono) + '.mp4'
out = cv2.VideoWriter(path,cv2.VideoWriter_fourcc(*'mp4v'), 15, size)
while True:
ret, frame = cap.read()
if ret == True:
cv2.imshow("frame",frame)
k = cv2.waitKey(5) & 0xff
if k == ord('r'):
frametovideo(img_array)
videono += 1
You do not understanding how to save filenames Do not used operator. Used string format python 3.8 or later. Try this in below. Actually, you can modified key press.
import cv2
import os
i = 1
wait = 0
video = cv2.VideoCapture(0)
while video.isOpened():
ret, img = video.read()
cv2.imshow('live video', img)
# wait for user to press any key
key = cv2.waitKey(100)
# wait variable is to calculate waiting time
wait = wait+100
if key == ord('q'):
break
# when it reaches to 5000 milliseconds
# we will save that frame in given folder
if wait == 5000:
filename = f'Frame_{str(i)}.jpg'
# Save the images in given path
cv2.imwrite(filename, img)
i += 1
wait = 0
# close the camera
video.release()
# close open windows
cv2.destroyAllWindows()
Btw,look in line #27-34. Correct way to do this if __name__ == "__main__":
Have attached the source code for frame differentiating and storing the differentiated frames in a specified place but am getting an error in indentation of error..post this problem as a question in stack overflow..i am restricted to question for a particular period..upload the code too
filename.py
import cv2
import os
import glob
def extractFrames(pathIn, pathOut):
os.mkdir(pathOut)
cap = cv2.VideoCapture(pathIn)
count = 0
while (cap.isOpened()):
# Capture frame-by-frame
ret, frame = cap.read()
current_frame_gray = cv2.cvtColor(current_frame, cv2.COLOR_BGR2GRAY)
previous_frame_gray = cv2.cvtcolor(previous_frame, cv2.COLOR_BGR2GRAY)
frame_diff = cv2.absdiff(current_frame_gray,previous_frame_gray)
if ret == True:
print('Read %d frame: ' % count, ret)
cv2.imwrite(os.path.join(pathOut, "frame{:d}.jpg".format(count)), frame_diff) # save frame as JPEG file
count += 1
else:
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
def main():
extractFrames('C:/Users/yaazmoha/Desktop/BE PROJECT/INPUT/Tiger in field(1080P_HD).mp4', 'fd3')
if __name__=="__main__":
main()
Fixed your code. You had some indentation errors. Since Python does not use braces like C++, it requires proper indentation to separate code.
import cv2
import os
import glob
def extractFrames(pathIn, pathOut):
os.mkdir(pathOut)
cap = cv2.VideoCapture(pathIn)
count = 0
while (cap.isOpened()):
# Capture frame-by-frame
ret, current_frame = cap.read()
current_frame_gray = cv2.cvtColor(current_frame, cv2.COLOR_BGR2GRAY)
if count > 1:
previous_frame_gray = cv2.cvtcolor(previous_frame, cv2.COLOR_BGR2GRAY)
frame_diff = cv2.absdiff(current_frame_gray,previous_frame_gray)
if ret == True:
print('Read %d frame: ' % count, ret)
cv2.imwrite(os.path.join(pathOut, "frame{:d}.jpg".format(count)), frame_diff) # save frame as JPEG file
count += 1
else:
break
previous_frame = current_frame
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
def main():
extractFrames(r"C:\Users\mathesn\Downloads\Wildlife.mp4", 'fd3')
if __name__=="__main__":
main()
I took liberties to fix other sections of your code. But there are some other fixes that this code needs, like creating a directory only if it doesn't exist, maintaining a colored version of the frame so that cv2.cvtColor() does not fail, etc., but I''ll leave those to you.
Here are my goals.
Capture video continuously until 'q; is pressed
Every ten seconds save the video in created directory file
Continue step two until 'q' is pressed
I am executing the following code. But when creating files it's creating 6kb files and saying cannot play. I am fairly new to opencv and python. Not sure what I am missing. Running this code on pycharm with Python 3.6. Also the
cv2.imshow('frame',frame)
stops after ten seconds but recording is happening in background and files are created.
import numpy as np
import cv2
import time
import os
import random
import sys
fps=24
width=864
height=640
video_codec=cv2.VideoWriter_fourcc('D','I','V','X')
name = random.randint(0,1000)
print (name)
if (os.path.isdir(str(name)) is False):
name = random.randint(0,1000)
name=str(name)
name = os.path.join(os.getcwd(), str(name))
print('ALl logs saved in dir:', name)
os.mkdir(name)
cap = cv2.VideoCapture(0)
ret=cap.set(3, 864)
ret=cap.set(4, 480)
cur_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
start=time.time()
video_file_count = 1
video_file = os.path.join(name, str(video_file_count) + ".avi")
print('Capture video saved location : {}'.format(video_file))
while(cap.isOpened()):
start_time = time.time()
ret, frame = cap.read()
if ret==True:
cv2.imshow('frame',frame)
if (time.time() - start > 10):
start = time.time()
video_file_count += 1
video_file = os.path.join(name, str(video_file_count) + ".avi")
video_writer = cv2.VideoWriter(video_file,video_codec, fps,(int(cap.get(3)),int(cap.get(4))))
time.sleep(10)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break
cap.release()
cv2.destroyAllWindows()
I want files with the recorded videos. Files are generated but size 6kb and nothing is being recorded.
You're almost there! Given that I understood what your goal is, and with minimal change to your code, here is what worked for me.
This writes a new video file every ten seconds while recording each frame into the current video.
import numpy as np
import cv2
import time
import os
import random
import sys
fps = 24
width = 864
height = 640
video_codec = cv2.VideoWriter_fourcc("D", "I", "V", "X")
name = random.randint(0, 1000)
print(name)
if os.path.isdir(str(name)) is False:
name = random.randint(0, 1000)
name = str(name)
name = os.path.join(os.getcwd(), str(name))
print("ALl logs saved in dir:", name)
os.mkdir(name)
cap = cv2.VideoCapture(0)
ret = cap.set(3, 864)
ret = cap.set(4, 480)
cur_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
start = time.time()
video_file_count = 1
video_file = os.path.join(name, str(video_file_count) + ".avi")
print("Capture video saved location : {}".format(video_file))
# Create a video write before entering the loop
video_writer = cv2.VideoWriter(
video_file, video_codec, fps, (int(cap.get(3)), int(cap.get(4)))
)
while cap.isOpened():
start_time = time.time()
ret, frame = cap.read()
if ret == True:
cv2.imshow("frame", frame)
if time.time() - start > 10:
start = time.time()
video_file_count += 1
video_file = os.path.join(name, str(video_file_count) + ".avi")
video_writer = cv2.VideoWriter(
video_file, video_codec, fps, (int(cap.get(3)), int(cap.get(4)))
)
# No sleeping! We don't want to sleep, we want to write
# time.sleep(10)
# Write the frame to the current video writer
video_writer.write(frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
else:
break
cap.release()
cv2.destroyAllWindows()
A sign that the videos are being received at 6 kb, an error with the codec. You need to download opencv_ffmpeg.dll and place it in the Python3.2.1 folder and renamed to opencv_ffmpeg321.dll
This solved the problem for me, and before that, 5.6 kb videos were created, regardless of what I do. But the problem is deeper than it seems, it can still be connected with a mismatch in the resolution of the stream and the recording.
For OpenCV version X.Y.Z
opencv_ffmpeg.dll ==> opencv_ffmpegXYZ.dll
For 64-bit version of OpenCV X.Y.Z
opencv_ffmpeg.dll ==> opencv_ffmpegXYZ_64.dll
I tried to capture (stereo) images with Python's opencv and two cameras so therefore every 5 seconds an image should be saved. But the problem here is that an old frame is saved.
The minified code is as follows:
cap = cv2.VideoCapture(0)
for i in range(20):
time.sleep(5)
print "Taking image %d:" % i
ret, frame = cap.read()
cv2.imwrite("image %d" % i, frame)
print " image done." if ret else " Error while taking image..."
cap.release()
cv2.destroyAllWindows()
To check this, I changed the position of the camera after each taken image. But nevertheless an image from an old position is saved (actually not the same, but I assume some frames after the last saved image). After 5 (or more) images finally the captured location in the image does also change.
So, is there any problem with time.sleep? I guess that I'm not getting the actual frame, but a buffered one. If this is the case, how could I fix it and capture the actual frame?
VideoCapture is buffering.
If you always want the actual frame, do this:
while True:
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
cap.release()
cv2.imshow(" ", frame)
if cv2.waitKey(2000) != -1:
break
you need to count the elapsed time, but not stop read frames. like this:
import cv2
import time
cap = cv2.VideoCapture(0)
preframe_tm = time.time()
i = 0
while True:
ret, frame = cap.read()
elapsed_time = time.time() - preframe_tm
if elapsed_time < 5:
continue
preframe_tm = time.time()
i += 1
print("Taking image %d:" % i)
cv2.imwrite("image_%d.jpg" % i, frame)
if i >= 20:
break
cap.release()
cv2.destroyAllWindows()
__author__ = 'Kyle'
HELP_MESSAGE = """Script: frame_grabber.py
Usage: python frame_grabber.py path/to/video
Requires: OpenCV 2.4.8+
Purpose: Select and save frames from a given video.
Commands:
Key Function
a previous frame
d next frame
q exit
SHIFT + a skip 10 frames forward
SHIFT + d skip 10 frames backwards
s saves current frame
dbl click saves current frame
Controls:
Slider Navigate through the video
"""
# Check if the user has provided a path to a file
# otherwise display the HELP_MESSAGE
import sys
import time as t
# Check if OpenCV module is present
# otherwise stop the application
try:
import cv2
except ImportError as e:
print "Fatal Error: Could not import OpenCV, ", e
exit(-1)
else:
print "Using OpenCV ", cv2.__version__
# these flags may depend on your opencv version:
# in opencv 3.0.0 these flags are implemented as
# cv2.CAP_PROP_POS_FRAMES and
# cv2.CAP_PROP_FRAME_COUNT
CURRENT_FRAME_FLAG = cv2.cv.CV_CAP_PROP_POS_FRAMES
TOTAL_FRAMES_FLAG = cv2.cv.CV_CAP_PROP_FRAME_COUNT
WIN_NAME = "Frame Grabber"
POS_TRACKBAR = "pos_trackbar"
#VIDEO_PATH = 'camera13.h264'
#try:
# VIDEO_PATH = sys.argv[1]
# except IndexError as e:
# print HELP_MESSAGE
# exit(-1)
cap = cv2.VideoCapture("camera13.h264")
if not cap.isOpened():
print "Fatal Error: Could not open the specified file."
exit(-1)
ret, frame = cap.read()
if not ret:
print "Fatal Error: Could not read/decode frames from specified file."
exit(-1)
def dummy():
pass
def save_image():
filename = "image_%0.5f.png" % t.time()
cv2.imwrite(filename, frame)
def seek_callback(x):
global frame
i = cv2.getTrackbarPos(POS_TRACKBAR, WIN_NAME)
cap.set(CURRENT_FRAME_FLAG, i-1)
_, frame = cap.read()
cv2.imshow(WIN_NAME, frame)
def mouse_callback(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDBLCLK:
save_image()
def skip_frame_generator(df):
def skip_frame():
global frame
cf = cap.get(CURRENT_FRAME_FLAG) - 1
cap.set(CURRENT_FRAME_FLAG, cf+df)
cv2.setTrackbarPos(POS_TRACKBAR, WIN_NAME, int(cap.get(CURRENT_FRAME_FLAG)))
_, frame = cap.read()
return skip_frame
cv2.namedWindow(WIN_NAME)
cv2.createTrackbar(POS_TRACKBAR, WIN_NAME, 0, int(cap.get(TOTAL_FRAMES_FLAG)), seek_callback)
cv2.setMouseCallback(WIN_NAME, mouse_callback)
actions = dict()
actions[ord("D")] = skip_frame_generator(10)
actions[ord("d")] = skip_frame_generator(1)
actions[ord("a")] = skip_frame_generator(-1)
actions[ord("A")] = skip_frame_generator(-10)
actions[ord("q")] = lambda: exit(0)
actions[ord("s")] = save_image
while True:
cv2.imshow(WIN_NAME, frame)
key = cv2.waitKey(0) & 0xFF
actions.get(key, dummy)()``
When the code is executed, I receive the error - OverflowError: Python int too large to convert to C long. How may I go about solving this please? This code is meant to create a frame grabber that allows me to pick a frame that I want and save it.
Well, the error is telling you that you are passing a number in this line:
cv2.createTrackbar(POS_TRACKBAR, WIN_NAME, 0,int(cap.get(TOTAL_FRAMES_FLAG)), seek_callback)
That is too large to be cast to ctypes.c_long; The maximum number that can be represented by this type is (2**31)-1 = 2147483647.
This is presumably your TOTAL_FRAMES_FLAG. Now that's a lot of frames (approx. 500 days #50fps), so it can't be right.
I notice that you are getting this value before you open the video file, so it's likely to be undefined at that point. Try doing it after you've opened your file, and see if that fixes things. You could print the value, as well, to see if it is indeed too large (before you try the suggested change).