Я пытаюсь обрабатывать видео с помощью OpenCV и Python.
Я работаю с двумя потоками, один для чтения кадров, а другой для их отображения. Теперь я пытаюсь остановить видео и возобновить его воспроизведение, установив функцию обратного вызова щелчка с помощью setMouseCallback.
Код работает до тех пор, пока я не остановлю видео в первый раз, после этого он не перехватывает событие щелчка снова, чтобы иметь возможность возобновить воспроизведение, и повторное нажатие перестает работать.
Вот мой код:
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()
Вы хоть представляете, что может происходить?
Есть еще пара проблем, а именно: он зависает, когда вы выходите раньше и когда вы доходите до конца видео. Также неподходящее время и слишком большой размер очереди. Кроме времени, вот фиксированный сценарий - я исправлю время и закончу выжимать полный ответ завтра.
@ DanMašek Привет, спасибо, что ответили мне. Я просматривал ваш код, я понимаю, что вы мне говорите о том, что cv2.waitKey ()
не запущен. Большое спасибо за ваши исправления, единственное, что я не до конца понимаю, - это окончательный код после tProcessingFile.join()
.
О, похоже на рудиментарный код до того, как я добавил таймауты к обоим блокирующим вызовам. Спасибо что подметил это.
Ваша основная проблема заключается в этом цикле:
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
, вы просто входите в бесконечный цикл, который вообще ничего не делает. К сожалению, для того, чтобы графический интерфейс продолжал работать (включая обработку событий мыши), вам нужно «прокачать цикл сообщений» - в случае фреймворка 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
.
Сценарий:
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
PS: Если вам интересно, с каким видео я его тестировал, это Вот этот.
Когда он остановлен, вы не используете
cv2.waitKey()
, поэтому события мыши не обрабатываются. Вам всегда нужно запускать эту функцию для обработки всех событий графического интерфейса пользователя, чтобы графический интерфейс оставался отзывчивым.