在日常的照片管理中,我们常会遇到 EXIF 信息中的拍摄日期不准确或者缺失的情况。有些图片(截图、程序生成、去除隐私的拍摄照片)的元数据中没有图片的拍摄日期,从网盘或者其他地方下载的图片的创建日期会变成下载时候的日期,导入相册中无法正确识别到图片的日期,导致相册按时间混乱。
这些图片的文件名大都含有时间信息,这篇文章将分享一个完整的 Python 脚本,用于根据图片文件名自动提取日期,并将其写入图片的 EXIF 元数据中,便于照片管理和排序。
这段代码实现了以下功能:
DateTimeOriginal
字段,并设置其他相关字段。代码首先通过正则表达式从文件名中提取日期时间,支持的格式包括:
YYYYMMDD_HHMMSS
格式,例如 20201231_200936
YYYYMMDD-HHMMSS
或 YYYYMMDD_HHMMSS[附加内容]
以下是提取日期时间的核心代码:
pythondef extract_datetime_from_filename(filename) -> datetime | None:
patterns = [
r"(\d{8})_(\d{6})", # 格式:YYYYMMDD_HHMMSS
r"(\d{13}|\d{10})", # 格式:TIMESTAMP
r"(\d{8})[_|-](\d{6}).*?" # 格式:YYYYMMDD_HHMMSS[其他内容]
]
for pattern in patterns:
match = re.search(pattern, filename)
if match:
if len(match.groups()) == 2:
date_part, time_part = match.groups()
try:
return datetime.strptime(date_part + time_part, "%Y%m%d%H%M%S")
except ValueError:
continue
elif len(match.groups()) == 1:
timestamp = match.group(1)
try:
if len(timestamp) == 13:
return datetime.fromtimestamp(int(timestamp) / 1000)
else:
return datetime.fromtimestamp(int(timestamp))
except ValueError:
continue
return None
提取到的日期可能会超出合理范围(例如系统错误生成的无效时间戳)。因此,我们用以下代码确保日期在合理范围内:
pythonstart_time = datetime(1970, 1, 1, 0, 0, 0)
end_time = datetime(2035, 1, 1, 0, 0, 0)
def valid_time(dt):
if start_time <= dt <= end_time:
return dt
else:
return None
代码使用了 piexif 库来加载和修改图片的 EXIF 元数据。以下是核心逻辑:
DateTimeOriginal
,与提取的日期比较,取较早者。Make
和 Model
)。以下是主要代码段:
pythondef modify_image(image_file: ImageFile, given_date, is_force):
try:
exif_dict = piexif.load(image_file.file_path)
date_time_original = get_exif_date(exif_dict, piexif.ExifIFD.DateTimeOriginal, 'Exif', None)
if is_force:
dates = [d for d in [date_time_original, given_date] if d is not None]
earliest_date = min(dates)
else:
earliest_date = given_date
if date_time_original != earliest_date:
exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = earliest_date.strftime("%Y:%m:%d %H:%M:%S").encode("utf-8")
exif_dict["0th"][piexif.ImageIFD.Make] = 'MemoTrace'.encode("utf-8")
exif_dict["0th"][piexif.ImageIFD.Model] = 'EasyBox'.encode("utf-8")
exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, image_file.save_path)
print(f"DateTimeOriginal 已更新为最早日期:{earliest_date}")
except Exception as e:
print(f"处理图片时出错:{e} {traceback.format_exc()}")
以下是代码的运行示例:
pythonif __name__ == '__main__':
img_path = './dist/图片/Screenshot_20201231_200936.jpg'
new_path = 'IMG_20201007_150144(3).jpg'
image_file = ImageFile(img_path)
image_file.save_path = new_path
modify_image(image_file, None, False)
运行结果如下:
plainDateTimeOriginal 已更新为最早日期:2020-12-31 20:09:36
>=3.9
piexif
安装依赖:
bashpip install piexif
完整代码如下:
python"""
根据文件名获取图片的日期,修改图片exif元数据里的拍摄日期
"""
import os.path
import re
import shutil
import traceback
from datetime import datetime
import piexif
def get_exif_date(exif_dict, field, key='Exif', default="Unknown"):
"""
安全获取 EXIF 字段的日期,如果字段不存在或解析失败,返回默认值。
"""
try:
date_str = exif_dict[key].get(field, b"").decode("utf-8")
return datetime.strptime(date_str, "%Y:%m:%d %H:%M:%S")
except (KeyError, ValueError, UnicodeDecodeError):
return default
start_time = datetime(1970, 1, 1, 0, 0, 0)
end_time = datetime(2035, 1, 1, 0, 0, 0)
def valid_time(dt):
if start_time <= dt <= end_time:
return dt
else:
return None
def extract_datetime_from_filename(filename) -> datetime | None:
# 定义正则表达式,匹配常见的时间格式
patterns = [
r"(\d{8})_(\d{6})", # 格式:YYYYMMDD_HHMMSS
r"(\d{13}|\d{10})", # 格式:TIMESTAMP (13位毫秒级时间戳)
r"(\d{8})[_|-](\d{6}).*?" # 格式:YYYYMMDD_HHMMSS[其他内容]
]
result = None
for pattern in patterns:
match = re.search(pattern, filename)
if match:
if len(match.groups()) == 2: # 包含日期和时间
date_part, time_part = match.groups()
try:
result = datetime.strptime(date_part + time_part, "%Y%m%d%H%M%S")
return valid_time(result)
except ValueError:
continue
elif len(match.groups()) == 1: # 只包含时间戳
timestamp = match.group(1)
try:
if len(timestamp) == 13:
result = datetime.fromtimestamp(int(timestamp) / 1000) # 转换为秒
else:
result = datetime.fromtimestamp(int(timestamp)) # 转换为秒
return valid_time(result)
except (ValueError, OverflowError):
continue
return None
class ImageFile:
def __init__(self, file_path):
self.save_path = file_path
self.file_path = file_path
self.file_name = os.path.basename(file_path)
self.save_inplace = True
self.save_fmt = 'auto'
self.save_quality = 95
def get_file_time_by_name(self):
return extract_datetime_from_filename(self.file_name)
def modify_image(image_file: ImageFile, given_date, is_force):
"""
修改图片的拍摄时间为given_date
:param image_file:
:param given_date:
:param is_force: 是否强制设置为given_date,否:取当前图片拍摄时间和给定时间的最小值
:return:
"""
file_path = image_file.file_path
if not given_date:
given_date = image_file.get_file_time_by_name()
try:
# 打开图片并加载 EXIF 数据
try:
exif_dict = piexif.load(file_path)
# 获取 EXIF 日期
date_time_original = get_exif_date(exif_dict, piexif.ExifIFD.DateTimeOriginal, 'Exif', None)
if is_force:
modify_date = get_exif_date(exif_dict, piexif.ImageIFD.DateTime, '0th', None)
dates = [d for d in [date_time_original, modify_date, given_date] if d is not None]
earliest_date = min(dates)
else:
earliest_date = given_date
# 如果 DateTimeOriginal 已经是最早的,则无需修改
if date_time_original != earliest_date:
# 更新 DateTimeOriginal
exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = earliest_date.strftime("%Y:%m:%d %H:%M:%S").encode(
"utf-8")
exif_dict["0th"][piexif.ImageIFD.Make] = 'MemoTrace'.encode("utf-8")
exif_dict["0th"][piexif.ImageIFD.Model] = 'EasyBox'.encode("utf-8")
exif_dict["0th"][piexif.ImageIFD.Software] = 'EasyBox-0.1.0'.encode("utf-8")
except:
earliest_date = given_date
exif_dict = {
'0th': {},
'Exif': {}
}
if earliest_date:
# 更新 DateTimeOriginal
exif_dict["Exif"][piexif.ExifIFD.DateTimeOriginal] = earliest_date.strftime("%Y:%m:%d %H:%M:%S").encode(
"utf-8")
exif_dict["0th"][piexif.ImageIFD.Make] = 'MemoTrace'.encode("utf-8")
exif_dict["0th"][piexif.ImageIFD.Model] = 'EasyBox'.encode("utf-8")
exif_dict["0th"][piexif.ImageIFD.Software] = 'EasyBox-0.1.0'.encode("utf-8")
# 保存修改后的图片
exif_bytes = piexif.dump(exif_dict)
# img.save(image_file.save_path, exif=exif_bytes, quality=image_file.save_quality, subsampling=0)
if image_file.save_path != image_file.file_path:
shutil.copy(file_path, image_file.save_path)
piexif.insert(exif_bytes, image_file.save_path)
print(f"DateTimeOriginal 已更新为最早日期:{earliest_date}")
except Exception as e:
print(f"处理图片时出错:{e} {traceback.format_exc()}")
if __name__ == '__main__':
img_path = './dist/图片/Screenshot_20201231_200936.jpg'
new_path = 'IMG_20201007_150144(3).jpg'
given_date = None
image_file = ImageFile(img_path)
image_file.save_path = new_path
modify_image(image_file, None, False)
软件下载地址https://memotrace.cn/tools/
这段脚本通过文件名自动提取日期,并写入图片的 EXIF 数据,简化了拍摄时间的修正流程。希望这篇文章对你管理照片有所帮助!如果你在使用过程中有任何问题或建议,欢迎留言讨论。
相关应用程序正在开发中,欢迎关注以便获取最新资源
本文作者:司小远
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!