我正在尝试使用OpenCV和Python处理视频。
我使用2个线程,一个线程读取框架,另一个线程显示它们。 现在,我试图通过使用setMouseCallback设置点击回调函数来停止视频并继续播放。
该代码一直有效,直到我第一次停止播放视频为止,此后它不再捕获click事件以继续播放,并且反复单击会停止工作。
这是我的代码:
import threading, time
import cv2
import queue
capFile = cv2.VideoCapture("../media/videoplayback.mp4")
input_buffer = queue.Queue(4000)
fps = capFile.get(cv2.CAP_PROP_FPS)
time_frame=1/fps
stopped=False
def clickListener(event, x, y, flags, param):
global stopped
if event==cv2.EVENT_LBUTTONDOWN:
pass
if event==cv2.EVENT_LBUTTONUP:
print("Stop/Resume video")
stopped = not stopped
def readFile():
while True:
ret, frame = capFile.read()
if ret:
input_buffer.put(frame)
def processingFile():
cv2.namedWindow('Video File')
cv2.setMouseCallback("Video File", clickListener)
global stopped
global frame
while True:
if not stopped:
frame=input_buffer.get()
cv2.imshow("Video File",frame)
time.sleep(time_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
pass
return
tReadFile = threading.Thread(target=readFile)
tProcessingFile = threading.Thread(target=processingFile)
tReadFile.start()
tProcessingFile.start()
你知道会发生什么吗?
答案 0 :(得分:1)
您的主要问题在于此循环:
while True:
if not stopped:
frame=input_buffer.get()
cv2.imshow("Video File",frame)
time.sleep(time_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
pass
当视频为stopped
时,您只是进入一个无限循环,根本什么也没做。不幸的是,为了使GUI继续工作(包括处理鼠标事件),您需要“泵送消息循环”-对于OpenCV HighGUI框架,这意味着要定期运行cv2.waitKey()
来处理和调度所有偶数处理程序,并在必要时执行诸如重绘窗口内容之类的事情。
因此,第一个解决方法是遵循这些原则:
while True:
if not stopped:
frame = input_buffer.get()
cv2.imshow("Video File", frame)
time.sleep(time_frame)
if (cv2.waitKey(1) & 0xFF) == ord('q'):
break
这可以解决您所询问的问题。不幸的是,这还不足以使代码正常运行。
还有其他几个问题:
q
键时,程序挂起问题1 很容易解决,只需减小队列大小即可。
问题2 有点困难。这里的窍门是与实时同步。
首先,您需要记录开始时间-这是您希望显示第一帧的时间。您还必须跟踪显示的帧数,这 包括视频暂停时重复播放的所有帧。
使用此信息,您可以计算出显示下一帧之前要等待的时间,从而保持恒定(正确的)帧速率。
NB: 此处要记住的关键是每次迭代执行的所有操作都需要花费一些时间。除非您对此做出补偿,否则您将落在后面。
问题#3和#4 可以通过添加一个表示停止请求的布尔变量以及为阻塞的Queue
调用添加超时来解决。可以通过按q
键或通过读取器线程到达文件末尾来触发此“停止”信号。
阅读器到达末尾时,会将“停止”标志设置为True
,然后结束。处理线程将读取队列,直到队列为空,最后队列也将结束。
阅读器将针对其读取的每一帧以及插入Queue
时的超时检查“ stop”标志。
脚本:
import threading, time
import cv2
import queue
capFile = cv2.VideoCapture("f:\\roadtrip\\Roadtrip_01_720p.mp4 ")
input_buffer = queue.Queue(20)
fps = capFile.get(cv2.CAP_PROP_FPS)
time_frame = 1.0 / fps
paused = False
finished = False
window_name = 'Video File'
def clickListener(event, x, y, flags, param):
global paused
if event==cv2.EVENT_LBUTTONUP:
print "%s video" % ("Resume" if paused else "Pause")
paused = not paused
def readFile():
global finished
while not finished:
ret, frame = capFile.read()
if not ret:
finished = True
while not finished:
try:
input_buffer.put(frame, timeout=1)
break
except queue.Full:
pass
def processingFile():
global finished
global frame
cv2.namedWindow(window_name)
cv2.setMouseCallback(window_name, clickListener)
start_time = time.time()
frame_number = 0
while True:
if not paused:
try:
frame = input_buffer.get(timeout=1)
cv2.imshow(window_name, frame)
except queue.Empty:
if finished:
break
wait_time = (start_time + frame_number * time_frame) - time.time()
if wait_time > 0:
time.sleep(wait_time)
if (cv2.waitKey(1) & 0xFF) == ord('q'):
finished = True
print "Playback terminated."
break
frame_number += 1
end_time = time.time()
print "Video FPS = %0.3f" % fps
print "Frames rendered = %d (includes repeats during pause)" % frame_number
print "Time taken = %0.3f seconds" % (end_time - start_time)
print "Actual FPS = %0.3f" % (frame_number / (end_time - start_time))
tReadFile = threading.Thread(target=readFile)
tProcessingFile = threading.Thread(target=processingFile)
tReadFile.start()
tProcessingFile.start()
tProcessingFile.join()
tReadFile.join()
控制台输出:
这包括3个较长的暂停时间
Pause video
Resume video
Pause video
Resume video
Pause video
Resume video
Video FPS = 25.000
Frames rendered = 15863 (includes repeats during pause)
Time taken = 635.481 seconds
Actual FPS = 24.962