news 2026/5/6 9:05:29

python 对视频帧提取图片,并去除变化不大的帧,保留清晰的帧。

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
python 对视频帧提取图片,并去除变化不大的帧,保留清晰的帧。

见代码,改进了对于大视频文件会溢出的问题

处理的视频文件下载:https://download.csdn.net/download/babytiger/12732306

# 视频输入目录
input_dir=r'E:\video\xueshan\1'
# 图像输出目录
output_dir=r'e:/image/'

# smoothing window size
len_window= int(25)
len_window 如果改大到100,会使局部运行感知不明显,图中的松鼠比较小,就不会被感知到,程序会认为整体没啥变化就不会生成关键帧


上面的要自行设定,实测一个小视频900多帧,下面程序提取出23多个有效帧

# -*- coding: utf-8 -*- import cv2 import operator import numpy as np import matplotlib.pyplot as plt import os from scipy.signal import argrelextrema # Setting fixed threshold criteria USE_THRESH = False # fixed threshold value THRESH = 0.6 # Setting fixed threshold criteria USE_TOP_ORDER = False # Setting local maxima criteria USE_LOCAL_MAXIMA = True # Number of top sorted frames NUM_TOP_FRAMES = 20 # 视频输入目录 input_dir=r'E:\video\xueshan\1' # 图像输出目录 output_dir=r'e:/image/' # smoothing window size len_window = int(25) def smooth(x, window_len=13, window='hanning'): """smooth the data using a window with requested size. This method is based on the convolution of a scaled window with the signal. The signal is prepared by introducing reflected copies of the signal (with the window size) in both ends so that transient parts are minimized in the begining and end part of the output signal. input: x: the input signal window_len: the dimension of the smoothing window window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman' flat window will produce a moving average smoothing. output: the smoothed signal example: import numpy as np t = np.linspace(-2,2,0.1) x = np.sin(t)+np.random.randn(len(t))*0.1 y = smooth(x) see also: numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve scipy.signal.lfilter TODO: the window parameter could be the window itself if an array instead of a string """ print(len(x), window_len) # if x.ndim != 1: # raise ValueError, "smooth only accepts 1 dimension arrays." # # if x.size < window_len: # raise ValueError, "Input vector needs to be bigger than window size." # # if window_len < 3: # return x # # if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']: # raise ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'" s = np.r_[2 * x[0] - x[window_len:1:-1], x, 2 * x[-1] - x[-1:-window_len:-1]] # print(len(s)) if window == 'flat': # moving average w = np.ones(window_len, 'd') else: w = getattr(np, window)(window_len) y = np.convolve(w / w.sum(), s, mode='same') return y[window_len - 1:-window_len + 1] # Class to hold information about each frame class Frame: def __init__(self, id, frame, value): self.id = id self.frame = frame self.value = value def __lt__(self, other): if self.id == other.id: return self.id < other.id return self.id < other.id def __gt__(self, other): return other.__lt__(self) def __eq__(self, other): return self.id == other.id and self.id == other.id def __ne__(self, other): return not self.__eq__(other) def rel_change(a, b): if(max(a,b)!=0): x = (b - a) / max(a, b) print(x) else: return 0 return x def write_frames(dir,filename): if USE_TOP_ORDER: # sort the list in descending order frames.sort(key=operator.attrgetter("value"), reverse=True) for keyframe in frames[:NUM_TOP_FRAMES]: name = "frame_" + str(keyframe.id) + ".jpg" cv2.imwrite(dir + "/" + filename+'_'+name, keyframe.frame) if USE_THRESH: print("Using Threshold") for i in range(1, len(frames)): if (rel_change(np.float(frames[i - 1].value), np.float(frames[i].value)) >= THRESH): # print("prev_frame:"+str(frames[i-1].value)+" curr_frame:"+str(frames[i].value)) name = "frame_" + str(frames[i].id) + ".jpg" cv2.imwrite(dir + "/" + filename+'_'+name, frames[i].frame) if USE_LOCAL_MAXIMA: print("Using Local Maxima") diff_array = np.array(frame_diffs) sm_diff_array = smooth(diff_array, len_window) frame_indexes = np.asarray(argrelextrema(sm_diff_array, np.greater))[0] for i in frame_indexes: name = "frame_" + str(frames[i - 1].id) + ".jpg" print(dir+name) cv2.imwrite(dir + "/" + filename+'_'+ name, frames[i - 1].frame) frame_diffs = [] frames = [] def all_path(dirname): for maindir, subdir, file_name_list in os.walk(dirname): for fn in file_name_list: file_path = os.path.join(maindir, fn) # 合并成一个完整路径 (filepath,tempfilename) = os.path.split(file_path) (filename,extension) = os.path.splitext(tempfilename) if not os.path.exists(output_dir+filename+'/'): os.mkdir(output_dir+filename+'/') videopath =file_path # Directory to store the processed frames dir = output_dir+filename print("Video :" + videopath) print("Frame Directory: " + dir) cap = cv2.VideoCapture(str(videopath)) curr_frame = None prev_frame = None ret, frame = cap.read() i = 1 while (ret): luv = cv2.cvtColor(frame, cv2.COLOR_BGR2LUV) curr_frame = luv if curr_frame is not None and prev_frame is not None: # logic here diff = cv2.absdiff(curr_frame, prev_frame) count = np.sum(diff) frame_diffs.append(count) frame = Frame(i, frame, count) frames.append(frame) print(filename,i ) #防止载入大视频内存溢出,每1000帧清空一次 if(i%1000==0): write_frames(dir,filename) frame_diffs.clear() frames.clear() prev_frame = curr_frame i = i + 1 ret, frame = cap.read() cv2.imshow('frame',luv) if cv2.waitKey(1) & 0xFF == ord('q'): break write_frames(dir,filename) cap.release() all_path(input_dir) cv2.destroyAllWindows()

但是这23帧中看到还有不少很相似的,于是用下面代码再次去重

代码中的,用户可以根据自己情况调整,大概原理是把图像缩小到46*46,计算相似哈希

#相似度,如果大于max_ssim就进行删除
max_ssim=0.6

又去重了7张,效果如下

# -*- coding: utf-8 -*- import os import cv2 from skimage.measure import compare_ssim import shutil def yidong(filename1,filename2): shutil.move(filename1,filename2) def delete(filename1): os.remove(filename1) #相似度,如果大于max_ssim就进行删除 max_ssim=0.5 #利用拉普拉斯算子计算图片的二阶导数,反映图片的边缘信息,同样事物的图片, # 清晰度高的,相对应的经过拉普拉斯算子滤波后的图片的方差也就越大。 def getImageVar(image): img2gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) imageVar = cv2.Laplacian(img2gray, cv2.CV_64F).var() return imageVar if __name__ == '__main__': img_path = r'E:\image\xuedi' del_list = [] img_files = [os.path.join(rootdir, file) for rootdir, _, files in os.walk(img_path) for file in files if (file.endswith('.jpg'))] for currIndex, filename in enumerate(img_files): if not os.path.exists(img_files[currIndex]): print('not exist', img_files[currIndex]) break new_cur = 0 for i in range(10000000): currIndex1 =new_cur if currIndex1 >= len(img_files) - currIndex - 1: break else: size = os.path.getsize(img_files[currIndex1 + currIndex + 1]) if size < 64: # delete(img_files[currIndex + 1]) del_list.append(img_files.pop(currIndex1 + currIndex + 1)) else: img = cv2.imread(img_files[currIndex]) #计算图像清晰度 var_img=getImageVar(img) img = cv2.resize(img, (46, 46), interpolation=cv2.INTER_CUBIC) img1 = cv2.imread(img_files[currIndex1 + currIndex + 1]) # 计算图像清晰度 var_img1=getImageVar(img1) img1 = cv2.resize(img1, (46, 46), interpolation=cv2.INTER_CUBIC) ssim = compare_ssim(img, img1, multichannel=True) if ssim > max_ssim: #当达到了门限,比较两个图像的清晰度,把不清晰的放入删除列表中 print(var_img,var_img1,img_files[currIndex], img_files[currIndex1 + currIndex + 1], ssim) if(var_img>var_img1): del_list.append(img_files.pop(currIndex1 + currIndex + 1)) else: del_list.append(img_files.pop(currIndex)) new_cur = currIndex1 else: new_cur = currIndex1 + 1 if ssim > 0.4: print('small_ssim',img_files[currIndex], img_files[currIndex1 + currIndex + 1], ssim) for image in del_list: #delete(image) print('delete',image)

去重过程中会保留更清晰的帧,比较两个图像的清晰度,把不清晰的放入删除列表中

#利用拉普拉斯算子计算图片的二阶导数,反映图片的边缘信息,同样事物的图片,
# 清晰度高的,相对应的经过拉普拉斯算子滤波后的图片的方差也就越大。
def getImageVar(image):
img2gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
imageVar = cv2.Laplacian(img2gray, cv2.CV_64F).var()
return imageVar

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/6 9:04:28

3个核心步骤实现NCM文件批量转换:ncmdumpGUI完整使用指南

3个核心步骤实现NCM文件批量转换&#xff1a;ncmdumpGUI完整使用指南 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换&#xff0c;Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 网易云音乐用户经常面临一个困扰&#xf…

作者头像 李华
网站建设 2026/5/6 9:04:27

【头歌系统Python实验】学习-Python元组之应用一

如果对你有帮助的话&#xff0c;不妨点赞收藏评论一下吧&#xff0c;爱你么么哒&#x1f618;❤️❤️❤️第1关&#xff1a;学习-Python元组之应用一任务描述本关任务&#xff1a;给定 a、b、c 三个变量&#xff0c;三个变量的值都是通过 input 函数获取&#xff0c;且都是整型…

作者头像 李华
网站建设 2026/5/6 9:01:44

3分钟掌握微信聊天记录解密:本地化数据恢复终极指南

3分钟掌握微信聊天记录解密&#xff1a;本地化数据恢复终极指南 【免费下载链接】WechatDecrypt 微信消息解密工具 项目地址: https://gitcode.com/gh_mirrors/we/WechatDecrypt 你是否曾因更换手机而丢失珍贵的微信聊天记录&#xff1f;或是误删了重要的商务对话却无法…

作者头像 李华
网站建设 2026/5/6 8:58:49

深入解析“点击者游戏”的赌博逻辑

在游戏开发中,引入随机元素不仅能提升游戏的趣味性,还能激发玩家的参与感。今天,我们将详细探讨一个名为“点击者游戏”的简单游戏中的赌博机制,分析其代码实现,并提出改进建议。 游戏概述 “点击者游戏”是一个典型的点击游戏,玩家通过点击按钮来积累点数,并可以使用…

作者头像 李华
网站建设 2026/5/6 8:57:58

能源转型智能MCP服务器:AI驱动的实时能源数据分析与决策工具

1. 能源转型智能MCP服务器&#xff1a;为AI代理注入实时能源洞察 如果你正在能源投资、电网运营或气候金融领域工作&#xff0c;每天面对海量、分散且格式不一的能源数据&#xff0c;那么你肯定理解那种“数据沼泽”的痛苦。从美国能源信息署&#xff08;EIA&#xff09;的发电…

作者头像 李华