python 查找重复文件,以及查找重复视频的一些思路 您所在的位置:网站首页 查询重复文件怎么查 python 查找重复文件,以及查找重复视频的一些思路

python 查找重复文件,以及查找重复视频的一些思路

2024-02-02 07:59| 来源: 网络整理| 查看: 265

查找重复文件(文件大小一致、md5相同)

思路很简单:

找出指定目录及子目录下所有文件找出大小重复的进一步确认md5也重复的,则认为是重复文件

这里md5,为了加速计算,没有算文件的完整md5。(之前看到过这种算法,忘了在哪里看来的,大概是用于上传文件时,快速判断是否与已有文件对比验证用的)将文件分成256块,每块取前8个字节计算md5,这样能快速计算出一个大概可以用于判断文件唯一性的md5。

完整代码如下:

#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os import re import time import hashlib def main(): path = 'd:/' fp_arr = file_search(path,repat=r'.*\.mp4') # 查找文件(文件类型自行填写,不写查所有文件类型) du_arr = find_duplicate_file(fp_arr) # 检查重复 # [fp_arr.remove(l) for j in [i[1:] for i in du_arr] for l in j] # 去重,重复文件只保留第1个即可 def file_search(path='.',repat = r'.*'): """ 文件查找: 文件夹及子文件夹下,所有匹配文件,返回list文件列表,绝对路径形式 Args: path: 文件路径(默认当前路径) repat: 文件名正则匹配,不区分大小写(默认匹配所有文件) return: 文件列表(绝对路径) Returns: files_match: 文件列表 """ # 获取文件夹,及子文件夹下所有文件,并转为绝对路径 folders,files = list(),list() st = time.time() repat = '^'+repat+'$' # walk结果形式 [(path:文件夹,[dirlist:该文件夹下的文件夹],[filelist:该文件夹下的文件]),(子文件夹1,[子子文件夹],[]),(子文件夹2,[],[])...] # 该遍历会走遍所有子文件夹,返回上述形式的结果信息。 for record in os.walk(path): fop = record[0] folders.append(fop) for fip in record[2]: fip = os.path.abspath(os.path.join(fop,fip)).replace('\\','/') files.append(fip) # 逐个检查是否符合要求 files_match = list() for file in files: a = re.findall(repat,file.lower()) if a: files_match+=a print('找到{0}个文件'.format(len(files_match))) # 返回满足要求的 return files_match def fastmd5(file_path,split_piece=256,get_front_bytes=8): """ 快速计算一个用于区分文件的md5(非全文件计算,是将文件分成s段后,取每段前d字节,合并后计算md5,以加快计算速度) Args: file_path: 文件路径 split_piece: 分割块数 get_front_bytes: 每块取前多少字节 """ size = os.path.getsize(file_path) # 取文件大小 block = size//split_piece # 每块大小 h = hashlib.md5() # 计算md5 if size } # 临时词典 {文件大小1:[文件路径1,文件路径2,……], 文件大小2:[文件路径1,文件路径2,……], ……} for f in f_arr: fmd5 = fastmd5(f) d[fmd5]=d.get(fmd5,list())+[f] # 找到相同md5的文件 for k in d: # 相同大小的文件,核对一下md5是否一致 if len(d[k])>1: ll.append(d[k]) print('查重完毕,发现{0}处重复'.format(len(ll))) for i in ll: print(i) return ll if __name__ == '__main__': main() 视频查重(部分完成)

思路:对视频进行抽帧,然后比对是否有关键帧的图片指纹是否一致

这里写一下研究过程,实现代码:

视频抽帧图像指纹生成找出包含同样图像指纹的视频

这个过程试过一些方案也都记录一下: 曾经考虑subprocess.Popen()执行ffmpeg抽帧,但是太慢了

def external_cmd(cmd, msg_in=''): # 将subprocess.call(cmd)包装了一下,这样就能获取到执行cmd命令时,产生的输出内容了。 try: proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout_value, stderr_value = proc.communicate(msg_in) return stdout_value, stderr_value except ValueError as err: # log("ValueError: %s" % err) return None, None except IOError as err: # log("IOError: %s" % err) return None, None '''方法一''' # 1秒抽0.05帧,也就是20s抽1帧,1420s长度视频抽73镇,耗时94s external_cmd('ffmpeg -i "{0}" -r 0.05 -q:v 2 -f image2 ./%08d.000000.jpg'.format(video_path)) '''方法二''' # 20s抽1帧,1420s长度视频抽70帧,耗时18s timeF = 20 for i in range(1,video_duration//timeF): h,m,s = (i*timeF)//3600, ((i*timeF)%3600)//60, (i*timeF)%60 external_cmd('ffmpeg -i "{0}" -ss {1:0=2}:{2:0=2}:{3:0=2} -vframes 1 {4}.jpg'.format(video_path,h,m,s,i)) # 抽取指定时间点起的第一帧 '''方法三''' # 20s抽1帧,1420s长度视频抽70帧,并压缩到100*100耗时17s(对图像的压缩处理基本不影响速度,时间开销的大头也不是出在文件存储上,而是ffmpeg定位时间为位置然后抽帧本身就慢) timeF = 20 for i in range(1,video_duration//timeF): h,m,s = (i*timeF)//3600, ((i*timeF)%3600)//60, (i*timeF)%60 hw = '{0}x{0}'.format(100) external_cmd('ffmpeg -i "{0}" -ss {1:0=2}:{2:0=2}:{3:0=2} -vframes 1 -s {5} -f image2 {4}.jpeg'.format(video_path,h,m,s,i,hw))

最后选定的还是cv2抽帧

这个是一开始想的,将抽到的帧保存为单张图像,发现还是慢。 ''' # 视频抽帧测试,这种抽帧方式太慢了,1000帧大概45秒长度视频,花费5秒左右 videopath = '01.mp4' vc = cv2.cv2.VideoCapture(videopath) if vc.isOpened(): # 是否正常打开 rval,frame = vc.read() else: rval = False timeF =1000 # 抽帧频率 c = 1 while rval: rval,frame = vc.read() if(c%timeF==0): cv2.imwrite('{0:0=3}.jpg'.format(c),frame) cv2.waitKey(1) c+=1 vc.release() ''' 然后换成了这种,不存图像了,直接将抽到图像计算成dhash保存,总算速度上来了。 # 视频,取指定时间点图片,转指定宽高后,计算图像指纹 v = 'c:/users/kindle/desktop/test/01.mp4' cap = cv2.VideoCapture(v) #打开视频文件 n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) #视频的帧数 fps = cap.get(cv2.CAP_PROP_FPS) #视频的帧率 dur = n_frames / fps #视频的时间 cap.set(cv2.CAP_PROP_POS_MSEC, (5*1000)) # 跳到指定时间点,单位毫秒 success, image_np = cap.read() # 返回该时间点的,图像(numpy数组),及读取是否成功 img = Image.fromarray(cv2.cvtColor(image_np,cv2.COLOR_BGR2RGB)) # 转成图像格式 imgrsz = img.resize((100,100)) # 缩放到指定宽高(后来发现是否缩放基本不影响) # imgrsz.save('5.jpg') # 保存图像 # imgrsz.show() # 显示图像

计算图像指纹,直接用了现成的模块,imagehash里的dhash

h5 = str(imagehash.dhash(imgrsz)) # 生成图像指纹

在上述基础上,视频转换为图像指纹组的函数基本如下

def video2imageprint(filepath): """ 返回整个视频的图片指纹列表 从3秒开始,每60秒抽帧,计算一张图像指纹 """ cap = cv2.VideoCapture(filepath) ##打开视频文件 n_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) #视频的帧数 fps = cap.get(cv2.CAP_PROP_FPS) #视频的帧率 dur = n_frames / fps *1000 #视频大致总长度 cap_set = 3000 hash_int_arr = [] while cap_set100: print('imgs当前上限100张图合并') return '' elif imgs==[]: print('imgs中没有包含图片,请检查') return '' elif 11: draw.text((x,y-btag),tags[i],(0,0,0),font=newfont) return J_img

到这里最开始的研究就完成了, 最开始的实现思路,就是上面这样。

================

后来发现图像指纹是有可能不是完全一致的, 而是相似的,还要考虑到相似的图像指纹。

imagehash.dhash算出来的图像指纹,本身的type类型不是字符串。 为了保存,转为字符串后,后续计算两个字符串的相似度,哪怕是很简单的字符串每一位是否与另一字符串每一位相等,数以10w个图像指纹,互相计算都要花费很长时间。 计算两个指纹的相似度,试了几种方法效率,最后发现bin最快,这个方法还是从dhash的官网看来的。 2020-5-13 看到还有一种写法是 num = 1 - (aHash - bHash)/len(aHash.hash)**2 直接imagehash计算,速度和bin的差不多,推荐使用这个。

'''关于dhash相似度比较方法研究,得到bin的方法计算最快,我的家用电脑10w次大概0.057秒。''' import time a = 'a1a8739f324eb01c' b = 'a1a8749f323eb01c' ai = int(str(a),16) bi = int(str(b),16) st = time.time() # 10w次执行速度,bin方式最快 for i in range(100000): # num = [a[j] is b[j] for j in range(16)].count(True)/16 # 0.2097s # num = [a[j] == b[j] for j in range(16)].count(True)/16 # 0.2082s # num = difflib.SequenceMatcher(None, a,b).ratio() # 4.2250s num = 1-bin(ai^bi).count("1")/64 # 0.0568s et = time.time() print(num,et-st)

接下来的考虑思路就是

计算得到相似图像指纹找到相似指纹对应的视频检查视频是否有连续相同地方列出相似视频对比缩略图

1秒比对200w个感觉是挺快 但是1000个,长度为1小时的视频,就需要30分钟比对完。 这个计算量感觉太大,即使写出来,为提高效率可能需要其他算法之类的优化。

关于效率处理这里,并没有完全想好,也没有时间测试,暂时就搁置了。 因为是个人闲暇研究,扔了可能后续就忘了,捡不起来了。 所以这里把之前的研究过程记录一下,希望其他有用到的人能得到一些参考。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有