摄影博主的归档日常:用 Python 快速找出 RAW 格式与 JPG 的重复废片
摄影博主的归档日常用 Python 快速找出 RAW 格式与 JPG 的重复废片十月四日的深夜我把相机里的 SD 卡插进拓展坞读卡器的蓝色指示灯开始飞速闪烁。今天趁着晴天带小狗 Token 去附近的湿地公园散步一口气按下了两百多次快门。我的相机设置的是“RAW JPG”双格式记录模式每一张照片不仅会生成一张色彩浓郁、方便手机即时发朋友圈的 8MB JPG 直出图还会同步生成一张体积超过 45MB 的无损.CR3RAW 原始文件。这种双格式设置在拍摄时固然让人放心但在后期挑选归档时简直是一场灾难。通常的挑选流程是我在手机或电脑的看图软件里快速翻阅 JPG 图片把那些手抖、对焦模糊、构图失误或者小狗突然闭眼的废片一张张按Delete删掉。删完之后JPG 预览图倒是只剩下了三四十张精华但存储卡或导入目录里的 RAW 原始文件却纹丝不动。几百个体积庞大的.CR3废片孤零零地留在硬盘深处不仅白白霸占着几十吉字节GB的高速固态硬盘空间而且很难靠人工一张张去比对文件名手动剔除。写一个几十行的轻量 Python 脚本自动扫描成对的文件名集合找出那些“失去了 JPG 对应凭证的孤独 RAW 废片”一键隔离到回收暂存区能让原本耗时半小时的枯燥体力活在两秒钟之内干净利落地解决。成对资产的集合运算之美在计算机科学中这个生活问题本质上是一个最典型的“差集Difference Set”运算。假设摄影素材目录里包含两类文件集合 $J$当前经过人工筛选后保留下来的 JPG 精选文件主名Stem集合 $R$相机原始生成的所有 RAW 格式文件主名如.CR3、.ARW、.NEF、.DNG。那么需要被安全清理的“RAW 废片集合”精准等于$$\text{Waste_RAW} R - J$$如果反过来还存在某些我们拍了 RAW 却没有导出 JPG 的特殊底片也可以通过相反的差集进行提示避免误伤。利用 Python 标准库的pathlib.Path与原生的set数据结构整个算法的时间复杂度为 $O(N)$即便是扫描包含几万张照片的专业摄影图库执行耗时也在百毫秒级别。安全第一的双重防御归档脚本对于珍贵的摄影原片自动化脚本永远不能直接调用os.remove()。万一误判或者删错任何算法都无法挽回生活里转瞬即逝的光影。成熟的整理脚本必须遵循两条安全底线绝对软删除Soft Quarantine绝不直接物理销毁文件而是将检测出的 RAW 孤立废片移动到一个名为_Trash_Candidate_YYYYMMDD/的临时暂存目录详细的审查清单回显在移动之前在终端打印出清晰的对比统计保留数量、废片数量、预期释放空间给使用者最后的复核机会。import os import shutil from pathlib import Path from typing import Set, Dict, List # 支持的主流相机 RAW 原始格式后缀 RAW_EXTENSIONS {.cr2, .cr3, .arw, .nef, .dng, .orf, .rw2} JPG_EXTENSIONS {.jpg, .jpeg, .webp} def clean_orphan_raw_files(target_dir: Path, dry_run: bool True): 扫描目标目录找出被删除 JPG 对应的无用 RAW 文件并移至隔离区 if not target_dir.exists(): print(f目录不存在: {target_dir}) return jpg_stems: Set[str] set() raw_files: Dict[str, Path] {} # 单次遍历收集所有文件的文件名主干Stem for file in target_dir.iterdir(): if file.is_file(): suffix file.suffix.lower() if suffix in JPG_EXTENSIONS: jpg_stems.add(file.stem) elif suffix in RAW_EXTENSIONS: raw_files[file.stem] file # 集合差集运算找出失去了 JPG 伴侣的 RAW 文件 orphan_stems set(raw_files.keys()) - jpg_stems total_waste_size sum(raw_files[stem].stat().st_size for stem in orphan_stems) waste_size_mb total_waste_size / (1024 * 1024) print( * 45) print(f 摄影资产扫描完毕: {target_dir.name}) print(f 现有保留精选 JPG: {len(jpg_stems)} 张) print(f 原始 RAW 文件总数: {len(raw_files)} 张) print(f 待清理孤立 RAW 废片: {len(orphan_stems)} 张) print(f 预计释放磁盘空间: {waste_size_mb:.2f} MB) print( * 45) if not orphan_stems: print( 图库非常干净没有发现多余的孤立 RAW 废片。) return if dry_run: print( 当前为演练模式Dry-run未执行实际文件移动。确认无误后传入 dry_runFalse。) return # 创建带日期标记的隔离暂存区 quarantine_dir target_dir / _Trash_Candidate_RAW quarantine_dir.mkdir(exist_okTrue) for stem in orphan_stems: raw_path raw_files[stem] dest_path quarantine_dir / raw_path.name shutil.move(str(raw_path), str(dest_path)) print(f✔ 成功将 {len(orphan_stems)} 张废片移动至隔离目录: {quarantine_dir.name})融入摄影师的日常工作心流现在每次外出拍摄回家我的相片整理心流变得异常丝滑SD 卡插入电脑将当天的文件夹拷入本地硬盘2026/1004_湿地公园秋日/打开系统自带的看图软件全屏翻看 JPG挑出喜欢的照片随手删掉表情怪异或虚焦的残次 JPG打开终端在这个目录下跑一下这个极简脚本终端滴的一声瞬间将几十个无用的.CR3巨型文件扫入暂存隔离区释放了 6.8GB 宝贵的磁盘空间。整个过程不需要打开臃肿的专业修图大软件也不需要面对繁琐复杂的导入导出规则。代码与生活在此刻达成了一种轻盈的默契。我们不需要成为技术上的全能专家只要能敏锐地发现日常琐事中的摩擦点用最朴素的数据结构与十几行脚本替自己省下大把枯燥的机械时间。剩下的时间就用来坐在暖光下静静修调那几张真正让人心动的秋日好照片。