I am trying to display 9 videos onto their own canvases using Tkinter. The problem I am running into is that the window is very laggy and unresponsive. This means nothing else can be done in the application whilst the videos are playing.
I have tried to use a second thread to perform steps 1-3 (as shown below), but this does not seem to help. I am struggling to see how I can make this process more efficient and any help would be much appreciated.
My process for doing this is as follows:
Read the video using cv2
import cv2
cap = cv2.VideoCapture(video_path)
Read a frame from the video
cap.set(1, frame_num)
ret, frame = cap.read()
Resize and convert to PhotoImage
frame = imutils.resize(frame, height=video_height, width=video_width)
photo = ImageTk.PhotoImage(image=Image.fromarray(frame))
Draw the image to canvas
im = self.create_image(0, 0, image=photo, anchor=NW)
This is done for all 9 videos, every 200ms.
The only solution for this is to buy a computer with more CPU cores / GPU / faster CPU, i'm afraid ! If the videos are displayed on a small canvas, you should resize them before playing them. That is, have a already resized video and play that instead of the original one. Reading frame by frame, then resizing and displaying on canvas the frame is very CPU expensive, already for one video, and you do it with 9 ! Thats why the GUI becomes laggy : Your CPU uses all its resources for resizing and playing, and has almost nothing left for responding to user events. A GPU would help there : it takes the part of resizing, which is the most CPU expensive.
Related
I am trying to change the Frame rate i.e., FPS of an existing video using openCV library in python. Below is the code that I am trying to execute. Even after setting the FPS property using cv2.CAP_PROP_FPS the video is not playing faster in the cv2.imshow() method. Even After setting the FPS property the getter returns the older FPS value. So how do I set the FPS value higher and make the video play faster?
Used version:
python = 3.7.4 and
opencv-python - 4.1.0.25
import cv2
video = cv2.VideoCapture("yourVideoPath.mp4");
video.set(cv2.CAP_PROP_FPS, int(60))
if __name__ == '__main__':
print("Frame rate : {0}".format(video.get(cv2.CAP_PROP_FPS)))
while video.isOpened():
ret1, frame2 = video.read()
cv2.imshow("Changed", frame2)
if cv2.waitKey(10) & 0xFF == ord('q'): # press q to quit
break
video.release()
cv2.destroyAllWindows()
If you're only trying to play the video in the displayed window, the limiting factor is not the fps of the video but the time spent waiting with the code waitKey(10) which makes the program wait for 10ms between each frame.
The read() method of the VideoCapture class simply returns the next frame with no concept of waiting or frame rate. The only thing preventing this code running as fast as it can is the waitKey(10) section, which is thus the main factor determining speed. To change the frame rate as seen through the imshow() method, you'd need to edit the time spent waiting. This is likely the dominant factor, but not the only one as the reading of a frame does take time.
If you're actually trying to change the playback rate of an existing file and have that saved to that file, I am unsure if OpenCV actually supports this, and I imagine it would be dependent on what back end you're using - OpenCV implements the VideoCapture class using different 3rd party backends.. As per the documentation of VideoCapture.set() I'd investigate the return value of video.set(cv2.CAP_PROP_FPS, int(60)) as the documentation suggests it will return true if this has changed something.
As an alternative you could investigate using something like FFMPEG which supports this relatively easily. If you want to stick with OpenCV I know from personal experience you can do this with the VideoWriter class. In this method you would read in the video frame by frame using the VideoCapture class, and then save it at the desired frame rate with VideoWriter. I suspect FFMPEG will likely meet your needs however!
There is about a 10-20 second delay between when I run my program and when the web camera actually takes the image. Is there any way to speed up this process?
I have looked several places and haven't found a solution.
video_capture = cv2.VideoCapture(1)
ret, frame = video_capture.read()
I just don't get what is taking these two lines of code so long to execute when I can take a picture with my webcam instantly through the normal camera application.
Ok so it took me a while but the problem was solved by switching the API. I changed the line of code:
video_capture = cv2.VideoCapture(1)
to
video_capture = cv2.VideoCapture(1, cv2.CAP_DSHOW)
by adding this, it now works instantly, removing the delay which was present before.
I am trying to use OpenCV to load a video file or access video stream from webcam but I'm unable to access all frames.
The video file is captured with the FPS of 60 but in my code I am only able to access a few frames per second. I tried using the threaded version of OpenCV, imutils. It works better but I still cannot access full frames.
In my code below, I use the threaded version video reader to load video file and resize it to smaller size to reduce the processing power required.
After the frame is grabbed successfully, I will so some image processing work (in the future). But now even with this boilerplate, I can at most read only a few (10+) frames and the results are stuttering. Is there a way to resolve this?
import cv2
from imutils import resize
from imutils.video import VideoStream
vs = VideoStream(src="./video.MOV").start()
while True:
frame = vs.read()
if frame is not None:
frame = resize(frame, 800)
# some heavy analytics work
cv2.imshow("Frame", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
vs.stop()
Experiment
I ran an experiment to calculate number of frames loaded and average time taken for each imshow function on both my iMac and an Ubuntu machine rocking Intel Core i5-7400 # 3.00Ghz with a 1080p monitor.
The video (h264) has a duration of 1:03 min and of size 185.7MB.
iMac can only load a total of 414 frames while the Ubuntu machine can load a total of 2826 frames.
Average time take for imshow function for both machine is 0.0003s
Basically, you just load the video and display, so there isn't any reason that you get low fps like that except that you are using a Macbook with Retina screen. Maybe it's the reason causes slow video display because the Retina screen has a lot more pixels, and it might take time for the compositing engine to render your image on the screen. I suggest to use an external screen.
Weird coding outcome which isn't making much sense. I am trying to capture from a raspberry pi camera using the V4L2 driver as I need to use cv2 for image processing. I am using python to write the code.
The weirdness revolves around capturing images using cv2. when I type in the following commands
import cv2
from matplotlib import pyplot
camera = cv2.VideoCapture(0)
grab,frame = camera.read()
pyplot.imshow(frame)
I am able to grab a frame and display it using matplotlib. When I grab a second frame
grab,frame2 = camera.read()
pyplot.imshow(frame2)
The code will grab a second frame and display it perfectly fine.
However when I try to use an existing variable like frame or frame2 the camera will not grab a new frame and just print the prior frame.
I tried to clear the variable by typing
frame = []
grab,frame = camera.read()
pyplot.imshow(frame)
but this didn't fix the issue, still printing the prior frame.
I think you are "suffering from buffering"!
When OpenCV reads a frame, it tends to gather a few, I think it is 5 frames or so, or there may be some algorithm that determines available memory or something similar.
Anyway, the answer is to read a few more frames to clear the buffer and then it will acquire some fresh frames.
I am developing an app that displays advertisements. It will display in fullscreen and divide the screen into 2 parts. One part will display an image (or a slide of images). The other part will display a loop of videos, it automatically plays a list of videos in the folder and repeats them. No pause or start button is required but the sound of the video. I have tried with opencv and PIL but it just helps me to play a single video once and play without the video's sound. The code below is what I have used in my project:
ret, frame = self.vid.get_frame()
if ret:
self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame), master=self)
self.videoCanvas.create_image(self.x, self.y, image=self.photo, anchor=CENTER)
self.after(self.delay, self.update)
As I understand, it basically captures all the images in the video and displays them again in order on the canvas.
I am looking for a solution to such problem.