寻找替代方案,因为OpenCV不会为live相机流(在Windows上)提供时间戳,这是我的计算机视觉算法所必需的,我找到了ffmpeg,这篇优秀的文章https://zulko.github.io/blog/2013/09/27/read-and-write-video-frames-in-python-using-ffmpeg/使用了ffmpeg,访问了它的标准输出(stdout)流。我还将其扩展为读取标准错误(stderr)流。
在windows上编写python代码时,我从ffmpeg stdout接收到视频帧,但是stderr在为第一帧提供显示信息视频过滤器详细信息(时间戳)之后冻结了。
我记得在ffmpeg论坛的某个地方看到,像显示信息这样的视频过滤器在重定向时会被绕过。这就是为什么下面的代码不能按预期工作吗?
期望:它应该将视频帧写入磁盘,以及打印时间戳细节。
实际:它写视频文件,但没有得到时间戳(显示信息)的细节。
下面是我尝试过的代码:
import subprocess as sp
import numpy
import cv2
command = [ 'ffmpeg',
'-i', 'e:\sample.wmv',
'-pix_fmt', 'rgb24',
'-vcodec', 'rawvideo',
'-vf', 'showinfo', # video filter - showinfo will provide frame timestamps
'-an','-sn', #-an, -sn disables audio and sub-title processing respectively
'-f', 'image2pipe', '-'] # we need to output to a pipe
pipe = sp.Popen(command, stdout = sp.PIPE, stderr = sp.PIPE) # TODO someone on ffmpeg forum said video filters (e.g. showinfo) are bypassed when stdout is redirected to pipes???
for i in range(10):
raw_image = pipe.stdout.read(1280*720*3)
img_info = pipe.stderr.read(244) # 244 characters is the current output of showinfo video filter
print "showinfo output", img_info
image1 = numpy.fromstring(raw_image, dtype='uint8')
image2 = image1.reshape((720,1280,3))
# write video frame to file just to verify
videoFrameName = 'Video_Frame{0}.png'.format(i)
cv2.imwrite(videoFrameName,image2)
# throw away the data in the pipe's buffer.
pipe.stdout.flush()
pipe.stderr.flush()
因此,如何仍然从ffmpeg获得帧时间戳到python代码,以便在我的计算机视觉算法中使用。
发布于 2017-02-24 15:53:00
重定向stderr在python中工作。
所以,而不是这个pipe = sp.Popen(command, stdout = sp.PIPE, stderr = sp.PIPE)
做这个pipe = sp.Popen(command, stdout = sp.PIPE, stderr = sp.STDOUT)
我们可以通过添加异步调用来读取ffmpeg的标准流(stdout和stderr)来避免重定向。这将避免视频帧和时间戳的任何混合,从而避免容易发生错误的分离。因此,修改原始代码以使用threading
模块如下所示:
# Python script to read video frames and timestamps using ffmpeg
import subprocess as sp
import threading
import matplotlib.pyplot as plt
import numpy
import cv2
ffmpeg_command = [ 'ffmpeg',
'-nostats', # do not print extra statistics
#'-debug_ts', # -debug_ts could provide timestamps avoiding showinfo filter (-vcodec copy). Need to check by providing expected fps TODO
'-r', '30', # output 30 frames per second
'-i', 'e:\sample.wmv',
'-an','-sn', #-an, -sn disables audio and sub-title processing respectively
'-pix_fmt', 'rgb24',
'-vcodec', 'rawvideo',
#'-vcodec', 'copy', # very fast!, direct copy - Note: No Filters, No Decode/Encode, no quality loss
#'-vframes', '20', # process n video frames only. For Debugging
'-vf', 'showinfo', # showinfo videofilter provides frame timestamps as pts_time
'-f', 'image2pipe', 'pipe:1' ] # outputs to stdout pipe. can also use '-' which is redirected to pipe
# seperate method to read images on stdout asynchronously
def AppendProcStdout(proc, nbytes, AppendList):
while proc.poll() is None: # continue while the process is alive
AppendList.append(proc.stdout.read(nbytes)) # read image bytes at a time
# seperate method to read image info. on stderr asynchronously
def AppendProcStderr(proc, AppendList):
while proc.poll() is None: # continue while the process is alive
try: AppendList.append(proc.stderr.next()) # read stderr until empty
except StopIteration: continue # ignore stderr empty exception and continue
if __name__ == '__main__':
# run ffmpeg command
pipe = sp.Popen(ffmpeg_command, stdout=sp.PIPE, stderr=sp.PIPE)
# 2 threads to talk with ffmpeg stdout and stderr pipes
framesList = [];
frameDetailsList = []
appendFramesThread = threading.Thread(group=None, target=AppendProcStdout, name='FramesThread', args=(pipe, 1280*720*3, framesList), kwargs=None, verbose=None) # assuming rgb video frame with size 1280*720
appendInfoThread = threading.Thread(group=None, target=AppendProcStderr, name='InfoThread', args=(pipe, frameDetailsList), kwargs=None, verbose=None)
# start threads to capture ffmpeg frames and info.
appendFramesThread.start()
appendInfoThread.start()
# wait for few seconds and close - simulating cancel
import time; time.sleep(2)
pipe.terminate()
# check if threads finished and close
appendFramesThread.join()
appendInfoThread.join()
# save an image per 30 frames to disk
savedList = []
for cnt,raw_image in enumerate(framesList):
if (cnt%30 != 0): continue
image1 = numpy.fromstring(raw_image, dtype='uint8')
image2 = image1.reshape((720,1280,3)) # assuming rgb image with size 1280 X 720
# write video frame to file just to verify
videoFrameName = 'video_frame{0}.png'.format(cnt)
cv2.imwrite(videoFrameName,image2)
savedList.append('{} {}'.format(videoFrameName, image2.shape))
print '### Results ###'
print 'Images captured: ({}) \nImages saved to disk:{}\n'.format(len(framesList), savedList) # framesList contains all the video frames got from the ffmpeg
print 'Images info captured: \n', ''.join(frameDetailsList) # this contains all the timestamp details got from the ffmpeg showinfo videofilter and some initial noise text which can be easily removed while parsing
发布于 2017-02-14 16:50:44
您可以使用MoviePy
import moviepy.editor as mpy
vid = mpy.VideoFileClip('e:\\sample.wmv')
for timestamp, raw_img in vid.iter_frames(with_times=True):
# do stuff
发布于 2018-05-08 13:52:23
您可以尝试指定缓冲区大小,以确保整个帧都符合它的要求:
bufsize = w*h*3 + 100
pipe = sp.Popen(command, bufsize=bufsize, stdout = sp.PIPE, stderr = sp.PIPE)
通过这种设置,您通常可以在pipe.stdout上读取帧,pipe.stderr可以读取其信息。
https://stackoverflow.com/questions/42230269
复制相似问题