我正在尝试制作一个mjpeg流光。帧前面的前5个字节给出了它的大小。然后我可以提取框架。我需要检查一下是否有正确的框架。我正在尝试将帧写入.jpeg文件;这不起作用。 我这样做了吗?
import os
from array import array
class VideoStream:
def __init__(self,filename):
self.fis = open(filename,'r')
self.frame_nb = 0
def getnextframe(self):
length = 0
frame_length = bytearray(5)
frame_length = self.fis.read(5)
fm = array('B',frame_length[:5])
length = fm[4]+((fm[3]<<8)&0xFF)+((fm[2]<<16)&0xFF)+((fm[1]<<24)&0xFF)+((fm[0]<<32)&0xFF)
frame = self.fis.read(length)
print 'len=',length
test = open("test.jpeg",'w')
test.write(frame)
test.close()
print 'frame=',frame
if __name__=='__main__':
vs = VideoStream("Movie.mjpeg")
vs.getnextframe()
答案 0 :(得分:2)
代码中的长度实际上只是第五个字节的值。将所有其他加数向左移动至少8位,然后屏蔽除8位最低位之外的所有位。由于前面的移位操作,这些位都为零。
可以实现的一个简单的附加测试是,如果帧数据以JPEG开始的图像标记(FF D8
)开始,并以图像结束标记(DD D9
)结束。
以下函数应该迭代JPEG图像,这些图像由五个ascii字符长帧长度计数分隔,就像您移植到Python的VideoStream.java一样:
def iter_frames(filename):
with open(filename, 'rb') as mjpeg_file:
while True:
frame_length_bytes = mjpeg_file.read(5)
if len(frame_length_bytes) != 5:
if frame_length_bytes:
raise ValueError('incomplete length')
else:
break
frame_length = int(frame_length_bytes)
frame = mjpeg_file.read(frame_length)
if len(frame) != frame_length:
raise ValueError('incomplete frame data')
if not (
frame.startswith(b'\xff\xd8') and frame.endswith(b'\xff\xd9')
):
raise ValueError('invalid jpeg')
yield frame
def main():
frames = iter_frames('Movie.mjpeg')
frame = next(frames)
with open('test.jpg', 'wb') as jpeg_file:
jpeg_file.write(frame)
if __name__ == '__main__':
main()
它检查字节计数值和JPEG数据是否完整,以及是否存在JPEG开始和结束标记。
比你想象的要简单得多。但有一个问题:这是一种很可能由Java类作者组成的格式。
MJPEG只是一个视频编解码器,它基本上只是连接的JPEG图像。但它很少出现“原始”格式,但嵌入在容器格式中,其元信息就像是MJPEG数据,帧速率,音频等等。
其中一种格式是AVI,就像您在评论中引用的example MJPEG avi一样。
将这样的文件中的帧提取为单个JPEG图像比读取前缀为简单长度信息的JPEG图像然后在一个文件中连接起来要多一些工作。人们需要实现一个AVI阅读器,它能够充分理解AVI格式以获得帧数据。然后是一个JPEG读取器,它能够理解足够的JPEG格式来读取整个帧,因为它们是在没有任何长度信息的情况下背靠背保存的。
下一个问题是并非所有MJPEG都包含可用作独立JPEG图像的帧。有些人缺少解压缩图像数据所需的数据表(霍夫曼表)。 MJPEG编解码器的AVI规范中有一个固定的表。该表由软件用于解码,并且在保存为JPEG文件时必须将其注入帧中。
最后一个“事物”:隔行扫描视频不包含完整图像,但需要将两个连续图像组合成一个。每个图像包含每隔一行。您给定的example MJPEG avi 这样的视频。在不解码,逐行扫描和重新编码的情况下提取帧时,每个图像只有视频高度的一半。
为了更好地了解单个图像的外观,ffmpeg
命令行提取帧数据并注入丢失的数据表,以获得独立的JPEG图像:
ffmpeg -i bowlerhatdancer.sleepytom.SGP.mjpeg.avi \
-c:v copy -bsf:v mjpeg2jpeg frame_%04d.jpg