作为一个偶尔拍照的人,拥有手机这么多年,手头也是积攒了很多照片的。想有一个在线相册很久了,一直不太确定使用哪些工具构建。最近在一封 newsletter 中遇到了这个代码库 petrovicz/astro-photoswipe,我挺喜欢的,顺便尝试一下以前没接触过的 Astro。至于 PhotoSwipe,它可是老朋友了,曾经用过一段时间,后来精力放在其他地方就没再使用。
Image to text
部署好整相册网站后,我注意到没有图片说明。一开始想的是可以手写,但这么多图片工作量很大,后来想着想着就想到:有没有可以图片转文字的 AI 模型呢?这个网站部署在 Cloudflare Pages 上,很自然地就去 Workers AI 那里找模型,有两个模型( @cf/llava-hf/llava-1.5-7b-hf
和 @cf/unum/uform-gen2-qwen-500m
)。
本来想着能不能实时生成呢?就是我点击一张图片,瞬间生成这张图片的说明,没找到办法。我通过 Node.js 脚本测试批量后发现生成 230 张左右的图片需要 2 分钟,其中包括了因为图片太大而无法生成、网络连接问题的情况。
在借助 AI 的帮助下完成了这个脚本的编写,遇到的一些问题:
- 本地网络无法连接 Cloudflare 的 API,动不动就超时,解决办法:将运行环境放到 GitHub Action 中,还添加了 15 秒的超时;
- Cloudflare Workers AI 对于 Image to Text 类模型有每分钟 720 次请求的限制,解决办法:限制最大并发请求频次;
- 怎样在得到图片说明后,把所有图片的说明文本上传到 Worker KV 中,解决办法:使用
Promise.all()
(它可以将多个迭代期约合并为一个并输出)。
一些感受:
这个脚本现在看起来不觉得有什么了不起,但在写出来以前,我也是很头疼,思考怎么解决上面这几个问题。在 AI 的帮助下,总算是完成了我的目的。写这个脚本时,参考了 dgurns/magic-ai-box 才直接用的 REST API,而不是 Cloudflare 的官方 SDK。
后来想到:
需要一个限定条件:如果生成文本失败就不上传了。如果不这样做会出现一个问题:如果同一张图片前一次生成成功、这一次生成失败,会使得原有的文本被移除。在实现的时候遇到一个 BUG——执行代码无法停止。
- 第一次修改(40b7a92):使用了
data.result.description
进行过滤,后来通过日志发现data.success
更简洁。 - 第二次修改(2d62d50):这次修改了很多内容,这一步的修改导致了代码执行陷入死循环,经过排查发现是在处理图片时期约无法停止造成的。
- 第三次修改(0184d83):在这一步里对
processImagesConcurrently
函数进行了调整。
另一个实现方法,使用 xenova/transformers.js:
import { pipeline } from '@xenova/transformers';
const captioner = await pipeline('image-to-text', 'Xenova/vit-gpt2-image-captioning');
const url = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/cats.jpg';
const output = await captioner(url);
// [{ generated_text: 'a cat laying on a couch with another cat' }]
console.log(output);
Remove exif info
两天以后,我注意到一个问题:拍摄的图片都来自手机,那么如果不把图片中附带的手机型号等信息删除,是很大的安全隐患。于是,我又写了个脚本来移除所有图片的 exif 信息。大致流程:
- 遍历目标文件夹下的所有图片,判断还有没有 exif 信息;
- 如果没有就不用处理,如果还有就移除 exif 信息。
代码仓库在GitHub。网址也在那里。