一、前言
近日在整理以前拍的照片,想做一本相册,中间遇到了许多坑,记录一下。
二、找到旧照片原图
首先是找到旧的照片,由于换了好几部手机,从红米K20Pro→K30s→小米11 Ultra→小米15,在2021年以前,由于没有整NAS,都是定期手动备份到机械硬盘里面。2021年以后有了NAS以后,就变懒了,都让软件自动上传到NAS上,还有一份小米云同步自动同步。问题恰恰就出在这里,由于以前的手机有的是128g,有的是256g,到后面手机存储空间就不够了,不知怎么,小米把本地手机里面的原图删了,换成了压缩后的图片。就这样,NAS中的原图也被替换成了压缩后的图片。这挺恶心的,近日整理照片的时候才发现原图没了。最后想到的解决办法是从小米云同步上把以前的照片下载下来,这是个大坑,这软件的PC端非常卡,体验非常不好。最后我选择整个相册都下载,只能烧一点小米CDN的流量来泄愤了。
三、按照月份分类整理照片
其次是按照月份分类整理照片,我的方法是年份-月份-相册名,
├─2021-01
│ ├─Camera
│ ├─Picture
│ ├─ScreenShot
│ ├─Video
│ └─WeiXin
├─2021-02
│ ├─Camera
│ ├─Picture
│ ├─ScreenShot
│ ├─Video
│ └─WeiXin
├─2021-03
......图片分别来自手机的/DCIM/Camera、/DCIM/Screenshots、/Pictures/WeiXin目录,让AI帮忙写了一个python脚本:
import os
import re
import time
import calendar
import shutil
import platform
import subprocess
from datetime import datetime
def set_file_timestamps(file_path, target_time):
"""设置文件的创建时间和修改时间"""
timestamp = target_time.timestamp()
try:
if platform.system() == "Windows":
# Windows系统使用PowerShell设置时间
time_str = target_time.strftime("%Y-%m-%d %H:%M:%S")
powershell_cmd = (
f'(Get-Item "{file_path}").CreationTime = "{time_str}"; '
f'(Get-Item "{file_path}").LastWriteTime = "{time_str}"'
)
subprocess.run(
["powershell", "-Command", powershell_cmd],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
elif platform.system() == "Darwin": # macOS
# macOS使用SetFile和utime
time_str = target_time.strftime("%Y%m%d%H%M.%S")
subprocess.run(
["SetFile", "-d", time_str, "-m", time_str, file_path],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
else: # Linux
# Linux设置访问时间和修改时间
os.utime(file_path, (timestamp, timestamp))
return True
except Exception as e:
print(f"设置文件 {file_path} 时间时出错: {str(e)}")
return False
def set_directory_timestamps(dir_path, year, month):
"""设置月份目录的创建时间和修改时间"""
try:
# 计算该月第一天和最后一天
first_day = datetime(year, month, 1, 0, 0, 0)
last_day_date = calendar.monthrange(year, month)[1]
last_day = datetime(year, month, last_day_date, 23, 59, 59)
if platform.system() == "Windows":
# Windows系统使用PowerShell设置时间
first_day_str = first_day.strftime("%Y-%m-%d %H:%M:%S")
last_day_str = last_day.strftime("%Y-%m-%d %H:%M:%S")
powershell_cmd = (
f'(Get-Item "{dir_path}").CreationTime = "{first_day_str}"; '
f'(Get-Item "{dir_path}").LastWriteTime = "{last_day_str}"'
)
subprocess.run(
["powershell", "-Command", powershell_cmd],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
elif platform.system() == "Darwin": # macOS
# macOS使用SetFile和utime
first_day_str = first_day.strftime("%Y%m%d%H%M.%S")
last_day_str = last_day.strftime("%Y%m%d%H%M.%S")
subprocess.run(["SetFile", "-d", first_day_str, dir_path], check=True)
os.utime(dir_path, (last_day.timestamp(), last_day.timestamp()))
else: # Linux
# Linux通过创建临时目录的方式设置
if os.path.exists(dir_path):
temp_dir = dir_path + "_temp"
os.rename(dir_path, temp_dir)
os.makedirs(dir_path)
# 设置新目录时间
os.utime(dir_path, (first_day.timestamp(), last_day.timestamp()))
# 移动内容
for item in os.listdir(temp_dir):
shutil.move(os.path.join(temp_dir, item), os.path.join(dir_path, item))
os.rmdir(temp_dir)
else:
os.makedirs(dir_path)
os.utime(dir_path, (first_day.timestamp(), last_day.timestamp()))
return True
except Exception as e:
print(f"设置目录 {dir_path} 时间时出错: {str(e)}")
return False
def process_images(source_dir):
"""处理指定目录下的图片文件"""
# 支持的图片文件扩展名
image_extensions = ('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.heic')
video_extensions = ('.mp4', '.mov', '.avi', '.mkv')
# 匹配mmexport开头的文件名正则表达式(简单形式)
mmexport_simple_pattern = re.compile(r'^mmexport(\d+)\.(.+)$')
# 匹配mmexport开头的文件名正则表达式(复杂形式,带哈希)
mmexport_complex_pattern = re.compile(r'^mmexport[a-f0-9]+_(\d+)\.(.+)$')
# 匹配wx_camera_开头的文件名正则表达式
wx_camera_pattern = re.compile(r'^wx_camera_(\d+)\.(.+)$')
# 匹配Screenshot开头的文件名正则表达式
screenshot_pattern = re.compile(r'^Screenshot_(\d{4})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d+)_.*\.(.+)$')
# 匹配IMG_日期格式的文件 (如 IMG_20210417_145554.jpg)
img_pattern = re.compile(r'^IMG_(\d{4})(\d{2})(\d{2})_(\d{6})\.(.+)$')
# 匹配MVIMG_日期格式的文件 (如 MVIMG_20210505_191758.jpg)
mvimg_pattern = re.compile(r'^MVIMG_(\d{4})(\d{2})(\d{2})_(\d{6})\.(.+)$')
# 匹配VID_日期格式的文件 (如 VID_20210801_182018.mp4)
vid_pattern = re.compile(r'^VID_(\d{4})(\d{2})(\d{2})_(\d{6})\.(.+)$')
# 遍历目录中的所有文件
for filename in os.listdir(source_dir):
file_path = os.path.join(source_dir, filename)
# 只处理文件,不处理目录
if not os.path.isfile(file_path):
continue
# 检查文件扩展名
file_ext = os.path.splitext(filename)[1].lower()
is_image = file_ext in image_extensions
is_video = file_ext in video_extensions
# 只处理图片和视频文件
if not (is_image or is_video):
continue
# 检查文件名是否符合IMG_格式
img_match = img_pattern.match(filename)
file_type = "img"
if not img_match:
# 检查是否符合MVIMG_格式
mvimg_match = mvimg_pattern.match(filename)
file_type = "mvimg"
if not img_match and not mvimg_match:
# 检查是否符合VID_格式
vid_match = vid_pattern.match(filename)
file_type = "vid"
if not img_match and not mvimg_match and not vid_match:
# 检查是否符合mmexport简单格式
match = mmexport_simple_pattern.match(filename)
file_type = "mmexport_simple"
if not img_match and not mvimg_match and not vid_match and not match:
# 检查是否符合mmexport复杂格式(带哈希)
match = mmexport_complex_pattern.match(filename)
file_type = "mmexport_complex"
if not img_match and not mvimg_match and not vid_match and not match:
# 检查是否符合wx_camera_格式
match = wx_camera_pattern.match(filename)
file_type = "wx_camera"
if not img_match and not mvimg_match and not vid_match and not match:
# 检查是否符合Screenshot格式
match = screenshot_pattern.match(filename)
file_type = "screenshot"
if not img_match and not mvimg_match and not vid_match and not match:
print(f"跳过非mmexport/wx_camera/screenshot/IMG_/MVIMG_/VID_格式文件: {filename}")
continue
try:
# 根据文件类型处理
if file_type == "img":
# 从IMG_文件名中提取时间信息
year = int(img_match.group(1))
month = int(img_match.group(2))
day = int(img_match.group(3))
hour = int(img_match.group(4)[:2])
minute = int(img_match.group(4)[2:4])
second = int(img_match.group(4)[4:6])
extension = img_match.group(5)
img_datetime = datetime(year, month, day, hour, minute, second)
new_filename = filename # IMG_文件不需要重命名
sub_dir_name = 'Camera'
month_dir_name = img_datetime.strftime("%Y-%m")
elif file_type == "mvimg":
# 从MVIMG_文件名中提取时间信息
year = int(mvimg_match.group(1))
month = int(mvimg_match.group(2))
day = int(mvimg_match.group(3))
hour = int(mvimg_match.group(4)[:2])
minute = int(mvimg_match.group(4)[2:4])
second = int(mvimg_match.group(4)[4:6])
extension = mvimg_match.group(5)
img_datetime = datetime(year, month, day, hour, minute, second)
new_filename = filename # MVIMG_文件不需要重命名
sub_dir_name = 'Camera'
month_dir_name = img_datetime.strftime("%Y-%m")
elif file_type == "vid":
# 从VID_文件名中提取时间信息
year = int(vid_match.group(1))
month = int(vid_match.group(2))
day = int(vid_match.group(3))
hour = int(vid_match.group(4)[:2])
minute = int(vid_match.group(4)[2:4])
second = int(vid_match.group(4)[4:6])
extension = vid_match.group(5)
img_datetime = datetime(year, month, day, hour, minute, second)
new_filename = filename # VID_文件不需要重命名
sub_dir_name = 'Video'
month_dir_name = img_datetime.strftime("%Y-%m")
elif file_type == "screenshot":
# 从文件名中提取时间信息
year = int(match.group(1))
month = int(match.group(2))
day = int(match.group(3))
hour = int(match.group(4))
minute = int(match.group(5))
second = int(match.group(6))
extension = match.group(8)
img_datetime = datetime(year, month, day, hour, minute, second)
new_filename = filename # Screenshot文件不需要重命名
sub_dir_name = 'ScreenShot'
month_dir_name = img_datetime.strftime("%Y-%m")
else:
# 处理微信导出的文件
# 提取时间戳
timestamp = int(match.group(1))
extension = match.group(2)
# 转换时间戳为datetime对象
# 处理毫秒级时间戳
if timestamp > 10**12: # 大于1e12的认为是毫秒级
timestamp = timestamp / 1000
img_datetime = datetime.fromtimestamp(timestamp)
# 生成新文件名
if file_type == "mmexport_simple" or file_type == "mmexport_complex":
new_filename = f"mmexport_{img_datetime.strftime('%Y%m%d_%H%M%S')}.{extension}"
sub_dir_name = 'WeiXin'
else: # wx_camera
new_filename = f"wx_camera_{img_datetime.strftime('%Y%m%d_%H%M%S')}.{extension}"
sub_dir_name = 'WeiXin'
month_dir_name = img_datetime.strftime("%Y-%m")
# 重命名文件(如果不是Screenshot类型)
new_file_path = os.path.join(source_dir, new_filename)
if file_path != new_file_path:
if os.path.exists(new_file_path):
print(f"文件已存在,跳过重命名: {new_filename}")
else:
os.rename(file_path, new_file_path)
print(f"重命名: {filename} -> {new_filename}")
file_path = new_file_path # 更新文件路径
# 设置文件的创建时间和修改时间(如果不是IMG_、MVIMG_或VID_类型)
if file_type not in ["img", "mvimg", "vid"]:
if set_file_timestamps(file_path, img_datetime):
print(f"已设置文件时间: {new_filename} -> {img_datetime}")
else:
# 对于IMG_、MVIMG_和VID_文件,仍然设置时间戳
if set_file_timestamps(file_path, img_datetime):
print(f"已设置文件时间: {new_filename} -> {img_datetime}")
# 创建月份目录
month_dir_path = os.path.join(source_dir, month_dir_name, sub_dir_name)
# 确保月份目录存在并设置时间
if not os.path.exists(month_dir_path):
os.makedirs(month_dir_path)
print(f"创建月份目录: {month_dir_name}/{sub_dir_name}")
# 设置月份目录时间属性
set_directory_timestamps(month_dir_path, img_datetime.year, img_datetime.month)
# 移动文件到月份目录
dest_path = os.path.join(month_dir_path, new_filename)
if not os.path.exists(dest_path):
shutil.move(file_path, dest_path)
print(f"移动文件到: {month_dir_name}/{sub_dir_name}/{new_filename}")
else:
print(f"目标文件已存在,跳过移动: {month_dir_name}/{sub_dir_name}/{new_filename}")
except Exception as e:
print(f"处理文件 {filename} 时出错: {str(e)}")
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='处理图片文件,重命名、设置时间并按月份归档')
parser.add_argument('directory', help='要处理的图片所在目录')
args = parser.parse_args()
# 验证目录是否存在
if not os.path.isdir(args.directory):
print(f"错误: 目录 '{args.directory}' 不存在或不是一个有效的目录")
else:
print(f"开始处理目录: {args.directory}")
process_images(args.directory)
print("处理完成")1. 脚本概述
这是一个用于整理和归档图片及视频文件的Python脚本,主要功能包括文件重命名、时间戳设置和按月份分类归档。
2. 核心功能
文件处理能力
- 支持格式:处理多种图片格式(.jpg, .jpeg, .png, .gif, .bmp, .webp, .heic)和视频格式(.mp4, .mov, .avi, .mkv)
文件类型识别:能够识别并处理以下命名格式的文件:
- 微信导出文件(mmexport开头)
- 微信相机文件(wx\_camera\_开头)
- 截图文件(Screenshot\_开头)
- 相机照片(IMG\_、MVIMG\_开头)
- 视频文件(VID\_开头)
文件重命名
- 对微信导出的文件进行标准化重命名,格式为:
mmexport_YYYYMMDD_HHMMSS.ext或wx_camera_YYYYMMDD_HHMMSS.ext - 其他格式文件保持原文件名不变
时间戳管理
- 精确时间设置:根据文件名中的时间信息设置文件的创建时间和修改时间
- 跨平台支持:针对Windows、macOS和Linux系统采用不同的时间设置方法
- 目录时间设置:为创建的月份目录设置合理的起止时间
文件归档组织
- 按年月创建目录结构(如:2023-05)
根据文件类型分类存储:
- Camera:相机拍摄的照片
- Video:视频文件
- ScreenShot:截图文件
- WeiXin:微信相关文件
3. 技术特点
智能解析
- 能够解析多种时间格式,包括时间戳和日期字符串
- 自动处理毫秒级时间戳转换
跨平台兼容
- 针对不同操作系统(Windows、macOS、Linux)采用相应的文件操作方法
- 使用PowerShell(Windows)、SetFile(macOS)和utime(Linux)等系统工具
安全机制
- 检查文件是否已存在,避免重复操作
- 异常处理机制,确保单个文件处理失败不影响整体流程
4. 使用场景
该脚本特别适用于:
- 整理手机导出的照片和视频文件
- 规范化管理大量杂乱的媒体文件
- 按时间顺序归档个人照片和视频资料
- 恢复文件的正确创建时间信息
5. 使用方法
通过命令行参数指定需要处理的目录:
python 整理图片.py [目录路径]四、为打印的照片添加水印
由于想把每一年的照片精选100张出来,修一修打印出来装订成册,所以想把需要打印的照片添加一些水印,包括时间、地点、设备、参数等。
了解了一下打印的尺寸,主要有:
手机拍摄的照片比例一般是4:3,如果选择大6寸打印是刚刚好的,但是如果在下方加了一条水印,打印出来长边就会有白边,
为了解决这个问题,我首先把照片裁切成3:2的比例,然后使用这个在线logo添加工具:milai.drtk.cn/logo/milai-frame
该工具的原链接为xxbiji.github.io/logo/milai-frame,GitHub地址为:https://github.com/xxbiji/logo
部分参数设置如下,最主要还是边框大小设置成60。logo调为自动,左上角文字可以使用模板中的{dateTime},左下角文字为:photo by XXX with {make} {model},右上角文字为省份 | 城市 | 地点·详细地址,右下角文字为:{focalLength}mm f/{F} {S} ISO{ISO}
最终的成品照片接近4:3比例,一下为部分样图:
最后就是去淘宝打印了,其实本来是想买个打印机的,看中小米的照片打印机1s,但是看网上说很容易吃灰,而且打印机+耗材套装就750,耗材100块80张,感觉没有淘宝打印划算。
先在淘宝上打印试试看吧,期待好结果。




