给 Immich 的 OCR 搜索装个外接查询器

warning: 这篇文章距离上次修改已过249天,其中的内容可能已经有所变动。

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已经积累了一大堆,因为我是白嫖用户,不好意思催他们进度,因此只能自己想点办法打补丁凑合下了。

不过确实好用是真的。

(完)


木头箱子脆脆,但是这样正好

如无特殊声明,本站内容遵循 CC BY-NC-SA 4.0 协议

转载请注明出处并保留作者信息,谢谢!

本站由 搬瓦工VPS 强力驱动

none
最后修改于:2025年11月04日 13:45

添加新评论

提醒:『评论回复邮件提醒』功能正在测试中
评论后,如果站长有回复,会有邮件通知