Fork me on GitHub

我做了一个 AI 图片重命名工具

我做了一个 AI 图片重命名工具:看懂照片内容,再生成有意义的文件名

整理旅行照片时,我经常遇到一个问题:相机和手机生成的文件名通常是 IMG_8428.JPG IMG_0698.JPG 这样的编号。

当照片数量较少时,还可以逐张打开查看;但一次旅行拍摄几十甚至几百张照片后,仅凭文件名几乎无法知道照片内容。想找到“国清寺古建筑”“天台山瀑布合影”或者“琼台仙谷悬索桥”,只能不断翻看缩略图。

为了解决这个问题,我开发了一个带图形界面的 Python 工具:AI 照片与视频重命名器。

它可以调用本地视觉模型识别照片内容,自动生成类似下面这样的文件名:

IMG_0506.JPG
→ 20260812-天台山大瀑布景区-一家三口合影.JPG

IMG_8428.JPG
→ 20260813-国清寺-黄墙古树与石狮.JPG

IMG_8422.JPG
→ 20260812-琼台仙谷-悬索桥近景.JPG

相比没有含义的相机编号,这样的名称更容易搜索、归档和长期保存。

视频演示

这个工具能做什么?

工具使用 Python 和 Tkinter 开发,提供了一个轻量的桌面操作界面。用户不需要编写命令,只需要选择媒体目录、配置模型,然后按照“扫描—分析—预览—确认”的流程操作。

目前支持以下功能:

  • 选择指定的图片或视频目录
  • 可选择是否递归扫描子目录
  • 支持 JPG、JPEG、PNG、WebP 和 BMP 图片
  • 支持 MOV、MP4、M4V、AVI 和 MKV 视频
  • 在界面或 .env 中配置接口类型、模型、API 地址和可选 API Key
  • 支持 Ollama 原生接口:/api/chat 和 /api/tags
  • 支持 OpenAI 兼容接口:/v1/chat/completions 和 /v1/models
  • 自动生成带日期和场景描述的文件名
  • 重命名前预览全部结果
  • 支持手工修改 AI 生成的名称
  • 自动清理 Windows 文件名中的非法字符
  • 自动处理同名文件并添加序号
  • 不覆盖目录中已经存在的文件
  • 批量重命名失败时尝试回滚

整个过程只会修改文件名,不会修改图片、视频内容或照片中的 EXIF 信息。

为什么支持本地视觉模型?

这款工具默认可以连接本机运行的 Ollama 服务。当前也支持通过 LLM_API_TYPE 切换到 OpenAI 兼容接口:

# Ollama 原生接口
LLM_API_TYPE=ollama
LLM_MODEL=qwen3.8:27b
LLM_API_HOST=http://127.0.0.1:11434
LLM_API_KEY=

如果使用 OpenAI 兼容服务,可以这样配置:

LLM_API_TYPE=openai
LLM_MODEL=你的视觉模型
LLM_API_HOST=https://你的服务地址/v1
LLM_API_KEY=你的密钥

LLM_API_TYPE 只有两个取值:ollama 和 openai,默认是 ollama。OpenAI 模式会请求标准的 /v1/chat/completions 接口,并将压缩后的图片作为视觉输入发送;如果地址已经以 /v1 结尾,程序不会重复追加路径。

1. 本地模型可以减少隐私顾虑

当 API 地址配置为 127.0.0.1 或其他本机回环地址时,图片分析请求只发送给本机运行的模型服务。家庭照片、旅行合影等内容不需要上传到外部云平台。

如果配置的是远程 Ollama 或 OpenAI 兼容服务,图片仍然会发送到该服务。因此,使用前应确认服务的隐私政策、数据处理方式和访问控制;远程 HTTP 地址也应改用 HTTPS。

2. 模型可以自由切换

模型名称、接口类型和 API 地址都可以在 .env 文件或图形界面中配置。OpenAI 兼容服务如果需要鉴权,可以填写 LLM_API_KEY,本地不需要鉴权的服务则可以留空。

需要注意的是,所选模型必须支持视觉输入。纯文本模型无法识别照片内容。

图片和视频是怎样处理的?

直接把数 MB 甚至十几 MB 的原图发送给模型,会占用较多内存和视觉 Token。为此,程序会先对图片进行预处理:

  1. 读取并修正照片的 EXIF 方向;
  2. 将图片最长边缩小到 1280 像素;
  3. 转换为适合模型分析的 JPEG 数据;
  4. 根据接口类型生成请求:Ollama 使用 images 字段,OpenAI 兼容接口使用 data:image/jpeg;base64,... 的 image_url;
  5. 解析模型返回的文件名描述,并清理 Windows 不允许的字符。

这个过程只在内存中生成缩略图,不会修改原始照片。

对于视频,程序会通过 FFmpeg 获取视频时长,抽取中间位置的一帧,然后让视觉模型根据这一帧生成名称。临时画面会在分析结束后自动删除。

这种方案无法概括长视频的全部内容,但对于手机拍摄的短视频、Live Photo 片段和旅行记录,通常已经能够提供有意义的名称。

为什么不让 AI 直接修改文件?

AI 生成的名称并不一定每次都完全准确。例如,它可能无法确定具体景点,或者把人物关系描述得过于笼统。

因此,这个工具没有采用“分析后立即重命名”的方式,而是加入了一个预览确认流程:

  1. 扫描目录中的媒体文件;
  2. AI 逐个分析并生成建议名称;
  3. 用户检查全部结果;
  4. 对不满意的名称进行手工修改;
  5. 最后统一确认并应用。

只有用户点击“应用重命名”并再次确认后,程序才会真正修改文件名。

文件安全方面做了哪些处理?

批量重命名看起来简单,但需要考虑文件重名、非法字符和中途失败等情况。

程序会自动清理 Windows 不允许出现在文件名中的字符:

< > : " / \ | ? *

如果多个文件得到相同的名称,程序会自动添加序号:

天台山大瀑布-瀑布近景.JPG
天台山大瀑布-瀑布近景-02.JPG
天台山大瀑布-瀑布近景-03.JPG

正式重命名前,程序还会检查目标文件是否已经存在,避免覆盖原有文件。

批量操作采用两阶段重命名:文件首先被改成唯一的临时名称,确认全部进入临时状态后,再转换成最终名称。如果过程中发生异常,程序会尽可能恢复原始文件名。

安装和运行

项目使用独立的 Python 虚拟环境,不会把依赖安装到系统 Python 中。

进入项目目录后运行:

powershell -ExecutionPolicy Bypass -File .\setup.ps1

安装脚本会自动:

  • 创建 .venv;
  • 安装锁定版本的 Python 依赖;
  • 在缺少 .env 时从模板创建配置文件。

然后编辑 .env。继续使用 Ollama 时:

LLM_API_TYPE=ollama
LLM_MODEL=qwen3.8:27b
LLM_API_HOST=http://127.0.0.1:11434
LLM_API_KEY=

切换到 OpenAI 兼容接口时:

LLM_API_TYPE=openai
LLM_MODEL=你的视觉模型
LLM_API_HOST=https://你的服务地址/v1
LLM_API_KEY=你的密钥

系统环境变量优先于 .env,程序界面中的修改只对当前运行生效。.env 不应提交到 Git 仓库,API Key 也不要写入公开配置或博客。

启动程序:

powershell -ExecutionPolicy Bypass -File .\run.ps1

run.ps1 会始终使用项目目录下 .venv 中的 Python。如果虚拟环境不存在,它会先自动执行安装脚本。

使用流程

实际操作只需要几步:

  1. 点击“选择目录”,指定待处理的照片或视频目录;
  2. 选择接口类型:ollama 或 openai;
  3. 配置 API 地址、视觉模型,以及需要时填写 API Key;
  4. 点击“测试连接”,确认服务可访问且模型名称正确;
  5. 点击“扫描目录”;
  6. 点击“AI 分析全部”;
  7. 检查并手工修改建议名称;
  8. 点击“应用重命名”,确认后完成批量处理。

分析过程采用顺序处理,不会同时把大量原图加载到内存中,更适合包含几十或几百个媒体文件的目录。正式重命名之前,所有建议都可以在表格中检查和修改。

当前版本的局限

目前版本仍有一些可以继续改进的地方:

  • 日期前缀暂时来自文件修改时间,还没有优先读取 EXIF 拍摄时间;
  • 视频只分析中间一帧,无法完整理解长视频内容;
  • 景点识别依赖视觉模型自身能力,模型可能只能描述画面而无法判断准确地点;
  • AI 生成的名称仍建议在应用前人工检查;
  • 不同 OpenAI 兼容服务对视觉消息格式和模型名称的支持程度可能存在差异。

未来可以继续增加 EXIF 拍摄时间、地理位置、多个视频关键帧、命名模板以及重复照片检测等功能。

项目源码

完整源码和安装说明已发布在 GitHub:

查看 GitHub 项目源码

如果你也有大量使用 IMG_XXXX.JPG 命名的旅行照片,希望这个工具能帮助你更高效地完成整理。

Continue reading →

Mindset 2026

Growth Mindset 2026

微信公众号“对刘谈心”记录一个“观念改造”的系列。计划是每个月一篇,月底刊登。

观念改造记录这个月我看过的书或网页,遇见的人或事情对我个人的观念的改造,至少当下我比较认同的观念,朋友们一看,能够遇到某一条您也比较接受或认同的,我便心满意足。

Continue reading →

球场上的镜像神经元

球场上的镜像

昨晚打羽毛球,我们赢了两局。可下场以后,我觉得对面那位刚刚输球的球友,才是四个人里收获最多的一个。

输球以后

一场酣畅淋漓的混双比赛

昨晚我和公司招聘团队的一位女生搭档。

两局下来,四个人都满头大汗。他的搭档有几个球没接住,尴尬地笑了笑。

我当时注意的还是输赢。我原以为,那位球风犀利的球友多少会有些懊恼:也许会复盘队友丢掉的球,也许会想着下一局怎样把比分追回来。至少换作我,刚输完球时大概还困在比分里,很难马上把注意力转到对手身上。

但他没有。

他径直走到我面前,先问我在羽毛球群里叫什么名字。真正让我意外的是,他接下来谈的不是输赢。

他谈的不是输赢

他先对我说:

球场上要交流。既要跟队友交流,也要跟对手交流。

他接着谈起刚才那两局球。我记得他的大意是:

打完球,不能只想自己哪个球没打好、下次该怎么改。对手打出的好球也要留意。为什么这个球打得好?是动作、意识,还是线路不一样?以后自己能不能照着学?

他说,为对手的好球喝彩,不只是客气,也是在观察和学习。

听到这里,我有些惭愧。场上,我一直在琢磨怎样调动他们、怎样抓住空当得分;下场后,我也还在为几个好球暗自得意。他谈的却是,那些球为什么能打成,自己能从中学到什么。

观察也是一种练习

这让我联想起前段时间读的《智能简史》提到,即使只是观察别人完成动作,大脑也会对这个动作进行某种模拟。参与这一过程的神经元被称为“镜像神经元”(Mirror Neurons),也被认为与模仿有关。

这也让我重新理解那位球友的做法:盯住一个好球,观察对手的动作、意识和线路,在脑子里重新过一遍,再设法变成自己的东西。

想到这里,我又联想起自己曾经教孩子运动时常犯的错。

不能只是埋头苦练

每个孩子的运动天赋和学习节奏都不一样。看到别人一学就会,而自己的孩子练了很多遍,提高却不明显,大人很容易着急,甚至脱口而出:“为什么别人会,你还不会?”这种比较除了打击信心,并不能帮他找到问题。

我以前更容易把“多练”当作答案,觉得动作做得不够好,就再多做几遍。后来才意识到,不能只让孩子埋头苦练、独自琢磨。适当地让他看教程里的动作分解,看比赛视频中高手怎样移动、发力和处理球,再回到场上尝试,也可能带来提高。

当然,看懂不等于做到,观察也代替不了亲自练习。但练习并不只发生在自己挥拍的那一刻。认真看别人怎样完成一个动作,也是在给下一次尝试做准备。

这位球友让我看到,输掉一场球和学到一点东西并不矛盾。能从对手身上看到值得喝彩、值得模仿的地方,也许正是他越打越好的原因。

Continue reading →

芳姐的光荣退休欢送会

芳姐的光荣退休欢送会

今天中午,我在芳姐的退休欢送会上,提前偷看了自己退休那天的样子。让我自己都有点不好意思的是——它并不苦,我甚至隐隐盼着它。

芳姐是单位网络组的同事,我 6 年前进公司时她就在了。这六年她一直在我身边:来得比我还早,走路低着头,风风火火,像永远赶着去处理下一个故障。可我们几乎没说过话,多是应用故障排查涉及网络时,在线上打个照面。

今天中午去茶水间倒水,恰好遇到视频组的头,拿着专业单反相机,匆匆朝会议室方向走去,会议室那头人头攒动,感觉有什么事情发生,他很激动地告诉我“今天芳姐退休”。

我当时心里一愣。大概数十天之前,我还去找芳姐修过一次连不上的 WiFi,那是我们唯一一次面对面打交道。她有条不紊地排查了几步,几下就好了,我连她做了什么都没记住。她在我身边六年,我却只在她要走的这天,才第一次认真看她。

我愣在那儿。替她高兴是真的,为一位老同事的突然离开发怵也是真的;可那点连自己都不好意思承认的兴奋,也是真的。那一刻我没想清楚这些感受从哪来,只是有股说不清的力量,把我也推向了会议室。

芳姐讲了几句,回顾自己的职业生涯,没想到她在惠普也待了不少年。之前分属不同部门,我们并不认识,竟到要分别了,才知道我们很巧地曾是同事。

她讲得很朴素,也很动情。最后几年,她感受到年轻人后浪推前浪的压力,拼命努力,不想掉队,心有不甘;可年岁又实实在在地压上来,让她不舍又无奈。组里最新来的 00 后,发现她岁数超过自己妈妈,开始叫她“阿姨”,她一开始很抵触,后来也就由它去了,连两鬓的白发也随之任之,不再染黑。

她反复地、近乎重复地讲着这团矛盾——我听着听着,忽然就被击中了。让我停不下来的,正是她那帽檐下两鬓的露出的白发,那是几十年职业生涯生动的注脚。她讲了那么多遍,我反倒觉得她并没有真和自己和解:一边为年轻人让出舞台,一边又对这里恋恋不舍。

长江后浪推前浪,每个人都会到站。这话我一直知道,今天却是第一次站在中场、看着一个人真的在我面前下车。我久久不能平静,说不清是舍不得她,还是在她身上撞见了那个终将轮到我的时刻——而我竟隐隐盼着它。仪式散了,我回工位继续干活。轮到我下车的那天,我不知道自己会像芳姐那样心有不甘,还是仍旧像今天这样,悄悄地盼着。

Continue reading →

浙江徒步之雪窦岭

浙江徒步之雪窦岭古道

往有草的地方戳

下山时儿子突然跟我说:”你往有草的地方戳。”

我没反应过来。他解释说,石头台阶上长草的地方,底下一定是石头缝——杖尖戳在缝里才稳。

这是他用了一天登山杖,自己一下一下试出来的。

石头缝中草

上周末我们走的是雪窦岭古道。台阶是那种大块不规则的条石,年代久了,表面被磨得光滑,有些地方还覆着一层湿滑的青苔。

出发前我给他买了根 NatureHike 的登山杖试水——说明书上讲了长度调节和腕带用法,他上山过程中都体会到了。但说明书没告诉你的是:杖尖戳在光滑湿石面上根本站不住。他一开始本能地往石头缝里戳,确实牢固很多,但石头缝窄,走快了根本瞄不准。有一次他戳空了,杖尖从石面上滑过去,整个人趔趄了一下,吓得我在后面喊了一声”慢点!”

后来他自己琢磨出了规律:台阶上凡是长草的地方,底下必定是缝。因为草只能从缝里长出来。

这个道理说出来很简单。但它不是想出来的,是脚走了一整天、杖尖戳了几百下之后,试出来的。

水起风生

下山到一处瀑布跟前,突然凉快了好多。

一开始没反应过来为什么,站了几秒才明白:水往下冲,带着风。不是风生水起,是水起风生。

我们在那儿站着不走了。后面上来一个光皮大哥,走近瀑布,愣了一下,然后冲后面喊:”快来这儿!这里凉快!”他朋友们呼啦都围了过来,帽子摘了,袖子撸起来,对着水雾吹。站了好几分钟,谁都不想走。

山里走了一天,这几分钟最惬意。

水起风生

Continue reading →