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

您所在的位置:网站首页 id导出的图片不清晰 python 对视频帧提取图片,并去除变化不大的帧,保留清晰的帧。

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

2024-05-31 05:34| 来源: 网络整理| 查看: 265

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

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

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

# smoothing window sizelen_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



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3