一、前言

近日在整理以前拍的照片,想做一本相册,中间遇到了许多坑,记录一下。

二、找到旧照片原图

首先是找到旧的照片,由于换了好几部手机,从红米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.extwx_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张出来,修一修打印出来装订成册,所以想把需要打印的照片添加一些水印,包括时间、地点、设备、参数等。

了解了一下打印的尺寸,主要有:

image

手机拍摄的照片比例一般是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}

20250909201140.webp

最终的成品照片接近4:3比例,一下为部分样图:

2025-09-09_13-31-52

2025-09-09_13-42-11

2025-09-09_13-43-49.webp

最后就是去淘宝打印了,其实本来是想买个打印机的,看中小米的照片打印机1s,但是看网上说很容易吃灰,而且打印机+耗材套装就750,耗材100块80张,感觉没有淘宝打印划算。

先在淘宝上打印试试看吧,期待好结果。

最后修改:2025 年 09 月 09 日