I want to draw a rectangle in a saved video. While drawing the rectangle,the video must freeze. I am successful in drawing a rectangle on a image,but I don't know how to do the same on a saved video using opencv and python.
I was in need of a ROI selection mechanism using opencv and I finally figured how to implement it.
The implementation can be found here (opencvdragrect). It uses opencv 3.1.0 and Python 2.7
For a saved video till the time you don't read another frame and display it, the video is considered as paused.
In terms of how to add it to a paused video (frame), this code below might help.
import cv2
import selectinwindow
wName = "select region"
video = cv2.VideoCapture(videoPath)
while(video.isOpened()):
# Read frame
ret, RGB = video.read()
frameCounter += 1
if frameCounter == 1 : # you can pause any frame you like
rectI = selectinwindow.dragRect
selectinwindow.init(rectI, I, wName, I.shape[1], I.shape[0])
cv2.namedWindow(wName)
cv2.setMouseCallback(wName, selectinwindow.dragrect, rectI)
while True:
# display the image
cv2.imshow(wName, rectI.image)
key = cv2.waitKey(1) & 0xFF
# if returnflag is set break
# this loop and run the video
if rectI.returnflag == True:
break
box = [rectI.outRect.x,rectI.outRect.y,rectI.outRect.w,rectI.outRect.h]
# process the video
# ...
# ...
In the (opencvdragrect) library you use double-click to stop the rectangle selection process and continue with video.
Hope this helps.
Related
I'm new to Qt and trying to figure out how position graphics items?
For example, I want to overlay some text onto a video. However, instead of overlaying them, the text is vertically stacked above the video.
My code is below, I've tried setting the position of the video/text elements (e.g. video.setPos(0,0)) but it didn't work. I also tried using QGraphicsLayout but ran into problems adding the video/text elements.
import sys
from PySide6 import QtWidgets, QtCore, QtMultimedia, QtMultimediaWidgets
class MyWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
view = QtWidgets.QGraphicsView(self)
# Create a QT player
player = QtMultimedia.QMediaPlayer(self)
player.setSource(QtCore.QUrl("https://archive.org/download/SampleVideo1280x7205mb/SampleVideo_1280x720_5mb.mp4"))
video = QtMultimediaWidgets.QGraphicsVideoItem()
player.setVideoOutput(video)
text = QtWidgets.QGraphicsTextItem("Hello World")
scene = QtWidgets.QGraphicsScene()
scene.addItem(video)
scene.addItem(text)
view.setScene(scene)
view.setFixedSize(800,800)
player.play()
player.setLoops(-1)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = MyWidget()
widget.resize(800, 800)
widget.show()
sys.exit(app.exec())
The problem is caused by two aspects:
QGraphicsVideoItem has a default size of 320x240; for conceptual and optimization reasons, it does not change its size when video content is loaded or changed;
the video you're using has a different aspect ratio: 320x240 is 4:3, while the video is 1280x720, which is the standard widescreen ratio, 16:9;
By default, QGraphicsVideoItem just adapts the video contents to its current size, respecting the aspectRatioMode. The result is that you get some blank space above and below the video, similarly to what was common when showing movies in old TVs ("letterboxing"):
Since graphics items are always positioned using the top left corner of their coordinate system, you see a shifted image. In fact, if you just print the boundingRect of the video item when playing starts, you'll see that it has a vertical offset:
player.mediaStatusChanged.connect(lambda: print(video.boundingRect()))
# result:
QtCore.QRectF(0.0, 30.0, 320.0, 180.0)
^^^^ down by 30 pixels!
There are various possibilities to solve this, but it all depends on your needs, and it all resorts on connecting to the nativeSizeChanged signal. Then it's just a matter of properly setting the size, adjust the content to the visible viewport area, or eventually always call fitInView() while adjusting the position of other items (and ignoring transformations).
For instance, the following code will keep the existing size (320x240) but will change the offset based on the adapted size of the video:
# ...
self.video = QtMultimediaWidgets.QGraphicsVideoItem()
# ...
self.video.nativeSizeChanged.connect(self.updateOffset)
def updateOffset(self, nativeSize):
if nativeSize.isNull():
return
realSize = self.video.size()
scaledSize = nativeSize.scaled(realSize,
QtCore.Qt.AspectRatioMode.KeepAspectRatio)
yOffset = (scaledSize.height() - realSize.height()) / 2
self.video.setOffset(QtCore.QPointF(0, yOffset))
I want to take a video from screen but it shouldn't use a while loop for taking the picture.I am using tkinter for my GUI.
I have tried after method for taking the picture every time when it needs to be taken. But it doesn't work appropriately. is there any way that I can do it without while true loop?
{def recording_loop(out):
"""take video by Imagegrab"""
img = ImageGrab.grab()
img_np = np.array(img)
frame = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB)
out.write(frame)
self.canvas.after(41, self.recording_loop, res))}
I expect that every 41ms the recording_loop revokes itself, so it can take 24 pictures in 1s (frame=24). but it doesn't work. Any help would appreciate. (out is output of cv2.videowriter(....) )
I have this situation where I use OpenCV to detect faces in front of the camera and do some ML on those faces. The issue that I have is that once I do all the processing, and go to grab the next frame I get the past, not the present. Meaning, I'll read whats inside the buffer, and not what is actually in front of the camera. Since I don't care which faces came in front of the camera while processing, I care what is now in front of the camera.
I did try to set the buffer size to 1, and that did help quite a lot, but I still will get at least 3 buffer reads. Setting the FPS to 1, also dose not help remove this situation 100%. Bellow is the flow that I have.
let cv = require('opencv4nodejs');
let camera = new cv.VideoCapture(camera_port);
camera.set(cv.CAP_PROP_BUFFERSIZE, 1);
camera.set(cv.CAP_PROP_FPS, 2);
camera.set(cv.CAP_PROP_POS_FRAMES , 1);
function loop()
{
//
// <>> Grab one frame from the Camera buffer.
//
let rgb_mat = camera.read();
// Do to gray scale
// Do face detection
// Crop the image
// Do some ML stuff
// Do whats needs to be done after the results are in.
//
// <>> Release data from memory
//
rgb_mat.release();
//
// <>> Restart the loop
//
loop();
}
My question is:
Is it possible to remove the buffer all-together? And if so, how. If not, a why would be much appreciated.
Whether CAP_PROP_BUFFERSIZE is supported appears quite operating system and backend-specific. E.g., the 2.4 docs state it is "only supported by DC1394 [Firewire] v 2.x backend currently," and for backend V4L, according to the code, support was added only on 9 Mar 2018.
The easiest non-brittle way to disable the buffer is using a separate thread; for details, see my comments under Piotr Kurowski's answer. Here Python code that uses a separate thread to implement a bufferless VideoCapture: (I did not have a opencv4nodejs environment.)
import cv2, Queue, threading, time
# bufferless VideoCapture
class VideoCapture:
def __init__(self, name):
self.cap = cv2.VideoCapture(name)
self.q = Queue.Queue()
t = threading.Thread(target=self._reader)
t.daemon = True
t.start()
# read frames as soon as they are available, keeping only most recent one
def _reader(self):
while True:
ret, frame = self.cap.read()
if not ret:
break
if not self.q.empty():
try:
self.q.get_nowait() # discard previous (unprocessed) frame
except Queue.Empty:
pass
self.q.put(frame)
def read(self):
return self.q.get()
cap = VideoCapture(0)
while True:
frame = cap.read()
time.sleep(.5) # simulate long processing
cv2.imshow("frame", frame)
if chr(cv2.waitKey(1)&255) == 'q':
break
The frame reader thread is encapsulated inside the custom VideoCapture class, and communication with the main thread is via a queue.
This answer suggests using cap.grab() in a reader thread, but the docs do not guarantee that grab() clears the buffer, so this may work in some cases but not in others.
I set cap value after reading each frame to None and my problem solved in this way:
import cv2
from PyQt5.QtCore import QThread
if __name__ == '__main__':
while True:
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
cv2.imshow('A', frame)
cv2.waitKey(0)
print('sleep!')
QThread.sleep(5)
print('wake up!')
cap = None
I have the same problem but in C++. I didn't find proper solution in OpenCV but I found a workaround. This buffer accumulates a constant number of images, say n frames. So You can read n frames without analysis and read frame once more. That last frame will be live image from camera. Something like:
buffer_size = n;
for n+1
{
// read frames to mat variable
}
// Do something on Mat with live image
I want to reduce the number of frames acquired per second in a webcam, this is the code that I'm using
#!/usr/bin/env python
import cv2
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FPS, 10)
fps = int(cap.get(5))
print("fps:", fps)
while(cap.isOpened()):
ret,frame = cap.read()
if not ret:
break
cv2.imshow('frame', frame)
k = cv2.waitKey(1)
if k == 27:
break
But it doesn't take effect, I still have 30 fps by default instead of 10 set up by cap.set(cv2.CAP_PROP_FPS, 10) . I want to reduce the frame rate because I have a hand detector which take quite a lot of time to process each frame, I can not store frames in buffer since it would detect the hand in previous positions. I could run the detector using a timer or something else but I thought changing the fps was an easier way, but it didn't work and I don't know why.
Im using Opencv 3.4.2 with Python 3.6.3 in Windows 8.1
Setting a frame rate doesn't always work like you expect. It depends on two things:
What your camera is capable of outputting.
Whether the current capture backend you're using supports changing frame rates.
So point (1). Your camera will have a list of formats which it is capable of delivering to a capture device (e.g. your computer). This might be 1920x1080 # 30 fps or 1920x1080 # 60 fps and it also specifies a pixel format. The vast majority of consumer cameras do not let you change their frame rates with any more granularity than that. And most capture libraries will refuse to change to a capture format that the camera isn't advertising.
Even machine vision cameras, which allow you much more control, typically only offer a selection of frame rates (e.g. 1, 2, 5, 10, 15, 25, 30, etc). If you want a non-supported frame rate at a hardware level, usually the only way to do it is to use hardware triggering.
And point (2). When you use cv.VideoCapture you're really calling a platform-specific library like DirectShow or V4L2. We call this a backend. You can specify exactly which backend is in use by using something like:
cv2.VideoCapture(0 + cv2.CAP_DSHOW)
There are lots of CAP_X's defined, but only some will apply to your platform (e.g CAP_V4L2 is for Linux only). On Windows, forcing the system to use DirectShow is a pretty good bet. However as above, if your camera only reports it can output 30fps and 60fps, requesting 10fps will be meaningless. Worse, a lot of settings simply report True in OpenCV when they're not actually implemented. You've seen that most of the time reading parameters will give you sensible results though, however if the parameter isn't implemented (e.g exposure is a common one that isn't) then you might get nonsense.
You're better off waiting for a period of time and then reading the last image.
Be careful with this strategy. Don't do this:
while capturing:
res, image = cap.read()
time.sleep(1)
you need to make sure you're continually purging the camera's frame buffer otherwise you will start to see lag in your videos. Something like the following should work:
frame_rate = 10
prev = 0
while capturing:
time_elapsed = time.time() - prev
res, image = cap.read()
if time_elapsed > 1./frame_rate:
prev = time.time()
# Do something with your image here.
process_image()
For an application like a hand detector, what works well is to have a thread capturing images and the detector running in another thread (which also controls the GUI). Your detector pulls the last image captured, runs and display the results (you may need to lock access to the image buffer while you're reading/writing it). That way your bottleneck is the detector, not the performance of the camera.
I could not set the FPS for my camera so I manage to limit the FPS based on time so that only 1 frame per second made it into the rest of my code. It is not exact, but I do not need exact, just a limiter instead of 30fps. HTH
import time
fpsLimit = 1 # throttle limit
startTime = time.time()
cv = cv2.VideoCapture(0)
While True:
frame = cv.read
nowTime = time.time()
if (int(nowTime - startTime)) > fpsLimit:
# do other cv2 stuff....
startTime = time.time() # reset time
As Josh stated, changing the camera's fps on openCV highly depends if your camera supports the configuration you are trying to set.
I managed to change my camera's fps for openCV in Ubuntu 18.04 LTS by:
Install v4l2 with "sudo apt-get install v4l-utils"
Run command "v4l2-ctl --list-formats-ext" to display supported video formats including frames sizes and intervals.
Results from running v4l2-ctl --list-formats-ext
In my python script:
import cv2
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) # depends on fourcc available camera
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap.set(cv2.CAP_PROP_FPS, 5)
The property CV_CAP_PROP_FPS only works on videos as far. If you use the follow command:
fps = cap.get(cv2.CAP_PROP_FPS)
It is returned zero. If you want to reduce frames per seconds, then you can increase a parameter of waitkey(). For example:
k = cv2.waitKey(100)
This would work for your problem
import cv2
import time
cap = cv2.VideoCapture(your video)
initial_time = time.time()
to_time = time.time()
set_fps = 25 # Set your desired frame rate
# Variables Used to Calculate FPS
prev_frame_time = 0 # Variables Used to Calculate FPS
new_frame_time = 0
while True:
while_running = time.time() # Keep updating time with each frame
new_time = while_running - initial_time # If time taken is 1/fps, then read a frame
if new_time >= 1 / set_fps:
ret, frame = cap.read()
if ret:
# Calculating True FPS
new_frame_time = time.time()
fps = 1 / (new_frame_time - prev_frame_time)
prev_frame_time = new_frame_time
fps = int(fps)
fps = str(fps)
print(fps)
cv2.imshow('joined', frame)
initial_time = while_running # Update the initial time with current time
else:
total_time_of_video = while_running - to_time # To get the total time of the video
print(total_time_of_video)
break
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
I am trying to make an mp3 player in python using pyglet and I want to display the current song name that is playing. I tried to use SourceInfo class, but I don't know how it works
pyglet.media.SourceInfo.title doesn't show anything. Could you please help me?
Thanks
First all all, make sure you got AvBin installed!
Otherwise you won't be able to play much other than wav files.
Remember that this is just a rough mock up of what you need.
Build on it, scrap it.. But this is a start.
import pyglet
pyglet.options['audio'] = ('pulse', 'alsa')
window = pyglet.window.Window(width=800, height=600)
player=pyglet.media.Player()
# == Queue more tracks,
# the label will update automatically
player.queue( pyglet.media.load('./Downloads/music/Pendulum - Hold Your Color.mp3', streaming=True) )
player.volume=0.3
# window is the variable 'window' from earlier.
#window.event
def on_draw():
window.clear()
if player.playing:
label = pyglet.text.Label('{artist} - {song}'.format(**{'artist' : player.source.info.author.decode('UTF-8'),
'song' : player.source.info.title.decode('UTF-8')}),
font_size=36,
x=window.width//2, y=window.height//2, # same here, window on line 6
anchor_x='center', anchor_y='center')
else:
label = pyglet.text.Label('Music paused!', font_size=36, x=window.width//2, y=window.height//2, anchor_x='center', anchor_y='center')
label.draw()
window.flip() # This is overkill but ensures proper rendering at all times.
#window.event
def on_key_press(symbol, modifiers):
# Up and Down controls the volume for instance
if symbol == pyglet.window.key.UP:
player.volume = player.volume+0.1
elif symbol == pyglet.window.key.DOWN:
player.volume = player.volume-0.1
# Any key really will start/pause the playback
elif player.playing:
player.pause()
else:
player.play()
pyglet.app.run()