相似图片搜索问题
- 数百万张经过 pHash 处理的图像并存储在 Elasticsearch 中。
- 格式为“11001101...11”(长度为 64),但可以更改(最好不要)。
给定主题图像的哈希“100111..10”,我们希望在8 的汉明距离内的 Elasticsearch 索引中找到所有相似的图像哈希。
当然query可以返回距离大于8的图片,Elasticsearch或者外部的脚本可以过滤结果集。但总搜索时间必须在 1 秒左右。
我们当前的映射
images
每个文档都有包含图像哈希的嵌套字段:
{
"images": {
"type": "nested",
"properties": {
"pHashFingerprint": {"index": "not_analysed", "type": "string"}
}
}
}
我们糟糕的解决方案
事实: Elasticsearch 模糊查询仅支持最大 2 的 Levenshtein 距离。
我们使用自定义标记器将 64 位字符串拆分为 4 组 16 位,并使用四个模糊查询进行 4 组搜索。
分析仪:
{
"analysis": {
"analyzer": {
"split4_fingerprint_analyzer": {
"type": "custom",
"tokenizer": "split4_fingerprint_tokenizer"
}
},
"tokenizer": {
"split4_fingerprint_tokenizer": {
"type": "pattern",
"group": 0,
"pattern": "([01]{16})"
}
}
}
}
然后是新的字段映射:
"index_analyzer": "split4_fingerprint_analyzer",
然后查询:
{
"query": {
"filtered": {
"query": {
"nested": {
"path": "images",
"query": {
"bool": {
"minimum_should_match": 2,
"should": [
{
"fuzzy": {
"phashFingerprint.split4": {
"value": "0010100100111001",
"fuzziness": 2
}
}
},
{
"fuzzy": {
"phashFingerprint.split4": {
"value": "1010100100111001",
"fuzziness": 2
}
}
},
{
"fuzzy": {
"phashFingerprint.split4": {
"value": "0110100100111001",
"fuzziness": 2
}
}
},
{
"fuzzy": {
"phashFingerprint.split4": {
"value": "1110100100111001",
"fuzziness": 2
}
}
}
]
}
}
}
},
"filter": {}
}
}
}
请注意,我们返回具有匹配图像的文档,而不是图像本身,但这不应该改变很多。
问题是即使在添加其他特定于域的过滤器以减少初始集之后,此查询也会返回数十万个结果。脚本有太多工作要再次计算汉明距离,因此查询可能需要几分钟。
正如预期的那样,如果增加到minimum_should_match
3 和 4,则只返回必须找到的图像子集,但结果集小而快。低于 95% 的所需图像返回minimum_should_match
== 3,但我们需要 100%(或 99.9%),如minimum_should_match
== 2。
我们用 n-gram 尝试了类似的方法,但在太多结果的类似方式中仍然没有太大的成功。
其他数据结构和查询的任何解决方案?
编辑:
我们注意到,我们的评估过程中有一个错误,minimum_should_match
== 2 返回 100% 的结果。但是,之后的处理时间平均需要 5 秒。我们将看看脚本是否值得优化。