
告别龟速导出!用Python写一个属于你自己的RAW转JPG神器
作为一名专业摄影师,你一定对这个场景不陌生:
一天拍摄结束,你将几百上千张 RAW 格式的照片导入电脑。为了快速筛选和分享,你需要将它们全部导出一份 JPG 格式的预览。于是你打开 Adobe Bridge 或 Lightroom,选中所有文件,点击“导出”,然后……进度条开始了它漫长而“优雅”的爬行。电脑风扇狂转,CPU 占用率飙升,而你只能泡杯茶,静静地等待,甚至不敢再操作电脑。
这个过程,我们称之为“生产力黑洞”。Photoshop 和 Bridge 功能强大,但在面对简单、重复的批量转换任务时,它们就像是用一辆重型坦克去送外卖——功能过剩,且效率低下。
但如果我告诉你,有一种方法,可以让这个过程提速数倍甚至十倍以上,并且完全免费、开源呢?
今天,我就来分享如何利用一段简单的 Python 脚本,将你的电脑打造成一台 RAW 格式转换的“性能猛兽”。
为什么脚本会比专业软件更快?
你可能会疑惑:Adobe 拥有顶尖的工程师团队,他们开发的软件难道还比不过一个几十行代码的脚本?
在“RAW 转 JPG”这个特定任务上,答案是:是的,脚本更快,而且快得多!
原因很简单:
- 轻装上阵 vs. 全副武装:Python 脚本只做一件事——解码 RAW 并保存为 JPG。它不会加载庞大的用户界面、数不清的插件、复杂的色彩管理引擎和历史记录功能。它就像一个百米冲刺的运动员,只穿最轻便的跑鞋。
- 榨干 CPU 性能:我们这个脚本的核心优势在于真正的并行处理。如果你的电脑有 16 个 CPU 核心,它就能启动 16 个进程,同时转换 16 张照片。而 Ps/Br 复杂的软件架构很难做到如此纯粹、高效的并行。这就像开通了 16 个收费站,车流速度自然飞快!
你的专属“火箭”:三步搞定!
别被“代码”、“脚本”这些词吓到,整个过程就像组装一个宜家家具一样简单。
第一步:搭建“发射架”(安装环境)
-
安装 Python: 如果你的电脑还没有 Python,请访问 Python 官网 下载并安装最新版本。切记,在安装时勾选 “Add Python to PATH”,这样会方便很多。
-
安装核心库: 打开你电脑的终端(Windows 用户请搜索 “CMD” 或 “PowerShell”,Mac 用户使用 “终端” App),然后输入以下命令并回车。这会自动安装我们需要的三个工具库:
rawpy
: 负责快速读取 RAW 文件。Pillow
: 负责处理和保存图像。tqdm
: 用来显示一个漂亮的进度条。
Terminal window pip install rawpy Pillow tqdm #winpip3 install rawpy Pillow tqdm #macos
第二步:获取“火箭引擎”(脚本代码)
在你的电脑上创建一个文件夹,比如 D:\MyScripts
。在文件夹里新建一个文本文档,将下面的所有代码复制进去,然后将文件名保存为 raw_converter.py
(注意后缀是 .py
)。
# --- raw_converter.py ---
import osimport sysimport rawpyfrom PIL import Imageimport multiprocessingimport timeimport globfrom tqdm import tqdm
# ===========================================================# 【核心功能函数】# ===========================================================
def convert_raw_to_jpg(raw_path, output_dir, quality, input_root_dir, search_subdirs): """转换单个 RAW 文件并返回处理结果信息""" try: filename = os.path.basename(raw_path) base_name = os.path.splitext(filename)[0]
if search_subdirs and os.path.dirname(raw_path) != input_root_dir: relative_dir = os.path.relpath(os.path.dirname(raw_path), input_root_dir) target_dir = os.path.join(output_dir, relative_dir) os.makedirs(target_dir, exist_ok=True) jpg_path = os.path.join(target_dir, f"{base_name}.jpg") else: jpg_path = os.path.join(output_dir, f"{base_name}.jpg")
if os.path.exists(jpg_path): return f"跳过: {filename}"
with rawpy.imread(raw_path) as raw: rgb_data = raw.postprocess(use_camera_wb=True, output_bps=8, no_auto_bright=True) pil_image = Image.fromarray(rgb_data) pil_image.save(jpg_path, 'JPEG', quality=quality, optimize=True) return f"成功: {filename}"
except Exception as e: return f"失败: {os.path.basename(raw_path)} - {e}"
def convert_raw_to_jpg_starmap_helper(args): """辅助函数""" return convert_raw_to_jpg(*args)
def batch_convert_raws(input_dir, output_dir, quality, search_subdirs, num_processes): """主函数""" start_time = time.time() os.makedirs(output_dir, exist_ok=True)
print("\n🚀 正在扫描 RAW 文件...") raw_extensions = ['*.CR2', '*.CR3', '*.NEF', '*.ARW', '*.DNG', '*.PEF', '*.RAF', '*.RW2', '*.ORF'] raw_files = [] for ext in raw_extensions: search_pattern = os.path.join(input_dir, '**', ext) if search_subdirs else os.path.join(input_dir, ext) raw_files.extend(glob.glob(search_pattern, recursive=search_subdirs))
if not raw_files: print(f"❌ 在目录 '{input_dir}' 中没有找到支持的 RAW 文件。请检查路径或文件类型。") return
print(f"✅ 共找到 {len(raw_files)} 个 RAW 文件。") print(f"🔥 开始使用 {num_processes} 个 CPU 核心进行转换...")
tasks = [(file, output_dir, quality, input_dir, search_subdirs) for file in raw_files]
with multiprocessing.Pool(processes=num_processes) as pool: results = list(tqdm(pool.imap_unordered(convert_raw_to_jpg_starmap_helper, tasks), total=len(tasks), desc="转换进度"))
success_count = sum(1 for r in results if r and r.startswith("成功")) skipped_count = sum(1 for r in results if r and r.startswith("跳过")) failed_count = sum(1 for r in results if r and r.startswith("失败"))
end_time = time.time() print("-" * 50) print("🎉 转换完成!") print(f"⏱️ 总耗时: {end_time - start_time:.2f} 秒") print(f"📊 结果: {success_count} 成功 | {skipped_count} 跳过 | {failed_count} 失败")
# ===========================================================# 【交互式设置主程序】# ===========================================================
def get_user_config(): """通过交互式问答获取用户配置""" print("=" * 50) print(" 欢迎使用极速 RAW to JPG 转换脚本!") print("=" * 50)
# 1. 获取 RAW 文件夹路径 while True: raw_input_path = input("\n📁 请将你的【RAW 源文件夹】拖到这里,然后按 Enter: ") clean_path = raw_input_path.strip().strip('"') if os.path.isdir(clean_path): input_dir = clean_path break else: print("❌ 错误: 这不是一个有效的文件夹路径,请重试。")
# 2. 获取输出文件夹路径 while True: raw_output_path = input("\n📂 请将你的【JPG 输出文件夹】拖到这里,然后按 Enter: ") clean_path = raw_output_path.strip().strip('"') if clean_path: output_dir = clean_path break else: print("❌ 错误: 输出路径不能为空,请重试。")
# 3. 获取 JPG 质量 while True: try: quality_str = input("\n⚙️ 请输入 JPG 压缩质量 (1-100, 推荐90, 直接按 Enter 使用默认值): ") if not quality_str: quality = 90 break quality = int(quality_str) if 1 <= quality <= 100: break else: print("❌ 错误: 请输入 1 到 100 之间的数字。") except ValueError: print("❌ 错误: 无效输入,请输入一个数字。")
# 4. 【新增】询问是否搜索子文件夹 while True: subdirs_choice = input("\n🔍 是否需要处理子文件夹中的文件? (y/n, 直接按 Enter 默认为 '是'): ").lower() if subdirs_choice in ['y', 'yes', '']: search_subdirs = True break elif subdirs_choice in ['n', 'no']: search_subdirs = False break else: print("❌ 错误: 无效输入,请输入 'y' 或 'n'。")
return input_dir, output_dir, quality, search_subdirs
if __name__ == '__main__': multiprocessing.freeze_support()
try: # 获取用户配置(现在会返回4个值) input_dir, output_dir, quality, search_subdirs = get_user_config()
# 显示最终配置并请求确认 print("\n" + "=" * 50) print("请确认您的设置:") print(f" - RAW 来源: {input_dir}") print(f" - JPG 输出: {output_dir}") print(f" - JPG 质量: {quality}") print(f" - 处理子文件夹: {'是' if search_subdirs else '否'}") # <-- 新增确认项 print("=" * 50)
input("✅ 设置无误,请按 Enter 键开始转换...")
# 执行转换 batch_convert_raws( input_dir=input_dir, output_dir=output_dir, quality=quality, search_subdirs=search_subdirs, # <-- 将用户的选择传递给核心函数 num_processes=multiprocessing.cpu_count() )
except KeyboardInterrupt: print("\n\n🛑 操作被用户中断。正在退出...") sys.exit(0) except Exception as e: print(f"\n\n💥 发生意外错误: {e}")
input("\n任务已结束,请按 Enter 键退出。")
第三步:点火发射!
运行: 再次打开终端(CMD 或 PowerShell),首先使用 cd
命令进入你保存脚本的文件夹,例如:
cd D:\MyScripts #wincd ~/Documents/Scripts #macos
然后,执行脚本:
python raw_converter.py #winpython3 raw_converter.py #macos
开始对话! 脚本会启动并像一个助手一样向你提问: * 它会让你提供 RAW 源文件夹:你无需手动输入路径,直接用鼠标把文件夹从桌面或文件管理器拖进终端窗口,然后按 Enter! * 它会让你提供 JPG 输出文件夹:同样,拖拽进来,按 Enter! * 它会询问 JPG 质量:输入你想要的数字(比如 95
),或者直接按 Enter 使用默认值。 * 它会询问是否处理子文件夹:输入 y
或 n
。
现在,见证奇迹的时刻到了!你会看到一个实时滚动的进度条,上面显示着处理速度(it/s 就是 张/秒)、完成百分比和预计剩余时间。这个速度,绝对会让你大吃一惊。
正在扫描 RAW 文件...共找到 1032 个 RAW 文件。开始使用 16 个进程进行转换...8%|██████ | 79/1032 [02:07<29:46, 1.87s/it]
这里的 1.87s/it (1.87秒/每项) 可能会让你觉得“一张照片就要快2秒,好慢”。但这是一个巨大的误解!
-
1.87s/it 的真正含义是:在你的16个CPU核心满负荷工作时,平均一个核心处理完一张照片需要1.87秒。
-
你真正的处理速度是:因为你有16个核心在同时工作,你的电脑实际的产出速度大约是 16张 / 1.87秒 ≈ 8.5 张/秒 的峰值吞吐能力!
我们换个角度计算:
-
在 2分07秒 (127秒) 内处理了 79 张照片。
-
平均每分钟处理 79 / (127/60) ≈ 37.3 张照片。
对于高像素的RAW文件(通常一张在30MB到80MB之间)来说,解码、应用白平衡、然后压缩成高质量JPG,每分钟能稳定处理30-40张,这已经是一个非常高效的速度了。
这个速度会受到以下几个因素影响:
- RAW文件的大小和像素:你的相机像素越高(例如4500万或6100万像素),文件越大,处理时间就越长。
- CPU的单核性能:即使核心数相同,新一代的CPU其单个核心的计算能力也更强。
- 硬盘读写速度 (I/O):如果你的RAW文件和输出目录都在一块慢速的机械硬盘上,硬盘读写可能会成为瓶颈。如果是在固态硬盘 (SSD) 上,则基本不会有影响。
我打包了一键版 点我下载
win用户双击 win.bat 运行 macos用户双击 macos.sh 运行
脚本会自动创建虚拟环境安装依赖运行