目录
一、概述
1.1 背景
个人照片非常多,(多个设备:相机、不同品牌手机、Pad、Switch等……),做视频前需要将所有照片都汇聚起来,如有照片没有按照统一方式进行重命名,整理起来比较费劲,此外,按年度整理之前老照片时,也希望看到同一瞬间不同设备不同机位所定格的画面,所以按照时间统一命名十分必要。
1.2 需求
将一个文件夹下(比如2022年)的所有照片都按照照片拍摄日期进行统一重命名。
二、工具设计
标准命名格式设计:按照华为手机拍摄照片统一命名,即IMG_日期_时间[后缀].jpg,是否存在后缀视是否存在同时间存在同名图片的情况而定,如果存在,后缀按照下划线+序号的方式儿确定,标准化命名样例如下:
- IMG_2022008_181128.jpg (当前文件夹下不存同一时间点的其他照片)
- IMG_2022008_181128.jpg IMG_2022008_181128_1.jpg IMG_2022008_181128_2.jpg (当前文件夹下存在同一时间点的其他照片,一共三张,后面两张采用序号后缀)。
三、实现步骤
3.1 关键逻辑
步骤:
- 遍历当前夹,获取所有文件图片名称、文件路径、EXIF拍摄信息、文件内容Md5等信息;
- 遍历文件,判断图片文件命名情况;
- 如果需要重命名:与标准化命名无法精确匹配或前缀匹配
- 执行重名名,如果成功跳过;
- 如果执行重名失败,尝试追加后缀方式进行标准化命名重试;
- 如果需要重命名:与标准化命名无法精确匹配或前缀匹配
- 直到所有图片重命名完成,并汇总图片文件所有重命名情况及图片文件重复情况;
3.2 相关代码
python实现代码main.py如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Author: zhangjie(zhangjie@zjzone.cc)
Date: 2023/01/14
"""
import os
import sys
import time
import imghdr
import exifread
import hashlib
import argparse
class StandardIMG(object):
"""
标准化图片类
"""
def __init__(self, origin_path):
self.origin_path = origin_path
def check_is_img(self):
imgType_list = {'jpg','bmp','png','jpeg','rgb','tif'}
if imghdr.what(self.origin_path) in imgType_list:
print(self.origin_path)
return True
return False
def get_file_info(self):
"""
获取原始信息
"""
ret = {
"success": False,
"message": ""
}
try:
self.file_name = self.origin_path.split("\\")[-1]
# 检查文件是否为图片
if not self.check_is_img():
ret["message"] = "%s is not a image" % self.origin_path
return ret
# 获取照片拍摄时间
with open(self.origin_path, 'rb') as f:
self.md5_sum = hashlib.md5(f.read()).hexdigest()
self.file_dir_path = os.path.dirname(self.origin_path)
tags = exifread.process_file(f)
shot_datetime = str(tags['EXIF DateTimeOriginal'])
time_array = time.strptime(shot_datetime, "%Y:%m:%d %H:%M:%S")
other_style_time = time.strftime("%Y%m%d_%H%M%S", time_array)
self.new_file_name = "IMG_%s.jpg" % other_style_time
ret["success"] = True
except Exception as e:
error_info = "get file info error:%s" % e
print(error_info)
ret["message"] = error_info
return ret
def find_all_file(base):
for root, ds, fs in os.walk(base):
for f in fs:
fullname = os.path.join(root, f)
yield fullname
def rename_file(src_file, dst_file):
"""重命名文件"""
ret = {
"success": False,
"message": ""
}
try:
os.rename(src_file, dst_file)
ret["success"] = True
except Exception as e:
error_info = "rename file error:%s" % e
print(error_info)
ret["message"] = error_info
return ret
def main():
"""
数据参数
- 新建一个重复文件,重复命名为_1
- 校验MD5文件,重复文件标记出来,或mv到一个新文件夹
"""
parser = argparse.ArgumentParser()
parser.add_argument('-v', '--version', action='version', version='1.0.0.0')
parser.add_argument('images_dir', help="Please input directory of images")
args = parser.parse_args()
images_dir = args.images_dir
print("target dir is %s" % images_dir)
rename_retry_times = 20
# 遍历当前文件夹所有文件
try:
changed_images_dict = {}
images_md5sum_dict = {}
all_file_list = []
for i in find_all_file(images_dir):
tmp_s_img = StandardIMG(i)
all_file_list.append(i)
# 最后标准化命名,用量统计是否存在相同文件
last_name = tmp_s_img.origin_path
ret = tmp_s_img.get_file_info()
if not ret["success"]:
print("get %s image info error %s" % (i, ret["message"]))
continue
# 如果名字不一样或标准命名的前缀不一样(前19位)
if tmp_s_img.file_name != tmp_s_img.new_file_name and tmp_s_img.file_name[:19] != tmp_s_img.new_file_name[:19]:
for j in range(0, rename_retry_times):
new_name = "%s/%s" % (tmp_s_img.file_dir_path, tmp_s_img.new_file_name)
if j != 0:
# 重命名错误,可能存在同一间分钟拍摄的照片
file_prefix = ".".join(tmp_s_img.new_file_name.split(".")[0:len(tmp_s_img.new_file_name.split("."))-1])
file_endfix = tmp_s_img.new_file_name.split(".")[-1]
new_name = "%s/%s_%s.%s" % (tmp_s_img.file_dir_path, file_prefix, j, file_endfix)
print("%s rename fail, try to retry %s" % (tmp_s_img.origin_path, new_name))
print("%s should rename to %s" % (tmp_s_img.origin_path, new_name))
rename_ret = rename_file(tmp_s_img.origin_path, new_name)
if not rename_ret["success"]:
print("%s rename to %s fail:%s" % (tmp_s_img.origin_path, tmp_s_img.new_file_name, rename_ret["message"]))
else:
changed_images_dict[tmp_s_img.origin_path] = new_name
last_name = new_name
print("%s rename to %s success" % (tmp_s_img.origin_path, new_name))
break
if tmp_s_img.md5_sum not in images_md5sum_dict:
images_md5sum_dict[tmp_s_img.md5_sum] = [last_name]
else:
images_md5sum_dict[tmp_s_img.md5_sum].append(last_name)
# 汇总所有重命名图片
if len(changed_images_dict) != 0:
print(u"\n汇总:进行重命名的图片共有%s个,详细如下:\n" % len(changed_images_dict))
for item in changed_images_dict:
print("%s -> %s" % (item, changed_images_dict[item]))
else:
print(u"不存在未标准化重命名的图片文件")
if len(images_md5sum_dict) < len(all_file_list):
print(u"存在重复图片,详细如下:\n")
for item in images_md5sum_dict:
if len(images_md5sum_dict[item]) > 1:
print("%s -> %s" % (item, images_md5sum_dict[item]))
except Exception as e:
message = "find all files error:%s" % e
print(message)
sys.exit(-1)
if __name__ == '__main__':
main()
3.3 效果
四、展望
- 当前python实现,终端命令行方式运行,后续有空可以Gui封装成Windows桌面程序出来;
- 照片命名格式比较固定,后续可以支持自定义的命名格式;
- 照片过多时,需要按照主题进行分类,比较麻烦,需要更智能的分类算法进行分类,比如用一些通用计算机视觉算法;