给 Immich 的 OCR 搜索装个外接查询器
Immich最近推出了新版本,支持对图片进行OCR了,真是喜大普奔,不过嘛,试用了一下之后发现,总是有些图搜索不出来:
我们的目标是 胖虎靠在墙边那张到GitHub看了一下,果不其然,找到了Issues #23507,这就证明了不是操作有误,而是有Bug。既然得等修Bug,又急着用OCR搜索,不如给他魔改一个外挂搜索功能好了。
1. 前言
自从有了PVE之后,干点啥都方便了起来,只要不是涉及到动内核的东西,直接开一个LXC跑就行,一人一个IP还能限制CPU和内存的用量,方便快捷。今天的主角Immich自然也不例外,虽然官方并不推荐在LXC(尤其是非特权LXC)里面运行这玩意,但是本着不作死就不会死精神,笔者还是尝试了一下,结果发现并没有什么太大的问题——也许问题和显卡硬件加速有关,但考虑到我的机器上根本没有什么好显卡给他加速(唯一一个还是U自带的UHD 630),所以问题并不大。
正如上文所说,OCR功能对笔者来说非常重要,因为笔者的图库里面除了日常拍摄的图片,更多的还是从知乎,B站等地方存下来的弔图,而这些弔图里文字的占比是很高的,比如说下面这张:
这个就是我们要找的靓仔图片,引言中并未找到所以有时候只记得图片文字,不记得具体存储时间和文件名的时候,OCR就非常有用了。过往版本的Immich只支持普通机器学习,也就是通过描述画面内容(海滩上的日出)等方式来寻找图片,虽然具备一定的OCR能力,但也仅仅算是个添头,并未针对OCR做特别优化。前两天更新的版本倒是添加了OCR专用模型,实测他的OCR效果是非常好的,甚至连模糊扭曲竖排的文字也能成功识别。
不过话虽如此,能成功识别出来的文字却不一定能在页面上找到,Issues #23507中有人测试过,表现为数据库中字段用空格分词,如果仅输入一部分内容就不能找到,必须输全了才能返回结果。在此Issue下面有开发者留言,他们的搜索功能基于PostgreSQL(下面简称pgsql)的三元索引(trigram index)来查找内容,同时考虑相似度与字符边界,这样的功能对于拉丁文字来说很有用,比如说在搜索brown的时候不会匹配到brownbear,减少误识别率,而且也提高了索引速度。但对于非拉丁文字而言,比如中文,不用空格来分词,这个方法就会失效,出现上面的问题。
所以就有了这次改造的思路:直接用别的方法查询数据库,找到OCR识别出来的文本,然后获取对应的图片ID,拿到图片。
2. 暴露数据库端口
Immich运行于Docker里面,而且采用的是分开service的方法,数据库与本体分离。因此第一步,就需要先修改数据库容器配置,让我们可以从外部访问到数据库
打开docker-compose.yml,找到并修改如下配置:
services:
database:
container_name: immich_postgres
# ...中间省略...
# 找个空闲地方添加如下配置,暴露数据库端口
ports:
- '127.0.0.1:5432:5432'保存,重新执行docker compose up -d,系统会重建容器。
3. 后台连接并搜索
首先需要准备好对应扩展
apt install php-pgsql此外还要准备好查询语句,用HeidiSQL连上数据库(数据库用户名密码,是安装Immich时在.env里面配置好的)之后,可以很轻松地在public下面找到ocr_search表,里面就存放了图片IDassetId以及对应的识别结果text。很显然,我们只需要对text字段进行查询即可。
一般来说,想要进行高效查询,正确的方法应该是利用上pgsql的GIN索引功能,但考虑到我们这里的数据量以及并发量,使用这玩意未免有杀鸡用牛刀之嫌。对于这次改造,简单的LIKE通配符外加LIMIT限制搜索数量,就已经足够了。
# ILIKE表示不区分大小写
SELECT "assetId" FROM PUBLIC.ocr_search WHERE "text" ILIKE "%关键词%" LIMIT 100根据这行sql,可以很轻松地写出来下面的PHP代码:
<?php
# 节约篇幅,代码省略了错误处理功能
# 实际使用时请自行添加(例如判空)
$db = pg_connect('连接信息,根据手册填就行');
$q = '%'.$_GET['t'].'%';
pg_prepare($db, 'ocr_query', 'SELECT "assetId" FROM PUBLIC.ocr_search WHERE "text" ILIKE $1 LIMIT 100');
$r = pg_execute($db, 'ocr_query', [$q]);
$out = [];
while($o = pg_fetch_assoc($r)) {
$out[] = $o['assetId'];
}
die(json_encode([
'ret' => 1,
'msg' => '成功',
'data' => $out
], JSON_UNESCAPED_UNICODE));
?>保存为(相对于网站根目录)/api/getSearch.php,就可以在浏览器里面测试并看到效果了:

4. 简单的前端页面
这一部分的代码不再给出,反正就是一个普通的搜索框和搜索按钮,点击之后触发fetch()发请求,拿到图片的ID之后批量生成<img>标签,地址设定为小缩略图地址;最后给<img>绑定一个点击事件,点击后跳转到大预览图地址;再把生成好的<img>插入到页面上的随便一个div里面,就可以了。这样的简单页面随便一个AI大模型都能轻松生成。
关于缩略图地址,也是不难获取的,因为Immich服务器已经提供了他前端所使用的接口,只需保持网页端登录,就可以很轻松地找到下面的路径
- 小缩略图:
/api/assets/图片UUID/thumbnail?size=thumbnail - 大缩略图:
/api/assets/图片UUID/thumbnail?size=preivew
最后的效果如下,可以看到已经能正确找到我们需要的靓仔图片:

5. 写在最后
官方仓库的Issues已经积累了一大堆,因为我是白嫖用户,不好意思催他们进度,因此只能自己想点办法打补丁凑合下了。
不过确实好用是真的。
(完)