使用阿里云试用Elasticsearch学习:2.4 深入搜索——近似匹配

使用 TF/IDF 的标准全文检索将文档或者文档中的字段作一大袋的词语处理。 match 查询可以告知我们这大袋子中是否包含查询的词条,但却无法告知词语之间的关系。

思考下面这几个句子的不同:

  • Sue ate the alligator.
  • The alligator ate Sue.
  • Sue never goes anywhere without her alligator-skin purse.

用 match 搜索 sue alligator 上面的三个文档都会得到匹配,但它却不能确定这两个词是否只来自于一种语境,甚至都不能确定是否来自于同一个段落。

理解分词之间的关系是一个复杂的难题,我们也无法通过换一种查询方式去解决。但我们至少可以通过出现在彼此附近或者仅仅是彼此相邻的分词来判断一些似乎相关的分词。

每个文档可能都比我们上面这个例子要长: Sue 和 alligator 这两个词可能会分散在其他的段落文字中,我们可能会希望得到尽可能包含这两个词的文档,但我们也同样需要这些文档与分词有很高的相关度。

这就是短语匹配或者近似匹配的所属领域。

在这一章节,我们还是使用在match 查询中使用过的文档作为例子。

短语匹配

就像 match 查询对于标准全文检索是一种最常用的查询一样,当你想找到彼此邻近搜索词的查询方法时,就会想到 match_phrase 查询。

GET /my_index/_search
{"query": {"match_phrase": {"title": "quick brown fox"}}
}

类似 match 查询, match_phrase 查询首先将查询字符串解析成一个词项列表,然后对这些词项进行搜索,但只保留那些包含 全部 搜索词项,且 位置 与搜索词项相同的文档。 比如对于 quick fox 的短语搜索可能不会匹配到任何文档,因为没有文档包含的 quick 词之后紧跟着 fox 。

词项的位置

当一个字符串被分词后,这个分析器不但会返回一个词项列表,而且还会返回各词项在原始字符串中的 位置 或者顺序关系:

GET /_analyze
{"text": "Quick brown fox","analyzer": "standard"
}

返回信息如下:

{"tokens": [{"token": "quick","start_offset": 0,"end_offset": 5,"type": "<ALPHANUM>","position": 0},{"token": "brown","start_offset": 6,"end_offset": 11,"type": "<ALPHANUM>","position": 1},{"token": "fox","start_offset": 12,"end_offset": 15,"type": "<ALPHANUM>","position": 2}]
}
  • position 代表各词项在原始字符串中的位置。

位置信息可以被存储在倒排索引中,因此 match_phrase 查询这类对词语位置敏感的查询, 就可以利用位置信息去匹配包含所有查询词项,且各词项顺序也与我们搜索指定一致的文档,中间不夹杂其他词项。

什么是短语

一个被认定为和短语 quick brown fox 匹配的文档,必须满足以下这些要求:

  • quick 、 brown 和 fox 需要全部出现在域中。
  • brown 的位置应该比 quick 的位置大 1 。
  • fox 的位置应该比 quick 的位置大 2 。

如果以上任何一个选项不成立,则该文档不能认定为匹配。

本质上来讲,match_phrase 查询是利用一种低级别的 span 查询族(query family)去做词语位置敏感的匹配。 Span 查询是一种词项级别的查询,所以它们没有分词阶段;它们只对指定的词项进行精确搜索。
值得庆幸的是,match_phrase 查询已经足够优秀,大多数人是不会直接使用 span 查询。 然而,在一些专业领域,例如专利检索,还是会采用这种低级别查询去执行非常具体而又精心构造的位置搜索。

混合起来

精确短语匹配 或许是过于严格了。也许我们想要包含 “quick brown fox” 的文档也能够匹配 “quick fox,” , 尽管情形不完全相同。
我们能够通过使用 slop 参数将灵活度引入短语匹配中:

GET /my_index/_search
{"query": {"match_phrase": {"body": {"query": "quick fox","slop": 1}}}
}

slop 参数告诉 match_phrase 查询词条相隔多远时仍然能将文档视为匹配 。 相隔多远的意思是为了让查询和文档匹配你需要移动词条多少次?
我们以一个简单的例子开始吧。 为了让查询 quick fox 能匹配一个包含 quick brown fox 的文档, 我们需要 slop 的值为 1:

            Pos 1         Pos 2         Pos 3
-----------------------------------------------
Doc:        quick         brown         fox
-----------------------------------------------
Query:      quick         fox
Slop 1:     quick                 ↳     fox

尽管在使用了 slop 短语匹配中所有的单词都需要出现, 但是这些单词也不必为了匹配而按相同的序列排列。 有了足够大的 slop 值, 单词就能按照任意顺序排列了。

为了使查询 fox quick 匹配我们的文档, 我们需要 slop 的值为 3:

            Pos 1         Pos 2         Pos 3
-----------------------------------------------
Doc:        quick         brown         fox
-----------------------------------------------
Query:      fox           quick
Slop 1:     fox|quick  ↵  
Slop 2:     quick      ↳  fox
Slop 3:     quick                 ↳     fox

注意 fox 和 quick 在这步中占据同样的位置。 因此将 fox quick 转换顺序成 quick fox 需要两步, 或者值为 2 的 slop 。

越近越好

鉴于一个短语查询仅仅排除了不包含确切查询短语的文档, 而 邻近查询 — 一个 slop 大于 0— 的短语查询将查询词条的邻近度考虑到最终相关度 _score 中。 通过设置一个像 50 或者 100 这样的高 slop 值, 你能够排除单词距离太远的文档, 但是也给予了那些单词临近的的文档更高的分数。

下列对 quick dog 的邻近查询匹配了同时包含 quick 和 dog 的文档, 但是也给了与 quick 和 dog 更加临近的文档更高的分数 :

POST my_index/_search
{"query": {"match_phrase": {"body": {"query": "quick dog","slop":  50 }}}
}

注意高 slop 值。

"hits": [{"_index": "my_index","_id": "1","_score": 0.38019663,"_source": {"title": "Keeping pets healthy","body": "The quick brown fox jumps over the quick dog."}},{"_index": "my_index","_id": "2","_score": 0.06361735,"_source": {"title": "Keeping pets healthy","body": "My quick brown fox eats rabbits on a regular basis dog."}}]
  • 分数较高因为 quick 和 dog 很接近
  • 分数较低因为 quick 和 dog 分开较远

使用邻近度提高相关度

虽然邻近查询很有用, 但是所有词条都出现在文档的要求过于严格了。 我们讨论 全文搜索 一章的 控制精度 也是同样的问题: 如果七个词条中有六个匹配, 那么这个文档对用户而言就已经足够相关了, 但是 match_phrase 查询可能会将它排除在外。

相比将使用邻近匹配作为绝对要求, 我们可以将它作为 信号— 使用, 作为许多潜在查询中的一个, 会对每个文档的最终分值做出贡献 (参考 多数字段)。

实际上我们想将多个查询的分数累计起来意味着我们应该用 bool 查询将它们合并。

我们可以将一个简单的 match 查询作为一个 must 子句。 这个查询将决定哪些文档需要被包含到结果集中。 我们可以用 minimum_should_match 参数去除长尾。 然后我们可以以 should 子句的形式添加更多特定查询。 每一个匹配成功的都会增加匹配文档的相关度。

GET /my_index/_search
{"query": {"bool": {"must": {"match": {"title": {"query": "quick brown fox","minimum_should_match": "30%"}}},"should": {"match_phrase": {"title": {"query": "quick brown fox","slop": 50}}}}}
}
  • must 子句从结果集中包含或者排除文档。
  • should 子句增加了匹配到文档的相关度评分。

我们当然可以在 should 子句里面添加其它的查询, 其中每一个查询只针对某一特定方面的相关度。

性能优化

短语查询和邻近查询都比简单的 query 查询代价更高。 一个 match 查询仅仅是看词条是否存在于倒排索引中,而一个 match_phrase 查询是必须计算并比较多个可能重复词项的位置。

Lucene nightly benchmarks 表明一个简单的 term 查询比一个短语查询大约快 10 倍,比邻近查询(有 slop 的短语 查询)大约快 20 倍。当然,这个代价指的是在搜索时而不是索引时。

通常,短语查询的额外成本并不像这些数字所暗示的那么吓人。事实上,性能上的差距只是证明一个简单的 term 查询有多快。标准全文数据的短语查询通常在几毫秒内完成,因此实际上都是完全可用,即使是在一个繁忙的集群上。
在某些特定病理案例下,短语查询可能成本太高了,但比较少见。一个典型例子就是DNA序列,在序列里很多同样的词项在很多位置重复出现。在这里使用高 slop 值会到导致位置计算大量增加。

那么我们应该如何限制短语查询和邻近近查询的性能消耗呢?一种有用的方法是减少需要通过短语查询检查的文档总数。

结果集重新评分

在先前的章节中 ,我们讨论了而使用邻近查询来调整相关度,而不是使用它将文档从结果列表中添加或者排除。一个查询可能会匹配成千上万的结果,但我们的用户很可能只对结果的前几页感兴趣。

一个简单的 match 查询已经通过排序把包含所有含有搜索词条的文档放在结果列表的前面了。事实上,我们只想对这些 顶部文档 重新排序,来给同时匹配了短语查询的文档一个额外的相关度升级。

search API 通过 重新评分 明确支持该功能。重新评分阶段支持一个代价更高的评分算法—​比如 phrase 查询—​只是为了从每个分片中获得前 K 个结果。 然后会根据它们的最新评分 重新排序。

该请求如下所示:

GET /my_index/_search
{"query": {"match": {"title": {"query": "quick brown fox","minimum_should_match": "30%"}}},"rescore": {"window_size": 50,"query": {"rescore_query": {"match_phrase": {"title": {"query": "quick brown fox","slop": 50}}}}}
}
  • match 查询决定哪些文档将包含在最终结果集中,并通过 TF/IDF 排序。
  • window_size 是每一分片进行重新评分的顶部文档数量。
  • 目前唯一支持的重新打分算法就是另一个查询,但是以后会有计划增加更多的算法。

寻找相关词

短语查询和邻近查询都很好用,但仍有一个缺点。它们过于严格了:为了匹配短语查询,所有词项都必须存在,即使使用了 slop 。

用 slop 得到的单词顺序的灵活性也需要付出代价,因为失去了单词对之间的联系。即使可以识别 sue 、 alligator 和 ate 相邻出现的文档,但无法分辨是 Sue ate 还是 alligator ate 。

当单词相互结合使用的时候,表达的含义比单独使用更丰富。两个子句 I’m not happy I’m working 和 I’m happy I’m not working 包含相同 的单词,也拥有相同的邻近度,但含义截然不同。

如果索引单词对而不是索引独立的单词,就能对这些单词的上下文尽可能多的保留。

对句子 Sue ate the alligator ,不仅要将每一个单词(或者 unigram )作为词项索引

["sue", "ate", "the", "alligator"]

也要将每个单词 以及它的邻近词 作为单个词项索引:

["sue ate", "ate the", "the alligator"]

这些单词对(或者 bigrams )被称为 shingles 。

Shingles 不限于单词对;你也可以索引三个单词( trigrams ):
[“sue ate the”, “ate the alligator”]
Trigrams 提供了更高的精度,但是也大大增加了索引中唯一词项的数量。在大多数情况下,Bigrams 就够了。

当然,只有当用户输入的查询内容和在原始文档中顺序相同时,shingles 才是有用的;对 sue alligator 的查询可能会匹配到单个单词,但是不会匹配任何 shingles 。

幸运的是,用户倾向于使用和搜索数据相似的构造来表达搜索意图。但这一点很重要:只是索引 bigrams 是不够的;我们仍然需要 unigrams ,但可以将匹配 bigrams 作为增加相关度评分的信号。

生成 Shingles

Shingles 需要在索引时作为分析过程的一部分被创建。我们可以将 unigrams 和 bigrams 都索引到单个字段中, 但将它们分开保存在能被独立查询的字段会更清晰。unigrams 字段将构成我们搜索的基础部分,而 bigrams 字段用来提高相关度。

PUT /my_index
{"settings": {"number_of_shards": 1,  "analysis": {"filter": {"my_shingle_filter": {"type":             "shingle","min_shingle_size": 2, "max_shingle_size": 2, "output_unigrams":  false   }},"analyzer": {"my_shingle_analyzer": {"tokenizer":        "standard","filter": ["lowercase","my_shingle_filter" ]}}}}
}
  • 默认最小/最大的 shingle 大小是 2 ,所以实际上不需要设置。
  • shingle 语汇单元过滤器默认输出 unigrams ,但是我们想让 unigrams 和 bigrams 分开。
  • my_shingle_analyzer 使用我们常规的 my_shingles_filter 语汇单元过滤器。

首先,用 analyze API 测试下分析器:

GET /my_index/_analyze
{"text": "Sue ate the alligator","analyzer": "my_shingle_analyzer"
}

果然, 我们得到了 3 个词项:

{"tokens": [{"token": "sue ate","start_offset": 0,"end_offset": 7,"type": "shingle","position": 0},{"token": "ate the","start_offset": 4,"end_offset": 11,"type": "shingle","position": 1},{"token": "the alligator","start_offset": 8,"end_offset": 21,"type": "shingle","position": 2}]
}
  • sue ate
  • ate the
  • the alligator

现在我们可以继续创建一个使用新的分析器的字段。

多字段

我们曾谈到将 unigrams 和 bigrams 分开索引更清晰,所以 title 字段将创建成一个多字段(参考 字符串排序与多字段 ):

PUT /my_index/_mapping
{"properties": {"title": {"type": "text","fields": {"shingles": {"type": "text","analyzer": "my_shingle_analyzer"}}}}
}

通过这个映射, JSON 文档中的 title 字段将会被以 unigrams (title)和 bigrams (title.shingles)被索引,这意味着可以独立地查询这些字段。

最后,我们可以索引以下示例文档:

POST /my_index/_bulk
{ "index": { "_id": 1 }}
{ "title": "Sue ate the alligator" }
{ "index": { "_id": 2 }}
{ "title": "The alligator ate Sue" }
{ "index": { "_id": 3 }}
{ "title": "Sue never goes anywhere without her alligator skin purse" }

搜索 Shingles

为了理解添加 shingles 字段的好处,让我们首先来看 The hungry alligator ate Sue 进行简单 match 查询的结果:

GET /my_index/_search
{"query": {"match": {"title": "the hungry alligator ate sue"}}
}

这个查询返回了所有的三个文档, 但是注意文档 1 和 2 有相同的相关度评分因为他们包含了相同的单词:

"hits": [{"_index": "my_index","_id": "1","_score": 1.3721708,"_source": {"title": "Sue ate the alligator"}},{"_index": "my_index","_id": "2","_score": 1.3721708,"_source": {"title": "The alligator ate Sue"}},{"_index": "my_index","_id": "3","_score": 0.21526179,"_source": {"title": "Sue never goes anywhere without her alligator skin purse"}}]
  • 两个文档都包含 the 、 alligator 和 ate ,所以获得相同的评分。
  • 我们可以通过设置 minimum_should_match 参数排除文档 3 ,参考 控制精度 。

现在在查询里添加 shingles 字段。不要忘了在 shingles 字段上的匹配是充当一 种信号—​为了提高相关度评分—​所以我们仍然需要将基本 title 字段包含到查询中:

GET /my_index/_search
{"query": {"bool": {"must": {"match": {"title": "the hungry alligator ate sue"}},"should": {"match": {"title.shingles": "the hungry alligator ate sue"}}}}
}

仍然匹配到了所有的 3 个文档, 但是文档 2 现在排到了第一名因为它匹配了 shingled 词项 ate sue.

"hits": [{"_index": "my_index","_id": "2","_score": 3.6694741,"_source": {"title": "The alligator ate Sue"}},{"_index": "my_index","_id": "1","_score": 1.3721708,"_source": {"title": "Sue ate the alligator"}},{"_index": "my_index","_id": "3","_score": 0.21526179,"_source": {"title": "Sue never goes anywhere without her alligator skin purse"}}]

即使查询包含的单词 hungry 没有在任何文档中出现,我们仍然使用单词邻近度返回了最相关的文档。

Performance性能

shingles 不仅比短语查询更灵活,而且性能也更好。 shingles 查询跟一个简单的 match 查询一样高效,而不用每次搜索花费短语查询的代价。只是在索引期间因为更多词项需要被索引会付出一些小的代价, 这也意味着有 shingles 的字段会占用更多的磁盘空间。 然而,大多数应用写入一次而读取多次,所以在索引期间优化我们的查询速度是有意义的。

这是一个在 Elasticsearch 里会经常碰到的话题:不需要任何前期进行过多的设置,就能够在搜索的时候有很好的效果。 一旦更清晰的理解了自己的需求,就能在索引时通过正确的为你的数据建模获得更好结果和性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/795064.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

javaweb学习(day11-监听器Listener过滤器Filter)

一、监听器Listener 1 Listener介绍 Listener 监听器它是 JavaWeb 的三大组件之一。JavaWeb 的三大组件分别是&#xff1a;Servlet 程 序、Listener 监听器、Filter 过滤器 Listener 是 JavaEE 的规范&#xff0c;就是接口 监听器的作用是&#xff0c;监听某种变化(一般就是对…

Java零基础入门-递归

一、概述 上一期&#xff0c;我们是具体学习了File类的一些概念基础知识点&#xff0c;以及对于该类的常用方法进行了一个全量举例演示&#xff0c;这也是考虑到有的小伙伴在阅读的同时&#xff0c;没有时间去实际测试&#xff0c;所以我也就顺带的给大家去做了实例演示&#x…

数据生成 | Matlab实现基于K-means和SVM的GMM高斯混合分布的数据生成

数据生成 | Matlab实现基于K-means和SVM的GMM高斯混合分布的数据生成 目录 数据生成 | Matlab实现基于K-means和SVM的GMM高斯混合分布的数据生成生成效果基本描述模型描述程序设计参考资料 生成效果 基本描述 1.Matlab实现基于K-means和SVM的GMM高斯混合分布的数据生成&#xf…

【实现100个unity特效之7】unity 3d实现各种粒子效果

文章目录 先看最终效果下雨效果萤火虫和火花四溅的效果 3d下雨粒子效果涟漪效果雨滴和涟漪效果结合水花效果雨滴涟漪水花结合问题雾气效果萤火虫火花效果萤火虫和火花效果结合其他特效爆炸、闪电、火焰、雷雨特效&#xff08;2023/7/5更新&#xff09;源码完结 先看最终效果 下…

爬虫 新闻网站 并存储到CSV文件 以红网为例 V2.0 (控制台版)升级自定义查询关键词、时间段,详细注释

爬虫&#xff1a;红网网站&#xff0c; 获取指定关键词与指定时间范围内的新闻&#xff0c;并存储到CSV文件 V2.0&#xff08;控制台版&#xff09; 爬取目的&#xff1a;为了获取某一地区更全面的在红网已发布的宣传新闻稿&#xff0c;同时也让自己的工作更便捷 对比V1.0升级的…

达梦体系结构:进程架构

达梦体系结构&#xff1a;进程架构 进程结构监听线程会话线程工作线程IO线程日志刷盘线程日志归档线程日志重做线程调度线程检查点线程 达梦数据库由数据库和实例构成&#xff1a; 数据库是指磁盘上存放的数据库文件的集合&#xff0c;包括参数文件、控制文件、数据文件、日志…

ObjectiveC-10-OOP面向对象程序设计-分类/类别

类别(Category)是OjectiveC的一个特性&#xff0c;主要目的是让开发者可以以模块的形式向类添加方法&#xff08;扩展&#xff09;&#xff0c;创建标准化的方法列表供给其他人实现。 有些文档也会翻译成类别&#xff0c;其实是一个意思。 概述 语法说明 类别提供了一个简单的…

Codeforces Round 933 (Div. 3)G. Rudolf and Subway 虚点辅佐的dijkstra,用的链式前向星

Problem - G - Codeforces 推荐视频题解&#xff1a;G_哔哩哔哩_bilibili 思路&#xff1a; 先不管同一个线路上的&#xff0c;就正常建边&#xff0c;这样点距都是1. 然后虚点就是该线路的每个点都连的点。 到虚点的边权是1&#xff0c;表示我们坐这趟线路。 然后这个虚点…

C语言中strlen函数的实现

C语言中strlen函数的实现 为了便于和strlen函数区别&#xff0c;以下命令为_strlen。 描述&#xff1a;实现strlen&#xff0c;获取字符串的长度&#xff0c;函数原型如下&#xff1a; size_t strlen(const char *str);_strlen实现&#xff1a; size_t _strlen(const char*…

在flutter中添加video_player【视频播放插件】

添加插件依赖 dependencies:video_player: ^2.8.3插件的用途 在Flutter框架中&#xff0c;video_player 插件是一个专门用于播放视频的插件。它允许开发者在Flutter应用中嵌入视频播放器&#xff0c;并提供了一系列功能来控制和定制视频播放体验。这个插件对于需要在应用中展…

Django - 视图和模板

视图 视图 - 是具体的业务代码 在 app 下的 views.py 文件中编写代码 from django.http import HttpResponsedef index(request):return HttpResponse("这里是我的站点")为了调用该视图&#xff0c;我们还需要编写urlconf&#xff0c;也就是路由配置。在polls目录中…

Vite 项目中环境变量的配置和使用

Vite 项目中环境变量的声明 我们要在 Vite 项目中进行环境变量的声明&#xff0c;那么需要在项目的根目录下&#xff0c;新建 .env.[mode] 文件用于声明环境变量&#xff0c;如&#xff1a; .env.test 文件用于测试环境下项目全局变量的声明.env.dev 文件用于开发环境下项目全…

Scikit-Learn K均值聚类

Scikit-Learn K均值聚类 1、K均值聚类1.1、K均值聚类及原理1.2、K均值聚类的优缺点1.3、聚类与分类的区别2、Scikit-Learn K均值聚类2.1、Scikit-Learn K均值聚类API2.2、K均值聚类初体验2.3、K均值聚类案例1、K均值聚类 K-均值(K-Means)是一种聚类算法,属于无监督学习。K-M…

OpenFOAM学习笔记

OpenFOAM 计算流体力学&#xff1a;用计算机求解流体控制方程&#xff0c;来模拟真实情况下&#xff0c;流体的流动状态OpenFOAM的离散方法&#xff1a;有限体积法&#xff0c;将整个空间划分成若干个控制体OpenFOAM使用的网格系统&#xff1a;同位网格&#xff08;Collocated…

nvm保姆级安装使用教程

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 开发环境篇 ✨特色专栏&#xff1a; M…

python相机校准

文章目录 张正友标定法角点检测标定去畸变 张正友标定法 相片是三维世界在二维平面上的投射&#xff0c;故而其深度信息是损失掉了的。但是&#xff0c;如果把拍照看作理想的小孔成像过程&#xff0c;那么相片中的每个像素&#xff0c;都将通过一个锥体与世界中真实的点一一对…

MongoDB聚合运算符:$median

文章目录 语法参数字段使用类型操作计算注意事项数组处理窗口函数 举例$median作为累加器在 p r o j e c t 阶段使用 project阶段使用 project阶段使用median在$setWindowField阶段使用$median $median聚合运算符以标量值返回中位数的近似值&#xff0c;即第50百分位数。$media…

常见的常见免费开源绘图工具对比 draw.io/Excalidraw/Lucidchart/yEd Graph Editor/Dia/

拓展阅读 常见免费开源绘图工具 OmniGraffle 创建精确、美观图形的工具 UML-架构图入门介绍 starUML UML 绘制工具 starUML 入门介绍 PlantUML 是绘制 uml 的一个开源项目 UML 等常见图绘制工具 绘图工具 draw.io / diagrams.net 免费在线图表编辑器 绘图工具 excalidr…

spa、vue、elementUi

spa (single page application). 动态重写当前页面而非从服务器重新加载整个新页面。使应用程序更像一个桌面应用程序。所有的html、javascript、css通过单个页面检索加载资源。前端页面使用ajax与后端通信。一个项目只有一个html页面。所有的页面跳转都通过路由导航。 vue可用…

【OpenCV进阶】图像中添加中文字幕

OpenCV中的putText函数可以在图像上添加一些你想要的信息&#xff0c;但是该函数只能添加英文字母或数字&#xff0c;无法添加中文字幕。函数原型如下&#xff1a; CV_EXPORTS_W void putText( InputOutputArray img, const String& text, Point org,int fontFace, double…