介绍最近需要用到这样一个功能,但是在网上搜了一下方法。简单易行的方法估计只是帧间差异。网上的帧间差异代码在我的Pycharm上好像跑不了。没办法,只好手写一个。之前没有接触过cv代码,所以算是一个入门的机会,希望源码可以帮助到大家。代码的效率可能不是最高的,尤其是两个关键循环还是可以优化的。在这篇博文中,我主要是想和大家分享思路,也希望能和大家一起探讨相关的内容。急需的也可以直接跳到后面,附上完整的代码链接和使用方法。思路逐帧导入视频文件,处理差异差异值列表在平滑窗口中寻找峰值剪切视频关键帧操作准备工作我们需要定义这样一个类KeyFrameGetter,然后将可能的参数写入构造函数:def__init__(self,video_path,img_path,window=25):'''在模型中定义参数。:paramvideo_path:指向电影数据的路径,str:paramimg_path:我们保存图像的路径,str:paramwindow:决定peek服务范围的比较域。'''self.window=window#关键帧数self.video_path=video_path#视频路径self.img_path=img_path#图像存储路径self.diff=[]#差值列表self.idx=[]#选择帧列表。窗口是后面选择峰值的窗口值。稍后将解释导入视频文件。接下来,我们需要导入视频文件。我们已经把视频路径放在了__init__中,所以可以直接调用:defload_diff_between_frm(self,smooth=True,alpha=0.07):'''计算得到modelparam:paramsmooth:决定是否要平滑差异。:paramalpha:差异因子:return:'''打印(“load_diff_between_frm")cap=cv2.VideoCapture(self.video_path)#打开视频文件diff=[]frm=0pre_image=np.array([])curr_image=np.array([])whileTrue:frm=frm+1success,data=cap.read()ifnotsuccess:break#这里写接下来要处理的函数体cap=cv2.VideoCapture(self.video_path)是调用cv2库直接打开视频文件,路径视频文件是self.video_pathdiff是计算差值的矩阵frm,存储差值,是记录轮数的变量,为了计算差值,用pre_image和curr_image记录之前的两张图片andafter.从whileTrue开始,正式遍历每一帧,success,data=cap.read()是读取每一帧,读取完最后一帧success会返回False,此时退出循环并逐帧处理,关键是每一帧如何处理:第一:iffrm==1:pre_image=datacurr_image=dataelse:pre_image=curr_imagecurr_image=data如果是第一帧,那么就没办法区别了,所以存储了自己前后的数据;如果是下一帧,则进入下一轮的时间将curr_image保存到pre_image。接下来会进行图像处理和差分:diff.append(abs_diff(pre_image,curr_image))这句话中abs_diff就是计算差分值。我们还没有编写这个函数。现在定义这个函数:defprocess_image(image):'''GrayingandGaussianBlur:paramimage:图像矩阵,np.array:return:处理后的图像矩阵,np.array'''gray_image=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)#灰度Gray_image=cv2.GaussianBlur(gray_image,(3,3),0)#高斯滤波器returngray_imagedefabs_diff(pre_image,curr_image):'''计算pre_image和curr_image的绝对差值:parampre_image:The过去一帧的图像,np.array:paramcurr_image:当前帧的图像,np.array:return:'''gray_pre_image=process_image(pre_image)gray_curr_image=process_image(curr_image)diff=cv2.absdiff(gray_pre_image,gray_curr_image)res,diffh=cv2.(diff,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)#fixme:这里先写成简单求和的形式cnt_diff=np.sum(np.sum(diff))returncnt_diff的思路是:将预处理前后的两幅图像进行灰度化和高斯滤波计算绝对差值,然后二值化并将矩阵中的所有值相加作为差值进行讨论:该方法可能粗暴一点,因为相当于把整个矩阵压缩成了一维,但是我们只是测量帧与帧之间的间隙,所以这个信息丢失问题不大,diff)#标准化数据self.diff=np.array(diff)mean=np.mean(self.diff)dev=np.std(self.diff)self.diff=(self.diff-mean)/dev#内部完成self.pick_idx()wheresmooth是一个布尔参数,即平滑时要传入的参数。当然我们这里要写平滑的代码hhh,也不能偷懒,因为平滑后选择的峰值更具有代表性(会去掉一些烦人的毛刺),接下来我们需要对数据进行标准化处理,最后调用self.pick_idx()函数选择关键帧并保存。对于指数平滑,我参考了别人的代码,做了一些改进:defexponential_smoothing(alpha,s):'''Primaryexponentialsmoothing:paramalpha:Smoothingfactor,num:params:Listofdata,list:return:List平滑后的数据,列出'''s_temp=[s[0]]print(s_temp)foriinrange(1,len(s),1):s_temp.append(alpha*s[i-1]+(1-alpha)*s_temp[i-1])returns_temp满足:$y_{t+1}^{\prime}=y_{t}^{\prime}+\alpha\cdot\left(y_{t}-y_{t}^{\prime}\right)$其中$\alpha$为函数传入的参数alpha。在窗口中找到峰值假设我要找到一些关键帧,我需要找到差异值较大的部分,一种方法是在所有差异值中找到顶部的$k$,其中$k$是输入requirementvalue,但是这种情况下,峰值后面的一些值会被算进去,会导致类似的场景被重复抓取。解决方法:将一阶差分改为二阶差分(恢复索引比较麻烦,强制DDL,所以放弃这种方法。使用在窗口中查找最大值的方法。这个方法的复杂度和二阶差分差不多,所以我用了这个方法:defpick_idx(self):'''获取符合我们想要的帧的索引(peekinthewindow):return:'''print("pick_idx")fori,dinenumerate(self.diff):ub=len(self.diff)-1lb=0如果不是i-self.window//2
