Assertion fctx error using Django & Opencv - django

I have a python app with the Django framework. I display a video stream with detection using yolov5, sometimes the stream crashes and it's display this eroor
Assertion fctx->async_lock failed at libavcodec/pthread_frame.c:167
I think that it happens because of my detection() function .
the goal of the detection() function is to send to the client the percent of the detection any time that I have detect when my custom object appears in the video.
cap = cv2.VideoCapture("video.mp4")
def stream():
model.iou=0.5
while (cap.isOpened()):
ret, frame = cap.read()
if not ret:
print("Error: failed to capture image")
break
results = model(frame,augment=False)
for i in results.render():
data=im.fromarray(i)
data.save('demo.jpg')
annotator = Annotator(frame, line_width=2, pil=not ascii)
im0 = annotator.result()
image_bytes = cv2.imencode('.jpg', im0)[1].tobytes()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + image_bytes + b'\r\n')
cap.release()
cv2.destroyAllWindows()
def detection():
if(cap.isOpened()):
ret,frame = cap.read()
if not ret:
print("Erorr:faild to capture image")
data={"isRunning":False}
dataJson=json.dumps(data)
return dataJson
results = model(frame, augment=False)
pred = results.pandas().xyxy[0]
for index, row in pred.iterrows():
if float(row['confidence']) > 0.15:
detection=float(row['confidence'])
det = '%.2f' % detection
data={"det":det,"isRunning":cap.isOpened()}
dataJson=json.dumps(data)
return dataJson
data={"isRunning":cap.isOpened()}
dataJson=json.dumps(data)
return dataJson
data={"isRunning":False}
dataJson=json.dumps(data)
return dataJson
def video_feed(request):
return StreamingHttpResponse(stream(), content_type='multipart/x-mixed-replace; boundary=frame')
def detection_percentage(request):
return HttpResponse(detection())
I think that I need to read the frame from the video with another approach but I am not sure if that is actually the problem.
I'll be grateful for helping me facing with this problem.

Related

Trying to modify a code to capture an image every 15 minutes or so (time lapse)

Below code was taken from an existing post by Kieleth which I use as a subset of my larger codebase. I'm trying to leverage it to capture a list of frames taken once every thousand+ real time frames and later play in a time-lapse fashion. I've captured the frames but can't seem to view them when calling a simple function. I've seen in other posts that for loops are not recommended for this type of event but haven't figured out how to properly display. Any advise on this one would be appreciated?
from tkinter import ttk
import time
import cv2
from PIL import Image,ImageTk
#import threading
root = Tk()
def video_button1():# this flips between start and stop when video button pressed.
if root.video_btn1.cget('text') == "Stop Video":
root.video_btn1.configure(text = "Start Video")
root.cap.release()
elif root.video_btn1.cget('text') == "Start Video":
root.video_btn1.configure(text = "Stop Video")
root.cap = cv2.VideoCapture(0)
show_frame()
def show_frame():
# if video_btn1.cget('text') == "Stop Video":
global time_lapse_counter
ret, frame = root.cap.read()
if ret:
frame = cv2.flip(frame, 1) #flip image
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img) #converts img into tkinter readable format
root.video_label.imgtk = imgtk
if time_lapse_counter >= 20: # for Every 20 frames, capture one into time_lapse_list
time_lapse_list.append(imgtk)
time_lapse_counter = 0
root.video_label.configure(image=imgtk)
if len(time_lapse_list) == 5: # keep only 4 frames in the list *** debug purposes.
time_lapse_list.pop(0)
time_lapse_counter += 1
video_loop = root.after(40, show_frame)
else:
root.video_btn1.configure(text = "Start Video")
def time_lapse_play():
root.cap.release() #stop capturing video.
for image in time_lapse_list:
print (image, " ", len(time_lapse_list)," ",time_lapse_list) #
#*** I see the print of the pyimagexxx but nothing appears on the video***#
imgtk = image
root.video_label.imgtk = imgtk
root.video_label.configure(image=imgtk)
cv2.waitKey(500)
# video_loop = root.after(500, time_lapse_play)
def setup_widgets(): #simple label and 2 button widget setup
#Setup Top Right Window with pictures
f_width, f_height = 810, 475
root.rightframe= Frame(root, border=0, width=f_width, height = f_height)
root.rightframe.grid(row=0, column=0, padx=10, pady=0)
# Show video in Right Frame
root.cap = cv2.VideoCapture(0)
root.cap.set(cv2.CAP_PROP_FRAME_WIDTH, f_width)
root.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, f_height)
root.video_label = Label(root.rightframe)
root.video_label.grid(row=0, column = 0)
root.video_btn1 = Button(root.rightframe, fg='maroon', bg="yellow", text = "Stop Video", font=("Arial",10),height=0, width = 10, command=video_button1)
root.video_btn1.grid(row=0, column = 1)
root.video_btn2 = Button(root.rightframe, fg='maroon', bg="yellow", text="Time Lapse", font=("Arial",10),height=0, width = 10, command=time_lapse_play)
root.video_btn2.grid(row=1, column = 1)
# Main Code
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
screen_resolution = str(screen_width)+'x'+str(screen_height)
root.geometry(screen_resolution)
time_lapse_counter = 0
time_lapse_list=[]
setup_widgets()
show_frame()
root.mainloop()```
I've finally figure this one out. Seems that the for loop effectively doesn't work when using callback. I've modified the code to remove the for loop. I'm sure it could use some improvements but it works. I'm not sure how to reset the image count within the list as pop/append grows the list image number over time and I wonder about an overflow error. i.e after a few minutes, the list will contain [pyimage100 - pyimage200], after a few hours, [pyimage1000000 - pyimage1000100].
from tkinter import *
import cv2
from PIL import Image,ImageTk
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg)
import matplotlib.pyplot as plt
root = Tk()
def video_button1():# this flips between start and stop when video button pressed.
if root.video_btn1.cget('text') == "Stop Video":
root.video_btn1.configure(text = "Start Video")
root.cap.release()
elif root.video_btn1.cget('text') == "Start Video":
root.video_btn1.configure(text = "Stop Video")
root.cap = cv2.VideoCapture(0)
root.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 400)
root.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 400)
show_frame()
def show_frame():
# if video_btn1.cget('text') == "Stop Video":
global time_lapse_counter
ret, frame = root.cap.read()
if ret:
frame = cv2.flip(frame, 1) #flip image
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img) #converts img into tkinter readable format
root.video_label.imgtk = imgtk
root.video_label.configure(image=imgtk)
if time_lapse_counter >= 20: # for Every 20 frames, capture one into time_lapse_list
time_lapse_list.append(imgtk)
time_lapse_counter = 0
if len(time_lapse_list) == 100: # keep 99 frames in the list
time_lapse_list.pop(0)
time_lapse_counter += 1
video_loop = root.after(40, show_frame)
else:
root.video_btn1.configure(text = "Start Video")
def time_lapse_play():
root.cap.release() #stop capturing video.
global frame_counter
if frame_counter <= len(time_lapse_list)-1:
imgtk = time_lapse_list[frame_counter] # get image from list
root.video_label.configure(image=imgtk) # update label with image from list
frame_counter += 1
video_loop = root.after(250, time_lapse_play) #wait 250ms until next frame
else:
frame_counter = 0 #reset frame_counter
def setup_widgets(): #simple label and 2 button widget setup
#Setup Top Right Window with pictures
f_width, f_height = 1200, 500
root.rightframe= Frame(root, border=0, width=f_width, height = f_height)
root.rightframe.grid(row=0, column=0, padx=10, pady=0)
# Show video in Right Frame
root.cap = cv2.VideoCapture(0)
root.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 400)
root.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 400)
root.video_label = Label(root.rightframe)
root.video_label.grid(row=0, column = 0)
root.video_btn1 = Button(root.rightframe, fg='maroon', bg="yellow", text =
"Stop Video", font=("Arial",10),height=0, width = 10, command=video_button1)
root.video_btn1.grid(row=0, column = 1)
root.video_btn2 = Button(root.rightframe, fg='maroon', bg="yellow", text="Time Lapse", font=("Arial",10),height=0, width = 10, command=time_lapse_play)
root.video_btn2.grid(row=1, column = 1)
fig = plt.figure(1)
canvas = FigureCanvasTkAgg(fig, root.rightframe)
canvas.get_tk_widget().place(x=700,y=0)
canvas.get_tk_widget().config(border=2, bg="yellow", width=400, height=400)
# Main Code
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
screen_resolution = str(screen_width)+'x'+str(screen_height)
root.geometry(screen_resolution)
time_lapse_counter = 0
frame_counter = 0
time_lapse_list=[]
setup_widgets()
show_frame()
root.mainloop()
`

how to filter the detected objects in SSD detector?

I just need to detect the person and ignoring rest objects
how to filter the classes
I tried more time but unfortunately failed
I downloaded mscoco_label_map.pbtxt from here enter link description here
and
frozen_inference_graph.pb from here enter link description here
this my code:
#!/usr/bin/env python
import os
import cv2
import logging
import numpy as np
import tensorflow as tf
from tqdm import tqdm
from abc import ABCMeta, abstractmethod
from object_detection.utils import label_map_util
from object_detection.utils import visualization_utils as vis_util
from imutils.video import WebcamVideoStream
class ObjectDetector():
"""
Base class for object detectors used by the package.
"""
__metaclass__ = ABCMeta
def __init__(self):
# create logger
self._logger = logging.getLogger('dodo_detector')
self._logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
self._fh = logging.FileHandler('/tmp/dodo_detector.log')
self._fh.setLevel(logging.DEBUG)
# create console handler with a higher log level
self._ch = logging.StreamHandler()
self._ch.setLevel(logging.DEBUG)
# create formatter and add it to the handlers
self._formatter = logging.Formatter('[%(asctime)s - %(name)s]: %(levelname)s: %(message)s')
self._fh.setFormatter(self._formatter)
self._ch.setFormatter(self._formatter)
# add the handlers to the logger
self._logger.addHandler(self._fh)
self._logger.addHandler(self._ch)
#abstractmethod
def from_image(self, frame):
"""
Detects objects in an image
:param frame: a numpy.ndarray containing the image where objects will be detected
:return: a tuple containing the image, with objects marked by rectangles,
and a dictionary listing objects and their locations as `(ymin, xmin, ymax, xmax)`
"""
pass
def _detect_from_stream(self, get_frame, stream):
"""
This internal method detects objects from images retrieved from a stream, given a method that extracts frames from this stream
:param get_frame: a method that extracts frames from the stream
:param stream: an object representing a stream of images
"""
ret, frame = get_frame(stream)
while ret:
marked_frame, objects = self.from_image(frame)
print ((objects))
cv2.imshow("image", marked_frame)
if cv2.waitKey(1) == 27:
break # ESC to quit
ret, frame = get_frame(stream)
cv2.destroyAllWindows()
def from_camera(self, camera_id=0):
"""
Detects objects in frames from a camera feed
:param camera_id: the ID of the camera in the system
"""
def get_frame(stream):
frame = stream.read()
ret = True
return ret, frame
stream = WebcamVideoStream(src=camera_id)
stream.start()
self._detect_from_stream(get_frame, stream)
stream.stop()
def from_video(self, filepath):
"""
Detects objects in frames from a video file
:param filepath: the path to the video file
"""
def get_frame(stream):
ret, frame = stream.read()
return ret, frame
stream = cv2.VideoCapture()
stream.open(filename=filepath)
self._detect_from_stream(get_frame, stream)
class SingleShotDetector(ObjectDetector):
"""
Object detector powered by the TensorFlow Object Detection API.
:param path_to_frozen_graph: path to the frozen inference graph file, a file with a `.pb` extension.
:param path_to_labels: path to the label map, a text file with the `.pbtxt` extension.
:param num_classes: number of object classes that will be detected. If None, it will be guessed by the contents of the label map.
:param confidence: a value between 0 and 1 representing the confidence level the network has in the detection to consider it an actual detection.
"""
def __init__(self, path_to_frozen_graph, path_to_labels, num_classes=None, confidence=.8):
super(ObjectDetector, self).__init__()
if not 0 < confidence <= 1:
raise ValueError("confidence must be between 0 and 1")
# load (frozen) tensorflow model into memory
# path_to_frozen_graph= '/frozen_inference_graph.pb'
self._detection_graph = tf.Graph()
with self._detection_graph.as_default():
od_graph_def = tf.GraphDef()
with tf.gfile.GFile(path_to_frozen_graph, 'rb') as fid:
serialized_graph = fid.read()
od_graph_def.ParseFromString(serialized_graph)
tf.import_graph_def(od_graph_def, name='')
# Label maps map indices to category names, so that when our convolution
# network predicts 5, we know that this corresponds to airplane.
# Here we use internal utility functions, but anything that returns a
# dictionary mapping integers to appropriate string labels would be fine
# path_to_labels= 'mscoco_label_map.pbtxt'
label_map = label_map_util.load_labelmap(path_to_labels)
# this is a workaround to guess the number of classes by the contents of the label map
# it may not be perfect
if num_classes is None:
label_map_contents = open(path_to_labels, 'r').read()
num_classes = label_map_contents.count('name:')
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=num_classes, use_display_name=True)
self._category_index = label_map_util.create_category_index(categories)
print self._category_index
print ('categories', categories)
self._categories = {}
# print self._categories
#
self._categories_public = []
for tmp in categories:
self._categories[int(tmp['id'])] = tmp['name']
self._categories_public.append(tmp['name'])
self._confidence = confidence
# create a session that will be used until our detector is set on fire by the gc
self._session = tf.Session(graph=self._detection_graph)
#property
def confidence(self):
return self._confidence
#property
def categories(self):
return self._categories_public
#confidence.setter
def confidence(self, value):
self._confidence = value
def from_image(self, frame):
# object recognition begins here
height, width, z = frame.shape
image_np_expanded = np.expand_dims(frame, axis=0)
image_tensor = self._detection_graph.get_tensor_by_name('image_tensor:0')
# Each box represents a part of the image where a particular object was detected.
boxes = self._detection_graph.get_tensor_by_name('detection_boxes:0')
# Each score represent how level of confidence for each of the objects.
# Score is shown on the result image, together with the class label.
scores = self._detection_graph.get_tensor_by_name('detection_scores:0')
classes = self._detection_graph.get_tensor_by_name('detection_classes:0')
# print classes
num_detections = self._detection_graph.get_tensor_by_name('num_detections:0')
# Actual detection
boxes, scores, classes, num_detections = self._session.run([boxes, scores, classes, num_detections], feed_dict={image_tensor: image_np_expanded})
# count how many scores are above the designated threshold
worthy_detections = sum(score >= self._confidence for score in scores[0])
# self._logger.debug('Found ' + str(worthy_detections) + ' objects')
detected_objects = {}
# analyze all worthy detections
for x in range(worthy_detections):
# capture the class of the detected object
class_name = self._categories[int(classes[0][x])]
# get the detection box around the object
box_objects = boxes[0][x]
# positions of the box are between 0 and 1, relative to the size of the image
# we multiply them by the size of the image to get the box location in pixels
ymin = int(box_objects[0] * height)
xmin = int(box_objects[1] * width)
ymax = int(box_objects[2] * height)
xmax = int(box_objects[3] * width)
# print ymin
if class_name not in detected_objects:
detected_objects[class_name] = []
# print detected_objects
detected_objects[class_name].append((ymin, xmin, ymax, xmax))
# Visualization of the results of a detection.
vis_util.visualize_boxes_and_labels_on_image_array(
frame,
np.squeeze(boxes),
np.squeeze(classes).astype(np.int32),
np.squeeze(scores),
self._category_index,
use_normalized_coordinates=True,
line_thickness=8,
min_score_thresh=self._confidence
)
return frame, detected_objects
def main():
# SingleShotDetector(self.path_to_frozen_graph, self.path_to_labels)
SingleShotDetector('frozen_inference_graph.pb', 'mscoco_label_map.pbtxt').from_camera(0)
if __name__ == '__main__':
main()
please help me or any suggestions
thank you in advance

how to use "cv2.createTrackbar()" function to resize the streaming video frame?

I am an enthusiastic learner of opencv and write down a code for video streaming with opencv I want to learn the use of cv2.createTrackbar() to add some interactive functionality. Though, I tried this function but its not working for me :
For streaming and resizing the frame i use this code
import cv2
import sys
import scipy.misc
import scipy
cap = cv2.VideoCapture(sys.argv[1])
new_size = 0.7 # value range(0,1) can be used for resizing the frame size
while(1):
ret, frame = cap.read()
frame = scipy.misc.imresize(frame, new_size)
cv2.imshow("t",frame)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
Then i have transformed the above code like this to add the track bar functionality to resize the frame.
import cv2
import sys
import scipy.misc
import scipy
def nothing(x):
pass
cv2.createTrackbar('t','frame',0,1,nothing)
cap = cv2.VideoCapture(sys.argv[1])
while(1):
ret, frame = cap.read()
j = cv2.getTrackbarPos('t','frame')
frame = scipy.misc.imresize(frame, j)
cv2.imshow("t",frame)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
but this code is not working and ended up with this error given bellow:
me#ubuntu:~/Desktop/final_video_soft$ python GUI_STREAM.py p.3gp
Traceback (most recent call last):
File "GUI_STREAM.py", line 20, in <module>
frame = scipy.misc.imresize(frame, j)
File "/usr/lib/python2.7/dist-packages/scipy/misc/pilutil.py", line 365, in imresize
imnew = im.resize(size, resample=func[interp])
File "/usr/lib/python2.7/dist-packages/PIL/Image.py", line 1305, in resize
im = self.im.resize(size, resample)
TypeError: must be 2-item sequence, not float
Your code will surely fail. There are too many issues indicating you haven't read the document. Even the 1st new_size one will fail for sure.
cap = cv2.VideoCapture(sys.argv[1]) this is wrong. Because it requires int instead of str. You have to do cap = cv2.VideoCapture(int(sys.argv[1]))
another obvious error is the conflicted window name you give in following code:
cv2.createTrackbar('t','frame',0,1,nothing)
cv2.imshow("t",frame)
imshow used a window name 't'. But the 't' is actually the trackbar name.
Moreover, if you have read the document you will know the createTrackbar will only accept int as val and count. Thus you either have j = 0 or j = 1 in your code. The value is initial value of the trackbar. Thus in your case it is always 0, which will raise an error in imshow.
The getTrackbarPos should be in event triggered callback not in the main loop. If you do it like you posted, it might still run, but it will not respond to every sliding-event. However, it does not cause visible trouble since the video capture loop is quite fast.
After fix all those errors, it will ends up like this:
scale = 700
max_scale = 1000
def modified():
scale = 500
_scale = float(scale)/max_scale
cv2.namedWindow('frame', cv2.WINDOW_AUTOSIZE)
cv2.createTrackbar('t','frame', scale, max_scale, nothing)
cap = cv2.VideoCapture(int(sys.argv[1]))
while(1):
ret, frame = cap.read()
if not ret:
break
scale = cv2.getTrackbarPos('t','frame')
if scale > 1:
_scale = float(scale)/max_scale
print "scale = ", _scale
frame = scipy.misc.imresize(frame, _scale)
cv2.imshow("frame",frame)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
Sorry, i am familiar with c++, here is the C++ code, hope it helps.
The code mentioned below adds a contrast adjustment to the live video stream from the camera using createTrackbar function
#include "opencv2\highgui.hpp"
#include "opencv2\core.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char **argv[])
{
string owin = "Live Feed Original";
string mwin = "Modified Live stream";
int trackval = 50;
Mat oframe;
Mat inframe;
VideoCapture video(0);
if (!video.isOpened())
{
cout << "The Camera cannot be accessed" << endl;
return -1;
}
namedWindow(owin);
moveWindow(owin, 0, 0);
namedWindow(mwin);
createTrackbar("Contrast", mwin, &trackval, 100);
while (1)
{
video >> inframe;
imshow(owin, inframe);
inframe.convertTo(oframe, -1, trackval / 50.0);
imshow(mwin, oframe);
if (waitKey(33) == 27)
break;
}
}

cv2.cvtColor error. Is it bug?

I need to use some motion detection code, then I use following code, provided by this link:
http://www.steinm.com/blog/motion-detection-webcam-python-opencv-differential-images/ .
Here is the code:
import cv2
def diffImg(t0, t1, t2):
d1 = cv2.absdiff(t2, t1)
d2 = cv2.absdiff(t1, t0)
return cv2.bitwise_and(d1, d2)
cam = cv2.VideoCapture(0)
winName = "Movement Indicator"
cv2.namedWindow(winName, cv2.CV_WINDOW_AUTOSIZE)
# Read three images first:
t_minus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY)
t = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY)
t_plus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY)
while True:
cv2.imshow(winName, diffImg(t_minus, t, t_plus) )
#diff = diffImg(t_minus, t, t_plus)
# Read next image
t_minus = t
t = t_plus
t_plus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY)
#cv2.imshow(winName, diff)
key = cv2.waitKey(10)
if key == 27:
cv2.destroyWindow(winName)
break
print "Goodbye"
At first, it run smoothly, but now, it gives me error :
cv2.error: ........\opencv\modules\imgproc\src\color.cpp:3737: error: (-215) scn == 3 || scn == 4 in function cv::cvtColor
i found various solutions in stackoverflow, but still the error occured. It has been said that the error occured because the source doesn’t have the right color format that the code (third argument in the function call) indicates it should.
Can anyone give me ideas why the error occured? Or is that opencv bug and there's no solution for that?
The problem is
t_minus = cv2.cvtColor(cam.read()[1], cv2.COLOR_RGB2GRAY)
# ^
When you're accessing the [1] index of an BGR image, it's no longer a colour image to be converted using cv2.COLOR_RGB2GRAY. Instead, just write cam.read(). Also note, OpenCV uses BGR by default, not RGB.
I met this trouble also, after i read above answers i tried it but have not solved it, and finally i find the path of my image was wrong, so u had better check the real path firstly.

Reading every nth frame from VideoCapture in OpenCV

Is it possible to read frames from a video in steps (eg I want to read every fifth frame of a video stream). Currently I'm doing this as a workaround but it's not very effecient.
bool bSuccess
int FramesSkipped = 5;
for (int a = 0; < FramesSkipped; a++)
bSuccess = cap.read(NextFrameBGR);
Any suggestions so I do not have to loop through the five frames to get the desired frame?
I'm afraid there's not much you can do and it's not just a shortcoming of OpenCV. You see, modern video codecs, are, generally, complex beasts. To get a higher compression rate the encoding of a frame is often dependent on previous and sometimes even successive frames.
So, most of the time you have to decode frames before the desired one even if you don't need them.
There are rather non-trivial tricks to specifically encode a video file, so that it would be cheap to get every Nth frame, but it's not feasible in general case.
That said, you can try the seeking functionality OpenCV provides (see OpenCV Seek Function/Rewind). It may (as well as may not) work faster depending on the circumstances. However, personally, I wouldn't bet on it.
I've had success in Python 3 using a simple counter and setting the capture to that counter's frame, as follows:
import cv2
cap = cv2.VideoCapture('XYZ.avi')
# For streams:
# cap = cv2.VideoCapture('rtsp://url.to.stream/media.amqp')
# Or e.g. most common ID for webcams:
# cap = cv2.VideoCapture(0)
count = 0
while cap.isOpened():
ret, frame = cap.read()
if ret:
cv2.imwrite('frame{:d}.jpg'.format(count), frame)
count += 30 # i.e. at 30 fps, this advances one second
cap.set(cv2.CAP_PROP_POS_FRAMES, count)
else:
cap.release()
break
I've tried to find a way to make this a little more pythonic using a with statement but I don't believe the CV2 library has been updated for it.
I got it to work in Python... See below for two sample use cases and some caveats.
# First, import some packages
import cv2
import math
import numpy as np
# Make sure that the print function works on Python 2 and 3
from future import print_function
# Capture every n seconds (here, n = 5)
#################### Setting up the file ################
videoFile = "Jumanji.mp4"
vidcap = cv2.VideoCapture(videoFile)
success, image = vidcap.read()
#################### Setting up parameters ################
seconds = 5
fps = vidcap.get(cv2.CAP_PROP_FPS) # Gets the frames per second
multiplier = fps * seconds
#################### Initiate Process ################
while success:
frameId = int(round(vidcap.get(1))) #current frame number, rounded b/c sometimes you get frame intervals which aren't integers...this adds a little imprecision but is likely good enough
success, image = vidcap.read()
if frameId % multiplier == 0:
cv2.imwrite("FolderSeconds/frame%d.jpg" % frameId, image)
vidcap.release()
print("Complete")
# Alternatively, capture every n frames (here, n = 10)
#################### Setting up the file ################
videoFile = "Jumanji.mp4"
vidcap = cv2.VideoCapture(videoFile)
success, image = vidcap.read()
#################### Setting up parameters ################
#OpenCV is notorious for not being able to good to
# predict how many frames are in a video. The point here is just to
# populate the "desired_frames" list for all the individual frames
# you'd like to capture.
fps = vidcap.get(cv2.CAP_PROP_FPS)
est_video_length_minutes = 3 # Round up if not sure.
est_tot_frames = est_video_length_minutes * 60 * fps # Sets an upper bound # of frames in video clip
n = 5 # Desired interval of frames to include
desired_frames = n * np.arange(est_tot_frames)
#################### Initiate Process ################
for i in desired_frames:
vidcap.set(1, i-1)
success, image = vidcap.read(1) # image is an array of array of [R,G,B] values
frameId = vidcap.get(1) # The 0th frame is often a throw-away
cv2.imwrite("FolderFrames/frame%d.jpg" % frameId, image)
vidcap.release()
print("Complete")
That's pretty much it.
Some unfortunate caveats... depending on your version of `opencv` (this is built for `opencv` V3), you may need to set the fps variable differently. See [here][1] for details. To find out your version, you can do the following:
major_ver, minor_ver, subminor_ver = cv2.__version__.split('.')
print(major_ver)
I encountered the same problem.
All i did was this:
import cv2
vs = cv2.VideoCapture("<path of your video>.mp4")
print("Showing frames...")
c=1
while True:
grabbed, frame = vs.read()
if c%5==0:
cv2.imshow('Frame',frame)
cv2.waitKey(1)
c+=1
vs.release()
You should use the grab function to move to next frame. And only use retrieve to decode the frame you need.
bool bSuccess
int FramesSkipped = 5;
for (int a = 0; < FramesSkipped; a++)
bSuccess = cap.grab();
bSuccess = cap.retrieve(NextFrameBGR);
Here is what I suggest:
CvCapture* capture = cvCaptureFromFile("input_video_path");
int loop = 0;
IplImage* frame = NULL;
Mat matframe;
char fname[20];
do {
frame = cvQueryFrame(capture);
matframe = cv::cvarrToMat(frame);
cvNamedWindow("video_frame", CV_WINDOW_AUTOSIZE);
cvShowImage("video_frame", frame);
sprintf(fname, "frame%d.jpg", loop);
cv::imwrite(fname, matframe);//save each frame locally
loop++;
cvWaitKey(100);
} while( frame != NULL );
Now that you have saved all the frames locally you can quickly read the nth frame that you want.
CATUION:A sample video of 12 secs I had was composed of >200 images. This will eat up lot of space.
A simple yet effective optimization will be to read the nth frame using the approach that you are using or the one suggested by #sergie. After this you can save the image with its index so that later query at same index will return the saved image rather than having to skip frames like you are. This way you will save the space that you would have wasted in saving frames that you wouldn't have queried and time taken to read & save those unwanted frames aswell.
When I had the same goal with OpenCV, I just tuned around the number of "keyframes" I wanted per second of video, regardless of the frame rate or total number of frames. So, this gets me the N-th key given my target KPS.
# python3.6 code using OpenCV 3.4.2
import cv2
KPS = 5 # Target Keyframes Per Second
VIDEO_PATH = "path/to/video/folder" # Change this
IMAGE_PATH = "path/to/image/folder" # ...and this
EXTENSION = ".png"
cap = cv2.VideoCapture(VIDEO_PATH)
# Set frames-per-second for capture
fps = round(cap.get(cv2.CAP_PROP_FPS))
hop = round(fps / KPS)
curr_frame = 0
while(True):
ret, frame = cap.read()
if not ret: break
if curr_frame % hop == 0:
print('Creating... {0}'.format(name,))
name = IMAGE_PATH + "_" + str(curr_frame) + EXTENSION
cv2.imwrite(name, frame)
curr_frame += 1
cap.release()
Note that I'm rolling through all the frames, but only writing the N-th frame using hop as N.
I use this repo!
Main idea is:
main.py
from camera import VideoCam
SKIPFRAME = 8
url = 0
v1 = VideoCam(url)
v1.check_camera(v1.cap)
ct = 0
while True:
ct += 1
try:
ret = v1.cap.grab()
if ct % SKIPFRAME == 0: # skip some frames
ret, frame = v1.get_frame()
if not ret:
v1.restart_capture(v1.cap)
v1.check_camera(v1.cap)
continue
# frame HERE
v1.show_frame(frame, 'frame')
except KeyboardInterrupt:
v1.close_cam()
exit(0)
camera.py
import cv2
import logging
class VideoCam():
def __init__(self, url=0):
self.url = url
self.cap = cv2.VideoCapture(self.url)
self.get_frame()
self.get_frame_read()
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
def check_camera(self, cap):
logging.info('Camera {} status: {}'.format(self.url, cap.isOpened()))
def show_frame(self, frame, name_fr='NAME'):
cv2.imshow(name_fr, frame)
# cv2.imshow(name_fr, cv2.resize(frame, (0, 0), fx=0.4, fy=0.4))
cv2.waitKey(1)
def get_frame(self):
return self.cap.retrieve()
def get_frame_read(self):
return self.cap.read()
def close_cam(self):
self.cap.release()
cv2.destroyAllWindows()
def restart_capture(self, cap):
cap.release()
self.cap = cv2.VideoCapture(self.url)
if anyone will need to capture every 5th frame and save it as jpg, based on Ishan Shah's code:
import cv2
vid = cv2.VideoCapture('C:/python_works/creatives_gardenscapes/0c52b83ed1dec617092aaf83278f12ad.mp4')
if not os.path.exists('images'):
os.makedirs('images')
index = 0
while(True):
ret, frame = vid.read()
if not ret:
break
name = 'C:/python_works/creatives_gardenscapes/frames/0c52b83ed1dec617092aaf83278f12ad' + str(index) + '.jpg'
if index%50==0:
cv2.imwrite(name, frame)
index += 1
code:
import cv2
vidcap = cv2.VideoCapture('bali.mp4')
success,image = vidcap.read()
fps = vidcap.get(cv2.CAP_PROP_FPS)
print('video fps :', fps)
length = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
print( 'total frames :', length )
count = 0
skip = 10
success = True
while success:
success,image = vidcap.read()
if count%skip==0:
print(count)
cv2.imwrite("frames/frame%d.jpg" % count, image) # save frame as JPEG file
if cv2.waitKey(10) == 27: # exit if Escape is hit
break
count += 1
output:
You can effectively "skip" a frame by reading it then discarding it. Granted, this does exactly not what you are asking, but might still suffice as a work-around. Obviously this has implications on performance but may be adequate for your purposes.
vs = cv2.videoCapture('/my/clip.mp4')
for i in (thing):
# Read a frame
_, frame = vs.read()
# Skip the frame you just read by reading another
_, frame = vs.read()
# Run a process on said frame
cv2.imshow("Window", frame)
vs.release()
This is what i did on skip frame
skip_frame = cap.get(cv2.CAP_PROP_POS_FRAMES) #get current frames
sk = skip_frame + 10 # How many frames you want to skip
cap.set(cv2.CAP_PROP_POS_FRAMES, sk) # set the Current frame to the sk
It is not possible to extract random frames as the encoding scheme is generally extremely complex. For example in MPEG-4, Only the information containing the difference between two frames is stored, Hence clearly the previous frames are required.
I've refactored #benJephunneh's answer to be more "Pythonic" and wrapped the code in an iterator.
def every_n_frames(path, n):
video = cv2.VideoCapture(path)
# start at frame 0
count = 0
while video.isOpened():
success, frame = video.read()
if not success:
break
try:
yield frame
except Exception as e:
video.release()
raise e
count += n # advance by n frames
video.set(cv2.CAP_PROP_POS_FRAMES, count)
video.release()
# Usage:
framerate = 30
for i, frame in enumerate(every_n_frames("XYZ.avi", framerate)):
cv2.imwrite("frame{:d}.jpg".format(i * framerate), frame)
Note: If you're using lower values of n (e.g. every 2, 3 frames), setting cv2.CAP_PROP_POS_FRAMES will be much slower than just reading every frame then discarding the ones you don't need.
Skipping frames in cv2 python
cap = cv2.VideoCapture(0)
x = 0
while True:
ret, frame = cap.read()
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray,(7, 7), 0)
(T, thresh) = cv2.threshold(blurred, 135, 255, cv2.THRESH_BINARY)
contours, hierarchy=cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:10]
for contour in contours:
area = cv2.contourArea(contour)
if(x != 0):
x -= 1
break
if(area >= 517000 and area <= 522000):
holdFrame = frame
results = yourAlgorithm(holdFrame) # Your Algorithm Here
x = 3
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.waitKey(0)
cv2.destroyAllWindows()
In the Code Above, I have done 3 things totally, that solved my problem
Capturing Frames and initializing the variable x = 0
Then if my required area of contour is detected I run my algorithm.
After running the algorithm I set my x = 3
Then next time my function goes to trigger my algorithm, it checks
if x != 0, while x = 3 so it breaks out of the loop twice while
decrementing the value of x by 1 each time. So, when the value of x
becomes 0, approximately 3,4 frames have been skipped.