龙空技术网

推荐一种视频抽帧的实现方式

南站往南 101

前言:

目前朋友们对“mp算法代码”都比较重视,各位老铁们都想要学习一些“mp算法代码”的相关知识。那么小编也在网络上汇集了一些关于“mp算法代码””的相关文章,希望看官们能喜欢,你们一起来了解一下吧!

抽帧

一个高帧率的视频流,AI准备处理的过程中,并不一定每个帧都需要进行处理。

例如大部分视频是25FPS,对应每张图的间隔大概是40ms,如果要做行人检测,那肯定不需要每个帧都检测,在视频画面内,人的移动速度有限,为了节省计算资源,我们可能进行抽帧处理。

比方只需要3FPS的图像处理即可,计算一下大概平均图,检测一次即可。

视频读取

视频和图像处理比较流行的组件就是opencv,使用opencv来读取视频实现也是比较简单。

opencv

我们使用unittest框架来写一个性能测试的TestCase,每个用例自动初始化视频读取的cap,用例执行完毕销毁。使用到setUp和tearDown方法,在每个test执行开始之前和结束之后会自动调用。

如下,setUp中初始化cap,用原视频帧率和我们期望检测的帧率做一个计算得到skip,方便处理向下取整。

 def setUp(self) -> None:   self.cap = cv2.VideoCapture(filename=self.mp,                                apiPreference=cv2.CAP_ANY)  # 获取帧率  self.fps = self.cap.get(cv2.CAP_PROP_FPS)  self.cap.set(cv2.CAP_PROP_MODE, 0)  self.frame_count = self.cap.get(cv2.CAP_PROP_FRAME_COUNT)  # 输出帧率  self.out_rate = 3  logging.warning(f"fps:{self.fps}")  self.ext = ".jpg"  self.skip = int(self.fps//self.out_rate)                  self.cur_frame = 0

用例执行完毕销毁cap

 def tearDown(self) -> None: 	self.cap.release()

定位帧来实现抽帧处理(不太好的方式)

抽帧比较很有意思了, 我们想到了数组下标,对于这些帧,我们设定读取的帧的位置是不是就很简单,cv2对于视频cap确实有一个这个属性设置cv2.CAP_PROP_POS_MSEC

关键代码如下,我们通过设置当前帧self.cur_frame,但不是连续的,而是按照skip跳转的,这样就很方便能够达到我们效果。

while ret:	self.cap.set(cv2.CAP_PROP_POS_MSEC, self.cur_frame)	ret, frame = self.cap.read()	self.cur_frame += self.skip	count += self.skip	handle_count += 1	if not ret:		logging.error("数据获取完成")		break

看下生成的图片,我们将帧号写入文件名称中,间隔刚好8倍数的帧正常输出

略过非关键帧方式(推荐)

我们是不是非要想着直接去取我们想要的帧? 因为这种方式性能慢。这就是比较神奇的地方,我开始也以为采用定位帧,设置cv2.CAP_PROP_POS_MSEC的方式,就像数组的下标,性能应该很快呀,经过实测,这个索引越大,几万几十万的时候,性能会下降,而且这个速度也不一定是最快的。

格局打开,如果能够略过我们不想要的帧,遇到我们想识别的帧再去解码,是不是就达到我们目的了呢?那我们期望cv2有两种功能的函数,一种是只遍历,不解码数据;一种是遍历要解码数据。

认真翻了一下opencv的官网,还真找到了。 grab()就是仅仅遍历的目的,它通过retrieve()去获取数据,而且这个retrieve()可以设置参数是否解码,方法如下,这个我也很无聊的测试了下,这样读取数据,每秒钟能读几千帧,基本就是IO,本文我就不展开了。

self.cap.set(cv2.CAP_PROP_FORMAT, -1)

那我们换个策略,每个skip个帧,read一下,其他帧都进行grab处理,这样就能达到我们的效果了。代码如下

while ret:	if self.cur_frame % self.skip == 0:  	# print(f"输出:{slice_sn}")		ret, frame = self.cap.read()	else:		ret = self.cap.grab()		frame = None	self.cur_frame += 1	if not ret:		logging.error("数据获取完成")		break

定位和跳过的性能比较

两种方式我们都实现了,我用自己的MAC来测试了一下,加上一些统计打印,看看性能测试如何呢。

定位帧的方式,每秒大概20+帧

定位帧方式抽帧的性能结果

采用grab跳过方式,每秒大概47+帧左右,非常稳定。

grab跳过方式抽帧性能测试结果

从结果也可以看出,grab方式速度还是快了很多。那抽帧用grab方式更合适。

另外为了加速,还可以采用GPU解码,本文就不展开了。

标签: #mp算法代码