问题描述
简单点讲,本方法就是将图片修改时间改成图片名称中的日期时间或时间戳。带EXIF的图片不建议修改。
首先描述一下问题,前几年上传到百度云盘的一些照片,他的修改时间都被修改成了统一的时间,而我下载到本地nas中想通过时间给他们建立相册时发现了本不该出现在这个时间段的照片,如:
所以需要通过想办法把时间给修正回来
问题解决
先备份再操作,先备份再操作
先备份再操作,先备份再操作
先备份再操作,先备份再操作
对于存在exif的文件,不建议用此脚本,因为我的图片没有涉及到这一块,所以我没有进行调整,而且经过测试会导致原始信息丢失,图片旋转,压缩
首先先筛选出这个特定时间的照片出来
也可以通过shell命令来完成,下载图片到本地,解压后进入到目录下,执行以下命令(时间需要替换成你上传的那个时间)
# 查找出符合条件的文件名
find . -newermt "2020-12-17 00:00" -a -not -newermt "2020-12-17 23:59"
# 查找出符合条件的文件的数量
find . -newermt "2020-12-17 00:00" -a -not -newermt "2020-12-17 23:59" | wc -l
分析
这些文件基本都丢失了EXIF的信息,这里面分成这么几种
文件带EXIF信息:
- 通过EXIF进行覆盖
文件名不带EXIF信息:
- 名字里带日期
- 名字是时间戳
在这里我又发现一个问题,我的nas是通过拍摄时间进行判断的,因此我下载下来的文件一定都是满足不带EXIF信息这个条件的,因此我优先解决这个问题,根据文件名称去修改文件的修改时间(非拍摄时间)
再细分一下,名称包括如下几种
不带日期信息
- 未命名_05_8263.BMP
- 57761fea0c32d014.png
- img-f06b35757655fead6629c4dd3abc8bc5.jpg
带日期格式
- IMG_20190313_101100.jpg
- Screenshot_2018-02-08-19-01-41-923_com.sina.weibo.png
- 2018-09-22 202310.jpg
- 2018-10-02 133427(1).jpg
- faceu_20180724190233.jpg
- Snipaste_2019-08-13_00-47-26.png
带时间戳
- 1538122641350.jpeg
- mmexport1515823446908.jpg
不带日期格式的基本救不回来了
那就根据现有格式去做匹配(匹配的方式多样,我跑完脚本想到了一些更好的方法,但是懒得改了,又不是不能用.jpg)
代码
调试部分代码我全部保留了,看自己需求使用
'''
Description:
Version: 2.0
Autor: Toby Shawn
Date: 2024-12-25 15:28:42
LastEditors: Toby Shawn
LastEditTime: 2024-12-26 14:06:10
'''
import os
import re
from PIL import Image, ExifTags
from PIL.ExifTags import TAGS, GPSTAGS
from datetime import datetime
import piexif
from tqdm import tqdm
def get_date_from_filename(filename):
patterns = [
r'(\d{8}_\d{6})', # IMG_20190313_101100.jpg
r'(\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2})', # Screenshot_2018-02-08-19-01-41-923_com.sina.weibo.png
r'(\d{4}-\d{2}-\d{2} \d{6})', # 2018-09-22 202310.jpg
r'(\d{13})', # 时间戳格式
r'(\d{8}\d{4})', # faceu_20180724190233.jpg
r'(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})', # Snipaste_2019-08-13_00-47-26.png
r'(\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2})' # Snipaste_2019-08-13_00-47-26.png
]
for pattern in patterns:
match = re.search(pattern, filename)
try:
# print('匹配的文件名:',filename)
if match:
date_str = match.group(1)
# print('匹配的文件名:',filename)
# print('匹配的正则',pattern)
# print('匹配到的时间',date_str)
# print('\n')
if len(str(date_str)) == 13 and not str(date_str).startswith(('19', '20')) and str(date_str).isdigit():
# print('处理的时间戳:',datetime.fromtimestamp(int(date_str) / 1000).strftime('%Y-%m-%d %H:%M:%S'))
if datetime.fromtimestamp(int(date_str) / 1000).strftime('%Y-%m-%d %H:%M:%S') <= datetime.now().strftime('%Y-%m-%d %H:%M:%S'):
return datetime.fromtimestamp(int(date_str) / 1000).strftime('%Y-%m-%d %H:%M:%S')
else:
if len(date_str) == 14:
dt = datetime.strptime(date_str, '%Y%m%d%H%M%S')
else:
cleaned_date_str = date_str.replace('-', '').replace('_', '').replace(' ', '')
dt = datetime.strptime(cleaned_date_str, '%Y%m%d%H%M%S')
# print('处理的时间:',dt)
if dt <= datetime.now():
# print('处理的时间:',dt)
return dt
except:
# print('匹配的正则',pattern)
# print('匹配的文件名:',filename)
# print('没有匹配到')
return None
return None
def add_date_to_image(image_path, date_str):
# print("准备添加日期")
image = Image.open(image_path)
try:
exif_data = image._getexif()
# print(exif_data)
except AttributeError as e:
# print(e)
exif_data = {}
if exif_data is not None:
# 更新EXIF数据
# 确保TAGS字典中有对应的键值
if ExifTags.TAGS.get(36867) is not None and 36867 in exif_data:
exif_data[ExifTags.TAGS[36867]] = date_str
if ExifTags.TAGS.get(36868) is not None and 36868 in exif_data:
exif_data[ExifTags.TAGS[36868]] = date_str
if ExifTags.TAGS.get(306) is not None and 306 in exif_data:
exif_data[ExifTags.TAGS[306]] = date_str
exif_bytes = piexif.dump(exif_data)
if image.mode != 'RGB':
image = image.convert('RGB')
image.save(image_path, exif=exif_bytes)
timestamp = datetime.strptime(str(date_str), '%Y-%m-%d %H:%M:%S').timestamp()
os.utime(image_path, (timestamp, timestamp))
def process_directory(directory):
num=0
for filename in tqdm(os.listdir(directory)):
if filename.lower().endswith(('.png', '.jpg', '.jpeg','.bmp','gif')):
date_str = get_date_from_filename(filename)
# print('接收到的',date_str)
if date_str:
image_path = os.path.join(directory, filename)
add_date_to_image(image_path, date_str)
# print(f'Updated {filename} with date {date_str}')
num+=1
# else:
# print(f'No date found in {filename}')
# print('\n')
return num
# 指定目录
directory_path = '/Users/tobyshawn/Downloads/picture/tmp'
Noum=process_directory(directory_path)
# 如果要手动改成固定日期,用这个
# for filename in tqdm(os.listdir(directory_path)):
# if filename.lower().endswith(('.png', '.jpg', '.jpeg','.bmp','gif')):
# image_path = os.path.join(directory_path, filename)
# add_date_to_image(image_path,'2021-06-30 12:00:00')
print(f"已修改{Noum}张照片")
调试输出,我的代码匹配我的问题,但是并不能保证通用,因此需要调试查看匹配程度
我需要处理的照片大概1600张,实际修改的图片对结果影响不大(数量级小),跑完大概4分30秒左右
- 890/1598
- 1244/1601(三次)
调优后
结果
替换
由于删除的操作太过敏感,我这边不提供脚本供大家删除原有照片,大家可以进入到各目录下根据时间排序删除原有图片再上传新的图片。我这边在存储时没有强需求,因此我新建了一个目录全部塞进去了,用相册去做匹配即可。
结束
在操作的工程中当然也出现了一些离谱的问题,比如:
- 因为正则匹配的优先级导致把日期当初时间戳去匹配了
- 不是时间戳的字符串当成时间戳去匹配了
做了一些处理,对于一些乱码的表情包,只会匹配到以前的时间,不会比配到今后的时间
再次提醒,对于存在exif的文件,不建议用此脚本,因为我的图片没有涉及到这一块,所以我没有进行调整,而且经过测试会导致原始信息丢失,图片旋转,压缩
如:
-
佳能
-
iPhone